diff -Nru linux/MAINTAINERS linux-2.4.19-pre5-mjc/MAINTAINERS
--- linux/MAINTAINERS	Mon Apr  8 22:26:52 2002
+++ linux-2.4.19-pre5-mjc/MAINTAINERS	Mon Apr  8 22:31:21 2002
@@ -1498,10 +1498,11 @@
 L:	linux-net@vger.kernel.org
 S:	Maintained
 
-SOUND
-P:	Alan Cox
-M:	alan@redhat.com
-S:	Maintained for 2.2 only
+SOUND - ALSA
+P:	Jaroslav Kysela
+M:	perex@suse.cz
+L:	alsa-devel@alsa-project.org
+S:	Maintained
 
 SPARC:
 P:	David S. Miller
diff -Nru linux/Makefile linux-2.4.19-pre5-mjc/Makefile
--- linux/Makefile	Mon Apr  8 22:26:52 2002
+++ linux-2.4.19-pre5-mjc/Makefile	Mon Apr  8 22:31:21 2002
@@ -130,7 +130,7 @@
 NETWORKS	=net/network.o
 
 LIBS		=$(TOPDIR)/lib/lib.a
-SUBDIRS		=kernel drivers mm fs net ipc lib
+SUBDIRS		=kernel drivers mm fs net ipc lib sound
 
 DRIVERS-n :=
 DRIVERS-y :=
@@ -165,6 +165,7 @@
 endif
 
 DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+DRIVERS-$(CONFIG_SOUND_ALSA) += sound/sound.o
 DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o
 DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o
 DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
@@ -210,6 +211,7 @@
 	drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist \
 	drivers/zorro/devlist.h drivers/zorro/gen-devlist \
 	drivers/sound/bin2hex drivers/sound/hex2hex \
+	sound/oss/bin2hex sound/oss/hex2hex \
 	drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \
 	drivers/scsi/aic7xxx/aicasm/aicasm_gram.c \
 	drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \
@@ -236,6 +238,11 @@
 	drivers/sound/pndsperm.c \
 	drivers/sound/pndspini.c \
 	drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \
+	sound/oss/*_boot.h sound/oss/.*.boot \
+	sound/oss/msndinit.c \
+	sound/oss/msndperm.c \
+	sound/oss/pndsperm.c \
+	sound/oss/pndspini.c \
 	.version .config* config.in config.old \
 	scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \
 	scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
@@ -352,7 +359,7 @@
 init/do_mounts.o: init/do_mounts.c include/config/MARKER
 	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $*.o $<
 
-fs lib mm ipc kernel drivers net: dummy
+fs lib mm ipc kernel drivers net sound: dummy
 	$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
 
 TAGS: dummy
diff -Nru linux/arch/i386/config.in linux-2.4.19-pre5-mjc/arch/i386/config.in
--- linux/arch/i386/config.in	Mon Apr  8 22:26:42 2002
+++ linux-2.4.19-pre5-mjc/arch/i386/config.in	Mon Apr  8 22:31:21 2002
@@ -416,14 +416,25 @@
    endmenu
 fi
 
+if [ "$CONFIG_SOUND" != "y" ] && [ "$CONFIG_SOUND" != "m" ]; then
+   mainmenu_option next_comment
+   comment 'ALSA Sound'
+   tristate 'Sound card support' CONFIG_SOUND_ALSA
+   if [ "$CONFIG_SOUND_ALSA" != "n" ]; then
+      source sound/Config.in
+   fi
+   endmenu
+fi
+
+if [ "$CONFIG_SOUND_ALSA" != "y" ] && [ "$CONFIG_SOUND_ALSA" != "m" ]; then
 mainmenu_option next_comment
 comment 'Sound'
-
 tristate 'Sound card support' CONFIG_SOUND
 if [ "$CONFIG_SOUND" != "n" ]; then
    source drivers/sound/Config.in
 fi
 endmenu
+fi
 
 source drivers/usb/Config.in
 
diff -Nru linux/arch/ppc/config.in linux-2.4.19-pre5-mjc/arch/ppc/config.in
--- linux/arch/ppc/config.in	Mon Apr  8 22:26:10 2002
+++ linux-2.4.19-pre5-mjc/arch/ppc/config.in	Mon Apr  8 22:31:21 2002
@@ -372,6 +372,18 @@
 
 source fs/Config.in
 
+if [ "$CONFIG_SOUND" != "y" ] && [ "$CONFIG_SOUND" != "m" ]; then
+   mainmenu_option next_comment
+   comment 'ALSA Sound'
+   tristate 'Sound card support' CONFIG_SOUND_ALSA
+   if [ "$CONFIG_SOUND_ALSA" != "n" ]; then
+      source sound/oss/dmasound/Config.in
+      source sound/Config.in
+   fi
+   endmenu
+fi
+
+if [ "$CONFIG_SOUND_ALSA" != "y" ] && [ "$CONFIG_SOUND_ALSA" != "m" ]; then
 mainmenu_option next_comment
 comment 'Sound'
 tristate 'Sound card support' CONFIG_SOUND
@@ -379,8 +391,8 @@
   source drivers/sound/dmasound/Config.in
   source drivers/sound/Config.in
 fi
-
 endmenu
+fi
 
 if [ "$CONFIG_8xx" = "y" ]; then
 source arch/ppc/8xx_io/Config.in
diff -Nru linux/include/asm-i386/io.h linux-2.4.19-pre5-mjc/include/asm-i386/io.h
--- linux/include/asm-i386/io.h	Mon Apr  8 22:25:53 2002
+++ linux-2.4.19-pre5-mjc/include/asm-i386/io.h	Mon Apr  8 22:31:24 2002
@@ -160,6 +160,13 @@
 extern void bt_iounmap(void *addr, unsigned long size);
 
 /*
+ * ISA I/O bus memory addresses are 1:1 with the physical address.
+ */
+#define isa_virt_to_bus virt_to_phys
+#define isa_page_to_bus page_to_phys
+#define isa_bus_to_virt phys_to_virt
+
+/*
  * IO bus memory addresses are also 1:1 with the physical address
  */
 #define virt_to_bus virt_to_phys
diff -Nru linux/include/linux/proc_fs.h linux-2.4.19-pre5-mjc/include/linux/proc_fs.h
--- linux/include/linux/proc_fs.h	Mon Apr  8 22:25:57 2002
+++ linux-2.4.19-pre5-mjc/include/linux/proc_fs.h	Mon Apr  8 22:31:24 2002
@@ -209,4 +209,26 @@
 
 #endif /* CONFIG_PROC_FS */
 
+struct proc_inode {
+	struct task_struct *task;
+	int type;
+	union {
+		int (*proc_get_link)(struct inode *, struct dentry **, struct vfsmount **);
+		int (*proc_read)(struct task_struct *task, char *page);
+	} op;
+	struct file *file;
+	struct proc_dir_entry *pde;
+	struct inode vfs_inode;
+};
+
+static inline struct proc_inode *PROC_I(const struct inode *inode)
+{
+	return list_entry(inode, struct proc_inode, vfs_inode);
+}
+
+static inline struct proc_dir_entry *PDE(const struct inode *inode)
+{
+	return PROC_I(inode)->pde;
+}
+
 #endif /* _LINUX_PROC_FS_H */
diff -Nru linux/include/sound/ac97_codec.h linux-2.4.19-pre5-mjc/include/sound/ac97_codec.h
--- linux/include/sound/ac97_codec.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ac97_codec.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,187 @@
+#ifndef __SOUND_AC97_CODEC_H
+#define __SOUND_AC97_CODEC_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.1
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   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
+ *
+ */
+
+#include "control.h"
+#include "info.h"
+
+/*
+ *  AC'97 codec registers
+ */
+
+#define AC97_RESET		0x00	/* Reset */
+#define AC97_MASTER		0x02	/* Master Volume */
+#define AC97_HEADPHONE		0x04	/* Headphone Volume (optional) */
+#define AC97_MASTER_MONO	0x06	/* Master Volume Mono (optional) */
+#define AC97_MASTER_TONE	0x08	/* Master Tone (Bass & Treble) (optional) */
+#define AC97_PC_BEEP		0x0a	/* PC Beep Volume (optinal) */
+#define AC97_PHONE		0x0c	/* Phone Volume (optional) */
+#define AC97_MIC		0x0e	/* MIC Volume */
+#define AC97_LINE		0x10	/* Line In Volume */
+#define AC97_CD			0x12	/* CD Volume */
+#define AC97_VIDEO		0x14	/* Video Volume (optional) */
+#define AC97_AUX		0x16	/* AUX Volume (optional) */
+#define AC97_PCM		0x18	/* PCM Volume */
+#define AC97_REC_SEL		0x1a	/* Record Select */
+#define AC97_REC_GAIN		0x1c	/* Record Gain */
+#define AC97_REC_GAIN_MIC	0x1e	/* Record Gain MIC (optional) */
+#define AC97_GENERAL_PURPOSE	0x20	/* General Purpose (optional) */
+#define AC97_3D_CONTROL		0x22	/* 3D Control (optional) */
+#define AC97_RESERVED		0x24	/* Reserved */
+#define AC97_POWERDOWN		0x26	/* Powerdown control / status */
+/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */
+#define AC97_EXTENDED_ID	0x28	/* Extended Audio ID */
+#define AC97_EXTENDED_STATUS	0x2a	/* Extended Audio Status */
+#define AC97_PCM_FRONT_DAC_RATE 0x2c	/* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE	0x2e	/* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE	0x30	/* PCM LFE DAC Rate */
+#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR DAC Rate */
+#define AC97_PCM_MIC_ADC_RATE	0x34	/* PCM MIC ADC Rate */
+#define AC97_CENTER_LFE_MASTER	0x36	/* Center + LFE Master Volume */
+#define AC97_SURROUND_MASTER	0x38	/* Surround (Rear) Master Volume */
+#define AC97_SPDIF		0x3a	/* S/PDIF control */
+/* range 0x3c-0x58 - MODEM */
+/* range 0x5a-0x7b - Vendor Specific */
+#define AC97_VENDOR_ID1		0x7c	/* Vendor ID1 */
+#define AC97_VENDOR_ID2		0x7e	/* Vendor ID2 / revision */
+
+/* extended audio status and control bit defines */
+#define AC97_EA_VRA		0x0001	/* Variable bit rate enable bit */
+#define AC97_EA_DRA		0x0002	/* Double-rate audio enable bit */
+#define AC97_EA_SPDIF		0x0004	/* S/PDIF Enable bit */
+#define AC97_EA_VRM		0x0008	/* Variable bit rate for MIC enable bit */
+#define AC97_EA_CDAC		0x0040	/* PCM Center DAC is ready (Read only) */
+#define AC97_EA_SDAC		0x0040	/* PCM Surround DACs are ready (Read only) */
+#define AC97_EA_LDAC		0x0080	/* PCM LFE DAC is ready (Read only) */
+#define AC97_EA_MDAC		0x0100	/* MIC ADC is ready (Read only) */
+#define AC97_EA_SPCV		0x0400	/* S/PDIF configuration valid (Read only) */
+#define AC97_EA_PRI		0x0800	/* Turns the PCM Center DAC off */
+#define AC97_EA_PRJ		0x1000	/* Turns the PCM Surround DACs off */
+#define AC97_EA_PRK		0x2000	/* Turns the PCM LFE DAC off */
+#define AC97_EA_PRL		0x4000	/* Turns the MIC ADC off */
+#define AC97_EA_SLOT_MASK	0xffcf	/* Mask for slot assignment bits */
+#define AC97_EA_SPSA_3_4	0x0000	/* Slot assigned to 3 & 4 */
+#define AC97_EA_SPSA_7_8	0x0010	/* Slot assigned to 7 & 8 */
+#define AC97_EA_SPSA_6_9	0x0020	/* Slot assigned to 6 & 9 */
+#define AC97_EA_SPSA_10_11	0x0030	/* Slot assigned to 10 & 11 */
+
+/* S/PDIF control bit defines */
+#define AC97_SC_PRO		0x0001	/* Professional status */
+#define AC97_SC_NAUDIO		0x0002	/* Non audio stream */
+#define AC97_SC_COPY		0x0004	/* Copyright status */
+#define AC97_SC_PRE		0x0008	/* Preemphasis status */
+#define AC97_SC_CC_MASK		0x07f0	/* Category Code mask */
+#define AC97_SC_L		0x0800	/* Generation Level status */
+#define AC97_SC_SPSR_MASK	0xcfff	/* S/PDIF Sample Rate bits */
+#define AC97_SC_SPSR_44K	0x0000	/* Use 44.1kHz Sample rate */
+#define AC97_SC_SPSR_48K	0x2000	/* Use 48kHz Sample rate */
+#define AC97_SC_SPSR_32K	0x3000	/* Use 32kHz Sample rate */
+#define AC97_SC_DRS		0x4000	/* Double Rate S/PDIF */
+#define AC97_SC_V		0x8000	/* Validity status */
+
+/* specific - SigmaTel */
+#define AC97_SIGMATEL_ANALOG	0x6c	/* Analog Special */
+#define AC97_SIGMATEL_DAC2INVERT 0x6e
+#define AC97_SIGMATEL_BIAS1	0x70
+#define AC97_SIGMATEL_BIAS2	0x72
+#define AC97_SIGMATEL_MULTICHN	0x74	/* Multi-Channel programming */
+#define AC97_SIGMATEL_CIC1	0x76
+#define AC97_SIGMATEL_CIC2	0x78
+
+/* specific - Analog Devices */
+#define AC97_AD_TEST		0x5a	/* test register */
+#define AC97_AD_CODEC_CFG	0x70	/* codec configuration */
+#define AC97_AD_JACK_SPDIF	0x72	/* Jack Sense & S/PDIF */
+#define AC97_AD_SERIAL_CFG	0x74	/* Serial Configuration */
+#define AC97_AD_MISC		0x76	/* Misc Control Bits */
+
+/* ac97->scaps */
+#define AC97_SCAP_SURROUND_DAC	(1<<0)	/* surround L&R DACs are present */
+#define AC97_SCAP_CENTER_LFE_DAC (1<<1)	/* center and LFE DACs are present */
+
+/* ac97->flags */
+#define AC97_HAS_PC_BEEP	(1<<0)
+#define AC97_AD_MULTI		(1<<1)	/* Analog Devices - multi codecs */
+
+/*
+
+ */
+
+typedef struct _snd_ac97 ac97_t;
+
+struct _snd_ac97 {
+	void (*write) (ac97_t *ac97, unsigned short reg, unsigned short val);
+	unsigned short (*read) (ac97_t *ac97, unsigned short reg);
+	void (*wait) (ac97_t *ac97);
+	void (*init) (ac97_t *ac97);
+	snd_info_entry_t *proc_entry;
+	snd_info_entry_t *proc_regs_entry;
+	void *private_data;
+	void (*private_free) (ac97_t *ac97);
+	/* --- */
+	snd_card_t *card;
+	spinlock_t reg_lock;
+	unsigned short num;	/* number of codec: 0 = primary, 1 = secondary */
+	unsigned short addr;	/* physical address of codec [0-3] */
+	unsigned int id;	/* identification of codec */
+	unsigned short caps;	/* capabilities (register 0) */
+	unsigned short ext_id;	/* extended feature identification (register 28) */
+	unsigned int scaps;	/* driver capabilities */
+	unsigned int flags;	/* specific code */
+	unsigned int clock;	/* AC'97 clock (usually 48000Hz) */
+	unsigned int rates_front_dac;
+	unsigned int rates_surr_dac;
+	unsigned int rates_lfe_dac;
+	unsigned int rates_adc;
+	unsigned int rates_mic_adc;
+	unsigned int spdif_status;
+	unsigned short regs[0x80]; /* register cache */
+	unsigned char reg_accessed[0x80 / 8]; /* bit flags */
+	union {			/* vendor specific code */
+		struct {
+			unsigned short unchained[3];	// 0 = C34, 1 = C79, 2 = C69
+			unsigned short chained[3];	// 0 = C34, 1 = C79, 2 = C69
+			unsigned short id[3];		// codec IDs (lower 16-bit word)
+			unsigned short pcmreg[3];	// PCM registers
+			struct semaphore mutex;
+		} ad18xx;
+	} spec;
+};
+
+int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97);
+
+void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value);
+unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg);
+void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value);
+int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value);
+int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value);
+int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate);
+#ifdef CONFIG_PM
+void snd_ac97_suspend(ac97_t *ac97);
+void snd_ac97_resume(ac97_t *ac97);
+#endif
+
+#endif /* __SOUND_AC97_CODEC_H */
diff -Nru linux/include/sound/ad1816a.h linux-2.4.19-pre5-mjc/include/sound/ad1816a.h
--- linux/include/sound/ad1816a.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ad1816a.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,174 @@
+#ifndef __SOUND_AD1816A_H
+#define __SOUND_AD1816A_H
+
+/*
+    ad1816a.h - definitions for ADI SoundPort AD1816A chip.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    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
+*/
+
+#include "control.h"
+#include "pcm.h"
+#include "timer.h"
+
+#define AD1816A_REG(r)			(chip->port + r)
+
+#define AD1816A_CHIP_STATUS		0x00
+#define AD1816A_INDIR_ADDR		0x00
+#define AD1816A_INTERRUPT_STATUS	0x01
+#define AD1816A_INDIR_DATA_LOW		0x02
+#define AD1816A_INDIR_DATA_HIGH		0x03
+#define AD1816A_PIO_DEBUG		0x04
+#define AD1816A_PIO_STATUS		0x05
+#define AD1816A_PIO_DATA		0x06
+#define AD1816A_RESERVED_7		0x07
+#define AD1816A_PLAYBACK_CONFIG		0x08
+#define AD1816A_CAPTURE_CONFIG		0x09
+#define AD1816A_RESERVED_10		0x0a
+#define AD1816A_RESERVED_11		0x0b
+#define AD1816A_JOYSTICK_RAW_DATA	0x0c
+#define AD1816A_JOYSTICK_CTRL		0x0d
+#define AD1816A_JOY_POS_DATA_LOW	0x0e
+#define AD1816A_JOY_POS_DATA_HIGH	0x0f
+
+#define AD1816A_LOW_BYTE_TMP		0x00
+#define AD1816A_INTERRUPT_ENABLE	0x01
+#define AD1816A_EXTERNAL_CTRL		0x01
+#define AD1816A_PLAYBACK_SAMPLE_RATE	0x02
+#define AD1816A_CAPTURE_SAMPLE_RATE	0x03
+#define AD1816A_VOICE_ATT		0x04
+#define AD1816A_FM_ATT			0x05
+#define AD1816A_I2S_1_ATT		0x06
+#define AD1816A_I2S_0_ATT		0x07
+#define AD1816A_PLAYBACK_BASE_COUNT	0x08
+#define AD1816A_PLAYBACK_CURR_COUNT	0x09
+#define AD1816A_CAPTURE_BASE_COUNT	0x0a
+#define AD1816A_CAPTURE_CURR_COUNT	0x0b
+#define AD1816A_TIMER_BASE_COUNT	0x0c
+#define AD1816A_TIMER_CURR_COUNT	0x0d
+#define AD1816A_MASTER_ATT		0x0e
+#define AD1816A_CD_GAIN_ATT		0x0f
+#define AD1816A_SYNTH_GAIN_ATT		0x10
+#define AD1816A_VID_GAIN_ATT		0x11
+#define AD1816A_LINE_GAIN_ATT		0x12
+#define AD1816A_MIC_GAIN_ATT		0x13
+#define AD1816A_PHONE_IN_GAIN_ATT	0x13
+#define AD1816A_ADC_SOURCE_SEL		0x14
+#define AD1816A_ADC_PGA			0x14
+#define AD1816A_CHIP_CONFIG		0x20
+#define AD1816A_DSP_CONFIG		0x21
+#define AD1816A_FM_SAMPLE_RATE		0x22
+#define AD1816A_I2S_1_SAMPLE_RATE	0x23
+#define AD1816A_I2S_0_SAMPLE_RATE	0x24
+#define AD1816A_RESERVED_37		0x25
+#define AD1816A_PROGRAM_CLOCK_RATE	0x26
+#define AD1816A_3D_PHAT_CTRL		0x27
+#define AD1816A_PHONE_OUT_ATT		0x27
+#define AD1816A_RESERVED_40		0x28
+#define AD1816A_HW_VOL_BUT		0x29
+#define AD1816A_DSP_MAILBOX_0		0x2a
+#define AD1816A_DSP_MAILBOX_1		0x2b
+#define AD1816A_POWERDOWN_CTRL		0x2c
+#define AD1816A_TIMER_CTRL		0x2c
+#define AD1816A_VERSION_ID		0x2d
+#define AD1816A_RESERVED_46		0x2e
+
+#define AD1816A_READY			0x80
+
+#define AD1816A_PLAYBACK_IRQ_PENDING	0x80
+#define AD1816A_CAPTURE_IRQ_PENDING	0x40
+#define AD1816A_TIMER_IRQ_PENDING	0x20
+
+#define AD1816A_PLAYBACK_ENABLE		0x01
+#define AD1816A_PLAYBACK_PIO		0x02
+#define AD1816A_CAPTURE_ENABLE		0x01
+#define AD1816A_CAPTURE_PIO		0x02
+
+#define AD1816A_FMT_LINEAR_8		0x00
+#define AD1816A_FMT_ULAW_8		0x08
+#define AD1816A_FMT_LINEAR_16_LIT	0x10
+#define AD1816A_FMT_ALAW_8		0x18
+#define AD1816A_FMT_LINEAR_16_BIG	0x30
+#define AD1816A_FMT_ALL			0x38
+#define AD1816A_FMT_STEREO		0x04
+
+#define AD1816A_PLAYBACK_IRQ_ENABLE	0x8000
+#define AD1816A_CAPTURE_IRQ_ENABLE	0x4000
+#define AD1816A_TIMER_IRQ_ENABLE	0x2000
+#define AD1816A_TIMER_ENABLE		0x0080
+
+#define AD1816A_SRC_LINE		0x00
+#define AD1816A_SRC_OUT			0x10
+#define AD1816A_SRC_CD			0x20
+#define AD1816A_SRC_SYNTH		0x30
+#define AD1816A_SRC_VIDEO		0x40
+#define AD1816A_SRC_MIC			0x50
+#define AD1816A_SRC_MONO		0x50
+#define AD1816A_SRC_PHONE_IN		0x60
+#define AD1816A_SRC_MASK		0x70
+
+#define AD1816A_CAPTURE_NOT_EQUAL	0x1000
+#define AD1816A_WSS_ENABLE		0x8000
+
+typedef struct _snd_ad1816a ad1816a_t;
+
+struct _snd_ad1816a {
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+	int dma1;
+	int dma2;
+
+	unsigned short hardware;
+	unsigned short version;
+
+	spinlock_t lock;
+
+	unsigned short mode;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	snd_timer_t *timer;
+};
+
+
+#define AD1816A_HW_AUTO		0
+#define AD1816A_HW_AD1816A	1
+#define AD1816A_HW_AD1815	2
+#define AD1816A_HW_AD18MAX10	3
+
+#define AD1816A_MODE_PLAYBACK	0x01
+#define AD1816A_MODE_CAPTURE	0x02
+#define AD1816A_MODE_TIMER	0x04
+#define AD1816A_MODE_OPEN	(AD1816A_MODE_PLAYBACK |	\
+				AD1816A_MODE_CAPTURE |		\
+				AD1816A_MODE_TIMER)
+
+
+extern int snd_ad1816a_create(snd_card_t *card, unsigned long port,
+			      int irq, int dma1, int dma2,
+			      ad1816a_t **chip);
+
+extern int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm);
+extern int snd_ad1816a_mixer(ad1816a_t *chip);
+
+#endif	/* __SOUND_AD1816A_H */
diff -Nru linux/include/sound/ad1848.h linux-2.4.19-pre5-mjc/include/sound/ad1848.h
--- linux/include/sound/ad1848.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ad1848.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,192 @@
+#ifndef __SOUND_AD1848_H
+#define __SOUND_AD1848_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for AD1847/AD1848/CS4248 chips
+ *
+ *
+ *   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
+ *
+ */
+
+#include "control.h"
+#include "pcm.h"
+
+/* IO ports */
+
+#define AD1848P( codec, x ) ( (chip) -> port + c_d_c_AD1848##x )
+
+#define c_d_c_AD1848REGSEL	0
+#define c_d_c_AD1848REG		1
+#define c_d_c_AD1848STATUS	2
+#define c_d_c_AD1848PIO		3
+
+/* codec registers */
+
+#define AD1848_LEFT_INPUT	0x00	/* left input control */
+#define AD1848_RIGHT_INPUT	0x01	/* right input control */
+#define AD1848_AUX1_LEFT_INPUT	0x02	/* left AUX1 input control */
+#define AD1848_AUX1_RIGHT_INPUT	0x03	/* right AUX1 input control */
+#define AD1848_AUX2_LEFT_INPUT	0x04	/* left AUX2 input control */
+#define AD1848_AUX2_RIGHT_INPUT	0x05	/* right AUX2 input control */
+#define AD1848_LEFT_OUTPUT	0x06	/* left output control register */
+#define AD1848_RIGHT_OUTPUT	0x07	/* right output control register */
+#define AD1848_DATA_FORMAT	0x08	/* clock and data format - playback/capture - bits 7-0 MCE */
+#define AD1848_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
+#define AD1848_PIN_CTRL		0x0a	/* pin control */
+#define AD1848_TEST_INIT	0x0b	/* test and initialization */
+#define AD1848_MISC_INFO	0x0c	/* miscellaneaous information */
+#define AD1848_LOOPBACK		0x0d	/* loopback control */
+#define AD1848_DATA_UPR_CNT	0x0e	/* playback/capture upper base count */
+#define AD1848_DATA_LWR_CNT	0x0f	/* playback/capture lower base count */
+
+/* definitions for codec register select port - CODECP( REGSEL ) */
+
+#define AD1848_INIT		0x80	/* CODEC is initializing */
+#define AD1848_MCE		0x40	/* mode change enable */
+#define AD1848_TRD		0x20	/* transfer request disable */
+
+/* definitions for codec status register - CODECP( STATUS ) */
+
+#define AD1848_GLOBALIRQ	0x01	/* IRQ is active */
+
+/* definitions for AD1848_LEFT_INPUT and AD1848_RIGHT_INPUT registers */
+
+#define AD1848_ENABLE_MIC_GAIN	0x20
+
+#define AD1848_MIXS_LINE1	0x00
+#define AD1848_MIXS_AUX1	0x40
+#define AD1848_MIXS_LINE2	0x80
+#define AD1848_MIXS_ALL		0xc0
+
+/* definitions for clock and data format register - AD1848_PLAYBK_FORMAT */
+
+#define AD1848_LINEAR_8		0x00	/* 8-bit unsigned data */
+#define AD1848_ALAW_8		0x60	/* 8-bit A-law companded */
+#define AD1848_ULAW_8		0x20	/* 8-bit U-law companded */
+#define AD1848_LINEAR_16	0x40	/* 16-bit twos complement data - little endian */
+#define AD1848_STEREO		0x10	/* stereo mode */
+/* bits 3-1 define frequency divisor */
+#define AD1848_XTAL1		0x00	/* 24.576 crystal */
+#define AD1848_XTAL2		0x01	/* 16.9344 crystal */
+
+/* definitions for interface control register - AD1848_IFACE_CTRL */
+
+#define AD1848_CAPTURE_PIO	0x80	/* capture PIO enable */
+#define AD1848_PLAYBACK_PIO	0x40	/* playback PIO enable */
+#define AD1848_CALIB_MODE	0x18	/* calibration mode bits */
+#define AD1848_AUTOCALIB	0x08	/* auto calibrate */
+#define AD1848_SINGLE_DMA	0x04	/* use single DMA channel */
+#define AD1848_CAPTURE_ENABLE	0x02	/* capture enable */
+#define AD1848_PLAYBACK_ENABLE	0x01	/* playback enable */
+
+/* definitions for pin control register - AD1848_PIN_CTRL */
+
+#define AD1848_IRQ_ENABLE	0x02	/* enable IRQ */
+#define AD1848_XCTL1		0x40	/* external control #1 */
+#define AD1848_XCTL0		0x80	/* external control #0 */
+
+/* definitions for test and init register - AD1848_TEST_INIT */
+
+#define AD1848_CALIB_IN_PROGRESS 0x20	/* auto calibrate in progress */
+#define AD1848_DMA_REQUEST	0x10	/* DMA request in progress */
+
+/* defines for codec.mode */
+
+#define AD1848_MODE_NONE	0x0000
+#define AD1848_MODE_PLAY	0x0001
+#define AD1848_MODE_CAPTURE	0x0002
+#define AD1848_MODE_TIMER	0x0004
+#define AD1848_MODE_OPEN	(AD1848_MODE_PLAY|AD1848_MODE_CAPTURE|AD1848_MODE_TIMER)
+#define AD1848_MODE_RUNNING	0x0010
+
+/* defines for codec.hardware */
+
+#define AD1848_HW_DETECT	0x0000	/* let AD1848 driver detect chip */
+#define AD1848_HW_AD1847	0x0001	/* AD1847 chip */
+#define AD1848_HW_AD1848	0x0002	/* AD1848 chip */
+#define AD1848_HW_CS4248	0x0003	/* CS4248 chip */
+#define AD1848_HW_CMI8330	0x0004	/* CMI8330 chip */
+
+struct _snd_ad1848 {
+	unsigned long port;		/* i/o port */
+	struct resource *res_port;
+	int irq;			/* IRQ line */
+	int dma;			/* data DMA */
+	unsigned short version;		/* version of CODEC chip */
+	unsigned short mode;		/* see to AD1848_MODE_XXXX */
+	unsigned short hardware;	/* see to AD1848_HW_XXXX */
+	unsigned short single_dma:1;	/* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */
+
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_card_t *card;
+
+	unsigned char image[32];	/* SGalaxy needs an access to extended registers */
+	int mce_bit;
+	int calibrate_mute;
+	int dma_size;
+
+	spinlock_t reg_lock;
+	struct semaphore open_mutex;
+};
+
+typedef struct _snd_ad1848 ad1848_t;
+
+/* exported functions */
+
+void snd_ad1848_out(ad1848_t *chip, unsigned char reg, unsigned char value);
+void snd_ad1848_dout(ad1848_t *chip, unsigned char reg, unsigned char value);
+unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg);
+void snd_ad1848_mce_up(ad1848_t *chip);
+void snd_ad1848_mce_down(ad1848_t *chip);
+
+int snd_ad1848_create(snd_card_t * card,
+		      unsigned long port,
+		      int irq, int dma,
+		      unsigned short hardware,
+		      ad1848_t ** chip);
+
+int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm);
+int snd_ad1848_mixer(ad1848_t * chip);
+void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ad1848_info_single, \
+  get: snd_ad1848_get_single, put: snd_ad1848_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ad1848_info_double, \
+  get: snd_ad1848_get_double, put: snd_ad1848_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#ifdef CONFIG_SND_DEBUG
+void snd_ad1848_debug(ad1848_t *chip);
+#endif
+
+#endif /* __SOUND_AD1848_H */
diff -Nru linux/include/sound/ainstr_fm.h linux-2.4.19-pre5-mjc/include/sound/ainstr_fm.h
--- linux/include/sound/ainstr_fm.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ainstr_fm.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,132 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  FM (OPL2/3) Instrument Format
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifndef __SOUND_AINSTR_FM_H
+#define __SOUND_AINSTR_FM_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define FM_SHARE_FILE		0
+
+/*
+ * FM operator
+ */
+
+typedef struct fm_operator {
+	unsigned char am_vib;
+	unsigned char ksl_level;
+	unsigned char attack_decay;
+	unsigned char sustain_release;
+	unsigned char wave_select;
+} fm_operator_t;
+
+/*
+ *  Instrument
+ */
+
+#define FM_PATCH_OPL2	0x01		/* OPL2 2 operators FM instrument */
+#define FM_PATCH_OPL3	0x02		/* OPL3 4 operators FM instrument */
+
+typedef struct {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned char type;		/* instrument type */
+
+	fm_operator_t op[4];
+	unsigned char feedback_connection[2];
+
+	unsigned char echo_delay;
+	unsigned char echo_atten;
+	unsigned char chorus_spread;
+	unsigned char trnsps;
+	unsigned char fix_dur;
+	unsigned char modes;
+	unsigned char fix_key;
+} fm_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    fm_xinstrument	FM_STRU_INSTR
+ *
+ */
+
+#define FM_STRU_INSTR	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ * FM operator
+ */
+
+typedef struct fm_xoperator {
+	__u8 am_vib;
+	__u8 ksl_level;
+	__u8 attack_decay;
+	__u8 sustain_release;
+	__u8 wave_select;
+} fm_xoperator_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct fm_xinstrument {
+	__u32 stype;			/* structure type */
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+	__u8 type;			/* instrument type */
+
+	fm_xoperator_t op[4];		/* fm operators */
+	__u8 feedback_connection[2];
+
+	__u8 echo_delay;
+	__u8 echo_atten;
+	__u8 chorus_spread;
+	__u8 trnsps;
+	__u8 fix_dur;
+	__u8 modes;
+	__u8 fix_key;
+} fm_xinstrument_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_fm_id;
+
+int snd_seq_fm_init(snd_seq_kinstr_ops_t * ops,
+		    snd_seq_kinstr_ops_t * next);
+
+#endif
+
+#endif	/* __SOUND_AINSTR_FM_H */
diff -Nru linux/include/sound/ainstr_gf1.h linux-2.4.19-pre5-mjc/include/sound/ainstr_gf1.h
--- linux/include/sound/ainstr_gf1.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ainstr_gf1.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,227 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  GF1 (GUS) Patch Instrument Format
+ *  Copyright (c) 1994-99 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifndef __SOUND_AINSTR_GF1_H
+#define __SOUND_AINSTR_GF1_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define GF1_SHARE_FILE			0
+
+/*
+ *  wave formats
+ */
+
+#define GF1_WAVE_16BIT			0x0001  /* 16-bit wave */
+#define GF1_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+#define GF1_WAVE_INVERT			0x0002  /* same as unsigned wave */
+#define GF1_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+#define GF1_WAVE_LOOP			0x0008  /* loop mode */
+#define GF1_WAVE_BIDIR			0x0010  /* bidirectional mode */
+#define GF1_WAVE_STEREO			0x0100	/* stereo mode */
+#define GF1_WAVE_ULAW			0x0200	/* uLaw compression mode */
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct gf1_wave {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned int format;		/* wave format */
+
+	struct {
+		unsigned int number;	/* some other ID for this instrument */
+		unsigned int memory;	/* begin of waveform in onboard memory */
+		unsigned char *ptr;	/* pointer to waveform in system memory */
+	} address;
+
+	unsigned int size;		/* size of waveform in samples */
+	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_start;	/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_end;		/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+
+	unsigned char flags;		/* GF1 patch flags */
+	unsigned char pad;
+	unsigned int sample_rate;	/* sample rate in Hz */
+	unsigned int low_frequency;	/* low frequency range */
+	unsigned int high_frequency;	/* high frequency range */
+	unsigned int root_frequency;	/* root frequency range */
+	signed short tune;
+	unsigned char balance;
+	unsigned char envelope_rate[6];
+	unsigned char envelope_offset[6];
+	unsigned char tremolo_sweep;
+	unsigned char tremolo_rate;
+	unsigned char tremolo_depth;
+	unsigned char vibrato_sweep;
+	unsigned char vibrato_rate;
+	unsigned char vibrato_depth;
+	unsigned short scale_frequency;
+	unsigned short scale_factor;	/* 0-2048 or 0-2 */
+  
+	struct gf1_wave *next;
+} gf1_wave_t;
+
+/*
+ *  Instrument
+ */
+
+#define IWFFFF_EXCLUDE_NONE		0x0000	/* exclusion mode - none */
+#define IWFFFF_EXCLUDE_SINGLE		0x0001	/* exclude single - single note from the instrument group */
+#define IWFFFF_EXCLUDE_MULTIPLE		0x0002	/* exclude multiple - stop only same note from this instrument */
+
+#define IWFFFF_EFFECT_NONE		0
+#define IWFFFF_EFFECT_REVERB		1
+#define IWFFFF_EFFECT_CHORUS		2
+#define IWFFFF_EFFECT_ECHO		3
+
+typedef struct {
+	unsigned short exclusion;
+	unsigned short exclusion_group;	/* 0 - none, 1-65535 */
+
+	unsigned char effect1;		/* effect 1 */
+	unsigned char effect1_depth;	/* 0-127 */
+	unsigned char effect2;		/* effect 2 */
+	unsigned char effect2_depth;	/* 0-127 */
+
+	gf1_wave_t *wave;		/* first waveform */
+} gf1_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    gf1_xinstrument		IWFFFF_STRU_INSTR
+ *      +gf1_xwave		IWFFFF_STRU_WAVE
+ *
+ */
+
+#define GF1_STRU_WAVE		__cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
+#define GF1_STRU_INSTR		__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct gf1_xwave {
+	__u32 stype;			/* structure type */
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+	__u32 format;			/* wave format */
+
+	__u32 size;			/* size of waveform in samples */
+	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+
+	__u8 flags;			/* GF1 patch flags */
+	__u8 pad;
+	__u32 sample_rate;		/* sample rate in Hz */
+	__u32 low_frequency;		/* low frequency range */
+	__u32 high_frequency;		/* high frequency range */
+	__u32 root_frequency;		/* root frequency range */
+	__s16 tune;
+	__u8 balance;
+	__u8 envelope_rate[6];
+	__u8 envelope_offset[6];
+	__u8 tremolo_sweep;
+	__u8 tremolo_rate;
+	__u8 tremolo_depth;
+	__u8 vibrato_sweep;
+	__u8 vibrato_rate;
+	__u8 vibrato_depth;
+	__u16 scale_frequency;
+	__u16 scale_factor;		/* 0-2048 or 0-2 */  
+} gf1_xwave_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct gf1_xinstrument {
+	__u32 stype;
+	
+	__u16 exclusion;
+	__u16 exclusion_group;		/* 0 - none, 1-65535 */
+
+	__u8 effect1;			/* effect 1 */
+	__u8 effect1_depth;		/* 0-127 */
+	__u8 effect2;			/* effect 2 */
+	__u8 effect2_depth;		/* 0-127 */
+} gf1_xinstrument_t;
+
+/*
+ *  Instrument info
+ */
+
+#define GF1_INFO_ENVELOPE		(1<<0)
+#define GF1_INFO_TREMOLO		(1<<1)
+#define GF1_INFO_VIBRATO		(1<<2)
+
+typedef struct gf1_info {
+	unsigned char flags;		/* supported wave flags */
+	unsigned char pad[3];
+	unsigned int features;		/* supported features */
+	unsigned int max8_len;		/* maximum 8-bit wave length */
+	unsigned int max16_len;		/* maximum 16-bit wave length */
+} gf1_info_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_gf1_id;
+
+typedef struct {
+	void *private_data;
+	int (*info)(void *private_data, gf1_info_t *info);
+	int (*put_sample)(void *private_data, gf1_wave_t *wave,
+	                  char *data, long len, int atomic);
+	int (*get_sample)(void *private_data, gf1_wave_t *wave,
+			  char *data, long len, int atomic);
+	int (*remove_sample)(void *private_data, gf1_wave_t *wave,
+			     int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what);
+	snd_seq_kinstr_ops_t kops;
+} snd_gf1_ops_t;
+
+int snd_seq_gf1_init(snd_gf1_ops_t *ops,
+		     void *private_data,
+		     snd_seq_kinstr_ops_t *next);
+
+#endif
+
+#endif /* __SOUND_AINSTR_GF1_H */
diff -Nru linux/include/sound/ainstr_iw.h linux-2.4.19-pre5-mjc/include/sound/ainstr_iw.h
--- linux/include/sound/ainstr_iw.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ainstr_iw.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,375 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  InterWave FFFF Instrument Format
+ *  Copyright (c) 1994-99 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifndef __SOUND_AINSTR_IW_H
+#define __SOUND_AINSTR_IW_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define IWFFFF_SHARE_FILE		0
+
+/*
+ *  wave formats
+ */
+
+#define IWFFFF_WAVE_16BIT		0x0001  /* 16-bit wave */
+#define IWFFFF_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+#define IWFFFF_WAVE_INVERT		0x0002  /* same as unsigned wave */
+#define IWFFFF_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+#define IWFFFF_WAVE_LOOP		0x0008  /* loop mode */
+#define IWFFFF_WAVE_BIDIR		0x0010  /* bidirectional mode */
+#define IWFFFF_WAVE_ULAW		0x0020  /* uLaw compressed wave */
+#define IWFFFF_WAVE_RAM			0x0040  /* wave is _preloaded_ in RAM (it is used for ROM simulation) */
+#define IWFFFF_WAVE_ROM			0x0080  /* wave is in ROM */
+#define IWFFFF_WAVE_STEREO		0x0100	/* wave is stereo */
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct iwffff_wave {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned int format;		/* wave format */
+
+	struct {
+		unsigned int number;	/* some other ID for this wave */
+		unsigned int memory;	/* begin of waveform in onboard memory */
+		unsigned char *ptr;	/* pointer to waveform in system memory */
+	} address;
+
+	unsigned int size;		/* size of waveform in samples */
+	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_start;	/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_end;		/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+	unsigned int sample_ratio;	/* sample ratio (44100 * 1024 / rate) */
+	unsigned char attenuation;	/* 0 - 127 (no corresponding midi controller) */
+	unsigned char low_note;		/* lower frequency range for this waveform */
+	unsigned char high_note;	/* higher frequency range for this waveform */
+	unsigned char pad;
+  
+	struct iwffff_wave *next;
+} iwffff_wave_t;
+
+/*
+ *  Layer
+ */
+
+#define IWFFFF_LFO_SHAPE_TRIANGLE	0
+#define IWFFFF_LFO_SHAPE_POSTRIANGLE	1
+
+typedef struct iwffff_lfo {
+	unsigned short freq;		/* (0-2047) 0.01Hz - 21.5Hz */
+	signed short depth;		/* volume +- (0-255) 0.48675dB/step */
+	signed short sweep;		/* 0 - 950 deciseconds */
+	unsigned char shape;		/* see to IWFFFF_LFO_SHAPE_XXXX */
+	unsigned char delay;		/* 0 - 255 deciseconds */
+} iwffff_lfo_t;
+
+#define IWFFFF_ENV_FLAG_RETRIGGER	0x0001	/* flag - retrigger */
+
+#define IWFFFF_ENV_MODE_ONE_SHOT	0x0001	/* mode - one shot */
+#define IWFFFF_ENV_MODE_SUSTAIN		0x0002	/* mode - sustain */
+#define IWFFFF_ENV_MODE_NO_SUSTAIN	0x0003	/* mode - no sustain */
+
+#define IWFFFF_ENV_INDEX_VELOCITY	0x0001	/* index - velocity */
+#define IWFFFF_ENV_INDEX_FREQUENCY	0x0002	/* index - frequency */
+
+typedef struct iwffff_env_point {
+	unsigned short offset;
+	unsigned short rate;
+} iwffff_env_point_t;
+
+typedef struct iwffff_env_record {
+	unsigned short nattack;
+	unsigned short nrelease;
+	unsigned short sustain_offset;
+	unsigned short sustain_rate;
+	unsigned short release_rate;
+	unsigned char hirange;
+	unsigned char pad;
+	struct iwffff_env_record *next;
+	/* points are stored here */
+	/* count of points = nattack + nrelease */
+} iwffff_env_record_t;
+
+typedef struct iwffff_env {
+	unsigned char flags;
+  	unsigned char mode;
+  	unsigned char index;
+	unsigned char pad;
+	struct iwffff_env_record *record;
+} iwffff_env_t;
+
+#define IWFFFF_LAYER_FLAG_RETRIGGER	0x0001	/* retrigger */
+
+#define IWFFFF_LAYER_VELOCITY_TIME	0x0000	/* velocity mode = time */
+#define IWFFFF_LAYER_VELOCITY_RATE	0x0001	/* velocity mode = rate */
+
+#define IWFFFF_LAYER_EVENT_KUP		0x0000	/* layer event - key up */
+#define IWFFFF_LAYER_EVENT_KDOWN	0x0001	/* layer event - key down */
+#define IWFFFF_LAYER_EVENT_RETRIG	0x0002	/* layer event - retrigger */
+#define IWFFFF_LAYER_EVENT_LEGATO	0x0003	/* layer event - legato */
+
+typedef struct iwffff_layer {
+	unsigned char flags;
+	unsigned char velocity_mode;
+      	unsigned char layer_event;
+	unsigned char low_range;	/* range for layer based */
+	unsigned char high_range;	/* on either velocity or frequency */
+	unsigned char pan;		/* pan offset from CC1 (0 left - 127 right) */
+	unsigned char pan_freq_scale;	/* position based on frequency (0-127) */
+	unsigned char attenuation;	/* 0-127 (no corresponding midi controller) */
+	iwffff_lfo_t tremolo;		/* tremolo effect */
+	iwffff_lfo_t vibrato;		/* vibrato effect */
+	unsigned short freq_scale;	/* 0-2048, 1024 is equal to semitone scaling */
+	unsigned char freq_center;	/* center for keyboard frequency scaling */
+	unsigned char pad;
+	iwffff_env_t penv;		/* pitch envelope */
+	iwffff_env_t venv;		/* volume envelope */
+
+	iwffff_wave_t *wave;
+	struct iwffff_layer *next;
+} iwffff_layer_t;
+
+/*
+ *  Instrument
+ */
+
+#define IWFFFF_EXCLUDE_NONE		0x0000	/* exclusion mode - none */
+#define IWFFFF_EXCLUDE_SINGLE		0x0001	/* exclude single - single note from the instrument group */
+#define IWFFFF_EXCLUDE_MULTIPLE		0x0002	/* exclude multiple - stop only same note from this instrument */
+
+#define IWFFFF_LAYER_NONE		0x0000	/* not layered */
+#define IWFFFF_LAYER_ON			0x0001	/* layered */
+#define IWFFFF_LAYER_VELOCITY		0x0002	/* layered by velocity */
+#define IWFFFF_LAYER_FREQUENCY		0x0003	/* layered by frequency */
+
+#define IWFFFF_EFFECT_NONE		0
+#define IWFFFF_EFFECT_REVERB		1
+#define IWFFFF_EFFECT_CHORUS		2
+#define IWFFFF_EFFECT_ECHO		3
+
+typedef struct {
+	unsigned short exclusion;
+	unsigned short layer_type;
+	unsigned short exclusion_group;	/* 0 - none, 1-65535 */
+
+	unsigned char effect1;		/* effect 1 */
+	unsigned char effect1_depth;	/* 0-127 */
+	unsigned char effect2;		/* effect 2 */
+	unsigned char effect2_depth;	/* 0-127 */
+
+	iwffff_layer_t *layer;		/* first layer */
+} iwffff_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    iwffff_xinstrument		IWFFFF_STRU_INSTR
+ *      +iwffff_xlayer			IWFFFF_STRU_LAYER
+ *        *iwffff_xenv_record		IWFFFF_STRU_ENV_RECT (tremolo)
+ *        *iwffff_xenv_record		IWFFFF_STRU_EVN_RECT (vibrato)
+ *          +iwffff_xwave		IWFFFF_STRU_WAVE
+ *
+ */
+
+#define IWFFFF_STRU_WAVE	__cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
+#define IWFFFF_STRU_ENV_RECP	__cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P')
+#define IWFFFF_STRU_ENV_RECV	__cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V')
+#define IWFFFF_STRU_LAYER 	__cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R')
+#define IWFFFF_STRU_INSTR 	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct iwffff_xwave {
+	__u32 stype;			/* structure type */
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+
+	__u32 format;			/* wave format */
+	__u32 offset;			/* offset to ROM (address) */
+
+	__u32 size;			/* size of waveform in samples */
+	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+	__u32 sample_ratio;		/* sample ratio (44100 * 1024 / rate) */
+	__u8 attenuation;		/* 0 - 127 (no corresponding midi controller) */
+	__u8 low_note;			/* lower frequency range for this waveform */
+	__u8 high_note;			/* higher frequency range for this waveform */
+	__u8 pad;
+} iwffff_xwave_t;
+
+/*
+ *  Layer
+ */
+
+typedef struct iwffff_xlfo {
+	__u16 freq;			/* (0-2047) 0.01Hz - 21.5Hz */
+	__s16 depth;			/* volume +- (0-255) 0.48675dB/step */
+	__s16 sweep;			/* 0 - 950 deciseconds */
+	__u8 shape;			/* see to ULTRA_IW_LFO_SHAPE_XXXX */
+	__u8 delay;			/* 0 - 255 deciseconds */
+} iwffff_xlfo_t;
+
+typedef struct iwffff_xenv_point {
+	__u16 offset;
+	__u16 rate;
+} iwffff_xenv_point_t;
+
+typedef struct iwffff_xenv_record {
+	__u32 stype;
+	__u16 nattack;
+	__u16 nrelease;
+	__u16 sustain_offset;
+	__u16 sustain_rate;
+	__u16 release_rate;
+	__u8 hirange;
+	__u8 pad;
+	/* points are stored here.. */
+	/* count of points = nattack + nrelease */
+} iwffff_xenv_record_t;
+
+typedef struct iwffff_xenv {
+	__u8 flags;
+  	__u8 mode;
+  	__u8 index;
+	__u8 pad;
+} iwffff_xenv_t;
+
+typedef struct iwffff_xlayer {
+	__u32 stype;
+	__u8 flags;
+	__u8 velocity_mode;
+      	__u8 layer_event;
+	__u8 low_range;			/* range for layer based */
+	__u8 high_range;		/* on either velocity or frequency */
+	__u8 pan;			/* pan offset from CC1 (0 left - 127 right) */
+	__u8 pan_freq_scale;		/* position based on frequency (0-127) */
+	__u8 attenuation;		/* 0-127 (no corresponding midi controller) */
+	iwffff_xlfo_t tremolo;		/* tremolo effect */
+	iwffff_xlfo_t vibrato;		/* vibrato effect */
+	__u16 freq_scale;		/* 0-2048, 1024 is equal to semitone scaling */
+	__u8 freq_center;		/* center for keyboard frequency scaling */
+	__u8 pad;
+	iwffff_xenv_t penv;		/* pitch envelope */
+	iwffff_xenv_t venv;		/* volume envelope */
+} iwffff_xlayer_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct iwffff_xinstrument {
+	__u32 stype;
+	
+	__u16 exclusion;
+	__u16 layer_type;
+	__u16 exclusion_group;		/* 0 - none, 1-65535 */
+
+	__u8 effect1;			/* effect 1 */
+	__u8 effect1_depth;		/* 0-127 */
+	__u8 effect2;			/* effect 2 */
+	__u8 effect2_depth;		/* 0-127 */
+} iwffff_xinstrument_t;
+
+/*
+ *  ROM support
+ *    InterWave ROMs are Little-Endian (x86)
+ */
+
+#define IWFFFF_ROM_HDR_SIZE	512
+
+typedef struct {
+	__u8 iwave[8];
+	__u8 revision;
+	__u8 series_number;
+	__u8 series_name[16];
+	__u8 date[10];
+	__u16 vendor_revision_major;
+	__u16 vendor_revision_minor;
+	__u32 rom_size;
+	__u8 copyright[128];
+	__u8 vendor_name[64];
+	__u8 description[128];
+} iwffff_rom_header_t;
+
+/*
+ *  Instrument info
+ */
+
+#define IWFFFF_INFO_LFO_VIBRATO		(1<<0)
+#define IWFFFF_INFO_LFO_VIBRATO_SHAPE	(1<<1)
+#define IWFFFF_INFO_LFO_TREMOLO		(1<<2)
+#define IWFFFF_INFO_LFO_TREMOLO_SHAPE	(1<<3)
+
+typedef struct iwffff_info {
+	unsigned int format;		/* supported format bits */
+	unsigned int effects;		/* supported effects (1 << IWFFFF_EFFECT*) */
+	unsigned int lfos;		/* LFO effects */
+	unsigned int max8_len;		/* maximum 8-bit wave length */
+	unsigned int max16_len;		/* maximum 16-bit wave length */
+} iwffff_info_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_iwffff_id;
+
+typedef struct {
+	void *private_data;
+	int (*info)(void *private_data, iwffff_info_t *info);
+	int (*put_sample)(void *private_data, iwffff_wave_t *wave,
+	                  char *data, long len, int atomic);
+	int (*get_sample)(void *private_data, iwffff_wave_t *wave,
+			  char *data, long len, int atomic);
+	int (*remove_sample)(void *private_data, iwffff_wave_t *wave,
+			     int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what);
+	snd_seq_kinstr_ops_t kops;
+} snd_iwffff_ops_t;
+
+int snd_seq_iwffff_init(snd_iwffff_ops_t *ops,
+			void *private_data,
+                        snd_seq_kinstr_ops_t *next);
+
+#endif
+
+#endif /* __SOUND_AINSTR_IW_H */
diff -Nru linux/include/sound/ainstr_simple.h linux-2.4.19-pre5-mjc/include/sound/ainstr_simple.h
--- linux/include/sound/ainstr_simple.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ainstr_simple.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,158 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  Simple (MOD player) Instrument Format
+ *  Copyright (c) 1994-99 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifndef __SOUND_AINSTR_SIMPLE_H
+#define __SOUND_AINSTR_SIMPLE_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define SIMPLE_SHARE_FILE		0
+
+/*
+ *  wave formats
+ */
+
+#define SIMPLE_WAVE_16BIT		0x0001  /* 16-bit wave */
+#define SIMPLE_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+#define SIMPLE_WAVE_INVERT		0x0002  /* same as unsigned wave */
+#define SIMPLE_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+#define SIMPLE_WAVE_LOOP		0x0008  /* loop mode */
+#define SIMPLE_WAVE_BIDIR		0x0010  /* bidirectional mode */
+#define SIMPLE_WAVE_STEREO		0x0100	/* stereo wave */
+#define SIMPLE_WAVE_ULAW		0x0200	/* uLaw compression mode */
+
+/*
+ *  instrument effects
+ */
+
+#define SIMPLE_EFFECT_NONE		0
+#define SIMPLE_EFFECT_REVERB		1
+#define SIMPLE_EFFECT_CHORUS		2
+#define SIMPLE_EFFECT_ECHO		3
+
+/*
+ *  instrument info
+ */
+
+typedef struct simple_instrument_info {
+	unsigned int format;		/* supported format bits */
+	unsigned int effects;		/* supported effects (1 << SIMPLE_EFFECT_*) */
+	unsigned int max8_len;		/* maximum 8-bit wave length */
+	unsigned int max16_len;		/* maximum 16-bit wave length */
+} simple_instrument_info_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned int format;		/* wave format */
+
+	struct {
+		unsigned int number;	/* some other ID for this instrument */
+		unsigned int memory;	/* begin of waveform in onboard memory */
+		unsigned char *ptr;	/* pointer to waveform in system memory */
+	} address;
+
+	unsigned int size;		/* size of waveform in samples */
+	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_start;	/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_end;		/* loop end offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+
+	unsigned char effect1;		/* effect 1 */
+	unsigned char effect1_depth;	/* 0-127 */
+	unsigned char effect2;		/* effect 2 */
+	unsigned char effect2_depth;	/* 0-127 */
+} simple_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    simple_xinstrument	SIMPLE_STRU_INSTR
+ *
+ */
+
+#define SIMPLE_STRU_INSTR	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ *  Instrument
+ */
+
+typedef struct simple_xinstrument {
+	__u32 stype;
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+	__u32 format;			/* wave format */
+
+	__u32 size;			/* size of waveform in samples */
+	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+	
+	__u8 effect1;			/* effect 1 */
+	__u8 effect1_depth;		/* 0-127 */
+	__u8 effect2;			/* effect 2 */
+	__u8 effect2_depth;		/* 0-127 */
+} simple_xinstrument_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_simple_id;
+
+typedef struct {
+	void *private_data;
+	int (*info)(void *private_data, simple_instrument_info_t *info);
+	int (*put_sample)(void *private_data, simple_instrument_t *instr,
+	                  char *data, long len, int atomic);
+	int (*get_sample)(void *private_data, simple_instrument_t *instr,
+			  char *data, long len, int atomic);
+	int (*remove_sample)(void *private_data, simple_instrument_t *instr,
+			     int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what);
+	snd_seq_kinstr_ops_t kops;
+} snd_simple_ops_t;
+
+int snd_seq_simple_init(snd_simple_ops_t *ops,
+			void *private_data,
+			snd_seq_kinstr_ops_t *next);
+
+#endif
+
+#endif /* __SOUND_AINSTR_SIMPLE_H */
diff -Nru linux/include/sound/ak4531_codec.h linux-2.4.19-pre5-mjc/include/sound/ak4531_codec.h
--- linux/include/sound/ak4531_codec.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ak4531_codec.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,81 @@
+#ifndef __SOUND_AK4531_CODEC_H
+#define __SOUND_AK4531_CODEC_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.1
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   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
+ *
+ */
+
+#include "info.h"
+#include "control.h"
+
+/*
+ *  ASAHI KASEI - AK4531 codec
+ *  - not really AC'97 codec, but it uses very similar interface as AC'97
+ */
+
+/*
+ *  AK4531 codec registers
+ */
+
+#define AK4531_LMASTER  0x00	/* master volume left */
+#define AK4531_RMASTER  0x01	/* master volume right */
+#define AK4531_LVOICE   0x02	/* channel volume left */
+#define AK4531_RVOICE   0x03	/* channel volume right */
+#define AK4531_LFM      0x04	/* FM volume left */
+#define AK4531_RFM      0x05	/* FM volume right */
+#define AK4531_LCD      0x06	/* CD volume left */
+#define AK4531_RCD      0x07	/* CD volume right */
+#define AK4531_LLINE    0x08	/* LINE volume left */
+#define AK4531_RLINE    0x09	/* LINE volume right */
+#define AK4531_LAUXA    0x0a	/* AUXA volume left */
+#define AK4531_RAUXA    0x0b	/* AUXA volume right */
+#define AK4531_MONO1    0x0c	/* MONO1 volume left */
+#define AK4531_MONO2    0x0d	/* MONO1 volume right */
+#define AK4531_MIC      0x0e	/* MIC volume */
+#define AK4531_MONO_OUT 0x0f	/* Mono-out volume */
+#define AK4531_OUT_SW1  0x10	/* Output mixer switch 1 */
+#define AK4531_OUT_SW2  0x11	/* Output mixer switch 2 */
+#define AK4531_LIN_SW1  0x12	/* Input left mixer switch 1 */
+#define AK4531_RIN_SW1  0x13	/* Input right mixer switch 1 */
+#define AK4531_LIN_SW2  0x14	/* Input left mixer switch 2 */
+#define AK4531_RIN_SW2  0x15	/* Input right mixer switch 2 */
+#define AK4531_RESET    0x16	/* Reset & power down */
+#define AK4531_CLOCK    0x17	/* Clock select */
+#define AK4531_AD_IN    0x18	/* AD input select */
+#define AK4531_MIC_GAIN 0x19	/* MIC amplified gain */
+
+typedef struct _snd_ak4531 ak4531_t;
+
+struct _snd_ak4531 {
+	void (*write) (ak4531_t *ak4531, unsigned short reg, unsigned short val);
+	snd_info_entry_t *proc_entry;
+	void *private_data;
+	void (*private_free) (ak4531_t *ak4531);
+	/* --- */
+	unsigned char regs[0x20];
+	spinlock_t reg_lock;
+};
+
+int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531);
+
+#endif /* __SOUND_AK4531_CODEC_H */
diff -Nru linux/include/sound/asequencer.h linux-2.4.19-pre5-mjc/include/sound/asequencer.h
--- linux/include/sound/asequencer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/asequencer.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,918 @@
+/*
+ *  Main header file for the ALSA sequencer
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *            (c) 1998-1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SOUND_ASEQUENCER_H
+#define __SOUND_ASEQUENCER_H
+
+#ifndef __KERNEL__
+#include <linux/ioctl.h>
+#include <sys/ipc.h>
+#endif
+
+#include <sound/asound.h>
+
+/** version of the sequencer */
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 0)
+
+/**
+ * definition of sequencer event types
+ */
+
+/** system messages
+ * event data type = #sndrv_seq_result_t
+ */
+#define SNDRV_SEQ_EVENT_SYSTEM		0
+#define SNDRV_SEQ_EVENT_RESULT		1
+
+/** note messages (channel specific)
+ * event data type = #sndrv_seq_ev_note
+ */
+#define SNDRV_SEQ_EVENT_NOTE		5
+#define SNDRV_SEQ_EVENT_NOTEON		6
+#define SNDRV_SEQ_EVENT_NOTEOFF		7
+#define SNDRV_SEQ_EVENT_KEYPRESS	8
+	
+/** control messages (channel specific)
+ * event data type = #sndrv_seq_ev_ctrl
+ */
+#define SNDRV_SEQ_EVENT_CONTROLLER	10
+#define SNDRV_SEQ_EVENT_PGMCHANGE	11
+#define SNDRV_SEQ_EVENT_CHANPRESS	12
+#define SNDRV_SEQ_EVENT_PITCHBEND	13	/**< from -8192 to 8191 */
+#define SNDRV_SEQ_EVENT_CONTROL14	14	/**< 14 bit controller value */
+#define SNDRV_SEQ_EVENT_NONREGPARAM	15	/**< 14 bit NRPN */
+#define SNDRV_SEQ_EVENT_REGPARAM	16	/**< 14 bit RPN */
+
+/** synchronisation messages
+ * event data type = #sndrv_seq_ev_ctrl
+ */
+#define SNDRV_SEQ_EVENT_SONGPOS		20	/* Song Position Pointer with LSB and MSB values */
+#define SNDRV_SEQ_EVENT_SONGSEL		21	/* Song Select with song ID number */
+#define SNDRV_SEQ_EVENT_QFRAME		22	/* midi time code quarter frame */
+#define SNDRV_SEQ_EVENT_TIMESIGN	23	/* SMF Time Signature event */
+#define SNDRV_SEQ_EVENT_KEYSIGN		24	/* SMF Key Signature event */
+	        
+/** timer messages
+ * event data type = sndrv_seq_ev_queue_control_t
+ */
+#define SNDRV_SEQ_EVENT_START		30	/* midi Real Time Start message */
+#define SNDRV_SEQ_EVENT_CONTINUE	31	/* midi Real Time Continue message */
+#define SNDRV_SEQ_EVENT_STOP		32	/* midi Real Time Stop message */	
+#define	SNDRV_SEQ_EVENT_SETPOS_TICK	33	/* set tick queue position */
+#define SNDRV_SEQ_EVENT_SETPOS_TIME	34	/* set realtime queue position */
+#define SNDRV_SEQ_EVENT_TEMPO		35	/* (SMF) Tempo event */
+#define SNDRV_SEQ_EVENT_CLOCK		36	/* midi Real Time Clock message */
+#define SNDRV_SEQ_EVENT_TICK		37	/* midi Real Time Tick message */
+#define SNDRV_SEQ_EVENT_QUEUE_SKEW	38	/* skew queue tempo */
+
+/** others
+ * event data type = none
+ */
+#define SNDRV_SEQ_EVENT_TUNE_REQUEST	40	/* tune request */
+#define SNDRV_SEQ_EVENT_RESET		41	/* reset to power-on state */
+#define SNDRV_SEQ_EVENT_SENSING		42	/* "active sensing" event */
+
+/** echo back, kernel private messages
+ * event data type = any type
+ */
+#define SNDRV_SEQ_EVENT_ECHO		50	/* echo event */
+#define SNDRV_SEQ_EVENT_OSS		51	/* OSS raw event */
+
+/** system status messages (broadcast for subscribers)
+ * event data type = sndrv_seq_addr_t
+ */
+#define SNDRV_SEQ_EVENT_CLIENT_START	60	/* new client has connected */
+#define SNDRV_SEQ_EVENT_CLIENT_EXIT	61	/* client has left the system */
+#define SNDRV_SEQ_EVENT_CLIENT_CHANGE	62	/* client status/info has changed */
+#define SNDRV_SEQ_EVENT_PORT_START	63	/* new port was created */
+#define SNDRV_SEQ_EVENT_PORT_EXIT	64	/* port was deleted from system */
+#define SNDRV_SEQ_EVENT_PORT_CHANGE	65	/* port status/info has changed */
+
+/** port connection changes
+ * event data type = sndrv_seq_connect_t
+ */
+#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED	66	/* ports connected */
+#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED	67	/* ports disconnected */
+
+/** synthesizer events
+ * event data type = sndrv_seq_eve_sample_control_t
+ */
+#define SNDRV_SEQ_EVENT_SAMPLE		70	/* sample select */
+#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER	71	/* sample cluster select */
+#define SNDRV_SEQ_EVENT_SAMPLE_START	72	/* voice start */
+#define SNDRV_SEQ_EVENT_SAMPLE_STOP	73	/* voice stop */
+#define SNDRV_SEQ_EVENT_SAMPLE_FREQ	74	/* playback frequency */
+#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME	75	/* volume and balance */
+#define SNDRV_SEQ_EVENT_SAMPLE_LOOP	76	/* sample loop */
+#define SNDRV_SEQ_EVENT_SAMPLE_POSITION	77	/* sample position */
+#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1	78	/* private (hardware dependent) event */
+
+/** user-defined events with fixed length
+ * event data type = any
+ */
+#define SNDRV_SEQ_EVENT_USR0		90
+#define SNDRV_SEQ_EVENT_USR1		91
+#define SNDRV_SEQ_EVENT_USR2		92
+#define SNDRV_SEQ_EVENT_USR3		93
+#define SNDRV_SEQ_EVENT_USR4		94
+#define SNDRV_SEQ_EVENT_USR5		95
+#define SNDRV_SEQ_EVENT_USR6		96
+#define SNDRV_SEQ_EVENT_USR7		97
+#define SNDRV_SEQ_EVENT_USR8		98
+#define SNDRV_SEQ_EVENT_USR9		99
+
+/** instrument layer
+ * variable length data can be passed directly to the driver
+ */
+#define SNDRV_SEQ_EVENT_INSTR_BEGIN	100	/* begin of instrument management */
+#define SNDRV_SEQ_EVENT_INSTR_END	101	/* end of instrument management */
+#define SNDRV_SEQ_EVENT_INSTR_INFO	102	/* instrument interface info */
+#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_FINFO	104	/* get format info */
+#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105	/* get format info */
+#define SNDRV_SEQ_EVENT_INSTR_RESET	106	/* reset instrument memory */
+#define SNDRV_SEQ_EVENT_INSTR_STATUS	107	/* instrument interface status */
+#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_PUT	109	/* put instrument to port */
+#define SNDRV_SEQ_EVENT_INSTR_GET	110	/* get instrument from port */
+#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT	111	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_FREE	112	/* free instrument(s) */
+#define SNDRV_SEQ_EVENT_INSTR_LIST	113	/* instrument list */
+#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER	115	/* cluster parameters */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET	116	/* get cluster parameters */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_CHANGE	118	/* instrument change */
+/* 119-129: reserved */
+
+/* 130-139: variable length events
+ * event data type = sndrv_seq_ev_ext
+ * (SNDRV_SEQ_EVENT_LENGTH_VARIABLE must be set)
+ */
+#define SNDRV_SEQ_EVENT_SYSEX		130	/* system exclusive data (variable length) */
+#define SNDRV_SEQ_EVENT_BOUNCE		131	/* error event */
+/* 132-134: reserved */
+#define SNDRV_SEQ_EVENT_USR_VAR0	135
+#define SNDRV_SEQ_EVENT_USR_VAR1	136
+#define SNDRV_SEQ_EVENT_USR_VAR2	137
+#define SNDRV_SEQ_EVENT_USR_VAR3	138
+#define SNDRV_SEQ_EVENT_USR_VAR4	139
+
+/* 140-149: IPC shared memory events (*NOT SUPPORTED YET*)
+ * event data type = sndrv_seq_ev_ipcshm
+ * (SNDRV_SEQ_EVENT_LENGTH_VARIPC must be set)
+ */
+#define SNDRV_SEQ_EVENT_IPCSHM		140
+/* 141-144: reserved */
+#define SNDRV_SEQ_EVENT_USR_VARIPC0	145
+#define SNDRV_SEQ_EVENT_USR_VARIPC1	146
+#define SNDRV_SEQ_EVENT_USR_VARIPC2	147
+#define SNDRV_SEQ_EVENT_USR_VARIPC3	148
+#define SNDRV_SEQ_EVENT_USR_VARIPC4	149
+
+/* 150-151: kernel events with quote - DO NOT use in user clients */
+#define SNDRV_SEQ_EVENT_KERNEL_ERROR	150
+#define SNDRV_SEQ_EVENT_KERNEL_QUOTE	151
+
+/* 152-191: reserved */
+
+/* 192-254: hardware specific events */
+
+/* 255: special event */
+#define SNDRV_SEQ_EVENT_NONE		255
+
+
+typedef unsigned char sndrv_seq_event_type_t;
+
+/** event address */
+struct sndrv_seq_addr {
+	unsigned char client;	/**< Client number:         0..255, 255 = broadcast to all clients */
+	unsigned char port;	/**< Port within client:    0..255, 255 = broadcast to all ports */
+};
+
+/** port connection */
+struct sndrv_seq_connect {
+	struct sndrv_seq_addr sender;
+	struct sndrv_seq_addr dest;
+};
+
+
+#define SNDRV_SEQ_ADDRESS_UNKNOWN	253	/* unknown source */
+#define SNDRV_SEQ_ADDRESS_SUBSCRIBERS	254	/* send event to all subscribed ports */
+#define SNDRV_SEQ_ADDRESS_BROADCAST	255	/* send event to all queues/clients/ports/channels */
+#define SNDRV_SEQ_QUEUE_DIRECT		253	/* direct dispatch */
+
+	/* event mode flag - NOTE: only 8 bits available! */
+#define SNDRV_SEQ_TIME_STAMP_TICK	(0<<0) /* timestamp in clock ticks */
+#define SNDRV_SEQ_TIME_STAMP_REAL	(1<<0) /* timestamp in real time */
+#define SNDRV_SEQ_TIME_STAMP_MASK	(1<<0)
+
+#define SNDRV_SEQ_TIME_MODE_ABS		(0<<1)	/* absolute timestamp */
+#define SNDRV_SEQ_TIME_MODE_REL		(1<<1)	/* relative to current time */
+#define SNDRV_SEQ_TIME_MODE_MASK	(1<<1)
+
+#define SNDRV_SEQ_EVENT_LENGTH_FIXED	(0<<2)	/* fixed event size */
+#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE	(1<<2)	/* variable event size */
+#define SNDRV_SEQ_EVENT_LENGTH_VARUSR	(2<<2)	/* variable event size - user memory space */
+#define SNDRV_SEQ_EVENT_LENGTH_VARIPC	(3<<2)	/* variable event size - IPC */
+#define SNDRV_SEQ_EVENT_LENGTH_MASK	(3<<2)
+
+#define SNDRV_SEQ_PRIORITY_NORMAL	(0<<4)	/* normal priority */
+#define SNDRV_SEQ_PRIORITY_HIGH		(1<<4)	/* event should be processed before others */
+#define SNDRV_SEQ_PRIORITY_MASK		(1<<4)
+
+
+	/* note event */
+struct sndrv_seq_ev_note {
+	unsigned char channel;
+	unsigned char note;
+	unsigned char velocity;
+	unsigned char off_velocity;	/* only for SNDRV_SEQ_EVENT_NOTE */
+	unsigned int duration;		/* only for SNDRV_SEQ_EVENT_NOTE */
+};
+
+	/* controller event */
+struct sndrv_seq_ev_ctrl {
+	unsigned char channel;
+	unsigned char unused1, unused2, unused3;	/* pad */
+	unsigned int param;
+	signed int value;
+};
+
+	/* generic set of bytes (12x8 bit) */
+struct sndrv_seq_ev_raw8 {
+	unsigned char d[12];	/* 8 bit value */
+};
+
+	/* generic set of integers (3x32 bit) */
+struct sndrv_seq_ev_raw32 {
+	unsigned int d[3];	/* 32 bit value */
+};
+
+	/* external stored data */
+struct sndrv_seq_ev_ext {
+	size_t len;		/* length of data */
+	void *ptr;		/* pointer to data (note: maybe 64-bit) */
+};
+
+	/* external stored data - IPC shared memory */
+struct sndrv_seq_ev_ipcshm {
+	size_t len;		/* length of data */
+	key_t ipc;		/* IPC key */
+};
+
+/* Instrument cluster type */
+typedef unsigned int sndrv_seq_instr_cluster_t;
+
+/* Instrument type */
+struct sndrv_seq_instr {
+	sndrv_seq_instr_cluster_t cluster;
+	unsigned int std;		/* the upper byte means a private instrument (owner - client #) */
+	unsigned short bank;
+	unsigned short prg;
+};
+
+	/* sample number */
+struct sndrv_seq_ev_sample {
+	unsigned int std;
+	unsigned short bank;
+	unsigned short prg;
+};
+
+	/* sample cluster */
+struct sndrv_seq_ev_cluster {
+	sndrv_seq_instr_cluster_t cluster;
+};
+
+	/* sample position */
+typedef unsigned int sndrv_seq_position_t; /* playback position (in samples) * 16 */
+
+	/* sample stop mode */
+enum sndrv_seq_stop_mode {
+	SAMPLE_STOP_IMMEDIATELY = 0,	/* terminate playing immediately */
+	SAMPLE_STOP_VENVELOPE = 1,	/* finish volume envelope */
+	SAMPLE_STOP_LOOP = 2		/* terminate loop and finish wave */
+};
+
+	/* sample frequency */
+typedef int sndrv_seq_frequency_t; /* playback frequency in HZ * 16 */
+
+	/* sample volume control; if any value is set to -1 == do not change */
+struct sndrv_seq_ev_volume {
+	signed short volume;	/* range: 0-16383 */
+	signed short lr;	/* left-right balance; range: 0-16383 */
+	signed short fr;	/* front-rear balance; range: 0-16383 */
+	signed short du;	/* down-up balance; range: 0-16383 */
+};
+
+	/* simple loop redefinition */
+struct sndrv_seq_ev_loop {
+	unsigned int start;	/* loop start (in samples) * 16 */
+	unsigned int end;	/* loop end (in samples) * 16 */
+};
+
+struct sndrv_seq_ev_sample_control {
+	unsigned char channel;
+	unsigned char unused1, unused2, unused3;	/* pad */
+	union {
+		struct sndrv_seq_ev_sample sample;
+		struct sndrv_seq_ev_cluster cluster;
+		sndrv_seq_position_t position;
+		enum sndrv_seq_stop_mode stop_mode;
+		sndrv_seq_frequency_t frequency;
+		struct sndrv_seq_ev_volume volume;
+		struct sndrv_seq_ev_loop loop;
+		unsigned char raw8[8];
+	} param;
+};
+
+
+
+/* INSTR_BEGIN event */
+struct sndrv_seq_ev_instr_begin {
+	int timeout;		/* zero = forever, otherwise timeout in ms */
+};
+
+struct sndrv_seq_result {
+	int event;		/* processed event type */
+	int result;
+};
+
+
+struct sndrv_seq_real_time {
+	unsigned int tv_sec;		/* seconds */
+	unsigned int tv_nsec;	/* nanoseconds */
+};
+
+typedef unsigned int sndrv_seq_tick_time_t;	/* midi ticks */
+
+union sndrv_seq_timestamp {
+	sndrv_seq_tick_time_t tick;
+	struct sndrv_seq_real_time time;
+};
+
+struct sndrv_seq_queue_skew {
+	unsigned int value;
+	unsigned int base;
+};
+
+	/* queue timer control */
+struct sndrv_seq_ev_queue_control {
+	unsigned char queue;			/* affected queue */
+	unsigned char pad[3];			/* reserved */
+	union {
+		signed int value;		/* affected value (e.g. tempo) */
+		union sndrv_seq_timestamp time;	/* time */
+		unsigned int position;		/* sync position */
+		struct sndrv_seq_queue_skew skew;
+		unsigned int d32[2];
+		unsigned char d8[8];
+	} param;
+};
+
+	/* quoted event - inside the kernel only */
+struct sndrv_seq_ev_quote {
+	struct sndrv_seq_addr origin;		/* original sender */
+	unsigned short value;		/* optional data */
+	struct sndrv_seq_event *event;		/* quoted event */
+};
+
+
+	/* sequencer event */
+struct sndrv_seq_event {
+	sndrv_seq_event_type_t type;	/* event type */
+	unsigned char flags;		/* event flags */
+	char tag;
+	
+	unsigned char queue;		/* schedule queue */
+	union sndrv_seq_timestamp time;	/* schedule time */
+
+
+	struct sndrv_seq_addr source;		/* source address */
+	struct sndrv_seq_addr dest;		/* destination address */
+
+	union {				/* event data... */
+		struct sndrv_seq_ev_note note;
+		struct sndrv_seq_ev_ctrl control;
+		struct sndrv_seq_ev_raw8 raw8;
+		struct sndrv_seq_ev_raw32 raw32;
+		struct sndrv_seq_ev_ext ext;
+		struct sndrv_seq_ev_ipcshm ipcshm;
+		struct sndrv_seq_ev_queue_control queue;
+		union sndrv_seq_timestamp time;
+		struct sndrv_seq_addr addr;
+		struct sndrv_seq_connect connect;
+		struct sndrv_seq_result result;
+		struct sndrv_seq_ev_instr_begin instr_begin;
+		struct sndrv_seq_ev_sample_control sample;
+		struct sndrv_seq_ev_quote quote;
+	} data;
+};
+
+
+/*
+ * bounce event - stored as variable size data
+ */
+struct sndrv_seq_event_bounce {
+	int err;
+	struct sndrv_seq_event event;
+	/* external data follows here. */
+};
+
+#define sndrv_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(sndrv_seq_event_bounce_t)))
+
+/*
+ * type check macros
+ */
+/* result events: 0-4 */
+#define sndrv_seq_ev_is_result_type(ev)	((ev)->type < 5)
+/* channel specific events: 5-19 */
+#define sndrv_seq_ev_is_channel_type(ev)	((ev)->type >= 5 && (ev)->type < 20)
+/* note events: 5-9 */
+#define sndrv_seq_ev_is_note_type(ev)	((ev)->type >= 5 && (ev)->type < 10)
+/* control events: 10-19 */
+#define sndrv_seq_ev_is_control_type(ev)	((ev)->type >= 10 && (ev)->type < 20)
+/* queue control events: 30-39 */
+#define sndrv_seq_ev_is_queue_type(ev)	((ev)->type >= 30 && (ev)->type < 40)
+/* system status messages */
+#define sndrv_seq_ev_is_message_type(ev)	((ev)->type >= 60 && (ev)->type < 69)
+/* sample messages */
+#define sndrv_seq_ev_is_sample_type(ev)	((ev)->type >= 70 && (ev)->type < 79)
+/* user-defined messages */
+#define sndrv_seq_ev_is_user_type(ev)	((ev)->type >= 90 && (ev)->type < 99)
+/* fixed length events: 0-99 */
+#define sndrv_seq_ev_is_fixed_type(ev)	((ev)->type < 100)
+/* instrument layer events: 100-129 */
+#define sndrv_seq_ev_is_instr_type(ev)	((ev)->type >= 100 && (ev)->type < 130)
+/* variable length events: 130-139 */
+#define sndrv_seq_ev_is_variable_type(ev)	((ev)->type >= 130 && (ev)->type < 140)
+/* ipc shmem events: 140-149 */
+#define sndrv_seq_ev_is_varipc_type(ev)	((ev)->type >= 140 && (ev)->type < 150)
+/* reserved for kernel */
+#define sndrv_seq_ev_is_reserved(ev)	((ev)->type >= 150)
+
+/* direct dispatched events */
+#define sndrv_seq_ev_is_direct(ev)	((ev)->queue == SNDRV_SEQ_QUEUE_DIRECT)
+
+/*
+ * macros to check event flags
+ */
+/* prior events */
+#define sndrv_seq_ev_is_prior(ev)		(((ev)->flags & SNDRV_SEQ_PRIORITY_MASK) == SNDRV_SEQ_PRIORITY_HIGH)
+
+/* event length type */
+#define sndrv_seq_ev_length_type(ev)	((ev)->flags & SNDRV_SEQ_EVENT_LENGTH_MASK)
+#define sndrv_seq_ev_is_fixed(ev)		(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_FIXED)
+#define sndrv_seq_ev_is_variable(ev)	(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+#define sndrv_seq_ev_is_varusr(ev)	(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+#define sndrv_seq_ev_is_varipc(ev)	(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIPC)
+
+/* time-stamp type */
+#define sndrv_seq_ev_timestamp_type(ev)	((ev)->flags & SNDRV_SEQ_TIME_STAMP_MASK)
+#define sndrv_seq_ev_is_tick(ev)		(sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_TICK)
+#define sndrv_seq_ev_is_real(ev)		(sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_REAL)
+
+/* time-mode type */
+#define sndrv_seq_ev_timemode_type(ev)	((ev)->flags & SNDRV_SEQ_TIME_MODE_MASK)
+#define sndrv_seq_ev_is_abstime(ev)	(sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS)
+#define sndrv_seq_ev_is_reltime(ev)	(sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL)
+
+/* queue sync port */
+#define sndrv_seq_queue_sync_port(q)	((q) + 16)
+
+	/* system information */
+struct sndrv_seq_system_info {
+	int queues;			/* maximum queues count */
+	int clients;			/* maximum clients count */
+	int ports;			/* maximum ports per client */
+	int channels;			/* maximum channels per port */
+	int cur_clients;		/* current clients */
+	int cur_queues;			/* current queues */
+	char reserved[24];
+};
+
+
+	/* known client numbers */
+#define SNDRV_SEQ_CLIENT_SYSTEM		0
+#define SNDRV_SEQ_CLIENT_DUMMY		62	/* dummy ports */
+#define SNDRV_SEQ_CLIENT_OSS		63	/* oss sequencer emulator */
+
+
+	/* client types */
+enum sndrv_seq_client_type {
+	NO_CLIENT       = 0,
+	USER_CLIENT     = 1,
+	KERNEL_CLIENT   = 2
+};
+                        
+	/* event filter flags */
+#define SNDRV_SEQ_FILTER_BROADCAST	(1<<0)	/* accept broadcast messages */
+#define SNDRV_SEQ_FILTER_MULTICAST	(1<<1)	/* accept multicast messages */
+#define SNDRV_SEQ_FILTER_BOUNCE		(1<<2)	/* accept bounce event in error */
+#define SNDRV_SEQ_FILTER_USE_EVENT	(1<<31)	/* use event filter */
+
+struct sndrv_seq_client_info {
+	int client;			/* client number to inquire */
+	enum sndrv_seq_client_type type;	/* client type */
+	char name[64];			/* client name */
+	unsigned int filter;		/* filter flags */
+	unsigned char multicast_filter[8]; /* multicast filter bitmap */
+	unsigned char event_filter[32];	/* event filter bitmap */
+	int num_ports;			/* RO: number of ports */
+	int event_lost;			/* number of lost events */
+	char reserved[64];		/* for future use */
+};
+
+
+/* client pool size */
+struct sndrv_seq_client_pool {
+	int client;			/* client number to inquire */
+	int output_pool;		/* outgoing (write) pool size */
+	int input_pool;			/* incoming (read) pool size */
+	int output_room;		/* minimum free pool size for select/blocking mode */
+	int output_free;		/* unused size */
+	int input_free;			/* unused size */
+	char reserved[64];
+};
+
+
+/* Remove events by specified criteria */
+
+#define SNDRV_SEQ_REMOVE_INPUT		(1<<0)	/* Flush input queues */
+#define SNDRV_SEQ_REMOVE_OUTPUT		(1<<1)	/* Flush output queues */
+#define SNDRV_SEQ_REMOVE_DEST		(1<<2)	/* Restrict by destination q:client:port */
+#define SNDRV_SEQ_REMOVE_DEST_CHANNEL	(1<<3)	/* Restrict by channel */
+#define SNDRV_SEQ_REMOVE_TIME_BEFORE	(1<<4)	/* Restrict to before time */
+#define SNDRV_SEQ_REMOVE_TIME_AFTER	(1<<5)	/* Restrict to time or after */
+#define SNDRV_SEQ_REMOVE_TIME_TICK	(1<<6)	/* Time is in ticks */
+#define SNDRV_SEQ_REMOVE_EVENT_TYPE	(1<<7)	/* Restrict to event type */
+#define SNDRV_SEQ_REMOVE_IGNORE_OFF 	(1<<8)	/* Do not flush off events */
+#define SNDRV_SEQ_REMOVE_TAG_MATCH 	(1<<9)	/* Restrict to events with given tag */
+
+struct sndrv_seq_remove_events {
+	unsigned int  remove_mode;	/* Flags that determine what gets removed */
+
+	union sndrv_seq_timestamp time;
+
+	unsigned char queue;	/* Queue for REMOVE_DEST */
+	struct sndrv_seq_addr dest;	/* Address for REMOVE_DEST */
+	unsigned char channel;	/* Channel for REMOVE_DEST */
+
+	int  type;	/* For REMOVE_EVENT_TYPE */
+	char  tag;	/* Tag for REMOVE_TAG */
+
+	int  reserved[10];	/* To allow for future binary compatibility */
+
+};
+
+
+	/* known port numbers */
+#define SNDRV_SEQ_PORT_SYSTEM_TIMER	0
+#define SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE	1
+
+	/* port capabilities (32 bits) */
+#define SNDRV_SEQ_PORT_CAP_READ		(1<<0)	/* readable from this port */
+#define SNDRV_SEQ_PORT_CAP_WRITE	(1<<1)	/* writable to this port */
+
+#define SNDRV_SEQ_PORT_CAP_SYNC_READ	(1<<2)
+#define SNDRV_SEQ_PORT_CAP_SYNC_WRITE	(1<<3)
+
+#define SNDRV_SEQ_PORT_CAP_DUPLEX	(1<<4)
+
+#define SNDRV_SEQ_PORT_CAP_SUBS_READ	(1<<5)	/* allow read subscription */
+#define SNDRV_SEQ_PORT_CAP_SUBS_WRITE	(1<<6)	/* allow write subscription */
+#define SNDRV_SEQ_PORT_CAP_NO_EXPORT	(1<<7)	/* routing not allowed */
+
+	/* port type */
+#define SNDRV_SEQ_PORT_TYPE_SPECIFIC	(1<<0)	/* hardware specific */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC (1<<1)	/* generic MIDI device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GM	(1<<2)	/* General MIDI compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GS	(1<<3)	/* GS compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_XG	(1<<4)	/* XG compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_MT32	(1<<5)	/* MT-32 compatible device */
+
+/* other standards...*/
+#define SNDRV_SEQ_PORT_TYPE_SYNTH	(1<<10)	/* Synth device */
+#define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11)	/* Sampling device (support sample download) */
+#define SNDRV_SEQ_PORT_TYPE_SAMPLE	(1<<12)	/* Sampling device (sample can be downloaded at any time) */
+/*...*/
+#define SNDRV_SEQ_PORT_TYPE_APPLICATION	(1<<20)	/* application (sequencer/editor) */
+
+/* misc. conditioning flags */
+#define SNDRV_SEQ_PORT_FLG_GIVEN_PORT	(1<<0)
+
+struct sndrv_seq_port_info {
+	struct sndrv_seq_addr addr;	/* client/port numbers */
+	char name[64];			/* port name */
+
+	unsigned int capability;	/* port capability bits */
+	unsigned int type;		/* port type bits */
+	int midi_channels;		/* channels per MIDI port */
+	int midi_voices;		/* voices per MIDI port */
+	int synth_voices;		/* voices per SYNTH port */
+
+	int read_use;			/* R/O: subscribers for output (from this port) */
+	int write_use;			/* R/O: subscribers for input (to this port) */
+
+	void *kernel;			/* reserved for kernel use (must be NULL) */
+
+	unsigned int flags;		/* misc. conditioning */
+	char reserved[60];		/* for future use */
+};
+
+
+/* queue flags */
+#define SNDRV_SEQ_QUEUE_FLG_SYNC	(1<<0)	/* sync enabled */
+
+/* queue information */
+struct sndrv_seq_queue_info {
+	int queue;		/* queue id */
+
+	/*
+	 *  security settings, only owner of this queue can start/stop timer
+	 *  etc. if the queue is locked for other clients
+	 */
+	int owner;		/* client id for owner of the queue */
+	int locked:1;		/* timing queue locked for other queues */
+	char name[64];		/* name of this queue */
+	unsigned int flags;	/* flags */
+	char reserved[60];	/* for future use */
+
+};
+
+/* queue info/status */
+struct sndrv_seq_queue_status {
+	int queue;			/* queue id */
+	int events;			/* read-only - queue size */
+	sndrv_seq_tick_time_t tick;	/* current tick */
+	struct sndrv_seq_real_time time; /* current time */
+	int running;			/* running state of queue */
+	int flags;			/* various flags */
+	char reserved[64];		/* for the future */
+};
+
+
+/* queue tempo */
+struct sndrv_seq_queue_tempo {
+	int queue;			/* sequencer queue */
+	unsigned int tempo;		/* current tempo, us/tick */
+	int ppq;			/* time resolution, ticks/quarter */
+	unsigned int skew_value;	/* queue skew */
+	unsigned int skew_base;		/* queue skew base */
+	char reserved[24];		/* for the future */
+};
+
+
+/* sequencer timer sources */
+#define SNDRV_SEQ_TIMER_ALSA		0	/* ALSA timer */
+#define SNDRV_SEQ_TIMER_MIDI_CLOCK	1	/* Midi Clock (CLOCK event) */
+#define SNDRV_SEQ_TIMER_MIDI_TICK	2	/* Midi Timer Tick (TICK event) */
+
+/* queue timer info */
+struct sndrv_seq_queue_timer {
+	int queue;			/* sequencer queue */
+	int type;			/* source timer type */
+	union {
+		struct {
+			struct sndrv_timer_id id;	/* ALSA's timer ID */
+			unsigned int resolution;	/* resolution in Hz */
+		} alsa;
+	} u;
+	char reserved[64];		/* for the future use */
+};
+
+
+struct sndrv_seq_queue_client {
+	int queue;		/* sequencer queue */
+	int client;		/* sequencer client */
+	int used;		/* queue is used with this client
+				   (must be set for accepting events) */
+	/* per client watermarks */
+	char reserved[64];	/* for future use */
+};
+
+
+#define SNDRV_SEQ_PORT_SUBS_EXCLUSIVE	(1<<0)	/* exclusive connection */
+#define SNDRV_SEQ_PORT_SUBS_TIMESTAMP	(1<<1)
+#define SNDRV_SEQ_PORT_SUBS_TIME_REAL	(1<<2)
+
+struct sndrv_seq_port_subscribe {
+	struct sndrv_seq_addr sender;	/* sender address */
+	struct sndrv_seq_addr dest;	/* destination address */
+	unsigned int voices;		/* number of voices to be allocated (0 = don't care) */
+	unsigned int flags;		/* modes */
+	unsigned char queue;		/* input time-stamp queue (optional) */
+	unsigned char pad[3];		/* reserved */
+	char reserved[64];
+};
+
+/* type of query subscription */
+#define SNDRV_SEQ_QUERY_SUBS_READ	0
+#define SNDRV_SEQ_QUERY_SUBS_WRITE	1
+
+struct sndrv_seq_query_subs {
+	struct sndrv_seq_addr root;	/* client/port id to be searched */
+	int type;		/* READ or WRITE */
+	int index;		/* 0..N-1 */
+	int num_subs;		/* R/O: number of subscriptions on this port */
+	struct sndrv_seq_addr addr;	/* R/O: result */
+	unsigned char queue;	/* R/O: result */
+	unsigned int flags;	/* R/O: result */
+	char reserved[64];	/* for future use */
+};
+
+
+/*
+ *  Instrument abstraction layer
+ *     - based on events
+ */
+
+/* instrument types */
+#define SNDRV_SEQ_INSTR_ATYPE_DATA	0	/* instrument data */
+#define SNDRV_SEQ_INSTR_ATYPE_ALIAS	1	/* instrument alias */
+
+/* instrument ASCII identifiers */
+#define SNDRV_SEQ_INSTR_ID_DLS1		"DLS1"
+#define SNDRV_SEQ_INSTR_ID_DLS2		"DLS2"
+#define SNDRV_SEQ_INSTR_ID_SIMPLE	"Simple Wave"
+#define SNDRV_SEQ_INSTR_ID_SOUNDFONT	"SoundFont"
+#define SNDRV_SEQ_INSTR_ID_GUS_PATCH	"GUS Patch"
+#define SNDRV_SEQ_INSTR_ID_INTERWAVE	"InterWave FFFF"
+#define SNDRV_SEQ_INSTR_ID_OPL2_3	"OPL2/3 FM"
+#define SNDRV_SEQ_INSTR_ID_OPL4		"OPL4"
+
+/* instrument types */
+#define SNDRV_SEQ_INSTR_TYPE0_DLS1	(1<<0)	/* MIDI DLS v1 */
+#define SNDRV_SEQ_INSTR_TYPE0_DLS2	(1<<1)	/* MIDI DLS v2 */
+#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE	(1<<0)	/* Simple Wave */
+#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT	(1<<1)	/* EMU SoundFont */
+#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH	(1<<2)	/* Gravis UltraSound Patch */
+#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE	(1<<3)	/* InterWave FFFF */
+#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3	(1<<0)	/* Yamaha OPL2/3 FM */
+#define SNDRV_SEQ_INSTR_TYPE2_OPL4	(1<<1)	/* Yamaha OPL4 */
+
+/* put commands */
+#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE	0
+#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE	1
+#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY	2
+#define SNDRV_SEQ_INSTR_PUT_CMD_ADD	3
+#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE	4
+
+/* get commands */
+#define SNDRV_SEQ_INSTR_GET_CMD_FULL	0
+#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL	1
+
+/* query flags */
+#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0)
+
+/* free commands */
+#define SNDRV_SEQ_INSTR_FREE_CMD_ALL		0
+#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE	1
+#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER	2
+#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE		3
+
+/* size of ROM/RAM */
+typedef unsigned int sndrv_seq_instr_size_t;
+
+/* INSTR_INFO */
+
+struct sndrv_seq_instr_info {
+	int result;			/* operation result */
+	unsigned int formats[8];	/* bitmap of supported formats */
+	int ram_count;			/* count of RAM banks */
+	sndrv_seq_instr_size_t ram_sizes[16];	/* size of RAM banks */
+	int rom_count;			/* count of ROM banks */
+	sndrv_seq_instr_size_t rom_sizes[8];	/* size of ROM banks */
+	char reserved[128];
+};
+
+/* INSTR_STATUS */
+
+struct sndrv_seq_instr_status {
+	int result;			/* operation result */
+	sndrv_seq_instr_size_t free_ram[16];	/* free RAM in banks */
+	int instrument_count;		/* count of downloaded instruments */
+	char reserved[128];
+};
+
+/* INSTR_FORMAT_INFO */
+
+struct sndrv_seq_instr_format_info {
+	char format[16];		/* format identifier - SNDRV_SEQ_INSTR_ID_* */	
+	unsigned int len;		/* max data length (without this structure) */
+};
+
+struct sndrv_seq_instr_format_info_result {
+	int result;			/* operation result */
+	char format[16];		/* format identifier */
+	unsigned int len;		/* filled data length (without this structure) */
+};
+
+/* instrument data */
+struct sndrv_seq_instr_data {
+	char name[32];			/* instrument name */
+	char reserved[16];		/* for the future use */
+	int type;			/* instrument type */
+	union {
+		char format[16];	/* format identifier */
+		struct sndrv_seq_instr alias;
+	} data;
+};
+
+/* INSTR_PUT/GET, data are stored in one block (extended or IPC), header + data */
+
+struct sndrv_seq_instr_header {
+	union {
+		struct sndrv_seq_instr instr;
+		sndrv_seq_instr_cluster_t cluster;
+	} id;				/* instrument identifier */
+	unsigned int cmd;		/* get/put/free command */
+	unsigned int flags;		/* query flags (only for get) */
+	unsigned int len;		/* real instrument data length (without header) */
+	int result;			/* operation result */
+	char reserved[16];		/* for the future */
+	struct sndrv_seq_instr_data data;	/* instrument data (for put/get result) */
+};
+
+/* INSTR_CLUSTER_SET */
+
+struct sndrv_seq_instr_cluster_set {
+	sndrv_seq_instr_cluster_t cluster; /* cluster identifier */
+	char name[32];			/* cluster name */
+	int priority;			/* cluster priority */
+	char reserved[64];		/* for the future use */
+};
+
+/* INSTR_CLUSTER_GET */
+
+struct sndrv_seq_instr_cluster_get {
+	sndrv_seq_instr_cluster_t cluster; /* cluster identifier */
+	char name[32];			/* cluster name */
+	int priority;			/* cluster priority */
+	char reserved[64];		/* for the future use */
+};
+
+/*
+ *  IOCTL commands
+ */
+
+#define SNDRV_SEQ_IOCTL_PVERSION	_IOR ('S', 0x00, int)
+#define SNDRV_SEQ_IOCTL_CLIENT_ID	_IOR ('S', 0x01, int)
+#define SNDRV_SEQ_IOCTL_SYSTEM_INFO	_IOWR('S', 0x02, struct sndrv_seq_system_info)
+
+#define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO	_IOWR('S', 0x10, struct sndrv_seq_client_info)
+#define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO	_IOW ('S', 0x11, struct sndrv_seq_client_info)
+
+#define SNDRV_SEQ_IOCTL_CREATE_PORT	_IOWR('S', 0x20, struct sndrv_seq_port_info)
+#define SNDRV_SEQ_IOCTL_DELETE_PORT	_IOW ('S', 0x21, struct sndrv_seq_port_info)
+#define SNDRV_SEQ_IOCTL_GET_PORT_INFO	_IOWR('S', 0x22, struct sndrv_seq_port_info)
+#define SNDRV_SEQ_IOCTL_SET_PORT_INFO	_IOW ('S', 0x23, struct sndrv_seq_port_info)
+
+#define SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT	_IOW ('S', 0x30, struct sndrv_seq_port_subscribe)
+#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT	_IOW ('S', 0x31, struct sndrv_seq_port_subscribe)
+
+#define SNDRV_SEQ_IOCTL_CREATE_QUEUE	_IOWR('S', 0x32, struct sndrv_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_DELETE_QUEUE	_IOW ('S', 0x33, struct sndrv_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_INFO	_IOWR('S', 0x34, struct sndrv_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_INFO	_IOWR('S', 0x35, struct sndrv_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE	_IOWR('S', 0x36, struct sndrv_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS	_IOWR('S', 0x40, struct sndrv_seq_queue_status)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO	_IOWR('S', 0x41, struct sndrv_seq_queue_tempo)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO	_IOW ('S', 0x42, struct sndrv_seq_queue_tempo)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER	_IOWR('S', 0x43, struct sndrv_seq_queue_owner)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER	_IOW ('S', 0x44, struct sndrv_seq_queue_owner)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER	_IOWR('S', 0x45, struct sndrv_seq_queue_timer)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER	_IOW ('S', 0x46, struct sndrv_seq_queue_timer)
+/* XXX
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC	_IOWR('S', 0x53, struct sndrv_seq_queue_sync)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC	_IOW ('S', 0x54, struct sndrv_seq_queue_sync)
+*/
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT	_IOWR('S', 0x49, struct sndrv_seq_queue_client)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT	_IOW ('S', 0x4a, struct sndrv_seq_queue_client)
+#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL	_IOWR('S', 0x4b, struct sndrv_seq_client_pool)
+#define SNDRV_SEQ_IOCTL_SET_CLIENT_POOL	_IOW ('S', 0x4c, struct sndrv_seq_client_pool)
+#define SNDRV_SEQ_IOCTL_REMOVE_EVENTS	_IOW ('S', 0x4e, struct sndrv_seq_remove_events)
+#define SNDRV_SEQ_IOCTL_QUERY_SUBS	_IOWR('S', 0x4f, struct sndrv_seq_query_subs)
+#define SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION	_IOWR('S', 0x50, struct sndrv_seq_port_subscribe)
+#define SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT	_IOWR('S', 0x51, struct sndrv_seq_client_info)
+#define SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT	_IOWR('S', 0x52, struct sndrv_seq_port_info)
+
+#endif /* __SOUND_ASEQUENCER_H */
diff -Nru linux/include/sound/asound.h linux-2.4.19-pre5-mjc/include/sound/asound.h
--- linux/include/sound/asound.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/asound.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,775 @@
+/*
+ *  Advanced Linux Sound Architecture - ALSA - Driver
+ *  Copyright (c) 1994-2000 by Jaroslav Kysela <perex@suse.cz>,
+ *                             Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifndef __SOUND_ASOUND_H
+#define __SOUND_ASOUND_H
+
+#if defined(LINUX) || defined(__LINUX__) || defined(__linux__)
+
+#include <linux/ioctl.h>
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#if  __LITTLE_ENDIAN == 1234
+#define SNDRV_LITTLE_ENDIAN
+#elif __BIG_ENDIAN == 4321
+#define SNDRV_BIG_ENDIAN
+#else
+#error "Unsupported endian..."
+#endif
+
+#else /* !__KERNEL__ */
+
+#include <endian.h>
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SNDRV_LITTLE_ENDIAN
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define SNDRV_BIG_ENDIAN
+#else
+#error "Unsupported endian..."
+#endif
+
+#endif /* __KERNEL **/
+
+#endif /* LINUX */
+
+#ifndef __KERNEL__
+#include <sys/time.h>
+#include <sys/types.h>
+#endif
+
+/*
+ *  protocol version
+ */
+
+#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor))
+#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff)
+#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff)
+#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff)
+#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) \
+	(SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || \
+	 (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && \
+	   SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion)))
+
+/****************************************************************************
+ *                                                                          *
+ *        Digital audio interface					    *
+ *                                                                          *
+ ****************************************************************************/
+
+struct sndrv_aes_iec958 {
+	unsigned char status[24];	/* AES/IEC958 channel status bits */
+	unsigned char subcode[147];	/* AES/IEC958 subcode bits */
+	unsigned char pad;		/* nothing */
+	unsigned char dig_subframe[4];	/* AES/IEC958 subframe bits */
+};
+
+/****************************************************************************
+ *                                                                          *
+ *      Section for driver hardware dependent interface - /dev/snd/hw?      *
+ *                                                                          *
+ ****************************************************************************/
+
+#define SNDRV_HWDEP_VERSION		SNDRV_PROTOCOL_VERSION(1, 0, 0)
+
+enum sndrv_hwdep_iface {
+	SNDRV_HWDEP_IFACE_OPL2 = 0,
+	SNDRV_HWDEP_IFACE_OPL3,
+	SNDRV_HWDEP_IFACE_OPL4,
+	SNDRV_HWDEP_IFACE_SB16CSP,	/* Creative Signal Processor */
+	SNDRV_HWDEP_IFACE_EMU10K1,	/* FX8010 processor in EMU10K1 chip */
+	SNDRV_HWDEP_IFACE_YSS225,	/* Yamaha FX processor */
+	SNDRV_HWDEP_IFACE_ICS2115,	/* Wavetable synth */
+
+	/* Don't forget to change the following: */
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_ICS2115,
+};
+
+struct sndrv_hwdep_info {
+	unsigned int device;		/* WR: device number */
+	int card;			/* R: card number */
+	unsigned char id[64];		/* ID (user selectable) */
+	unsigned char name[80];		/* hwdep name */
+	enum sndrv_hwdep_iface iface;	/* hwdep interface */
+	unsigned char reserved[64];	/* reserved for future */
+};
+
+enum {
+	SNDRV_HWDEP_IOCTL_PVERSION = _IOR ('H', 0x00, int),
+	SNDRV_HWDEP_IOCTL_INFO = _IOR ('H', 0x01, struct sndrv_hwdep_info),
+};
+
+/*****************************************************************************
+ *                                                                           *
+ *             Digital Audio (PCM) interface - /dev/snd/pcm??                *
+ *                                                                           *
+ *****************************************************************************/
+
+#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 1)
+
+typedef unsigned long sndrv_pcm_uframes_t;
+typedef long sndrv_pcm_sframes_t;
+
+enum sndrv_pcm_class {
+	SNDRV_PCM_CLASS_GENERIC = 0,	/* standard mono or stereo device */
+	SNDRV_PCM_CLASS_MULTI,		/* multichannel device */
+	SNDRV_PCM_CLASS_MODEM,		/* software modem class */
+	SNDRV_PCM_CLASS_DIGITIZER,	/* digitizer class */
+	/* Don't forget to change the following: */
+	SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER,
+};
+
+enum sndrv_pcm_subclass {
+	SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, /* mono or stereo subdevices are mixed together */
+	SNDRV_PCM_SUBCLASS_MULTI_MIX,	/* multichannel subdevices are mixed together */
+	/* Don't forget to change the following: */
+	SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX,
+};
+
+enum sndrv_pcm_stream {
+	SNDRV_PCM_STREAM_PLAYBACK = 0,
+	SNDRV_PCM_STREAM_CAPTURE,
+	SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE,
+};
+
+enum sndrv_pcm_access {
+	SNDRV_PCM_ACCESS_MMAP_INTERLEAVED = 0,	/* interleaved mmap */
+	SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED, 	/* noninterleaved mmap */
+	SNDRV_PCM_ACCESS_MMAP_COMPLEX,		/* complex mmap */
+	SNDRV_PCM_ACCESS_RW_INTERLEAVED,	/* readi/writei */
+	SNDRV_PCM_ACCESS_RW_NONINTERLEAVED,	/* readn/writen */
+	SNDRV_PCM_ACCESS_LAST = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED,
+};
+
+enum sndrv_pcm_format {
+	SNDRV_PCM_FORMAT_S8 = 0,
+	SNDRV_PCM_FORMAT_U8,
+	SNDRV_PCM_FORMAT_S16_LE,
+	SNDRV_PCM_FORMAT_S16_BE,
+	SNDRV_PCM_FORMAT_U16_LE,
+	SNDRV_PCM_FORMAT_U16_BE,
+	SNDRV_PCM_FORMAT_S24_LE,	/* low three bytes */
+	SNDRV_PCM_FORMAT_S24_BE,	/* low three bytes */
+	SNDRV_PCM_FORMAT_U24_LE,	/* low three bytes */
+	SNDRV_PCM_FORMAT_U24_BE,	/* low three bytes */
+	SNDRV_PCM_FORMAT_S32_LE,
+	SNDRV_PCM_FORMAT_S32_BE,
+	SNDRV_PCM_FORMAT_U32_LE,
+	SNDRV_PCM_FORMAT_U32_BE,
+	SNDRV_PCM_FORMAT_FLOAT_LE,	/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
+	SNDRV_PCM_FORMAT_FLOAT_BE,	/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
+	SNDRV_PCM_FORMAT_FLOAT64_LE,	/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
+	SNDRV_PCM_FORMAT_FLOAT64_BE,	/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
+	SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE,	/* IEC-958 subframe, Little Endian */
+	SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,	/* IEC-958 subframe, Big Endian */
+	SNDRV_PCM_FORMAT_MU_LAW,
+	SNDRV_PCM_FORMAT_A_LAW,
+	SNDRV_PCM_FORMAT_IMA_ADPCM,
+	SNDRV_PCM_FORMAT_MPEG,
+	SNDRV_PCM_FORMAT_GSM,
+	SNDRV_PCM_FORMAT_SPECIAL = 31,
+	SNDRV_PCM_FORMAT_LAST = 31,
+
+#ifdef SNDRV_LITTLE_ENDIAN
+	SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_LE,
+	SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_LE,
+	SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_LE,
+	SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_LE,
+	SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_LE,
+	SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_LE,
+	SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_LE,
+	SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_LE,
+	SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE,
+#endif
+#ifdef SNDRV_BIG_ENDIAN
+	SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_BE,
+	SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_BE,
+	SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_BE,
+	SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_BE,
+	SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_BE,
+	SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_BE,
+	SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_BE,
+	SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_BE,
+	SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+#endif
+};
+
+enum sndrv_pcm_subformat {
+	SNDRV_PCM_SUBFORMAT_STD = 0,
+	SNDRV_PCM_SUBFORMAT_LAST = SNDRV_PCM_SUBFORMAT_STD,
+};
+
+#define SNDRV_PCM_INFO_MMAP		0x00000001	/* hardware supports mmap */
+#define SNDRV_PCM_INFO_MMAP_VALID	0x00000002	/* period data are valid during transfer */
+#define SNDRV_PCM_INFO_DOUBLE		0x00000004	/* Double buffering needed for PCM start/stop */
+#define SNDRV_PCM_INFO_BATCH		0x00000010	/* double buffering */
+#define SNDRV_PCM_INFO_INTERLEAVED	0x00000100	/* channels are interleaved */
+#define SNDRV_PCM_INFO_NONINTERLEAVED	0x00000200	/* channels are not interleaved */
+#define SNDRV_PCM_INFO_COMPLEX		0x00000400	/* complex frame organization (mmap only) */
+#define SNDRV_PCM_INFO_BLOCK_TRANSFER	0x00010000	/* hardware transfer block of samples */
+#define SNDRV_PCM_INFO_OVERRANGE	0x00020000	/* hardware supports ADC (capture) overrange detection */
+#define SNDRV_PCM_INFO_RESUME		0x00040000	/* hardware supports stream resume after suspend */
+#define SNDRV_PCM_INFO_PAUSE		0x00080000	/* pause ioctl is supported */
+#define SNDRV_PCM_INFO_HALF_DUPLEX	0x00100000	/* only half duplex */
+#define SNDRV_PCM_INFO_JOINT_DUPLEX	0x00200000	/* playback and capture stream are somewhat correlated */
+#define SNDRV_PCM_INFO_SYNC_START	0x00400000	/* pcm support some kind of sync go */
+
+enum sndrv_pcm_state {
+	SNDRV_PCM_STATE_OPEN = 0,	/* stream is open */
+	SNDRV_PCM_STATE_SETUP,		/* stream has a setup */
+	SNDRV_PCM_STATE_PREPARED,	/* stream is ready to start */
+	SNDRV_PCM_STATE_RUNNING,	/* stream is running */
+	SNDRV_PCM_STATE_XRUN,		/* stream reached an xrun */
+	SNDRV_PCM_STATE_DRAINING,	/* stream is draining */
+	SNDRV_PCM_STATE_PAUSED,		/* stream is paused */
+	SNDRV_PCM_STATE_SUSPENDED,	/* hardware is suspended */
+	SNDRV_PCM_STATE_LAST = SNDRV_PCM_STATE_SUSPENDED,
+};
+
+enum {
+	SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000,
+	SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000,
+	SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000,
+};
+
+union sndrv_pcm_sync_id {
+	unsigned char id[16];
+	unsigned short id16[8];
+	unsigned int id32[4];
+};
+
+struct sndrv_pcm_info {
+	unsigned int device;		/* RO/WR (control): device number */
+	unsigned int subdevice;		/* RO/WR (control): subdevice number */
+	enum sndrv_pcm_stream stream;	/* RO/WR (control): stream number */
+	int card;			/* R: card number */
+	unsigned char id[64];		/* ID (user selectable) */
+	unsigned char name[80];		/* name of this device */
+	unsigned char subname[32];	/* subdevice name */
+	enum sndrv_pcm_class dev_class;	/* SNDRV_PCM_CLASS_* */
+	enum sndrv_pcm_subclass dev_subclass; /* SNDRV_PCM_SUBCLASS_* */
+	unsigned int subdevices_count;
+	unsigned int subdevices_avail;
+	union sndrv_pcm_sync_id sync;	/* hardware synchronization ID */
+	unsigned char reserved[64];	/* reserved for future... */
+};
+
+enum sndrv_pcm_hw_param {
+	SNDRV_PCM_HW_PARAM_ACCESS = 0,	/* Access type */
+	SNDRV_PCM_HW_PARAM_FIRST_MASK = SNDRV_PCM_HW_PARAM_ACCESS,
+	SNDRV_PCM_HW_PARAM_FORMAT,	/* Format */
+	SNDRV_PCM_HW_PARAM_SUBFORMAT,	/* Subformat */
+	SNDRV_PCM_HW_PARAM_LAST_MASK = SNDRV_PCM_HW_PARAM_SUBFORMAT,
+
+	SNDRV_PCM_HW_PARAM_SAMPLE_BITS, /* Bits per sample */
+	SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+	SNDRV_PCM_HW_PARAM_FRAME_BITS,	/* Bits per frame */
+	SNDRV_PCM_HW_PARAM_CHANNELS,	/* Channels */
+	SNDRV_PCM_HW_PARAM_RATE,	/* Approx rate */
+	SNDRV_PCM_HW_PARAM_PERIOD_TIME,	/* Approx distance between interrupts
+					   in us */
+	SNDRV_PCM_HW_PARAM_PERIOD_SIZE,	/* Approx frames between interrupts */
+	SNDRV_PCM_HW_PARAM_PERIOD_BYTES, /* Approx bytes between interrupts */
+	SNDRV_PCM_HW_PARAM_PERIODS,	/* Approx interrupts per buffer */
+	SNDRV_PCM_HW_PARAM_BUFFER_TIME,	/* Approx duration of buffer in us */
+	SNDRV_PCM_HW_PARAM_BUFFER_SIZE,	/* Size of buffer in frames */
+	SNDRV_PCM_HW_PARAM_BUFFER_BYTES, /* Size of buffer in bytes */
+	SNDRV_PCM_HW_PARAM_TICK_TIME,	/* Approx tick duration in us */
+	SNDRV_PCM_HW_PARAM_LAST_INTERVAL = SNDRV_PCM_HW_PARAM_TICK_TIME,
+	SNDRV_PCM_HW_PARAM_LAST = SNDRV_PCM_HW_PARAM_LAST_INTERVAL,
+};
+
+#define SNDRV_PCM_HW_PARAMS_RUNTIME		(1<<0)
+
+struct sndrv_interval {
+	unsigned int min, max;
+	unsigned int openmin:1,
+		     openmax:1,
+		     integer:1,
+		     empty:1;
+};
+
+struct sndrv_pcm_hw_params {
+	unsigned int flags;
+	unsigned int masks[SNDRV_PCM_HW_PARAM_LAST_MASK - 
+			   SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
+	struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
+				        SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+	unsigned int rmask;
+	unsigned int cmask;
+	unsigned int info;		/* R: Info flags for returned setup */
+	unsigned int msbits;		/* R: used most significant bits */
+	unsigned int rate_num;		/* R: rate numerator */
+	unsigned int rate_den;		/* R: rate denominator */
+	sndrv_pcm_uframes_t fifo_size;	/* R: chip FIFO size in frames */
+	unsigned char reserved[64];
+};
+
+enum sndrv_pcm_tstamp {
+	SNDRV_PCM_TSTAMP_NONE = 0,
+	SNDRV_PCM_TSTAMP_MMAP,
+	SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP,
+};
+
+struct sndrv_pcm_sw_params {
+	enum sndrv_pcm_tstamp tstamp_mode;	/* timestamp mode */
+	unsigned int period_step;
+	unsigned int sleep_min;			/* min ticks to sleep */
+	sndrv_pcm_uframes_t avail_min;		/* min avail frames for wakeup */
+	sndrv_pcm_uframes_t xfer_align;		/* xfer size need to be a multiple */
+	sndrv_pcm_uframes_t start_threshold;	/* min hw_avail frames for automatic start */
+	sndrv_pcm_uframes_t stop_threshold;	/* min avail frames for automatic stop */
+	sndrv_pcm_uframes_t silence_threshold;	/* min distance from noise for silence filling */
+	sndrv_pcm_uframes_t silence_size;	/* silence block size */
+	sndrv_pcm_uframes_t boundary;		/* pointers wrap point */
+	unsigned char reserved[64];
+};
+
+struct sndrv_pcm_channel_info {
+	unsigned int channel;
+	off_t offset;			/* mmap offset */
+	unsigned int first;		/* offset to first sample in bits */
+	unsigned int step;		/* samples distance in bits */
+};
+
+struct sndrv_pcm_status {
+	enum sndrv_pcm_state state;	/* stream state */
+	struct timeval trigger_tstamp;	/* time when stream was started/stopped/paused */
+	struct timeval tstamp;		/* reference timestamp */
+	sndrv_pcm_uframes_t appl_ptr;	/* appl ptr */
+	sndrv_pcm_uframes_t hw_ptr;	/* hw ptr */
+	sndrv_pcm_sframes_t delay;	/* current delay in frames */
+	sndrv_pcm_uframes_t avail;	/* number of frames available */
+	sndrv_pcm_uframes_t avail_max;	/* max frames available on hw since last status */
+	sndrv_pcm_uframes_t overrange;	/* count of ADC (capture) overrange detections from last status */
+	enum sndrv_pcm_state suspended_state; /* suspended stream state */
+	unsigned char reserved[60];	/* must be filled with zero */
+};
+
+struct sndrv_pcm_mmap_status {
+	enum sndrv_pcm_state state;	/* RO: state - SNDRV_PCM_STATE_XXXX */
+	int pad1;			/* Needed for 64 bit alignment */
+	sndrv_pcm_uframes_t hw_ptr;	/* RO: hw ptr (0...boundary-1) */
+	struct timeval tstamp;		/* Timestamp */
+	enum sndrv_pcm_state suspended_state; /* RO: suspended stream state */
+};
+
+struct sndrv_pcm_mmap_control {
+	sndrv_pcm_uframes_t appl_ptr;	/* RW: appl ptr (0...boundary-1) */
+	sndrv_pcm_uframes_t avail_min;	/* RW: min available frames for wakeup */
+};
+
+struct sndrv_xferi {
+	sndrv_pcm_sframes_t result;
+	void *buf;
+	sndrv_pcm_uframes_t frames;
+};
+
+struct sndrv_xfern {
+	sndrv_pcm_sframes_t result;
+	void **bufs;
+	sndrv_pcm_uframes_t frames;
+};
+
+enum {
+	SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
+	SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info),
+	SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params),
+	SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params),
+	SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
+	SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct sndrv_pcm_sw_params),
+	SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct sndrv_pcm_status),
+	SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, sndrv_pcm_sframes_t),
+	SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct sndrv_pcm_channel_info),
+	SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40),
+	SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41),
+	SNDRV_PCM_IOCTL_START = _IO('A', 0x42),
+	SNDRV_PCM_IOCTL_DROP = _IO('A', 0x43),
+	SNDRV_PCM_IOCTL_DRAIN = _IO('A', 0x44),
+	SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int),
+	SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, sndrv_pcm_uframes_t),
+	SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47),
+	SNDRV_PCM_IOCTL_XRUN = _IO('A', 0x48),
+	SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct sndrv_xferi),
+	SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct sndrv_xferi),
+	SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct sndrv_xfern),
+	SNDRV_PCM_IOCTL_READN_FRAMES = _IOR('A', 0x53, struct sndrv_xfern),
+	SNDRV_PCM_IOCTL_LINK = _IOW('A', 0x60, int),
+	SNDRV_PCM_IOCTL_UNLINK = _IO('A', 0x61),
+};
+
+/* Trick to make alsa-lib/acinclude.m4 happy */
+#define SNDRV_PCM_IOCTL_REWIND SNDRV_PCM_IOCTL_REWIND
+
+/*****************************************************************************
+ *                                                                           *
+ *                            MIDI v1.0 interface                            *
+ *                                                                           *
+ *****************************************************************************/
+
+/*
+ *  Raw MIDI section - /dev/snd/midi??
+ */
+
+#define SNDRV_RAWMIDI_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 0)
+
+enum sndrv_rawmidi_stream {
+	SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
+	SNDRV_RAWMIDI_STREAM_INPUT,
+	SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT,
+};
+
+#define SNDRV_RAWMIDI_INFO_OUTPUT		0x00000001
+#define SNDRV_RAWMIDI_INFO_INPUT		0x00000002
+#define SNDRV_RAWMIDI_INFO_DUPLEX		0x00000004
+
+struct sndrv_rawmidi_info {
+	unsigned int device;		/* RO/WR (control): device number */
+	unsigned int subdevice;		/* RO/WR (control): subdevice number */
+	enum sndrv_rawmidi_stream stream; /* WR: stream */
+	int card;			/* R: card number */
+	unsigned int flags;		/* SNDRV_RAWMIDI_INFO_XXXX */
+	unsigned char id[64];		/* ID (user selectable) */
+	unsigned char name[80];		/* name of device */
+	unsigned char subname[32];	/* name of active or selected subdevice */
+	unsigned int subdevices_count;
+	unsigned int subdevices_avail;
+	unsigned char reserved[64];	/* reserved for future use */
+};
+
+struct sndrv_rawmidi_params {
+	enum sndrv_rawmidi_stream stream;
+	size_t buffer_size;		/* queue size in bytes */
+	size_t avail_min;		/* minimum avail bytes for wakeup */
+	unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
+	unsigned char reserved[16];	/* reserved for future use */
+};
+
+struct sndrv_rawmidi_status {
+	enum sndrv_rawmidi_stream stream;
+	struct timeval tstamp;		/* Timestamp */
+	size_t avail;			/* available bytes */
+	size_t xruns;			/* count of overruns since last status (in bytes) */
+	unsigned char reserved[16];	/* reserved for future use */
+};
+
+enum {
+	SNDRV_RAWMIDI_IOCTL_PVERSION = _IOR('W', 0x00, int),
+	SNDRV_RAWMIDI_IOCTL_INFO = _IOR('W', 0x01, struct sndrv_rawmidi_info),
+	SNDRV_RAWMIDI_IOCTL_PARAMS = _IOWR('W', 0x10, struct sndrv_rawmidi_params),
+	SNDRV_RAWMIDI_IOCTL_STATUS = _IOWR('W', 0x20, struct sndrv_rawmidi_status),
+	SNDRV_RAWMIDI_IOCTL_DROP = _IOW('W', 0x30, int),
+	SNDRV_RAWMIDI_IOCTL_DRAIN = _IOW('W', 0x31, int),
+};
+
+/*
+ *  Timer section - /dev/snd/timer
+ */
+
+#define SNDRV_TIMER_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 0)
+
+enum sndrv_timer_class {
+	SNDRV_TIMER_CLASS_NONE = -1,
+	SNDRV_TIMER_CLASS_SLAVE = 0,
+	SNDRV_TIMER_CLASS_GLOBAL,
+	SNDRV_TIMER_CLASS_CARD,
+	SNDRV_TIMER_CLASS_PCM,
+	SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM,
+};
+
+/* slave timer classes */
+enum sndrv_timer_slave_class {
+	SNDRV_TIMER_SCLASS_NONE = 0,
+	SNDRV_TIMER_SCLASS_APPLICATION,
+	SNDRV_TIMER_SCLASS_SEQUENCER,		/* alias */
+	SNDRV_TIMER_SCLASS_OSS_SEQUENCER,	/* alias */
+	SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER,
+};
+
+/* global timers (device member) */
+#define SNDRV_TIMER_GLOBAL_SYSTEM	0
+#define SNDRV_TIMER_GLOBAL_RTC		1
+
+struct sndrv_timer_id {
+	enum sndrv_timer_class dev_class;	
+	enum sndrv_timer_slave_class dev_sclass;
+	int card;
+	int device;
+	int subdevice;
+};
+
+struct sndrv_timer_select {
+	struct sndrv_timer_id id;	/* bind to timer ID */
+	unsigned char reserved[32];	/* reserved */
+};
+
+#define SNDRV_TIMER_FLG_SLAVE		(1<<0)	/* cannot be controlled */
+
+struct sndrv_timer_info {
+	unsigned int flags;		/* timer flags - SNDRV_TIMER_FLG_* */
+	int card;			/* R: card number */
+	unsigned char id[64];		/* timer identificator */
+	unsigned char name[80];		/* timer name */
+	unsigned long ticks;		/* maximum ticks */
+	unsigned long resolution;	/* average resolution */
+	unsigned char reserved[64];	/* reserved */
+};
+
+#define SNDRV_TIMER_PSFLG_AUTO		(1<<0)	/* supports auto start */
+
+struct sndrv_timer_params {
+	unsigned int flags;		/* flags - SNDRV_MIXER_PSFLG_* */
+	unsigned int ticks;		/* requested resolution in ticks */
+	unsigned int queue_size;	/* total size of queue (32-1024) */
+	unsigned int reserved0;		/* reserved, was: failure locations */
+	unsigned char reserved[64];	/* reserved */
+};
+
+struct sndrv_timer_status {
+	struct timeval tstamp;		/* Timestamp */
+	unsigned int resolution;	/* current resolution */
+	unsigned int lost;		/* counter of master tick lost */
+	unsigned int overrun;		/* count of read queue overruns */
+	unsigned int queue;		/* used queue size */
+	unsigned char reserved[64];	/* reserved */
+};
+
+enum {
+	SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int),
+	SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct sndrv_timer_id),
+	SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct sndrv_timer_select),
+	SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info),
+	SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params),
+	SNDRV_TIMER_IOCTL_STATUS = _IOW('T', 0x14, struct sndrv_timer_status),
+	SNDRV_TIMER_IOCTL_START = _IO('T', 0x20),
+	SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21),
+	SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22),
+};
+
+struct sndrv_timer_read {
+	unsigned int resolution;
+	unsigned int ticks;
+};
+
+/****************************************************************************
+ *                                                                          *
+ *        Section for driver control interface - /dev/snd/control?          *
+ *                                                                          *
+ ****************************************************************************/
+
+#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 0)
+
+struct sndrv_ctl_card_info {
+	int card;			/* card number */
+	int pad;			/* reserved for future (was type) */
+	unsigned char id[16];		/* ID of card (user selectable) */
+	unsigned char driver[16];	/* Driver name */
+	unsigned char name[32];		/* Short name of soundcard */
+	unsigned char longname[80];	/* name + info text about soundcard */
+	unsigned char reserved_[16];	/* reserved for future (was ID of mixer) */
+	unsigned char mixername[80];	/* visual mixer identification */
+	unsigned char components[80];	/* card components / fine identification, delimited with one space (AC97 etc..) */
+	unsigned char reserved[48];	/* reserved for future */
+};
+
+enum sndrv_ctl_elem_type {
+	SNDRV_CTL_ELEM_TYPE_NONE = 0,		/* invalid */
+	SNDRV_CTL_ELEM_TYPE_BOOLEAN,		/* boolean type */
+	SNDRV_CTL_ELEM_TYPE_INTEGER,		/* integer type */
+	SNDRV_CTL_ELEM_TYPE_ENUMERATED,		/* enumerated type */
+	SNDRV_CTL_ELEM_TYPE_BYTES,		/* byte array */
+	SNDRV_CTL_ELEM_TYPE_IEC958,		/* IEC958 (S/PDIF) setup */
+	SNDRV_CTL_ELEM_TYPE_LAST = SNDRV_CTL_ELEM_TYPE_IEC958,
+};
+
+enum sndrv_ctl_elem_iface {
+	SNDRV_CTL_ELEM_IFACE_CARD = 0,		/* global control */
+	SNDRV_CTL_ELEM_IFACE_HWDEP,		/* hardware dependent device */
+	SNDRV_CTL_ELEM_IFACE_MIXER,		/* virtual mixer device */
+	SNDRV_CTL_ELEM_IFACE_PCM,		/* PCM device */
+	SNDRV_CTL_ELEM_IFACE_RAWMIDI,		/* RawMidi device */
+	SNDRV_CTL_ELEM_IFACE_TIMER,		/* timer device */
+	SNDRV_CTL_ELEM_IFACE_SEQUENCER,		/* sequencer client */
+	SNDRV_CTL_ELEM_IFACE_LAST = SNDRV_CTL_ELEM_IFACE_SEQUENCER,
+};
+
+#define SNDRV_CTL_ELEM_ACCESS_READ		(1<<0)
+#define SNDRV_CTL_ELEM_ACCESS_WRITE		(1<<1)
+#define SNDRV_CTL_ELEM_ACCESS_READWRITE		(SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
+#define SNDRV_CTL_ELEM_ACCESS_VOLATILE		(1<<2)	/* control value may be changed without a notification */
+#define SNDRV_CTL_ELEM_ACCESS_INACTIVE		(1<<8)	/* control does actually nothing, but may be updated */
+#define SNDRV_CTL_ELEM_ACCESS_LOCK		(1<<9)	/* write lock */
+#define SNDRV_CTL_ELEM_ACCESS_OWNER		(1<<10)	/* write lock owner */
+#define SNDRV_CTL_ELEM_ACCESS_INDIRECT		(1<<31)	/* indirect access */
+
+/* for further details see the ACPI and PCI power management specification */
+#define SNDRV_CTL_POWER_D0		0x0000	/* full On */
+#define SNDRV_CTL_POWER_D1		0x0100	/* partial On */
+#define SNDRV_CTL_POWER_D2		0x0200	/* partial On */
+#define SNDRV_CTL_POWER_D3		0x0300	/* Off */
+#define SNDRV_CTL_POWER_D3hot		(SNDRV_CTL_POWER_D3|0x0000)	/* Off, with power */
+#define SNDRV_CTL_POWER_D3cold		(SNDRV_CTL_POWER_D3|0x0001)	/* Off, without power */
+
+struct sndrv_ctl_elem_id {
+	unsigned int numid;		/* numeric identifier, zero = invalid */
+	enum sndrv_ctl_elem_iface iface; /* interface identifier */
+	unsigned int device;		/* device/client number */
+	unsigned int subdevice;		/* subdevice (substream) number */
+        unsigned char name[44];		/* ASCII name of item */
+	unsigned int index;		/* index of item */
+};
+
+struct sndrv_ctl_elem_list {
+	unsigned int offset;		/* W: first element ID to get */
+	unsigned int space;		/* W: count of element IDs to get */
+	unsigned int used;		/* R: count of element IDs set */
+	unsigned int count;		/* R: count of all elements */
+	struct sndrv_ctl_elem_id *pids; /* R: IDs */
+	unsigned char reserved[50];
+};
+
+struct sndrv_ctl_elem_info {
+	struct sndrv_ctl_elem_id id;	/* W: element ID */
+	enum sndrv_ctl_elem_type type;	/* R: value type - SNDRV_CTL_ELEM_TYPE_* */
+	unsigned int access;		/* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */
+	unsigned int count;		/* count of values */
+	pid_t owner;			/* owner's PID of this control */
+	union {
+		struct {
+			long min;		/* R: minimum value */
+			long max;		/* R: maximum value */
+			long step;		/* R: step (0 variable) */
+		} integer;
+		struct {
+			unsigned int items;	/* R: number of items */
+			unsigned int item;	/* W: item number */
+			char name[64];		/* R: value name */
+		} enumerated;
+		unsigned char reserved[128];
+	} value;
+	unsigned char reserved[64];
+};
+
+struct sndrv_ctl_elem_value {
+	struct sndrv_ctl_elem_id id;	/* W: element ID */
+	unsigned int indirect: 1;	/* W: use indirect pointer (xxx_ptr member) */
+        union {
+		union {
+			long value[128];
+			long *value_ptr;
+		} integer;
+		union {
+			unsigned int item[128];
+			unsigned int *item_ptr;
+		} enumerated;
+		union {
+			unsigned char data[512];
+			unsigned char *data_ptr;
+		} bytes;
+		struct sndrv_aes_iec958 iec958;
+        } value;                /* RO */
+        unsigned char reserved[128];
+};
+
+enum {
+	SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int),
+	SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info),
+	SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct sndrv_ctl_elem_list),
+	SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct sndrv_ctl_elem_info),
+	SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct sndrv_ctl_elem_value),
+	SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct sndrv_ctl_elem_value),
+	SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct sndrv_ctl_elem_id),
+	SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct sndrv_ctl_elem_id),
+	SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int),
+	SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int),
+	SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info),
+	SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int),
+	SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct sndrv_pcm_info),
+	SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int),
+	SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int),
+	SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct sndrv_rawmidi_info),
+	SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int),
+	SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int),
+	SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int),
+};
+
+/*
+ *  Read interface.
+ */
+
+enum sndrv_ctl_event_type {
+	SNDRV_CTL_EVENT_ELEM = 0,
+	SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM,
+};
+
+#define SNDRV_CTL_EVENT_MASK_VALUE	(1<<0)	/* element value was changed */
+#define SNDRV_CTL_EVENT_MASK_INFO	(1<<1)	/* element info was changed */
+#define SNDRV_CTL_EVENT_MASK_ADD	(1<<2)	/* element was added */
+#define SNDRV_CTL_EVENT_MASK_REMOVE	(~0U)	/* element was removed */
+
+struct sndrv_ctl_event {
+	enum sndrv_ctl_event_type type;	/* event type - SNDRV_CTL_EVENT_* */
+	union {
+		struct {
+			unsigned int mask;
+			struct sndrv_ctl_elem_id id;
+		} elem;
+                unsigned char data8[60];
+        } data;
+};
+
+/*
+ *  Control names
+ */
+
+#define SNDRV_CTL_NAME_NONE				""
+#define SNDRV_CTL_NAME_PLAYBACK				"Playback "
+#define SNDRV_CTL_NAME_CAPTURE				"Capture "
+
+#define SNDRV_CTL_NAME_IEC958_NONE			""
+#define SNDRV_CTL_NAME_IEC958_SWITCH			"Switch"
+#define SNDRV_CTL_NAME_IEC958_VOLUME			"Volume"
+#define SNDRV_CTL_NAME_IEC958_DEFAULT			"Default"
+#define SNDRV_CTL_NAME_IEC958_MASK			"Mask"
+#define SNDRV_CTL_NAME_IEC958_CON_MASK			"Con Mask"
+#define SNDRV_CTL_NAME_IEC958_PRO_MASK			"Pro Mask"
+#define SNDRV_CTL_NAME_IEC958_PCM_STREAM		"PCM Stream"
+#define SNDRV_CTL_NAME_IEC958(expl,direction,what)	"IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what
+
+/*
+ *
+ */
+
+struct sndrv_xferv {
+	const struct iovec *vector;
+	unsigned long count;
+};
+
+enum {
+	SNDRV_IOCTL_READV = _IOW('K', 0x00, struct sndrv_xferv),
+	SNDRV_IOCTL_WRITEV = _IOW('K', 0x01, struct sndrv_xferv),
+};
+
+#endif /* __SOUND_ASOUND_H */
diff -Nru linux/include/sound/asound_fm.h linux-2.4.19-pre5-mjc/include/sound/asound_fm.h
--- linux/include/sound/asound_fm.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/asound_fm.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,119 @@
+#ifndef __SOUND_ASOUND_FM_H
+#define __SOUND_ASOUND_FM_H
+
+/*
+ *  Advanced Linux Sound Architecture - ALSA
+ *
+ *  Interface file between ALSA driver & user space
+ *  Copyright (c) 1994-98 by Jaroslav Kysela <perex@suse.cz>,
+ *                           4Front Technologies
+ *
+ *  Direct FM control
+ *
+ *   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
+ *
+ */
+
+#define SNDRV_DM_FM_MODE_OPL2	0x00
+#define SNDRV_DM_FM_MODE_OPL3	0x01
+
+typedef struct snd_dm_fm_info {
+	unsigned char fm_mode;		/* OPL mode, see SNDRV_DM_FM_MODE_XXX */
+	unsigned char rhythm;		/* percussion mode flag */
+} snd_dm_fm_info_t;
+
+/*
+ *  Data structure composing an FM "note" or sound event.
+ */
+
+typedef struct snd_dm_fm_voice {
+	unsigned char op;		/* operator cell (0 or 1) */
+	unsigned char voice;		/* FM voice (0 to 17) */
+
+	unsigned char am;		/* amplitude modulation */
+	unsigned char vibrato;		/* vibrato effect */
+	unsigned char do_sustain;	/* sustain phase */
+	unsigned char kbd_scale;	/* keyboard scaling */
+	unsigned char harmonic;		/* 4 bits: harmonic and multiplier */
+	unsigned char scale_level;	/* 2 bits: decrease output freq rises */
+	unsigned char volume;		/* 6 bits: volume */
+
+	unsigned char attack;		/* 4 bits: attack rate */
+	unsigned char decay;		/* 4 bits: decay rate */
+	unsigned char sustain;		/* 4 bits: sustain level */
+	unsigned char release;		/* 4 bits: release rate */
+
+	unsigned char feedback;		/* 3 bits: feedback for op0 */
+	unsigned char connection;	/* 0 for serial, 1 for parallel */
+	unsigned char left;		/* stereo left */
+	unsigned char right;		/* stereo right */
+	unsigned char waveform;		/* 3 bits: waveform shape */
+} snd_dm_fm_voice_t;
+
+/*
+ *  This describes an FM note by its voice, octave, frequency number (10bit)
+ *  and key on/off.
+ */
+
+typedef struct snd_dm_fm_note {
+	unsigned char voice;	/* 0-17 voice channel */
+	unsigned char octave;	/* 3 bits: what octave to play */
+	unsigned int fnum;	/* 10 bits: frequency number */
+	unsigned char key_on;	/* set for active, clear for silent */
+} snd_dm_fm_note_t;
+
+/*
+ *  FM parameters that apply globally to all voices, and thus are not "notes"
+ */
+
+typedef struct snd_dm_fm_params {
+	unsigned char am_depth;		/* amplitude modulation depth (1=hi) */
+	unsigned char vib_depth;	/* vibrato depth (1=hi) */
+	unsigned char kbd_split;	/* keyboard split */
+	unsigned char rhythm;		/* percussion mode select */
+
+	/* This block is the percussion instrument data */
+	unsigned char bass;
+	unsigned char snare;
+	unsigned char tomtom;
+	unsigned char cymbal;
+	unsigned char hihat;
+} snd_dm_fm_params_t;
+
+/*
+ *  FM mode ioctl settings
+ */
+
+#define SNDRV_DM_FM_IOCTL_INFO		_IOR('H', 0x20, snd_dm_fm_info_t)
+#define SNDRV_DM_FM_IOCTL_RESET		_IO ('H', 0x21)
+#define SNDRV_DM_FM_IOCTL_PLAY_NOTE	_IOW('H', 0x22, snd_dm_fm_note_t)
+#define SNDRV_DM_FM_IOCTL_SET_VOICE	_IOW('H', 0x23, snd_dm_fm_voice_t)
+#define SNDRV_DM_FM_IOCTL_SET_PARAMS	_IOW('H', 0x24, snd_dm_fm_params_t)
+#define SNDRV_DM_FM_IOCTL_SET_MODE	_IOW('H', 0x25, int)
+/* for OPL3 only */
+#define SNDRV_DM_FM_IOCTL_SET_CONNECTION	_IOW('H', 0x26, int)
+
+#ifdef __SND_OSS_COMPAT__
+
+#define SNDRV_DM_FM_OSS_IOCTL_RESET		0x20
+#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE		0x21
+#define SNDRV_DM_FM_OSS_IOCTL_SET_VOICE		0x22
+#define SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS	0x23
+#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE		0x24
+#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL		0x25
+
+#endif
+
+#endif /* __SOUND_ASOUND_FM_H */
diff -Nru linux/include/sound/asoundef.h linux-2.4.19-pre5-mjc/include/sound/asoundef.h
--- linux/include/sound/asoundef.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/asoundef.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,227 @@
+#ifndef __SOUND_ASOUNDEF_H
+#define __SOUND_ASOUNDEF_H
+
+/*
+ *  Advanced Linux Sound Architecture - ALSA - Driver
+ *  Copyright (c) 1994-2000 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+/****************************************************************************
+ *                                                                          *
+ *        Digital audio interface					    *
+ *                                                                          *
+ ****************************************************************************/
+
+/* AES/IEC958 channel status bits */
+#define IEC958_AES0_PROFESSIONAL	(1<<0)	/* 0 = consumer, 1 = professional */
+#define IEC958_AES0_NONAUDIO		(1<<1)	/* 0 = audio, 1 = non-audio */
+#define IEC958_AES0_PRO_EMPHASIS	(7<<2)	/* mask - emphasis */
+#define IEC958_AES0_PRO_EMPHASIS_NOTID	(0<<2)	/* emphasis not indicated */
+#define IEC958_AES0_PRO_EMPHASIS_NONE	(1<<2)	/* none emphasis */
+#define IEC958_AES0_PRO_EMPHASIS_5015	(3<<2)	/* 50/15us emphasis */
+#define IEC958_AES0_PRO_EMPHASIS_CCITT	(7<<2)	/* CCITT J.17 emphasis */
+#define IEC958_AES0_PRO_FREQ_UNLOCKED	(1<<5)	/* source sample frequency: 0 = locked, 1 = unlocked */
+#define IEC958_AES0_PRO_FS		(3<<6)	/* mask - sample frequency */
+#define IEC958_AES0_PRO_FS_NOTID	(0<<6)	/* fs not indicated */
+#define IEC958_AES0_PRO_FS_44100	(1<<6)	/* 44.1kHz */
+#define IEC958_AES0_PRO_FS_48000	(2<<6)	/* 48kHz */
+#define IEC958_AES0_PRO_FS_32000	(3<<6)	/* 32kHz */
+#define IEC958_AES0_CON_NOT_COPYRIGHT	(1<<2)	/* 0 = copyright, 1 = not copyright */
+#define IEC958_AES0_CON_EMPHASIS	(7<<3)	/* mask - emphasis */
+#define IEC958_AES0_CON_EMPHASIS_NONE	(0<<3)	/* none emphasis */
+#define IEC958_AES0_CON_EMPHASIS_5015	(1<<3)	/* 50/15us emphasis */
+#define IEC958_AES0_CON_MODE		(3<<6)	/* mask - mode */
+#define IEC958_AES1_PRO_MODE		(15<<0)	/* mask - channel mode */
+#define IEC958_AES1_PRO_MODE_NOTID	(0<<0)	/* not indicated */
+#define IEC958_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */
+#define IEC958_AES1_PRO_MODE_SINGLE	(4<<0)	/* single channel */
+#define IEC958_AES1_PRO_MODE_TWO	(8<<0)	/* two channels */
+#define IEC958_AES1_PRO_MODE_PRIMARY	(12<<0)	/* primary/secondary */
+#define IEC958_AES1_PRO_MODE_BYTE3	(15<<0)	/* vector to byte 3 */
+#define IEC958_AES1_PRO_USERBITS	(15<<4)	/* mask - user bits */
+#define IEC958_AES1_PRO_USERBITS_NOTID	(0<<4)	/* not indicated */
+#define IEC958_AES1_PRO_USERBITS_192	(8<<4)	/* 192-bit structure */
+#define IEC958_AES1_PRO_USERBITS_UDEF	(12<<4)	/* user defined application */
+#define IEC958_AES1_CON_CATEGORY	0x7f
+#define IEC958_AES1_CON_GENERAL		0x00
+#define IEC958_AES1_CON_EXPERIMENTAL	0x40
+#define IEC958_AES1_CON_SOLIDMEM_MASK	0x0f
+#define IEC958_AES1_CON_SOLIDMEM_ID	0x08
+#define IEC958_AES1_CON_BROADCAST1_MASK 0x07
+#define IEC958_AES1_CON_BROADCAST1_ID	0x04
+#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07
+#define IEC958_AES1_CON_DIGDIGCONV_ID	0x02
+#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f
+#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x06
+#define IEC958_AES1_CON_ADC_MASK	0x1f
+#define IEC958_AES1_CON_ADC_ID		0x16
+#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f
+#define IEC958_AES1_CON_BROADCAST2_ID	0x0e
+#define IEC958_AES1_CON_LASEROPT_MASK	0x07
+#define IEC958_AES1_CON_LASEROPT_ID	0x01
+#define IEC958_AES1_CON_MUSICAL_MASK	0x07
+#define IEC958_AES1_CON_MUSICAL_ID	0x05
+#define IEC958_AES1_CON_MAGNETIC_MASK	0x07
+#define IEC958_AES1_CON_MAGNETIC_ID	0x03
+#define IEC958_AES1_CON_IEC908_CD	(IEC958_AES1_CON_LASEROPT_ID|0x00)
+#define IEC958_AES1_CON_NON_IEC908_CD	(IEC958_AES1_CON_LASEROPT_ID|0x08)
+#define IEC958_AES1_CON_PCM_CODER	(IEC958_AES1_CON_DIGDIGCONV_ID|0x00)
+#define IEC958_AES1_CON_SAMPLER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x20)
+#define IEC958_AES1_CON_MIXER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x10)
+#define IEC958_AES1_CON_RATE_CONVERTER	(IEC958_AES1_CON_DIGDIGCONV_ID|0x18)
+#define IEC958_AES1_CON_SYNTHESIZER	(IEC958_AES1_CON_MUSICAL_ID|0x00)
+#define IEC958_AES1_CON_MICROPHONE	(IEC958_AES1_CON_MUSICAL_ID|0x08)
+#define IEC958_AES1_CON_DAT		(IEC958_AES1_CON_MAGNETIC_ID|0x00)
+#define IEC958_AES1_CON_VCR		(IEC958_AES1_CON_MAGNETIC_ID|0x08)
+#define IEC958_AES1_CON_ORIGINAL	(1<<7)	/* this bits depends on the category code */
+#define IEC958_AES2_PRO_SBITS		(7<<0)	/* mask - sample bits */
+#define IEC958_AES2_PRO_SBITS_20	(2<<0)	/* 20-bit - coordination */
+#define IEC958_AES2_PRO_SBITS_24	(4<<0)	/* 24-bit - main audio */
+#define IEC958_AES2_PRO_SBITS_UDEF	(6<<0)	/* user defined application */
+#define IEC958_AES2_PRO_WORDLEN		(7<<3)	/* mask - source word length */
+#define IEC958_AES2_PRO_WORDLEN_NOTID	(0<<3)	/* not indicated */
+#define IEC958_AES2_PRO_WORDLEN_22_18	(2<<3)	/* 22-bit or 18-bit */
+#define IEC958_AES2_PRO_WORDLEN_23_19	(4<<3)	/* 23-bit or 19-bit */
+#define IEC958_AES2_PRO_WORDLEN_24_20	(5<<3)	/* 24-bit or 20-bit */
+#define IEC958_AES2_PRO_WORDLEN_20_16	(6<<3)	/* 20-bit or 16-bit */
+#define IEC958_AES2_CON_SOURCE		(15<<0)	/* mask - source number */
+#define IEC958_AES2_CON_SOURCE_UNSPEC	(0<<0)	/* unspecified */
+#define IEC958_AES2_CON_CHANNEL		(15<<4)	/* mask - channel number */
+#define IEC958_AES2_CON_CHANNEL_UNSPEC	(0<<4)	/* unspecified */
+#define IEC958_AES3_CON_FS		(15<<0)	/* mask - sample frequency */
+#define IEC958_AES3_CON_FS_44100	(0<<0)	/* 44.1kHz */
+#define IEC958_AES3_CON_FS_48000	(2<<0)	/* 48kHz */
+#define IEC958_AES3_CON_FS_32000	(3<<0)	/* 32kHz */
+#define IEC958_AES3_CON_CLOCK		(3<<4)	/* mask - clock accuracy */
+#define IEC958_AES3_CON_CLOCK_1000PPM	(0<<4)	/* 1000 ppm */
+#define IEC958_AES3_CON_CLOCK_50PPM	(1<<4)	/* 50 ppm */
+#define IEC958_AES3_CON_CLOCK_VARIABLE	(2<<4)	/* variable pitch */
+
+/*****************************************************************************
+ *                                                                           *
+ *                            MIDI v1.0 interface                            *
+ *                                                                           *
+ *****************************************************************************/
+
+#define MIDI_CHANNELS			16
+#define MIDI_GM_DRUM_CHANNEL		(10-1)
+
+/*
+ *  MIDI commands
+ */
+
+#define MIDI_CMD_NOTE_OFF		0x80
+#define MIDI_CMD_NOTE_ON		0x90
+#define MIDI_CMD_NOTE_PRESSURE		0xa0
+#define MIDI_CMD_CONTROL		0xb0
+#define MIDI_CMD_PGM_CHANGE		0xc0
+#define MIDI_CMD_CHANNEL_PRESSURE	0xd0
+#define MIDI_CMD_BENDER			0xe0
+
+#define MIDI_CMD_COMMON_SYSEX		0xf0
+#define MIDI_CMD_COMMON_MTC_QUARTER	0xf1
+#define MIDI_CMD_COMMON_SONG_POS	0xf2
+#define MIDI_CMD_COMMON_SONG_SELECT	0xf3
+#define MIDI_CMD_COMMON_TUNE_REQUEST	0xf6
+#define MIDI_CMD_COMMON_SYSEX_END	0xf7
+#define MIDI_CMD_COMMON_CLOCK		0xf8
+#define MIDI_CMD_COMMON_START		0xfa
+#define MIDI_CMD_COMMON_CONTINUE	0xfb
+#define MIDI_CMD_COMMON_STOP		0xfc
+#define MIDI_CMD_COMMON_SENSING		0xfe
+#define MIDI_CMD_COMMON_RESET		0xff
+
+/*
+ *  MIDI controllers
+ */
+
+#define MIDI_CTL_MSB_BANK		0x00
+#define MIDI_CTL_MSB_MODWHEEL         	0x01
+#define MIDI_CTL_MSB_BREATH           	0x02
+#define MIDI_CTL_MSB_FOOT             	0x04
+#define MIDI_CTL_MSB_PORTNAMENTO_TIME 	0x05
+#define MIDI_CTL_MSB_DATA_ENTRY		0x06
+#define MIDI_CTL_MSB_MAIN_VOLUME      	0x07
+#define MIDI_CTL_MSB_BALANCE          	0x08
+#define MIDI_CTL_MSB_PAN              	0x0a
+#define MIDI_CTL_MSB_EXPRESSION       	0x0b
+#define MIDI_CTL_MSB_EFFECT1		0x0c
+#define MIDI_CTL_MSB_EFFECT2		0x0d
+#define MIDI_CTL_MSB_GENERAL_PURPOSE1 	0x10
+#define MIDI_CTL_MSB_GENERAL_PURPOSE2 	0x11
+#define MIDI_CTL_MSB_GENERAL_PURPOSE3 	0x12
+#define MIDI_CTL_MSB_GENERAL_PURPOSE4 	0x13
+#define MIDI_CTL_LSB_BANK		0x20
+#define MIDI_CTL_LSB_MODWHEEL        	0x21
+#define MIDI_CTL_LSB_BREATH           	0x22
+#define MIDI_CTL_LSB_FOOT             	0x24
+#define MIDI_CTL_LSB_PORTNAMENTO_TIME 	0x25
+#define MIDI_CTL_LSB_DATA_ENTRY		0x26
+#define MIDI_CTL_LSB_MAIN_VOLUME      	0x27
+#define MIDI_CTL_LSB_BALANCE          	0x28
+#define MIDI_CTL_LSB_PAN              	0x2a
+#define MIDI_CTL_LSB_EXPRESSION       	0x2b
+#define MIDI_CTL_LSB_EFFECT1		0x2c
+#define MIDI_CTL_LSB_EFFECT2		0x2d
+#define MIDI_CTL_LSB_GENERAL_PURPOSE1 	0x30
+#define MIDI_CTL_LSB_GENERAL_PURPOSE2 	0x31
+#define MIDI_CTL_LSB_GENERAL_PURPOSE3 	0x32
+#define MIDI_CTL_LSB_GENERAL_PURPOSE4 	0x33
+#define MIDI_CTL_SUSTAIN              	0x40
+#define MIDI_CTL_PORTAMENTO           	0x41
+#define MIDI_CTL_SUSTENUTO            	0x42
+#define MIDI_CTL_SOFT_PEDAL           	0x43
+#define MIDI_CTL_LEGATO_FOOTSWITCH	0x44
+#define MIDI_CTL_HOLD2                	0x45
+#define MIDI_CTL_SC1_SOUND_VARIATION	0x46
+#define MIDI_CTL_SC2_TIMBRE		0x47
+#define MIDI_CTL_SC3_RELEASE_TIME	0x48
+#define MIDI_CTL_SC4_ATTACK_TIME	0x49
+#define MIDI_CTL_SC5_BRIGHTNESS		0x4a
+#define MIDI_CTL_SC6			0x4b
+#define MIDI_CTL_SC7			0x4c
+#define MIDI_CTL_SC8			0x4d
+#define MIDI_CTL_SC9			0x4e
+#define MIDI_CTL_SC10			0x4f
+#define MIDI_CTL_GENERAL_PURPOSE5     	0x50
+#define MIDI_CTL_GENERAL_PURPOSE6     	0x51
+#define MIDI_CTL_GENERAL_PURPOSE7     	0x52
+#define MIDI_CTL_GENERAL_PURPOSE8     	0x53
+#define MIDI_CTL_PORNAMENTO_CONTROL	0x54
+#define MIDI_CTL_E1_REVERB_DEPTH	0x5b
+#define MIDI_CTL_E2_TREMOLO_DEPTH	0x5c
+#define MIDI_CTL_E3_CHORUS_DEPTH	0x5d
+#define MIDI_CTL_E4_DETUNE_DEPTH	0x5e
+#define MIDI_CTL_E5_PHASER_DEPTH	0x5f
+#define MIDI_CTL_DATA_INCREMENT       	0x60
+#define MIDI_CTL_DATA_DECREMENT       	0x61
+#define MIDI_CTL_NONREG_PARM_NUM_LSB  	0x62
+#define MIDI_CTL_NONREG_PARM_NUM_MSB  	0x63
+#define MIDI_CTL_REGIST_PARM_NUM_LSB  	0x64
+#define MIDI_CTL_REGIST_PARM_NUM_MSB	0x65
+#define MIDI_CTL_ALL_SOUNDS_OFF		0x78
+#define MIDI_CTL_RESET_CONTROLLERS	0x79
+#define MIDI_CTL_LOCAL_CONTROL_SWITCH	0x7a
+#define MIDI_CTL_ALL_NOTES_OFF		0x7b
+#define MIDI_CTL_OMNI_OFF		0x7c
+#define MIDI_CTL_OMNI_ON		0x7d
+#define MIDI_CTL_MONO1			0x7e
+#define MIDI_CTL_MONO2			0x7f
+
+#endif /* __SOUND_ASOUNDEF_H */
diff -Nru linux/include/sound/control.h linux-2.4.19-pre5-mjc/include/sound/control.h
--- linux/include/sound/control.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/control.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,118 @@
+#ifndef __SOUND_CONTROL_H
+#define __SOUND_CONTROL_H
+
+/*
+ *  Header file for control interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/asound.h>
+
+typedef struct sndrv_aes_iec958 snd_aes_iec958_t;
+typedef struct sndrv_ctl_card_info snd_ctl_card_info_t;
+typedef enum sndrv_ctl_elem_type snd_ctl_elem_type_t;
+typedef enum sndrv_ctl_elem_iface snd_ctl_elem_iface_t;
+typedef struct sndrv_ctl_elem_id snd_ctl_elem_id_t;
+typedef struct sndrv_ctl_elem_list snd_ctl_elem_list_t;
+typedef struct sndrv_ctl_elem_info snd_ctl_elem_info_t;
+typedef struct sndrv_ctl_elem_value snd_ctl_elem_value_t;
+typedef enum sndrv_ctl_event_type snd_ctl_event_type_t;
+typedef struct sndrv_ctl_event snd_ctl_event_t;
+
+#define _snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data)
+#define snd_kcontrol_chip(kcontrol) snd_magic_cast1(chip_t, _snd_kcontrol_chip(kcontrol), return -ENXIO)
+
+typedef int (snd_kcontrol_info_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo);
+typedef int (snd_kcontrol_get_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+typedef int (snd_kcontrol_put_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+typedef struct _snd_kcontrol_new {
+	snd_ctl_elem_iface_t iface;	/* interface identifier */
+	unsigned int device;		/* device/client number */
+	unsigned int subdevice;		/* subdevice (substream) number */
+	unsigned char *name;		/* ASCII name of item */
+	unsigned int index;		/* index of item */
+	unsigned int access;		/* access rights */
+	snd_kcontrol_info_t *info;
+	snd_kcontrol_get_t *get;
+	snd_kcontrol_put_t *put;
+	unsigned long private_value;
+} snd_kcontrol_new_t;
+
+struct _snd_kcontrol {
+	struct list_head list;		/* list of controls */
+	snd_ctl_elem_id_t id;
+	snd_ctl_file_t *owner;		/* locked */
+	pid_t owner_pid;
+	unsigned int access;		/* access rights */
+	snd_kcontrol_info_t *info;
+	snd_kcontrol_get_t *get;
+	snd_kcontrol_put_t *put;
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_kcontrol_t *kcontrol);
+};
+
+#define snd_kcontrol(n) list_entry(n, snd_kcontrol_t, list)
+
+typedef struct _snd_kctl_event {
+	struct list_head list;	/* list of events */
+	snd_ctl_elem_id_t id;
+	unsigned int mask;
+} snd_kctl_event_t;
+
+#define snd_kctl_event(n) list_entry(n, snd_kctl_event_t, list)
+
+struct _snd_ctl_file {
+	struct list_head list;		/* list of all control files */
+	snd_card_t *card;
+	pid_t pid;
+	int prefer_pcm_subdevice;
+	int prefer_rawmidi_subdevice;
+	wait_queue_head_t change_sleep;
+	spinlock_t read_lock;
+	struct fasync_struct *fasync;
+	int subscribed;			/* read interface is activated */
+	struct list_head events;	/* waiting events for read */
+};
+
+#define snd_ctl_file(n) list_entry(n, snd_ctl_file_t, list)
+
+typedef int (*snd_kctl_ioctl_func_t) (snd_card_t * card,
+				 snd_ctl_file_t * control,
+				 unsigned int cmd, unsigned long arg);
+
+void snd_ctl_notify(snd_card_t * card, unsigned int mask, snd_ctl_elem_id_t * id);
+
+snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * kcontrol);
+snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * kcontrolnew, void * private_data);
+void snd_ctl_free_one(snd_kcontrol_t * kcontrol);
+int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol);
+int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol);
+int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id);
+int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id);
+snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid);
+snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id);
+
+int snd_ctl_register(snd_card_t *card);
+int snd_ctl_unregister(snd_card_t *card);
+int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn);
+int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn);
+
+#endif	/* __SOUND_CONTROL_H */
diff -Nru linux/include/sound/core.h linux-2.4.19-pre5-mjc/include/sound/core.h
--- linux/include/sound/core.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/core.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,356 @@
+#ifndef __SOUND_CORE_H
+#define __SOUND_CORE_H
+
+/*
+ *  Main header file for the ALSA driver
+ *  Copyright (c) 1994-2001 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef CONFIG_PM
+#include <linux/sched.h>		/* wake_up() and struct semaphore */
+#endif
+
+/* Typedef's */
+typedef struct timeval snd_timestamp_t;
+typedef struct sndrv_interval snd_interval_t;
+typedef enum sndrv_card_type snd_card_type;
+typedef struct sndrv_xferi snd_xferi_t;
+typedef struct sndrv_xfern snd_xfern_t;
+typedef struct sndrv_xferv snd_xferv_t;
+
+/* device allocation stuff */
+
+#define SNDRV_DEV_TYPE_RANGE_SIZE		0x1000
+
+typedef enum {
+	SNDRV_DEV_TOPLEVEL =		(0*SNDRV_DEV_TYPE_RANGE_SIZE),
+	SNDRV_DEV_LOWLEVEL_PRE,
+	SNDRV_DEV_LOWLEVEL_NORMAL =	(1*SNDRV_DEV_TYPE_RANGE_SIZE),
+	SNDRV_DEV_PCM,
+	SNDRV_DEV_RAWMIDI,
+	SNDRV_DEV_TIMER,
+	SNDRV_DEV_SEQUENCER,
+	SNDRV_DEV_HWDEP,
+	SNDRV_DEV_LOWLEVEL =		(2*SNDRV_DEV_TYPE_RANGE_SIZE)
+} snd_device_type_t;
+
+typedef enum {
+	SNDRV_DEV_BUILD = 0,
+	SNDRV_DEV_REGISTERED = 1
+} snd_device_state_t;
+
+typedef enum {
+	SNDRV_DEV_CMD_PRE = 0,
+	SNDRV_DEV_CMD_NORMAL = 1,
+	SNDRV_DEV_CMD_POST = 2
+} snd_device_cmd_t;
+
+typedef struct _snd_card snd_card_t;
+typedef struct _snd_device snd_device_t;
+
+typedef int (snd_dev_free_t)(snd_device_t *device);
+typedef int (snd_dev_register_t)(snd_device_t *device);
+typedef int (snd_dev_unregister_t)(snd_device_t *device);
+
+typedef struct {
+	snd_dev_free_t *dev_free;
+	snd_dev_register_t *dev_register;
+	snd_dev_unregister_t *dev_unregister;
+} snd_device_ops_t;
+
+struct _snd_device {
+	struct list_head list;		/* list of registered devices */
+	snd_card_t *card;		/* card which holds this device */
+	snd_device_state_t state;	/* state of the device */
+	snd_device_type_t type;		/* device type */
+	void *device_data;		/* device structure */
+	snd_device_ops_t *ops;		/* operations */
+};
+
+#define snd_device(n) list_entry(n, snd_device_t, list)
+
+/* various typedefs */
+
+typedef struct snd_info_entry snd_info_entry_t;
+typedef struct _snd_pcm snd_pcm_t;
+typedef struct _snd_pcm_str snd_pcm_str_t;
+typedef struct _snd_pcm_substream snd_pcm_substream_t;
+typedef struct _snd_mixer snd_kmixer_t;
+typedef struct _snd_rawmidi snd_rawmidi_t;
+typedef struct _snd_ctl_file snd_ctl_file_t;
+typedef struct _snd_kcontrol snd_kcontrol_t;
+typedef struct _snd_timer snd_timer_t;
+typedef struct _snd_timer_instance snd_timer_instance_t;
+typedef struct _snd_hwdep snd_hwdep_t;
+#ifdef CONFIG_SND_OSSEMUL
+typedef struct _snd_oss_mixer snd_mixer_oss_t;
+#endif
+
+/* main structure for soundcard */
+
+struct _snd_card {
+	int number;			/* number of soundcard (index to snd_cards) */
+
+	char id[16];			/* id string of this card */
+	char driver[16];		/* driver name */
+	char shortname[32];		/* short name of this soundcard */
+	char longname[80];		/* name of this soundcard */
+	char mixername[80];		/* mixer name */
+	char components[80];		/* card components delimited with space */
+
+	struct module *module;		/* top-level module */
+
+	void *private_data;		/* private data for soundcard */
+	void (*private_free) (snd_card_t *card); /* callback for freeing of private data */
+
+	struct list_head devices;	/* devices */
+
+	unsigned int last_numid;	/* last used numeric ID */
+	rwlock_t control_rwlock;	/* control list lock */
+	rwlock_t control_owner_lock;	/* control list lock */
+	int controls_count;		/* count of all controls */
+	struct list_head controls;	/* all controls for this card */
+	struct list_head ctl_files;	/* active control files */
+
+	snd_info_entry_t *proc_root;	/* root for soundcard specific files */
+	snd_info_entry_t *proc_id;	/* the card id */
+	struct proc_dir_entry *proc_root_link;	/* number link to real id */
+
+#ifdef CONFIG_PM
+	int (*set_power_state) (snd_card_t *card, unsigned int state);
+	void *power_state_private_data;
+	unsigned int power_state;	/* power state */
+	struct semaphore power_lock;	/* power lock */
+	wait_queue_head_t power_sleep;
+#endif
+
+#ifdef CONFIG_SND_OSSEMUL
+	snd_mixer_oss_t *mixer_oss;
+	int mixer_oss_change_count;
+#endif
+};
+
+#ifdef CONFIG_PM
+static inline void snd_power_lock(snd_card_t *card)
+{
+	down(&card->power_lock);
+}
+
+static inline void snd_power_unlock(snd_card_t *card)
+{
+	up(&card->power_lock);
+}
+
+void snd_power_wait(snd_card_t *card);
+
+static inline unsigned int snd_power_get_state(snd_card_t *card)
+{
+	return card->power_state;
+}
+
+static inline void snd_power_change_state(snd_card_t *card, unsigned int state)
+{
+	card->power_state = state;
+	wake_up(&card->power_sleep);
+}
+#else
+#define snd_power_lock(card) do { ; } while (0)
+#define snd_power_unlock(card) do { ; } while (0)
+#define snd_power_wait(card) do { ; } while (0)
+#define snd_power_get_state(card) SNDRV_CTL_POWER_D0
+#define snd_power_change_state(card, state) do { ; } while (0)
+#endif
+
+/* device.c */
+
+struct _snd_minor {
+	struct list_head list;		/* list of all minors per card */
+	int number;			/* minor number */
+	int device;			/* device number */
+	const char *comment;		/* for /proc/asound/devices */
+	snd_info_entry_t *dev;		/* for /proc/asound/dev */
+	struct file_operations *f_ops;	/* file operations */
+};
+
+typedef struct _snd_minor snd_minor_t;
+
+/* sound.c */
+
+extern int snd_ecards_limit;
+extern int snd_device_mode;
+extern int snd_device_gid;
+extern int snd_device_uid;
+
+void snd_request_card(int card);
+
+int snd_register_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name);
+int snd_unregister_device(int type, snd_card_t *card, int dev);
+
+#ifdef CONFIG_SND_OSSEMUL
+int snd_register_oss_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name);
+int snd_unregister_oss_device(int type, snd_card_t *card, int dev);
+#endif
+
+int snd_minor_info_init(void);
+int snd_minor_info_done(void);
+
+/* sound_oss.c */
+
+#ifdef CONFIG_SND_OSSEMUL
+
+int snd_minor_info_oss_init(void);
+int snd_minor_info_oss_done(void);
+
+int snd_oss_init_module(void);
+void snd_oss_cleanup_module(void);
+
+#endif
+
+/* memory.c */
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+void snd_memory_init(void);
+void snd_memory_done(void);
+int snd_memory_info_init(void);
+int snd_memory_info_done(void);
+void *snd_hidden_kmalloc(size_t size, int flags);
+void snd_hidden_kfree(const void *obj);
+void *snd_hidden_vmalloc(unsigned long size);
+void snd_hidden_vfree(void *obj);
+#define kmalloc(size, flags) snd_hidden_kmalloc(size, flags)
+#define kfree(obj) snd_hidden_kfree(obj)
+#define kfree_nocheck(obj) snd_wrapper_kfree(obj)
+#define vmalloc(size) snd_hidden_vmalloc(size)
+#define vfree(obj) snd_hidden_vfree(obj)
+#else
+#define kfree_nocheck(obj) kfree(obj)
+#endif
+void *snd_kcalloc(size_t size, int flags);
+char *snd_kmalloc_strdup(const char *string, int flags);
+void *snd_malloc_pages(unsigned long size, unsigned int dma_flags);
+void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size);
+void snd_free_pages(void *ptr, unsigned long size);
+#ifdef CONFIG_ISA
+void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr);
+void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size);
+#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size)
+#endif
+#ifdef CONFIG_PCI
+void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr);
+void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size);
+void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr);
+#endif
+int copy_to_user_fromio(void *dst, unsigned long src, size_t count);
+int copy_from_user_toio(unsigned long dst, const void *src, size_t count);
+
+/* init.c */
+
+extern int snd_cards_count;
+extern snd_card_t *snd_cards[SNDRV_CARDS];
+extern rwlock_t snd_card_rwlock;
+#ifdef CONFIG_SND_OSSEMUL
+extern int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag);
+#endif
+
+snd_card_t *snd_card_new(int idx, const char *id,
+			 struct module *module, int extra_size);
+int snd_card_free(snd_card_t *card);
+int snd_card_register(snd_card_t *card);
+int snd_card_info_init(void);
+int snd_card_info_done(void);
+int snd_component_add(snd_card_t *card, const char *component);
+
+/* device.c */
+
+int snd_device_new(snd_card_t *card, snd_device_type_t type,
+		   void *device_data, snd_device_ops_t *ops);
+int snd_device_register(snd_card_t *card, void *device_data);
+int snd_device_register_all(snd_card_t *card);
+int snd_device_free(snd_card_t *card, void *device_data);
+int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd);
+
+/* isadma.c */
+
+#define DMA_MODE_NO_ENABLE	0x0100
+
+void snd_dma_program(unsigned long dma, unsigned long addr, unsigned int size, unsigned short mode);
+void snd_dma_disable(unsigned long dma);
+unsigned int snd_dma_residue(unsigned long dma);
+
+/* misc.c */
+
+int snd_task_name(struct task_struct *task, char *name, size_t size);
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+int snd_verbose_printk(const char *file, int line, const char *format);
+#endif
+
+/* --- */
+
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+#define snd_printk(format, args...) do { \
+	printk(snd_verbose_printk(__FILE__, __LINE__, format) ? format + 3 : format, ##args); \
+} while (0)
+#else
+#define snd_printk(format, args...) do { \
+	printk(format, ##args); \
+} while (0)
+#endif
+
+#ifdef CONFIG_SND_DEBUG
+
+#define __ASTRING__(x) #x
+
+#define snd_printd(format, args...) snd_printk(format, ##args)
+#define snd_assert(expr, args...) do {\
+	if (!(expr)) {\
+		snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
+		args;\
+	}\
+} while (0)
+#define snd_runtime_check(expr, args...) do {\
+	if (!(expr)) {\
+		snd_printk("ERROR (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
+		args;\
+	}\
+} while (0)
+
+#else /* !CONFIG_SND_DEBUG */
+
+#define snd_printd(format, args...)	/* nothing */
+#define snd_assert(expr, args...)	/* nothing */
+#define snd_runtime_check(expr, args...) do { if (!(expr)) { args; } } while (0)
+
+#endif /* CONFIG_SND_DEBUG */
+
+#ifdef CONFIG_SND_DEBUG_DETECT
+#define snd_printdd(format, args...) snd_printk(format, ##args)
+#else
+#define snd_printdd(format, args...) /* nothing */
+#endif
+
+#define snd_BUG() snd_assert(0, )
+
+
+#define snd_timestamp_now(tstamp) do_gettimeofday(tstamp)
+#define snd_timestamp_zero(tstamp) do { (tstamp)->tv_sec = 0; (tstamp)->tv_usec = 0; } while (0)
+#define snd_timestamp_null(tstamp) ((tstamp)->tv_sec == 0 && (tstamp)->tv_usec ==0)
+
+#define SNDRV_OSS_VERSION         ((3<<16)|(8<<8)|(1<<4)|(0))	/* 3.8.1a */
+
+#endif /* __SOUND_CORE_H */
diff -Nru linux/include/sound/cs4231.h linux-2.4.19-pre5-mjc/include/sound/cs4231.h
--- linux/include/sound/cs4231.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/cs4231.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,324 @@
+#ifndef __SOUND_CS4231_H
+#define __SOUND_CS4231_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for CS4231 & InterWave chips & compatible chips
+ *
+ *
+ *   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
+ *
+ */
+
+#include "control.h"
+#include "pcm.h"
+#include "timer.h"
+
+/* IO ports */
+
+#define CS4231P(chip, x)	((chip)->port + c_d_c_CS4231##x)
+
+#define c_d_c_CS4231REGSEL	0
+#define c_d_c_CS4231REG		1
+#define c_d_c_CS4231STATUS	2
+#define c_d_c_CS4231PIO		3
+
+/* codec registers */
+
+#define CS4231_LEFT_INPUT	0x00	/* left input control */
+#define CS4231_RIGHT_INPUT	0x01	/* right input control */
+#define CS4231_AUX1_LEFT_INPUT	0x02	/* left AUX1 input control */
+#define CS4231_AUX1_RIGHT_INPUT	0x03	/* right AUX1 input control */
+#define CS4231_AUX2_LEFT_INPUT	0x04	/* left AUX2 input control */
+#define CS4231_AUX2_RIGHT_INPUT	0x05	/* right AUX2 input control */
+#define CS4231_LEFT_OUTPUT	0x06	/* left output control register */
+#define CS4231_RIGHT_OUTPUT	0x07	/* right output control register */
+#define CS4231_PLAYBK_FORMAT	0x08	/* clock and data format - playback - bits 7-0 MCE */
+#define CS4231_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
+#define CS4231_PIN_CTRL		0x0a	/* pin control */
+#define CS4231_TEST_INIT	0x0b	/* test and initialization */
+#define CS4231_MISC_INFO	0x0c	/* miscellaneaous information */
+#define CS4231_LOOPBACK		0x0d	/* loopback control */
+#define CS4231_PLY_UPR_CNT	0x0e	/* playback upper base count */
+#define CS4231_PLY_LWR_CNT	0x0f	/* playback lower base count */
+#define CS4231_ALT_FEATURE_1	0x10	/* alternate #1 feature enable */
+#define CS4231_ALT_FEATURE_2	0x11	/* alternate #2 feature enable */
+#define CS4231_LEFT_LINE_IN	0x12	/* left line input control */
+#define CS4231_RIGHT_LINE_IN	0x13	/* right line input control */
+#define CS4231_TIMER_LOW	0x14	/* timer low byte */
+#define CS4231_TIMER_HIGH	0x15	/* timer high byte */
+#define CS4231_LEFT_MIC_INPUT	0x16	/* left MIC input control register (InterWave only) */
+#define CS4231_RIGHT_MIC_INPUT	0x17	/* right MIC input control register (InterWave only) */
+#define CS4236_EXT_REG		0x17	/* extended register access */
+#define CS4231_IRQ_STATUS	0x18	/* irq status register */
+#define CS4231_LINE_LEFT_OUTPUT	0x19	/* left line output control register (InterWave only) */
+#define CS4231_VERSION		0x19	/* CS4231(A) - version values */
+#define CS4231_MONO_CTRL	0x1a	/* mono input/output control */
+#define CS4231_LINE_RIGHT_OUTPUT 0x1b	/* right line output control register (InterWave only) */
+#define CS4235_LEFT_MASTER	0x1b	/* left master output control */
+#define CS4231_REC_FORMAT	0x1c	/* clock and data format - record - bits 7-0 MCE */
+#define CS4231_PLY_VAR_FREQ	0x1d	/* playback variable frequency */
+#define CS4235_RIGHT_MASTER	0x1d	/* right master output control */
+#define CS4231_REC_UPR_CNT	0x1e	/* record upper count */
+#define CS4231_REC_LWR_CNT	0x1f	/* record lower count */
+
+/* definitions for codec register select port - CODECP( REGSEL ) */
+
+#define CS4231_INIT		0x80	/* CODEC is initializing */
+#define CS4231_MCE		0x40	/* mode change enable */
+#define CS4231_TRD		0x20	/* transfer request disable */
+
+/* definitions for codec status register - CODECP( STATUS ) */
+
+#define CS4231_GLOBALIRQ	0x01	/* IRQ is active */
+
+/* definitions for codec irq status */
+
+#define CS4231_PLAYBACK_IRQ	0x10
+#define CS4231_RECORD_IRQ	0x20
+#define CS4231_TIMER_IRQ	0x40
+#define CS4231_ALL_IRQS		0x70
+#define CS4231_REC_UNDERRUN	0x08
+#define CS4231_REC_OVERRUN	0x04
+#define CS4231_PLY_OVERRUN	0x02
+#define CS4231_PLY_UNDERRUN	0x01
+
+/* definitions for CS4231_LEFT_INPUT and CS4231_RIGHT_INPUT registers */
+
+#define CS4231_ENABLE_MIC_GAIN	0x20
+
+#define CS4231_MIXS_LINE	0x00
+#define CS4231_MIXS_AUX1	0x40
+#define CS4231_MIXS_MIC		0x80
+#define CS4231_MIXS_ALL		0xc0
+
+/* definitions for clock and data format register - CS4231_PLAYBK_FORMAT */
+
+#define CS4231_LINEAR_8		0x00	/* 8-bit unsigned data */
+#define CS4231_ALAW_8		0x60	/* 8-bit A-law companded */
+#define CS4231_ULAW_8		0x20	/* 8-bit U-law companded */
+#define CS4231_LINEAR_16	0x40	/* 16-bit twos complement data - little endian */
+#define CS4231_LINEAR_16_BIG	0xc0	/* 16-bit twos complement data - big endian */
+#define CS4231_ADPCM_16		0xa0	/* 16-bit ADPCM */
+#define CS4231_STEREO		0x10	/* stereo mode */
+/* bits 3-1 define frequency divisor */
+#define CS4231_XTAL1		0x00	/* 24.576 crystal */
+#define CS4231_XTAL2		0x01	/* 16.9344 crystal */
+
+/* definitions for interface control register - CS4231_IFACE_CTRL */
+
+#define CS4231_RECORD_PIO	0x80	/* record PIO enable */
+#define CS4231_PLAYBACK_PIO	0x40	/* playback PIO enable */
+#define CS4231_CALIB_MODE	0x18	/* calibration mode bits */
+#define CS4231_AUTOCALIB	0x08	/* auto calibrate */
+#define CS4231_SINGLE_DMA	0x04	/* use single DMA channel */
+#define CS4231_RECORD_ENABLE	0x02	/* record enable */
+#define CS4231_PLAYBACK_ENABLE	0x01	/* playback enable */
+
+/* definitions for pin control register - CS4231_PIN_CTRL */
+
+#define CS4231_IRQ_ENABLE	0x02	/* enable IRQ */
+#define CS4231_XCTL1		0x40	/* external control #1 */
+#define CS4231_XCTL0		0x80	/* external control #0 */
+
+/* definitions for test and init register - CS4231_TEST_INIT */
+
+#define CS4231_CALIB_IN_PROGRESS 0x20	/* auto calibrate in progress */
+#define CS4231_DMA_REQUEST	0x10	/* DMA request in progress */
+
+/* definitions for misc control register - CS4231_MISC_INFO */
+
+#define CS4231_MODE2		0x40	/* MODE 2 */
+#define CS4231_IW_MODE3		0x6c	/* MODE 3 - InterWave enhanced mode */
+#define CS4231_4236_MODE3	0xe0	/* MODE 3 - CS4236+ enhanced mode */
+
+/* definitions for alternate feature 1 register - CS4231_ALT_FEATURE_1 */
+
+#define	CS4231_DACZ		0x01	/* zero DAC when underrun */
+#define CS4231_TIMER_ENABLE	0x40	/* codec timer enable */
+#define CS4231_OLB		0x80	/* output level bit */
+
+/* definitions for Extended Registers - CS4236+ */
+
+#define CS4236_REG(i23val)	(((i23val << 2) & 0x10) | ((i23val >> 4) & 0x0f))
+#define CS4236_I23VAL(reg)	((((reg)&0xf) << 4) | (((reg)&0x10) >> 2) | 0x8)
+
+#define CS4236_LEFT_LINE	0x08	/* left LINE alternate volume */
+#define CS4236_RIGHT_LINE	0x18	/* right LINE alternate volume */
+#define CS4236_LEFT_MIC		0x28	/* left MIC volume */
+#define CS4236_RIGHT_MIC	0x38	/* right MIC volume */
+#define CS4236_LEFT_MIX_CTRL	0x48	/* synthesis and left input mixer control */
+#define CS4236_RIGHT_MIX_CTRL	0x58	/* right input mixer control */
+#define CS4236_LEFT_FM		0x68	/* left FM volume */
+#define CS4236_RIGHT_FM		0x78	/* right FM volume */
+#define CS4236_LEFT_DSP		0x88	/* left DSP serial port volume */
+#define CS4236_RIGHT_DSP	0x98	/* right DSP serial port volume */
+#define CS4236_RIGHT_LOOPBACK	0xa8	/* right loopback monitor volume */
+#define CS4236_DAC_MUTE		0xb8	/* DAC mute and IFSE enable */
+#define CS4236_ADC_RATE		0xc8	/* indenpendent ADC sample frequency */
+#define CS4236_DAC_RATE		0xd8	/* indenpendent DAC sample frequency */
+#define CS4236_LEFT_MASTER	0xe8	/* left master digital audio volume */
+#define CS4236_RIGHT_MASTER	0xf8	/* right master digital audio volume */
+#define CS4236_LEFT_WAVE	0x0c	/* left wavetable serial port volume */
+#define CS4236_RIGHT_WAVE	0x1c	/* right wavetable serial port volume */
+#define CS4236_VERSION		0x9c	/* chip version and ID */
+
+/* defines for codec.mode */
+
+#define CS4231_MODE_NONE	0x0000
+#define CS4231_MODE_PLAY	0x0001
+#define CS4231_MODE_RECORD	0x0002
+#define CS4231_MODE_TIMER	0x0004
+#define CS4231_MODE_OPEN	(CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER)
+
+/* defines for codec.hardware */
+
+#define CS4231_HW_DETECT        0x0000	/* let CS4231 driver detect chip */
+#define CS4231_HW_DETECT3	0x0001	/* allow mode 3 */
+#define CS4231_HW_TYPE_MASK	0xff00	/* type mask */
+#define CS4231_HW_CS4231_MASK   0x0100	/* CS4231 serie */
+#define CS4231_HW_CS4231        0x0100	/* CS4231 chip */
+#define CS4231_HW_CS4231A       0x0101	/* CS4231A chip */
+#define CS4231_HW_CS4232_MASK   0x0200	/* CS4232 serie (has control ports) */
+#define CS4231_HW_CS4232        0x0200	/* CS4232 */
+#define CS4231_HW_CS4232A       0x0201	/* CS4232A */
+#define CS4231_HW_CS4236	0x0202	/* CS4236 */
+#define CS4231_HW_CS4236B_MASK	0x0400	/* CS4236B serie (has extended control regs) */
+#define CS4231_HW_CS4235	0x0400	/* CS4235 - Crystal Clear (tm) stereo enhancement */
+#define CS4231_HW_CS4236B       0x0401	/* CS4236B */
+#define CS4231_HW_CS4237B       0x0402	/* CS4237B - SRS 3D */
+#define CS4231_HW_CS4238B	0x0403	/* CS4238B - QSOUND 3D */
+#define CS4231_HW_CS4239	0x0404	/* CS4239 - Crystal Clear (tm) stereo enhancement */
+/* compatible, but clones */
+#define CS4231_HW_INTERWAVE     0x1000	/* InterWave chip */
+#define CS4231_HW_OPL3SA2       0x1001	/* OPL3-SA2 chip */
+
+/* defines for codec.hwshare */
+#define CS4231_HWSHARE_IRQ	(1<<0)
+#define CS4231_HWSHARE_DMA1	(1<<1)
+#define CS4231_HWSHARE_DMA2	(1<<2)
+
+typedef struct _snd_cs4231 cs4231_t;
+
+struct _snd_cs4231 {
+	unsigned long port;		/* base i/o port */
+	struct resource *res_port;
+	unsigned long cport;		/* control base i/o port (CS4236) */
+	struct resource *res_cport;
+	int irq;			/* IRQ line */
+	int dma1;			/* playback DMA */
+	int dma2;			/* record DMA */
+	unsigned short version;		/* version of CODEC chip */
+	unsigned short mode;		/* see to CS4231_MODE_XXXX */
+	unsigned short hardware;	/* see to CS4231_HW_XXXX */
+	unsigned short hwshare;		/* shared resources */
+	unsigned short single_dma:1;	/* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_timer_t *timer;
+
+	unsigned char image[32];	/* registers image */
+	unsigned char eimage[32];	/* extended registers image */
+	unsigned char cimage[16];	/* control registers image */
+	int mce_bit;
+	int calibrate_mute;
+	int sw_3d_bit;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	spinlock_t reg_lock;
+	struct semaphore mce_mutex;
+	struct semaphore open_mutex;
+
+	int (*rate_constraint) (snd_pcm_runtime_t *runtime);
+	void (*set_playback_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char pdfr);
+	void (*set_capture_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char cdfr);
+#ifdef CONFIG_PM
+	struct pm_dev *pm_dev;
+	void (*suspend) (cs4231_t *chip);
+	void (*resume) (cs4231_t *chip);
+#endif
+	void *dma_private_data;
+	int (*claim_dma) (cs4231_t *chip, void *dma_private_data, int dma);
+	int (*release_dma) (cs4231_t *chip, void *dma_private_data, int dma);
+};
+
+/* exported functions */
+
+void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char val);
+unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg);
+void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, unsigned char mask, unsigned char val);
+void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val);
+unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg);
+void snd_cs4231_mce_up(cs4231_t *chip);
+void snd_cs4231_mce_down(cs4231_t *chip);
+
+void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+const char *snd_cs4231_chip_id(cs4231_t *chip);
+
+int snd_cs4231_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip);
+int snd_cs4231_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm);
+int snd_cs4231_timer(cs4231_t * chip, int device, snd_timer_t **rtimer);
+int snd_cs4231_mixer(cs4231_t * chip);
+
+int snd_cs4236_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip);
+int snd_cs4236_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm);
+int snd_cs4236_mixer(cs4231_t * chip);
+
+/*
+ *  mixer library
+ */
+
+#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4231_info_single, \
+  get: snd_cs4231_get_single, put: snd_cs4231_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4231_info_double, \
+  get: snd_cs4231_get_double, put: snd_cs4231_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#ifdef CONFIG_SND_DEBUG
+void snd_cs4231_debug(cs4231_t *chip);
+#endif
+
+#endif /* __SOUND_CS4231_H */
diff -Nru linux/include/sound/cs46xx.h linux-2.4.19-pre5-mjc/include/sound/cs46xx.h
--- linux/include/sound/cs46xx.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/cs46xx.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1720 @@
+#ifndef __SOUND_CS46XX_H
+#define __SOUND_CS46XX_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Cirrus Logic, Inc.
+ *  Definitions for Cirrus Logic CS46xx chips
+ *
+ *
+ *   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
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "ac97_codec.h"
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS            0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4610
+#define PCI_DEVICE_ID_CIRRUS_4610       0x6001
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4612
+#define PCI_DEVICE_ID_CIRRUS_4612       0x6003
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4615
+#define PCI_DEVICE_ID_CIRRUS_4615       0x6004
+#endif
+
+/*
+ *  Direct registers
+ */
+
+/*
+ *  The following define the offsets of the registers accessed via base address
+ *  register zero on the CS46xx part.
+ */
+#define BA0_HISR				0x00000000
+#define BA0_HSR0                                0x00000004
+#define BA0_HICR                                0x00000008
+#define BA0_DMSR                                0x00000100
+#define BA0_HSAR                                0x00000110
+#define BA0_HDAR                                0x00000114
+#define BA0_HDMR                                0x00000118
+#define BA0_HDCR                                0x0000011C
+#define BA0_PFMC                                0x00000200
+#define BA0_PFCV1                               0x00000204
+#define BA0_PFCV2                               0x00000208
+#define BA0_PCICFG00                            0x00000300
+#define BA0_PCICFG04                            0x00000304
+#define BA0_PCICFG08                            0x00000308
+#define BA0_PCICFG0C                            0x0000030C
+#define BA0_PCICFG10                            0x00000310
+#define BA0_PCICFG14                            0x00000314
+#define BA0_PCICFG18                            0x00000318
+#define BA0_PCICFG1C                            0x0000031C
+#define BA0_PCICFG20                            0x00000320
+#define BA0_PCICFG24                            0x00000324
+#define BA0_PCICFG28                            0x00000328
+#define BA0_PCICFG2C                            0x0000032C
+#define BA0_PCICFG30                            0x00000330
+#define BA0_PCICFG34                            0x00000334
+#define BA0_PCICFG38                            0x00000338
+#define BA0_PCICFG3C                            0x0000033C
+#define BA0_CLKCR1                              0x00000400
+#define BA0_CLKCR2                              0x00000404
+#define BA0_PLLM                                0x00000408
+#define BA0_PLLCC                               0x0000040C
+#define BA0_FRR                                 0x00000410 
+#define BA0_CFL1                                0x00000414
+#define BA0_CFL2                                0x00000418
+#define BA0_SERMC1                              0x00000420
+#define BA0_SERMC2                              0x00000424
+#define BA0_SERC1                               0x00000428
+#define BA0_SERC2                               0x0000042C
+#define BA0_SERC3                               0x00000430
+#define BA0_SERC4                               0x00000434
+#define BA0_SERC5                               0x00000438
+#define BA0_SERBSP                              0x0000043C
+#define BA0_SERBST                              0x00000440
+#define BA0_SERBCM                              0x00000444
+#define BA0_SERBAD                              0x00000448
+#define BA0_SERBCF                              0x0000044C
+#define BA0_SERBWP                              0x00000450
+#define BA0_SERBRP                              0x00000454
+#ifndef NO_CS4612
+#define BA0_ASER_FADDR                          0x00000458
+#endif
+#define BA0_ACCTL                               0x00000460
+#define BA0_ACSTS                               0x00000464
+#define BA0_ACOSV                               0x00000468
+#define BA0_ACCAD                               0x0000046C
+#define BA0_ACCDA                               0x00000470
+#define BA0_ACISV                               0x00000474
+#define BA0_ACSAD                               0x00000478
+#define BA0_ACSDA                               0x0000047C
+#define BA0_JSPT                                0x00000480
+#define BA0_JSCTL                               0x00000484
+#define BA0_JSC1                                0x00000488
+#define BA0_JSC2                                0x0000048C
+#define BA0_MIDCR                               0x00000490
+#define BA0_MIDSR                               0x00000494
+#define BA0_MIDWP                               0x00000498
+#define BA0_MIDRP                               0x0000049C
+#define BA0_JSIO                                0x000004A0
+#ifndef NO_CS4612
+#define BA0_ASER_MASTER                         0x000004A4
+#endif
+#define BA0_CFGI                                0x000004B0
+#define BA0_SSVID                               0x000004B4
+#define BA0_GPIOR                               0x000004B8
+#ifndef NO_CS4612
+#define BA0_EGPIODR                             0x000004BC
+#define BA0_EGPIOPTR                            0x000004C0
+#define BA0_EGPIOTR                             0x000004C4
+#define BA0_EGPIOWR                             0x000004C8
+#define BA0_EGPIOSR                             0x000004CC
+#define BA0_SERC6                               0x000004D0
+#define BA0_SERC7                               0x000004D4
+#define BA0_SERACC                              0x000004D8
+#define BA0_ACCTL2                              0x000004E0
+#define BA0_ACSTS2                              0x000004E4
+#define BA0_ACOSV2                              0x000004E8
+#define BA0_ACCAD2                              0x000004EC
+#define BA0_ACCDA2                              0x000004F0
+#define BA0_ACISV2                              0x000004F4
+#define BA0_ACSAD2                              0x000004F8
+#define BA0_ACSDA2                              0x000004FC
+#define BA0_IOTAC0                              0x00000500
+#define BA0_IOTAC1                              0x00000504
+#define BA0_IOTAC2                              0x00000508
+#define BA0_IOTAC3                              0x0000050C
+#define BA0_IOTAC4                              0x00000510
+#define BA0_IOTAC5                              0x00000514
+#define BA0_IOTAC6                              0x00000518
+#define BA0_IOTAC7                              0x0000051C
+#define BA0_IOTAC8                              0x00000520
+#define BA0_IOTAC9                              0x00000524
+#define BA0_IOTAC10                             0x00000528
+#define BA0_IOTAC11                             0x0000052C
+#define BA0_IOTFR0                              0x00000540
+#define BA0_IOTFR1                              0x00000544
+#define BA0_IOTFR2                              0x00000548
+#define BA0_IOTFR3                              0x0000054C
+#define BA0_IOTFR4                              0x00000550
+#define BA0_IOTFR5                              0x00000554
+#define BA0_IOTFR6                              0x00000558
+#define BA0_IOTFR7                              0x0000055C
+#define BA0_IOTFIFO                             0x00000580
+#define BA0_IOTRRD                              0x00000584
+#define BA0_IOTFP                               0x00000588
+#define BA0_IOTCR                               0x0000058C
+#define BA0_DPCID                               0x00000590
+#define BA0_DPCIA                               0x00000594
+#define BA0_DPCIC                               0x00000598
+#define BA0_PCPCIR                              0x00000600
+#define BA0_PCPCIG                              0x00000604
+#define BA0_PCPCIEN                             0x00000608
+#define BA0_EPCIPMC                             0x00000610
+#endif
+
+/*
+ *  The following define the offsets of the registers and memories accessed via
+ *  base address register one on the CS46xx part.
+ */
+#define BA1_SP_DMEM0                            0x00000000
+#define BA1_SP_DMEM1                            0x00010000
+#define BA1_SP_PMEM                             0x00020000
+#define BA1_SP_REG				0x00030000
+#define BA1_SPCR                                0x00030000
+#define BA1_DREG                                0x00030004
+#define BA1_DSRWP                               0x00030008
+#define BA1_TWPR                                0x0003000C
+#define BA1_SPWR                                0x00030010
+#define BA1_SPIR                                0x00030014
+#define BA1_FGR1                                0x00030020
+#define BA1_SPCS                                0x00030028
+#define BA1_SDSR                                0x0003002C
+#define BA1_FRMT                                0x00030030
+#define BA1_FRCC                                0x00030034
+#define BA1_FRSC                                0x00030038
+#define BA1_OMNI_MEM                            0x000E0000
+
+/*
+ *  The following defines are for the flags in the host interrupt status
+ *  register.
+ */
+#define HISR_VC_MASK                            0x0000FFFF
+#define HISR_VC0                                0x00000001
+#define HISR_VC1                                0x00000002
+#define HISR_VC2                                0x00000004
+#define HISR_VC3                                0x00000008
+#define HISR_VC4                                0x00000010
+#define HISR_VC5                                0x00000020
+#define HISR_VC6                                0x00000040
+#define HISR_VC7                                0x00000080
+#define HISR_VC8                                0x00000100
+#define HISR_VC9                                0x00000200
+#define HISR_VC10                               0x00000400
+#define HISR_VC11                               0x00000800
+#define HISR_VC12                               0x00001000
+#define HISR_VC13                               0x00002000
+#define HISR_VC14                               0x00004000
+#define HISR_VC15                               0x00008000
+#define HISR_INT0                               0x00010000
+#define HISR_INT1                               0x00020000
+#define HISR_DMAI                               0x00040000
+#define HISR_FROVR                              0x00080000
+#define HISR_MIDI                               0x00100000
+#ifdef NO_CS4612
+#define HISR_RESERVED                           0x0FE00000
+#else
+#define HISR_SBINT                              0x00200000
+#define HISR_RESERVED                           0x0FC00000
+#endif
+#define HISR_H0P                                0x40000000
+#define HISR_INTENA                             0x80000000
+
+/*
+ *  The following defines are for the flags in the host signal register 0.
+ */
+#define HSR0_VC_MASK                            0xFFFFFFFF
+#define HSR0_VC16                               0x00000001
+#define HSR0_VC17                               0x00000002
+#define HSR0_VC18                               0x00000004
+#define HSR0_VC19                               0x00000008
+#define HSR0_VC20                               0x00000010
+#define HSR0_VC21                               0x00000020
+#define HSR0_VC22                               0x00000040
+#define HSR0_VC23                               0x00000080
+#define HSR0_VC24                               0x00000100
+#define HSR0_VC25                               0x00000200
+#define HSR0_VC26                               0x00000400
+#define HSR0_VC27                               0x00000800
+#define HSR0_VC28                               0x00001000
+#define HSR0_VC29                               0x00002000
+#define HSR0_VC30                               0x00004000
+#define HSR0_VC31                               0x00008000
+#define HSR0_VC32                               0x00010000
+#define HSR0_VC33                               0x00020000
+#define HSR0_VC34                               0x00040000
+#define HSR0_VC35                               0x00080000
+#define HSR0_VC36                               0x00100000
+#define HSR0_VC37                               0x00200000
+#define HSR0_VC38                               0x00400000
+#define HSR0_VC39                               0x00800000
+#define HSR0_VC40                               0x01000000
+#define HSR0_VC41                               0x02000000
+#define HSR0_VC42                               0x04000000
+#define HSR0_VC43                               0x08000000
+#define HSR0_VC44                               0x10000000
+#define HSR0_VC45                               0x20000000
+#define HSR0_VC46                               0x40000000
+#define HSR0_VC47                               0x80000000
+
+/*
+ *  The following defines are for the flags in the host interrupt control
+ *  register.
+ */
+#define HICR_IEV                                0x00000001
+#define HICR_CHGM                               0x00000002
+
+/*
+ *  The following defines are for the flags in the DMA status register.
+ */
+#define DMSR_HP                                 0x00000001
+#define DMSR_HR                                 0x00000002
+#define DMSR_SP                                 0x00000004
+#define DMSR_SR                                 0x00000008
+
+/*
+ *  The following defines are for the flags in the host DMA source address
+ *  register.
+ */
+#define HSAR_HOST_ADDR_MASK                     0xFFFFFFFF
+#define HSAR_DSP_ADDR_MASK                      0x0000FFFF
+#define HSAR_MEMID_MASK                         0x000F0000
+#define HSAR_MEMID_SP_DMEM0                     0x00000000
+#define HSAR_MEMID_SP_DMEM1                     0x00010000
+#define HSAR_MEMID_SP_PMEM                      0x00020000
+#define HSAR_MEMID_SP_DEBUG                     0x00030000
+#define HSAR_MEMID_OMNI_MEM                     0x000E0000
+#define HSAR_END                                0x40000000
+#define HSAR_ERR                                0x80000000
+
+/*
+ *  The following defines are for the flags in the host DMA destination address
+ *  register.
+ */
+#define HDAR_HOST_ADDR_MASK                     0xFFFFFFFF
+#define HDAR_DSP_ADDR_MASK                      0x0000FFFF
+#define HDAR_MEMID_MASK                         0x000F0000
+#define HDAR_MEMID_SP_DMEM0                     0x00000000
+#define HDAR_MEMID_SP_DMEM1                     0x00010000
+#define HDAR_MEMID_SP_PMEM                      0x00020000
+#define HDAR_MEMID_SP_DEBUG                     0x00030000
+#define HDAR_MEMID_OMNI_MEM                     0x000E0000
+#define HDAR_END                                0x40000000
+#define HDAR_ERR                                0x80000000
+
+/*
+ *  The following defines are for the flags in the host DMA control register.
+ */
+#define HDMR_AC_MASK                            0x0000F000
+#define HDMR_AC_8_16                            0x00001000
+#define HDMR_AC_M_S                             0x00002000
+#define HDMR_AC_B_L                             0x00004000
+#define HDMR_AC_S_U                             0x00008000
+
+/*
+ *  The following defines are for the flags in the host DMA control register.
+ */
+#define HDCR_COUNT_MASK                         0x000003FF
+#define HDCR_DONE                               0x00004000
+#define HDCR_OPT                                0x00008000
+#define HDCR_WBD                                0x00400000
+#define HDCR_WBS                                0x00800000
+#define HDCR_DMS_MASK                           0x07000000
+#define HDCR_DMS_LINEAR                         0x00000000
+#define HDCR_DMS_16_DWORDS                      0x01000000
+#define HDCR_DMS_32_DWORDS                      0x02000000
+#define HDCR_DMS_64_DWORDS                      0x03000000
+#define HDCR_DMS_128_DWORDS                     0x04000000
+#define HDCR_DMS_256_DWORDS                     0x05000000
+#define HDCR_DMS_512_DWORDS                     0x06000000
+#define HDCR_DMS_1024_DWORDS                    0x07000000
+#define HDCR_DH                                 0x08000000
+#define HDCR_SMS_MASK                           0x70000000
+#define HDCR_SMS_LINEAR                         0x00000000
+#define HDCR_SMS_16_DWORDS                      0x10000000
+#define HDCR_SMS_32_DWORDS                      0x20000000
+#define HDCR_SMS_64_DWORDS                      0x30000000
+#define HDCR_SMS_128_DWORDS                     0x40000000
+#define HDCR_SMS_256_DWORDS                     0x50000000
+#define HDCR_SMS_512_DWORDS                     0x60000000
+#define HDCR_SMS_1024_DWORDS                    0x70000000
+#define HDCR_SH                                 0x80000000
+#define HDCR_COUNT_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the performance monitor control
+ *  register.
+ */
+#define PFMC_C1SS_MASK                          0x0000001F
+#define PFMC_C1EV                               0x00000020
+#define PFMC_C1RS                               0x00008000
+#define PFMC_C2SS_MASK                          0x001F0000
+#define PFMC_C2EV                               0x00200000
+#define PFMC_C2RS                               0x80000000
+#define PFMC_C1SS_SHIFT                         0
+#define PFMC_C2SS_SHIFT                         16
+#define PFMC_BUS_GRANT                          0
+#define PFMC_GRANT_AFTER_REQ                    1
+#define PFMC_TRANSACTION                        2
+#define PFMC_DWORD_TRANSFER                     3
+#define PFMC_SLAVE_READ                         4
+#define PFMC_SLAVE_WRITE                        5
+#define PFMC_PREEMPTION                         6
+#define PFMC_DISCONNECT_RETRY                   7
+#define PFMC_INTERRUPT                          8
+#define PFMC_BUS_OWNERSHIP                      9
+#define PFMC_TRANSACTION_LAG                    10
+#define PFMC_PCI_CLOCK                          11
+#define PFMC_SERIAL_CLOCK                       12
+#define PFMC_SP_CLOCK                           13
+
+/*
+ *  The following defines are for the flags in the performance counter value 1
+ *  register.
+ */
+#define PFCV1_PC1V_MASK                         0xFFFFFFFF
+#define PFCV1_PC1V_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the performance counter value 2
+ *  register.
+ */
+#define PFCV2_PC2V_MASK                         0xFFFFFFFF
+#define PFCV2_PC2V_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the clock control register 1.
+ */
+#define CLKCR1_OSCS                             0x00000001
+#define CLKCR1_OSCP                             0x00000002
+#define CLKCR1_PLLSS_MASK                       0x0000000C
+#define CLKCR1_PLLSS_SERIAL                     0x00000000
+#define CLKCR1_PLLSS_CRYSTAL                    0x00000004
+#define CLKCR1_PLLSS_PCI                        0x00000008
+#define CLKCR1_PLLSS_RESERVED                   0x0000000C
+#define CLKCR1_PLLP                             0x00000010
+#define CLKCR1_SWCE                             0x00000020
+#define CLKCR1_PLLOS                            0x00000040
+
+/*
+ *  The following defines are for the flags in the clock control register 2.
+ */
+#define CLKCR2_PDIVS_MASK                       0x0000000F
+#define CLKCR2_PDIVS_1                          0x00000001
+#define CLKCR2_PDIVS_2                          0x00000002
+#define CLKCR2_PDIVS_4                          0x00000004
+#define CLKCR2_PDIVS_7                          0x00000007
+#define CLKCR2_PDIVS_8                          0x00000008
+#define CLKCR2_PDIVS_16                         0x00000000
+
+/*
+ *  The following defines are for the flags in the PLL multiplier register.
+ */
+#define PLLM_MASK                               0x000000FF
+#define PLLM_SHIFT                              0
+
+/*
+ *  The following defines are for the flags in the PLL capacitor coefficient
+ *  register.
+ */
+#define PLLCC_CDR_MASK                          0x00000007
+#ifndef NO_CS4610
+#define PLLCC_CDR_240_350_MHZ                   0x00000000
+#define PLLCC_CDR_184_265_MHZ                   0x00000001
+#define PLLCC_CDR_144_205_MHZ                   0x00000002
+#define PLLCC_CDR_111_160_MHZ                   0x00000003
+#define PLLCC_CDR_87_123_MHZ                    0x00000004
+#define PLLCC_CDR_67_96_MHZ                     0x00000005
+#define PLLCC_CDR_52_74_MHZ                     0x00000006
+#define PLLCC_CDR_45_58_MHZ                     0x00000007
+#endif
+#ifndef NO_CS4612
+#define PLLCC_CDR_271_398_MHZ                   0x00000000
+#define PLLCC_CDR_227_330_MHZ                   0x00000001
+#define PLLCC_CDR_167_239_MHZ                   0x00000002
+#define PLLCC_CDR_150_215_MHZ                   0x00000003
+#define PLLCC_CDR_107_154_MHZ                   0x00000004
+#define PLLCC_CDR_98_140_MHZ                    0x00000005
+#define PLLCC_CDR_73_104_MHZ                    0x00000006
+#define PLLCC_CDR_63_90_MHZ                     0x00000007
+#endif
+#define PLLCC_LPF_MASK                          0x000000F8
+#ifndef NO_CS4610
+#define PLLCC_LPF_23850_60000_KHZ               0x00000000
+#define PLLCC_LPF_7960_26290_KHZ                0x00000008
+#define PLLCC_LPF_4160_10980_KHZ                0x00000018
+#define PLLCC_LPF_1740_4580_KHZ                 0x00000038
+#define PLLCC_LPF_724_1910_KHZ                  0x00000078
+#define PLLCC_LPF_317_798_KHZ                   0x000000F8
+#endif
+#ifndef NO_CS4612
+#define PLLCC_LPF_25580_64530_KHZ               0x00000000
+#define PLLCC_LPF_14360_37270_KHZ               0x00000008
+#define PLLCC_LPF_6100_16020_KHZ                0x00000018
+#define PLLCC_LPF_2540_6690_KHZ                 0x00000038
+#define PLLCC_LPF_1050_2780_KHZ                 0x00000078
+#define PLLCC_LPF_450_1160_KHZ                  0x000000F8
+#endif
+
+/*
+ *  The following defines are for the flags in the feature reporting register.
+ */
+#define FRR_FAB_MASK                            0x00000003
+#define FRR_MASK_MASK                           0x0000001C
+#ifdef NO_CS4612
+#define FRR_CFOP_MASK                           0x000000E0
+#else
+#define FRR_CFOP_MASK                           0x00000FE0
+#endif
+#define FRR_CFOP_NOT_DVD                        0x00000020
+#define FRR_CFOP_A3D                            0x00000040
+#define FRR_CFOP_128_PIN                        0x00000080
+#ifndef NO_CS4612
+#define FRR_CFOP_CS4280                         0x00000800
+#endif
+#define FRR_FAB_SHIFT                           0
+#define FRR_MASK_SHIFT                          2
+#define FRR_CFOP_SHIFT                          5
+
+/*
+ *  The following defines are for the flags in the configuration load 1
+ *  register.
+ */
+#define CFL1_CLOCK_SOURCE_MASK                  0x00000003
+#define CFL1_CLOCK_SOURCE_CS423X                0x00000000
+#define CFL1_CLOCK_SOURCE_AC97                  0x00000001
+#define CFL1_CLOCK_SOURCE_CRYSTAL               0x00000002
+#define CFL1_CLOCK_SOURCE_DUAL_AC97             0x00000003
+#define CFL1_VALID_DATA_MASK                    0x000000FF
+
+/*
+ *  The following defines are for the flags in the configuration load 2
+ *  register.
+ */
+#define CFL2_VALID_DATA_MASK                    0x000000FF
+
+/*
+ *  The following defines are for the flags in the serial port master control
+ *  register 1.
+ */
+#define SERMC1_MSPE                             0x00000001
+#define SERMC1_PTC_MASK                         0x0000000E
+#define SERMC1_PTC_CS423X                       0x00000000
+#define SERMC1_PTC_AC97                         0x00000002
+#define SERMC1_PTC_DAC                          0x00000004
+#define SERMC1_PLB                              0x00000010
+#define SERMC1_XLB                              0x00000020
+
+/*
+ *  The following defines are for the flags in the serial port master control
+ *  register 2.
+ */
+#define SERMC2_LROE                             0x00000001
+#define SERMC2_MCOE                             0x00000002
+#define SERMC2_MCDIV                            0x00000004
+
+/*
+ *  The following defines are for the flags in the serial port 1 configuration
+ *  register.
+ */
+#define SERC1_SO1EN                             0x00000001
+#define SERC1_SO1F_MASK                         0x0000000E
+#define SERC1_SO1F_CS423X                       0x00000000
+#define SERC1_SO1F_AC97                         0x00000002
+#define SERC1_SO1F_DAC                          0x00000004
+#define SERC1_SO1F_SPDIF                        0x00000006
+
+/*
+ *  The following defines are for the flags in the serial port 2 configuration
+ *  register.
+ */
+#define SERC2_SI1EN                             0x00000001
+#define SERC2_SI1F_MASK                         0x0000000E
+#define SERC2_SI1F_CS423X                       0x00000000
+#define SERC2_SI1F_AC97                         0x00000002
+#define SERC2_SI1F_ADC                          0x00000004
+#define SERC2_SI1F_SPDIF                        0x00000006
+
+/*
+ *  The following defines are for the flags in the serial port 3 configuration
+ *  register.
+ */
+#define SERC3_SO2EN                             0x00000001
+#define SERC3_SO2F_MASK                         0x00000006
+#define SERC3_SO2F_DAC                          0x00000000
+#define SERC3_SO2F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port 4 configuration
+ *  register.
+ */
+#define SERC4_SO3EN                             0x00000001
+#define SERC4_SO3F_MASK                         0x00000006
+#define SERC4_SO3F_DAC                          0x00000000
+#define SERC4_SO3F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port 5 configuration
+ *  register.
+ */
+#define SERC5_SI2EN                             0x00000001
+#define SERC5_SI2F_MASK                         0x00000006
+#define SERC5_SI2F_ADC                          0x00000000
+#define SERC5_SI2F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor sample
+ *  pointer register.
+ */
+#define SERBSP_FSP_MASK                         0x0000000F
+#define SERBSP_FSP_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor status
+ *  register.
+ */
+#define SERBST_RRDY                             0x00000001
+#define SERBST_WBSY                             0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor command
+ *  register.
+ */
+#define SERBCM_RDC                              0x00000001
+#define SERBCM_WRC                              0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor address
+ *  register.
+ */
+#ifdef NO_CS4612
+#define SERBAD_FAD_MASK                         0x000000FF
+#else
+#define SERBAD_FAD_MASK                         0x000001FF
+#endif
+#define SERBAD_FAD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor
+ *  configuration register.
+ */
+#define SERBCF_HBP                              0x00000001
+
+/*
+ *  The following defines are for the flags in the serial port backdoor write
+ *  port register.
+ */
+#define SERBWP_FWD_MASK                         0x000FFFFF
+#define SERBWP_FWD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor read
+ *  port register.
+ */
+#define SERBRP_FRD_MASK                         0x000FFFFF
+#define SERBRP_FRD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the async FIFO address register.
+ */
+#ifndef NO_CS4612
+#define ASER_FADDR_A1_MASK                      0x000001FF
+#define ASER_FADDR_EN1                          0x00008000
+#define ASER_FADDR_A2_MASK                      0x01FF0000
+#define ASER_FADDR_EN2                          0x80000000
+#define ASER_FADDR_A1_SHIFT                     0
+#define ASER_FADDR_A2_SHIFT                     16
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 control register.
+ */
+#define ACCTL_RSTN                              0x00000001
+#define ACCTL_ESYN                              0x00000002
+#define ACCTL_VFRM                              0x00000004
+#define ACCTL_DCV                               0x00000008
+#define ACCTL_CRW                               0x00000010
+#define ACCTL_ASYN                              0x00000020
+#ifndef NO_CS4612
+#define ACCTL_TC                                0x00000040
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status register.
+ */
+#define ACSTS_CRDY                              0x00000001
+#define ACSTS_VSTS                              0x00000002
+#ifndef NO_CS4612
+#define ACSTS_WKUP                              0x00000004
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 output slot valid
+ *  register.
+ */
+#define ACOSV_SLV3                              0x00000001
+#define ACOSV_SLV4                              0x00000002
+#define ACOSV_SLV5                              0x00000004
+#define ACOSV_SLV6                              0x00000008
+#define ACOSV_SLV7                              0x00000010
+#define ACOSV_SLV8                              0x00000020
+#define ACOSV_SLV9                              0x00000040
+#define ACOSV_SLV10                             0x00000080
+#define ACOSV_SLV11                             0x00000100
+#define ACOSV_SLV12                             0x00000200
+
+/*
+ *  The following defines are for the flags in the AC97 command address
+ *  register.
+ */
+#define ACCAD_CI_MASK                           0x0000007F
+#define ACCAD_CI_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 command data register.
+ */
+#define ACCDA_CD_MASK                           0x0000FFFF
+#define ACCDA_CD_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 input slot valid
+ *  register.
+ */
+#define ACISV_ISV3                              0x00000001
+#define ACISV_ISV4                              0x00000002
+#define ACISV_ISV5                              0x00000004
+#define ACISV_ISV6                              0x00000008
+#define ACISV_ISV7                              0x00000010
+#define ACISV_ISV8                              0x00000020
+#define ACISV_ISV9                              0x00000040
+#define ACISV_ISV10                             0x00000080
+#define ACISV_ISV11                             0x00000100
+#define ACISV_ISV12                             0x00000200
+
+/*
+ *  The following defines are for the flags in the AC97 status address
+ *  register.
+ */
+#define ACSAD_SI_MASK                           0x0000007F
+#define ACSAD_SI_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 status data register.
+ */
+#define ACSDA_SD_MASK                           0x0000FFFF
+#define ACSDA_SD_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the joystick poll/trigger
+ *  register.
+ */
+#define JSPT_CAX                                0x00000001
+#define JSPT_CAY                                0x00000002
+#define JSPT_CBX                                0x00000004
+#define JSPT_CBY                                0x00000008
+#define JSPT_BA1                                0x00000010
+#define JSPT_BA2                                0x00000020
+#define JSPT_BB1                                0x00000040
+#define JSPT_BB2                                0x00000080
+
+/*
+ *  The following defines are for the flags in the joystick control register.
+ */
+#define JSCTL_SP_MASK                           0x00000003
+#define JSCTL_SP_SLOW                           0x00000000
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002
+#define JSCTL_SP_FAST                           0x00000003
+#define JSCTL_ARE                               0x00000004
+
+/*
+ *  The following defines are for the flags in the joystick coordinate pair 1
+ *  readback register.
+ */
+#define JSC1_Y1V_MASK                           0x0000FFFF
+#define JSC1_X1V_MASK                           0xFFFF0000
+#define JSC1_Y1V_SHIFT                          0
+#define JSC1_X1V_SHIFT                          16
+
+/*
+ *  The following defines are for the flags in the joystick coordinate pair 2
+ *  readback register.
+ */
+#define JSC2_Y2V_MASK                           0x0000FFFF
+#define JSC2_X2V_MASK                           0xFFFF0000
+#define JSC2_Y2V_SHIFT                          0
+#define JSC2_X2V_SHIFT                          16
+
+/*
+ *  The following defines are for the flags in the MIDI control register.
+ */
+#define MIDCR_TXE                               0x00000001	/* Enable transmitting. */
+#define MIDCR_RXE                               0x00000002	/* Enable receiving. */
+#define MIDCR_RIE                               0x00000004	/* Interrupt upon tx ready. */
+#define MIDCR_TIE                               0x00000008	/* Interrupt upon rx ready. */
+#define MIDCR_MLB                               0x00000010	/* Enable midi loopback. */
+#define MIDCR_MRST                              0x00000020	/* Reset interface. */
+
+/*
+ *  The following defines are for the flags in the MIDI status register.
+ */
+#define MIDSR_TBF                               0x00000001	/* Tx FIFO is full. */
+#define MIDSR_RBE                               0x00000002	/* Rx FIFO is empty. */
+
+/*
+ *  The following defines are for the flags in the MIDI write port register.
+ */
+#define MIDWP_MWD_MASK                          0x000000FF
+#define MIDWP_MWD_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the MIDI read port register.
+ */
+#define MIDRP_MRD_MASK                          0x000000FF
+#define MIDRP_MRD_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the joystick GPIO register.
+ */
+#define JSIO_DAX                                0x00000001
+#define JSIO_DAY                                0x00000002
+#define JSIO_DBX                                0x00000004
+#define JSIO_DBY                                0x00000008
+#define JSIO_AXOE                               0x00000010
+#define JSIO_AYOE                               0x00000020
+#define JSIO_BXOE                               0x00000040
+#define JSIO_BYOE                               0x00000080
+
+/*
+ *  The following defines are for the flags in the master async/sync serial
+ *  port enable register.
+ */
+#ifndef NO_CS4612
+#define ASER_MASTER_ME                          0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the configuration interface
+ *  register.
+ */
+#define CFGI_CLK                                0x00000001
+#define CFGI_DOUT                               0x00000002
+#define CFGI_DIN_EEN                            0x00000004
+#define CFGI_EELD                               0x00000008
+
+/*
+ *  The following defines are for the flags in the subsystem ID and vendor ID
+ *  register.
+ */
+#define SSVID_VID_MASK                          0x0000FFFF
+#define SSVID_SID_MASK                          0xFFFF0000
+#define SSVID_VID_SHIFT                         0
+#define SSVID_SID_SHIFT                         16
+
+/*
+ *  The following defines are for the flags in the GPIO pin interface register.
+ */
+#define GPIOR_VOLDN                             0x00000001
+#define GPIOR_VOLUP                             0x00000002
+#define GPIOR_SI2D                              0x00000004
+#define GPIOR_SI2OE                             0x00000008
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin direction
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIODR_GPOE0                           0x00000001
+#define EGPIODR_GPOE1                           0x00000002
+#define EGPIODR_GPOE2                           0x00000004
+#define EGPIODR_GPOE3                           0x00000008
+#define EGPIODR_GPOE4                           0x00000010
+#define EGPIODR_GPOE5                           0x00000020
+#define EGPIODR_GPOE6                           0x00000040
+#define EGPIODR_GPOE7                           0x00000080
+#define EGPIODR_GPOE8                           0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin polarity/
+ *  type register.
+ */
+#ifndef NO_CS4612
+#define EGPIOPTR_GPPT0                          0x00000001
+#define EGPIOPTR_GPPT1                          0x00000002
+#define EGPIOPTR_GPPT2                          0x00000004
+#define EGPIOPTR_GPPT3                          0x00000008
+#define EGPIOPTR_GPPT4                          0x00000010
+#define EGPIOPTR_GPPT5                          0x00000020
+#define EGPIOPTR_GPPT6                          0x00000040
+#define EGPIOPTR_GPPT7                          0x00000080
+#define EGPIOPTR_GPPT8                          0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin sticky
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOTR_GPS0                            0x00000001
+#define EGPIOTR_GPS1                            0x00000002
+#define EGPIOTR_GPS2                            0x00000004
+#define EGPIOTR_GPS3                            0x00000008
+#define EGPIOTR_GPS4                            0x00000010
+#define EGPIOTR_GPS5                            0x00000020
+#define EGPIOTR_GPS6                            0x00000040
+#define EGPIOTR_GPS7                            0x00000080
+#define EGPIOTR_GPS8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO ping wakeup
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOWR_GPW0                            0x00000001
+#define EGPIOWR_GPW1                            0x00000002
+#define EGPIOWR_GPW2                            0x00000004
+#define EGPIOWR_GPW3                            0x00000008
+#define EGPIOWR_GPW4                            0x00000010
+#define EGPIOWR_GPW5                            0x00000020
+#define EGPIOWR_GPW6                            0x00000040
+#define EGPIOWR_GPW7                            0x00000080
+#define EGPIOWR_GPW8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin status
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOSR_GPS0                            0x00000001
+#define EGPIOSR_GPS1                            0x00000002
+#define EGPIOSR_GPS2                            0x00000004
+#define EGPIOSR_GPS3                            0x00000008
+#define EGPIOSR_GPS4                            0x00000010
+#define EGPIOSR_GPS5                            0x00000020
+#define EGPIOSR_GPS6                            0x00000040
+#define EGPIOSR_GPS7                            0x00000080
+#define EGPIOSR_GPS8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port 6 configuration
+ *  register.
+ */
+#ifndef NO_CS4612
+#define SERC6_ASDO2EN                           0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port 7 configuration
+ *  register.
+ */
+#ifndef NO_CS4612
+#define SERC7_ASDI2EN                           0x00000001
+#define SERC7_POSILB                            0x00000002
+#define SERC7_SIPOLB                            0x00000004
+#define SERC7_SOSILB                            0x00000008
+#define SERC7_SISOLB                            0x00000010
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port AC link
+ *  configuration register.
+ */
+#ifndef NO_CS4612
+#define SERACC_CHIP_TYPE_MASK                  0x00000001
+#define SERACC_CHIP_TYPE_1_03                  0x00000000
+#define SERACC_CHIP_TYPE_2_0                   0x00000001
+#define SERACC_TWO_CODECS                       0x00000002
+#define SERACC_MDM                              0x00000004
+#define SERACC_HSP                              0x00000008
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 control register 2.
+ */
+#ifndef NO_CS4612
+#define ACCTL2_RSTN                             0x00000001
+#define ACCTL2_ESYN                             0x00000002
+#define ACCTL2_VFRM                             0x00000004
+#define ACCTL2_DCV                              0x00000008
+#define ACCTL2_CRW                              0x00000010
+#define ACCTL2_ASYN                             0x00000020
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status register 2.
+ */
+#ifndef NO_CS4612
+#define ACSTS2_CRDY                             0x00000001
+#define ACSTS2_VSTS                             0x00000002
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 output slot valid
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACOSV2_SLV3                             0x00000001
+#define ACOSV2_SLV4                             0x00000002
+#define ACOSV2_SLV5                             0x00000004
+#define ACOSV2_SLV6                             0x00000008
+#define ACOSV2_SLV7                             0x00000010
+#define ACOSV2_SLV8                             0x00000020
+#define ACOSV2_SLV9                             0x00000040
+#define ACOSV2_SLV10                            0x00000080
+#define ACOSV2_SLV11                            0x00000100
+#define ACOSV2_SLV12                            0x00000200
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 command address
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACCAD2_CI_MASK                          0x0000007F
+#define ACCAD2_CI_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 command data register
+ *  2.
+ */
+#ifndef NO_CS4612
+#define ACCDA2_CD_MASK                          0x0000FFFF
+#define ACCDA2_CD_SHIFT                         0  
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 input slot valid
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACISV2_ISV3                             0x00000001
+#define ACISV2_ISV4                             0x00000002
+#define ACISV2_ISV5                             0x00000004
+#define ACISV2_ISV6                             0x00000008
+#define ACISV2_ISV7                             0x00000010
+#define ACISV2_ISV8                             0x00000020
+#define ACISV2_ISV9                             0x00000040
+#define ACISV2_ISV10                            0x00000080
+#define ACISV2_ISV11                            0x00000100
+#define ACISV2_ISV12                            0x00000200
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status address
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACSAD2_SI_MASK                          0x0000007F
+#define ACSAD2_SI_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status data register 2.
+ */
+#ifndef NO_CS4612
+#define ACSDA2_SD_MASK                          0x0000FFFF
+#define ACSDA2_SD_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap address and control
+ *  registers (all 12).
+ */
+#ifndef NO_CS4612
+#define IOTAC_SA_MASK                           0x0000FFFF
+#define IOTAC_MSK_MASK                          0x000F0000
+#define IOTAC_IODC_MASK                         0x06000000
+#define IOTAC_IODC_16_BIT                       0x00000000
+#define IOTAC_IODC_10_BIT                       0x02000000
+#define IOTAC_IODC_12_BIT                       0x04000000
+#define IOTAC_WSPI                              0x08000000
+#define IOTAC_RSPI                              0x10000000
+#define IOTAC_WSE                               0x20000000
+#define IOTAC_WE                                0x40000000
+#define IOTAC_RE                                0x80000000
+#define IOTAC_SA_SHIFT                          0
+#define IOTAC_MSK_SHIFT                         16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap fast read registers
+ *  (all 8).
+ */
+#ifndef NO_CS4612
+#define IOTFR_D_MASK                            0x0000FFFF
+#define IOTFR_A_MASK                            0x000F0000
+#define IOTFR_R_MASK                            0x0F000000
+#define IOTFR_ALL                               0x40000000
+#define IOTFR_VL                                0x80000000
+#define IOTFR_D_SHIFT                           0
+#define IOTFR_A_SHIFT                           16
+#define IOTFR_R_SHIFT                           24
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap FIFO register.
+ */
+#ifndef NO_CS4612
+#define IOTFIFO_BA_MASK                         0x00003FFF
+#define IOTFIFO_S_MASK                          0x00FF0000
+#define IOTFIFO_OF                              0x40000000
+#define IOTFIFO_SPIOF                           0x80000000
+#define IOTFIFO_BA_SHIFT                        0
+#define IOTFIFO_S_SHIFT                         16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap retry read data
+ *  register.
+ */
+#ifndef NO_CS4612
+#define IOTRRD_D_MASK                           0x0000FFFF
+#define IOTRRD_RDV                              0x80000000
+#define IOTRRD_D_SHIFT                          0
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap FIFO pointer
+ *  register.
+ */
+#ifndef NO_CS4612
+#define IOTFP_CA_MASK                           0x00003FFF
+#define IOTFP_PA_MASK                           0x3FFF0000
+#define IOTFP_CA_SHIFT                          0
+#define IOTFP_PA_SHIFT                          16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap control register.
+ */
+#ifndef NO_CS4612
+#define IOTCR_ITD                               0x00000001
+#define IOTCR_HRV                               0x00000002
+#define IOTCR_SRV                               0x00000004
+#define IOTCR_DTI                               0x00000008
+#define IOTCR_DFI                               0x00000010
+#define IOTCR_DDP                               0x00000020
+#define IOTCR_JTE                               0x00000040
+#define IOTCR_PPE                               0x00000080
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI data register.
+ */
+#ifndef NO_CS4612
+#define DPCID_D_MASK                            0xFFFFFFFF
+#define DPCID_D_SHIFT                           0
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI address register.
+ */
+#ifndef NO_CS4612
+#define DPCIA_A_MASK                            0xFFFFFFFF
+#define DPCIA_A_SHIFT                           0
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI command register.
+ */
+#ifndef NO_CS4612
+#define DPCIC_C_MASK                            0x0000000F
+#define DPCIC_C_IOREAD                          0x00000002
+#define DPCIC_C_IOWRITE                         0x00000003
+#define DPCIC_BE_MASK                           0x000000F0
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI request register.
+ */
+#ifndef NO_CS4612
+#define PCPCIR_RDC_MASK                         0x00000007
+#define PCPCIR_C_MASK                           0x00007000
+#define PCPCIR_REQ                              0x00008000
+#define PCPCIR_RDC_SHIFT                        0
+#define PCPCIR_C_SHIFT                          12
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI grant register.
+ */ 
+#ifndef NO_CS4612
+#define PCPCIG_GDC_MASK                         0x00000007
+#define PCPCIG_VL                               0x00008000
+#define PCPCIG_GDC_SHIFT                        0
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI master enable
+ *  register.
+ */
+#ifndef NO_CS4612
+#define PCPCIEN_EN                              0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the extended PCI power
+ *  management control register.
+ */
+#ifndef NO_CS4612
+#define EPCIPMC_GWU                             0x00000001
+#define EPCIPMC_FSPC                            0x00000002
+#endif 
+
+/*
+ *  The following defines are for the flags in the SP control register.
+ */
+#define SPCR_RUN                                0x00000001
+#define SPCR_STPFR                              0x00000002
+#define SPCR_RUNFR                              0x00000004
+#define SPCR_TICK                               0x00000008
+#define SPCR_DRQEN                              0x00000020
+#define SPCR_RSTSP                              0x00000040
+#define SPCR_OREN                               0x00000080
+#ifndef NO_CS4612
+#define SPCR_PCIINT                             0x00000100
+#define SPCR_OINTD                              0x00000200
+#define SPCR_CRE                                0x00008000
+#endif
+
+/*
+ *  The following defines are for the flags in the debug index register.
+ */
+#define DREG_REGID_MASK                         0x0000007F
+#define DREG_DEBUG                              0x00000080
+#define DREG_RGBK_MASK                          0x00000700
+#define DREG_TRAP                               0x00000800
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_TRAPX                              0x00001000
+#endif
+#endif
+#define DREG_REGID_SHIFT                        0
+#define DREG_RGBK_SHIFT                         8
+#define DREG_RGBK_REGID_MASK                    0x0000077F
+#define DREG_REGID_R0                           0x00000010
+#define DREG_REGID_R1                           0x00000011
+#define DREG_REGID_R2                           0x00000012
+#define DREG_REGID_R3                           0x00000013
+#define DREG_REGID_R4                           0x00000014
+#define DREG_REGID_R5                           0x00000015
+#define DREG_REGID_R6                           0x00000016
+#define DREG_REGID_R7                           0x00000017
+#define DREG_REGID_R8                           0x00000018
+#define DREG_REGID_R9                           0x00000019
+#define DREG_REGID_RA                           0x0000001A
+#define DREG_REGID_RB                           0x0000001B
+#define DREG_REGID_RC                           0x0000001C
+#define DREG_REGID_RD                           0x0000001D
+#define DREG_REGID_RE                           0x0000001E
+#define DREG_REGID_RF                           0x0000001F
+#define DREG_REGID_RA_BUS_LOW                   0x00000020
+#define DREG_REGID_RA_BUS_HIGH                  0x00000038
+#define DREG_REGID_YBUS_LOW                     0x00000050
+#define DREG_REGID_YBUS_HIGH                    0x00000058
+#define DREG_REGID_TRAP_0                       0x00000100
+#define DREG_REGID_TRAP_1                       0x00000101
+#define DREG_REGID_TRAP_2                       0x00000102
+#define DREG_REGID_TRAP_3                       0x00000103
+#define DREG_REGID_TRAP_4                       0x00000104
+#define DREG_REGID_TRAP_5                       0x00000105
+#define DREG_REGID_TRAP_6                       0x00000106
+#define DREG_REGID_TRAP_7                       0x00000107
+#define DREG_REGID_INDIRECT_ADDRESS             0x0000010E
+#define DREG_REGID_TOP_OF_STACK                 0x0000010F
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_8                       0x00000110
+#define DREG_REGID_TRAP_9                       0x00000111
+#define DREG_REGID_TRAP_10                      0x00000112
+#define DREG_REGID_TRAP_11                      0x00000113
+#define DREG_REGID_TRAP_12                      0x00000114
+#define DREG_REGID_TRAP_13                      0x00000115
+#define DREG_REGID_TRAP_14                      0x00000116
+#define DREG_REGID_TRAP_15                      0x00000117
+#define DREG_REGID_TRAP_16                      0x00000118
+#define DREG_REGID_TRAP_17                      0x00000119
+#define DREG_REGID_TRAP_18                      0x0000011A
+#define DREG_REGID_TRAP_19                      0x0000011B
+#define DREG_REGID_TRAP_20                      0x0000011C
+#define DREG_REGID_TRAP_21                      0x0000011D
+#define DREG_REGID_TRAP_22                      0x0000011E
+#define DREG_REGID_TRAP_23                      0x0000011F
+#endif
+#endif
+#define DREG_REGID_RSA0_LOW                     0x00000200
+#define DREG_REGID_RSA0_HIGH                    0x00000201
+#define DREG_REGID_RSA1_LOW                     0x00000202
+#define DREG_REGID_RSA1_HIGH                    0x00000203
+#define DREG_REGID_RSA2                         0x00000204
+#define DREG_REGID_RSA3                         0x00000205
+#define DREG_REGID_RSI0_LOW                     0x00000206
+#define DREG_REGID_RSI0_HIGH                    0x00000207
+#define DREG_REGID_RSI1                         0x00000208
+#define DREG_REGID_RSI2                         0x00000209
+#define DREG_REGID_SAGUSTATUS                   0x0000020A
+#define DREG_REGID_RSCONFIG01_LOW               0x0000020B
+#define DREG_REGID_RSCONFIG01_HIGH              0x0000020C
+#define DREG_REGID_RSCONFIG23_LOW               0x0000020D
+#define DREG_REGID_RSCONFIG23_HIGH              0x0000020E
+#define DREG_REGID_RSDMA01E                     0x0000020F
+#define DREG_REGID_RSDMA23E                     0x00000210
+#define DREG_REGID_RSD0_LOW                     0x00000211
+#define DREG_REGID_RSD0_HIGH                    0x00000212
+#define DREG_REGID_RSD1_LOW                     0x00000213
+#define DREG_REGID_RSD1_HIGH                    0x00000214
+#define DREG_REGID_RSD2_LOW                     0x00000215
+#define DREG_REGID_RSD2_HIGH                    0x00000216
+#define DREG_REGID_RSD3_LOW                     0x00000217
+#define DREG_REGID_RSD3_HIGH                    0x00000218
+#define DREG_REGID_SRAR_HIGH                    0x0000021A
+#define DREG_REGID_SRAR_LOW                     0x0000021B
+#define DREG_REGID_DMA_STATE                    0x0000021C
+#define DREG_REGID_CURRENT_DMA_STREAM           0x0000021D
+#define DREG_REGID_NEXT_DMA_STREAM              0x0000021E
+#define DREG_REGID_CPU_STATUS                   0x00000300
+#define DREG_REGID_MAC_MODE                     0x00000301
+#define DREG_REGID_STACK_AND_REPEAT             0x00000302
+#define DREG_REGID_INDEX0                       0x00000304
+#define DREG_REGID_INDEX1                       0x00000305
+#define DREG_REGID_DMA_STATE_0_3                0x00000400
+#define DREG_REGID_DMA_STATE_4_7                0x00000404
+#define DREG_REGID_DMA_STATE_8_11               0x00000408
+#define DREG_REGID_DMA_STATE_12_15              0x0000040C
+#define DREG_REGID_DMA_STATE_16_19              0x00000410
+#define DREG_REGID_DMA_STATE_20_23              0x00000414
+#define DREG_REGID_DMA_STATE_24_27              0x00000418
+#define DREG_REGID_DMA_STATE_28_31              0x0000041C
+#define DREG_REGID_DMA_STATE_32_35              0x00000420
+#define DREG_REGID_DMA_STATE_36_39              0x00000424
+#define DREG_REGID_DMA_STATE_40_43              0x00000428
+#define DREG_REGID_DMA_STATE_44_47              0x0000042C
+#define DREG_REGID_DMA_STATE_48_51              0x00000430
+#define DREG_REGID_DMA_STATE_52_55              0x00000434
+#define DREG_REGID_DMA_STATE_56_59              0x00000438
+#define DREG_REGID_DMA_STATE_60_63              0x0000043C
+#define DREG_REGID_DMA_STATE_64_67              0x00000440
+#define DREG_REGID_DMA_STATE_68_71              0x00000444
+#define DREG_REGID_DMA_STATE_72_75              0x00000448
+#define DREG_REGID_DMA_STATE_76_79              0x0000044C
+#define DREG_REGID_DMA_STATE_80_83              0x00000450
+#define DREG_REGID_DMA_STATE_84_87              0x00000454
+#define DREG_REGID_DMA_STATE_88_91              0x00000458
+#define DREG_REGID_DMA_STATE_92_95              0x0000045C
+#define DREG_REGID_TRAP_SELECT                  0x00000500
+#define DREG_REGID_TRAP_WRITE_0                 0x00000500
+#define DREG_REGID_TRAP_WRITE_1                 0x00000501
+#define DREG_REGID_TRAP_WRITE_2                 0x00000502
+#define DREG_REGID_TRAP_WRITE_3                 0x00000503
+#define DREG_REGID_TRAP_WRITE_4                 0x00000504
+#define DREG_REGID_TRAP_WRITE_5                 0x00000505
+#define DREG_REGID_TRAP_WRITE_6                 0x00000506
+#define DREG_REGID_TRAP_WRITE_7                 0x00000507
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_WRITE_8                 0x00000510
+#define DREG_REGID_TRAP_WRITE_9                 0x00000511
+#define DREG_REGID_TRAP_WRITE_10                0x00000512
+#define DREG_REGID_TRAP_WRITE_11                0x00000513
+#define DREG_REGID_TRAP_WRITE_12                0x00000514
+#define DREG_REGID_TRAP_WRITE_13                0x00000515
+#define DREG_REGID_TRAP_WRITE_14                0x00000516
+#define DREG_REGID_TRAP_WRITE_15                0x00000517
+#define DREG_REGID_TRAP_WRITE_16                0x00000518
+#define DREG_REGID_TRAP_WRITE_17                0x00000519
+#define DREG_REGID_TRAP_WRITE_18                0x0000051A
+#define DREG_REGID_TRAP_WRITE_19                0x0000051B
+#define DREG_REGID_TRAP_WRITE_20                0x0000051C
+#define DREG_REGID_TRAP_WRITE_21                0x0000051D
+#define DREG_REGID_TRAP_WRITE_22                0x0000051E
+#define DREG_REGID_TRAP_WRITE_23                0x0000051F
+#endif
+#endif
+#define DREG_REGID_MAC0_ACC0_LOW                0x00000600
+#define DREG_REGID_MAC0_ACC1_LOW                0x00000601
+#define DREG_REGID_MAC0_ACC2_LOW                0x00000602
+#define DREG_REGID_MAC0_ACC3_LOW                0x00000603
+#define DREG_REGID_MAC1_ACC0_LOW                0x00000604
+#define DREG_REGID_MAC1_ACC1_LOW                0x00000605
+#define DREG_REGID_MAC1_ACC2_LOW                0x00000606
+#define DREG_REGID_MAC1_ACC3_LOW                0x00000607
+#define DREG_REGID_MAC0_ACC0_MID                0x00000608
+#define DREG_REGID_MAC0_ACC1_MID                0x00000609
+#define DREG_REGID_MAC0_ACC2_MID                0x0000060A
+#define DREG_REGID_MAC0_ACC3_MID                0x0000060B
+#define DREG_REGID_MAC1_ACC0_MID                0x0000060C
+#define DREG_REGID_MAC1_ACC1_MID                0x0000060D
+#define DREG_REGID_MAC1_ACC2_MID                0x0000060E
+#define DREG_REGID_MAC1_ACC3_MID                0x0000060F
+#define DREG_REGID_MAC0_ACC0_HIGH               0x00000610
+#define DREG_REGID_MAC0_ACC1_HIGH               0x00000611
+#define DREG_REGID_MAC0_ACC2_HIGH               0x00000612
+#define DREG_REGID_MAC0_ACC3_HIGH               0x00000613
+#define DREG_REGID_MAC1_ACC0_HIGH               0x00000614
+#define DREG_REGID_MAC1_ACC1_HIGH               0x00000615
+#define DREG_REGID_MAC1_ACC2_HIGH               0x00000616
+#define DREG_REGID_MAC1_ACC3_HIGH               0x00000617
+#define DREG_REGID_RSHOUT_LOW                   0x00000620
+#define DREG_REGID_RSHOUT_MID                   0x00000628
+#define DREG_REGID_RSHOUT_HIGH                  0x00000630
+
+/*
+ *  The following defines are for the flags in the DMA stream requestor write
+ */
+#define DSRWP_DSR_MASK                          0x0000000F
+#define DSRWP_DSR_BG_RQ                         0x00000001
+#define DSRWP_DSR_PRIORITY_MASK                 0x00000006
+#define DSRWP_DSR_PRIORITY_0                    0x00000000
+#define DSRWP_DSR_PRIORITY_1                    0x00000002
+#define DSRWP_DSR_PRIORITY_2                    0x00000004
+#define DSRWP_DSR_PRIORITY_3                    0x00000006
+#define DSRWP_DSR_RQ_PENDING                    0x00000008
+
+/*
+ *  The following defines are for the flags in the trap write port register.
+ */
+#define TWPR_TW_MASK                            0x0000FFFF
+#define TWPR_TW_SHIFT                           0
+
+/*
+ *  The following defines are for the flags in the stack pointer write
+ *  register.
+ */
+#define SPWR_STKP_MASK                          0x0000000F
+#define SPWR_STKP_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the SP interrupt register.
+ */
+#define SPIR_FRI                                0x00000001
+#define SPIR_DOI                                0x00000002
+#define SPIR_GPI2                               0x00000004
+#define SPIR_GPI3                               0x00000008
+#define SPIR_IP0                                0x00000010
+#define SPIR_IP1                                0x00000020
+#define SPIR_IP2                                0x00000040
+#define SPIR_IP3                                0x00000080
+
+/*
+ *  The following defines are for the flags in the functional group 1 register.
+ */
+#define FGR1_F1S_MASK                           0x0000FFFF
+#define FGR1_F1S_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the SP clock status register.
+ */
+#define SPCS_FRI                                0x00000001
+#define SPCS_DOI                                0x00000002
+#define SPCS_GPI2                               0x00000004
+#define SPCS_GPI3                               0x00000008
+#define SPCS_IP0                                0x00000010
+#define SPCS_IP1                                0x00000020
+#define SPCS_IP2                                0x00000040
+#define SPCS_IP3                                0x00000080
+#define SPCS_SPRUN                              0x00000100
+#define SPCS_SLEEP                              0x00000200
+#define SPCS_FG                                 0x00000400
+#define SPCS_ORUN                               0x00000800
+#define SPCS_IRQ                                0x00001000
+#define SPCS_FGN_MASK                           0x0000E000
+#define SPCS_FGN_SHIFT                          13
+
+/*
+ *  The following defines are for the flags in the SP DMA requestor status
+ *  register.
+ */
+#define SDSR_DCS_MASK                           0x000000FF
+#define SDSR_DCS_SHIFT                          0
+#define SDSR_DCS_NONE                           0x00000007
+
+/*
+ *  The following defines are for the flags in the frame timer register.
+ */
+#define FRMT_FTV_MASK                           0x0000FFFF
+#define FRMT_FTV_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the frame timer current count
+ *  register.
+ */
+#define FRCC_FCC_MASK                           0x0000FFFF
+#define FRCC_FCC_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the frame timer save count
+ *  register.
+ */
+#define FRSC_FCS_MASK                           0x0000FFFF
+#define FRSC_FCS_SHIFT                          0
+
+/*
+ *  The following define the various flags stored in the scatter/gather
+ *  descriptors.
+ */
+#define DMA_SG_NEXT_ENTRY_MASK                  0x00000FF8
+#define DMA_SG_SAMPLE_END_MASK                  0x0FFF0000
+#define DMA_SG_SAMPLE_END_FLAG                  0x10000000
+#define DMA_SG_LOOP_END_FLAG                    0x20000000
+#define DMA_SG_SIGNAL_END_FLAG                  0x40000000
+#define DMA_SG_SIGNAL_PAGE_FLAG                 0x80000000
+#define DMA_SG_NEXT_ENTRY_SHIFT                 3
+#define DMA_SG_SAMPLE_END_SHIFT                 16
+
+/*
+ *  The following define the offsets of the fields within the on-chip generic
+ *  DMA requestor.
+ */
+#define DMA_RQ_CONTROL1                         0x00000000
+#define DMA_RQ_CONTROL2                         0x00000004
+#define DMA_RQ_SOURCE_ADDR                      0x00000008
+#define DMA_RQ_DESTINATION_ADDR                 0x0000000C
+#define DMA_RQ_NEXT_PAGE_ADDR                   0x00000010
+#define DMA_RQ_NEXT_PAGE_SGDESC                 0x00000014
+#define DMA_RQ_LOOP_START_ADDR                  0x00000018
+#define DMA_RQ_POST_LOOP_ADDR                   0x0000001C
+#define DMA_RQ_PAGE_MAP_ADDR                    0x00000020
+
+/*
+ *  The following defines are for the flags in the first control word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C1_COUNT_MASK                    0x000003FF
+#define DMA_RQ_C1_DESTINATION_SCATTER           0x00001000
+#define DMA_RQ_C1_SOURCE_GATHER                 0x00002000
+#define DMA_RQ_C1_DONE_FLAG                     0x00004000
+#define DMA_RQ_C1_OPTIMIZE_STATE                0x00008000
+#define DMA_RQ_C1_SAMPLE_END_STATE_MASK         0x00030000
+#define DMA_RQ_C1_FULL_PAGE                     0x00000000
+#define DMA_RQ_C1_BEFORE_SAMPLE_END             0x00010000
+#define DMA_RQ_C1_PAGE_MAP_ERROR                0x00020000
+#define DMA_RQ_C1_AT_SAMPLE_END                 0x00030000
+#define DMA_RQ_C1_LOOP_END_STATE_MASK           0x000C0000
+#define DMA_RQ_C1_NOT_LOOP_END                  0x00000000
+#define DMA_RQ_C1_BEFORE_LOOP_END               0x00040000
+#define DMA_RQ_C1_2PAGE_LOOP_BEGIN              0x00080000
+#define DMA_RQ_C1_LOOP_BEGIN                    0x000C0000
+#define DMA_RQ_C1_PAGE_MAP_MASK                 0x00300000
+#define DMA_RQ_C1_PM_NONE_PENDING               0x00000000
+#define DMA_RQ_C1_PM_NEXT_PENDING               0x00100000
+#define DMA_RQ_C1_PM_RESERVED                   0x00200000
+#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING          0x00300000
+#define DMA_RQ_C1_WRITEBACK_DEST_FLAG           0x00400000
+#define DMA_RQ_C1_WRITEBACK_SRC_FLAG            0x00800000
+#define DMA_RQ_C1_DEST_SIZE_MASK                0x07000000
+#define DMA_RQ_C1_DEST_LINEAR                   0x00000000
+#define DMA_RQ_C1_DEST_MOD16                    0x01000000
+#define DMA_RQ_C1_DEST_MOD32                    0x02000000
+#define DMA_RQ_C1_DEST_MOD64                    0x03000000
+#define DMA_RQ_C1_DEST_MOD128                   0x04000000
+#define DMA_RQ_C1_DEST_MOD256                   0x05000000
+#define DMA_RQ_C1_DEST_MOD512                   0x06000000
+#define DMA_RQ_C1_DEST_MOD1024                  0x07000000
+#define DMA_RQ_C1_DEST_ON_HOST                  0x08000000
+#define DMA_RQ_C1_SOURCE_SIZE_MASK              0x70000000
+#define DMA_RQ_C1_SOURCE_LINEAR                 0x00000000
+#define DMA_RQ_C1_SOURCE_MOD16                  0x10000000
+#define DMA_RQ_C1_SOURCE_MOD32                  0x20000000
+#define DMA_RQ_C1_SOURCE_MOD64                  0x30000000
+#define DMA_RQ_C1_SOURCE_MOD128                 0x40000000
+#define DMA_RQ_C1_SOURCE_MOD256                 0x50000000
+#define DMA_RQ_C1_SOURCE_MOD512                 0x60000000
+#define DMA_RQ_C1_SOURCE_MOD1024                0x70000000
+#define DMA_RQ_C1_SOURCE_ON_HOST                0x80000000
+#define DMA_RQ_C1_COUNT_SHIFT                   0
+
+/*
+ *  The following defines are for the flags in the second control word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK          0x0000003F
+#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK           0x00000300
+#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL             0x00000000
+#define DMA_RQ_C2_SIGNAL_EVERY_DMA              0x00000100
+#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG        0x00000200
+#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG          0x00000300
+#define DMA_RQ_C2_AUDIO_CONVERT_MASK            0x0000F000
+#define DMA_RQ_C2_AC_NONE                       0x00000000
+#define DMA_RQ_C2_AC_8_TO_16_BIT                0x00001000
+#define DMA_RQ_C2_AC_MONO_TO_STEREO             0x00002000
+#define DMA_RQ_C2_AC_ENDIAN_CONVERT             0x00004000
+#define DMA_RQ_C2_AC_SIGNED_CONVERT             0x00008000
+#define DMA_RQ_C2_LOOP_END_MASK                 0x0FFF0000
+#define DMA_RQ_C2_LOOP_MASK                     0x30000000
+#define DMA_RQ_C2_NO_LOOP                       0x00000000
+#define DMA_RQ_C2_ONE_PAGE_LOOP                 0x10000000
+#define DMA_RQ_C2_TWO_PAGE_LOOP                 0x20000000
+#define DMA_RQ_C2_MULTI_PAGE_LOOP               0x30000000
+#define DMA_RQ_C2_SIGNAL_LOOP_BACK              0x40000000
+#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE        0x80000000
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT         0
+#define DMA_RQ_C2_LOOP_END_SHIFT                16
+
+/*
+ *  The following defines are for the flags in the source and destination words
+ *  of the on-chip generic DMA requestor.
+ */
+#define DMA_RQ_SD_ADDRESS_MASK                  0x0000FFFF
+#define DMA_RQ_SD_MEMORY_ID_MASK                0x000F0000
+#define DMA_RQ_SD_SP_PARAM_ADDR                 0x00000000
+#define DMA_RQ_SD_SP_SAMPLE_ADDR                0x00010000
+#define DMA_RQ_SD_SP_PROGRAM_ADDR               0x00020000
+#define DMA_RQ_SD_SP_DEBUG_ADDR                 0x00030000
+#define DMA_RQ_SD_OMNIMEM_ADDR                  0x000E0000
+#define DMA_RQ_SD_END_FLAG                      0x40000000
+#define DMA_RQ_SD_ERROR_FLAG                    0x80000000
+#define DMA_RQ_SD_ADDRESS_SHIFT                 0
+
+/*
+ *  The following defines are for the flags in the page map address word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK   0x00000FF8
+#define DMA_RQ_PMA_PAGE_TABLE_MASK              0xFFFFF000
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT  3
+#define DMA_RQ_PMA_PAGE_TABLE_SHIFT             12
+
+#define BA1_VARIDEC_BUF_1       0x000
+
+#define BA1_PDTC                0x0c0    /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */
+#define BA1_PFIE                0x0c4    /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */
+#define BA1_PBA                 0x0c8    /* BA1_PLAY_BUFFER_ADDRESS */
+#define BA1_PVOL                0x0f8    /* BA1_PLAY_VOLUME_REG */
+#define BA1_PSRC                0x288    /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_PCTL                0x2a4    /* BA1_PLAY_CONTROL_REG */
+#define BA1_PPI                 0x2b4    /* BA1_PLAY_PHASE_INCREMENT_REG */
+
+#define BA1_CCTL                0x064    /* BA1_CAPTURE_CONTROL_REG */
+#define BA1_CIE                 0x104    /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */
+#define BA1_CBA                 0x10c    /* BA1_CAPTURE_BUFFER_ADDRESS */
+#define BA1_CSRC                0x2c8    /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_CCI                 0x2d8    /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */
+#define BA1_CD                  0x2e0    /* BA1_CAPTURE_DELAY_REG */
+#define BA1_CPI                 0x2f4    /* BA1_CAPTURE_PHASE_INCREMENT_REG */
+#define BA1_CVOL                0x2f8    /* BA1_CAPTURE_VOLUME_REG */
+
+#define BA1_CFG1                0x134    /* BA1_CAPTURE_FRAME_GROUP_1_REG */
+#define BA1_CFG2                0x138    /* BA1_CAPTURE_FRAME_GROUP_2_REG */
+#define BA1_CCST                0x13c    /* BA1_CAPTURE_CONSTANT_REG */
+#define BA1_CSPB                0x340    /* BA1_CAPTURE_SPB_ADDRESS */
+
+/*
+ *
+ */
+
+#define CS46XX_MODE_OUTPUT	(1<<0)	 /* MIDI UART - output */ 
+#define CS46XX_MODE_INPUT	(1<<1)	 /* MIDI UART - input */
+
+/*
+ *
+ */
+
+#define SAVE_REG_MAX             0x10
+#define POWER_DOWN_ALL         0x7f0f
+
+/*
+ *
+ */
+
+typedef struct _snd_cs46xx cs46xx_t;
+
+typedef struct {
+	char name[24];
+	unsigned long base;
+	unsigned long remap_addr;
+	unsigned long size;
+	struct resource *resource;
+	void *proc_entry;
+} snd_cs46xx_region_t;
+
+struct _snd_cs46xx {
+	int irq;
+	unsigned long ba0_addr;
+	unsigned long ba1_addr;
+	union {
+		struct {
+			snd_cs46xx_region_t ba0;
+			snd_cs46xx_region_t data0;
+			snd_cs46xx_region_t data1;
+			snd_cs46xx_region_t pmem;
+			snd_cs46xx_region_t reg;
+		} name;
+		snd_cs46xx_region_t idx[5];
+	} region;
+
+	unsigned int mode;
+	
+	struct {
+		unsigned char *hw_area;
+		dma_addr_t hw_addr;	/* PCI bus address, not accessible */
+		unsigned long hw_size;
+
+		unsigned int ctl;
+		unsigned int shift;	/* Shift count to trasform frames in bytes */
+		unsigned int sw_bufsize;
+		unsigned int sw_data;	/* Offset to next dst (or src) in sw ring buffer */
+		unsigned int sw_io;
+		int sw_ready;		/* Bytes ready to be transferred to/from hw */
+		unsigned int hw_data;	/* Offset to next dst (or src) in hw ring buffer */
+		unsigned int hw_io;	/* Ring buffer hw pointer */
+		int hw_ready;		/* Bytes ready for play (or captured) in hw ring buffer */
+		size_t appl_ptr;	/* Last seen appl_ptr */
+		snd_pcm_substream_t *substream;
+	} play, capt;
+
+
+	ac97_t *ac97;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *midi_input;
+	snd_rawmidi_substream_t *midi_output;
+
+	spinlock_t reg_lock;
+	unsigned int midcr;
+	unsigned int uartm;
+	snd_info_entry_t *proc_entry;
+
+	int amplifier;
+	void (*amplifier_ctrl)(cs46xx_t *, int);
+	void (*active_ctrl)(cs46xx_t *, int);
+	struct pci_dev *acpi_dev;
+	int acpi_port;
+	snd_kcontrol_t *eapd_switch; /* for amplifier hack */
+
+#ifdef CONFIG_PM
+	struct pm_dev *pm_dev;
+#endif
+};
+
+int snd_cs46xx_create(snd_card_t *card,
+		      struct pci_dev *pci,
+		      int external_amp, int thinkpad,
+		      cs46xx_t **rcodec);
+
+int snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t **rpcm);
+int snd_cs46xx_mixer(cs46xx_t *chip);
+int snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rmidi);
+
+#ifdef CONFIG_PM
+void snd_cs46xx_suspend(cs46xx_t *chip);
+void snd_cs46xx_resume(cs46xx_t *chip);
+#endif
+
+#endif /* __SOUND_CS46XX_H */
diff -Nru linux/include/sound/cs8403.h linux-2.4.19-pre5-mjc/include/sound/cs8403.h
--- linux/include/sound/cs8403.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/cs8403.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,257 @@
+#ifndef __SOUND_CS8403_H
+#define __SOUND_CS8403_H
+
+/*
+ *  Routines for Cirrus Logic CS8403/CS8404A IEC958 (S/PDIF) Transmitter
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef SND_CS8403
+
+#ifndef SND_CS8403_DECL
+#define SND_CS8403_DECL static
+#endif
+#ifndef SND_CS8403_DECODE
+#define SND_CS8403_DECODE snd_cs8403_decode_spdif_bits
+#endif
+#ifndef SND_CS8403_ENCODE
+#define SND_CS8403_ENCODE snd_cs8403_encode_spdif_bits
+#endif
+
+
+SND_CS8403_DECL void SND_CS8403_DECODE(snd_aes_iec958_t *diga, unsigned char bits)
+{
+	if (bits & 0x01) {	/* consumer */
+		if (!(bits & 0x02))
+			diga->status[0] |= IEC958_AES0_NONAUDIO;
+		if (!(bits & 0x08))
+			diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
+		switch (bits & 0x10) {
+		case 0x10: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; break;
+		case 0x00: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; break;
+		}
+		if (!(bits & 0x80))
+			diga->status[1] |= IEC958_AES1_CON_ORIGINAL;
+		switch (bits & 0x60) {
+		case 0x00: diga->status[1] |= IEC958_AES1_CON_MAGNETIC_ID; break;
+		case 0x20: diga->status[1] |= IEC958_AES1_CON_DIGDIGCONV_ID; break;
+		case 0x40: diga->status[1] |= IEC958_AES1_CON_LASEROPT_ID; break;
+		case 0x60: diga->status[1] |= IEC958_AES1_CON_GENERAL; break;
+		}
+		switch (bits & 0x06) {
+		case 0x00: diga->status[3] |= IEC958_AES3_CON_FS_44100; break;
+		case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_48000; break;
+		case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_32000; break;
+		}
+	} else {
+		diga->status[0] = IEC958_AES0_PROFESSIONAL;
+		switch (bits & 0x18) {
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break;
+		case 0x10: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break;
+		case 0x08: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break;
+		case 0x18: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break;
+		}
+		switch (bits & 0x60) {
+		case 0x20: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break;
+		case 0x40: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break;
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break;
+		case 0x60: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break;
+		}
+		if (bits & 0x80)
+			diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC;
+	}
+}
+
+SND_CS8403_DECL unsigned char SND_CS8403_ENCODE(snd_aes_iec958_t *diga)
+{
+	unsigned char bits;
+
+	if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) {
+		bits = 0x01;	/* consumer mode */
+		if (diga->status[0] & IEC958_AES0_NONAUDIO)
+			bits &= ~0x02;
+		else
+			bits |= 0x02;
+		if (diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT)
+			bits &= ~0x08;
+		else
+			bits |= 0x08;
+		switch (diga->status[0] & IEC958_AES0_CON_EMPHASIS) {
+		default:
+		case IEC958_AES0_CON_EMPHASIS_NONE: bits |= 0x10; break;
+		case IEC958_AES0_CON_EMPHASIS_5015: bits |= 0x00; break;
+		}
+		if (diga->status[1] & IEC958_AES1_CON_ORIGINAL)
+			bits &= ~0x80;
+		else
+			bits |= 0x80;
+		if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL)
+			bits |= 0x60;
+		else {
+			switch(diga->status[1] & IEC958_AES1_CON_MAGNETIC_MASK) {
+			case IEC958_AES1_CON_MAGNETIC_ID:
+				bits |= 0x00; break;
+			case IEC958_AES1_CON_DIGDIGCONV_ID:
+				bits |= 0x20; break;
+			default:
+			case IEC958_AES1_CON_LASEROPT_ID:
+				bits |= 0x40; break;
+			}
+		}
+		switch (diga->status[3] & IEC958_AES3_CON_FS) {
+		default:
+		case IEC958_AES3_CON_FS_44100: bits |= 0x00; break;
+		case IEC958_AES3_CON_FS_48000: bits |= 0x02; break;
+		case IEC958_AES3_CON_FS_32000: bits |= 0x04; break;
+		}
+	} else {
+		bits = 0x00;	/* professional mode */
+		if (diga->status[0] & IEC958_AES0_NONAUDIO)
+			bits &= ~0x02;
+		else
+			bits |= 0x02;
+		/* CHECKME: I'm not sure about the bit order in val here */
+		switch (diga->status[0] & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_32000:	bits |= 0x00; break;
+		case IEC958_AES0_PRO_FS_44100:	bits |= 0x10; break;	/* 44.1kHz */
+		case IEC958_AES0_PRO_FS_48000:	bits |= 0x08; break;	/* 48kHz */
+		default:
+		case IEC958_AES0_PRO_FS_NOTID: bits |= 0x18; break;
+		}
+		switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) {
+		case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x20; break;
+		case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x40; break;
+		case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break;
+		default:
+		case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x60; break;
+		}
+		switch (diga->status[1] & IEC958_AES1_PRO_MODE) {
+		case IEC958_AES1_PRO_MODE_TWO:
+		case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break;
+		default: bits |= 0x80; break;
+		}
+	}
+	return bits;
+}
+
+#endif /* SND_CS8403 */
+
+#ifdef SND_CS8404
+
+#ifndef SND_CS8404_DECL
+#define SND_CS8404_DECL static
+#endif
+#ifndef SND_CS8404_DECODE
+#define SND_CS8404_DECODE snd_cs8404_decode_spdif_bits
+#endif
+#ifndef SND_CS8404_ENCODE
+#define SND_CS8404_ENCODE snd_cs8404_encode_spdif_bits
+#endif
+
+
+SND_CS8404_DECL void SND_CS8404_DECODE(snd_aes_iec958_t *diga, unsigned char bits)
+{
+	if (bits & 0x10) {	/* consumer */
+		if (!(bits & 0x20))
+			diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
+		if (!(bits & 0x40))
+			diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015;
+		if (!(bits & 0x80))
+			diga->status[1] |= IEC958_AES1_CON_ORIGINAL;
+		switch (bits & 0x03) {
+		case 0x00: diga->status[1] |= IEC958_AES1_CON_DAT; break;
+		case 0x03: diga->status[1] |= IEC958_AES1_CON_GENERAL; break;
+		}
+		switch (bits & 0x06) {
+		case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_32000; break;
+		case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_48000; break;
+		case 0x06: diga->status[3] |= IEC958_AES3_CON_FS_44100; break;
+		}
+	} else {
+		diga->status[0] = IEC958_AES0_PROFESSIONAL;
+		if (!(bits & 0x04))
+			diga->status[0] |= IEC958_AES0_NONAUDIO;
+		switch (bits & 0x60) {
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break;
+		case 0x40: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break;
+		case 0x20: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break;
+		case 0x60: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break;
+		}
+		switch (bits & 0x03) {
+		case 0x02: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break;
+		case 0x01: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break;
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break;
+		case 0x03: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break;
+		}
+		if (!(bits & 0x80))
+			diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC;
+	}
+}
+
+SND_CS8404_DECL unsigned char SND_CS8404_ENCODE(snd_aes_iec958_t *diga)
+{
+	unsigned char bits;
+
+	if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) {
+		bits = 0x10;	/* consumer mode */
+		if (!(diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT))
+			bits |= 0x20;
+		if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_NONE)
+			bits |= 0x40;
+		if (!(diga->status[1] & IEC958_AES1_CON_ORIGINAL))
+			bits |= 0x80;
+		if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL)
+			bits |= 0x03;
+		switch (diga->status[3] & IEC958_AES3_CON_FS) {
+		default:
+		case IEC958_AES3_CON_FS_44100: bits |= 0x06; break;
+		case IEC958_AES3_CON_FS_48000: bits |= 0x04; break;
+		case IEC958_AES3_CON_FS_32000: bits |= 0x02; break;
+		}
+	} else {
+		bits = 0x00;	/* professional mode */
+		if (!(diga->status[0] & IEC958_AES0_NONAUDIO))
+			bits |= 0x04;
+		switch (diga->status[0] & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_32000:	bits |= 0x00; break;
+		case IEC958_AES0_PRO_FS_44100:	bits |= 0x40; break;	/* 44.1kHz */
+		case IEC958_AES0_PRO_FS_48000:	bits |= 0x20; break;	/* 48kHz */
+		default:
+		case IEC958_AES0_PRO_FS_NOTID:	bits |= 0x00; break;
+		}
+		switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) {
+		case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x02; break;
+		case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x01; break;
+		case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break;
+		default:
+		case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x03; break;
+		}
+		switch (diga->status[1] & IEC958_AES1_PRO_MODE) {
+		case IEC958_AES1_PRO_MODE_TWO:
+		case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break;
+		default: bits |= 0x80; break;
+		}
+	}
+	return bits;
+}
+
+#endif /* SND_CS8404 */
+
+#endif /* __SOUND_CS8403_H */
diff -Nru linux/include/sound/cs8427.h linux-2.4.19-pre5-mjc/include/sound/cs8427.h
--- linux/include/sound/cs8427.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/cs8427.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,195 @@
+#ifndef __SOUND_CS8427_H
+#define __SOUND_CS8427_H
+
+/*
+ *  Routines for Cirrus Logic CS8427
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/i2c.h>
+
+#define CS8427_BASE_ADDR	0x10	/* base I2C address */
+
+#define CS8427_REG_AUTOINC	0x80	/* flag - autoincrement */
+#define CS8427_REG_CONTROL1	0x01
+#define CS8427_REG_CONTROL2	0x02
+#define CS8427_REG_DATAFLOW	0x03
+#define CS8427_REG_CLOCKSOURCE	0x04
+#define CS8427_REG_SERIALINPUT	0x05
+#define CS8427_REG_SERIALOUTPUT	0x06
+#define CS8427_REG_INT1STATUS	0x07
+#define CS8427_REG_INT2STATUS	0x08
+#define CS8427_REG_INT1MASK	0x09
+#define CS8427_REG_INT1MODEMSB	0x0a
+#define CS8427_REG_INT1MODELSB	0x0b
+#define CS8427_REG_INT2MASK	0x0c
+#define CS8427_REG_INT2MODEMSB	0x0d
+#define CS8427_REG_INT2MODELSB	0x0e
+#define CS8427_REG_RECVCSDATA	0x0f
+#define CS8427_REG_RECVERRORS	0x10
+#define CS8427_REG_RECVERRMASK	0x11
+#define CS8427_REG_CSDATABUF	0x12
+#define CS8427_REG_UDATABUF	0x13
+#define CS8427_REG_QSUBCODE	0x14	/* 0x14-0x1d (10 bytes) */
+#define CS8427_REG_OMCKRMCKRATIO 0x1e
+#define CS8427_REG_CORU_DATABUF	0x20
+#define CS8427_REG_ID_AND_VER	0x7f
+
+/* CS8427_REG_CONTROL1 bits */
+#define CS8427_SWCLK		(1<<7)	/* 0 = RMCK default, 1 = OMCK output on RMCK pin */
+#define CS8427_VSET		(1<<6)	/* 0 = valid PCM data, 1 = invalid PCM data */
+#define CS8427_MUTESAO		(1<<5)	/* mute control for the serial audio output port, 0 = disabled, 1 = enabled */
+#define CS8427_MUTEAES		(1<<4)	/* mute control for the AES transmitter output, 0 = disabled, 1 = enabled */
+#define CS8427_INTMASK		(3<<1)	/* interrupt output pin setup mask */
+#define CS8427_INTACTHIGH	(0<<1)	/* active high */
+#define CS8427_INTACTLOW	(1<<1)	/* active low */
+#define CS8427_INTOPENDRAIN	(2<<1)	/* open drain, active low */
+#define CS8427_TCBLDIR		(1<<0)	/* 0 = TCBL is an input, 1 = TCBL is an output */
+
+/* CS8427_REQ_CONTROL2 bits */
+#define CS8427_HOLDMASK		(3<<5)	/* action when a receiver error occurs */
+#define CS8427_HOLDLASTSAMPLE	(0<<5)	/* hold the last valid sample */
+#define CS8427_HOLDZERO		(1<<5)	/* replace the current audio sample with zero (mute) */
+#define CS8427_HOLDNOCHANGE	(2<<5)	/* do not change the received audio sample */
+#define CS8427_RMCKF		(1<<4)	/* 0 = 256*Fsi, 1 = 128*Fsi */
+#define CS8427_MMR		(1<<3)	/* AES3 receiver operation, 0 = stereo, 1 = mono */
+#define CS8427_MMT		(1<<2)	/* AES3 transmitter operation, 0 = stereo, 1 = mono */
+#define CS8427_MMTCS		(1<<1)	/* 0 = use A + B CS data, 1 = use MMTLR CS data */
+#define CS8427_MMTLR		(1<<0)	/* 0 = use A CS data, 1 = use B CS data */
+
+/* CS8427_REG_DATAFLOW */
+#define CS8427_TXOFF		(1<<6)	/* AES3 transmitter Output, 0 = normal operation, 1 = off (0V) */
+#define CS8427_AESBP		(1<<5)	/* AES3 hardware bypass mode, 0 = normal, 1 = bypass (RX->TX) */
+#define CS8427_TXDMASK		(3<<3)	/* AES3 Transmitter Data Source Mask */
+#define CS8427_TXDSERIAL	(1<<3)	/* TXD - serial audio input port */
+#define CS8427_TXAES3DRECEIVER	(2<<3)	/* TXD - AES3 receiver */
+#define CS8427_SPDMASK		(3<<1)	/* Serial Audio Output Port Data Source Mask */
+#define CS8427_SPDSERIAL	(1<<1)	/* SPD - serial audio input port */
+#define CS8427_SPDAES3RECEIVER	(2<<1)	/* SPD - AES3 receiver */
+
+/* CS8427_REG_CLOCKSOURCE */
+#define CS8427_RUN		(1<<6)	/* 0 = clock off, 1 = clock on */
+#define CS8427_CLKMASK		(3<<4)	/* OMCK frequency mask */
+#define CS8427_CLK256		(0<<4)	/* 256*Fso */
+#define CS8427_CLK384		(1<<4)	/* 384*Fso */
+#define CS8427_CLK512		(2<<4)	/* 512*Fso */
+#define CS8427_OUTC		(1<<3)	/* Output Time Base, 0 = OMCK, 1 = recovered input clock */
+#define CS8427_INC		(1<<2)	/* Input Time Base Clock Source, 0 = recoverd input clock, 1 = OMCK input pin */
+#define CS8427_RXDMASK		(3<<0)	/* Recovered Input Clock Source Mask */
+#define CS8427_RXDILRCK		(0<<0)	/* 256*Fsi from ILRCK pin */
+#define CS8427_RXDAES3INPUT	(1<<0)	/* 256*Fsi from AES3 input */
+#define CS8427_EXTCLOCKRESET	(2<<0)	/* bypass PLL, 256*Fsi clock, synchronous reset */
+#define CS8427_EXTCLOCK		(3<<0)	/* bypass PLL, 256*Fsi clock */
+
+/* CS8427_REG_SERIALINPUT */
+#define CS8427_SIMS		(1<<7)	/* 0 = slave, 1 = master mode */
+#define CS8427_SISF		(1<<6)	/* ISCLK freq, 0 = 64*Fsi, 1 = 128*Fsi */
+#define CS8427_SIRESMASK	(3<<4)	/* Resolution of the input data for right justified formats */
+#define CS8427_SIRES24		(0<<4)	/* SIRES 24-bit */
+#define CS8427_SIRES20		(1<<4)	/* SIRES 20-bit */
+#define CS8427_SIRES16		(2<<4)	/* SIRES 16-bit */
+#define CS8427_SIJUST		(1<<3)	/* Justification of SDIN data relative to ILRCK, 0 = left-justified, 1 = right-justified */
+#define CS8427_SIDEL		(1<<2)	/* Delay of SDIN data relative to ILRCK for left-justified data formats, 0 = first ISCLK period, 1 = second ISCLK period */
+#define CS8427_SISPOL		(1<<1)	/* ICLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */
+#define CS8427_SILRPOL		(1<<0)	/* ILRCK clock polarity, 0 = SDIN data left channel when ILRCK is high, 1 = SDIN right when ILRCK is high */
+
+/* CS8427_REG_SERIALOUTPUT */
+#define CS8427_SOMS		(1<<7)	/* 0 = slave, 1 = master mode */
+#define CS8427_SOSF		(1<<6)	/* OSCLK freq, 0 = 64*Fso, 1 = 128*Fso */
+#define CS8427_SORESMASK	(3<<4)	/* Resolution of the output data on SDOUT and AES3 output */
+#define CS8427_SORES24		(0<<4)	/* SIRES 24-bit */
+#define CS8427_SORES20		(1<<4)	/* SIRES 20-bit */
+#define CS8427_SORES16		(2<<4)	/* SIRES 16-bit */
+#define CS8427_SORESDIRECT	(2<<4)	/* SIRES direct copy from AES3 receiver */
+#define CS8427_SOJUST		(1<<3)	/* Justification of SDOUT data relative to OLRCK, 0 = left-justified, 1 = right-justified */
+#define CS8427_SODEL		(1<<2)	/* Delay of SDOUT data relative to OLRCK for left-justified data formats, 0 = first OSCLK period, 1 = second OSCLK period */
+#define CS8427_SOSPOL		(1<<1)	/* OSCLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */
+#define CS8427_SOLRPOL		(1<<0)	/* OLRCK clock polarity, 0 = SDOUT data left channel when OLRCK is high, 1 = SDOUT right when OLRCK is high */
+
+/* CS8427_REG_INT1STATUS */
+#define CS8427_TSLIP		(1<<7)	/* AES3 transmitter source data slip interrupt */
+#define CS8427_OSLIP		(1<<6)	/* Serial audio output port data slip interrupt */
+#define CS8427_DETC		(1<<2)	/* D to E C-buffer transfer interrupt */
+#define CS8427_EFTC		(1<<1)	/* E to F C-buffer transfer interrupt */
+#define CS8427_RERR		(1<<0)	/* A receiver error has occurred */
+
+/* CS8427_REG_INT2STATUS */
+#define CS8427_DETU		(1<<3)	/* D to E U-buffer transfer interrupt */
+#define CS8427_EFTU		(1<<2)	/* E to F U-buffer transfer interrupt */
+#define CS8427_QCH		(1<<1)	/* A new block of Q-subcode data is available for reading */
+
+/* CS8427_REG_INT1MODEMSB && CS8427_REG_INT1MODELSB */
+/* bits are defined in CS8427_REG_INT1STATUS */
+/* CS8427_REG_INT2MODEMSB && CS8427_REG_INT2MODELSB */
+/* bits are defined in CS8427_REG_INT2STATUS */
+#define CS8427_INTMODERISINGMSB	0
+#define CS8427_INTMODERESINGLSB	0
+#define CS8427_INTMODEFALLINGMSB 0
+#define CS8427_INTMODEFALLINGLSB 1
+#define CS8427_INTMODELEVELMSB	1
+#define CS8427_INTMODELEVELLSB	0
+
+/* CS8427_REG_RECVCSDATA */
+#define CS8427_AUXMASK		(15<<4)	/* auxiliary data field width */
+#define CS8427_AUXSHIFT		4
+#define CS8427_PRO		(1<<3)	/* Channel status block format indicator */
+#define CS8427_AUDIO		(1<<2)	/* Audio indicator (0 = audio, 1 = nonaudio */
+#define CS8427_COPY		(1<<1)	/* 0 = copyright asserted, 1 = copyright not asserted */
+#define CS8427_ORIG		(1<<0)	/* SCMS generation indicator, 0 = 1st generation or highter, 1 = original */
+
+/* CS8427_REG_RECVERRORS */
+/* CS8427_REG_RECVERRMASK for CS8427_RERR */
+#define CS8427_QCRC		(1<<6)	/* Q-subcode data CRC error indicator */
+#define CS8427_CCRC		(1<<5)	/* Chancnel Status Block Cyclick Redundancy Check Bit */
+#define CS8427_UNLOCK		(1<<4)	/* PLL lock status bit */
+#define CS8427_V		(1<<3)	/* 0 = valid data */
+#define CS8427_CONF		(1<<2)	/* Confidence bit */
+#define CS8427_BIP		(1<<1)	/* Bi-phase error bit */
+#define CS8427_PAR		(1<<0)	/* Parity error */
+
+/* CS8427_REG_CSDATABUF	*/
+#define CS8427_BSEL		(1<<5)	/* 0 = CS data, 1 = U data */
+#define CS8427_CBMR		(1<<4)	/* 0 = overwrite first 5 bytes for CS D to E buffer, 1 = prevent */
+#define CS8427_DETCI		(1<<3)	/* D to E CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */
+#define CS8427_EFTCI		(1<<2)	/* E to F CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */
+#define CS8427_CAM		(1<<1)	/* CS data buffer control port access mode bit, 0 = one byte, 1 = two byte */
+#define CS8427_CHS		(1<<0)	/* Channel select bit, 0 = Channel A, 1 = Channel B */
+
+/* CS8427_REG_UDATABUF */
+#define CS8427_UD		(1<<4)	/* User data pin (U) direction, 0 = input, 1 = output */
+#define CS8427_UBMMASK		(3<<2)	/* Operating mode of the AES3 U bit manager */
+#define CS8427_UBMZEROS		(0<<2)	/* transmit all zeros mode */
+#define CS8427_UBMBLOCK		(1<<2)	/* block mode */
+#define CS8427_DETUI		(1<<1)	/* D to E U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */
+#define CS8427_EFTUI		(1<<1)	/* E to F U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */
+
+/* CS8427_REG_ID_AND_VER */
+#define CS8427_IDMASK		(15<<4)
+#define CS8427_IDSHIFT		4
+#define CS8427_VERMASK		(15<<0)
+#define CS8427_VERSHIFT		0
+#define CS8427_VER8427A		0x71
+
+int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr);
+int snd_cs8427_create(snd_i2c_bus_t *bus, unsigned char addr, snd_i2c_device_t **r_cs8427);
+int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, snd_pcm_substream_t *playback_substream, snd_pcm_substream_t *capture_substream);
+int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active);
+int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate);
+
+#endif /* __SOUND_CS8427_H */
diff -Nru linux/include/sound/driver.h linux-2.4.19-pre5-mjc/include/sound/driver.h
--- linux/include/sound/driver.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/driver.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,81 @@
+#ifndef __SOUND_DRIVER_H
+#define __SOUND_DRIVER_H
+
+/*
+ *  Main header file for the ALSA driver
+ *  Copyright (c) 1994-2000 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef ALSA_BUILD
+#include "config.h"
+#endif
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#define SNDRV_CARDS		8	/* number of supported soundcards - don't change - minor numbers */
+
+#ifndef CONFIG_SND_MAJOR	/* standard configuration */
+#define CONFIG_SND_MAJOR	116
+#endif
+
+#ifndef CONFIG_SND_DEBUG
+#undef CONFIG_SND_DEBUG_MEMORY
+#endif
+
+#ifdef ALSA_BUILD
+#include "adriver.h"
+#endif
+
+#include <linux/module.h>
+
+/*
+ *  ==========================================================================
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+#if defined(__i386__) || defined(__ppc__)
+/*
+ * Here a dirty hack for 2.4 kernels.. See kernel/memory.c.
+ */
+#define HACK_PCI_ALLOC_CONSISTENT
+#include <linux/pci.h>
+void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+				    dma_addr_t *dma_handle);
+#undef pci_alloc_consistent
+#define pci_alloc_consistent snd_pci_hack_alloc_consistent
+#endif /* i386 or ppc */
+#endif /* 2.4.0 */
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+void *snd_wrapper_kmalloc(size_t, int);
+#undef kmalloc
+void snd_wrapper_kfree(const void *);
+#undef kfree
+void *snd_wrapper_vmalloc(size_t);
+#undef vmalloc
+void snd_wrapper_vfree(void *);
+#undef vfree
+#endif
+
+#include "sndmagic.h"
+
+#endif /* __SOUND_DRIVER_H */
diff -Nru linux/include/sound/emu10k1.h linux-2.4.19-pre5-mjc/include/sound/emu10k1.h
--- linux/include/sound/emu10k1.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/emu10k1.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1341 @@
+#ifndef __SOUND_EMU10K1_H
+#define __SOUND_EMU10K1_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Creative Labs, Inc.
+ *  Definitions for EMU10K1 (SB Live!) chips
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef __KERNEL__
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "hwdep.h"
+#include "ac97_codec.h"
+#include "util_mem.h"
+#include <asm/io.h>
+
+#ifndef PCI_VENDOR_ID_CREATIVE
+#define PCI_VENDOR_ID_CREATIVE		0x1102
+#endif
+#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1
+#define PCI_DEVICE_ID_CREATIVE_EMU10K1	0x0002
+#endif
+
+/* ------------------- DEFINES -------------------- */
+
+#define EMUPAGESIZE     4096
+#define MAXREQVOICES    8
+#define MAXPAGES        8192
+#define RESERVED        0
+#define NUM_MIDI        16
+#define NUM_G           64              /* use all channels */
+#define NUM_FXSENDS     4
+
+
+#define TMEMSIZE        256*1024
+#define TMEMSIZEREG     4
+
+#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL))
+
+// Audigy specify registers are prefixed with 'A_'
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+						/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+						/* be modified independently of each other.	*/
+#define PTR_CHANNELNUM_MASK	0x0000003f	/* For each per-channel register, indicates the	*/
+						/* channel number of the register to be		*/
+						/* accessed.  For non per-channel registers the	*/
+						/* value should be set to zero.			*/
+#define PTR_ADDRESS_MASK	0x07ff0000	/* Register index				*/
+#define A_PTR_ADDRESS_MASK	0x0fff0000
+
+#define DATA			0x04		/* Indexed register set data register		*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+						/* Clear pending interrupts by writing a 1 to	*/
+						/* the relevant bits and zero to the other bits	*/
+
+/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1)			*/
+#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000	/* MIDI UART transmit buffer empty		*/
+#define IPR_A_MIDIRECVBUFEMPTY2	0x08000000	/* MIDI UART receive buffer empty		*/
+
+#define IPR_SAMPLERATETRACKER	0x01000000	/* Sample rate tracker lock status change	*/
+#define IPR_FXDSP		0x00800000	/* Enable FX DSP interrupts			*/
+#define IPR_FORCEINT		0x00400000	/* Force Sound Blaster interrupt		*/
+#define IPR_PCIERROR		0x00200000	/* PCI bus error				*/
+#define IPR_VOLINCR		0x00100000	/* Volume increment button pressed		*/
+#define IPR_VOLDECR		0x00080000	/* Volume decrement button pressed		*/
+#define IPR_MUTE		0x00040000	/* Mute button pressed				*/
+#define IPR_MICBUFFULL		0x00020000	/* Microphone buffer full			*/
+#define IPR_MICBUFHALFFULL	0x00010000	/* Microphone buffer half full			*/
+#define IPR_ADCBUFFULL		0x00008000	/* ADC buffer full				*/
+#define IPR_ADCBUFHALFFULL	0x00004000	/* ADC buffer half full				*/
+#define IPR_EFXBUFFULL		0x00002000	/* Effects buffer full				*/
+#define IPR_EFXBUFHALFFULL	0x00001000	/* Effects buffer half full			*/
+#define IPR_GPSPDIFSTATUSCHANGE	0x00000800	/* GPSPDIF channel status change		*/
+#define IPR_CDROMSTATUSCHANGE	0x00000400	/* CD-ROM channel status change			*/
+#define IPR_INTERVALTIMER	0x00000200	/* Interval timer terminal count		*/
+#define IPR_MIDITRANSBUFEMPTY	0x00000100	/* MIDI UART transmit buffer empty		*/
+#define IPR_MIDIRECVBUFEMPTY	0x00000080	/* MIDI UART receive buffer empty		*/
+#define IPR_CHANNELLOOP		0x00000040	/* One or more channel loop interrupts pending	*/
+#define IPR_CHANNELNUMBERMASK	0x0000003f	/* When IPR_CHANNELLOOP is set, indicates the	*/
+						/* Highest set channel in CLIPL or CLIPH.  When	*/
+						/* IP is written with CL set, the bit in CLIPL	*/
+						/* or CLIPH corresponding to the CIN value 	*/
+						/* written will be cleared.			*/
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+#define INTE_VIRTUALSB_MASK	0xc0000000	/* Virtual Soundblaster I/O port capture	*/
+#define INTE_VIRTUALSB_220	0x00000000	/* Capture at I/O base address 0x220-0x22f	*/
+#define INTE_VIRTUALSB_240	0x40000000	/* Capture at I/O base address 0x240		*/
+#define INTE_VIRTUALSB_260	0x80000000	/* Capture at I/O base address 0x260		*/
+#define INTE_VIRTUALSB_280	0xc0000000	/* Capture at I/O base address 0x280		*/
+#define INTE_VIRTUALMPU_MASK	0x30000000	/* Virtual MPU I/O port capture			*/
+#define INTE_VIRTUALMPU_300	0x00000000	/* Capture at I/O base address 0x300-0x301	*/
+#define INTE_VIRTUALMPU_310	0x10000000	/* Capture at I/O base address 0x310		*/
+#define INTE_VIRTUALMPU_320	0x20000000	/* Capture at I/O base address 0x320		*/
+#define INTE_VIRTUALMPU_330	0x30000000	/* Capture at I/O base address 0x330		*/
+#define INTE_MASTERDMAENABLE	0x08000000	/* Master DMA emulation at 0x000-0x00f		*/
+#define INTE_SLAVEDMAENABLE	0x04000000	/* Slave DMA emulation at 0x0c0-0x0df		*/
+#define INTE_MASTERPICENABLE	0x02000000	/* Master PIC emulation at 0x020-0x021		*/
+#define INTE_SLAVEPICENABLE	0x01000000	/* Slave PIC emulation at 0x0a0-0x0a1		*/
+#define INTE_VSBENABLE		0x00800000	/* Enable virtual Soundblaster			*/
+#define INTE_ADLIBENABLE	0x00400000	/* Enable AdLib emulation at 0x388-0x38b	*/
+#define INTE_MPUENABLE		0x00200000	/* Enable virtual MPU				*/
+#define INTE_FORCEINT		0x00100000	/* Continuously assert INTAN			*/
+
+#define INTE_MRHANDENABLE	0x00080000	/* Enable the "Mr. Hand" logic			*/
+						/* NOTE: There is no reason to use this under	*/
+						/* Linux, and it will cause odd hardware 	*/
+						/* behavior and possibly random segfaults and	*/
+						/* lockups if enabled.				*/
+
+/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1)			*/
+#define INTE_A_MIDITXENABLE2	0x00020000	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_A_MIDIRXENABLE2	0x00010000	/* Enable MIDI receive-buffer-empty interrupts	*/
+
+
+#define INTE_SAMPLERATETRACKER	0x00002000	/* Enable sample rate tracker interrupts	*/
+						/* NOTE: This bit must always be enabled       	*/
+#define INTE_FXDSPENABLE	0x00001000	/* Enable FX DSP interrupts			*/
+#define INTE_PCIERRORENABLE	0x00000800	/* Enable PCI bus error interrupts		*/
+#define INTE_VOLINCRENABLE	0x00000400	/* Enable volume increment button interrupts	*/
+#define INTE_VOLDECRENABLE	0x00000200	/* Enable volume decrement button interrupts	*/
+#define INTE_MUTEENABLE		0x00000100	/* Enable mute button interrupts		*/
+#define INTE_MICBUFENABLE	0x00000080	/* Enable microphone buffer interrupts		*/
+#define INTE_ADCBUFENABLE	0x00000040	/* Enable ADC buffer interrupts			*/
+#define INTE_EFXBUFENABLE	0x00000020	/* Enable Effects buffer interrupts		*/
+#define INTE_GPSPDIFENABLE	0x00000010	/* Enable GPSPDIF status interrupts		*/
+#define INTE_CDSPDIFENABLE	0x00000008	/* Enable CDSPDIF status interrupts		*/
+#define INTE_INTERVALTIMERENB	0x00000004	/* Enable interval timer interrupts		*/
+#define INTE_MIDITXENABLE	0x00000002	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_MIDIRXENABLE	0x00000001	/* Enable MIDI receive-buffer-empty interrupts	*/
+
+#define WC			0x10		/* Wall Clock register				*/
+#define WC_SAMPLECOUNTER_MASK	0x03FFFFC0	/* Sample periods elapsed since reset		*/
+#define WC_SAMPLECOUNTER	0x14060010
+#define WC_CURRENTCHANNEL	0x0000003F	/* Channel [0..63] currently being serviced	*/
+						/* NOTE: Each channel takes 1/64th of a sample	*/
+						/* period to be serviced.			*/
+
+#define HCFG			0x14		/* Hardware config register			*/
+						/* NOTE: There is no reason to use the legacy	*/
+						/* SoundBlaster emulation stuff described below	*/
+						/* under Linux, and all kinds of weird hardware	*/
+						/* behavior can result if you try.  Don't.	*/
+#define HCFG_LEGACYFUNC_MASK	0xe0000000	/* Legacy function number 			*/
+#define HCFG_LEGACYFUNC_MPU	0x00000000	/* Legacy MPU	 				*/
+#define HCFG_LEGACYFUNC_SB	0x40000000	/* Legacy SB					*/
+#define HCFG_LEGACYFUNC_AD	0x60000000	/* Legacy AD					*/
+#define HCFG_LEGACYFUNC_MPIC	0x80000000	/* Legacy MPIC					*/
+#define HCFG_LEGACYFUNC_MDMA	0xa0000000	/* Legacy MDMA					*/
+#define HCFG_LEGACYFUNC_SPCI	0xc0000000	/* Legacy SPCI					*/
+#define HCFG_LEGACYFUNC_SDMA	0xe0000000	/* Legacy SDMA					*/
+#define HCFG_IOCAPTUREADDR	0x1f000000	/* The 4 LSBs of the captured I/O address.	*/
+#define HCFG_LEGACYWRITE	0x00800000	/* 1 = write, 0 = read 				*/
+#define HCFG_LEGACYWORD		0x00400000	/* 1 = word, 0 = byte 				*/
+#define HCFG_LEGACYINT		0x00200000	/* 1 = legacy event captured. Write 1 to clear.	*/
+						/* NOTE: The rest of the bits in this register	*/
+						/* _are_ relevant under Linux.			*/
+#define HCFG_CODECFORMAT_MASK	0x00070000	/* CODEC format					*/
+#define HCFG_CODECFORMAT_AC97	0x00000000	/* AC97 CODEC format -- Primary Output		*/
+#define HCFG_CODECFORMAT_I2S	0x00010000	/* I2S CODEC format -- Secondary (Rear) Output	*/
+#define HCFG_GPINPUT0		0x00004000	/* External pin112				*/
+#define HCFG_GPINPUT1		0x00002000	/* External pin110				*/
+#define HCFG_GPOUTPUT_MASK	0x00001c00	/* External pins which may be controlled	*/
+#define HCFG_GPOUT0		0x00001000	/* External pin? (spdif enable on 5.1)		*/
+#define HCFG_GPOUT1		0x00000800	/* External pin? (IR)				*/
+#define HCFG_GPOUT2		0x00000400	/* External pin? (IR)				*/
+#define HCFG_JOYENABLE      	0x00000200	/* Internal joystick enable    			*/
+#define HCFG_PHASETRACKENABLE	0x00000100	/* Phase tracking enable			*/
+						/* 1 = Force all 3 async digital inputs to use	*/
+						/* the same async sample rate tracker (ZVIDEO)	*/
+#define HCFG_AC3ENABLE_MASK	0x000000e0	/* AC3 async input control - Not implemented	*/
+#define HCFG_AC3ENABLE_ZVIDEO	0x00000080	/* Channels 0 and 1 replace ZVIDEO		*/
+#define HCFG_AC3ENABLE_CDSPDIF	0x00000040	/* Channels 0 and 1 replace CDSPDIF		*/
+#define HCFG_AC3ENABLE_GPSPDIF  0x00000020      /* Channels 0 and 1 replace GPSPDIF             */
+#define HCFG_AUTOMUTE		0x00000010	/* When set, the async sample rate convertors	*/
+						/* will automatically mute their output when	*/
+						/* they are not rate-locked to the external	*/
+						/* async audio source  				*/
+#define HCFG_LOCKSOUNDCACHE	0x00000008	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCKTANKCACHE_MASK	0x00000004	/* 1 = Cancel bustmaster accesses to tankcache	*/
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCKTANKCACHE	0x01020014
+#define HCFG_MUTEBUTTONENABLE	0x00000002	/* 1 = Master mute button sets AUDIOENABLE = 0.	*/
+						/* NOTE: This is a 'cheap' way to implement a	*/
+						/* master mute function on the mute button, and	*/
+						/* in general should not be used unless a more	*/
+						/* sophisticated master mute function has not	*/
+						/* been written.       				*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+						/* Should be set to 1 when the EMU10K1 is	*/
+						/* completely initialized.			*/
+
+//For Audigy, MPU port move to 0x70-0x74 ptr register
+
+#define MUDATA			0x18		/* MPU401 data register (8 bits)       		*/
+
+#define MUCMD			0x19		/* MPU401 command register (8 bits)    		*/
+#define MUCMD_RESET		0xff		/* RESET command				*/
+#define MUCMD_ENTERUARTMODE	0x3f		/* Enter_UART_mode command			*/
+						/* NOTE: All other commands are ignored		*/
+
+#define MUSTAT			MUCMD		/* MPU401 status register (8 bits)     		*/
+#define MUSTAT_IRDYN		0x80		/* 0 = MIDI data or command ACK			*/
+#define MUSTAT_ORDYN		0x40		/* 0 = MUDATA can accept a command or data	*/
+
+#define A_IOCFG			0x18		/* GPIO on Audigy card (16bits)			*/
+#define A_GPINPUT_MASK		0xff00
+#define A_GPOUTPUT_MASK		0x00ff
+
+#define TIMER			0x1a		/* Timer terminal count register		*/
+						/* NOTE: After the rate is changed, a maximum	*/
+						/* of 1024 sample periods should be allowed	*/
+						/* before the new rate is guaranteed accurate.	*/
+#define TIMER_RATE_MASK		0x000003ff	/* Timer interrupt rate in sample periods	*/
+						/* 0 == 1024 periods, [1..4] are not useful	*/
+#define TIMER_RATE		0x0a00001a
+
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+#define AC97ADDRESS_READY	0x80		/* Read-only bit, reflects CODEC READY signal	*/
+#define AC97ADDRESS_ADDRESS	0x7f		/* Address of indexed AC97 register		*/
+
+/************************************************************************************************/
+/* PCI function 1 registers, address = <val> + PCIBASE1						*/
+/************************************************************************************************/
+
+#define JOYSTICK1		0x00		/* Analog joystick port register		*/
+#define JOYSTICK2		0x01		/* Analog joystick port register		*/
+#define JOYSTICK3		0x02		/* Analog joystick port register		*/
+#define JOYSTICK4		0x03		/* Analog joystick port register		*/
+#define JOYSTICK5		0x04		/* Analog joystick port register		*/
+#define JOYSTICK6		0x05		/* Analog joystick port register		*/
+#define JOYSTICK7		0x06		/* Analog joystick port register		*/
+#define JOYSTICK8		0x07		/* Analog joystick port register		*/
+
+/* When writing, any write causes JOYSTICK_COMPARATOR output enable to be pulsed on write.	*/
+/* When reading, use these bitfields: */
+#define JOYSTICK_BUTTONS	0x0f		/* Joystick button data				*/
+#define JOYSTICK_COMPARATOR	0xf0		/* Joystick comparator data			*/
+
+
+/********************************************************************************************************/
+/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers			*/
+/********************************************************************************************************/
+
+#define CPF			0x00		/* Current pitch and fraction register			*/
+#define CPF_CURRENTPITCH_MASK	0xffff0000	/* Current pitch (linear, 0x4000 == unity pitch shift) 	*/
+#define CPF_CURRENTPITCH	0x10100000
+#define CPF_STEREO_MASK		0x00008000	/* 1 = Even channel interleave, odd channel locked	*/
+#define CPF_STOP_MASK		0x00004000	/* 1 = Current pitch forced to 0			*/
+#define CPF_FRACADDRESS_MASK	0x00003fff	/* Linear fractional address of the current channel	*/
+
+#define PTRX			0x01		/* Pitch target and send A/B amounts register		*/
+#define PTRX_PITCHTARGET_MASK	0xffff0000	/* Pitch target of specified channel			*/
+#define PTRX_PITCHTARGET	0x10100001
+#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00	/* Linear level of channel output sent to FX send bus A	*/
+#define PTRX_FXSENDAMOUNT_A	0x08080001
+#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff	/* Linear level of channel output sent to FX send bus B	*/
+#define PTRX_FXSENDAMOUNT_B	0x08000001
+
+#define CVCF			0x02		/* Current volume and filter cutoff register		*/
+#define CVCF_CURRENTVOL_MASK	0xffff0000	/* Current linear volume of specified channel		*/
+#define CVCF_CURRENTVOL		0x10100002
+#define CVCF_CURRENTFILTER_MASK	0x0000ffff	/* Current filter cutoff frequency of specified channel	*/
+#define CVCF_CURRENTFILTER	0x10000002
+
+#define VTFT			0x03		/* Volume target and filter cutoff target register	*/
+#define VTFT_VOLUMETARGET_MASK	0xffff0000	/* Volume target of specified channel			*/
+#define VTFT_VOLUMETARGET	0x10100003
+#define VTFT_FILTERTARGET_MASK	0x0000ffff	/* Filter cutoff target of specified channel		*/
+#define VTFT_FILTERTARGET	0x10000003
+
+#define Z1			0x05		/* Filter delay memory 1 register			*/
+
+#define Z2			0x04		/* Filter delay memory 2 register			*/
+
+#define PSST			0x06		/* Send C amount and loop start address register	*/
+#define PSST_FXSENDAMOUNT_C_MASK 0xff000000	/* Linear level of channel output sent to FX send bus C	*/
+
+#define PSST_FXSENDAMOUNT_C	0x08180006
+
+#define PSST_LOOPSTARTADDR_MASK	0x00ffffff	/* Loop start address of the specified channel		*/
+#define PSST_LOOPSTARTADDR	0x18000006
+
+#define DSL			0x07		/* Send D amount and loop start address register	*/
+#define DSL_FXSENDAMOUNT_D_MASK	0xff000000	/* Linear level of channel output sent to FX send bus D	*/
+
+#define DSL_FXSENDAMOUNT_D	0x08180007
+
+#define DSL_LOOPENDADDR_MASK	0x00ffffff	/* Loop end address of the specified channel		*/
+#define DSL_LOOPENDADDR		0x18000007
+
+#define CCCA			0x08		/* Filter Q, interp. ROM, byte size, cur. addr register */
+#define CCCA_RESONANCE		0xf0000000	/* Lowpass filter resonance (Q) height			*/
+#define CCCA_INTERPROMMASK	0x0e000000	/* Selects passband of interpolation ROM		*/
+						/* 1 == full band, 7 == lowpass				*/
+						/* ROM 0 is used when pitch shifting downward or less	*/
+						/* then 3 semitones upward.  Increasingly higher ROM	*/
+						/* numbers are used, typically in steps of 3 semitones,	*/
+						/* as upward pitch shifting is performed.		*/
+#define CCCA_INTERPROM_0	0x00000000	/* Select interpolation ROM 0				*/
+#define CCCA_INTERPROM_1	0x02000000	/* Select interpolation ROM 1				*/
+#define CCCA_INTERPROM_2	0x04000000	/* Select interpolation ROM 2				*/
+#define CCCA_INTERPROM_3	0x06000000	/* Select interpolation ROM 3				*/
+#define CCCA_INTERPROM_4	0x08000000	/* Select interpolation ROM 4				*/
+#define CCCA_INTERPROM_5	0x0a000000	/* Select interpolation ROM 5				*/
+#define CCCA_INTERPROM_6	0x0c000000	/* Select interpolation ROM 6				*/
+#define CCCA_INTERPROM_7	0x0e000000	/* Select interpolation ROM 7				*/
+#define CCCA_8BITSELECT		0x01000000	/* 1 = Sound memory for this channel uses 8-bit samples	*/
+#define CCCA_CURRADDR_MASK	0x00ffffff	/* Current address of the selected channel		*/
+#define CCCA_CURRADDR		0x18000008
+
+#define CCR			0x09		/* Cache control register				*/
+#define CCR_CACHEINVALIDSIZE	0x07190009
+#define CCR_CACHEINVALIDSIZE_MASK	0xfe000000	/* Number of invalid samples cache for this channel    	*/
+#define CCR_CACHELOOPFLAG	0x01000000	/* 1 = Cache has a loop service pending			*/
+#define CCR_INTERLEAVEDSAMPLES	0x00800000	/* 1 = A cache service will fetch interleaved samples	*/
+#define CCR_WORDSIZEDSAMPLES	0x00400000	/* 1 = A cache service will fetch word sized samples	*/
+#define CCR_READADDRESS		0x06100009
+#define CCR_READADDRESS_MASK	0x003f0000	/* Location of cache just beyond current cache service	*/
+#define CCR_LOOPINVALSIZE	0x0000fe00	/* Number of invalid samples in cache prior to loop	*/
+						/* NOTE: This is valid only if CACHELOOPFLAG is set	*/
+#define CCR_LOOPFLAG		0x00000100	/* Set for a single sample period when a loop occurs	*/
+#define CCR_CACHELOOPADDRHI	0x000000ff	/* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set	*/
+
+#define CLP			0x0a		/* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */
+						/* NOTE: This register is normally not used		*/
+#define CLP_CACHELOOPADDR	0x0000ffff	/* Cache loop address (DSL_LOOPSTARTADDR [0..15])	*/
+
+#define FXRT			0x0b		/* Effects send routing register			*/
+						/* NOTE: It is illegal to assign the same routing to	*/
+						/* two effects sends.					*/
+#define FXRT_CHANNELA		0x000f0000	/* Effects send bus number for channel's effects send A	*/
+#define FXRT_CHANNELB		0x00f00000	/* Effects send bus number for channel's effects send B	*/
+#define FXRT_CHANNELC		0x0f000000	/* Effects send bus number for channel's effects send C	*/
+#define FXRT_CHANNELD		0xf0000000	/* Effects send bus number for channel's effects send D	*/
+
+#define MAPA			0x0c		/* Cache map A						*/
+
+#define MAPB			0x0d		/* Cache map B						*/
+
+#define MAP_PTE_MASK		0xffffe000	/* The 19 MSBs of the PTE indexed by the PTI		*/
+#define MAP_PTI_MASK		0x00001fff	/* The 13 bit index to one of the 8192 PTE dwords      	*/
+
+#define ENVVOL			0x10		/* Volume envelope register				*/
+#define ENVVOL_MASK		0x0000ffff	/* Current value of volume envelope state variable	*/  
+						/* 0x8000-n == 666*n usec delay	       			*/
+
+#define ATKHLDV 		0x11		/* Volume envelope hold and attack register		*/
+#define ATKHLDV_PHASE0		0x00008000	/* 0 = Begin attack phase				*/
+#define ATKHLDV_HOLDTIME_MASK	0x00007f00	/* Envelope hold time (127-n == n*88.2msec)		*/
+#define ATKHLDV_ATTACKTIME_MASK	0x0000007f	/* Envelope attack time, log encoded			*/
+						/* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec	*/
+
+#define DCYSUSV 		0x12		/* Volume envelope sustain and decay register		*/
+#define DCYSUSV_PHASE1_MASK	0x00008000	/* 0 = Begin attack phase, 1 = begin release phase	*/
+#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00	/* 127 = full, 0 = off, 0.75dB increments		*/
+#define DCYSUSV_CHANNELENABLE_MASK 0x00000080	/* 1 = Inhibit envelope engine from writing values in	*/
+						/* this channel and from writing to pitch, filter and	*/
+						/* volume targets.					*/
+#define DCYSUSV_DECAYTIME_MASK	0x0000007f	/* Volume envelope decay time, log encoded     		*/
+						/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec		*/
+
+#define LFOVAL1 		0x13		/* Modulation LFO value					*/
+#define LFOVAL_MASK		0x0000ffff	/* Current value of modulation LFO state variable	*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define ENVVAL			0x14		/* Modulation envelope register				*/
+#define ENVVAL_MASK		0x0000ffff	/* Current value of modulation envelope state variable 	*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define ATKHLDM			0x15		/* Modulation envelope hold and attack register		*/
+#define ATKHLDM_PHASE0		0x00008000	/* 0 = Begin attack phase				*/
+#define ATKHLDM_HOLDTIME	0x00007f00	/* Envelope hold time (127-n == n*42msec)		*/
+#define ATKHLDM_ATTACKTIME	0x0000007f	/* Envelope attack time, log encoded			*/
+						/* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec		*/
+
+#define DCYSUSM			0x16		/* Modulation envelope decay and sustain register	*/
+#define DCYSUSM_PHASE1_MASK	0x00008000	/* 0 = Begin attack phase, 1 = begin release phase	*/
+#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00	/* 127 = full, 0 = off, 0.75dB increments		*/
+#define DCYSUSM_DECAYTIME_MASK	0x0000007f	/* Envelope decay time, log encoded			*/
+						/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec		*/
+
+#define LFOVAL2 		0x17		/* Vibrato LFO register					*/
+#define LFOVAL2_MASK		0x0000ffff	/* Current value of vibrato LFO state variable 		*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define IP			0x18		/* Initial pitch register				*/
+#define IP_MASK			0x0000ffff	/* Exponential initial pitch shift			*/
+						/* 4 bits of octave, 12 bits of fractional octave	*/
+#define IP_UNITY		0x0000e000	/* Unity pitch shift					*/
+
+#define IFATN			0x19		/* Initial filter cutoff and attenuation register	*/
+#define IFATN_FILTERCUTOFF_MASK	0x0000ff00	/* Initial filter cutoff frequency in exponential units	*/
+						/* 6 most significant bits are semitones		*/
+						/* 2 least significant bits are fractions		*/
+#define IFATN_FILTERCUTOFF	0x08080019
+#define IFATN_ATTENUATION_MASK	0x000000ff	/* Initial attenuation in 0.375dB steps			*/
+#define IFATN_ATTENUATION	0x08000019
+
+
+#define PEFE			0x1a		/* Pitch envelope and filter envelope amount register	*/
+#define PEFE_PITCHAMOUNT_MASK	0x0000ff00	/* Pitch envlope amount					*/
+						/* Signed 2's complement, +/- one octave peak extremes	*/
+#define PEFE_PITCHAMOUNT	0x0808001a
+#define PEFE_FILTERAMOUNT_MASK	0x000000ff	/* Filter envlope amount				*/
+						/* Signed 2's complement, +/- six octaves peak extremes */
+#define PEFE_FILTERAMOUNT	0x0800001a
+#define FMMOD			0x1b		/* Vibrato/filter modulation from LFO register		*/
+#define FMMOD_MODVIBRATO	0x0000ff00	/* Vibrato LFO modulation depth				*/
+						/* Signed 2's complement, +/- one octave extremes	*/
+#define FMMOD_MOFILTER		0x000000ff	/* Filter LFO modulation depth				*/
+						/* Signed 2's complement, +/- three octave extremes	*/
+
+
+#define TREMFRQ 		0x1c		/* Tremolo amount and modulation LFO frequency register	*/
+#define TREMFRQ_DEPTH		0x0000ff00	/* Tremolo depth					*/
+						/* Signed 2's complement, with +/- 12dB extremes	*/
+
+#define TREMFRQ_FREQUENCY	0x000000ff	/* Tremolo LFO frequency				*/
+						/* ??Hz steps, maximum of ?? Hz.			*/
+#define FM2FRQ2 		0x1d		/* Vibrato amount and vibrato LFO frequency register	*/
+#define FM2FRQ2_DEPTH		0x0000ff00	/* Vibrato LFO vibrato depth				*/
+						/* Signed 2's complement, +/- one octave extremes	*/
+#define FM2FRQ2_FREQUENCY	0x000000ff	/* Vibrato LFO frequency				*/
+						/* 0.039Hz steps, maximum of 9.85 Hz.			*/
+
+#define TEMPENV 		0x1e		/* Tempory envelope register				*/
+#define TEMPENV_MASK		0x0000ffff	/* 16-bit value						*/
+						/* NOTE: All channels contain internal variables; do	*/
+						/* not write to these locations.			*/
+
+#define CD0			0x20		/* Cache data 0 register				*/
+#define CD1			0x21		/* Cache data 1 register				*/
+#define CD2			0x22		/* Cache data 2 register				*/
+#define CD3			0x23		/* Cache data 3 register				*/
+#define CD4			0x24		/* Cache data 4 register				*/
+#define CD5			0x25		/* Cache data 5 register				*/
+#define CD6			0x26		/* Cache data 6 register				*/
+#define CD7			0x27		/* Cache data 7 register				*/
+#define CD8			0x28		/* Cache data 8 register				*/
+#define CD9			0x29		/* Cache data 9 register				*/
+#define CDA			0x2a		/* Cache data A register				*/
+#define CDB			0x2b		/* Cache data B register				*/
+#define CDC			0x2c		/* Cache data C register				*/
+#define CDD			0x2d		/* Cache data D register				*/
+#define CDE			0x2e		/* Cache data E register				*/
+#define CDF			0x2f		/* Cache data F register				*/
+
+#define PTB			0x40		/* Page table base register				*/
+#define PTB_MASK		0xfffff000	/* Physical address of the page table in host memory	*/
+
+#define TCB			0x41		/* Tank cache base register    				*/
+#define TCB_MASK		0xfffff000	/* Physical address of the bottom of host based TRAM	*/
+
+#define ADCCR			0x42		/* ADC sample rate/stereo control register		*/
+#define ADCCR_RCHANENABLE	0x00000010	/* Enables right channel for writing to the host       	*/
+#define ADCCR_LCHANENABLE	0x00000008	/* Enables left channel for writing to the host		*/
+						/* NOTE: To guarantee phase coherency, both channels	*/
+						/* must be disabled prior to enabling both channels.	*/
+#define A_ADCCR_RCHANENABLE	0x00000020
+#define A_ADCCR_LCHANENABLE	0x00000010
+
+#define A_ADCCR_SAMPLERATE_MASK 0x0000000F      /* Audigy sample rate convertor output rate		*/
+#define ADCCR_SAMPLERATE_MASK	0x00000007	/* Sample rate convertor output rate			*/
+#define ADCCR_SAMPLERATE_48	0x00000000	/* 48kHz sample rate					*/
+#define ADCCR_SAMPLERATE_44	0x00000001	/* 44.1kHz sample rate					*/
+#define ADCCR_SAMPLERATE_32	0x00000002	/* 32kHz sample rate					*/
+#define ADCCR_SAMPLERATE_24	0x00000003	/* 24kHz sample rate					*/
+#define ADCCR_SAMPLERATE_22	0x00000004	/* 22.05kHz sample rate					*/
+#define ADCCR_SAMPLERATE_16	0x00000005	/* 16kHz sample rate					*/
+#define ADCCR_SAMPLERATE_11	0x00000006	/* 11.025kHz sample rate				*/
+#define ADCCR_SAMPLERATE_8	0x00000007	/* 8kHz sample rate					*/
+#define A_ADCCR_SAMPLERATE_12	0x00000006	/* 12kHz sample rate					*/
+#define A_ADCCR_SAMPLERATE_11	0x00000007	/* 11.025kHz sample rate				*/
+#define A_ADCCR_SAMPLERATE_8	0x00000008	/* 8kHz sample rate					*/
+
+#define FXWC			0x43		/* FX output write channels register			*/
+						/* When set, each bit enables the writing of the	*/
+						/* corresponding FX output channel into host memory	*/
+#define FXWC_DEFAULTROUTE_C     (1<<0)		/* left emu out? */
+#define FXWC_DEFAULTROUTE_B     (1<<1)		/* right emu out? */
+#define FXWC_DEFAULTROUTE_A     (1<<12)
+#define FXWC_DEFAULTROUTE_D     (1<<13)
+#define FXWC_ADCLEFT            (1<<18)
+#define FXWC_CDROMSPDIFLEFT     (1<<18)
+#define FXWC_ADCRIGHT           (1<<19)
+#define FXWC_CDROMSPDIFRIGHT    (1<<19)
+#define FXWC_MIC                (1<<20)
+#define FXWC_ZOOMLEFT           (1<<20)
+#define FXWC_ZOOMRIGHT          (1<<21)
+#define FXWC_SPDIFLEFT          (1<<22)		/* 0x00400000 */
+#define FXWC_SPDIFRIGHT         (1<<23)		/* 0x00800000 */
+
+#define TCBS			0x44		/* Tank cache buffer size register			*/
+#define TCBS_MASK		0x00000007	/* Tank cache buffer size field				*/
+#define TCBS_BUFFSIZE_16K	0x00000000
+#define TCBS_BUFFSIZE_32K	0x00000001
+#define TCBS_BUFFSIZE_64K	0x00000002
+#define TCBS_BUFFSIZE_128K	0x00000003
+#define TCBS_BUFFSIZE_256K	0x00000004
+#define TCBS_BUFFSIZE_512K	0x00000005
+#define TCBS_BUFFSIZE_1024K	0x00000006
+#define TCBS_BUFFSIZE_2048K	0x00000007
+
+#define MICBA			0x45		/* AC97 microphone buffer address register		*/
+#define MICBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define ADCBA			0x46		/* ADC buffer address register				*/
+#define ADCBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define FXBA			0x47		/* FX Buffer Address */
+#define FXBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define MICBS			0x49		/* Microphone buffer size register			*/
+
+#define ADCBS			0x4a		/* ADC buffer size register				*/
+
+#define FXBS			0x4b		/* FX buffer size register				*/
+
+/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */
+#define ADCBS_BUFSIZE_NONE	0x00000000
+#define ADCBS_BUFSIZE_384	0x00000001
+#define ADCBS_BUFSIZE_448	0x00000002
+#define ADCBS_BUFSIZE_512	0x00000003
+#define ADCBS_BUFSIZE_640	0x00000004
+#define ADCBS_BUFSIZE_768	0x00000005
+#define ADCBS_BUFSIZE_896	0x00000006
+#define ADCBS_BUFSIZE_1024	0x00000007
+#define ADCBS_BUFSIZE_1280	0x00000008
+#define ADCBS_BUFSIZE_1536	0x00000009
+#define ADCBS_BUFSIZE_1792	0x0000000a
+#define ADCBS_BUFSIZE_2048	0x0000000b
+#define ADCBS_BUFSIZE_2560	0x0000000c
+#define ADCBS_BUFSIZE_3072	0x0000000d
+#define ADCBS_BUFSIZE_3584	0x0000000e
+#define ADCBS_BUFSIZE_4096	0x0000000f
+#define ADCBS_BUFSIZE_5120	0x00000010
+#define ADCBS_BUFSIZE_6144	0x00000011
+#define ADCBS_BUFSIZE_7168	0x00000012
+#define ADCBS_BUFSIZE_8192	0x00000013
+#define ADCBS_BUFSIZE_10240	0x00000014
+#define ADCBS_BUFSIZE_12288	0x00000015
+#define ADCBS_BUFSIZE_14366	0x00000016
+#define ADCBS_BUFSIZE_16384	0x00000017
+#define ADCBS_BUFSIZE_20480	0x00000018
+#define ADCBS_BUFSIZE_24576	0x00000019
+#define ADCBS_BUFSIZE_28672	0x0000001a
+#define ADCBS_BUFSIZE_32768	0x0000001b
+#define ADCBS_BUFSIZE_40960	0x0000001c
+#define ADCBS_BUFSIZE_49152	0x0000001d
+#define ADCBS_BUFSIZE_57344	0x0000001e
+#define ADCBS_BUFSIZE_65536	0x0000001f
+
+
+#define CDCS			0x50		/* CD-ROM digital channel status register	*/
+
+#define GPSCS			0x51		/* General Purpose SPDIF channel status register*/
+
+#define DBG			0x52		/* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+
+#define REG53			0x53		/* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+
+#define A_DBG			 0x53
+#define A_DBG_SINGLE_STEP	 0x00020000	/* Set to zero to start dsp */
+#define A_DBG_ZC		 0x40000000	/* zero tram counter */
+#define A_DBG_STEP_ADDR		 0x000003ff
+#define A_DBG_SATURATION_OCCURED 0x20000000
+#define A_DBG_SATURATION_ADDR	 0x0ffc0000
+
+#define SPCS0			0x54		/* SPDIF output Channel Status 0 register	*/
+
+#define SPCS1			0x55		/* SPDIF output Channel Status 1 register	*/
+
+#define SPCS2			0x56		/* SPDIF output Channel Status 2 register	*/
+
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status      		*/
+#define CLIEL			0x58		/* Channel loop interrupt enable low register	*/
+
+#define CLIEH			0x59		/* Channel loop interrupt enable high register	*/
+
+#define CLIPL			0x5a		/* Channel loop interrupt pending low register	*/
+
+#define CLIPH			0x5b		/* Channel loop interrupt pending high register	*/
+
+#define SOLEL			0x5c		/* Stop on loop enable low register		*/
+
+#define SOLEH			0x5d		/* Stop on loop enable high register		*/
+
+#define SPBYPASS		0x5e		/* SPDIF BYPASS mode register			*/
+#define SPBYPASS_ENABLE		0x00000001	/* Enable SPDIF bypass mode			*/
+
+#define AC97SLOT		0x5f            /* additional AC97 slots enable bits		*/
+#define AC97SLOT_CNTR		0x10            /* Center enable */
+#define AC97SLOT_LFE		0x20            /* LFE enable */
+
+#define CDSRCS			0x60		/* CD-ROM Sample Rate Converter status register	*/
+
+#define GPSRCS			0x61		/* General Purpose SPDIF sample rate cvt status */
+
+#define ZVSRCS			0x62		/* ZVideo sample rate converter status		*/
+						/* NOTE: This one has no SPDIFLOCKED field	*/
+						/* Assumes sample lock				*/
+
+/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS.			*/
+#define SRCS_SPDIFLOCKED	0x02000000	/* SPDIF stream locked				*/
+#define SRCS_RATELOCKED		0x01000000	/* Sample rate locked				*/
+#define SRCS_ESTSAMPLERATE	0x0007ffff	/* Do not modify this field.			*/
+
+/* Note that these values can vary +/- by a small amount                                        */
+#define SRCS_SPDIFRATE_44	0x0003acd9
+#define SRCS_SPDIFRATE_48	0x00040000
+#define SRCS_SPDIFRATE_96	0x00080000
+
+#define MICIDX                  0x63            /* Microphone recording buffer index register   */
+#define MICIDX_MASK             0x0000ffff      /* 16-bit value                                 */
+#define MICIDX_IDX		0x10000063
+
+#define ADCIDX			0x64		/* ADC recording buffer index register		*/
+#define ADCIDX_MASK		0x0000ffff	/* 16 bit index field				*/
+#define ADCIDX_IDX		0x10000064
+
+#define A_ADCIDX		0x63
+#define A_ADCIDX_IDX		0x10000063
+
+#define FXIDX			0x65		/* FX recording buffer index register		*/
+#define FXIDX_MASK		0x0000ffff	/* 16-bit value					*/
+#define FXIDX_IDX		0x10000065
+
+/* This is the MPU port on the card (via the game port)						*/
+#define A_MUDATA1		0x70
+#define A_MUCMD1		0x71
+#define A_MUSTAT1		A_MUCMD1
+
+/* This is the MPU port on the Audigy Drive 							*/
+#define A_MUDATA2		0x72
+#define A_MUCMD2		0x73
+#define A_MUSTAT2		A_MUCMD2	
+
+#define A_SPDIF_SAMPLERATE	0x76		/* Set the sample rate of SPDIF output		*/
+#define A_SPDIF_48000		0x00000080
+#define A_SPDIF_44100		0x00000000
+#define A_SPDIF_96000		0x00000040
+
+#define A_FXRT2			0x7c
+#define A_FXRT_CHANNELE		0x0000003f	/* Effects send bus number for channel's effects send E	*/
+#define A_FXRT_CHANNELF		0x00003f00	/* Effects send bus number for channel's effects send F	*/
+#define A_FXRT_CHANNELG		0x003f0000	/* Effects send bus number for channel's effects send G	*/
+#define A_FXRT_CHANNELH		0x3f000000	/* Effects send bus number for channel's effects send H	*/
+
+#define A_SENDAMOUNTS		0x7d
+#define A_FXSENDAMOUNT_E_MASK	0xFF000000
+#define A_FXSENDAMOUNT_F_MASK	0x00FF0000
+#define A_FXSENDAMOUNT_G_MASK	0x0000FF00
+#define A_FXSENDAMOUNT_H_MASK	0x000000FF
+
+/* The send amounts for this one are the same as used with the emu10k1 */
+#define A_FXRT1			0x7e
+#define A_FXRT_CHANNELA		0x0000003f
+#define A_FXRT_CHANNELB		0x00003f00
+#define A_FXRT_CHANNELC		0x003f0000
+#define A_FXRT_CHANNELD		0x3f000000
+
+
+/* Each FX general purpose register is 32 bits in length, all bits are used			*/
+#define FXGPREGBASE		0x100		/* FX general purpose registers base       	*/
+#define A_FXGPREGBASE		0x400		/* Audigy GPRs, 0x400 to 0x5ff			*/
+
+/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is	*/
+/* decompressed back to 20 bits on a read.  There are a total of 160 locations, the last 32	*/
+/* locations are for external TRAM. 								*/
+#define TANKMEMDATAREGBASE	0x200		/* Tank memory data registers base     		*/
+#define TANKMEMDATAREG_MASK	0x000fffff	/* 20 bit tank audio data field			*/
+
+/* Combined address field and memory opcode or flag field.  160 locations, last 32 are external	*/
+#define TANKMEMADDRREGBASE	0x300		/* Tank memory address registers base		*/
+#define TANKMEMADDRREG_ADDR_MASK 0x000fffff	/* 20 bit tank address field			*/
+#define TANKMEMADDRREG_CLEAR	0x00800000	/* Clear tank memory				*/
+#define TANKMEMADDRREG_ALIGN	0x00400000	/* Align read or write relative to tank access	*/
+#define TANKMEMADDRREG_WRITE	0x00200000	/* Write to tank memory				*/
+#define TANKMEMADDRREG_READ	0x00100000	/* Read from tank memory			*/
+
+#define MICROCODEBASE		0x400		/* Microcode data base address			*/
+
+/* Each DSP microcode instruction is mapped into 2 doublewords 					*/
+/* NOTE: When writing, always write the LO doubleword first.  Reads can be in either order.	*/
+#define LOWORD_OPX_MASK		0x000ffc00	/* Instruction operand X			*/
+#define LOWORD_OPY_MASK		0x000003ff	/* Instruction operand Y			*/
+#define HIWORD_OPCODE_MASK	0x00f00000	/* Instruction opcode				*/
+#define HIWORD_RESULT_MASK	0x000ffc00	/* Instruction result				*/
+#define HIWORD_OPA_MASK		0x000003ff	/* Instruction operand A			*/
+
+
+/* Audigy Soundcard have a different instruction format */
+#define A_MICROCODEBASE		0x600
+#define A_LOWORD_OPY_MASK	0x000007ff
+#define A_LOWORD_OPX_MASK	0x007ff000
+#define A_HIWORD_OPCODE_MASK	0x0f000000
+#define A_HIWORD_RESULT_MASK	0x007ff000
+#define A_HIWORD_OPA_MASK	0x000007ff
+
+
+/* ------------------- STRUCTURES -------------------- */
+
+typedef struct _snd_emu10k1 emu10k1_t;
+typedef struct _snd_emu10k1_voice emu10k1_voice_t;
+typedef struct _snd_emu10k1_pcm emu10k1_pcm_t;
+
+typedef enum {
+	EMU10K1_PCM,
+	EMU10K1_SYNTH,
+	EMU10K1_MIDI
+} emu10k1_voice_type_t;
+
+struct _snd_emu10k1_voice {
+	emu10k1_t *emu;
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth: 1,
+	    midi: 1;
+	void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice);
+
+	emu10k1_pcm_t *epcm;
+};
+
+typedef enum {
+	PLAYBACK_EMUVOICE,
+	CAPTURE_AC97ADC,
+	CAPTURE_AC97MIC,
+	CAPTURE_EFX
+} snd_emu10k1_pcm_type_t;
+
+struct _snd_emu10k1_pcm {
+	emu10k1_t *emu;
+	snd_emu10k1_pcm_type_t type;
+	snd_pcm_substream_t *substream;
+	emu10k1_voice_t *voices[2];
+	emu10k1_voice_t *extra;
+	int running;
+	snd_util_memblk_t *memblk;
+	unsigned int start_addr;
+	unsigned int ccca_start_addr;
+	unsigned int capture_ipr;	/* interrupt acknowledge mask */
+	unsigned int capture_inte;	/* interrupt enable mask */
+	unsigned int capture_ba_reg;	/* buffer address register */
+	unsigned int capture_bs_reg;	/* buffer size register */
+	unsigned int capture_idx_reg;	/* buffer index register */
+	unsigned int capture_cr_val;	/* control value */
+	unsigned int capture_bs_val;	/* buffer size value */
+	unsigned int capture_bufsize;	/* buffer size in bytes */
+};
+
+typedef struct {
+	unsigned char send_routing[3][8];
+	unsigned char send_volume[3][8];
+	unsigned short attn[3];
+	snd_kcontrol_t *ctl_send_routing;
+	snd_kcontrol_t *ctl_send_volume;
+	snd_kcontrol_t *ctl_attn;
+	emu10k1_pcm_t *epcm;
+} emu10k1_pcm_mixer_t;
+
+#define snd_emu10k1_compose_send_routing(route) \
+((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16)
+
+#define snd_emu10k1_compose_audigy_fxrt1(route) \
+(((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 12)) << 24)
+
+#define snd_emu10k1_compose_audigy_fxrt2(route) \
+(((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 12)) << 24)
+
+typedef struct snd_emu10k1_memblk {
+	snd_util_memblk_t mem;
+	/* private part */
+	short first_page, last_page, pages, mapped_page;
+	unsigned int map_locked;
+	struct list_head mapped_link;
+	struct list_head mapped_order_link;
+} emu10k1_memblk_t;
+
+#define snd_emu10k1_memblk_offset(blk)	(((blk)->mapped_page << PAGE_SHIFT) | ((blk)->mem.offset & (PAGE_SIZE - 1)))
+
+#define EMU10K1_MAX_TRAM_BLOCKS_PER_CODE	16
+
+typedef struct {
+	struct list_head list;		/* list link container */
+	unsigned int vcount;
+	unsigned int count;		/* count of GPR (1..16) */
+	unsigned char gpr[32];		/* GPR number(s) */
+	unsigned int value[32];
+	unsigned int min;		/* minimum range */
+	unsigned int max;		/* maximum range */
+	unsigned int translation;	/* translation type (EMU10K1_GRP_TRANSLATION*) */
+	snd_kcontrol_t *kcontrol;
+} snd_emu10k1_fx8010_ctl_t;
+
+typedef void (snd_fx8010_irq_handler_t)(emu10k1_t *emu, void *private_data);
+
+typedef struct _snd_emu10k1_fx8010_irq {
+	struct _snd_emu10k1_fx8010_irq *next;
+	snd_fx8010_irq_handler_t *handler;
+	unsigned char gpr_running;
+	void *private_data;
+} snd_emu10k1_fx8010_irq_t;
+
+typedef struct {
+	unsigned int valid: 1,
+		     opened: 1,
+		     active: 1;
+	unsigned int channels;		/* 16-bit channels count */
+	unsigned int tram_start;	/* initial ring buffer position in TRAM (in samples) */
+	unsigned int buffer_size;	/* count of buffered samples */
+	unsigned char gpr_size;		/* GPR containing size of ring buffer in samples (host) */
+	unsigned char gpr_ptr;		/* GPR containing current pointer in the ring buffer (host = reset, FX8010) */
+	unsigned char gpr_count;	/* GPR containing count of samples between two interrupts (host) */
+	unsigned char gpr_tmpcount;	/* GPR containing current count of samples to interrupt (host = set, FX8010) */
+	unsigned char gpr_trigger;	/* GPR containing trigger (activate) information (host) */
+	unsigned char gpr_running;	/* GPR containing info if PCM is running (FX8010) */
+	unsigned char etram[32];	/* external TRAM address & data */
+	unsigned int sw_data, hw_data;
+	unsigned int sw_io, hw_io;
+	unsigned int sw_ready, hw_ready;
+	unsigned int appl_ptr;
+	unsigned int tram_pos;
+	unsigned int tram_shift;
+	snd_emu10k1_fx8010_irq_t *irq;
+} snd_emu10k1_fx8010_pcm_t;
+
+typedef struct {
+	unsigned short fxbus_mask;	/* used FX buses (bitmask) */
+	unsigned short extin_mask;	/* used external inputs (bitmask) */
+	unsigned short extout_mask;	/* used external outputs (bitmask) */
+	unsigned short pad1;
+	unsigned int itram_size;	/* internal TRAM size in samples */
+	unsigned int etram_size;	/* external TRAM size in samples */
+	void *etram_pages;		/* allocated pages for external TRAM */
+	dma_addr_t etram_pages_dmaaddr;
+	unsigned int dbg;		/* FX debugger register */
+	unsigned char name[128];
+	int gpr_size;			/* size of allocated GPR controls */
+	int gpr_count;			/* count of used kcontrols */
+	struct list_head gpr_ctl;	/* GPR controls */
+	struct semaphore lock;
+	snd_emu10k1_fx8010_pcm_t pcm[8];
+	spinlock_t irq_lock;
+	snd_emu10k1_fx8010_irq_t *irq_handlers;
+} snd_emu10k1_fx8010_t;
+
+#define emu10k1_gpr_ctl(n) list_entry(n, snd_emu10k1_fx8010_ctl_t, list)
+
+typedef struct {
+	struct _snd_emu10k1 *emu;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *substream_input;
+	snd_rawmidi_substream_t *substream_output;
+	unsigned int midi_mode;
+	spinlock_t input_lock;
+	spinlock_t output_lock;
+	spinlock_t open_lock;
+	int tx_enable, rx_enable;
+	int port;
+	int ipr_tx, ipr_rx;
+	void (*interrupt)(emu10k1_t *emu, unsigned int status);
+} emu10k1_midi_t;
+
+struct _snd_emu10k1 {
+	int irq;
+
+	unsigned long port;			/* I/O port number */
+	struct resource *res_port;
+	int APS: 1,				/* APS flag */
+	    tos_link: 1;			/* tos link detected */
+	unsigned int audigy;			/* is Audigy? */
+	unsigned int revision;			/* chip revision */
+	unsigned int serial;			/* serial number */
+	unsigned short model;			/* subsystem id */
+	unsigned int card_type;			/* EMU10K1_CARD_* */
+	unsigned int ecard_ctrl;		/* ecard control bits */
+	int max_cache_pages;			/* max memory size / PAGE_SIZE */
+	void *silent_page;			/* silent page */
+	dma_addr_t silent_page_dmaaddr;
+	volatile unsigned int *ptb_pages;	/* page table pages */
+	dma_addr_t ptb_pages_dmaaddr;
+	snd_util_memhdr_t *memhdr;		/* page allocation list */
+	emu10k1_memblk_t *reserved_page;	/* reserved page */
+
+	struct list_head mapped_link_head;
+	struct list_head mapped_order_link_head;
+	void **page_ptr_table;
+	unsigned long *page_addr_table;
+	spinlock_t memblk_lock;
+
+	unsigned int spdif_bits[3];		/* s/pdif out setup */
+
+	snd_emu10k1_fx8010_t fx8010;		/* FX8010 info */
+	int gpr_base;
+	
+	ac97_t *ac97;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm_mic;
+	snd_pcm_t *pcm_efx;
+	snd_pcm_t *pcm_fx8010;
+
+	spinlock_t synth_lock;
+	void *synth;
+	int (*get_synth_voice)(emu10k1_t *emu);
+
+	spinlock_t reg_lock;
+	spinlock_t emu_lock;
+	spinlock_t voice_lock;
+	struct semaphore ptb_lock;
+
+	emu10k1_voice_t voices[64];
+	emu10k1_pcm_mixer_t pcm_mixer[32];
+
+	void (*hwvol_interrupt)(emu10k1_t *emu, unsigned int status);
+	void (*capture_interrupt)(emu10k1_t *emu, unsigned int status);
+	void (*capture_mic_interrupt)(emu10k1_t *emu, unsigned int status);
+	void (*capture_efx_interrupt)(emu10k1_t *emu, unsigned int status);
+	void (*timer_interrupt)(emu10k1_t *emu);
+	void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status);
+	void (*dsp_interrupt)(emu10k1_t *emu);
+
+	snd_pcm_substream_t *pcm_capture_substream;
+	snd_pcm_substream_t *pcm_capture_mic_substream;
+	snd_pcm_substream_t *pcm_capture_efx_substream;
+
+	emu10k1_midi_t midi;
+	emu10k1_midi_t midi2; /* for audigy */
+
+	unsigned int efx_voices_mask;
+
+	snd_info_entry_t *proc_entry;
+	snd_info_entry_t *proc_entry_fx8010_gpr;
+	snd_info_entry_t *proc_entry_fx8010_tram_data;
+	snd_info_entry_t *proc_entry_fx8010_tram_addr;
+	snd_info_entry_t *proc_entry_fx8010_code;
+	snd_info_entry_t *proc_entry_fx8010_iblocks;
+};
+
+int snd_emu10k1_create(snd_card_t * card,
+		       struct pci_dev *pci,
+		       unsigned short extin_mask,
+		       unsigned short extout_mask,
+		       long max_cache_bytes,
+		       int enable_ir,
+		       emu10k1_t ** remu);
+
+int snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
+int snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
+int snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
+int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
+int snd_emu10k1_mixer(emu10k1_t * emu);
+int snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep);
+
+void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/* initialization */
+void snd_emu10k1_voice_init(emu10k1_t * emu, int voice);
+int snd_emu10k1_init_efx(emu10k1_t *emu);
+void snd_emu10k1_free_efx(emu10k1_t *emu);
+int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size);
+
+/* I/O functions */
+unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn);
+void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data);
+void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data);
+unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc);
+void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb);
+void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb);
+void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait);
+static inline unsigned int snd_emu10k1_wc(emu10k1_t *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; }
+unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg);
+void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data);
+unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate);
+unsigned char snd_emu10k1_sum_vol_attn(unsigned int value);
+
+/* memory allocation */
+snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size);
+int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk);
+snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size);
+int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk);
+int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size);
+int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size);
+int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk);
+
+/* voice allocation */
+int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice);
+int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice);
+
+/* MIDI uart */
+int snd_emu10k1_midi(emu10k1_t * emu);
+int snd_emu10k1_audigy_midi(emu10k1_t * emu);
+
+/* proc interface */
+int snd_emu10k1_proc_init(emu10k1_t * emu);
+int snd_emu10k1_proc_done(emu10k1_t * emu);
+
+#endif /* __KERNEL__ */
+
+/*
+ * ---- FX8010 ----
+ */
+
+#define EMU10K1_CARD_CREATIVE			0x00000000
+#define EMU10K1_CARD_EMUAPS			0x00000001
+
+#define EMU10K1_FX8010_PCM_COUNT		8
+
+/* instruction set */
+#define iMAC0	 0x00	/* R = A + (X * Y >> 31)   ; saturation */
+#define iMAC1	 0x01	/* R = A + (-X * Y >> 31)  ; saturation */
+#define iMAC2	 0x02	/* R = A + (X * Y >> 31)   ; wraparound */
+#define iMAC3	 0x03	/* R = A + (-X * Y >> 31)  ; wraparound */
+#define iMACINT0 0x04	/* R = A + X * Y	   ; saturation */
+#define iMACINT1 0x05	/* R = A + X * Y	   ; wraparound (31-bit) */
+#define iACC3	 0x06	/* R = A + X + Y	   ; saturation */
+#define iMACMV   0x07	/* R = A, acc += X * Y >> 31 */
+#define iANDXOR  0x08	/* R = (A & X) ^ Y */
+#define iTSTNEG  0x09	/* R = (A >= Y) ? X : ~X */
+#define iLIMITGE 0x0a	/* R = (A >= Y) ? X : Y */
+#define iLIMITLT 0x0b	/* R = (A < Y) ? X : Y */
+#define iLOG	 0x0c	/* R = linear_data, A (log_data), X (max_exp), Y (format_word) */
+#define iEXP	 0x0d	/* R = log_data, A (linear_data), X (max_exp), Y (format_word) */
+#define iINTERP  0x0e	/* R = A + (X * (Y - A) >> 31)  ; saturation */
+#define iSKIP    0x0f	/* R = A (cc_reg), X (count), Y (cc_test) */
+
+/* GPRs */
+#define FXBUS(x)	(0x00 + (x))	/* x = 0x00 - 0x0f */
+#define EXTIN(x)	(0x10 + (x))	/* x = 0x00 - 0x0f */
+#define EXTOUT(x)	(0x20 + (x))	/* x = 0x00 - 0x0f */
+#define C_00000000	0x40
+#define C_00000001	0x41
+#define C_00000002	0x42
+#define C_00000003	0x43
+#define C_00000004	0x44
+#define C_00000008	0x45
+#define C_00000010	0x46
+#define C_00000020	0x47
+#define C_00000100	0x48
+#define C_00010000	0x49
+#define C_00080000	0x4a
+#define C_10000000	0x4b
+#define C_20000000	0x4c
+#define C_40000000	0x4d
+#define C_80000000	0x4e
+#define C_7fffffff	0x4f
+#define C_ffffffff	0x50
+#define C_fffffffe	0x51
+#define C_c0000000	0x52
+#define C_4f1bbcdc	0x53
+#define C_5a7ef9db	0x54
+#define C_00100000	0x55		/* ?? */
+#define GPR_ACCU	0x56		/* ACCUM, accumulator */
+#define GPR_COND	0x57		/* CCR, condition register */
+#define GPR_NOISE0	0x58		/* noise source */
+#define GPR_NOISE1	0x59		/* noise source */
+#define GPR_IRQ		0x5a		/* IRQ register */
+#define GPR_DBAC	0x5b		/* TRAM Delay Base Address Counter */
+#define GPR(x)		(FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */
+#define ITRAM_DATA(x)	(TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */
+#define ETRAM_DATA(x)	(TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */
+#define ITRAM_ADDR(x)	(TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */
+#define ETRAM_ADDR(x)	(TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */
+
+#define A_FXBUS(x)	(0x00 + (x))	/* x = 0x00 - 0x3f? */
+#define A_EXTIN(x)	(0x40 + (x))	/* x = 0x00 - 0x1f? */
+#define A_EXTOUT(x)	(0x60 + (x))	/* x = 0x00 - 0x1f? */
+#define A_GPR(x)	(A_FXGPREGBASE + (x))
+
+/* cc_reg constants */
+#define CC_REG_NORMALIZED C_00000001
+#define CC_REG_BORROW	C_00000002
+#define CC_REG_MINUS	C_00000004
+#define CC_REG_ZERO	C_00000008
+#define CC_REG_SATURATE	C_00000010
+#define CC_REG_NONZERO	C_00000100
+
+/* FX buses */
+#define FXBUS_PCM_LEFT		0x00
+#define FXBUS_PCM_RIGHT		0x01
+#define FXBUS_PCM_LEFT_REAR	0x02
+#define FXBUS_PCM_RIGHT_REAR	0x03
+#define FXBUS_MIDI_LEFT		0x04
+#define FXBUS_MIDI_RIGHT	0x05
+#define FXBUS_PCM_CENTER	0x06
+#define FXBUS_PCM_LFE		0x07
+#define FXBUS_MIDI_REVERB	0x0c
+#define FXBUS_MIDI_CHORUS	0x0d
+
+/* Inputs */
+#define EXTIN_AC97_L	   0x00	/* AC'97 capture channel - left */
+#define EXTIN_AC97_R	   0x01	/* AC'97 capture channel - right */
+#define EXTIN_SPDIF_CD_L   0x02	/* internal S/PDIF CD - onboard - left */
+#define EXTIN_SPDIF_CD_R   0x03	/* internal S/PDIF CD - onboard - right */
+#define EXTIN_ZOOM_L	   0x04	/* Zoom Video I2S - left */
+#define EXTIN_ZOOM_R	   0x05	/* Zoom Video I2S - right */
+#define EXTIN_TOSLINK_L	   0x06	/* LiveDrive - TOSLink Optical - left */
+#define EXTIN_TOSLINK_R    0x07	/* LiveDrive - TOSLink Optical - right */
+#define EXTIN_LINE1_L	   0x08	/* LiveDrive - Line/Mic 1 - left */
+#define EXTIN_LINE1_R	   0x09	/* LiveDrive - Line/Mic 1 - right */
+#define EXTIN_COAX_SPDIF_L 0x0a	/* LiveDrive - Coaxial S/PDIF - left */
+#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */
+#define EXTIN_LINE2_L	   0x0c	/* LiveDrive - Line/Mic 2 - left */
+#define EXTIN_LINE2_R	   0x0d	/* LiveDrive - Line/Mic 2 - right */
+
+/* Outputs */
+#define EXTOUT_AC97_L	   0x00	/* AC'97 playback channel - left */
+#define EXTOUT_AC97_R	   0x01	/* AC'97 playback channel - right */
+#define EXTOUT_TOSLINK_L   0x02	/* LiveDrive - TOSLink Optical - left */
+#define EXTOUT_TOSLINK_R   0x03	/* LiveDrive - TOSLink Optical - right */
+#define EXTOUT_CENTER      0x04	/* SB Live 5.1 - center */
+#define EXTOUT_LFE         0x05 /* SB Live 5.1 - LFE */
+#define EXTOUT_HEADPHONE_L 0x06	/* LiveDrive - Headphone - left */
+#define EXTOUT_HEADPHONE_R 0x07	/* LiveDrive - Headphone - right */
+#define EXTOUT_REAR_L	   0x08	/* Rear channel - left */
+#define EXTOUT_REAR_R	   0x09	/* Rear channel - right */
+#define EXTOUT_ADC_CAP_L   0x0a	/* ADC Capture buffer - left */
+#define EXTOUT_ADC_CAP_R   0x0b	/* ADC Capture buffer - right */
+#define EXTOUT_MIC_CAP	   0x0c	/* MIC Capture buffer */
+#define EXTOUT_ACENTER	   0x11 /* Analog Center */
+#define EXTOUT_ALFE	   0x12 /* Analog LFE */
+
+/* Audigy Inputs */
+#define A_EXTIN_AC97_L		0x00	/* AC'97 capture channel - left */
+#define A_EXTIN_AC97_R		0x01	/* AC'97 capture channel - right */
+#define A_EXTIN_SPDIF_CD_L	0x02	/* digital CD left */
+#define A_EXTIN_SPDIF_CD_R	0x03	/* digital CD left */
+#define A_EXTIN_LINE2_L		0x08	/* audigy drive line2/mic2 - left */
+#define A_EXTIN_LINE2_R		0x09	/*                           right */
+#define A_EXTIN_AUX2_L		0x0c	/* audigy drive aux2 - left */
+#define A_EXTIN_AUX2_R		0x0d	/*                   - right */
+
+/* Audigiy Outputs */
+#define A_EXTOUT_FRONT_L	0x00	/* digital front left */
+#define A_EXTOUT_FRONT_R	0x01	/*               right */
+#define A_EXTOUT_CENTER		0x02	/* digital front center */
+#define A_EXTOUT_LFE		0x03	/* digital front lfe */
+#define A_EXTOUT_HEADPHONE_L	0x04	/* headphone audigy drive left */
+#define A_EXTOUT_HEADPHONE_R	0x05	/*                        right */
+#define A_EXTOUT_REAR_L		0x06	/* digital rear left */
+#define A_EXTOUT_REAR_R		0x07	/*              right */
+#define A_EXTOUT_AFRONT_L	0x08	/* analog front left */
+#define A_EXTOUT_AFRONT_R	0x09	/*              right */
+#define A_EXTOUT_ACENTER	0x0a	/* analog center */
+#define A_EXTOUT_ALFE		0x0b	/* analog LFE */
+/* 0x0c ?? */
+/* 0x0d ?? */
+#define A_EXTOUT_AREAR_L	0x0e	/* analog rear left */
+#define A_EXTOUT_AREAR_R	0x0f	/*             right */
+#define A_EXTOUT_AC97_L		0x10	/* AC97 left (front) */
+#define A_EXTOUT_AC97_R		0x11	/*      right */
+#define A_EXTOUT_ADC_CAP_L	0x12	/* ADC capture buffer left */
+#define A_EXTOUT_ADC_CAP_R	0x13	/*                    right */
+
+/* Audigy constants */
+#define A_C_00000000	0xc0
+#define A_C_00000001	0xc1
+#define A_C_00000002	0xc2
+#define A_C_00000003	0xc3
+#define A_C_00000004	0xc4
+#define A_C_00000008	0xc5
+#define A_C_00000010	0xc6
+#define A_C_00000020	0xc7
+#define A_C_00000100	0xc8
+#define A_C_00010000	0xc9
+#define A_C_00000800	0xca
+#define A_C_10000000	0xcb
+#define A_C_20000000	0xcc
+#define A_C_40000000	0xcd
+#define A_C_80000000	0xce
+#define A_C_7fffffff	0xcf
+#define A_C_ffffffff	0xd0
+#define A_C_fffffffe	0xd1
+#define A_C_c0000000	0xd2
+#define A_C_4f1bbcdc	0xd3
+#define A_C_5a7ef9db	0xd4
+#define A_C_00100000	0xd5
+/* 0xd6 = 0x7fffffff  (?) ACCUM? */
+/* 0xd7 = 0x0000000   CCR */
+/* 0xd8 = noise1 */
+/* 0xd9 = noise2 */
+
+/* definitions for debug register */
+#define EMU10K1_DBG_ZC			0x80000000	/* zero tram counter */
+#define EMU10K1_DBG_SATURATION_OCCURED	0x02000000	/* saturation control */
+#define EMU10K1_DBG_SATURATION_ADDR	0x01ff0000	/* saturation address */
+#define EMU10K1_DBG_SINGLE_STEP		0x00008000	/* single step mode */
+#define EMU10K1_DBG_STEP		0x00004000	/* start single step */
+#define EMU10K1_DBG_CONDITION_CODE	0x00003e00	/* condition code */
+#define EMU10K1_DBG_SINGLE_STEP_ADDR	0x000001ff	/* single step address */
+
+/* tank memory address line */
+#ifndef __KERNEL__
+#define TANKMEMADDRREG_ADDR_MASK 0x000fffff	/* 20 bit tank address field			*/
+#define TANKMEMADDRREG_CLEAR	 0x00800000	/* Clear tank memory				*/
+#define TANKMEMADDRREG_ALIGN	 0x00400000	/* Align read or write relative to tank access	*/
+#define TANKMEMADDRREG_WRITE	 0x00200000	/* Write to tank memory				*/
+#define TANKMEMADDRREG_READ	 0x00100000	/* Read from tank memory			*/
+#endif
+
+typedef struct {
+	unsigned int card;			/* card type */
+	unsigned int internal_tram_size;	/* in samples */
+	unsigned int external_tram_size;	/* in samples */
+	char fxbus_names[16][32];		/* names of FXBUSes */
+	char extin_names[16][32];		/* names of external inputs */
+	char extout_names[32][32];		/* names of external outputs */
+	unsigned int gpr_controls;		/* count of GPR controls */
+} emu10k1_fx8010_info_t;
+
+#define EMU10K1_GPR_TRANSLATION_NONE		0
+#define EMU10K1_GPR_TRANSLATION_TABLE100	1
+#define EMU10K1_GRP_TRANSLATION_BASS		2
+#define EMU10K1_GRP_TRANSLATION_TREBLE		3
+#define EMU10K1_GPR_TRANSLATION_ONOFF		4
+
+typedef struct {
+	snd_ctl_elem_id_t id;		/* full control ID definition */
+	unsigned int vcount;		/* visible count */
+	unsigned int count;		/* count of GPR (1..16) */
+	unsigned char gpr[32];		/* GPR number(s) */
+	unsigned int value[32];		/* initial values */
+	unsigned int min;		/* minimum range */
+	unsigned int max;		/* maximum range */
+	unsigned int translation;	/* translation type (EMU10K1_GRP_TRANSLATION*) */
+} emu10k1_fx8010_control_gpr_t;
+
+typedef struct {
+	char name[128];
+	unsigned int gpr_valid[0x100/32]; /* bitmask of valid initializers */
+	unsigned int gpr_map[0x100];	  /* initializers */
+	unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */
+	emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */
+	unsigned int gpr_del_control_count; /* count of GPR controls to remove */
+	snd_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */
+	unsigned int tram_valid[0xa0/32]; /* bitmask of valid initializers */
+	unsigned int tram_data_map[0xa0]; /* data initializers */
+	unsigned int tram_addr_map[0xa0]; /* map initializers */
+	unsigned int code_valid[512/32];  /* bitmask of valid instructions */
+	unsigned int code[512][2];	  /* one instruction - 64 bits */
+} emu10k1_fx8010_code_t;
+
+typedef struct {
+	unsigned int address;		/* 31.bit == 1 -> external TRAM */
+	unsigned int size;		/* size in samples (4 bytes) */
+	unsigned int *samples;		/* pointer to samples (20-bit) */
+					/* NULL->clear memory */
+} emu10k1_fx8010_tram_t;
+
+typedef struct {
+	unsigned int substream;		/* substream number */
+	unsigned int res1;		/* reserved */
+	unsigned int channels;		/* 16-bit channels count, zero = remove this substream */
+	unsigned int tram_start;	/* ring buffer position in TRAM (in samples) */
+	unsigned int buffer_size;	/* count of buffered samples */
+	unsigned char gpr_size;		/* GPR containing size of ringbuffer in samples (host) */
+	unsigned char gpr_ptr;		/* GPR containing current pointer in the ring buffer (host = reset, FX8010) */
+	unsigned char gpr_count;	/* GPR containing count of samples between two interrupts (host) */
+	unsigned char gpr_tmpcount;	/* GPR containing current count of samples to interrupt (host = set, FX8010) */
+	unsigned char gpr_trigger;	/* GPR containing trigger (activate) information (host) */
+	unsigned char gpr_running;	/* GPR containing info if PCM is running (FX8010) */
+	unsigned char pad;		/* reserved */
+	unsigned char etram[32];	/* external TRAM address & data (one per channel) */
+	unsigned int res2;		/* reserved */
+} emu10k1_fx8010_pcm_t;
+
+#define SNDRV_EMU10K1_IOCTL_INFO	_IOR ('H', 0x10, emu10k1_fx8010_info_t)
+#define SNDRV_EMU10K1_IOCTL_CODE_POKE	_IOW ('H', 0x11, emu10k1_fx8010_code_t)
+#define SNDRV_EMU10K1_IOCTL_CODE_PEEK	_IOW ('H', 0x12, emu10k1_fx8010_code_t)
+#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP	_IOW ('H', 0x20, int)
+#define SNDRV_EMU10K1_IOCTL_TRAM_POKE	_IOW ('H', 0x21, emu10k1_fx8010_tram_t)
+#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK	_IOR ('H', 0x22, emu10k1_fx8010_tram_t)
+#define SNDRV_EMU10K1_IOCTL_PCM_POKE	_IOW ('H', 0x30, emu10k1_fx8010_pcm_t)
+#define SNDRV_EMU10K1_IOCTL_PCM_PEEK	_IOWR('H', 0x31, emu10k1_fx8010_pcm_t)
+#define SNDRV_EMU10K1_IOCTL_STOP	_IO  ('H', 0x80)
+#define SNDRV_EMU10K1_IOCTL_CONTINUE	_IO  ('H', 0x81)
+#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82)
+#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP	_IOW ('H', 0x83, int)
+#define SNDRV_EMU10K1_IOCTL_DBG_READ	_IOR ('H', 0x84, int)
+
+#endif	/* __SOUND_EMU10K1_H */
diff -Nru linux/include/sound/emu10k1_synth.h linux-2.4.19-pre5-mjc/include/sound/emu10k1_synth.h
--- linux/include/sound/emu10k1_synth.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/emu10k1_synth.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,39 @@
+#ifndef __EMU10K1_SYNTH_H
+#define __EMU10K1_SYNTH_H
+/*
+ *  Defines for the Emu10k1 WaveTable synth
+ *
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include "emu10k1.h"
+#include "emux_synth.h"
+
+/* sequencer device id */
+#define SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH	"emu10k1-synth"
+
+/* argument for snd_seq_device_new */
+typedef struct snd_emu10k1_synth_arg {
+	emu10k1_t *hwptr;	/* chip */
+	int index;		/* sequencer client index */
+	int seq_ports;		/* number of sequencer ports to be created */
+	int max_voices;		/* maximum number of voices for wavetable */
+} snd_emu10k1_synth_arg_t;
+
+#define EMU10K1_MAX_MEMSIZE	(32 * 1024 * 1024) /* 32MB */
+
+#endif
diff -Nru linux/include/sound/emu8000.h linux-2.4.19-pre5-mjc/include/sound/emu8000.h
--- linux/include/sound/emu8000.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/emu8000.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,116 @@
+#ifndef __SOUND_EMU8000_H
+#define __SOUND_EMU8000_H
+/*
+ *  Defines for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include "emux_synth.h"
+#include "seq_kernel.h"
+
+/*
+ * Hardware parameters.
+ */
+#define EMU8000_MAX_DRAM (28 * 1024 * 1024) /* Max on-board mem is 28Mb ???*/
+#define EMU8000_DRAM_OFFSET 0x200000	/* Beginning of on board ram */
+#define EMU8000_CHANNELS   32	/* Number of hardware channels */
+#define EMU8000_DRAM_VOICES	30	/* number of normal voices */
+
+/* Flags to set a dma channel to read or write */
+#define EMU8000_RAM_READ   0
+#define EMU8000_RAM_WRITE  1
+#define EMU8000_RAM_CLOSE  2
+
+enum {
+	EMU8000_CONTROL_BASS = 0,
+	EMU8000_CONTROL_TREBLE,
+	EMU8000_CONTROL_CHORUS_MODE,
+	EMU8000_CONTROL_REVERB_MODE,
+	EMU8000_CONTROL_FM_CHORUS_DEPTH,
+	EMU8000_CONTROL_FM_REVERB_DEPTH,
+	EMU8000_NUM_CONTROLS,
+};
+
+/*
+ * Structure to hold all state information for the emu8000 driver.
+ *
+ * Note 1: The chip supports 32 channels in hardware this is max_channels
+ * some of the channels may be used for other things so max_channels is
+ * the number in use for wave voices.
+ */
+typedef struct snd_emu8000 {
+
+	snd_emux_t *emu;
+
+	int index;		/* sequencer client index */
+	int seq_ports;		/* number of sequencer ports */
+	int fm_chorus_depth;	/* FM OPL3 chorus depth */
+	int fm_reverb_depth;	/* FM OPL3 reverb depth */
+
+	int mem_size;		/* memory size */
+	unsigned long port1;	/* Port usually base+0 */
+	unsigned long port2;	/* Port usually at base+0x400 */
+	unsigned long port3;	/* Port usually at base+0x800 */
+	struct resource *res_port1;
+	struct resource *res_port2;
+	struct resource *res_port3;
+	unsigned short last_reg;/* Last register command */
+	spinlock_t reg_lock;
+
+	int dram_checked;
+
+	snd_card_t *card;		/* The card that this belongs to */
+
+	int chorus_mode;
+	int reverb_mode;
+	int bass_level;
+	int treble_level;
+
+	snd_util_memhdr_t *memhdr;
+
+	spinlock_t control_lock;
+	snd_kcontrol_t *controls[EMU8000_NUM_CONTROLS];
+
+} emu8000_t;
+
+/* sequencer device id */
+#define SNDRV_SEQ_DEV_ID_EMU8000	"emu8000-synth"
+
+
+/* exported functions */
+int snd_emu8000_new(snd_card_t *card, int device, long port, int seq_ports, snd_seq_device_t **ret);
+void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg,
+		      unsigned int val);
+unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port,
+				unsigned int reg);
+void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg,
+			 unsigned int val);
+unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port,
+				 unsigned int reg);
+void snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode);
+
+void snd_emu8000_init_fm(emu8000_t *emu);
+
+void snd_emu8000_update_chorus_mode(emu8000_t *emu);
+void snd_emu8000_update_reverb_mode(emu8000_t *emu);
+void snd_emu8000_update_equalizer(emu8000_t *emu);
+int snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len);
+int snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len);
+
+#endif /* __SOUND_EMU8000_H */
diff -Nru linux/include/sound/emu8000_reg.h linux-2.4.19-pre5-mjc/include/sound/emu8000_reg.h
--- linux/include/sound/emu8000_reg.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/emu8000_reg.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,207 @@
+#ifndef __SOUND_EMU8000_REG_H
+#define __SOUND_EMU8000_REG_H
+/*
+ *  Register operations for the EMU8000
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *  Based on awe_wave.c by Takashi Iwai
+ *
+ *   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
+ *
+ */
+
+/*
+ * Data port addresses relative to the EMU base.
+ */
+#define EMU8000_DATA0(e)    ((e)->port1)
+#define EMU8000_DATA1(e)    ((e)->port2)
+#define EMU8000_DATA2(e)    ((e)->port2+2)
+#define EMU8000_DATA3(e)    ((e)->port3)
+#define EMU8000_PTR(e)      ((e)->port3+2)
+
+/*
+ * Make a command from a register and channel.
+ */
+#define EMU8000_CMD(reg, chan) ((reg)<<5 | (chan))
+
+/*
+ * Commands to read and write the EMU8000 registers.
+ * These macros should be used for all register accesses.
+ */
+#define EMU8000_CPF_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)))
+#define EMU8000_PTRX_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)))
+#define EMU8000_CVCF_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_VTFT_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_PSST_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)))
+#define EMU8000_CSL_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)))
+#define EMU8000_CCCA_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)))
+#define EMU8000_HWCF4_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9))
+#define EMU8000_HWCF5_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10))
+#define EMU8000_HWCF6_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13))
+#define EMU8000_SMALR_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20))
+#define EMU8000_SMARR_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21))
+#define EMU8000_SMALW_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22))
+#define EMU8000_SMARW_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23))
+#define EMU8000_SMLD_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26))
+#define EMU8000_SMRD_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26))
+#define EMU8000_WC_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27))
+#define EMU8000_HWCF1_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29))
+#define EMU8000_HWCF2_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30))
+#define EMU8000_HWCF3_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31))
+#define EMU8000_INIT1_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_INIT2_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_INIT3_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_INIT4_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_ENVVOL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)))
+#define EMU8000_DCYSUSV_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)))
+#define EMU8000_ENVVAL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)))
+#define EMU8000_DCYSUS_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)))
+#define EMU8000_ATKHLDV_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)))
+#define EMU8000_LFO1VAL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)))
+#define EMU8000_ATKHLD_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)))
+#define EMU8000_LFO2VAL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)))
+#define EMU8000_IP_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)))
+#define EMU8000_IFATN_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)))
+#define EMU8000_PEFE_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_FMMOD_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_TREMFRQ_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)))
+#define EMU8000_FM2FRQ2_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)))
+
+
+#define EMU8000_CPF_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)), (val))
+#define EMU8000_PTRX_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)), (val))
+#define EMU8000_CVCF_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_VTFT_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_PSST_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)), (val))
+#define EMU8000_CSL_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)), (val))
+#define EMU8000_CCCA_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)), (val))
+#define EMU8000_HWCF4_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9), (val))
+#define EMU8000_HWCF5_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10), (val))
+#define EMU8000_HWCF6_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13), (val))
+/* this register is not documented */
+#define EMU8000_HWCF7_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 14), (val))
+#define EMU8000_SMALR_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20), (val))
+#define EMU8000_SMARR_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21), (val))
+#define EMU8000_SMALW_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22), (val))
+#define EMU8000_SMARW_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23), (val))
+#define EMU8000_SMLD_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26), (val))
+#define EMU8000_SMRD_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26), (val))
+#define EMU8000_WC_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27), (val))
+#define EMU8000_HWCF1_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29), (val))
+#define EMU8000_HWCF2_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30), (val))
+#define EMU8000_HWCF3_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31), (val))
+#define EMU8000_INIT1_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_INIT2_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_INIT3_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_INIT4_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_ENVVOL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_DCYSUSV_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)), (val))
+#define EMU8000_ENVVAL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)), (val))
+#define EMU8000_DCYSUS_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)), (val))
+#define EMU8000_ATKHLDV_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_LFO1VAL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)), (val))
+#define EMU8000_ATKHLD_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)), (val))
+#define EMU8000_LFO2VAL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)), (val))
+#define EMU8000_IP_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)), (val))
+#define EMU8000_IFATN_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)), (val))
+#define EMU8000_PEFE_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_FMMOD_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_TREMFRQ_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_FM2FRQ2_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)), (val))
+
+#define EMU8000_0080_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_00A0_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(5, (chan)), (val))
+
+#endif /* __SOUND_EMU8000_REG_H */
diff -Nru linux/include/sound/emux_legacy.h linux-2.4.19-pre5-mjc/include/sound/emux_legacy.h
--- linux/include/sound/emux_legacy.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/emux_legacy.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,146 @@
+#ifndef __SOUND_EMUX_LEGACY_H
+#define __SOUND_EMUX_LEGACY_H
+
+/*
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Definitions of OSS compatible headers for Emu8000 device informations
+ *
+ *   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
+ *
+ */
+
+#include "seq_oss_legacy.h"
+
+/*
+ * awe hardware controls
+ */
+
+#define _EMUX_OSS_DEBUG_MODE		0x00
+#define _EMUX_OSS_REVERB_MODE		0x01
+#define _EMUX_OSS_CHORUS_MODE		0x02
+#define _EMUX_OSS_REMOVE_LAST_SAMPLES	0x03
+#define _EMUX_OSS_INITIALIZE_CHIP	0x04
+#define _EMUX_OSS_SEND_EFFECT		0x05
+#define _EMUX_OSS_TERMINATE_CHANNEL	0x06
+#define _EMUX_OSS_TERMINATE_ALL		0x07
+#define _EMUX_OSS_INITIAL_VOLUME	0x08
+#define _EMUX_OSS_INITIAL_ATTEN	_EMUX_OSS_INITIAL_VOLUME
+#define _EMUX_OSS_RESET_CHANNEL		0x09
+#define _EMUX_OSS_CHANNEL_MODE		0x0a
+#define _EMUX_OSS_DRUM_CHANNELS		0x0b
+#define _EMUX_OSS_MISC_MODE		0x0c
+#define _EMUX_OSS_RELEASE_ALL		0x0d
+#define _EMUX_OSS_NOTEOFF_ALL		0x0e
+#define _EMUX_OSS_CHN_PRESSURE		0x0f
+#define _EMUX_OSS_EQUALIZER		0x11
+
+#define _EMUX_OSS_MODE_FLAG		0x80
+#define _EMUX_OSS_COOKED_FLAG		0x40	/* not supported */
+#define _EMUX_OSS_MODE_VALUE_MASK	0x3F
+
+
+/*
+ * mode type definitions
+ */
+enum {
+/* 0*/	EMUX_MD_EXCLUSIVE_OFF,	/* obsolete */
+/* 1*/	EMUX_MD_EXCLUSIVE_ON,	/* obsolete */
+/* 2*/	EMUX_MD_VERSION,		/* read only */
+/* 3*/	EMUX_MD_EXCLUSIVE_SOUND,	/* 0/1: exclusive note on (default=1) */
+/* 4*/	EMUX_MD_REALTIME_PAN,	/* 0/1: do realtime pan change (default=1) */
+/* 5*/	EMUX_MD_GUS_BANK,	/* bank number for GUS patches (default=0) */
+/* 6*/	EMUX_MD_KEEP_EFFECT,	/* 0/1: keep effect values, (default=0) */
+/* 7*/	EMUX_MD_ZERO_ATTEN,	/* attenuation of max volume (default=32) */
+/* 8*/	EMUX_MD_CHN_PRIOR,	/* 0/1: set MIDI channel priority mode (default=1) */
+/* 9*/	EMUX_MD_MOD_SENSE,	/* integer: modwheel sensitivity (def=18) */
+/*10*/	EMUX_MD_DEF_PRESET,	/* integer: default preset number (def=0) */
+/*11*/	EMUX_MD_DEF_BANK,	/* integer: default bank number (def=0) */
+/*12*/	EMUX_MD_DEF_DRUM,	/* integer: default drumset number (def=0) */
+/*13*/	EMUX_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */
+/*14*/	EMUX_MD_NEW_VOLUME_CALC,	/* 0/1: volume calculation mode (def=1) */
+/*15*/	EMUX_MD_CHORUS_MODE,	/* integer: chorus mode (def=2) */
+/*16*/	EMUX_MD_REVERB_MODE,	/* integer: chorus mode (def=4) */
+/*17*/	EMUX_MD_BASS_LEVEL,	/* integer: bass level (def=5) */
+/*18*/	EMUX_MD_TREBLE_LEVEL,	/* integer: treble level (def=9) */
+/*19*/	EMUX_MD_DEBUG_MODE,	/* integer: debug level (def=0) */
+/*20*/	EMUX_MD_PAN_EXCHANGE,	/* 0/1: exchange panning direction (def=0) */
+	EMUX_MD_END,
+};
+
+
+/*
+ * effect parameters
+ */
+enum {
+
+/* modulation envelope parameters */
+/* 0*/	EMUX_FX_ENV1_DELAY,	/* WORD: ENVVAL */
+/* 1*/	EMUX_FX_ENV1_ATTACK,	/* BYTE: up ATKHLD */
+/* 2*/	EMUX_FX_ENV1_HOLD,	/* BYTE: lw ATKHLD */
+/* 3*/	EMUX_FX_ENV1_DECAY,	/* BYTE: lw DCYSUS */
+/* 4*/	EMUX_FX_ENV1_RELEASE,	/* BYTE: lw DCYSUS */
+/* 5*/	EMUX_FX_ENV1_SUSTAIN,	/* BYTE: up DCYSUS */
+/* 6*/	EMUX_FX_ENV1_PITCH,	/* BYTE: up PEFE */
+/* 7*/	EMUX_FX_ENV1_CUTOFF,	/* BYTE: lw PEFE */
+
+/* volume envelope parameters */
+/* 8*/	EMUX_FX_ENV2_DELAY,	/* WORD: ENVVOL */
+/* 9*/	EMUX_FX_ENV2_ATTACK,	/* BYTE: up ATKHLDV */
+/*10*/	EMUX_FX_ENV2_HOLD,	/* BYTE: lw ATKHLDV */
+/*11*/	EMUX_FX_ENV2_DECAY,	/* BYTE: lw DCYSUSV */
+/*12*/	EMUX_FX_ENV2_RELEASE,	/* BYTE: lw DCYSUSV */
+/*13*/	EMUX_FX_ENV2_SUSTAIN,	/* BYTE: up DCYSUSV */
+	
+/* LFO1 (tremolo & vibrato) parameters */
+/*14*/	EMUX_FX_LFO1_DELAY,	/* WORD: LFO1VAL */
+/*15*/	EMUX_FX_LFO1_FREQ,	/* BYTE: lo TREMFRQ */
+/*16*/	EMUX_FX_LFO1_VOLUME,	/* BYTE: up TREMFRQ */
+/*17*/	EMUX_FX_LFO1_PITCH,	/* BYTE: up FMMOD */
+/*18*/	EMUX_FX_LFO1_CUTOFF,	/* BYTE: lo FMMOD */
+
+/* LFO2 (vibrato) parameters */
+/*19*/	EMUX_FX_LFO2_DELAY,	/* WORD: LFO2VAL */
+/*20*/	EMUX_FX_LFO2_FREQ,	/* BYTE: lo FM2FRQ2 */
+/*21*/	EMUX_FX_LFO2_PITCH,	/* BYTE: up FM2FRQ2 */
+
+/* Other overall effect parameters */
+/*22*/	EMUX_FX_INIT_PITCH,	/* SHORT: pitch offset */
+/*23*/	EMUX_FX_CHORUS,		/* BYTE: chorus effects send (0-255) */
+/*24*/	EMUX_FX_REVERB,		/* BYTE: reverb effects send (0-255) */
+/*25*/	EMUX_FX_CUTOFF,		/* BYTE: up IFATN */
+/*26*/	EMUX_FX_FILTERQ,		/* BYTE: up CCCA */
+
+/* Sample / loop offset changes */
+/*27*/	EMUX_FX_SAMPLE_START,	/* SHORT: offset */
+/*28*/	EMUX_FX_LOOP_START,	/* SHORT: offset */
+/*29*/	EMUX_FX_LOOP_END,	/* SHORT: offset */
+/*30*/	EMUX_FX_COARSE_SAMPLE_START,	/* SHORT: upper word offset */
+/*31*/	EMUX_FX_COARSE_LOOP_START,	/* SHORT: upper word offset */
+/*32*/	EMUX_FX_COARSE_LOOP_END,		/* SHORT: upper word offset */
+/*33*/	EMUX_FX_ATTEN,		/* BYTE: lo IFATN */
+
+	EMUX_FX_END,
+};
+/* number of effects */
+#define EMUX_NUM_EFFECTS  EMUX_FX_END
+
+/* effect flag values */
+#define EMUX_FX_FLAG_OFF	0
+#define EMUX_FX_FLAG_SET	1
+#define EMUX_FX_FLAG_ADD	2
+
+
+#endif /* __SOUND_EMUX_LEGACY_H */
diff -Nru linux/include/sound/emux_synth.h linux-2.4.19-pre5-mjc/include/sound/emux_synth.h
--- linux/include/sound/emux_synth.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/emux_synth.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,239 @@
+#ifndef __SOUND_EMUX_SYNTH_H
+#define __SOUND_EMUX_SYNTH_H
+
+/*
+ *  Defines for the Emu-series WaveTable chip
+ *
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include "seq_kernel.h"
+#include "seq_device.h"
+#include "soundfont.h"
+#include "seq_midi_emul.h"
+#ifdef CONFIG_SND_OSSEMUL
+#include "seq_oss.h"
+#endif
+#include "emux_legacy.h"
+#include "seq_virmidi.h"
+
+/*
+ * compile flags
+ */
+#define SNDRV_EMUX_USE_RAW_EFFECT
+
+
+/*
+ * typedefs
+ */
+typedef struct snd_emux_effect_table snd_emux_effect_table_t;
+typedef struct snd_emux_port snd_emux_port_t;
+typedef struct snd_emux_voice snd_emux_voice_t;
+typedef struct snd_emux snd_emux_t;
+
+
+/*
+ * operators
+ */
+typedef struct snd_emux_operators {
+	struct module *owner;
+	snd_emux_voice_t *(*get_voice)(snd_emux_t *emu, snd_emux_port_t *port);
+	int (*prepare)(snd_emux_voice_t *vp);
+	void (*trigger)(snd_emux_voice_t *vp);
+	void (*release)(snd_emux_voice_t *vp);
+	void (*update)(snd_emux_voice_t *vp, int update);
+	void (*terminate)(snd_emux_voice_t *vp);
+	void (*free_voice)(snd_emux_voice_t *vp);
+	void (*reset)(snd_emux_t *emu, int ch);
+	/* the first parameters are snd_emux_t */
+	int (*sample_new)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count);
+	int (*sample_free)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr);
+	void (*sample_reset)(snd_emux_t *emu);
+	int (*load_fx)(snd_emux_t *emu, int type, int arg, const void *data, long count);
+	void (*sysex)(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+#ifdef CONFIG_SND_OSSEMUL
+	int (*oss_ioctl)(snd_emux_t *emu, int cmd, int p1, int p2);
+#endif
+} snd_emux_operators_t;
+
+
+/*
+ * constant values
+ */
+#define SNDRV_EMUX_MAX_PORTS		32	/* max # of sequencer ports */
+#define SNDRV_EMUX_MAX_VOICES		64	/* max # of voices */
+#define SNDRV_EMUX_MAX_MULTI_VOICES	16	/* max # of playable voices
+						 * simultineously
+						 */
+
+/*
+ * flags
+ */
+#define SNDRV_EMUX_ACCEPT_ROM		(1<<0)
+
+/*
+ * emuX wavetable
+ */
+struct snd_emux {
+
+	snd_card_t *card;	/* assigned card */
+
+	/* following should be initialized before registration */
+	int max_voices;		/* Number of voices */
+	int mem_size;		/* memory size (in byte) */
+	int num_ports;		/* number of ports to be created */
+	int pitch_shift;	/* pitch shift value (for Emu10k1) */
+	snd_emux_operators_t ops;	/* operators */
+	void *hw;		/* hardware */
+	unsigned long flags;	/* other conditions */
+	int midi_ports;		/* number of virtual midi devices */
+	int midi_devidx;	/* device offset of virtual midi */
+
+	/* private */
+	int num_voices;		/* current number of voices */
+	snd_sf_list_t *sflist;	/* root of SoundFont list */
+	snd_emux_voice_t *voices;	/* Voices (EMU 'channel') */
+	int use_time;	/* allocation counter */
+	spinlock_t voice_lock;	/* Lock for voice access */
+	struct semaphore register_mutex;
+	int client;		/* For the sequencer client */
+	int ports[SNDRV_EMUX_MAX_PORTS];	/* The ports for this device */
+	int used;	/* use counter */
+	char *name;	/* name of the device (internal) */
+	snd_rawmidi_t **vmidi;
+	struct timer_list tlist;	/* for pending note-offs */
+	int timer_active;
+
+	snd_util_memhdr_t *memhdr;	/* memory chunk information */
+
+#ifdef CONFIG_PROC_FS
+	snd_info_entry_t *proc;
+#endif
+
+#ifdef CONFIG_SND_OSSEMUL
+	snd_seq_device_t *oss_synth;
+#endif
+};
+
+
+/*
+ * sequencer port information
+ */
+struct snd_emux_port {
+
+	snd_midi_channel_set_t chset;
+	snd_emux_t *emu;
+
+	char port_mode;			/* operation mode */
+	int volume_atten;		/* emuX raw attenuation */
+	unsigned long drum_flags;	/* drum bitmaps */
+	int ctrls[EMUX_MD_END];		/* control parameters */
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_effect_table_t *effect;
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+	snd_seq_oss_arg_t *oss_arg;
+#endif
+};
+
+/* port_mode */
+#define SNDRV_EMUX_PORT_MODE_MIDI		0	/* normal MIDI port */
+#define SNDRV_EMUX_PORT_MODE_OSS_SYNTH	1	/* OSS synth port */
+#define SNDRV_EMUX_PORT_MODE_OSS_MIDI	2	/* OSS multi channel synth port */
+
+/*
+ * A structure to keep track of each hardware voice
+ */
+struct snd_emux_voice {
+	int  ch;		/* Hardware channel number */
+
+	int  state;		/* status */
+#define SNDRV_EMUX_ST_OFF		0x00	/* Not playing, and inactive */
+#define SNDRV_EMUX_ST_ON		0x01	/* Note on */
+#define SNDRV_EMUX_ST_RELEASED 	(0x02|SNDRV_EMUX_ST_ON)	/* Note released */
+#define SNDRV_EMUX_ST_SUSTAINED	(0x04|SNDRV_EMUX_ST_ON)	/* Note sustained */
+#define SNDRV_EMUX_ST_STANDBY	(0x08|SNDRV_EMUX_ST_ON)	/* Waiting to be triggered */
+#define SNDRV_EMUX_ST_PENDING 	(0x10|SNDRV_EMUX_ST_ON)	/* Note will be released */
+#define SNDRV_EMUX_ST_LOCKED		0x100	/* Not accessible */
+
+	unsigned int  time;	/* An allocation time */
+	unsigned char note;	/* Note currently assigned to this voice */
+	unsigned char key;
+	unsigned char velocity;	/* Velocity of current note */
+
+	snd_sf_zone_t *zone;	/* Zone assigned to this note */
+	void *block;		/* sample block pointer (optional) */
+	snd_midi_channel_t *chan;	/* Midi channel for this note */
+	snd_emux_port_t *port;	/* associated port */
+	snd_emux_t *emu;	/* assigned root info */
+	void *hw;		/* hardware pointer (emu8000_t or emu10k1_t) */
+	unsigned long ontime;	/* jiffies at note triggered */
+	
+	/* Emu8k/Emu10k1 registers */
+	soundfont_voice_info_t reg;
+
+	/* additional registers */
+	int avol;		/* volume attenuation */
+	int acutoff;		/* cutoff target */
+	int apitch;		/* pitch offset */
+	int apan;		/* pan/aux pair */
+	int aaux;
+	int ptarget;		/* pitch target */
+	int vtarget;		/* volume target */
+	int ftarget;		/* filter target */
+
+};
+
+/*
+ * update flags (can be combined)
+ */
+#define SNDRV_EMUX_UPDATE_VOLUME		(1<<0)
+#define SNDRV_EMUX_UPDATE_PITCH		(1<<1)
+#define SNDRV_EMUX_UPDATE_PAN		(1<<2)
+#define SNDRV_EMUX_UPDATE_FMMOD		(1<<3)
+#define SNDRV_EMUX_UPDATE_TREMFREQ	(1<<4)
+#define SNDRV_EMUX_UPDATE_FM2FRQ2		(1<<5)
+#define SNDRV_EMUX_UPDATE_Q		(1<<6)
+
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+/*
+ * effect table
+ */
+struct snd_emux_effect_table {
+	/* Emu8000 specific effects */
+	short val[EMUX_NUM_EFFECTS];
+	unsigned char flag[EMUX_NUM_EFFECTS];
+};
+#endif /* SNDRV_EMUX_USE_RAW_EFFECT */
+
+
+/*
+ * prototypes - interface to Emu10k1 and Emu8k routines
+ */
+int snd_emux_new(snd_emux_t **remu);
+int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name);
+int snd_emux_free(snd_emux_t *emu);
+
+/*
+ * exported functions
+ */
+void snd_emux_terminate_all(snd_emux_t *emu);
+void snd_emux_lock_voice(snd_emux_t *emu, int voice);
+void snd_emux_unlock_voice(snd_emux_t *emu, int voice);
+
+#endif /* __SOUND_EMUX_SYNTH_H */
diff -Nru linux/include/sound/es1688.h linux-2.4.19-pre5-mjc/include/sound/es1688.h
--- linux/include/sound/es1688.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/es1688.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,125 @@
+#ifndef __SOUND_ES1688_H
+#define __SOUND_ES1688_H
+
+/*
+ *  Header file for ES488/ES1688
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include "control.h"
+#include "pcm.h"
+
+#define ES1688_HW_AUTO		0x0000
+#define ES1688_HW_688		0x0001
+#define ES1688_HW_1688		0x0002
+
+struct _snd_es1688 {
+	unsigned long port;		/* port of ESS chip */
+	struct resource *res_port;
+	unsigned long mpu_port;		/* MPU-401 port of ESS chip */
+	int irq;			/* IRQ number of ESS chip */
+	int mpu_irq;			/* MPU IRQ */
+	int dma8;			/* 8-bit DMA */
+	unsigned short version;		/* version of ESS chip */
+	unsigned short hardware;	/* see to ES1688_HW_XXXX */
+
+	unsigned short trigger_value;
+	unsigned char pad;
+	unsigned int dma_size;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+};
+
+typedef struct _snd_es1688 es1688_t;
+
+/* I/O ports */
+
+#define ES1688P(codec, x) ((codec)->port + e_s_s_ESS1688##x)
+
+#define e_s_s_ESS1688RESET	0x6
+#define e_s_s_ESS1688READ	0xa
+#define e_s_s_ESS1688WRITE	0xc
+#define e_s_s_ESS1688COMMAND	0xc
+#define e_s_s_ESS1688STATUS	0xc
+#define e_s_s_ESS1688DATA_AVAIL	0xe
+#define e_s_s_ESS1688DATA_AVAIL_16 0xf
+#define e_s_s_ESS1688MIXER_ADDR	0x4
+#define e_s_s_ESS1688MIXER_DATA	0x5
+#define e_s_s_ESS1688OPL3_LEFT	0x0
+#define e_s_s_ESS1688OPL3_RIGHT	0x2
+#define e_s_s_ESS1688OPL3_BOTH	0x8
+#define e_s_s_ESS1688ENABLE0	0x0
+#define e_s_s_ESS1688ENABLE1	0x9
+#define e_s_s_ESS1688ENABLE2	0xb
+#define e_s_s_ESS1688INIT1	0x7
+
+#define ES1688_DSP_CMD_DMAOFF	0xd0
+#define ES1688_DSP_CMD_SPKON	0xd1
+#define ES1688_DSP_CMD_SPKOFF	0xd3
+#define ES1688_DSP_CMD_DMAON	0xd4
+
+#define ES1688_PCM_DEV		0x14
+#define ES1688_MIC_DEV		0x1a
+#define ES1688_REC_DEV		0x1c
+#define ES1688_MASTER_DEV	0x32
+#define ES1688_FM_DEV		0x36
+#define ES1688_CD_DEV		0x38
+#define ES1688_AUX_DEV		0x3a
+#define ES1688_SPEAKER_DEV	0x3c
+#define ES1688_LINE_DEV		0x3e
+#define ES1688_RECLEV_DEV	0xb4
+
+#define ES1688_MIXS_MASK	0x17
+#define ES1688_MIXS_MIC		0x00
+#define ES1688_MIXS_MIC_MASTER	0x01
+#define ES1688_MIXS_CD		0x02
+#define ES1688_MIXS_AOUT	0x03
+#define ES1688_MIXS_MIC1	0x04
+#define ES1688_MIXS_REC_MIX	0x05
+#define ES1688_MIXS_LINE	0x06
+#define ES1688_MIXS_MASTER	0x07
+#define ES1688_MIXS_MUTE	0x10
+
+/*
+
+ */
+
+void snd_es1688_mixer_write(es1688_t *chip, unsigned char reg, unsigned char data);
+unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg);
+
+void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+int snd_es1688_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long mpu_port,
+		      int irq,
+		      int mpu_irq,
+		      int dma8,
+		      unsigned short hardware,
+		      es1688_t ** rchip);
+int snd_es1688_pcm(es1688_t *chip, int device, snd_pcm_t ** rpcm);
+int snd_es1688_mixer(es1688_t *chip);
+
+#endif /* __SOUND_ES1688_H */
diff -Nru linux/include/sound/gus.h linux-2.4.19-pre5-mjc/include/sound/gus.h
--- linux/include/sound/gus.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/gus.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,730 @@
+#ifndef __SOUND_GUS_H
+#define __SOUND_GUS_H
+
+/*
+ *  Global structures used for GUS part of ALSA driver
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "timer.h"
+#include "seq_midi_emul.h"
+#include "seq_device.h"
+#include "ainstr_iw.h"
+#include "ainstr_gf1.h"
+#include "ainstr_simple.h"
+#include <asm/io.h>
+
+#define SNDRV_SEQ_DEV_ID_GUS			"gus-synth"
+
+/* IO ports */
+
+#define GUSP(gus, x)			((gus)->gf1.port + SNDRV_g_u_s_##x)
+
+#define SNDRV_g_u_s_MIDICTRL		(0x320-0x220)
+#define SNDRV_g_u_s_MIDISTAT		(0x320-0x220)
+#define SNDRV_g_u_s_MIDIDATA		(0x321-0x220)
+
+#define SNDRV_g_u_s_GF1PAGE		(0x322-0x220)
+#define SNDRV_g_u_s_GF1REGSEL		(0x323-0x220)
+#define SNDRV_g_u_s_GF1DATALOW		(0x324-0x220)
+#define SNDRV_g_u_s_GF1DATAHIGH		(0x325-0x220)
+#define SNDRV_g_u_s_IRQSTAT		(0x226-0x220)
+#define SNDRV_g_u_s_TIMERCNTRL		(0x228-0x220)
+#define SNDRV_g_u_s_TIMERDATA		(0x229-0x220)
+#define SNDRV_g_u_s_DRAM			(0x327-0x220)
+#define SNDRV_g_u_s_MIXCNTRLREG		(0x220-0x220)
+#define SNDRV_g_u_s_IRQDMACNTRLREG	(0x22b-0x220)
+#define SNDRV_g_u_s_REGCNTRLS		(0x22f-0x220)
+#define SNDRV_g_u_s_BOARDVERSION		(0x726-0x220)
+#define SNDRV_g_u_s_MIXCNTRLPORT		(0x726-0x220)
+#define SNDRV_g_u_s_IVER			(0x325-0x220)
+#define SNDRV_g_u_s_MIXDATAPORT		(0x326-0x220)
+#define SNDRV_g_u_s_MAXCNTRLPORT		(0x326-0x220)
+
+/* GF1 registers */
+
+/* global registers */
+#define SNDRV_GF1_GB_ACTIVE_VOICES		0x0e
+#define SNDRV_GF1_GB_VOICES_IRQ			0x0f
+#define SNDRV_GF1_GB_GLOBAL_MODE			0x19
+#define SNDRV_GF1_GW_LFO_BASE			0x1a
+#define SNDRV_GF1_GB_VOICES_IRQ_READ		0x1f
+#define SNDRV_GF1_GB_DRAM_DMA_CONTROL		0x41
+#define SNDRV_GF1_GW_DRAM_DMA_LOW			0x42
+#define SNDRV_GF1_GW_DRAM_IO_LOW			0x43
+#define SNDRV_GF1_GB_DRAM_IO_HIGH			0x44
+#define SNDRV_GF1_GB_SOUND_BLASTER_CONTROL	0x45
+#define SNDRV_GF1_GB_ADLIB_TIMER_1		0x46
+#define SNDRV_GF1_GB_ADLIB_TIMER_2		0x47
+#define SNDRV_GF1_GB_RECORD_RATE			0x48
+#define SNDRV_GF1_GB_REC_DMA_CONTROL		0x49
+#define SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL		0x4b
+#define SNDRV_GF1_GB_RESET			0x4c
+#define SNDRV_GF1_GB_DRAM_DMA_HIGH		0x50
+#define SNDRV_GF1_GW_DRAM_IO16			0x51
+#define SNDRV_GF1_GW_MEMORY_CONFIG		0x52
+#define SNDRV_GF1_GB_MEMORY_CONTROL		0x53
+#define SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR	0x54
+#define SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR		0x55
+#define SNDRV_GF1_GW_FIFO_SIZE			0x56
+#define SNDRV_GF1_GW_INTERLEAVE			0x57
+#define SNDRV_GF1_GB_COMPATIBILITY		0x59
+#define SNDRV_GF1_GB_DECODE_CONTROL		0x5a
+#define SNDRV_GF1_GB_VERSION_NUMBER		0x5b
+#define SNDRV_GF1_GB_MPU401_CONTROL_A		0x5c
+#define SNDRV_GF1_GB_MPU401_CONTROL_B		0x5d
+#define SNDRV_GF1_GB_EMULATION_IRQ		0x60
+/* voice specific registers */
+#define SNDRV_GF1_VB_ADDRESS_CONTROL		0x00
+#define SNDRV_GF1_VW_FREQUENCY			0x01
+#define SNDRV_GF1_VW_START_HIGH			0x02
+#define SNDRV_GF1_VW_START_LOW			0x03
+#define SNDRV_GF1_VA_START			SNDRV_GF1_VW_START_HIGH
+#define SNDRV_GF1_VW_END_HIGH			0x04
+#define SNDRV_GF1_VW_END_LOW			0x05
+#define SNDRV_GF1_VA_END				SNDRV_GF1_VW_END_HIGH
+#define SNDRV_GF1_VB_VOLUME_RATE			0x06
+#define SNDRV_GF1_VB_VOLUME_START			0x07
+#define SNDRV_GF1_VB_VOLUME_END			0x08
+#define SNDRV_GF1_VW_VOLUME			0x09
+#define SNDRV_GF1_VW_CURRENT_HIGH			0x0a
+#define SNDRV_GF1_VW_CURRENT_LOW			0x0b
+#define SNDRV_GF1_VA_CURRENT			SNDRV_GF1_VW_CURRENT_HIGH
+#define SNDRV_GF1_VB_PAN				0x0c
+#define SNDRV_GF1_VW_OFFSET_RIGHT			0x0c
+#define SNDRV_GF1_VB_VOLUME_CONTROL		0x0d
+#define SNDRV_GF1_VB_UPPER_ADDRESS		0x10
+#define SNDRV_GF1_VW_EFFECT_HIGH			0x11
+#define SNDRV_GF1_VW_EFFECT_LOW			0x12
+#define SNDRV_GF1_VA_EFFECT			SNDRV_GF1_VW_EFFECT_HIGH
+#define SNDRV_GF1_VW_OFFSET_LEFT			0x13
+#define SNDRV_GF1_VB_ACCUMULATOR			0x14
+#define SNDRV_GF1_VB_MODE				0x15
+#define SNDRV_GF1_VW_EFFECT_VOLUME		0x16
+#define SNDRV_GF1_VB_FREQUENCY_LFO		0x17
+#define SNDRV_GF1_VB_VOLUME_LFO			0x18
+#define SNDRV_GF1_VW_OFFSET_RIGHT_FINAL		0x1b
+#define SNDRV_GF1_VW_OFFSET_LEFT_FINAL		0x1c
+#define SNDRV_GF1_VW_EFFECT_VOLUME_FINAL		0x1d
+
+/* ICS registers */
+
+#define SNDRV_ICS_MIC_DEV		0
+#define SNDRV_ICS_LINE_DEV	1
+#define SNDRV_ICS_CD_DEV		2
+#define SNDRV_ICS_GF1_DEV		3
+#define SNDRV_ICS_NONE_DEV	4
+#define SNDRV_ICS_MASTER_DEV	5
+
+/* LFO */
+
+#define SNDRV_LFO_TREMOLO		0
+#define SNDRV_LFO_VIBRATO		1
+
+/* misc */
+
+#define SNDRV_GF1_DMA_UNSIGNED	0x80
+#define SNDRV_GF1_DMA_16BIT	0x40
+#define SNDRV_GF1_DMA_IRQ		0x20
+#define SNDRV_GF1_DMA_WIDTH16	0x04
+#define SNDRV_GF1_DMA_READ	0x02	/* read from GUS's DRAM */
+#define SNDRV_GF1_DMA_ENABLE	0x01
+
+/* ramp ranges */
+
+#define SNDRV_GF1_ATTEN(x)	(snd_gf1_atten_table[x])
+#define SNDRV_GF1_MIN_VOLUME	1800
+#define SNDRV_GF1_MAX_VOLUME	4095
+#define SNDRV_GF1_MIN_OFFSET	(SNDRV_GF1_MIN_VOLUME>>4)
+#define SNDRV_GF1_MAX_OFFSET	255
+#define SNDRV_GF1_MAX_TDEPTH	90
+
+/* defines for memory manager */
+
+#define SNDRV_GF1_MEM_BLOCK_16BIT		0x0001
+
+#define SNDRV_GF1_MEM_OWNER_DRIVER	0x0001
+#define SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE	0x0002
+#define SNDRV_GF1_MEM_OWNER_WAVE_GF1	0x0003
+#define SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF	0x0004
+
+/* constants for interrupt handlers */
+
+#define SNDRV_GF1_HANDLER_MIDI_OUT	0x00010000
+#define SNDRV_GF1_HANDLER_MIDI_IN		0x00020000
+#define SNDRV_GF1_HANDLER_TIMER1		0x00040000
+#define SNDRV_GF1_HANDLER_TIMER2		0x00080000
+#define SNDRV_GF1_HANDLER_VOICE		0x00100000
+#define SNDRV_GF1_HANDLER_DMA_WRITE	0x00200000
+#define SNDRV_GF1_HANDLER_DMA_READ	0x00400000
+#define SNDRV_GF1_HANDLER_ALL		(0xffff0000&~SNDRV_GF1_HANDLER_VOICE)
+
+/* constants for DMA flags */
+
+#define SNDRV_GF1_DMA_TRIGGER		1
+
+/* --- */
+
+struct _snd_gus_card;
+typedef struct _snd_gus_card snd_gus_card_t;
+
+/* GF1 specific structure */
+
+typedef struct _snd_gf1_bank_info {
+	unsigned int address;
+	unsigned int size;
+} snd_gf1_bank_info_t;
+
+typedef struct _snd_gf1_mem_block {
+	unsigned short flags;	/* flags - SNDRV_GF1_MEM_BLOCK_XXXX */
+	unsigned short owner;	/* owner - SNDRV_GF1_MEM_OWNER_XXXX */
+	unsigned int share;	/* share count */
+	unsigned int share_id[4]; /* share ID */
+	unsigned int ptr;
+	unsigned int size;
+	char *name;
+	struct _snd_gf1_mem_block *next;
+	struct _snd_gf1_mem_block *prev;
+} snd_gf1_mem_block_t;
+
+typedef struct _snd_gf1_mem {
+	snd_gf1_bank_info_t banks_8[4];
+	snd_gf1_bank_info_t banks_16[4];
+	snd_gf1_mem_block_t *first;
+	snd_gf1_mem_block_t *last;
+	snd_info_entry_t *info_entry;
+	struct semaphore memory_mutex;
+} snd_gf1_mem_t;
+
+typedef struct snd_gf1_dma_block {
+	void *buffer;		/* buffer in computer's RAM */
+	unsigned long buf_addr;	/* buffer address */
+	unsigned int addr;	/* address in onboard memory */
+	unsigned int count;	/* count in bytes */
+	unsigned int cmd;	/* DMA command (format) */
+	void (*ack)(snd_gus_card_t * gus, void *private_data);
+	void *private_data;
+	struct snd_gf1_dma_block *next;
+} snd_gf1_dma_block_t;
+
+typedef struct {
+	snd_midi_channel_set_t * chset;
+	snd_gus_card_t * gus;
+	int mode;		/* operation mode */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	int midi_has_voices: 1;
+} snd_gus_port_t;
+
+typedef struct _snd_gus_voice snd_gus_voice_t;
+
+typedef struct {
+	void (*sample_start)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position);
+	void (*sample_stop)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode);
+	void (*sample_freq)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq);
+	void (*sample_volume)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume);
+	void (*sample_loop)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop);
+	void (*sample_pos)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position);
+	void (*sample_private1)(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data);
+} snd_gus_sample_ops_t;
+
+#define SNDRV_GF1_VOICE_TYPE_PCM		0
+#define SNDRV_GF1_VOICE_TYPE_SYNTH 	1
+#define SNDRV_GF1_VOICE_TYPE_MIDI		2
+
+#define SNDRV_GF1_VFLG_RUNNING		(1<<0)
+#define SNDRV_GF1_VFLG_EFFECT_TIMER1	(1<<1)
+#define SNDRV_GF1_VFLG_PAN		(1<<2)
+
+typedef enum {
+	VENV_BEFORE,
+	VENV_ATTACK,
+	VENV_SUSTAIN,
+	VENV_RELEASE,
+	VENV_DONE,
+	VENV_VOLUME
+} snd_gus_volume_state_t;
+
+struct _snd_gus_voice {
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth:1,
+	    midi: 1;
+	unsigned int flags;
+	unsigned char client;
+	unsigned char port;
+	unsigned char index;
+	unsigned char pad;
+	
+#ifdef CONFIG_SND_DEBUG
+	unsigned int interrupt_stat_wave;
+	unsigned int interrupt_stat_volume;
+#endif
+	void (*handler_wave) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*handler_volume) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*handler_effect) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*volume_change) (snd_gus_card_t * gus);
+
+	snd_gus_sample_ops_t *sample_ops;
+
+	snd_seq_instr_t instr;
+
+	/* running status / registers */
+
+	snd_seq_ev_volume_t sample_volume;
+
+	unsigned short fc_register;
+	unsigned short fc_lfo;
+	unsigned short gf1_volume;
+	unsigned char control;
+	unsigned char mode;
+	unsigned char gf1_pan;
+	unsigned char effect_accumulator;
+	unsigned char volume_control;
+	unsigned char venv_value_next;
+	snd_gus_volume_state_t venv_state;
+	snd_gus_volume_state_t venv_state_prev;
+	unsigned short vlo;
+	unsigned short vro;
+	unsigned short gf1_effect_volume;
+	
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(snd_gus_voice_t *voice);
+};
+
+struct _snd_gf1 {
+
+	unsigned int enh_mode:1,	/* enhanced mode (GFA1) */
+		     hw_lfo:1,		/* use hardware LFO */
+		     sw_lfo:1,		/* use software LFO */
+		     effect:1;		/* use effect voices */
+
+	unsigned long port;		/* port of GF1 chip */
+	struct resource *res_port1;
+	struct resource *res_port2;
+	int irq;			/* IRQ number */
+	int dma1;			/* DMA1 number */
+	int dma2;			/* DMA2 number */
+	unsigned int memory;		/* GUS's DRAM size in bytes */
+	unsigned int rom_memory;	/* GUS's ROM size in bytes */
+	unsigned int rom_present;	/* bitmask */
+	unsigned int rom_banks;		/* GUS's ROM banks */
+
+	snd_gf1_mem_t mem_alloc;
+	snd_info_entry_t *ram_entries[4];
+	snd_info_entry_t *rom_entries[4];
+
+	/* registers */
+	unsigned short reg_page;
+	unsigned short reg_regsel;
+	unsigned short reg_data8;
+	unsigned short reg_data16;
+	unsigned short reg_irqstat;
+	unsigned short reg_dram;
+	unsigned short reg_timerctrl;
+	unsigned short reg_timerdata;
+	unsigned char ics_regs[6][2];
+	/* --------- */
+
+	unsigned char active_voices;	/* active voices */
+	unsigned char active_voice;	/* selected voice (GF1PAGE register) */
+
+	snd_gus_voice_t voices[32];	/* GF1 voices */
+
+	unsigned int default_voice_address;
+
+	unsigned short playback_freq;	/* GF1 playback (mixing) frequency */
+	unsigned short mode;		/* see to SNDRV_GF1_MODE_XXXX */
+	unsigned char volume_ramp;
+	unsigned char smooth_pan;
+	unsigned char full_range_pan;
+	unsigned char pad0;
+
+	unsigned char *lfos;
+
+	/* interrupt handlers */
+
+	void (*interrupt_handler_midi_out) (snd_gus_card_t * gus);
+	void (*interrupt_handler_midi_in) (snd_gus_card_t * gus);
+	void (*interrupt_handler_timer1) (snd_gus_card_t * gus);
+	void (*interrupt_handler_timer2) (snd_gus_card_t * gus);
+	void (*interrupt_handler_dma_write) (snd_gus_card_t * gus);
+	void (*interrupt_handler_dma_read) (snd_gus_card_t * gus);
+
+#ifdef CONFIG_SND_DEBUG
+	unsigned int interrupt_stat_midi_out;
+	unsigned int interrupt_stat_midi_in;
+	unsigned int interrupt_stat_timer1;
+	unsigned int interrupt_stat_timer2;
+	unsigned int interrupt_stat_dma_write;
+	unsigned int interrupt_stat_dma_read;
+	unsigned int interrupt_stat_voice_lost;
+#endif
+
+	/* synthesizer */
+
+	int seq_client;
+	snd_gus_port_t seq_ports[4];
+	snd_seq_kinstr_list_t *ilist;
+	snd_iwffff_ops_t iwffff_ops;
+	snd_gf1_ops_t gf1_ops;
+	snd_simple_ops_t simple_ops;
+
+	/* timer */
+
+	unsigned short timer_enabled;
+	snd_timer_t *timer1;
+	snd_timer_t *timer2;
+
+	/* midi */
+
+	unsigned short uart_cmd;
+	unsigned int uart_framing;
+	unsigned int uart_overrun;
+
+	/* dma operations */
+
+	unsigned int dma_flags;
+	unsigned int dma_shared;
+	snd_gf1_dma_block_t *dma_data_pcm;
+	snd_gf1_dma_block_t *dma_data_pcm_last;
+	snd_gf1_dma_block_t *dma_data_synth;
+	snd_gf1_dma_block_t *dma_data_synth_last;
+	void (*dma_ack)(snd_gus_card_t * gus, void *private_data);
+	void *dma_private_data;
+
+	/* pcm */
+	int pcm_channels;
+	int pcm_alloc_voices;
+        unsigned short pcm_volume_level_left;
+	unsigned short pcm_volume_level_right;
+	unsigned short pcm_volume_level_left1;
+	unsigned short pcm_volume_level_right1;
+                                
+	unsigned char pcm_rcntrl_reg;
+	unsigned char pad_end;
+};
+
+/* main structure for GUS card */
+
+struct _snd_gus_card {
+	snd_card_t *card;
+
+	unsigned int
+	 initialized: 1,		/* resources were initialized */
+	 equal_irq:1,			/* GF1 and CODEC shares IRQ (GUS MAX only) */
+	 equal_dma:1,			/* if dma channels are equal (not valid for daughter board) */
+	 ics_flag:1,			/* have we ICS mixer chip */
+	 ics_flipped:1,			/* ICS mixer have flipped some channels? */
+	 codec_flag:1,			/* have we CODEC chip? */
+	 max_flag:1,			/* have we GUS MAX card? */
+	 max_ctrl_flag:1,		/* have we original GUS MAX card? */
+	 daughter_flag:1,		/* have we daughter board? */
+	 interwave:1,			/* hey - we have InterWave card */
+	 ess_flag:1,			/* ESS chip found... GUS Extreme */
+	 ace_flag:1,			/* GUS ACE detected */
+	 uart_enable:1;			/* enable MIDI UART */
+	unsigned short revision;	/* revision of chip */
+	unsigned short max_cntrl_val;	/* GUS MAX control value */
+	unsigned short mix_cntrl_reg;	/* mixer control register */
+	unsigned short joystick_dac;	/* joystick DAC level */
+	int timer_dev;			/* timer device */
+
+	struct _snd_gf1 gf1;	/* gf1 specific variables */
+#ifdef CONFIG_SND_DEBUG
+	snd_info_entry_t *irq_entry;
+#endif
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *pcm_cap_substream;
+	unsigned int c_dma_size;
+	unsigned int c_period_size;
+	unsigned int c_pos;
+
+	snd_rawmidi_t *midi_uart;
+	snd_rawmidi_substream_t *midi_substream_output;
+	snd_rawmidi_substream_t *midi_substream_input;
+
+	snd_seq_device_t *seq_dev;
+
+	spinlock_t reg_lock;
+	spinlock_t voice_alloc;
+	spinlock_t active_voice_lock;
+	spinlock_t event_lock;
+	spinlock_t dma_lock;
+	spinlock_t pcm_volume_level_lock;
+	spinlock_t uart_cmd_lock;
+	struct semaphore dma_mutex;
+	struct semaphore register_mutex;
+};
+
+/* I/O functions for GF1/InterWave chip - gus_io.c */
+
+static inline void snd_gf1_select_voice(snd_gus_card_t * gus, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->active_voice_lock, flags);
+	if (voice != gus->gf1.active_voice) {
+		gus->gf1.active_voice = voice;
+		outb(voice, GUSP(gus, GF1PAGE));
+	}
+	spin_unlock_irqrestore(&gus->active_voice_lock, flags);
+}
+
+static inline void snd_gf1_uart_cmd(snd_gus_card_t * gus, unsigned char b)
+{
+	outb(gus->gf1.uart_cmd = b, GUSP(gus, MIDICTRL));
+}
+
+static inline unsigned char snd_gf1_uart_stat(snd_gus_card_t * gus)
+{
+	return inb(GUSP(gus, MIDISTAT));
+}
+
+static inline void snd_gf1_uart_put(snd_gus_card_t * gus, unsigned char b)
+{
+	outb(b, GUSP(gus, MIDIDATA));
+}
+
+static inline unsigned char snd_gf1_uart_get(snd_gus_card_t * gus)
+{
+	return inb(GUSP(gus, MIDIDATA));
+}
+
+extern void snd_gf1_delay(snd_gus_card_t * gus);
+
+extern void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
+
+extern void snd_gf1_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned char snd_gf1_read8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_look8(gus, reg | 0x80);
+}
+extern void snd_gf1_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data);
+extern unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned short snd_gf1_read16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_look16(gus, reg | 0x80);
+}
+extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data);
+extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data);
+extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count);
+extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
+extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
+extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
+extern void snd_gf1_i_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg);
+extern void snd_gf1_i_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data);
+extern inline unsigned char snd_gf1_i_read8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_i_look8(gus, reg | 0x80);
+}
+extern unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_i_look16(gus, reg | 0x80);
+}
+extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
+extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
+
+extern void snd_gf1_select_active_voices(snd_gus_card_t * gus);
+
+/* gus_lfo.c */
+
+struct _SND_IW_LFO_PROGRAM {
+	unsigned short freq_and_control;
+	unsigned char depth_final;
+	unsigned char depth_inc;
+	unsigned short twave;
+	unsigned short depth;
+};
+
+#if 0
+extern void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice);
+#endif
+extern void snd_gf1_lfo_init(snd_gus_card_t * gus);
+extern void snd_gf1_lfo_done(snd_gus_card_t * gus);
+extern void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, struct _SND_IW_LFO_PROGRAM *program);
+extern void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type);
+extern void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type);
+extern void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, int lfo_type, int freq);
+extern void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, int lfo_type, int depth);
+extern void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, int freq, int current_depth, int depth, int sweep, int shape);
+extern void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type);
+#if 0
+extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *command);
+#endif
+
+/* gus_mem.c */
+
+void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup);
+int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block);
+snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+				      unsigned int address);
+snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+				       unsigned int *share_id);
+snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
+				       char *name, int size, int w_16,
+				       int align, unsigned int *share_id);
+int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address);
+int snd_gf1_mem_free_owner(snd_gf1_mem_t * alloc, int owner);
+int snd_gf1_mem_init(snd_gus_card_t * gus);
+int snd_gf1_mem_done(snd_gus_card_t * gus);
+
+/* gus_mem_proc.c */
+
+int snd_gf1_mem_proc_init(snd_gus_card_t * gus);
+int snd_gf1_mem_proc_done(snd_gus_card_t * gus);
+
+/* gus_dma.c */
+
+void snd_gf1_dma_program(snd_gus_card_t * gus, unsigned int addr,
+			 unsigned long buf_addr, unsigned int count,
+			 unsigned int cmd);
+void snd_gf1_dma_ack(snd_gus_card_t * gus);
+int snd_gf1_dma_init(snd_gus_card_t * gus);
+int snd_gf1_dma_done(snd_gus_card_t * gus);
+int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
+			       snd_gf1_dma_block_t * block,
+			       int atomic,
+			       int synth);
+
+/* gus_volume.c */
+
+unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol);
+unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol);
+unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
+				    unsigned short start,
+				    unsigned short end,
+				    unsigned int us);
+unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2);
+unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens);
+unsigned short snd_gf1_compute_freq(unsigned int freq,
+				    unsigned int rate,
+				    unsigned short mix_rate);
+
+/* gus_reset.c */
+
+void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what);
+void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice);
+void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice);
+void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
+void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
+snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port);
+void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice);
+int snd_gf1_start(snd_gus_card_t * gus);
+int snd_gf1_stop(snd_gus_card_t * gus);
+
+/* gus_mixer.c */
+
+int snd_gf1_new_mixer(snd_gus_card_t * gus);
+
+/* gus_pcm.c */
+
+int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm);
+
+#ifdef CONFIG_SND_DEBUG
+extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus);
+extern void snd_gf1_print_global_registers(snd_gus_card_t * gus);
+extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus);
+extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit);
+#endif
+
+/* gus.c */
+
+int snd_gus_use_inc(snd_gus_card_t * gus);
+void snd_gus_use_dec(snd_gus_card_t * gus);
+int snd_gus_create(snd_card_t * card,
+		   unsigned long port,
+		   int irq, int dma1, int dma2,
+		   int timer_dev,
+		   int voices,
+		   int pcm_channels,
+		   int effect,
+		   snd_gus_card_t ** rgus);
+int snd_gus_initialize(snd_gus_card_t * gus);
+
+/* gus_irq.c */
+
+void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_SND_DEBUG
+void snd_gus_irq_profile_init(snd_gus_card_t *gus);
+void snd_gus_irq_profile_done(snd_gus_card_t *gus);
+#endif
+
+/* gus_uart.c */
+
+int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t **rrawmidi);
+
+#if 0
+extern void snd_engine_instrument_register(unsigned short mode,
+		struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds,
+		struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds,
+	      	struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds);
+extern int snd_engine_instrument_register_ask(unsigned short mode);
+#endif
+
+/* gus_dram.c */
+int snd_gus_dram_write(snd_gus_card_t *gus, char *ptr,
+		       unsigned int addr, unsigned int size);
+int snd_gus_dram_read(snd_gus_card_t *gus, char *ptr,
+		      unsigned int addr, unsigned int size, int rom);
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+
+/* gus_sample.c */
+void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p);
+
+/* gus_simple.c */
+void snd_gf1_simple_init(snd_gus_voice_t *voice);
+
+/* gus_instr.c */
+int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic);
+int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic);
+int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave,
+				 int atomic);
+int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic);
+int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic);
+int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave,
+			      int atomic);
+int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic);
+int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic);
+int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr,
+				 int atomic);
+
+#endif /* CONFIG_SND_SEQUENCER */
+
+#endif /* __SOUND_GUS_H */
diff -Nru linux/include/sound/hwdep.h linux-2.4.19-pre5-mjc/include/sound/hwdep.h
--- linux/include/sound/hwdep.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/hwdep.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,63 @@
+#ifndef __SOUND_HWDEP_H
+#define __SOUND_HWDEP_H
+
+/*
+ *  Hardware dependent layer 
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/asound.h>
+#include <linux/poll.h>
+
+typedef enum sndrv_hwdep_iface snd_hwdep_iface_t;
+typedef struct sndrv_hwdep_info snd_hwdep_info_t;
+
+typedef struct _snd_hwdep_ops {
+	long long (*llseek) (snd_hwdep_t *hw, struct file * file, long long offset, int orig);
+	long (*read) (snd_hwdep_t * hw, char *buf, long count, loff_t *offset);
+	long (*write) (snd_hwdep_t * hw, const char *buf, long count, loff_t *offset);
+	int (*open) (snd_hwdep_t * hw, struct file * file);
+	int (*release) (snd_hwdep_t * hw, struct file * file);
+	unsigned int (*poll) (snd_hwdep_t * hw, struct file * file, poll_table * wait);
+	int (*ioctl) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg);
+	int (*mmap) (snd_hwdep_t * hw, struct file * file, struct vm_area_struct * vma);
+} snd_hwdep_ops_t;
+
+struct _snd_hwdep {
+	snd_card_t *card;
+	int device;
+	char id[32];
+	char name[80];
+	int iface;
+
+#ifdef CONFIG_SND_OSSEMUL
+	char oss_dev[32];
+	int oss_type;
+	int ossreg;
+#endif
+
+	snd_hwdep_ops_t ops;
+	wait_queue_head_t open_wait;
+	void *private_data;
+	void (*private_free) (snd_hwdep_t *hwdep);
+};
+
+extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep);
+
+#endif /* __SOUND_HWDEP_H */
diff -Nru linux/include/sound/i2c.h linux-2.4.19-pre5-mjc/include/sound/i2c.h
--- linux/include/sound/i2c.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/i2c.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,92 @@
+#ifndef __SOUND_I2C_H
+#define __SOUND_I2C_H
+
+/*
+ *
+ *
+ *   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
+ *
+ *
+ */
+
+typedef struct _snd_i2c_device snd_i2c_device_t;
+typedef struct _snd_i2c_bus snd_i2c_bus_t;
+
+#define SND_I2C_DEVICE_ADDRTEN	(1<<0)	/* 10-bit I2C address */
+
+struct _snd_i2c_device {
+	struct list_head list;
+	snd_i2c_bus_t *bus;	/* I2C bus */
+	char name[32];		/* some useful device name */
+	unsigned short flags;	/* device flags */
+	unsigned short addr;	/* device address (might be 10-bit) */
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_i2c_device_t *device);
+};
+
+#define snd_i2c_device(n) list_entry(n, snd_i2c_device_t, list)
+
+typedef struct _snd_i2c_bit_ops {
+	void (*start)(snd_i2c_bus_t *bus);	/* transfer start */
+	void (*stop)(snd_i2c_bus_t *bus);	/* transfer stop */
+	void (*direction)(snd_i2c_bus_t *bus, int clock, int data);  /* set line direction (0 = write, 1 = read) */
+	void (*setlines)(snd_i2c_bus_t *bus, int clock, int data);
+	int (*getclock)(snd_i2c_bus_t *bus);
+	int (*getdata)(snd_i2c_bus_t *bus, int ack);
+} snd_i2c_bit_ops_t;
+
+typedef struct _snd_i2c_ops {
+	int (*sendbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count);
+	int (*readbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count);
+	int (*probeaddr)(snd_i2c_bus_t *bus, unsigned short addr);
+} snd_i2c_ops_t;
+
+struct _snd_i2c_bus {
+	snd_card_t *card;	/* card which I2C belongs to */
+	char name[32];		/* some useful label */
+
+	spinlock_t lock;
+
+	snd_i2c_bus_t *master;	/* master bus when SCK/SCL is shared */
+	struct list_head buses;	/* master: slave buses sharing SCK/SCL, slave: link list */
+
+	struct list_head devices; /* attached devices to this bus */
+
+	union {
+		snd_i2c_bit_ops_t *bit;
+		void *ops;
+	} hw_ops;		/* lowlevel operations */
+	snd_i2c_ops_t *ops;	/* midlevel operations */
+
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_i2c_bus_t *bus);
+};
+
+#define snd_i2c_slave_bus(n) list_entry(n, snd_i2c_bus_t, buses)
+
+int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c);
+int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice);
+int snd_i2c_device_free(snd_i2c_device_t *device);
+
+static inline void snd_i2c_lock(snd_i2c_bus_t *bus) { spin_lock(&(bus->master ? bus->master->lock : bus->lock)); }
+static inline void snd_i2c_unlock(snd_i2c_bus_t *bus) { spin_unlock(&(bus->master ? bus->master->lock : bus->lock)); }
+
+int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count);
+int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count);
+int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr);
+
+#endif /* __SOUND_I2C_H */
diff -Nru linux/include/sound/info.h linux-2.4.19-pre5-mjc/include/sound/info.h
--- linux/include/sound/info.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/info.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,192 @@
+#ifndef __SOUND_INFO_H
+#define __SOUND_INFO_H
+
+/*
+ *  Header file for info interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <linux/poll.h>
+
+/* buffer for information */
+struct snd_info_buffer {
+	char *buffer;		/* pointer to begin of buffer */
+	char *curr;		/* current position in buffer */
+	unsigned long size;	/* current size */
+	unsigned long len;	/* total length of buffer */
+	int stop;		/* stop flag */
+	int error;		/* error code */
+};
+
+typedef struct snd_info_buffer snd_info_buffer_t;
+
+#define SNDRV_INFO_CONTENT_TEXT		0
+#define SNDRV_INFO_CONTENT_DATA		1
+#define SNDRV_INFO_CONTENT_DEVICE		2
+
+struct snd_info_entry;
+
+struct snd_info_entry_text {
+	unsigned long read_size;
+	unsigned long write_size;
+	void (*read) (snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+	void (*write) (snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+};
+
+struct snd_info_entry_ops {
+	int (*open) (snd_info_entry_t *entry,
+		     unsigned short mode, void **file_private_data);
+	int (*release) (snd_info_entry_t * entry,
+			unsigned short mode, void *file_private_data);
+	long (*read) (snd_info_entry_t *entry, void *file_private_data,
+		      struct file * file, char *buf, long count);
+	long (*write) (snd_info_entry_t *entry, void *file_private_data,
+		       struct file * file, const char *buf, long count);
+	long long (*llseek) (snd_info_entry_t *entry, void *file_private_data,
+			    struct file * file, long long offset, int orig);
+	unsigned int (*poll) (snd_info_entry_t *entry, void *file_private_data,
+			      struct file * file, poll_table * wait);
+	int (*ioctl) (snd_info_entry_t *entry, void *file_private_data,
+		      struct file * file, unsigned int cmd, unsigned long arg);
+	int (*mmap) (snd_info_entry_t *entry, void *file_private_data,
+		     struct inode * inode, struct file * file,
+		     struct vm_area_struct * vma);
+};
+
+struct snd_info_entry_device {
+	unsigned short major;
+	unsigned short minor;
+};
+
+struct snd_info_entry {
+	const char *name;
+	mode_t mode;
+	long size;
+	unsigned short content;
+	union {
+		struct snd_info_entry_text text;
+		struct snd_info_entry_ops *ops;
+		struct snd_info_entry_device device;
+	} c;
+	snd_info_entry_t *parent;
+	snd_card_t *card;
+	struct module *module;
+	void *private_data;
+	void (*private_free)(snd_info_entry_t *entry);
+	struct proc_dir_entry *p;
+	struct semaphore access;
+};
+
+extern int snd_info_check_reserved_words(const char *str);
+
+#ifdef CONFIG_SND_OSSEMUL
+extern int snd_info_minor_register(void);
+extern int snd_info_minor_unregister(void);
+#endif
+
+
+#ifdef CONFIG_PROC_FS
+
+extern snd_info_entry_t *snd_seq_root;
+#ifdef CONFIG_SND_OSSEMUL
+extern snd_info_entry_t *snd_oss_root;
+#else
+#define snd_oss_root NULL
+#endif
+
+int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3)));
+int snd_info_init(void);
+int snd_info_done(void);
+
+int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len);
+char *snd_info_get_str(char *dest, char *src, int len);
+snd_info_entry_t *snd_info_create_module_entry(struct module * module,
+					       const char *name,
+					       snd_info_entry_t * parent);
+snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card,
+					     const char *name,
+					     snd_info_entry_t * parent);
+void snd_info_free_entry(snd_info_entry_t * entry);
+snd_info_entry_t *snd_info_create_device(const char *name,
+					 unsigned int number,
+					 unsigned int mode);
+void snd_info_free_device(snd_info_entry_t * entry);
+int snd_info_store_text(snd_info_entry_t * entry);
+int snd_info_restore_text(snd_info_entry_t * entry);
+
+int snd_info_card_register(snd_card_t * card);
+int snd_info_card_unregister(snd_card_t * card);
+int snd_info_register(snd_info_entry_t * entry);
+int snd_info_unregister(snd_info_entry_t * entry);
+
+struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
+					     struct proc_dir_entry *parent);
+void snd_remove_proc_entry(struct proc_dir_entry *parent,
+			   struct proc_dir_entry *de);
+
+#else
+
+#define snd_seq_root NULL
+#define snd_oss_root NULL
+
+static inline int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { return 0; }
+static inline int snd_info_init(void) { return 0; }
+static inline int snd_info_done(void) { return 0; }
+
+static inline int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) { return 0; }
+static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; }
+static inline snd_info_entry_t *snd_info_create_module_entry(struct module * module, const char *name) { return NULL; }
+static inline snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, const char *name) { return NULL; }
+static inline void snd_info_free_entry(snd_info_entry_t * entry) { ; }
+static inline snd_info_entry_t *snd_info_create_device(const char *name,
+						       unsigned int number,
+						       unsigned int mode) { return NULL; }
+static inline void snd_info_free_device(snd_info_entry_t * entry) { ; }
+
+static inline int snd_info_card_register(snd_card_t * card) { return 0; }
+static inline int snd_info_card_unregister(snd_card_t * card) { return 0; }
+static inline int snd_info_register(snd_info_entry_t * entry) { return 0; }
+static inline int snd_info_unregister(snd_info_entry_t * entry) { return 0; }
+
+static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return 0; }
+static inline void snd_remove_proc_entry(struct proc_dir_entry *parent,
+					 struct proc_dir_entry *de) { ; }
+
+#endif
+
+/*
+ * OSS info part
+ */
+
+#ifdef CONFIG_SND_OSSEMUL
+
+#define SNDRV_OSS_INFO_DEV_AUDIO	0
+#define SNDRV_OSS_INFO_DEV_SYNTH	1
+#define SNDRV_OSS_INFO_DEV_MIDI		2
+#define SNDRV_OSS_INFO_DEV_TIMERS	4
+#define SNDRV_OSS_INFO_DEV_MIXERS	5
+
+#define SNDRV_OSS_INFO_DEV_COUNT	6
+
+extern int snd_oss_info_register(int dev, int num, char *string);
+#define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL)
+
+#endif /* CONFIG_SND_OSSEMUL */
+
+#endif /* __SOUND_INFO_H */
diff -Nru linux/include/sound/initval.h linux-2.4.19-pre5-mjc/include/sound/initval.h
--- linux/include/sound/initval.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/initval.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,171 @@
+#ifndef __SOUND_INITVAL_H
+#define __SOUND_INITVAL_H
+
+/*
+ *  Init values for soundcard modules
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+
+#ifndef MODULE_GENERIC_STRING
+#ifdef MODULE
+#define MODULE_GENERIC_STRING(name, string) \
+static const char __module_generic_string_##name [] \
+  __attribute__ ((section(".modstring"))) = #name "=" string;
+#else
+#define MODULE_GENERIC_STRING(name, string)
+#endif
+#endif
+
+#define MODULE_CLASSES(val) MODULE_GENERIC_STRING(info_classes, val)
+#define MODULE_DEVICES(val) MODULE_GENERIC_STRING(info_devices, val)
+#define MODULE_PARM_SYNTAX(id, val) MODULE_GENERIC_STRING(info_parm_##id, val)
+
+#define SNDRV_AUTO_PORT		0xffff
+#define SNDRV_AUTO_IRQ		0xffff
+#define SNDRV_AUTO_DMA		0xffff
+#define SNDRV_AUTO_DMA_SIZE	(0x7fffffff)
+
+#define SNDRV_DEFAULT_IDX1	(-1)
+#define SNDRV_DEFAULT_STR1	NULL
+#define SNDRV_DEFAULT_ENABLE1	1
+#define SNDRV_DEFAULT_PORT1	SNDRV_AUTO_PORT
+#define SNDRV_DEFAULT_IRQ1	SNDRV_AUTO_IRQ
+#define SNDRV_DEFAULT_DMA1	SNDRV_AUTO_DMA
+#define SNDRV_DEFAULT_DMA_SIZE1	SNDRV_AUTO_DMA_SIZE
+#define SNDRV_DEFAULT_PTR1	SNDRV_DEFAULT_STR1
+
+#define SNDRV_DEFAULT_IDX	{ [0 ... (SNDRV_CARDS-1)] = -1 }
+#define SNDRV_DEFAULT_STR	{ [0 ... (SNDRV_CARDS-1)] = NULL }
+#define SNDRV_DEFAULT_ENABLE	{ 1, [1 ... (SNDRV_CARDS-1)] = 0 }
+#define SNDRV_DEFAULT_ENABLE_PNP { [0 ... (SNDRV_CARDS-1)] = 1 }
+#ifdef __ISAPNP__
+#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE_PNP
+#else
+#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE
+#endif
+#define SNDRV_DEFAULT_PORT	{ SNDRV_AUTO_PORT, [1 ... (SNDRV_CARDS-1)] = -1 }
+#define SNDRV_DEFAULT_IRQ	{ [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_IRQ }
+#define SNDRV_DEFAULT_DMA	{ [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA }
+#define SNDRV_DEFAULT_DMA_SIZE	{ [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE }
+#define SNDRV_DEFAULT_PTR	SNDRV_DEFAULT_STR
+
+#define SNDRV_BOOLEAN_TRUE_DESC	"allows:{{0,Disabled},{1,Enabled}},default:1,dialog:check"
+#define SNDRV_BOOLEAN_FALSE_DESC "allows:{{0,Disabled},{1,Enabled}},default:0,dialog:check"
+
+#define SNDRV_ENABLED		"enable:(snd_enable)"
+
+#define SNDRV_INDEX_DESC	SNDRV_ENABLED ",allows:{{0,7}},unique,skill:required,dialog:list"
+#define SNDRV_ID_DESC		SNDRV_ENABLED ",unique"
+#define SNDRV_ENABLE_DESC	SNDRV_BOOLEAN_FALSE_DESC
+#define SNDRV_ISAPNP_DESC	SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC
+#define SNDRV_DMA8_DESC		SNDRV_ENABLED ",allows:{{0,1},{3}},dialog:list"
+#define SNDRV_DMA16_DESC	SNDRV_ENABLED ",allows:{{5,7}},dialog:list"
+#define SNDRV_DMA_DESC		SNDRV_ENABLED ",allows:{{0,1},{3},{5,7}},dialog:list"
+#define SNDRV_IRQ_DESC		SNDRV_ENABLED ",allows:{{5},{7},{9},{10,12},{14,15}},dialog:list"
+#define SNDRV_DMA_SIZE_DESC	SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced"
+#define SNDRV_DMA8_SIZE_DESC	SNDRV_ENABLED ",allows:{{4, 64}},default:64,skill:advanced"
+#define SNDRV_DMA16_SIZE_DESC	SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced"
+#define SNDRV_PORT12_DESC	SNDRV_ENABLED ",allows:{{0,0x3fff}},base:16"
+#define SNDRV_PORT_DESC		SNDRV_ENABLED ",allows:{{0,0xffff}},base:16"
+
+#ifdef SNDRV_LEGACY_AUTO_PROBE
+static int snd_legacy_auto_probe(unsigned long *ports, int (*probe)(unsigned long port))
+{
+	int result = 0;	/* number of detected cards */
+
+	while ((signed long)*ports != -1) {
+		if (probe(*ports) >= 0)
+			result++;
+		ports++;
+	}
+	return result;
+}
+#endif
+
+#ifdef SNDRV_LEGACY_FIND_FREE_IOPORT
+static long snd_legacy_find_free_ioport(long *port_table, long size)
+{
+	while (*port_table != -1) {
+		if (!check_region(*port_table, size))
+			return *port_table;
+		port_table++;
+	}
+	return -1;
+}
+#endif
+
+#ifdef SNDRV_LEGACY_FIND_FREE_IRQ
+static void snd_legacy_empty_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+static int snd_legacy_find_free_irq(int *irq_table)
+{
+	while (*irq_table != -1) {
+		if (!request_irq(*irq_table, snd_legacy_empty_irq_handler,
+				 SA_INTERRUPT, "ALSA Test IRQ", (void *) irq_table)) {
+			free_irq(*irq_table, (void *) irq_table);
+			return *irq_table;
+		}
+		irq_table++;
+	}
+	return -1;
+}
+#endif
+
+#ifdef SNDRV_LEGACY_FIND_FREE_DMA
+static int snd_legacy_find_free_dma(int *dma_table)
+{
+	while (*dma_table != -1) {
+		if (!request_dma(*dma_table, "ALSA Test DMA")) {
+			free_dma(*dma_table);
+			return *dma_table;
+		}
+		dma_table++;
+	}
+	return -1;
+}
+#endif
+
+#if defined(SNDRV_GET_ID) && !defined(MODULE)
+#include <linux/ctype.h>
+static int __init get_id(char **str, char **dst)
+{
+	char *s, *d;
+
+	if (!(*str) || !(**str))
+		return 0;
+	for (s = *str; isalpha(*s) || isdigit(*s) || *s == '_'; s++);
+	if (s != *str) {
+		*dst = (char *)kmalloc(s - *str, GFP_KERNEL);
+		if ((d = *dst) != NULL) {
+			s = *str;
+			while (isalpha(*s) || isdigit(*s) || *s == '_')
+				*d++ = *s++;
+		}
+	}
+	*str = s;
+	if (*s == ',') {
+		(*str)++;
+		return 2;
+	}
+	return 1;
+}
+#endif
+
+#endif /* __SOUND_INITVAL_H */
diff -Nru linux/include/sound/minors.h linux-2.4.19-pre5-mjc/include/sound/minors.h
--- linux/include/sound/minors.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/minors.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,86 @@
+#ifndef __SOUND_MINORS_H
+#define __SOUND_MINORS_H
+
+/*
+ *  MINOR numbers
+ *
+ *
+ *   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
+ *
+ */
+
+#define SNDRV_MINOR_DEVICES		32
+#define SNDRV_MINOR_CARD(minor)		((minor) >> 5)
+#define SNDRV_MINOR_DEVICE(minor)	((minor) & 0x001f)
+#define SNDRV_MINOR(card, dev)		(((card) << 5) | (dev))
+
+#define SNDRV_MINOR_CONTROL		0	/* 0 - 0 */
+#define SNDRV_MINOR_SEQUENCER		1
+#define SNDRV_MINOR_TIMER		(1+32)
+#define SNDRV_MINOR_HWDEP		4	/* 4 - 7 */
+#define SNDRV_MINOR_HWDEPS		4
+#define SNDRV_MINOR_RAWMIDI		8	/* 8 - 11 */
+#define SNDRV_MINOR_RAWMIDIS		4
+#define SNDRV_MINOR_PCM_PLAYBACK	16	/* 16 - 23 */
+#define SNDRV_MINOR_PCM_CAPTURE		24	/* 24 - 31 */
+#define SNDRV_MINOR_PCMS		8
+
+#define SNDRV_DEVICE_TYPE_CONTROL	SNDRV_MINOR_CONTROL
+#define SNDRV_DEVICE_TYPE_HWDEP		SNDRV_MINOR_HWDEP
+#define SNDRV_DEVICE_TYPE_MIXER		SNDRV_MINOR_MIXER
+#define SNDRV_DEVICE_TYPE_RAWMIDI	SNDRV_MINOR_RAWMIDI
+#define SNDRV_DEVICE_TYPE_PCM_PLAYBACK	SNDRV_MINOR_PCM_PLAYBACK
+#define SNDRV_DEVICE_TYPE_PCM_PLOOP	SNDRV_MINOR_PCM_PLOOP
+#define SNDRV_DEVICE_TYPE_PCM_CAPTURE	SNDRV_MINOR_PCM_CAPTURE
+#define SNDRV_DEVICE_TYPE_PCM_CLOOP	SNDRV_MINOR_PCM_CLOOP
+#define SNDRV_DEVICE_TYPE_SEQUENCER	SNDRV_MINOR_SEQUENCER
+#define SNDRV_DEVICE_TYPE_TIMER		SNDRV_MINOR_TIMER
+
+#ifdef CONFIG_SND_OSSEMUL
+
+#define SNDRV_MINOR_OSS_DEVICES		16
+#define SNDRV_MINOR_OSS_CARD(minor)	((minor) >> 4)
+#define SNDRV_MINOR_OSS_DEVICE(minor)	((minor) & 0x000f)
+#define SNDRV_MINOR_OSS(card, dev)	(((card) << 4) | (dev))
+
+#define SNDRV_MINOR_OSS_MIXER		0	/* /dev/mixer - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_SEQUENCER	1	/* /dev/sequencer - OSS 3.XX compatible */
+#define	SNDRV_MINOR_OSS_MIDI		2	/* /dev/midi - native midi interface - OSS 3.XX compatible - UART */
+#define SNDRV_MINOR_OSS_PCM		3	/* alias */
+#define SNDRV_MINOR_OSS_PCM_8		3	/* /dev/dsp - 8bit PCM - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_AUDIO		4	/* /dev/audio - SunSparc compatible */
+#define SNDRV_MINOR_OSS_PCM_16		5	/* /dev/dsp16 - 16bit PCM - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_SNDSTAT		6	/* /dev/sndstat - for compatibility with OSS */
+#define SNDRV_MINOR_OSS_RESERVED7	7	/* reserved for future use */
+#define SNDRV_MINOR_OSS_MUSIC		8	/* /dev/music - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_DMMIDI		9	/* /dev/dmmidi0 - this device can have another minor # with OSS */
+#define SNDRV_MINOR_OSS_DMFM		10	/* /dev/dmfm0 - this device can have another minor # with OSS */
+#define SNDRV_MINOR_OSS_MIXER1		11	/* alternate mixer */
+#define SNDRV_MINOR_OSS_PCM1		12	/* alternate PCM (GF-A-1) */
+#define SNDRV_MINOR_OSS_MIDI1		13	/* alternate midi - SYNTH */
+#define SNDRV_MINOR_OSS_DMMIDI1		14	/* alternate dmmidi - SYNTH */
+#define SNDRV_MINOR_OSS_RESERVED15	15	/* reserved for future use */
+
+#define SNDRV_OSS_DEVICE_TYPE_MIXER	0
+#define SNDRV_OSS_DEVICE_TYPE_SEQUENCER	1
+#define SNDRV_OSS_DEVICE_TYPE_PCM	2
+#define SNDRV_OSS_DEVICE_TYPE_MIDI	3
+#define SNDRV_OSS_DEVICE_TYPE_DMFM	4
+#define SNDRV_OSS_DEVICE_TYPE_SNDSTAT	5
+#define SNDRV_OSS_DEVICE_TYPE_MUSIC	6
+
+#endif
+
+#endif /* __SOUND_MINORS_H */
diff -Nru linux/include/sound/mixer_oss.h linux-2.4.19-pre5-mjc/include/sound/mixer_oss.h
--- linux/include/sound/mixer_oss.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/mixer_oss.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,71 @@
+#ifndef __SOUND_MIXER_OSS_H
+#define __SOUND_MIXER_OSS_H
+
+/*
+ *  OSS MIXER API
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef CONFIG_SND_OSSEMUL
+
+typedef struct _snd_oss_mixer_slot snd_mixer_oss_slot_t;
+typedef struct _snd_oss_file snd_mixer_oss_file_t;
+
+typedef int (*snd_mixer_oss_get_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *left, int *right);
+typedef int (*snd_mixer_oss_put_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int left, int right);
+typedef int (*snd_mixer_oss_get_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *active);
+typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int active);
+typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index);
+typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index);
+
+struct _snd_oss_mixer_slot {
+	int number;
+	int stereo: 1;
+	snd_mixer_oss_get_volume_t get_volume;
+	snd_mixer_oss_put_volume_t put_volume;
+	snd_mixer_oss_get_recsrc_t get_recsrc;
+	snd_mixer_oss_put_recsrc_t put_recsrc;
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_mixer_oss_slot_t *slot);
+};
+
+struct _snd_oss_mixer {
+	snd_card_t *card;
+	char id[16];
+	char name[32];
+	snd_mixer_oss_slot_t slots[32];		/* OSS mixer slots */
+	unsigned int mask_recsrc;		/* exclusive recsrc mask */
+	snd_mixer_oss_get_recsrce_t get_recsrc;
+	snd_mixer_oss_put_recsrce_t put_recsrc;
+	void *private_data_recsrc;
+	void (*private_free_recsrc)(snd_mixer_oss_t *mixer);
+	/* --- */
+	int oss_recsrc;
+};
+
+struct _snd_oss_file {
+	int volume[32][2];
+	snd_card_t *card;
+	snd_mixer_oss_t *mixer;
+};
+
+#endif /* CONFIG_SND_OSSEMUL */
+
+#endif /* __SOUND_MIXER_OSS_H */
diff -Nru linux/include/sound/mpu401.h linux-2.4.19-pre5-mjc/include/sound/mpu401.h
--- linux/include/sound/mpu401.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/mpu401.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,109 @@
+#ifndef __SOUND_MPU401_H
+#define __SOUND_MPU401_H
+
+/*
+ *  Header file for MPU-401 and compatible cards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include "rawmidi.h"
+
+#define MPU401_HW_MPU401		1	/* native MPU401 */
+#define MPU401_HW_SB			2	/* SoundBlaster MPU-401 UART */
+#define MPU401_HW_ES1688		3	/* AudioDrive ES1688 MPU-401 UART */
+#define MPU401_HW_OPL3SA2		4	/* Yamaha OPL3-SA2 */
+#define MPU401_HW_SONICVIBES		5	/* S3 SonicVibes */
+#define MPU401_HW_CS4232		6	/* CS4232 */
+#define MPU401_HW_ES18XX		7	/* AudioDrive ES18XX MPU-401 UART */
+#define MPU401_HW_FM801			8	/* ForteMedia FM801 */
+#define MPU401_HW_TRID4DWAVE		9	/* Trident 4DWave */
+#define MPU401_HW_AZT2320		10	/* Aztech AZT2320 */
+#define MPU401_HW_ALS100		11	/* Avance Logic ALS100 */
+#define MPU401_HW_ICE1712		12	/* Envy24 */
+#define MPU401_HW_VIA686A		13	/* VIA 82C686A */
+#define MPU401_HW_YMFPCI		14	/* YMF DS-XG PCI */
+#define MPU401_HW_CMIPCI		15	/* CMIPCI MPU-401 UART */
+#define MPU401_HW_ALS4000		16	/* Avance Logic ALS4000 */
+
+#define MPU401_MODE_BIT_INPUT		0
+#define MPU401_MODE_BIT_OUTPUT		1
+#define MPU401_MODE_BIT_INPUT_TRIGGER	2
+#define MPU401_MODE_BIT_OUTPUT_TRIGGER	3
+#define MPU401_MODE_BIT_RX_LOOP		4
+#define MPU401_MODE_BIT_TX_LOOP		5
+
+#define MPU401_MODE_INPUT		(1<<MPU401_MODE_BIT_INPUT)
+#define MPU401_MODE_OUTPUT		(1<<MPU401_MODE_BIT_OUTPUT)
+#define MPU401_MODE_INPUT_TRIGGER	(1<<MPU401_MODE_BIT_INPUT_TRIGGER)
+#define MPU401_MODE_OUTPUT_TRIGGER	(1<<MPU401_MODE_BIT_OUTPUT_TRIGGER)
+
+#define MPU401_MODE_INPUT_TIMER		(1<<0)
+#define MPU401_MODE_OUTPUT_TIMER	(1<<1)
+
+typedef struct _snd_mpu401 mpu401_t;
+
+struct _snd_mpu401 {
+	snd_rawmidi_t *rmidi;
+
+	unsigned short hardware;	/* MPU401_HW_XXXX */
+	unsigned long port;		/* base port of MPU-401 chip */
+	struct resource *res;		/* port resource */
+	int irq;			/* IRQ number of MPU-401 chip (-1 = poll) */
+	int irq_flags;
+
+	unsigned int mode;		/* MPU401_MODE_XXXX */
+	int timer_invoked;
+
+	int (*open_input) (mpu401_t * mpu);
+	void (*close_input) (mpu401_t * mpu);
+	int (*open_output) (mpu401_t * mpu);
+	void (*close_output) (mpu401_t * mpu);
+	void *private_data;
+
+	snd_rawmidi_substream_t *substream_input;
+	snd_rawmidi_substream_t *substream_output;
+
+	spinlock_t input_lock;
+	spinlock_t output_lock;
+	spinlock_t timer_lock;
+
+	struct timer_list timer;
+};
+
+/* I/O ports */
+
+#define MPU401C(mpu) ((mpu)->port + 1)
+#define MPU401D(mpu) ((mpu)->port + 0)
+
+/*
+
+ */
+
+void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+int snd_mpu401_uart_new(snd_card_t * card,
+			int device,
+			unsigned short hardware,
+			unsigned long port,
+			int integrated,
+			int irq,
+			int irq_flags,
+			snd_rawmidi_t ** rrawmidi);
+
+#endif /* __SOUND_MPU401_H */
diff -Nru linux/include/sound/opl3.h linux-2.4.19-pre5-mjc/include/sound/opl3.h
--- linux/include/sound/opl3.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/opl3.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,332 @@
+#ifndef __SOUND_OPL3_H
+#define __SOUND_OPL3_H
+
+/*
+ * Definitions of the OPL-3 registers.
+ *
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                  Hannu Savolainen 1993-1996
+ *
+ *
+ *      The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ *      of the right side.
+ *
+ *      Another special register at the right side is at offset 4. It contains
+ *      a bit mask defining which voices are used as 4 OP voices.
+ *
+ *      The percussive mode is implemented in the left side only.
+ *
+ *      With the above exceptions the both sides can be operated independently.
+ *      
+ *      A 4 OP voice can be created by setting the corresponding
+ *      bit at offset 4 of the right side.
+ *
+ *      For example setting the rightmost bit (0x01) changes the
+ *      first voice on the right side to the 4 OP mode. The fourth
+ *      voice is made inaccessible.
+ *
+ *      If a voice is set to the 2 OP mode, it works like 2 OP modes
+ *      of the original YM3812 (AdLib). In addition the voice can 
+ *      be connected the left, right or both stereo channels. It can
+ *      even be left unconnected. This works with 4 OP voices also.
+ *
+ *      The stereo connection bits are located in the FEEDBACK_CONNECTION
+ *      register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ *      in the second half of the voice.
+ *
+ *
+ *   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
+ *
+ */
+
+#include "driver.h"
+#include <linux/time.h>
+#include "core.h"
+#include "hwdep.h"
+#include "timer.h"
+#include "seq_midi_emul.h"
+#ifdef CONFIG_SND_OSSEMUL
+#include "seq_oss.h"
+#include "seq_oss_legacy.h"
+#endif
+#include "seq_device.h"
+#include "ainstr_fm.h"
+
+/*
+ *    Register numbers for the global registers
+ */
+
+#define OPL3_REG_TEST			0x01
+#define   OPL3_ENABLE_WAVE_SELECT	0x20
+
+#define OPL3_REG_TIMER1			0x02
+#define OPL3_REG_TIMER2			0x03
+#define OPL3_REG_TIMER_CONTROL		0x04	/* Left side */
+#define   OPL3_IRQ_RESET		0x80
+#define   OPL3_TIMER1_MASK		0x40
+#define   OPL3_TIMER2_MASK		0x20
+#define   OPL3_TIMER1_START		0x01
+#define   OPL3_TIMER2_START		0x02
+
+#define OPL3_REG_CONNECTION_SELECT	0x04	/* Right side */
+#define   OPL3_LEFT_4OP_0		0x01
+#define   OPL3_LEFT_4OP_1		0x02
+#define   OPL3_LEFT_4OP_2		0x04
+#define   OPL3_RIGHT_4OP_0		0x08
+#define   OPL3_RIGHT_4OP_1		0x10
+#define   OPL3_RIGHT_4OP_2		0x20
+
+#define OPL3_REG_MODE			0x05	/* Right side */
+#define   OPL3_OPL3_ENABLE		0x01	/* OPL3 mode */
+#define   OPL3_OPL4_ENABLE		0x02	/* OPL4 mode */
+
+#define OPL3_REG_KBD_SPLIT		0x08	/* Left side */
+#define   OPL3_COMPOSITE_SINE_WAVE_MODE	0x80	/* Don't use with OPL-3? */
+#define   OPL3_KEYBOARD_SPLIT		0x40
+
+#define OPL3_REG_PERCUSSION		0xbd	/* Left side only */
+#define   OPL3_TREMOLO_DEPTH		0x80
+#define   OPL3_VIBRATO_DEPTH		0x40
+#define	  OPL3_PERCUSSION_ENABLE	0x20
+#define   OPL3_BASSDRUM_ON		0x10
+#define   OPL3_SNAREDRUM_ON		0x08
+#define   OPL3_TOMTOM_ON		0x04
+#define   OPL3_CYMBAL_ON		0x02
+#define   OPL3_HIHAT_ON			0x01
+
+/*
+ *    Offsets to the register banks for operators. To get the
+ *      register number just add the operator offset to the bank offset
+ *
+ *      AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+#define OPL3_REG_AM_VIB			0x20
+#define   OPL3_TREMOLO_ON		0x80
+#define   OPL3_VIBRATO_ON		0x40
+#define   OPL3_SUSTAIN_ON		0x20
+#define   OPL3_KSR			0x10	/* Key scaling rate */
+#define   OPL3_MULTIPLE_MASK		0x0f	/* Frequency multiplier */
+
+ /*
+  *   KSL/Total level (0x40 to 0x55)
+  */
+#define OPL3_REG_KSL_LEVEL		0x40
+#define   OPL3_KSL_MASK			0xc0	/* Envelope scaling bits */
+#define   OPL3_TOTAL_LEVEL_MASK		0x3f	/* Strength (volume) of OP */
+
+/*
+ *    Attack / Decay rate (0x60 to 0x75)
+ */
+#define OPL3_REG_ATTACK_DECAY		0x60
+#define   OPL3_ATTACK_MASK		0xf0
+#define   OPL3_DECAY_MASK		0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define OPL3_REG_SUSTAIN_RELEASE	0x80
+#define   OPL3_SUSTAIN_MASK		0xf0
+#define   OPL3_RELEASE_MASK		0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define OPL3_REG_WAVE_SELECT		0xe0
+#define   OPL3_WAVE_SELECT_MASK		0x07
+
+/*
+ *    Offsets to the register banks for voices. Just add to the
+ *      voice number to get the register number.
+ *
+ *      F-Number low bits (0xA0 to 0xA8).
+ */
+#define OPL3_REG_FNUM_LOW		0xa0
+
+/*
+ *    F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define OPL3_REG_KEYON_BLOCK		0xb0
+#define	  OPL3_KEYON_BIT		0x20
+#define	  OPL3_BLOCKNUM_MASK		0x1c
+#define   OPL3_FNUM_HIGH_MASK		0x03
+
+/*
+ *    Feedback / Connection (0xc0 to 0xc8)
+ *
+ *      These registers have two new bits when the OPL-3 mode
+ *      is selected. These bits controls connecting the voice
+ *      to the stereo channels. For 4 OP voices this bit is
+ *      defined in the second half of the voice (add 3 to the
+ *      register offset).
+ *
+ *      For 4 OP voices the connection bit is used in the
+ *      both halves (gives 4 ways to connect the operators).
+ */
+#define OPL3_REG_FEEDBACK_CONNECTION	0xc0
+#define   OPL3_FEEDBACK_MASK		0x0e	/* Valid just for 1st OP of a voice */
+#define   OPL3_CONNECTION_BIT		0x01
+/*
+ *    In the 4 OP mode there is four possible configurations how the
+ *      operators can be connected together (in 2 OP modes there is just
+ *      AM or FM). The 4 OP connection mode is defined by the rightmost
+ *      bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
+ *
+ *      First half      Second half     Mode
+ *
+ *                                       +---+
+ *                                       v   |
+ *      0               0               >+-1-+--2--3--4-->
+ *
+ *
+ *                                      
+ *                                       +---+
+ *                                       |   |
+ *      0               1               >+-1-+--2-+
+ *                                                |->
+ *                                      >--3----4-+
+ *                                      
+ *                                       +---+
+ *                                       |   |
+ *      1               0               >+-1-+-----+
+ *                                                 |->
+ *                                      >--2--3--4-+
+ *
+ *                                       +---+
+ *                                       |   |
+ *      1               1               >+-1-+--+
+ *                                              |
+ *                                      >--2--3-+->
+ *                                              |
+ *                                      >--4----+
+ */
+#define   OPL3_STEREO_BITS		0x30	/* OPL-3 only */
+#define     OPL3_VOICE_TO_LEFT		0x10
+#define     OPL3_VOICE_TO_RIGHT		0x20
+
+/*
+
+ */
+
+#define OPL3_LEFT		0x0000
+#define OPL3_RIGHT		0x0100
+
+#define OPL3_HW_AUTO		0x0000
+#define OPL3_HW_OPL2		0x0200
+#define OPL3_HW_OPL3		0x0300
+#define OPL3_HW_OPL3_SV		0x0301	/* S3 SonicVibes */
+#define OPL3_HW_OPL3_CS		0x0302	/* CS4232/CS4236+ */
+#define OPL3_HW_OPL3_FM801	0x0303	/* FM801 */
+#define OPL3_HW_OPL3_CS4281	0x0304	/* CS4281 */
+#define OPL3_HW_OPL4		0x0400
+#define OPL3_HW_MASK		0xff00
+
+#define MAX_OPL2_VOICES		9
+#define MAX_OPL3_VOICES		18
+
+typedef struct snd_opl3 opl3_t;
+
+/*
+ * A structure to keep track of each hardware voice
+ */
+typedef struct snd_opl3_voice {
+	int  state;		/* status */
+#define SNDRV_OPL3_ST_OFF		0	/* Not playing */
+#define SNDRV_OPL3_ST_ON_2OP	1	/* 2op voice is allocated */
+#define SNDRV_OPL3_ST_ON_4OP	2	/* 4op voice is allocated */
+#define SNDRV_OPL3_ST_NOT_AVAIL	-1	/* voice is not available */
+
+	unsigned int time;	/* An allocation time */
+	unsigned char note;	/* Note currently assigned to this voice */
+
+	unsigned long note_off;	/* note-off time */
+	int note_off_check;	/* check note-off time */
+
+	unsigned char keyon_reg;	/* KON register shadow */
+
+	snd_midi_channel_t *chan;	/* Midi channel for this note */
+} snd_opl3_voice_t;
+
+struct snd_opl3 {
+	unsigned long l_port;
+	unsigned long r_port;
+	struct resource *res_l_port;
+	struct resource *res_r_port;
+	unsigned short hardware;
+	/* hardware access */
+	void (*command) (opl3_t * opl3, unsigned short cmd, unsigned char val);
+	unsigned short timer_enable;
+	int seq_dev_num;	/* sequencer device number */
+	snd_timer_t *timer1;
+	snd_timer_t *timer2;
+	spinlock_t timer_lock;
+
+	spinlock_t reg_lock;
+	snd_card_t *card;		/* The card that this belongs to */
+	int used;			/* usage flag - exclusive */
+	unsigned char fm_mode;		/* OPL mode, see SNDRV_DM_FM_MODE_XXX */
+	unsigned char rhythm;		/* percussion mode flag */
+	unsigned char max_voices;	/* max number of voices */
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#define SNDRV_OPL3_MODE_SYNTH 0		/* OSS - voices allocated by application */
+#define SNDRV_OPL3_MODE_SEQ 1		/* ALSA - driver handles voice allocation */
+	int synth_mode;			/* synth mode */
+	int seq_client;
+
+	snd_seq_device_t *seq_dev;	/* sequencer device */
+	snd_midi_channel_set_t * chset;
+
+#ifdef CONFIG_SND_OSSEMUL
+	snd_seq_device_t *oss_seq_dev;	/* OSS sequencer device, WIP */
+	snd_midi_channel_set_t * oss_chset;
+#endif
+ 
+	snd_seq_kinstr_ops_t fm_ops;
+	snd_seq_kinstr_list_t *ilist;
+
+	snd_opl3_voice_t voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */
+	int use_time;			/* allocation counter */
+
+	unsigned short connection_reg;	/* connection reg shadow */
+	unsigned char drum_reg;		/* percussion reg shadow */
+
+	spinlock_t voice_lock;		/* Lock for voice access */
+
+	struct timer_list tlist;	/* timer for note-offs and effects */
+	int sys_timer_status;		/* system timer run status */
+	spinlock_t sys_timer_lock;	/* Lock for system timer access */
+#endif
+	struct semaphore access_mutex;	/* locking */
+};
+
+/* opl3.c */
+void snd_opl3_interrupt(snd_hwdep_t * hw);
+int snd_opl3_create(snd_card_t * card,
+		    unsigned long l_port, unsigned long r_port,
+		    unsigned short hardware,
+		    int integrated,
+		    opl3_t ** opl3);
+int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev);
+int snd_opl3_hwdep_new(opl3_t * opl3, int device, int seq_device,
+		       snd_hwdep_t ** rhwdep);
+
+/* opl3_synth */
+int snd_opl3_open(snd_hwdep_t * hw, struct file *file);
+int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file,
+		   unsigned int cmd, unsigned long arg);
+int snd_opl3_release(snd_hwdep_t * hw, struct file *file);
+
+void snd_opl3_reset(opl3_t * opl3);
+
+#endif /* __SOUND_OPL3_H */
diff -Nru linux/include/sound/pcm.h linux-2.4.19-pre5-mjc/include/sound/pcm.h
--- linux/include/sound/pcm.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/pcm.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,864 @@
+#ifndef __SOUND_PCM_H
+#define __SOUND_PCM_H
+
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/asound.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+
+typedef sndrv_pcm_uframes_t snd_pcm_uframes_t;
+typedef sndrv_pcm_sframes_t snd_pcm_sframes_t;
+typedef enum sndrv_pcm_class snd_pcm_class_t;
+typedef enum sndrv_pcm_subclass snd_pcm_subclass_t;
+typedef enum sndrv_pcm_stream snd_pcm_stream_t;
+typedef enum sndrv_pcm_access snd_pcm_access_t;
+typedef enum sndrv_pcm_format snd_pcm_format_t;
+typedef enum sndrv_pcm_subformat snd_pcm_subformat_t;
+typedef enum sndrv_pcm_state snd_pcm_state_t;
+typedef union sndrv_pcm_sync_id snd_pcm_sync_id_t;
+typedef struct sndrv_pcm_info snd_pcm_info_t;
+typedef enum sndrv_pcm_hw_param snd_pcm_hw_param_t;
+typedef struct sndrv_pcm_hw_params snd_pcm_hw_params_t;
+typedef enum sndrv_pcm_start snd_pcm_start_t;
+typedef enum sndrv_pcm_xrun snd_pcm_xrun_t;
+typedef enum sndrv_pcm_tstamp snd_pcm_tstamp_t;
+typedef struct sndrv_pcm_sw_params snd_pcm_sw_params_t;
+typedef struct sndrv_pcm_channel_info snd_pcm_channel_info_t;
+typedef struct sndrv_pcm_status snd_pcm_status_t;
+typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t;
+typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t;
+
+#define _snd_pcm_substream_chip(substream) ((substream)->pcm->private_data)
+#define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO)
+#define _snd_pcm_chip(pcm) ((pcm)->private_data)
+#define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO)
+
+typedef struct _snd_pcm_file snd_pcm_file_t;
+typedef struct _snd_pcm_runtime snd_pcm_runtime_t;
+
+#ifdef CONFIG_SND_OSSEMUL
+#include "pcm_oss.h"
+#endif
+
+/*
+ *  Hardware (lowlevel) section
+ */
+
+typedef struct _snd_pcm_hardware {
+	unsigned int info;		/* SNDRV_PCM_INFO_* */
+	unsigned int formats;		/* SNDRV_PCM_FMTBIT_* */
+	unsigned int rates;		/* SNDRV_PCM_RATE_* */
+	unsigned int rate_min;		/* min rate */
+	unsigned int rate_max;		/* max rate */
+	unsigned int channels_min;	/* min channels */
+	unsigned int channels_max;	/* max channels */
+	size_t buffer_bytes_max;	/* max buffer size */
+	size_t period_bytes_min;	/* min period size */
+	size_t period_bytes_max;	/* max period size */
+	unsigned int periods_min;	/* min # of periods */
+	unsigned int periods_max;	/* max # of periods */
+	size_t fifo_size;		/* fifo size in bytes */
+} snd_pcm_hardware_t;
+
+typedef struct _snd_pcm_ops {
+	int (*open)(snd_pcm_substream_t *substream);
+	int (*close)(snd_pcm_substream_t *substream);
+	int (*ioctl)(snd_pcm_substream_t * substream,
+		     unsigned int cmd, void *arg);
+	int (*hw_params)(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params);
+	int (*hw_free)(snd_pcm_substream_t *substream);
+	int (*prepare)(snd_pcm_substream_t * substream);
+	int (*trigger)(snd_pcm_substream_t * substream, int cmd);
+	snd_pcm_uframes_t (*pointer)(snd_pcm_substream_t * substream);
+	int (*copy)(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos,
+		    void *buf, snd_pcm_uframes_t count);
+	int (*silence)(snd_pcm_substream_t *substream, int channel, 
+		       snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
+} snd_pcm_ops_t;
+
+/*
+ *
+ */
+
+#define SNDRV_PCM_DEVICES		8
+
+#define SNDRV_PCM_IOCTL1_FALSE		((void *)0)
+#define SNDRV_PCM_IOCTL1_TRUE		((void *)1)
+
+#define SNDRV_PCM_IOCTL1_RESET		0
+#define SNDRV_PCM_IOCTL1_INFO		1
+#define SNDRV_PCM_IOCTL1_CHANNEL_INFO	2
+#define SNDRV_PCM_IOCTL1_GSTATE		3
+
+#define SNDRV_PCM_TRIGGER_STOP		0
+#define SNDRV_PCM_TRIGGER_START		1
+#define SNDRV_PCM_TRIGGER_PAUSE_PUSH	3
+#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE	4
+#define SNDRV_PCM_TRIGGER_SUSPEND	5
+#define SNDRV_PCM_TRIGGER_RESUME	6
+
+#define SNDRV_PCM_DMA_TYPE_CONTINUOUS	0	/* continuous no-DMA memory */
+#define SNDRV_PCM_DMA_TYPE_ISA		1	/* ISA continuous */
+#define SNDRV_PCM_DMA_TYPE_PCI		2	/* PCI continuous */
+
+/* If you change this don't forget to changed snd_pcm_rates table in pcm_lib.c */
+#define SNDRV_PCM_RATE_5512		(1<<0)		/* 5512Hz */
+#define SNDRV_PCM_RATE_8000		(1<<1)		/* 8000Hz */
+#define SNDRV_PCM_RATE_11025		(1<<2)		/* 11025Hz */
+#define SNDRV_PCM_RATE_16000		(1<<3)		/* 16000Hz */
+#define SNDRV_PCM_RATE_22050		(1<<4)		/* 22050Hz */
+#define SNDRV_PCM_RATE_32000		(1<<5)		/* 32000Hz */
+#define SNDRV_PCM_RATE_44100		(1<<6)		/* 44100Hz */
+#define SNDRV_PCM_RATE_48000		(1<<7)		/* 48000Hz */
+#define SNDRV_PCM_RATE_64000		(1<<8)		/* 64000Hz */
+#define SNDRV_PCM_RATE_88200		(1<<9)		/* 88200Hz */
+#define SNDRV_PCM_RATE_96000		(1<<10)		/* 96000Hz */
+#define SNDRV_PCM_RATE_176400		(1<<11)		/* 176400Hz */
+#define SNDRV_PCM_RATE_192000		(1<<12)		/* 192000Hz */
+
+#define SNDRV_PCM_RATE_CONTINUOUS	(1<<30)		/* continuous range */
+#define SNDRV_PCM_RATE_KNOT		(1<<31)		/* supports more non-continuos rates */
+
+#define SNDRV_PCM_RATE_8000_44100	(SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\
+					 SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\
+					 SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100)
+#define SNDRV_PCM_RATE_8000_48000	(SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000)
+#define SNDRV_PCM_RATE_8000_96000	(SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\
+					 SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
+#define SNDRV_PCM_RATE_8000_192000	(SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
+					 SNDRV_PCM_RATE_192000)
+#define SNDRV_PCM_FMTBIT_S8		(1 << SNDRV_PCM_FORMAT_S8)
+#define SNDRV_PCM_FMTBIT_U8		(1 << SNDRV_PCM_FORMAT_U8)
+#define SNDRV_PCM_FMTBIT_S16_LE		(1 << SNDRV_PCM_FORMAT_S16_LE)
+#define SNDRV_PCM_FMTBIT_S16_BE		(1 << SNDRV_PCM_FORMAT_S16_BE)
+#define SNDRV_PCM_FMTBIT_U16_LE		(1 << SNDRV_PCM_FORMAT_U16_LE)
+#define SNDRV_PCM_FMTBIT_U16_BE		(1 << SNDRV_PCM_FORMAT_U16_BE)
+#define SNDRV_PCM_FMTBIT_S24_LE		(1 << SNDRV_PCM_FORMAT_S24_LE)
+#define SNDRV_PCM_FMTBIT_S24_BE		(1 << SNDRV_PCM_FORMAT_S24_BE)
+#define SNDRV_PCM_FMTBIT_U24_LE		(1 << SNDRV_PCM_FORMAT_U24_LE)
+#define SNDRV_PCM_FMTBIT_U24_BE		(1 << SNDRV_PCM_FORMAT_U24_BE)
+#define SNDRV_PCM_FMTBIT_S32_LE		(1 << SNDRV_PCM_FORMAT_S32_LE)
+#define SNDRV_PCM_FMTBIT_S32_BE		(1 << SNDRV_PCM_FORMAT_S32_BE)
+#define SNDRV_PCM_FMTBIT_U32_LE		(1 << SNDRV_PCM_FORMAT_U32_LE)
+#define SNDRV_PCM_FMTBIT_U32_BE		(1 << SNDRV_PCM_FORMAT_U32_BE)
+#define SNDRV_PCM_FMTBIT_FLOAT_LE	(1 << SNDRV_PCM_FORMAT_FLOAT_LE)
+#define SNDRV_PCM_FMTBIT_FLOAT_BE	(1 << SNDRV_PCM_FORMAT_FLOAT_BE)
+#define SNDRV_PCM_FMTBIT_FLOAT64_LE	(1 << SNDRV_PCM_FORMAT_FLOAT64_LE)
+#define SNDRV_PCM_FMTBIT_FLOAT64_BE	(1 << SNDRV_PCM_FORMAT_FLOAT64_BE)
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1 << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1 << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE)
+#define SNDRV_PCM_FMTBIT_MU_LAW		(1 << SNDRV_PCM_FORMAT_MU_LAW)
+#define SNDRV_PCM_FMTBIT_A_LAW		(1 << SNDRV_PCM_FORMAT_A_LAW)
+#define SNDRV_PCM_FMTBIT_IMA_ADPCM	(1 << SNDRV_PCM_FORMAT_IMA_ADPCM)
+#define SNDRV_PCM_FMTBIT_MPEG		(1 << SNDRV_PCM_FORMAT_MPEG)
+#define SNDRV_PCM_FMTBIT_GSM		(1 << SNDRV_PCM_FORMAT_GSM)
+#define SNDRV_PCM_FMTBIT_SPECIAL	(1 << SNDRV_PCM_FORMAT_SPECIAL)
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define SNDRV_PCM_FMTBIT_S16		SNDRV_PCM_FMTBIT_S16_LE
+#define SNDRV_PCM_FMTBIT_U16		SNDRV_PCM_FMTBIT_U16_LE
+#define SNDRV_PCM_FMTBIT_S24		SNDRV_PCM_FMTBIT_S24_LE
+#define SNDRV_PCM_FMTBIT_U24		SNDRV_PCM_FMTBIT_U24_LE
+#define SNDRV_PCM_FMTBIT_S32		SNDRV_PCM_FMTBIT_S32_LE
+#define SNDRV_PCM_FMTBIT_U32		SNDRV_PCM_FMTBIT_U32_LE
+#define SNDRV_PCM_FMTBIT_FLOAT		SNDRV_PCM_FMTBIT_FLOAT_LE
+#define SNDRV_PCM_FMTBIT_FLOAT64	SNDRV_PCM_FMTBIT_FLOAT64_LE
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+#endif
+#ifdef SNDRV_BIG_ENDIAN
+#define SNDRV_PCM_FMTBIT_S16		SNDRV_PCM_FMTBIT_S16_BE
+#define SNDRV_PCM_FMTBIT_U16		SNDRV_PCM_FMTBIT_U16_BE
+#define SNDRV_PCM_FMTBIT_S24		SNDRV_PCM_FMTBIT_S24_BE
+#define SNDRV_PCM_FMTBIT_U24		SNDRV_PCM_FMTBIT_U24_BE
+#define SNDRV_PCM_FMTBIT_S32		SNDRV_PCM_FMTBIT_S32_BE
+#define SNDRV_PCM_FMTBIT_U32		SNDRV_PCM_FMTBIT_U32_BE
+#define SNDRV_PCM_FMTBIT_FLOAT		SNDRV_PCM_FMTBIT_FLOAT_BE
+#define SNDRV_PCM_FMTBIT_FLOAT64	SNDRV_PCM_FMTBIT_FLOAT64_BE
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+#endif
+
+struct _snd_pcm_file {
+	snd_pcm_substream_t * substream;
+	struct _snd_pcm_file * next;
+};
+
+typedef struct _snd_pcm_hw_rule snd_pcm_hw_rule_t;
+
+typedef int (*snd_pcm_hw_rule_func_t)(snd_pcm_hw_params_t *params,
+				      snd_pcm_hw_rule_t *rule);
+
+struct _snd_pcm_hw_rule {
+	unsigned int cond;
+	snd_pcm_hw_rule_func_t func;
+	int var;
+	int deps[4];
+	void *private;
+};
+
+typedef struct _snd_pcm_hw_constraints {
+	unsigned int masks[SNDRV_PCM_HW_PARAM_LAST_MASK - 
+			   SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
+	snd_interval_t intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
+			     SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+	unsigned int rules_num;
+	unsigned int rules_all;
+	snd_pcm_hw_rule_t *rules;
+} snd_pcm_hw_constraints_t;
+
+static inline unsigned int *constrs_mask(snd_pcm_hw_constraints_t *constrs,
+					snd_pcm_hw_param_t var)
+{
+	return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline snd_interval_t *constrs_interval(snd_pcm_hw_constraints_t *constrs,
+					  snd_pcm_hw_param_t var)
+{
+	return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+typedef struct {
+	unsigned int num;
+	unsigned int den_min, den_max, den_step;
+} ratnum_t;
+
+typedef struct {
+	unsigned int num_min, num_max, num_step;
+	unsigned int den;
+} ratden_t;
+
+typedef struct {
+	int nrats;
+	ratnum_t *rats;
+} snd_pcm_hw_constraint_ratnums_t;
+
+typedef struct {
+	int nrats;
+	ratden_t *rats;
+} snd_pcm_hw_constraint_ratdens_t;
+
+typedef struct {
+	unsigned int count;
+	unsigned int *list;
+	unsigned int mask;
+} snd_pcm_hw_constraint_list_t;
+
+struct _snd_pcm_runtime {
+	/* -- Status -- */
+	snd_pcm_substream_t *trigger_master;
+	snd_timestamp_t trigger_tstamp;	/* trigger timestamp */
+	int overrange;
+	snd_pcm_uframes_t avail_max;
+	snd_pcm_uframes_t hw_ptr_base;	/* Position at buffer restart */
+	snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/
+
+	/* -- HW params -- */
+	snd_pcm_access_t access;	/* access mode */
+	snd_pcm_format_t format;	/* SNDRV_PCM_FORMAT_* */
+	snd_pcm_subformat_t subformat;	/* subformat */
+	unsigned int rate;		/* rate in Hz */
+	unsigned int channels;		/* channels */
+	snd_pcm_uframes_t period_size;	/* period size */
+	unsigned int periods;		/* periods */
+	snd_pcm_uframes_t buffer_size;	/* buffer size */
+	unsigned int tick_time;		/* tick time */
+	snd_pcm_uframes_t min_align;	/* Min alignment for the format */
+	size_t byte_align;
+	unsigned int frame_bits;
+	unsigned int sample_bits;
+	unsigned int info;
+	unsigned int rate_num;
+	unsigned int rate_den;
+
+	/* -- SW params -- */
+	snd_pcm_tstamp_t tstamp_mode;	/* mmap timestamp is updated */
+  	unsigned int period_step;
+	unsigned int sleep_min;		/* min ticks to sleep */
+	snd_pcm_uframes_t xfer_align;	/* xfer size need to be a multiple */
+	snd_pcm_uframes_t start_threshold;
+	snd_pcm_uframes_t stop_threshold;
+	snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
+						noise is nearest than this */
+	snd_pcm_uframes_t silence_size;	/* Silence filling size */
+	snd_pcm_uframes_t boundary;	/* pointers wrap point */
+
+	snd_pcm_uframes_t silenced_start;
+	snd_pcm_uframes_t silenced_size;
+
+	snd_pcm_sync_id_t sync;		/* hardware synchronization ID */
+
+	/* -- mmap -- */
+	volatile snd_pcm_mmap_status_t *status;
+	volatile snd_pcm_mmap_control_t *control;
+	atomic_t mmap_count;
+
+	/* -- locking / scheduling -- */
+	spinlock_t lock;
+	wait_queue_head_t sleep;
+	struct timer_list tick_timer;
+	struct fasync_struct *fasync;
+
+	/* -- private section -- */
+	void *private_data;
+	void (*private_free)(snd_pcm_runtime_t *runtime);
+
+	/* -- hardware description -- */
+	snd_pcm_hardware_t hw;
+	snd_pcm_hw_constraints_t hw_constraints;
+
+	/* -- interrupt callbacks -- */
+	void (*transfer_ack_begin)(snd_pcm_substream_t *substream);
+	void (*transfer_ack_end)(snd_pcm_substream_t *substream);
+
+	/* -- timer -- */
+	unsigned int timer_resolution;	/* timer resolution */
+
+	/* -- DMA -- */           
+	unsigned char *dma_area;	/* DMA area */
+	dma_addr_t dma_addr;		/* physical bus address (not accessible from main CPU) */
+	unsigned long dma_bytes;	/* size of DMA area */
+
+#ifdef CONFIG_SND_OSSEMUL
+	/* -- OSS things -- */
+	snd_pcm_oss_runtime_t oss;
+#endif
+};
+
+struct _snd_pcm_substream {
+	snd_pcm_t *pcm;
+	snd_pcm_str_t *pstr;
+	int number;
+	char name[32];			/* substream name */
+	int stream;			/* stream (direction) */
+	size_t buffer_bytes_max;	/* limit ring buffer size */
+	int dma_type;			
+	void *dma_area;
+	dma_addr_t dma_addr;
+	size_t dma_bytes;
+	size_t dma_max;
+	void *dma_private;
+	/* -- hardware operations -- */
+	snd_pcm_ops_t *ops;
+	/* -- runtime information -- */
+	snd_pcm_runtime_t *runtime;
+        /* -- timer section -- */
+	snd_timer_t *timer;		/* timer */
+	int timer_running;		/* time is running */
+	spinlock_t timer_lock;
+	/* -- next substream -- */
+	snd_pcm_substream_t *next;
+	/* -- linked substreams -- */
+	snd_pcm_substream_t *link_next;
+	snd_pcm_substream_t *link_prev;
+	snd_pcm_file_t *file;
+	struct file *ffile;
+#ifdef CONFIG_SND_OSSEMUL
+	/* -- OSS things -- */
+	snd_pcm_oss_substream_t oss;
+#endif
+	snd_info_entry_t *proc_root;
+	snd_info_entry_t *proc_info_entry;
+	snd_info_entry_t *proc_hw_params_entry;
+	snd_info_entry_t *proc_sw_params_entry;
+	snd_info_entry_t *proc_status_entry;
+	snd_info_entry_t *proc_prealloc_entry;
+};
+
+#ifdef CONFIG_SND_OSSEMUL
+#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL))
+#else
+#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL)
+#endif
+
+
+struct _snd_pcm_str {
+	int stream;				/* stream (direction) */
+	snd_pcm_t *pcm;
+	/* -- substreams -- */
+	unsigned int substream_count;
+	unsigned int substream_opened;
+	snd_pcm_substream_t *substream;
+#ifdef CONFIG_SND_OSSEMUL
+	/* -- OSS things -- */
+	snd_pcm_oss_stream_t oss;
+#endif
+	snd_pcm_file_t *files;
+	snd_minor_t *reg;
+	snd_info_entry_t *proc_root;
+	snd_info_entry_t *proc_info_entry;
+};
+
+struct _snd_pcm {
+	snd_card_t *card;
+	unsigned int device;	/* device number */
+	unsigned int info_flags;
+	unsigned short dev_class;
+	unsigned short dev_subclass;
+	char id[64];
+	char name[80];
+	snd_pcm_str_t streams[2];
+	struct semaphore open_mutex;
+	wait_queue_head_t open_wait;
+	void *private_data;
+	void (*private_free) (snd_pcm_t *pcm);
+#ifdef CONFIG_SND_OSSEMUL
+	snd_pcm_oss_t oss;
+#endif
+};
+
+typedef struct _snd_pcm_notify {
+	int (*n_register) (unsigned short minor, snd_pcm_t * pcm);
+	int (*n_unregister) (unsigned short minor, snd_pcm_t * pcm);
+	struct list_head list;
+} snd_pcm_notify_t;
+
+/*
+ *  Registering
+ */
+
+extern snd_pcm_t *snd_pcm_devices[];
+extern snd_minor_t snd_pcm_reg[2];
+
+void snd_pcm_lock(int unlock);
+
+int snd_pcm_new(snd_card_t * card, char *id, int device,
+		int playback_count, int capture_count,
+		snd_pcm_t **rpcm);
+
+int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree);
+
+/*
+ *  Native I/O
+ */
+
+int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info);
+int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t *info);
+int snd_pcm_status(snd_pcm_substream_t * substream, snd_pcm_status_t *status);
+int snd_pcm_prepare(snd_pcm_substream_t *substream);
+int snd_pcm_start(snd_pcm_substream_t *substream);
+int snd_pcm_stop(snd_pcm_substream_t *substream, int status);
+#ifdef CONFIG_PM
+int snd_pcm_suspend(snd_pcm_substream_t *substream);
+int snd_pcm_suspend_all(snd_pcm_t *pcm);
+#endif
+int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg);
+int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg);
+int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg);
+int snd_pcm_open(struct inode *inode, struct file *file);
+int snd_pcm_release(struct inode *inode, struct file *file);
+unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait);
+unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait);
+int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, snd_pcm_substream_t **rsubstream);
+void snd_pcm_release_substream(snd_pcm_substream_t *substream);
+void snd_pcm_vma_notify_data(void *client, void *data);
+int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, struct vm_area_struct *area);
+
+#if BITS_PER_LONG >= 64
+
+static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
+{
+	*rem = *n % div;
+	*n /= div;
+}
+
+#elif defined(i386)
+
+static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
+{
+	u_int32_t low, high;
+	low = *n & 0xffffffff;
+	high = *n >> 32;
+	if (high) {
+		u_int32_t high1 = high % div;
+		high /= div;
+		asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1));
+		*n = (u_int64_t)high << 32 | low;
+	} else {
+		*n = low / div;
+		*rem = low % div;
+	}
+}
+#else
+
+static inline void divl(u_int32_t high, u_int32_t low,
+			u_int32_t div,
+			u_int32_t *q, u_int32_t *r)
+{
+	u_int64_t n = (u_int64_t)high << 32 | low;
+	u_int64_t d = (u_int64_t)div << 31;
+	u_int32_t q1 = 0;
+	int c = 32;
+	while (n > 0xffffffffU) {
+		q1 <<= 1;
+		if (n > d) {
+			n -= d;
+			q1 |= 1;
+		}
+		d >>= 1;
+		c--;
+	}
+	q1 <<= c;
+	if (n) {
+		low = n;
+		*q = q1 | (low / div);
+		*r = low % div;
+	} else {
+		*r = 0;
+		*q = q1;
+	}
+	return;
+}
+
+static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
+{
+	u_int32_t low, high;
+	low = *n & 0xffffffff;
+	high = *n >> 32;
+	if (high) {
+		u_int32_t high1 = high % div;
+		u_int32_t low1 = low;
+		high /= div;
+		divl(high1, low1, div, &low, rem);
+		*n = (u_int64_t)high << 32 | low;
+	} else {
+		*n = low / div;
+		*rem = low % div;
+	}
+}
+#endif
+
+/*
+ *  PCM library
+ */
+
+static inline int snd_pcm_running(snd_pcm_substream_t *substream)
+{
+	return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
+		(substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+		 substream->stream == SNDRV_PCM_STREAM_PLAYBACK));
+}
+
+static inline ssize_t bytes_to_samples(snd_pcm_runtime_t *runtime, ssize_t size)
+{
+	return size * 8 / runtime->sample_bits;
+}
+
+static inline snd_pcm_sframes_t bytes_to_frames(snd_pcm_runtime_t *runtime, ssize_t size)
+{
+	return size * 8 / runtime->frame_bits;
+}
+
+static inline ssize_t samples_to_bytes(snd_pcm_runtime_t *runtime, ssize_t size)
+{
+	return size * runtime->sample_bits / 8;
+}
+
+static inline ssize_t frames_to_bytes(snd_pcm_runtime_t *runtime, snd_pcm_sframes_t size)
+{
+	return size * runtime->frame_bits / 8;
+}
+
+static inline int frame_aligned(snd_pcm_runtime_t *runtime, ssize_t bytes)
+{
+	return bytes % runtime->byte_align == 0;
+}
+
+static inline size_t snd_pcm_lib_buffer_bytes(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return frames_to_bytes(runtime, runtime->buffer_size);
+}
+
+static inline size_t snd_pcm_lib_period_bytes(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return frames_to_bytes(runtime, runtime->period_size);
+}
+
+/*
+ *  result is: 0 ... (boundary - 1)
+ */
+static inline snd_pcm_uframes_t snd_pcm_playback_avail(snd_pcm_runtime_t *runtime)
+{
+	snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr;
+	if (avail < 0)
+		avail += runtime->boundary;
+	else if ((snd_pcm_uframes_t) avail >= runtime->boundary)
+		avail -= runtime->boundary;
+	return avail;
+}
+
+/*
+ *  result is: 0 ... (boundary - 1)
+ */
+static inline snd_pcm_uframes_t snd_pcm_capture_avail(snd_pcm_runtime_t *runtime)
+{
+	snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr;
+	if (avail < 0)
+		avail += runtime->boundary;
+	return avail;
+}
+
+static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(snd_pcm_runtime_t *runtime)
+{
+	return runtime->buffer_size - snd_pcm_playback_avail(runtime);
+}
+
+static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(snd_pcm_runtime_t *runtime)
+{
+	return runtime->buffer_size - snd_pcm_capture_avail(runtime);
+}
+
+static inline void snd_pcm_trigger_done(snd_pcm_substream_t *substream, 
+					snd_pcm_substream_t *master)
+{
+	substream->runtime->trigger_master = master;
+}
+
+static inline int hw_is_mask(int var)
+{
+	return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK &&
+		var <= SNDRV_PCM_HW_PARAM_LAST_MASK;
+}
+
+static inline int hw_is_interval(int var)
+{
+	return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL &&
+		var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL;
+}
+
+typedef unsigned int snd_mask_t;
+#define SND_MASK_MAX 32
+
+static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
+				     snd_pcm_hw_param_t var)
+{
+	return (snd_mask_t*)&params->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
+					     snd_pcm_hw_param_t var)
+{
+	return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+static inline const snd_mask_t *hw_param_mask_c(const snd_pcm_hw_params_t *params,
+					     snd_pcm_hw_param_t var)
+{
+	return (const snd_mask_t *)hw_param_mask((snd_pcm_hw_params_t*) params, var);
+}
+
+static inline const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params,
+						     snd_pcm_hw_param_t var)
+{
+	return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var);
+}
+
+#define params_access(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) - 1)
+#define params_format(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) - 1)
+#define params_subformat(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) - 1)
+#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min
+#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min
+#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min
+#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8)
+#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min
+#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min
+#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min
+#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min
+
+
+int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v);
+void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c);
+void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c);
+void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, 
+			  unsigned int k, snd_interval_t *c);
+void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k,
+			  const snd_interval_t *b, snd_interval_t *c);
+int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask);
+int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step);
+int snd_interval_ratnum(snd_interval_t *i,
+			unsigned int rats_count, ratnum_t *rats,
+			unsigned int *nump, unsigned int *denp);
+int snd_interval_ratden(snd_interval_t *i,
+			unsigned int rats_count, ratden_t *rats,
+			unsigned int *nump, unsigned int *denp);
+
+void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params);
+void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var);
+int snd_pcm_hw_param_min(snd_pcm_substream_t *substream, 
+			 snd_pcm_hw_params_t *params,
+			 snd_pcm_hw_param_t var,
+			 unsigned int val, int *dir);
+int snd_pcm_hw_param_max(snd_pcm_substream_t *substream, 
+			 snd_pcm_hw_params_t *params,
+			 snd_pcm_hw_param_t var,
+			 unsigned int val, int *dir);
+int snd_pcm_hw_param_setinteger(snd_pcm_substream_t *substream, 
+				snd_pcm_hw_params_t *params,
+				snd_pcm_hw_param_t var);
+int snd_pcm_hw_param_first(snd_pcm_substream_t *substream, 
+			   snd_pcm_hw_params_t *params,
+			   snd_pcm_hw_param_t var, int *dir);
+int snd_pcm_hw_param_last(snd_pcm_substream_t *substream, 
+			  snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, int *dir);
+int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, 
+			  snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, 
+			  unsigned int val, int *dir);
+int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
+
+int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
+
+int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream);
+int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream);
+
+int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+			       unsigned int mask);
+int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+				 unsigned int min, unsigned int max);
+int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var);
+int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, 
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var,
+			       snd_pcm_hw_constraint_list_t *l);
+int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, 
+				  unsigned int cond,
+				  snd_pcm_hw_param_t var,
+				  snd_pcm_hw_constraint_ratnums_t *r);
+int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, 
+				  unsigned int cond,
+				  snd_pcm_hw_param_t var,
+				  snd_pcm_hw_constraint_ratdens_t *r);
+int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, 
+				 unsigned int cond,
+				 unsigned int width,
+				 unsigned int msbits);
+int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime,
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var,
+			       unsigned long step);
+int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime,
+			unsigned int cond,
+			int var,
+			snd_pcm_hw_rule_func_t func, void *private,
+			int dep, ...);
+
+int snd_pcm_format_signed(snd_pcm_format_t format);
+int snd_pcm_format_unsigned(snd_pcm_format_t format);
+int snd_pcm_format_linear(snd_pcm_format_t format);
+int snd_pcm_format_little_endian(snd_pcm_format_t format);
+int snd_pcm_format_big_endian(snd_pcm_format_t format);
+int snd_pcm_format_width(snd_pcm_format_t format);			/* in bits */
+int snd_pcm_format_physical_width(snd_pcm_format_t format);		/* in bits */
+u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format);
+int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames);
+snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian);
+ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples);
+ 
+void snd_pcm_set_ops(snd_pcm_t * pcm, int direction, snd_pcm_ops_t *ops);
+void snd_pcm_set_sync(snd_pcm_substream_t * substream);
+int snd_pcm_lib_interleave_len(snd_pcm_substream_t *substream);
+int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream,
+		      unsigned int cmd, void *arg);                      
+int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream);
+int snd_pcm_playback_xrun_check(snd_pcm_substream_t *substream);
+int snd_pcm_capture_xrun_check(snd_pcm_substream_t *substream);
+int snd_pcm_playback_xrun_asap(snd_pcm_substream_t *substream);
+int snd_pcm_capture_xrun_asap(snd_pcm_substream_t *substream);
+void snd_pcm_playback_silence(snd_pcm_substream_t *substream);
+int snd_pcm_playback_ready(snd_pcm_substream_t *substream);
+int snd_pcm_capture_ready(snd_pcm_substream_t *substream);
+long snd_pcm_playback_ready_jiffies(snd_pcm_substream_t *substream);
+long snd_pcm_capture_ready_jiffies(snd_pcm_substream_t *substream);
+int snd_pcm_playback_data(snd_pcm_substream_t *substream);
+int snd_pcm_playback_empty(snd_pcm_substream_t *substream);
+int snd_pcm_capture_empty(snd_pcm_substream_t *substream);
+void snd_pcm_tick_prepare(snd_pcm_substream_t *substream);
+void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks);
+void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream);
+void snd_pcm_period_elapsed(snd_pcm_substream_t *substream);
+snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream,
+				    const void *buf, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream,
+				   void *buf, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream,
+				     void **bufs, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream,
+				    void **bufs, snd_pcm_uframes_t frames);
+
+/*
+ *  Timer interface
+ */
+
+void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream);
+void snd_pcm_timer_init(snd_pcm_substream_t * substream);
+void snd_pcm_timer_done(snd_pcm_substream_t * substream);
+
+/*
+ *  Memory
+ */
+
+int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream);
+int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm);
+int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream,
+				  size_t size, size_t max,
+				  unsigned int flags);
+int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm,
+					  size_t size, size_t max,
+					  unsigned int flags);
+int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size);
+int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream);
+
+#ifdef CONFIG_ISA
+int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream,
+				      size_t size, size_t max);
+int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm,
+					      size_t size, size_t max);
+#endif
+#ifdef CONFIG_PCI
+int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci,
+				      snd_pcm_substream_t *substream,
+				      size_t size, size_t max);
+int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci,
+					      snd_pcm_t *pcm,
+					      size_t size,
+					      size_t max);
+#endif
+
+static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
+{
+	*max = dma < 4 ? 64 * 1024 : 128 * 1024;
+}
+
+/*
+ *  Misc
+ */
+
+#define SNDRV_PCM_DEFAULT_CON_SPDIF	(IEC958_AES0_CON_EMPHASIS_NONE|\
+					 (IEC958_AES1_CON_ORIGINAL<<8)|\
+					 (IEC958_AES1_CON_PCM_CODER<<8)|\
+					 (IEC958_AES3_CON_FS_48000<<24))
+
+#endif /* __SOUND_PCM_H */
diff -Nru linux/include/sound/pcm_oss.h linux-2.4.19-pre5-mjc/include/sound/pcm_oss.h
--- linux/include/sound/pcm_oss.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/pcm_oss.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,82 @@
+#ifndef __SOUND_PCM_OSS_H
+#define __SOUND_PCM_OSS_H
+
+/*
+ *  Digital Audio (PCM) - OSS compatibility abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+typedef struct _snd_pcm_plugin snd_pcm_plugin_t;
+typedef struct _snd_pcm_oss_setup snd_pcm_oss_setup_t;
+
+struct _snd_pcm_oss_setup {
+	char *task_name;
+	unsigned int disable:1,
+		     direct:1,
+		     block:1,
+		     nonblock:1;
+	unsigned int periods;
+	unsigned int period_size;
+	snd_pcm_oss_setup_t *next;
+};
+
+typedef struct _snd_pcm_oss_runtime {
+	int params: 1,				/* format/parameter change */
+            prepare: 1,				/* need to prepare the operation */
+            trigger: 1,				/* trigger flag */
+            sync_trigger: 1;			/* sync trigger flag */
+	int rate;				/* requested rate */
+	int format;				/* requested OSS format */
+	unsigned int channels;			/* requested channels */
+	unsigned int fragshift;
+	unsigned int maxfrags;
+	unsigned int subdivision;		/* requested subdivision */
+	size_t period_bytes;			/* requested period size */
+	unsigned int periods;
+	size_t buffer_bytes;			/* requested period size */
+	size_t bytes;				/* total # bytes processed */
+	size_t mmap_bytes;
+	char *buffer;				/* vmallocated period */
+	size_t buffer_used;			/* used length from buffer */
+	snd_pcm_plugin_t *plugin_first;
+	snd_pcm_plugin_t *plugin_last;
+	unsigned int prev_hw_ptr_interrupt;
+} snd_pcm_oss_runtime_t;
+
+typedef struct _snd_pcm_oss_file {
+	snd_pcm_substream_t *streams[2];
+} snd_pcm_oss_file_t;
+
+typedef struct _snd_pcm_oss_substream {
+	int oss: 1;				/* oss mode */
+	snd_pcm_oss_setup_t *setup;		/* active setup */
+	snd_pcm_oss_file_t *file;
+} snd_pcm_oss_substream_t;
+
+typedef struct _snd_pcm_oss_stream {
+	snd_pcm_oss_setup_t *setup_list;	/* setup list */
+        struct semaphore setup_mutex;
+	snd_info_entry_t *proc_entry;
+} snd_pcm_oss_stream_t;
+
+typedef struct _snd_pcm_oss {
+	int reg;
+} snd_pcm_oss_t;
+
+#endif /* __SOUND_PCM_OSS_H */
diff -Nru linux/include/sound/pcm_params.h linux-2.4.19-pre5-mjc/include/sound/pcm_params.h
--- linux/include/sound/pcm_params.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/pcm_params.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,334 @@
+#ifndef __SOUND_PCM_PARAMS_H
+#define __SOUND_PCM_PARAMS_H
+
+/*
+ *  PCM params helpers
+ *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+
+extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, const snd_mask_t *val);
+extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params,
+					       snd_pcm_hw_param_t var, int *dir);
+extern unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params,
+					       snd_pcm_hw_param_t var, int *dir);
+extern int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, unsigned int val, int dir);
+extern int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params,
+					snd_pcm_hw_param_t var);
+extern int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, unsigned int val, int dir);
+
+/* To share the same code we have  alsa-lib */
+#define snd_mask_bits(mask) (*(mask))
+#define INLINE static inline
+#define assert(a)
+
+INLINE unsigned int ld2(u_int32_t v)
+{
+        unsigned r = 0;
+
+        if (v >= 0x10000) {
+                v >>= 16;
+                r += 16;
+        }
+        if (v >= 0x100) {
+                v >>= 8;
+                r += 8;
+        }
+        if (v >= 0x10) {
+                v >>= 4;
+                r += 4;
+        }
+        if (v >= 4) {
+                v >>= 2;
+                r += 2;
+        }
+        if (v >= 2)
+                r++;
+        return r;
+}
+
+INLINE size_t snd_mask_sizeof(void)
+{
+	return sizeof(snd_mask_t);
+}
+
+INLINE void snd_mask_none(snd_mask_t *mask)
+{
+	snd_mask_bits(mask) = 0;
+}
+
+INLINE void snd_mask_any(snd_mask_t *mask)
+{
+	snd_mask_bits(mask) = ~0U;
+}
+
+INLINE void snd_mask_load(snd_mask_t *mask, unsigned int msk)
+{
+	snd_mask_bits(mask) = msk;
+}
+
+INLINE int snd_mask_empty(const snd_mask_t *mask)
+{
+	return snd_mask_bits(mask) == 0;
+}
+
+INLINE unsigned int snd_mask_min(const snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	return ffs(snd_mask_bits(mask)) - 1;
+}
+
+INLINE unsigned int snd_mask_max(const snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	return ld2(snd_mask_bits(mask));
+}
+
+INLINE void snd_mask_set(snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SND_MASK_MAX);
+	snd_mask_bits(mask) |= (1U << val);
+}
+
+INLINE void snd_mask_reset(snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SND_MASK_MAX);
+	snd_mask_bits(mask) &= ~(1U << val);
+}
+
+INLINE void snd_mask_set_range(snd_mask_t *mask, unsigned int from, unsigned int to)
+{
+	assert(to <= SND_MASK_MAX && from <= to);
+	snd_mask_bits(mask) |= ((1U << (from - to + 1)) - 1) << from;
+}
+
+INLINE void snd_mask_reset_range(snd_mask_t *mask, unsigned int from, unsigned int to)
+{
+	assert(to <= SND_MASK_MAX && from <= to);
+	snd_mask_bits(mask) &= ~(((1U << (from - to + 1)) - 1) << from);
+}
+
+INLINE void snd_mask_leave(snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SND_MASK_MAX);
+	snd_mask_bits(mask) &= 1U << val;
+}
+
+INLINE void snd_mask_intersect(snd_mask_t *mask, const snd_mask_t *v)
+{
+	snd_mask_bits(mask) &= snd_mask_bits(v);
+}
+
+INLINE int snd_mask_eq(const snd_mask_t *mask, const snd_mask_t *v)
+{
+	return snd_mask_bits(mask) == snd_mask_bits(v);
+}
+
+INLINE void snd_mask_copy(snd_mask_t *mask, const snd_mask_t *v)
+{
+	snd_mask_bits(mask) = snd_mask_bits(v);
+}
+
+INLINE int snd_mask_test(const snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SND_MASK_MAX);
+	return snd_mask_bits(mask) & (1U << val);
+}
+
+INLINE int snd_mask_single(const snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	return !(snd_mask_bits(mask) & (snd_mask_bits(mask) - 1));
+}
+
+INLINE int snd_mask_refine(snd_mask_t *mask, const snd_mask_t *v)
+{
+	snd_mask_t old;
+	assert(!snd_mask_empty(mask));
+	snd_mask_copy(&old, mask);
+	snd_mask_intersect(mask, v);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return !snd_mask_eq(mask, &old);
+}
+
+INLINE int snd_mask_refine_first(snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_single(mask))
+		return 0;
+	snd_mask_leave(mask, snd_mask_min(mask));
+	return 1;
+}
+
+INLINE int snd_mask_refine_last(snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_single(mask))
+		return 0;
+	snd_mask_leave(mask, snd_mask_max(mask));
+	return 1;
+}
+
+INLINE int snd_mask_refine_min(snd_mask_t *mask, unsigned int val)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_min(mask) >= val)
+		return 0;
+	snd_mask_reset_range(mask, 0, val - 1);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return 1;
+}
+
+INLINE int snd_mask_refine_max(snd_mask_t *mask, unsigned int val)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_max(mask) <= val)
+		return 0;
+	snd_mask_reset_range(mask, val + 1, SND_MASK_MAX);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return 1;
+}
+
+INLINE int snd_mask_refine_set(snd_mask_t *mask, unsigned int val)
+{
+	int changed;
+	assert(!snd_mask_empty(mask));
+	changed = !snd_mask_single(mask);
+	snd_mask_leave(mask, val);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return changed;
+}
+
+INLINE int snd_mask_value(const snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	return snd_mask_min(mask);
+}
+
+INLINE void snd_interval_any(snd_interval_t *i)
+{
+	i->min = 0;
+	i->openmin = 0;
+	i->max = UINT_MAX;
+	i->openmax = 0;
+	i->integer = 0;
+	i->empty = 0;
+}
+
+INLINE void snd_interval_none(snd_interval_t *i)
+{
+	i->empty = 1;
+}
+
+INLINE int snd_interval_checkempty(const snd_interval_t *i)
+{
+	return (i->min > i->max ||
+		(i->min == i->max && (i->openmin || i->openmax)));
+}
+
+INLINE int snd_interval_empty(const snd_interval_t *i)
+{
+	return i->empty;
+}
+
+INLINE int snd_interval_single(const snd_interval_t *i)
+{
+	assert(!snd_interval_empty(i));
+	return (i->min == i->max || 
+		(i->min + 1 == i->max && i->openmax));
+}
+
+INLINE int snd_interval_value(const snd_interval_t *i)
+{
+	assert(snd_interval_single(i));
+	return i->min;
+}
+
+INLINE int snd_interval_min(const snd_interval_t *i)
+{
+	assert(!snd_interval_empty(i));
+	return i->min;
+}
+
+INLINE int snd_interval_max(const snd_interval_t *i)
+{
+	unsigned int v;
+	assert(!snd_interval_empty(i));
+	v = i->max;
+	if (i->openmax)
+		v--;
+	return v;
+}
+
+INLINE int snd_interval_test(const snd_interval_t *i, unsigned int val)
+{
+	return !((i->min > val || (i->min == val && i->openmin) ||
+		  i->max < val || (i->max == val && i->openmax)));
+}
+
+INLINE void snd_interval_copy(snd_interval_t *d, const snd_interval_t *s)
+{
+	*d = *s;
+}
+
+INLINE int snd_interval_setinteger(snd_interval_t *i)
+{
+	if (i->integer)
+		return 0;
+	if (i->openmin && i->openmax && i->min == i->max)
+		return -EINVAL;
+	i->integer = 1;
+	return 1;
+}
+
+INLINE int snd_interval_eq(const snd_interval_t *i1, const snd_interval_t *i2)
+{
+	if (i1->empty)
+		return i2->empty;
+	if (i2->empty)
+		return i1->empty;
+	return i1->min == i2->min && i1->openmin == i2->openmin &&
+		i1->max == i2->max && i1->openmax == i2->openmax;
+}
+
+static inline unsigned int add(unsigned int a, unsigned int b)
+{
+	if (a >= UINT_MAX - b)
+		return UINT_MAX;
+	return a + b;
+}
+
+static inline unsigned int sub(unsigned int a, unsigned int b)
+{
+	if (a > b)
+		return a - b;
+	return 0;
+}
+
+#undef INLINE
+#undef assert
+
+#endif /* __SOUND_PCM_PARAMS_H */
+
diff -Nru linux/include/sound/rawmidi.h linux-2.4.19-pre5-mjc/include/sound/rawmidi.h
--- linux/include/sound/rawmidi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/rawmidi.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,181 @@
+#ifndef __SOUND_RAWMIDI_H
+#define __SOUND_RAWMIDI_H
+
+/*
+ *  Abstract layer for MIDI v1.0 stream
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/asound.h>
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#include "seq_device.h"
+#endif
+
+/*
+ *  Raw MIDI interface
+ */
+
+typedef enum sndrv_rawmidi_stream snd_rawmidi_stream_t;
+typedef struct sndrv_rawmidi_info snd_rawmidi_info_t;
+typedef struct sndrv_rawmidi_params snd_rawmidi_params_t;
+typedef struct sndrv_rawmidi_status snd_rawmidi_status_t;
+
+#define SNDRV_RAWMIDI_DEVICES	4
+
+#define SNDRV_RAWMIDI_LFLG_OUTPUT         (1<<0)
+#define SNDRV_RAWMIDI_LFLG_INPUT          (1<<1)
+#define SNDRV_RAWMIDI_LFLG_OPEN           (3<<0)
+#define SNDRV_RAWMIDI_LFLG_APPEND         (1<<2)
+
+typedef struct _snd_rawmidi_runtime snd_rawmidi_runtime_t;
+typedef struct _snd_rawmidi_substream snd_rawmidi_substream_t;
+typedef struct _snd_rawmidi_str snd_rawmidi_str_t;
+
+typedef struct _snd_rawmidi_ops {
+	int (*open) (snd_rawmidi_substream_t * substream);
+	int (*close) (snd_rawmidi_substream_t * substream);
+	void (*trigger) (snd_rawmidi_substream_t * substream, int up);
+	void (*drain) (snd_rawmidi_substream_t * substream);
+} snd_rawmidi_ops_t;
+
+typedef struct _snd_rawmidi_global_ops {
+	int (*dev_register) (snd_rawmidi_t * rmidi);
+	int (*dev_unregister) (snd_rawmidi_t * rmidi);
+} snd_rawmidi_global_ops_t;
+
+struct _snd_rawmidi_runtime {
+	unsigned int trigger: 1, /* transfer is running */
+		     drain: 1,	/* drain stage */
+		     oss: 1;	/* OSS compatible mode */
+	/* midi stream buffer */
+	unsigned char *buffer;	/* buffer for MIDI data */
+	size_t buffer_size;	/* size of buffer */
+	size_t appl_ptr;	/* application pointer */
+	size_t hw_ptr;		/* hardware pointer */
+	size_t avail_min;	/* min avail for wakeup */
+	size_t avail;		/* max used buffer for wakeup */
+	size_t xruns;		/* over/underruns counter */
+	/* misc */
+	spinlock_t lock;
+	wait_queue_head_t sleep;
+	/* event handler (room [output] or new bytes [input]) */
+	void (*event)(snd_rawmidi_substream_t *substream);
+	/* private data */
+	void *private_data;
+	void (*private_free)(snd_rawmidi_substream_t *substream);
+};
+
+struct _snd_rawmidi_substream {
+	struct list_head list;		/* list of all substream for given stream */
+	int stream;			/* direction */
+	int number;			/* substream number */
+	unsigned int opened: 1,		/* open flag */
+		     append: 1,		/* append flag (merge more streams) */
+		     active_sensing: 1; /* send active sensing when close */
+	int use_count;			/* use counter (for output) */
+	size_t bytes;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_str_t *pstr;
+	char name[32];
+	snd_rawmidi_runtime_t *runtime;
+	/* hardware layer */
+	snd_rawmidi_ops_t *ops;
+};
+
+typedef struct _snd_rawmidi_file {
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *input;
+	snd_rawmidi_substream_t *output;
+} snd_rawmidi_file_t;
+
+struct _snd_rawmidi_str {
+	unsigned int substream_count;
+	unsigned int substream_opened;
+	struct list_head substreams;
+};
+
+struct _snd_rawmidi {
+	snd_card_t *card;
+
+	unsigned int device;		/* device number */
+	unsigned int info_flags;	/* SNDRV_RAWMIDI_INFO_XXXX */
+	char id[64];
+	char name[80];
+
+#ifdef CONFIG_SND_OSSEMUL
+	int ossreg;
+#endif
+
+	snd_rawmidi_global_ops_t *ops;
+
+	snd_rawmidi_str_t streams[2];
+
+	void *private_data;
+	void (*private_free) (snd_rawmidi_t *rmidi);
+
+	struct semaphore open_mutex;
+	wait_queue_head_t open_wait;
+
+	snd_info_entry_t *dev;
+	snd_info_entry_t *proc_entry;
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	snd_seq_device_t *seq_dev;
+#endif
+};
+
+/* main rawmidi functions */
+
+int snd_rawmidi_new(snd_card_t * card, char *id, int device,
+		    int output_count, int input_count,
+		    snd_rawmidi_t ** rmidi);
+void snd_rawmidi_set_ops(snd_rawmidi_t * rmidi, int stream, snd_rawmidi_ops_t * ops);
+
+/* control functions */
+
+int snd_rawmidi_control_ioctl(snd_card_t * card,
+			      snd_ctl_file_t * control,
+			      unsigned int cmd,
+			      unsigned long arg);
+
+/* callbacks */
+
+void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream);
+int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count);
+void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream);
+int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream);
+int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count);
+int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count);
+int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count);
+
+/* main midi functions */
+
+int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info);
+int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, int mode, snd_rawmidi_file_t * rfile);
+int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile);
+int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params);
+int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params);
+int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream);
+int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream);
+int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream);
+long snd_rawmidi_kernel_read(snd_rawmidi_substream_t * substream, unsigned char *buf, long count);
+long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count);
+
+#endif /* __SOUND_RAWMIDI_H */
diff -Nru linux/include/sound/sb.h linux-2.4.19-pre5-mjc/include/sound/sb.h
--- linux/include/sound/sb.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/sb.h	Mon Apr  8 22:31:21 2002
@@ -0,0 +1,289 @@
+#ifndef __SOUND_SB_H
+#define __SOUND_SB_H
+
+/*
+ *  Header file for SoundBlaster cards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include <asm/io.h>
+
+enum sb_hw_type {
+	SB_HW_AUTO,
+	SB_HW_10,
+	SB_HW_20,
+	SB_HW_201,
+	SB_HW_PRO,
+	SB_HW_16,
+	SB_HW_16CSP,		/* SB16 with CSP chip */
+	SB_HW_ALS100,		/* Avance Logic ALS100 chip */
+	SB_HW_ALS4000,		/* Avance Logic ALS4000 chip */
+};
+
+#define SB_OPEN_PCM		0x01
+#define SB_OPEN_MIDI_INPUT	0x02
+#define SB_OPEN_MIDI_OUTPUT	0x04
+#define SB_OPEN_MIDI_TRIGGER	0x08
+
+#define SB_MODE_HALT		0x00
+#define SB_MODE_PLAYBACK_8	0x01
+#define SB_MODE_PLAYBACK_16	0x02
+#define SB_MODE_PLAYBACK	(SB_MODE_PLAYBACK_8 | SB_MODE_PLAYBACK_16)
+#define SB_MODE_CAPTURE_8	0x04
+#define SB_MODE_CAPTURE_16	0x08
+#define SB_MODE_CAPTURE		(SB_MODE_CAPTURE_8 | SB_MODE_CAPTURE_16)
+
+#define SB_RATE_LOCK_PLAYBACK	0x10
+#define SB_RATE_LOCK_CAPTURE	0x20
+#define SB_RATE_LOCK		(SB_RATE_LOCK_PLAYBACK | SB_RATE_LOCK_CAPTURE)
+
+#define SB_MPU_INPUT		1
+
+struct _snd_sb {
+	unsigned long port;		/* base port of DSP chip */
+	struct resource *res_port;
+	unsigned long alt_port;		/* alternate port (ALS4000) */
+	struct resource *res_alt_port;
+	unsigned long mpu_port;		/* MPU port for SB DSP 4.0+ */
+	int irq;			/* IRQ number of DSP chip */
+	int dma8;			/* 8-bit DMA */
+	int dma16;			/* 16-bit DMA */
+	unsigned short version;		/* version of DSP chip */
+	enum sb_hw_type hardware;	/* see to SB_HW_XXXX */
+
+	struct pci_dev *pci;		/* ALS4000 */
+
+	unsigned int open;		/* see to SB_OPEN_XXXX for sb8 */
+					/* also SNDRV_SB_CSP_MODE_XXX for sb16_csp */
+	unsigned int mode;		/* current mode of stream */
+	unsigned int force_mode16;	/* force 16-bit mode of streams */
+	unsigned int locked_rate;	/* sb16 duplex */
+	unsigned int playback_format;
+	unsigned int capture_format;
+	struct timer_list midi_timer;
+	unsigned int p_dma_size;
+	unsigned int p_period_size;
+	unsigned int c_dma_size;
+	unsigned int c_period_size;
+
+	spinlock_t mixer_lock;
+
+	char name[32];
+
+	void *csp; /* used only when CONFIG_SND_SB16_CSP is set */
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *midi_substream_input;
+	snd_rawmidi_substream_t *midi_substream_output;
+
+	spinlock_t reg_lock;
+	spinlock_t open_lock;
+	spinlock_t midi_input_lock;
+
+	snd_info_entry_t *proc_entry;
+};
+
+typedef struct _snd_sb sb_t;
+
+/* I/O ports */
+
+#define SBP(chip, x)		((chip)->port + s_b_SB_##x)
+#define SBP1(port, x)		((port) + s_b_SB_##x)
+
+#define s_b_SB_RESET		0x6
+#define s_b_SB_READ		0xa
+#define s_b_SB_WRITE		0xc
+#define s_b_SB_COMMAND		0xc
+#define s_b_SB_STATUS		0xc
+#define s_b_SB_DATA_AVAIL	0xe
+#define s_b_SB_DATA_AVAIL_16 	0xf
+#define s_b_SB_MIXER_ADDR	0x4
+#define s_b_SB_MIXER_DATA	0x5
+#define s_b_SB_OPL3_LEFT	0x0
+#define s_b_SB_OPL3_RIGHT	0x2
+#define s_b_SB_OPL3_BOTH	0x8
+
+#define SB_DSP_OUTPUT		0x14
+#define SB_DSP_INPUT		0x24
+#define SB_DSP_BLOCK_SIZE	0x48
+#define SB_DSP_HI_OUTPUT	0x91
+#define SB_DSP_HI_INPUT		0x99
+#define SB_DSP_LO_OUTPUT_AUTO	0x1c
+#define SB_DSP_LO_INPUT_AUTO	0x2c
+#define SB_DSP_HI_OUTPUT_AUTO	0x90
+#define SB_DSP_HI_INPUT_AUTO	0x98
+#define SB_DSP_IMMED_INT	0xf2
+#define SB_DSP_GET_VERSION	0xe1
+#define SB_DSP_SPEAKER_ON	0xd1
+#define SB_DSP_SPEAKER_OFF	0xd3
+#define SB_DSP_DMA8_OFF		0xd0
+#define SB_DSP_DMA8_ON		0xd4
+#define SB_DSP_DMA8_EXIT	0xda
+#define SB_DSP_DMA16_OFF	0xd5
+#define SB_DSP_DMA16_ON		0xd6
+#define SB_DSP_DMA16_EXIT	0xd9
+#define SB_DSP_SAMPLE_RATE	0x40
+#define SB_DSP_SAMPLE_RATE_OUT	0x41
+#define SB_DSP_SAMPLE_RATE_IN	0x42
+#define SB_DSP_MONO_8BIT	0xa0
+#define SB_DSP_MONO_16BIT	0xa4
+#define SB_DSP_STEREO_8BIT	0xa8
+#define SB_DSP_STEREO_16BIT	0xac
+
+#define SB_DSP_MIDI_INPUT_IRQ	0x31
+#define SB_DSP_MIDI_OUTPUT	0x38
+
+#define SB_DSP4_OUT8_AI		0xc6
+#define SB_DSP4_IN8_AI		0xce
+#define SB_DSP4_OUT16_AI	0xb6
+#define SB_DSP4_IN16_AI		0xbe
+#define SB_DSP4_MODE_UNS_MONO	0x00
+#define SB_DSP4_MODE_SIGN_MONO	0x10
+#define SB_DSP4_MODE_UNS_STEREO	0x20
+#define SB_DSP4_MODE_SIGN_STEREO 0x30
+
+#define SB_DSP4_OUTPUT		0x3c
+#define SB_DSP4_INPUT_LEFT	0x3d
+#define SB_DSP4_INPUT_RIGHT	0x3e
+
+/* registers for SB 2.0 mixer */
+#define SB_DSP20_MASTER_DEV	0x02
+#define SB_DSP20_PCM_DEV	0x0A
+#define SB_DSP20_CD_DEV		0x08
+#define SB_DSP20_FM_DEV		0x06
+
+/* registers for SB PRO mixer */
+#define SB_DSP_MASTER_DEV	0x22
+#define SB_DSP_PCM_DEV		0x04
+#define SB_DSP_LINE_DEV		0x2e
+#define SB_DSP_CD_DEV		0x28
+#define SB_DSP_FM_DEV		0x26
+#define SB_DSP_MIC_DEV		0x0a
+#define SB_DSP_CAPTURE_SOURCE	0x0c
+#define SB_DSP_CAPTURE_FILT	0x0c
+#define SB_DSP_PLAYBACK_FILT	0x0e
+#define SB_DSP_STEREO_SW	0x0e
+
+#define SB_DSP_MIXS_MIC0	0x00	/* same as MIC */
+#define SB_DSP_MIXS_CD		0x01
+#define SB_DSP_MIXS_MIC		0x02
+#define SB_DSP_MIXS_LINE	0x03
+
+/* registers (only for left channel) for SB 16 mixer */
+#define SB_DSP4_MASTER_DEV	0x30
+#define SB_DSP4_BASS_DEV	0x46
+#define SB_DSP4_TREBLE_DEV	0x44
+#define SB_DSP4_SYNTH_DEV	0x34
+#define SB_DSP4_PCM_DEV		0x32
+#define SB_DSP4_SPEAKER_DEV	0x3b
+#define SB_DSP4_LINE_DEV	0x38
+#define SB_DSP4_MIC_DEV		0x3a
+#define SB_DSP4_OUTPUT_SW	0x3c
+#define SB_DSP4_CD_DEV		0x36
+#define SB_DSP4_IGAIN_DEV	0x3f
+#define SB_DSP4_OGAIN_DEV	0x41
+#define SB_DSP4_MIC_AGC		0x43
+
+/* additional registers for SB 16 mixer */
+#define SB_DSP4_IRQSETUP	0x80
+#define SB_DSP4_DMASETUP	0x81
+#define SB_DSP4_IRQSTATUS	0x82
+#define SB_DSP4_MPUSETUP	0x84
+
+#define SB_DSP4_3DSE		0x90
+
+/* IRQ setting bitmap */
+#define SB_IRQSETUP_IRQ9	0x01
+#define SB_IRQSETUP_IRQ5	0x02
+#define SB_IRQSETUP_IRQ7	0x04
+#define SB_IRQSETUP_IRQ10	0x08
+
+/* IRQ types */
+#define SB_IRQTYPE_8BIT		0x01
+#define SB_IRQTYPE_16BIT	0x02
+#define SB_IRQTYPE_MPUIN	0x04
+
+/* DMA setting bitmap */
+#define SB_DMASETUP_DMA0	0x01
+#define SB_DMASETUP_DMA1	0x02
+#define SB_DMASETUP_DMA3	0x08
+#define SB_DMASETUP_DMA5	0x20
+#define SB_DMASETUP_DMA6	0x40
+#define SB_DMASETUP_DMA7	0x80
+
+/*
+ *
+ */
+
+static inline void snd_sb_ack_8bit(sb_t *chip)
+{
+	inb(SBP(chip, DATA_AVAIL));
+}
+
+static inline void snd_sb_ack_16bit(sb_t *chip)
+{
+	inb(SBP(chip, DATA_AVAIL_16));
+}
+
+/* sb_common.c */
+int snd_sbdsp_command(sb_t *chip, unsigned char val);
+int snd_sbdsp_get_byte(sb_t *chip);
+int snd_sbdsp_reset(sb_t *chip);
+int snd_sbdsp_create(snd_card_t *card,
+		     unsigned long port,
+		     int irq,
+		     void (*irq_handler)(int, void *, struct pt_regs *),
+		     int dma8, int dma16,
+		     unsigned short hardware,
+		     sb_t **r_chip);
+/* sb_mixer.c */
+void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data);
+unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg);
+int snd_sbmixer_new(sb_t *chip);
+
+/* sb8_init.c */
+int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm);
+/* sb8.c */
+void snd_sb8dsp_interrupt(sb_t *chip);
+int snd_sb8_playback_open(snd_pcm_substream_t *substream);
+int snd_sb8_capture_open(snd_pcm_substream_t *substream);
+int snd_sb8_playback_close(snd_pcm_substream_t *substream);
+int snd_sb8_capture_close(snd_pcm_substream_t *substream);
+/* midi8.c */
+void snd_sb8dsp_midi_interrupt(sb_t *chip);
+int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi);
+
+/* sb16_init.c */
+int snd_sb16dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm);
+int snd_sb16dsp_configure(sb_t *chip);
+/* sb16.c */
+void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+int snd_sb16_playback_open(snd_pcm_substream_t *substream);
+int snd_sb16_capture_open(snd_pcm_substream_t *substream);
+int snd_sb16_playback_close(snd_pcm_substream_t *substream);
+int snd_sb16_capture_close(snd_pcm_substream_t *substream);
+
+#endif /* __SOUND_SB_H */
diff -Nru linux/include/sound/sb16_csp.h linux-2.4.19-pre5-mjc/include/sound/sb16_csp.h
--- linux/include/sound/sb16_csp.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/sb16_csp.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,168 @@
+#ifndef __SOUND_SB16_CSP_H
+#define __SOUND_SB16_CSP_H
+
+/*
+ *  Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
+ *                        Takashi Iwai <tiwai@suse.de>
+ *
+ *  SB16ASP/AWE32 CSP control
+ *
+ *   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
+ *
+ */
+
+/* CSP modes */
+#define SNDRV_SB_CSP_MODE_NONE		0x00
+#define SNDRV_SB_CSP_MODE_DSP_READ	0x01	/* Record from DSP */
+#define SNDRV_SB_CSP_MODE_DSP_WRITE	0x02	/* Play to DSP */
+#define SNDRV_SB_CSP_MODE_QSOUND		0x04	/* QSound */
+
+/* CSP load flags */
+#define SNDRV_SB_CSP_LOAD_FROMUSER	0x01
+#define SNDRV_SB_CSP_LOAD_INITBLOCK	0x02
+
+/* CSP sample width */
+#define SNDRV_SB_CSP_SAMPLE_8BIT		0x01
+#define SNDRV_SB_CSP_SAMPLE_16BIT		0x02
+
+/* CSP channels */
+#define SNDRV_SB_CSP_MONO			0x01
+#define SNDRV_SB_CSP_STEREO		0x02
+
+/* CSP rates */
+#define SNDRV_SB_CSP_RATE_8000		0x01
+#define SNDRV_SB_CSP_RATE_11025		0x02
+#define SNDRV_SB_CSP_RATE_22050		0x04
+#define SNDRV_SB_CSP_RATE_44100		0x08
+#define SNDRV_SB_CSP_RATE_ALL		0x0f
+
+/* CSP running state */
+#define SNDRV_SB_CSP_ST_IDLE		0x00
+#define SNDRV_SB_CSP_ST_LOADED		0x01
+#define SNDRV_SB_CSP_ST_RUNNING		0x02
+#define SNDRV_SB_CSP_ST_PAUSED		0x04
+#define SNDRV_SB_CSP_ST_AUTO		0x08
+#define SNDRV_SB_CSP_ST_QSOUND		0x10
+
+/* maximum QSound value (180 degrees right) */
+#define SNDRV_SB_CSP_QSOUND_MAX_RIGHT	0x20
+
+/* maximum microcode RIFF file size */
+#define SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE	0x3000
+
+/* microcode header */
+typedef struct snd_sb_csp_mc_header {
+	char codec_name[16];		/* id name of codec */
+	unsigned short func_req;	/* requested function */
+} snd_sb_csp_mc_header_t;
+
+/* microcode to be loaded */
+typedef struct snd_sb_csp_microcode {
+	snd_sb_csp_mc_header_t info;
+	unsigned char data[SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE];
+} snd_sb_csp_microcode_t;
+
+/* start CSP with sample_width in mono/stereo */
+typedef struct snd_sb_csp_start {
+	int sample_width;	/* sample width, look above */
+	int channels;		/* channels, look above */
+} snd_sb_csp_start_t;
+
+/* CSP information */
+typedef struct snd_sb_csp_info {
+	char codec_name[16];		/* id name of codec */
+	unsigned short func_nr;		/* function number */
+	unsigned int acc_format;	/* accepted PCM formats */
+	unsigned short acc_channels;	/* accepted channels */
+	unsigned short acc_width;	/* accepted sample width */
+	unsigned short acc_rates;	/* accepted sample rates */
+	unsigned short csp_mode;	/* CSP mode, see above */
+	unsigned short run_channels;	/* current channels  */
+	unsigned short run_width;	/* current sample width */
+	unsigned short version;		/* version id: 0x10 - 0x1f */
+	unsigned short state;		/* state bits */
+} snd_sb_csp_info_t;
+
+/* HWDEP controls */
+/* get CSP information */
+#define SNDRV_SB_CSP_IOCTL_INFO		_IOR('H', 0x10, snd_sb_csp_info_t)
+/* load microcode to CSP */
+#define SNDRV_SB_CSP_IOCTL_LOAD_CODE	_IOW('H', 0x11, snd_sb_csp_microcode_t)
+/* unload microcode from CSP */
+#define SNDRV_SB_CSP_IOCTL_UNLOAD_CODE	_IO('H', 0x12)
+/* start CSP */
+#define SNDRV_SB_CSP_IOCTL_START		_IOW('H', 0x13, snd_sb_csp_start_t)
+/* stop CSP */
+#define SNDRV_SB_CSP_IOCTL_STOP		_IO('H', 0x14)
+/* pause CSP and DMA transfer */
+#define SNDRV_SB_CSP_IOCTL_PAUSE		_IO('H', 0x15)
+/* restart CSP and DMA transfer */
+#define SNDRV_SB_CSP_IOCTL_RESTART	_IO('H', 0x16)
+
+#ifdef __KERNEL__
+#include "sb.h"
+#include "hwdep.h"
+
+typedef struct snd_sb_csp snd_sb_csp_t;
+
+/*
+ * CSP operators
+ */
+typedef struct {
+	int (*csp_use) (snd_sb_csp_t * p);
+	int (*csp_unuse) (snd_sb_csp_t * p);
+	int (*csp_autoload) (snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode);
+	int (*csp_start) (snd_sb_csp_t * p, int sample_width, int channels);
+	int (*csp_stop) (snd_sb_csp_t * p);
+	int (*csp_qsound_transfer) (snd_sb_csp_t * p);
+} snd_sb_csp_ops_t;
+
+/*
+ * CSP private data
+ */
+struct snd_sb_csp {
+	sb_t *chip;		/* SB16 DSP */
+	int used;		/* usage flag - exclusive */
+	char codec_name[16];	/* name of codec */
+	unsigned short func_nr;	/* function number */
+	unsigned int acc_format;	/* accepted PCM formats */
+	int acc_channels;	/* accepted channels */
+	int acc_width;		/* accepted sample width */
+	int acc_rates;		/* accepted sample rates */
+	int mode;		/* MODE */
+	int run_channels;	/* current CSP channels */
+	int run_width;		/* current sample width */
+	int version;		/* CSP version (0x10 - 0x1f) */
+	int running;		/* running state */
+
+	snd_sb_csp_ops_t ops;	/* operators */
+
+	spinlock_t q_lock;	/* locking */
+	int q_enabled;		/* enabled flag */
+	int qpos_left;		/* left position */
+	int qpos_right;		/* right position */
+	int qpos_changed;	/* position changed flag */
+
+	snd_kcontrol_t *qsound_switch;
+	snd_kcontrol_t *qsound_space;
+
+	struct semaphore access_mutex;	/* locking */
+	snd_info_entry_t *proc;	/* proc interface */
+};
+
+int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep);
+#endif
+
+#endif /* __SOUND_SB16_CSP */
diff -Nru linux/include/sound/seq_device.h linux-2.4.19-pre5-mjc/include/sound/seq_device.h
--- linux/include/sound/seq_device.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_device.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,88 @@
+#ifndef __SOUND_SEQ_DEVICE_H
+#define __SOUND_SEQ_DEVICE_H
+
+/*
+ *  ALSA sequencer device management
+ *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+typedef struct snd_seq_device snd_seq_device_t;
+typedef struct snd_seq_dev_ops snd_seq_dev_ops_t;
+
+/*
+ * registered device information
+ */
+
+#define ID_LEN	32
+
+/* status flag */
+#define SNDRV_SEQ_DEVICE_FREE		0
+#define SNDRV_SEQ_DEVICE_REGISTERED	1
+
+struct snd_seq_device {
+	/* device info */
+	snd_card_t *card;	/* sound card */
+	int device;		/* device number */
+	char id[ID_LEN];	/* driver id */
+	char name[80];		/* device name */
+	int argsize;		/* size of the argument */
+	void *driver_data;	/* private data for driver */
+	int status;		/* flag - read only */
+	void *private_data;	/* private data for the caller */
+	void (*private_free)(snd_seq_device_t *device);
+	struct list_head list;	/* link to next device */
+};
+
+
+/* driver operators
+ * init_device:
+ *	Initialize the device with given parameters.
+ *	Typically,
+ *		1. call snd_hwdep_new
+ *		2. allocate private data and initialize it
+ *		3. call snd_hwdep_register
+ *		4. store the instance to dev->driver_data pointer.
+ *		
+ * free_device:
+ *	Release the private data.
+ *	Typically, call snd_device_free(dev->card, dev->driver_data)
+ */
+struct snd_seq_dev_ops {
+	int (*init_device)(snd_seq_device_t *dev);
+	int (*free_device)(snd_seq_device_t *dev);
+};
+
+/*
+ * prototypes
+ */
+void snd_seq_device_load_drivers(void);
+int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, snd_seq_device_t **result);
+int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize);
+int snd_seq_device_unregister_driver(char *id);
+
+#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(snd_seq_device_t))
+
+
+/*
+ * id strings for generic devices
+ */
+#define SNDRV_SEQ_DEV_ID_MIDISYNTH	"seq-midi"
+#define SNDRV_SEQ_DEV_ID_OPL3		"opl3-synth"
+
+
+#endif /* __SOUND_SEQ_DEVICE_H */
diff -Nru linux/include/sound/seq_instr.h linux-2.4.19-pre5-mjc/include/sound/seq_instr.h
--- linux/include/sound/seq_instr.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_instr.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,112 @@
+#ifndef __SOUND_SEQ_INSTR_H
+#define __SOUND_SEQ_INSTR_H
+
+/*
+ *  Main kernel header file for the ALSA sequencer
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+#include "seq_kernel.h"
+
+/* Instrument cluster */
+typedef struct _snd_seq_kcluster {
+	snd_seq_instr_cluster_t cluster;
+	char name[32];
+	int priority;
+	struct _snd_seq_kcluster *next;
+} snd_seq_kcluster_t;
+
+/* return pointer to private data */
+#define KINSTR_DATA(kinstr)	(void *)(((char *)kinstr) + sizeof(snd_seq_kinstr_t))
+
+typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t;
+
+/* Instrument structure */
+typedef struct _snd_seq_kinstr {
+	snd_seq_instr_t instr;
+	char name[32];
+	int type;			/* instrument type */
+	int use;			/* use count */
+	int busy;			/* not useable */
+	int add_len;			/* additional length */
+	snd_seq_kinstr_ops_t *ops;	/* operations */
+	struct _snd_seq_kinstr *next;
+} snd_seq_kinstr_t;
+
+#define SNDRV_SEQ_INSTR_HASH_SIZE		32
+
+/* Instrument flags */
+#define SNDRV_SEQ_INSTR_FLG_DIRECT	(1<<0)	/* accept only direct events */
+
+/* List of all instruments */
+typedef struct {
+	snd_seq_kinstr_t *hash[SNDRV_SEQ_INSTR_HASH_SIZE];
+	int count;			/* count of all instruments */
+	
+	snd_seq_kcluster_t *chash[SNDRV_SEQ_INSTR_HASH_SIZE];
+	int ccount;			/* count of all clusters */
+
+	int owner;			/* current owner of the instrument list */
+	unsigned int flags;
+
+	spinlock_t lock;
+	spinlock_t ops_lock;
+	struct semaphore ops_mutex;
+	unsigned long ops_flags;
+} snd_seq_kinstr_list_t;
+
+#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE	0
+#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE	1
+
+struct snd_seq_kinstr_ops {
+	void *private_data;
+	long add_len;			/* additional length */
+	char *instr_type;
+	int (*info)(void *private_data, char *info_data, long len);
+	int (*put)(void *private_data, snd_seq_kinstr_t *kinstr,
+		   char *instr_data, long len, int atomic, int cmd);
+	int (*get)(void *private_data, snd_seq_kinstr_t *kinstr,
+		   char *instr_data, long len, int atomic, int cmd);
+	int (*get_size)(void *private_data, snd_seq_kinstr_t *kinstr, long *size);
+	int (*remove)(void *private_data, snd_seq_kinstr_t *kinstr, int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *kinstr, int what);
+	struct snd_seq_kinstr_ops *next;
+};
+
+
+/* instrument operations */
+snd_seq_kinstr_list_t *snd_seq_instr_list_new(void);
+void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list);
+int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list,
+				 snd_seq_instr_header_t *ifree,
+				 int client,
+				 int atomic);
+snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list,
+				     snd_seq_instr_t *instr,
+				     int exact,
+				     int follow_alias);
+void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list,
+			    snd_seq_kinstr_t *instr);
+int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops,
+			snd_seq_kinstr_list_t *list,
+			snd_seq_event_t *ev,
+			int client,
+			int atomic,
+			int hop);
+
+#endif /* __SOUND_SEQ_INSTR_H */
diff -Nru linux/include/sound/seq_kernel.h linux-2.4.19-pre5-mjc/include/sound/seq_kernel.h
--- linux/include/sound/seq_kernel.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_kernel.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,182 @@
+#ifndef __SOUND_SEQ_KERNEL_H
+#define __SOUND_SEQ_KERNEL_H
+
+/*
+ *  Main kernel header file for the ALSA sequencer
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#include <linux/time.h>
+#include "asequencer.h"
+
+typedef sndrv_seq_tick_time_t snd_seq_tick_time_t;
+typedef sndrv_seq_position_t snd_seq_position_t;
+typedef sndrv_seq_frequency_t snd_seq_frequency_t;
+typedef sndrv_seq_instr_cluster_t snd_seq_instr_cluster_t;
+typedef enum sndrv_seq_client_type snd_seq_client_type_t;
+typedef enum sndrv_seq_stop_mode snd_seq_stop_mode_t;
+typedef struct sndrv_seq_port_info snd_seq_port_info_t;
+typedef struct sndrv_seq_port_subscribe snd_seq_port_subscribe_t;
+typedef struct sndrv_seq_event snd_seq_event_t;
+typedef struct sndrv_seq_addr snd_seq_addr_t;
+typedef struct sndrv_seq_ev_volume snd_seq_ev_volume_t;
+typedef struct sndrv_seq_ev_loop snd_seq_ev_loop_t;
+typedef struct sndrv_seq_remove_events snd_seq_remove_events_t;
+typedef struct sndrv_seq_query_subs snd_seq_query_subs_t;
+typedef struct sndrv_seq_real_time snd_seq_real_time_t;
+typedef struct sndrv_seq_system_info snd_seq_system_info_t;
+typedef struct sndrv_seq_client_info snd_seq_client_info_t;
+typedef struct sndrv_seq_queue_info snd_seq_queue_info_t;
+typedef struct sndrv_seq_queue_status snd_seq_queue_status_t;
+typedef struct sndrv_seq_queue_tempo snd_seq_queue_tempo_t;
+typedef struct sndrv_seq_queue_owner snd_seq_queue_owner_t;
+typedef struct sndrv_seq_queue_timer snd_seq_queue_timer_t;
+typedef struct sndrv_seq_queue_client snd_seq_queue_client_t;
+typedef struct sndrv_seq_client_pool snd_seq_client_pool_t;
+typedef struct sndrv_seq_instr snd_seq_instr_t;
+typedef struct sndrv_seq_instr_data snd_seq_instr_data_t;
+typedef struct sndrv_seq_instr_header snd_seq_instr_header_t;
+typedef union sndrv_seq_timestamp snd_seq_timestamp_t;
+
+#define snd_seq_event_bounce_ext_data	sndrv_seq_event_bounce_ext_data 
+#define snd_seq_ev_is_result_type	sndrv_seq_ev_is_result_type     
+#define snd_seq_ev_is_channel_type	sndrv_seq_ev_is_channel_type    
+#define snd_seq_ev_is_note_type		sndrv_seq_ev_is_note_type       
+#define snd_seq_ev_is_control_type	sndrv_seq_ev_is_control_type    
+#define snd_seq_ev_is_queue_type	sndrv_seq_ev_is_queue_type      
+#define snd_seq_ev_is_message_type	sndrv_seq_ev_is_message_type    
+#define snd_seq_ev_is_sample_type	sndrv_seq_ev_is_sample_type     
+#define snd_seq_ev_is_user_type		sndrv_seq_ev_is_user_type       
+#define snd_seq_ev_is_fixed_type	sndrv_seq_ev_is_fixed_type      
+#define snd_seq_ev_is_instr_type	sndrv_seq_ev_is_instr_type      
+#define snd_seq_ev_is_variable_type	sndrv_seq_ev_is_variable_type   
+#define snd_seq_ev_is_varipc_type	sndrv_seq_ev_is_varipc_type     
+#define snd_seq_ev_is_reserved		sndrv_seq_ev_is_reserved        
+#define snd_seq_ev_is_direct		sndrv_seq_ev_is_direct          
+#define snd_seq_ev_is_prior		sndrv_seq_ev_is_prior           
+#define snd_seq_ev_length_type		sndrv_seq_ev_length_type        
+#define snd_seq_ev_is_fixed		sndrv_seq_ev_is_fixed           
+#define snd_seq_ev_is_variable		sndrv_seq_ev_is_variable        
+#define snd_seq_ev_is_varusr		sndrv_seq_ev_is_varusr          
+#define snd_seq_ev_is_varipc		sndrv_seq_ev_is_varipc          
+#define snd_seq_ev_timestamp_type	sndrv_seq_ev_timestamp_type     
+#define snd_seq_ev_is_tick		sndrv_seq_ev_is_tick            
+#define snd_seq_ev_is_real		sndrv_seq_ev_is_real            
+#define snd_seq_ev_timemode_type	sndrv_seq_ev_timemode_type      
+#define snd_seq_ev_is_abstime		sndrv_seq_ev_is_abstime         
+#define snd_seq_ev_is_reltime		sndrv_seq_ev_is_reltime         
+#define snd_seq_queue_sync_port		sndrv_seq_queue_sync_port       
+#define snd_seq_queue_owner		sndrv_seq_queue_owner           
+
+/* maximum number of events dequeued per schedule interval */
+#define SNDRV_SEQ_MAX_DEQUEUE		50
+
+/* maximum number of queues */
+#define SNDRV_SEQ_MAX_QUEUES		8
+
+/* max number of concurrent clients */
+#define SNDRV_SEQ_MAX_CLIENTS 		192
+
+/* max number of concurrent ports */
+#define SNDRV_SEQ_MAX_PORTS 		254
+
+/* max number of events in memory pool */
+#define SNDRV_SEQ_MAX_EVENTS		2000
+
+/* default number of events in memory chunk */
+#define SNDRV_SEQ_DEFAULT_CHUNK_EVENTS	64
+
+/* default number of events in memory pool */
+#define SNDRV_SEQ_DEFAULT_EVENTS	500
+
+/* max number of events in memory pool for one client (outqueue) */
+#define SNDRV_SEQ_MAX_CLIENT_EVENTS	2000
+
+/* default number of events in memory pool for one client (outqueue) */
+#define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS	200
+
+/* max delivery path length */
+#define SNDRV_SEQ_MAX_HOPS		10
+
+/* max size of event size */
+#define SNDRV_SEQ_MAX_EVENT_LEN		0x3fffffff
+
+/* typedefs */
+struct _snd_seq_user_client;
+struct _snd_seq_kernel_client;
+struct _snd_seq_client;
+struct _snd_seq_queue;
+
+typedef struct _snd_seq_user_client user_client_t;
+typedef struct _snd_seq_kernel_client kernel_client_t;
+typedef struct _snd_seq_client client_t;
+typedef struct _snd_seq_queue queue_t;
+
+/* call-backs for kernel client */
+
+typedef struct {
+	void *private_data;
+	int allow_input: 1,
+	    allow_output: 1;
+	/*...*/
+} snd_seq_client_callback_t;
+
+/* call-backs for kernel port */
+typedef int (snd_seq_kernel_port_open_t)(void *private_data, snd_seq_port_subscribe_t *info);
+typedef int (snd_seq_kernel_port_close_t)(void *private_data, snd_seq_port_subscribe_t *info);
+typedef int (snd_seq_kernel_port_input_t)(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop);
+typedef void (snd_seq_kernel_port_private_free_t)(void *private_data);
+
+typedef struct {
+	struct module *owner;
+	void *private_data;
+	snd_seq_kernel_port_open_t *subscribe;
+	snd_seq_kernel_port_close_t *unsubscribe;
+	snd_seq_kernel_port_open_t *use;
+	snd_seq_kernel_port_close_t *unuse;
+	snd_seq_kernel_port_input_t *event_input;
+	snd_seq_kernel_port_private_free_t *private_free;
+	unsigned int callback_all;	/* call subscribe callbacks at each connection/disconnection */
+	/*...*/
+} snd_seq_port_callback_t;
+
+/* interface for kernel client */
+extern int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t *callback);
+extern int snd_seq_delete_kernel_client(int client);
+extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop);
+extern int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t *ev, int atomic, int hop);
+extern int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg);
+
+#define SNDRV_SEQ_EXT_MASK	0xc0000000
+#define SNDRV_SEQ_EXT_USRPTR	0x80000000
+#define SNDRV_SEQ_EXT_CHAINED	0x40000000
+
+typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count);
+int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned);
+int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data);
+
+/* port callback routines */
+void snd_port_init_callback(snd_seq_port_callback_t *p);
+snd_seq_port_callback_t *snd_port_alloc_callback(void);
+
+/* port attach/detach */
+int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp,
+			      int cap, int type, char *portname);
+int snd_seq_event_port_detach(int client, int port);
+
+#endif /* __SOUND_SEQ_KERNEL_H */
diff -Nru linux/include/sound/seq_midi_emul.h linux-2.4.19-pre5-mjc/include/sound/seq_midi_emul.h
--- linux/include/sound/seq_midi_emul.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_midi_emul.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,197 @@
+#ifndef __SOUND_SEQ_MIDI_EMUL_H
+#define __SOUND_SEQ_MIDI_EMUL_H
+
+/*
+ *  Midi channel definition for optional channel management.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *   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
+ *
+ */
+
+#include "seq_kernel.h"
+
+/*
+ * This structure is used to keep track of the current state on each
+ * channel.  All drivers for hardware that does not understand midi
+ * directly will probably need to use this structure.
+ */
+typedef struct snd_midi_channel {
+	void *private;		/* A back pointer to driver data */
+	int  number;		/* The channel number */
+	int  client;		/* The client associated with this channel */
+	int  port;		/* The port associated with this channel */
+
+	unsigned char midi_mode;	/* GM, GS, XG etc */
+	unsigned int 
+		drum_channel:1,		/* Drum channel */
+		param_type:1		/* RPN/NRPN */
+		;
+
+	unsigned char midi_aftertouch;	/* Aftertouch (key pressure) */
+	unsigned char midi_pressure;	/* Channel pressure */
+	unsigned char midi_program;	/* Instrument number */
+	short midi_pitchbend;		/* Pitch bend amount */
+
+	unsigned char control[128];	/* Current value of all controls */
+	unsigned char note[128];	/* Current status for all notes */
+
+	short gm_rpn_pitch_bend_range;	/* Pitch bend range */
+	short gm_rpn_fine_tuning; 	/* Master fine tuning */
+	short gm_rpn_coarse_tuning;	/* Master coarse tuning */
+
+} snd_midi_channel_t;
+
+/*
+ * A structure that represets a set of channels bound to a port.  There
+ * would usually be 16 channels per port.  But fewer could be used for
+ * particular cases.
+ * The channel set consists of information describing the client and
+ * port for this midi synth and an array of snd_midi_channel_t structures.
+ * A driver that had no need for snd_midi_channel_t could still use the
+ * channel set type if it wished with the channel array null.
+ */
+typedef struct snd_midi_channel_set {
+	void *private_data;		/* Driver data */
+	int  client;			/* Client for this port */
+	int  port;			/* The port number */
+
+	int  max_channels;		/* Size of the channels array */
+	snd_midi_channel_t *channels;
+
+	unsigned char midi_mode;	/* MIDI operating mode */
+	unsigned char gs_master_volume;	/* SYSEX master volume: 0-127 */
+	unsigned char gs_chorus_mode;
+	unsigned char gs_reverb_mode;
+
+} snd_midi_channel_set_t;
+
+typedef struct snd_seq_midi_op {
+	void (*note_on)(void *private_data, int note, int vel, snd_midi_channel_t *chan);
+	void (*note_off)(void *private_data,int note, int vel, snd_midi_channel_t *chan); /* release note */
+	void (*key_press)(void *private_data, int note, int vel, snd_midi_channel_t *chan);
+	void (*note_terminate)(void *private_data, int note, snd_midi_channel_t *chan); /* terminate note immediately */
+	void (*control)(void *private_data, int type, snd_midi_channel_t *chan);
+	void (*nrpn)(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+	void (*sysex)(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+} snd_midi_op_t;
+
+/*
+ * These defines are used so that pitchbend, aftertouch etc, can be
+ * distinguished from controller values.
+ */
+/* 0-127 controller values */
+#define MIDI_CTL_PITCHBEND	0x80
+#define MIDI_CTL_AFTERTOUCH	0x81
+#define MIDI_CTL_CHAN_PRESSURE	0x82
+
+/*
+ * These names exist to allow symbolic access to the controls array.
+ * The usage is eg: chan->gm_bank_select.  Another implementation would
+ * be really have these members in the struct, and not the array.
+ */
+#define gm_bank_select		control[0]
+#define gm_modulation		control[1]
+#define gm_breath		control[2]
+#define gm_foot_pedal		control[4]
+#define gm_portamento_time	control[5]
+#define gm_data_entry		control[6]
+#define gm_volume		control[7]
+#define gm_balance		control[8]
+#define gm_pan			control[10]
+#define gm_expression		control[11]
+#define gm_effect_control1	control[12]
+#define gm_effect_control2	control[13]
+#define gm_slider1		control[16]
+#define gm_slider2		control[17]
+#define gm_slider3		control[18]
+#define gm_slider4		control[19]
+
+#define gm_bank_select_lsb	control[32]
+#define gm_modulation_wheel_lsb	control[33]
+#define gm_breath_lsb		control[34]
+#define gm_foot_pedal_lsb	control[36]
+#define gm_portamento_time_lsb	control[37]
+#define gm_data_entry_lsb	control[38]
+#define gm_volume_lsb		control[39]
+#define gm_balance_lsb		control[40]
+#define gm_pan_lsb		control[42]
+#define gm_expression_lsb	control[43]
+#define gm_effect_control1_lsb	control[44]
+#define gm_effect_control2_lsb	control[45]
+
+#define gm_sustain	 	control[MIDI_CTL_SUSTAIN]
+#define gm_hold			gm_sustain
+#define gm_portamento		control[MIDI_CTL_PORTAMENTO]
+#define gm_sustenuto		control[MIDI_CTL_SUSTENUTO]
+
+/*
+ * These macros give the complete value of the controls that consist
+ * of coarse and fine pairs.  Of course the fine controls are seldom used
+ * but there is no harm in being complete.
+ */
+#define SNDRV_GM_BANK_SELECT(cp)		(((cp)->control[0]<<7)|((cp)->control[32]))
+#define SNDRV_GM_MODULATION_WHEEL(cp)	(((cp)->control[1]<<7)|((cp)->control[33]))
+#define SNDRV_GM_BREATH(cp)		(((cp)->control[2]<<7)|((cp)->control[34]))
+#define SNDRV_GM_FOOT_PEDAL(cp)		(((cp)->control[4]<<7)|((cp)->control[36]))
+#define SNDRV_GM_PORTAMENTO_TIME(cp)	(((cp)->control[5]<<7)|((cp)->control[37]))
+#define SNDRV_GM_DATA_ENTRY(cp)		(((cp)->control[6]<<7)|((cp)->control[38]))
+#define SNDRV_GM_VOLUME(cp)		(((cp)->control[7]<<7)|((cp)->control[39]))
+#define SNDRV_GM_BALANCE(cp)		(((cp)->control[8]<<7)|((cp)->control[40]))
+#define SNDRV_GM_PAN(cp)			(((cp)->control[10]<<7)|((cp)->control[42]))
+#define SNDRV_GM_EXPRESSION(cp)		(((cp)->control[11]<<7)|((cp)->control[43]))
+
+
+/* MIDI mode */
+#define SNDRV_MIDI_MODE_NONE	0	/* Generic midi */
+#define SNDRV_MIDI_MODE_GM	1
+#define SNDRV_MIDI_MODE_GS	2
+#define SNDRV_MIDI_MODE_XG	3
+#define SNDRV_MIDI_MODE_MT32	4
+
+/* MIDI note state */
+#define SNDRV_MIDI_NOTE_OFF		0x00
+#define SNDRV_MIDI_NOTE_ON		0x01
+#define SNDRV_MIDI_NOTE_RELEASED		0x02
+#define SNDRV_MIDI_NOTE_SUSTENUTO		0x04
+ 
+#define SNDRV_MIDI_PARAM_TYPE_REGISTERED		0
+#define SNDRV_MIDI_PARAM_TYPE_NONREGISTERED	1
+
+/* SYSEX parse flag */
+enum {
+	SNDRV_MIDI_SYSEX_NOT_PARSED = 0,
+	SNDRV_MIDI_SYSEX_GM_ON,	
+	SNDRV_MIDI_SYSEX_GS_ON,	
+	SNDRV_MIDI_SYSEX_GS_RESET,	
+	SNDRV_MIDI_SYSEX_GS_CHORUS_MODE,
+	SNDRV_MIDI_SYSEX_GS_REVERB_MODE,
+	SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME,
+	SNDRV_MIDI_SYSEX_GS_PROGRAM,
+	SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL,
+	SNDRV_MIDI_SYSEX_XG_ON,	
+};
+
+/* Prototypes for midi_process.c */
+void snd_midi_process_event(snd_midi_op_t *ops, snd_seq_event_t *ev,
+			    snd_midi_channel_set_t *chanset);
+void snd_midi_channel_set_clear(snd_midi_channel_set_t *chset);
+void snd_midi_channel_init(snd_midi_channel_t *p, int n);
+snd_midi_channel_t *snd_midi_channel_init_set(int n);
+snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n);
+void snd_midi_channel_free_set(snd_midi_channel_set_t *chset);
+
+#endif /* __SOUND_SEQ_MIDI_EMUL_H */
diff -Nru linux/include/sound/seq_midi_event.h linux-2.4.19-pre5-mjc/include/sound/seq_midi_event.h
--- linux/include/sound/seq_midi_event.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_midi_event.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,54 @@
+#ifndef __SOUND_SEQ_MIDI_EVENT_H
+#define __SOUND_SEQ_MIDI_EVENT_H
+
+/*
+ *  MIDI byte <-> sequencer event coder
+ *
+ *  Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ */
+
+#include "asequencer.h"
+
+#define MAX_MIDI_EVENT_BUF	256
+
+typedef struct snd_midi_event_t snd_midi_event_t;
+
+/* midi status */
+struct snd_midi_event_t {
+	int qlen;	/* queue length */
+	int read;	/* chars read */
+	int type;	/* current event type */
+	unsigned char lastcmd;
+	int bufsize;
+	unsigned char *buf;	/* input buffer */
+	spinlock_t lock;
+};
+
+int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev);
+int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize);
+void snd_midi_event_free(snd_midi_event_t *dev);
+void snd_midi_event_init(snd_midi_event_t *dev);
+void snd_midi_event_reset_encode(snd_midi_event_t *dev);
+void snd_midi_event_reset_decode(snd_midi_event_t *dev);
+/* encode from byte stream - return number of written bytes if success */
+long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev);
+int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev);
+/* decode from event to bytes - return number of written bytes if success */
+long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev);
+
+#endif /* __SOUND_SEQ_MIDI_EVENT_H */
diff -Nru linux/include/sound/seq_oss.h linux-2.4.19-pre5-mjc/include/sound/seq_oss.h
--- linux/include/sound/seq_oss.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_oss.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,102 @@
+#ifndef __SOUND_SEQ_OSS_H
+#define __SOUND_SEQ_OSS_H
+
+/*
+ * OSS compatible sequencer driver
+ *
+ * Copyright (C) 1998,99 Takashi Iwai
+ *
+ * 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
+ */
+
+#include "asequencer.h"
+#include "seq_kernel.h"
+
+/*
+ * type definitions
+ */
+typedef struct snd_seq_oss_arg_t snd_seq_oss_arg_t;
+typedef struct snd_seq_oss_callback_t snd_seq_oss_callback_t;
+
+/*
+ * argument structure for synthesizer operations
+ */
+struct snd_seq_oss_arg_t {
+	/* given by OSS sequencer */
+	int app_index;	/* application unique index */
+	int file_mode;	/* file mode - see below */
+	int seq_mode;	/* sequencer mode - see below */
+
+	/* following must be initialized in open callback */
+	snd_seq_addr_t addr;	/* opened port address */
+	void *private_data;	/* private data for lowlevel drivers */
+
+	/* note-on event passing mode: initially given by OSS seq,
+	 * but configurable by drivers - see below
+	 */
+	int event_passing;
+};
+
+
+/*
+ * synthesizer operation callbacks
+ */
+struct snd_seq_oss_callback_t {
+	struct module *owner;
+	int (*open)(snd_seq_oss_arg_t *p, void *closure);
+	int (*close)(snd_seq_oss_arg_t *p);
+	int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
+	int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
+	int (*reset)(snd_seq_oss_arg_t *p);
+	int (*raw_event)(snd_seq_oss_arg_t *p, unsigned char *data);
+};
+
+/* flag: file_mode */
+#define SNDRV_SEQ_OSS_FILE_ACMODE		3
+#define SNDRV_SEQ_OSS_FILE_READ		1
+#define SNDRV_SEQ_OSS_FILE_WRITE		2
+#define SNDRV_SEQ_OSS_FILE_NONBLOCK	4
+
+/* flag: seq_mode */
+#define SNDRV_SEQ_OSS_MODE_SYNTH		0
+#define SNDRV_SEQ_OSS_MODE_MUSIC		1
+
+/* flag: event_passing */
+#define SNDRV_SEQ_OSS_PROCESS_EVENTS	0	/* key == 255 is processed as velocity change */
+#define SNDRV_SEQ_OSS_PASS_EVENTS		1	/* pass all events to callback */
+#define SNDRV_SEQ_OSS_PROCESS_KEYPRESS	2	/* key >= 128 will be processed as key-pressure */
+
+/* default control rate: fixed */
+#define SNDRV_SEQ_OSS_CTRLRATE		100
+
+/* default max queue length: configurable by module option */
+#define SNDRV_SEQ_OSS_MAX_QLEN		1024
+
+
+/*
+ * data pointer to snd_seq_register_device
+ */
+typedef struct snd_seq_oss_reg {
+	int type;
+	int subtype;
+	int nvoices;
+	snd_seq_oss_callback_t oper;
+	void *private_data;
+} snd_seq_oss_reg_t;
+
+/* device id */
+#define SNDRV_SEQ_DEV_ID_OSS		"seq-oss"
+
+#endif /* __SOUND_SEQ_OSS_H */
diff -Nru linux/include/sound/seq_oss_legacy.h linux-2.4.19-pre5-mjc/include/sound/seq_oss_legacy.h
--- linux/include/sound/seq_oss_legacy.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_oss_legacy.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,31 @@
+#ifndef __SOUND_SEQ_OSS_LEGACY_H
+#define __SOUND_SEQ_OSS_LEGACY_H
+
+/*
+ * OSS compatible macro definitions
+ *
+ * Copyright (C) 2000 Abramo Bagnara <abramo@alsa-project.org>
+ *
+ * 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
+ */
+
+#include <linux/soundcard.h>
+
+#ifndef SAMPLE_TYPE_AWE32
+#define SAMPLE_TYPE_AWE32		0x20
+#endif
+
+#endif /* __SOUND_SEQ_OSS_LEGACY_H */
+
diff -Nru linux/include/sound/seq_virmidi.h linux-2.4.19-pre5-mjc/include/sound/seq_virmidi.h
--- linux/include/sound/seq_virmidi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/seq_virmidi.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,82 @@
+#ifndef __SOUND_SEQ_VIRMIDI_H
+#define __SOUND_SEQ_VIRMIDI_H
+
+/*
+ *  Virtual Raw MIDI client on Sequencer
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+
+#include "rawmidi.h"
+#include "seq_midi_event.h"
+
+typedef struct _snd_virmidi_dev snd_virmidi_dev_t;
+
+/*
+ * device file instance:
+ * This instance is created at each time the midi device file is
+ * opened.  Each instance has its own input buffer and MIDI parser
+ * (buffer), and is associated with the device instance.
+ */
+typedef struct _snd_virmidi {
+	struct list_head list;
+	int seq_mode;
+	int client;
+	int port;
+	int trigger: 1;
+	snd_midi_event_t *parser;
+	snd_seq_event_t event;
+	snd_virmidi_dev_t *rdev;
+	snd_rawmidi_substream_t *substream;
+} snd_virmidi_t;
+
+#define SNDRV_VIRMIDI_SUBSCRIBE	(1<<0)
+#define SNDRV_VIRMIDI_USE		(1<<1)
+
+/*
+ * device record:
+ * Each virtual midi device has one device instance.  It contains
+ * common information and the linked-list of opened files, 
+ */
+struct _snd_virmidi_dev {
+	snd_card_t *card;		/* associated card */
+	snd_rawmidi_t *rmidi;		/* rawmidi device */
+	int seq_mode;			/* SNDRV_VIRMIDI_XXX */
+	int device;			/* sequencer device */
+	int client;			/* created/attached client */
+	int port;			/* created/attached port */
+	unsigned int flags;		/* SNDRV_VIRMIDI_* */
+	rwlock_t filelist_lock;
+	struct list_head filelist;
+};
+
+/* sequencer mode:
+ * ATTACH = input/output events from midi device are routed to the
+ *          attached sequencer port.  sequencer port is not created
+ *          by virmidi itself.
+ * DISPATCH = input/output events are routed to subscribers.
+ *            sequencer port is created in virmidi.
+ */
+#define SNDRV_VIRMIDI_SEQ_NONE		0
+#define SNDRV_VIRMIDI_SEQ_ATTACH		1
+#define SNDRV_VIRMIDI_SEQ_DISPATCH	2
+
+int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi);
+
+#endif /* __SOUND_SEQ_VIRMIDI */
+
diff -Nru linux/include/sound/sfnt_info.h linux-2.4.19-pre5-mjc/include/sound/sfnt_info.h
--- linux/include/sound/sfnt_info.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/sfnt_info.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,184 @@
+#ifndef __SOUND_SFNT_INFO_H
+#define __SOUND_SFNT_INFO_H
+
+/*
+ *  Patch record compatible with AWE driver on OSS
+ *
+ *  Copyright (C) 1999-2000 Takashi Iwai
+ *
+ *   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
+ *
+ */
+
+#include "seq_oss_legacy.h"
+
+/*
+ * patch information record
+ */
+
+/* patch interface header: 16 bytes */
+typedef struct soundfont_patch_info_t {
+	unsigned short key;		/* use the key below */
+#define SNDRV_OSS_SOUNDFONT_PATCH		_PATCHKEY(0x07)
+
+	short device_no;		/* synthesizer number */
+	unsigned short sf_id;		/* file id (should be zero) */
+	short optarg;			/* optional argument */
+	int len;			/* data length (without this header) */
+
+	short type;			/* patch operation type */
+#define SNDRV_SFNT_LOAD_INFO		0	/* awe_voice_rec */
+#define SNDRV_SFNT_LOAD_DATA		1	/* awe_sample_info */
+#define SNDRV_SFNT_OPEN_PATCH	2	/* awe_open_parm */
+#define SNDRV_SFNT_CLOSE_PATCH	3	/* none */
+	/* 4 is obsolete */
+#define SNDRV_SFNT_REPLACE_DATA	5	/* awe_sample_info (optarg=#channels)*/
+#define SNDRV_SFNT_MAP_PRESET	6	/* awe_voice_map */
+	/* 7 is not used */
+#define SNDRV_SFNT_PROBE_DATA		8	/* optarg=sample */
+#define SNDRV_SFNT_REMOVE_INFO		9	/* optarg=(bank<<8)|instr */
+
+	short reserved;			/* word alignment data */
+
+	/* the actual patch data begins after this */
+} soundfont_patch_info_t;
+
+
+/*
+ * open patch
+ */
+
+#define SNDRV_SFNT_PATCH_NAME_LEN	32
+
+typedef struct soundfont_open_parm_t {
+	unsigned short type;		/* sample type */
+#define SNDRV_SFNT_PAT_TYPE_MISC	0
+#define SNDRV_SFNT_PAT_TYPE_GUS	6
+#define SNDRV_SFNT_PAT_TYPE_MAP	7
+#define SNDRV_SFNT_PAT_LOCKED	0x100	/* lock the samples */
+#define SNDRV_SFNT_PAT_SHARED	0x200	/* sample is shared */
+
+	short reserved;
+	char name[SNDRV_SFNT_PATCH_NAME_LEN];
+} soundfont_open_parm_t;
+
+
+/*
+ * raw voice information record
+ */
+
+/* wave table envelope & effect parameters to control EMU8000 */
+typedef struct soundfont_voice_parm_t {
+	unsigned short moddelay;	/* modulation delay (0x8000) */
+	unsigned short modatkhld;	/* modulation attack & hold time (0x7f7f) */
+	unsigned short moddcysus;	/* modulation decay & sustain (0x7f7f) */
+	unsigned short modrelease;	/* modulation release time (0x807f) */
+	short modkeyhold, modkeydecay;	/* envelope change per key (not used) */
+	unsigned short voldelay;	/* volume delay (0x8000) */
+	unsigned short volatkhld;	/* volume attack & hold time (0x7f7f) */
+	unsigned short voldcysus;	/* volume decay & sustain (0x7f7f) */
+	unsigned short volrelease;	/* volume release time (0x807f) */
+	short volkeyhold, volkeydecay;	/* envelope change per key (not used) */
+	unsigned short lfo1delay;	/* LFO1 delay (0x8000) */
+	unsigned short lfo2delay;	/* LFO2 delay (0x8000) */
+	unsigned short pefe;		/* modulation pitch & cutoff (0x0000) */
+	unsigned short fmmod;		/* LFO1 pitch & cutoff (0x0000) */
+	unsigned short tremfrq;		/* LFO1 volume & freq (0x0000) */
+	unsigned short fm2frq2;		/* LFO2 pitch & freq (0x0000) */
+	unsigned char cutoff;		/* initial cutoff (0xff) */
+	unsigned char filterQ;		/* initial filter Q [0-15] (0x0) */
+	unsigned char chorus;		/* chorus send (0x00) */
+	unsigned char reverb;		/* reverb send (0x00) */
+	unsigned short reserved[4];	/* not used */
+} soundfont_voice_parm_t;
+
+
+/* wave table parameters: 92 bytes */
+typedef struct soundfont_voice_info_t {
+	unsigned short sf_id;		/* file id (should be zero) */
+	unsigned short sample;		/* sample id */
+	int start, end;			/* sample offset correction */
+	int loopstart, loopend;		/* loop offset correction */
+	short rate_offset;		/* sample rate pitch offset */
+	unsigned short mode;		/* sample mode */
+#define SNDRV_SFNT_MODE_ROMSOUND		0x8000
+#define SNDRV_SFNT_MODE_STEREO		1
+#define SNDRV_SFNT_MODE_LOOPING		2
+#define SNDRV_SFNT_MODE_NORELEASE		4	/* obsolete */
+#define SNDRV_SFNT_MODE_INIT_PARM		8
+
+	short root;			/* midi root key */
+	short tune;			/* pitch tuning (in cents) */
+	unsigned char low, high;	/* key note range */
+	unsigned char vellow, velhigh;	/* velocity range */
+	signed char fixkey, fixvel;	/* fixed key, velocity */
+	signed char pan, fixpan;	/* panning, fixed panning */
+	short exclusiveClass;		/* exclusive class (0 = none) */
+	unsigned char amplitude;	/* sample volume (127 max) */
+	unsigned char attenuation;	/* attenuation (0.375dB) */
+	short scaleTuning;		/* pitch scale tuning(%), normally 100 */
+	soundfont_voice_parm_t parm;	/* voice envelope parameters */
+	unsigned short sample_mode;	/* sample mode_flag (set by driver) */
+} soundfont_voice_info_t;
+
+
+/* instrument info header: 4 bytes */
+typedef struct soundfont_voice_rec_hdr_t {
+	unsigned char bank;		/* midi bank number */
+	unsigned char instr;		/* midi preset number */
+	char nvoices;			/* number of voices */
+	char write_mode;		/* write mode; normally 0 */
+#define SNDRV_SFNT_WR_APPEND		0	/* append anyway */
+#define SNDRV_SFNT_WR_EXCLUSIVE		1	/* skip if already exists */
+#define SNDRV_SFNT_WR_REPLACE		2	/* replace if already exists */
+} soundfont_voice_rec_hdr_t;
+
+
+/*
+ * sample wave information
+ */
+
+/* wave table sample header: 32 bytes */
+typedef struct soundfont_sample_info_t {
+	unsigned short sf_id;		/* file id (should be zero) */
+	unsigned short sample;		/* sample id */
+	int start, end;			/* start & end offset */
+	int loopstart, loopend;		/* loop start & end offset */
+	int size;			/* size (0 = ROM) */
+	short dummy;			/* not used */
+	unsigned short mode_flags;	/* mode flags */
+#define SNDRV_SFNT_SAMPLE_8BITS		1	/* wave data is 8bits */
+#define SNDRV_SFNT_SAMPLE_UNSIGNED	2	/* wave data is unsigned */
+#define SNDRV_SFNT_SAMPLE_NO_BLANK	4	/* no blank loop is attached */
+#define SNDRV_SFNT_SAMPLE_SINGLESHOT	8	/* single-shot w/o loop */
+#define SNDRV_SFNT_SAMPLE_BIDIR_LOOP	16	/* bidirectional looping */
+#define SNDRV_SFNT_SAMPLE_STEREO_LEFT	32	/* stereo left sound */
+#define SNDRV_SFNT_SAMPLE_STEREO_RIGHT	64	/* stereo right sound */
+#define SNDRV_SFNT_SAMPLE_REVERSE_LOOP	128	/* reverse looping */
+	unsigned int truesize;		/* used memory size (set by driver) */
+} soundfont_sample_info_t;
+
+
+/*
+ * voice preset mapping (aliasing)
+ */
+
+typedef struct soundfont_voice_map_t {
+	int map_bank, map_instr, map_key;	/* key = -1 means all keys */
+	int src_bank, src_instr, src_key;
+} soundfont_voice_map_t;
+
+
+#endif /* __SOUND_SFNT_INFO_H */
diff -Nru linux/include/sound/snd_wavefront.h linux-2.4.19-pre5-mjc/include/sound/snd_wavefront.h
--- linux/include/sound/snd_wavefront.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/snd_wavefront.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,141 @@
+#ifndef __SOUND_SND_WAVEFRONT_H__
+#define __SOUND_SND_WAVEFRONT_H__
+
+#include "cs4231.h"
+#include "mpu401.h"
+#include "hwdep.h"
+#include "rawmidi.h"
+#include "wavefront.h"  /* generic OSS/ALSA/user-level wavefront header */
+
+/* MIDI interface */
+
+struct _snd_wavefront_midi;
+struct _snd_wavefront_card;
+struct _snd_wavefront;
+
+typedef struct _snd_wavefront_midi snd_wavefront_midi_t;
+typedef struct _snd_wavefront_card snd_wavefront_card_t;
+typedef struct _snd_wavefront snd_wavefront_t;
+
+typedef enum { internal_mpu = 0, external_mpu = 1 } snd_wavefront_mpu_id;
+
+struct _snd_wavefront_midi {
+        unsigned long            base;        /* I/O port address */
+	char                     isvirtual;   /* doing virtual MIDI stuff ? */
+	char			 istimer;     /* timer is used */
+        snd_wavefront_mpu_id     output_mpu;  /* most-recently-used */
+        snd_wavefront_mpu_id     input_mpu;   /* most-recently-used */
+        unsigned int             mode[2];     /* MPU401_MODE_XXX */
+	snd_rawmidi_substream_t	 *substream_output[2];
+	snd_rawmidi_substream_t	 *substream_input[2];
+	struct timer_list	 timer;
+        spinlock_t               open;
+        spinlock_t               virtual;     /* protects isvirtual */
+};
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	UART_MODE_ON	0x3F
+
+extern snd_rawmidi_ops_t snd_wavefront_midi_output;
+extern snd_rawmidi_ops_t snd_wavefront_midi_input;
+
+extern void   snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *);
+extern void   snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *);
+extern void   snd_wavefront_midi_interrupt (snd_wavefront_card_t *);
+extern int    snd_wavefront_midi_start (snd_wavefront_card_t *);
+
+struct _snd_wavefront {
+	unsigned long    irq;   /* "you were one, one of the few ..." */
+	unsigned long    base;  /* low i/o port address */
+	struct resource	 *res_base; /* i/o port resource allocation */
+
+#define mpu_data_port    base 
+#define mpu_command_port base + 1 /* write semantics */
+#define mpu_status_port  base + 1 /* read semantics */
+#define data_port        base + 2 
+#define status_port      base + 3 /* read semantics */
+#define control_port     base + 3 /* write semantics  */
+#define block_port       base + 4 /* 16 bit, writeonly */
+#define last_block_port  base + 6 /* 16 bit, writeonly */
+
+	/* FX ports. These are mapped through the ICS2115 to the YS225.
+	   The ICS2115 takes care of flipping the relevant pins on the
+	   YS225 so that access to each of these ports does the right
+	   thing. Note: these are NOT documented by Turtle Beach.
+	*/
+
+#define fx_status       base + 8 
+#define fx_op           base + 8 
+#define fx_lcr          base + 9 
+#define fx_dsp_addr     base + 0xa
+#define fx_dsp_page     base + 0xb 
+#define fx_dsp_lsb      base + 0xc 
+#define fx_dsp_msb      base + 0xd 
+#define fx_mod_addr     base + 0xe
+#define fx_mod_data     base + 0xf 
+
+	volatile int irq_ok;               /* set by interrupt handler */
+        volatile int irq_cnt;              /* ditto */
+	char debug;                        /* debugging flags */
+	int freemem;                       /* installed RAM, in bytes */ 
+
+	char fw_version[2];                /* major = [0], minor = [1] */
+	char hw_version[2];                /* major = [0], minor = [1] */
+	char israw;                        /* needs Motorola microcode */
+	char has_fx;                       /* has FX processor (Tropez+) */
+	char prog_status[WF_MAX_PROGRAM];  /* WF_SLOT_* */
+	char patch_status[WF_MAX_PATCH];   /* WF_SLOT_* */
+	char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */
+	int samples_used;                  /* how many */
+	char interrupts_are_midi;          /* h/w MPU interrupts enabled ? */
+	char rom_samples_rdonly;           /* can we write on ROM samples */
+	wait_queue_head_t interrupt_sleeper; 
+        snd_wavefront_midi_t midi;         /* ICS2115 MIDI interface */
+};
+
+struct _snd_wavefront_card {
+	snd_wavefront_t wavefront;
+#ifdef __ISAPNP__
+	struct isapnp_dev *wss;
+	struct isapnp_dev *ctrl;
+	struct isapnp_dev *mpu;
+	struct isapnp_dev *synth;
+#endif /* CONFIG_ISAPNP */
+};
+
+extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card);
+extern int  snd_wavefront_interrupt_bits (int irq);
+extern int  snd_wavefront_detect_irq (snd_wavefront_t *dev) ;
+extern int  snd_wavefront_check_irq (snd_wavefront_t *dev, int irq);
+extern int  snd_wavefront_restart (snd_wavefront_t *dev);
+extern int  snd_wavefront_start (snd_wavefront_t *dev);
+extern int  snd_wavefront_detect (snd_wavefront_card_t *card);
+extern int  snd_wavefront_config_midi (snd_wavefront_t *dev) ;
+extern int  snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *,
+			       unsigned char *);
+
+extern int snd_wavefront_synth_ioctl   (snd_hwdep_t *, 
+					struct file *,
+					unsigned int cmd, 
+					unsigned long arg);
+extern int  snd_wavefront_synth_open    (snd_hwdep_t *, struct file *);
+extern int  snd_wavefront_synth_release (snd_hwdep_t *, struct file *);
+
+/* FX processor - see also yss225.[ch] */
+
+extern int  snd_wavefront_fx_start  (snd_wavefront_t *);
+extern int  snd_wavefront_fx_detect (snd_wavefront_t *);
+extern int  snd_wavefront_fx_ioctl  (snd_hwdep_t *, 
+				     struct file *,
+				     unsigned int cmd, 
+				     unsigned long arg);
+extern int snd_wavefront_fx_open    (snd_hwdep_t *, struct file *);
+extern int snd_wavefront_fx_release (snd_hwdep_t *, struct file *);
+
+/* prefix in all snd_printk() delivered messages */
+
+#define LOGNAME "WaveFront: "
+
+#endif  /* __SOUND_SND_WAVEFRONT_H__ */
diff -Nru linux/include/sound/sndmagic.h linux-2.4.19-pre5-mjc/include/sound/sndmagic.h
--- linux/include/sound/sndmagic.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/sndmagic.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,145 @@
+#ifndef __SOUND_SNDMAGIC_H
+#define __SOUND_SNDMAGIC_H
+
+/*
+ *  Magic allocation, deallocation, check
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+
+void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags);
+void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags);
+void snd_magic_kfree(void *ptr);
+
+#define snd_magic_kcalloc(type, extra, flags) (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags)
+#define snd_magic_kmalloc(type, extra, flags) (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags)
+
+static inline unsigned long _snd_magic_value(void *obj)
+{
+	return obj == NULL ? -1 : *(((unsigned long *)obj) - 1);
+}
+
+static inline int _snd_magic_bad(void *obj, unsigned long magic)
+{
+	return _snd_magic_value(obj) != magic;
+}
+
+#define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd)
+
+#define snd_magic_cast(type, ptr, action...) (type *) ({\
+	void *__ptr = ptr;\
+	unsigned long __magic = _snd_magic_value(__ptr);\
+	if (__magic != type##_magic) {\
+		snd_printk("bad MAGIC (0x%lx)\n", __magic);\
+		action;\
+	}\
+	__ptr;\
+})
+
+#define snd_device_t_magic			0xa15a00ff
+#define snd_pcm_t_magic				0xa15a0101
+#define snd_pcm_file_t_magic			0xa15a0102
+#define snd_pcm_substream_t_magic		0xa15a0103
+#define snd_pcm_proc_private_t_magic		0xa15a0104
+#define snd_pcm_oss_file_t_magic		0xa15a0105
+#define snd_mixer_oss_t_magic			0xa15a0106
+
+#define snd_info_private_data_t_magic		0xa15a0201
+#define snd_ctl_file_t_magic			0xa15a0301
+#define snd_kcontrol_t_magic			0xa15a0302
+#define snd_rawmidi_t_magic			0xa15a0401
+#define snd_rawmidi_file_t_magic		0xa15a0402
+#define snd_virmidi_t_magic			0xa15a0403
+#define snd_virmidi_dev_t_magic			0xa15a0404
+#define snd_timer_t_magic			0xa15a0501
+#define snd_timer_user_t_magic			0xa15a0502
+#define snd_hwdep_t_magic			0xa15a0601
+#define snd_seq_device_t_magic			0xa15a0701
+
+#define es18xx_t_magic				0xa15a1101
+#define trident_t_magic				0xa15a1201
+#define es1938_t_magic				0xa15a1301
+#define cs46xx_t_magic				0xa15a1401
+#define ensoniq_t_magic				0xa15a1501
+#define sonicvibes_t_magic			0xa15a1601
+#define mpu401_t_magic				0xa15a1701
+#define fm801_t_magic				0xa15a1801
+#define ac97_t_magic				0xa15a1901
+#define ak4531_t_magic				0xa15a1a01
+#define snd_uart16550_t_magic			0xa15a1b01
+#define emu10k1_t_magic				0xa15a1c01
+#define emu10k1_pcm_t_magic			0xa15a1c02
+#define emu10k1_midi_t_magic			0xa15a1c03
+#define snd_gus_card_t_magic			0xa15a1d01
+#define gus_pcm_private_t_magic			0xa15a1d02
+#define gus_proc_private_t_magic		0xa15a1d03
+#define tea6330t_t_magic			0xa15a1e01
+#define ad1848_t_magic				0xa15a1f01
+#define cs4231_t_magic				0xa15a2001
+#define es1688_t_magic				0xa15a2101
+#define opti93x_t_magic				0xa15a2201
+#define emu8000_t_magic				0xa15a2301
+#define emu8000_proc_private_t_magic		0xa15a2302
+#define snd_emux_t_magic			0xa15a2303
+#define snd_emux_port_t_magic			0xa15a2304
+#define sb_t_magic				0xa15a2401
+#define snd_sb_csp_t_magic			0xa15a2402
+#define snd_card_dummy_t_magic			0xa15a2501
+#define snd_card_dummy_pcm_t_magic		0xa15a2502
+#define opl3_t_magic				0xa15a2601
+#define snd_seq_dummy_port_t_magic		0xa15a2701
+#define ice1712_t_magic				0xa15a2801
+#define ad1816a_t_magic				0xa15a2901
+#define intel8x0_t_magic			0xa15a2a01
+#define es1968_t_magic				0xa15a2b01
+#define esschan_t_magic				0xa15a2b02
+#define via686a_t_magic				0xa15a2c01
+#define pdplus_t_magic				0xa15a2d01
+#define cmipci_t_magic				0xa15a2e01
+#define ymfpci_t_magic				0xa15a2f01
+#define ymfpci_pcm_t_magic			0xa15a2f02
+#define cs4281_t_magic				0xa15a3001
+#define snd_i2c_bus_t_magic			0xa15a3101
+#define snd_i2c_device_t_magic			0xa15a3102
+#define cs8427_t_magic				0xa15a3111
+#define m3_t_magic				0xa15a3201
+#define m3_dma_t_magic				0xa15a3202
+#define nm256_t_magic				0xa15a3301
+#define nm256_dma_t_magic			0xa15a3302
+#define via8233_t_magic				0xa15a3401
+#define pmac_t_magic				0xa15a3501
+#define ali_t_magic				0xa15a3601
+#define mtpav_t_magic				0xa15a3701
+#define mtpav_port_t_magic			0xa15a3702
+#define korg1212_t_magic			0xa15a3800
+#define opl3sa2_t_magic				0xa15a3900
+
+#else
+
+#define snd_magic_kcalloc(type, extra, flags) (type *) snd_kcalloc(sizeof(type) + extra, flags)
+#define snd_magic_kmalloc(type, extra, flags) (type *) kmalloc(sizeof(type) + extra, flags)
+#define snd_magic_cast(type, ptr, retval) (type *) ptr
+#define snd_magic_cast1(type, ptr, retval) snd_magic_cast(type, ptr, retval)
+#define snd_magic_kfree kfree
+
+#endif
+
+#endif /* __SOUND_SNDMAGIC_H */
diff -Nru linux/include/sound/soundfont.h linux-2.4.19-pre5-mjc/include/sound/soundfont.h
--- linux/include/sound/soundfont.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/soundfont.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,130 @@
+#ifndef __SOUND_SOUNDFONT_H
+#define __SOUND_SOUNDFONT_H
+
+/*
+ *  Soundfont defines and definitions.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include "sfnt_info.h"
+#include "util_mem.h"
+
+#define SF_MAX_INSTRUMENTS	128	/* maximum instrument number */
+#define SF_MAX_PRESETS  256	/* drums are mapped from 128 to 256 */
+#define SF_IS_DRUM_BANK(z) ((z) == 128)
+
+typedef struct snd_sf_zone {
+	struct snd_sf_zone *next;	/* Link to next */
+	unsigned char bank;		/* Midi bank for this zone */
+	unsigned char instr;		/* Midi program for this zone */
+	unsigned char mapped;		/* True if mapped to something else */
+
+	soundfont_voice_info_t v;	/* All the soundfont parameters */
+	int counter;
+	struct snd_sf_sample *sample;	/* Link to sample */
+
+	/* The following deals with preset numbers (programs) */
+	struct snd_sf_zone *next_instr;	/* Next zone of this instrument */
+	struct snd_sf_zone *next_zone;	/* Next zone in play list */
+} snd_sf_zone_t;
+
+typedef struct snd_sf_sample {
+	soundfont_sample_info_t v;
+	int counter;
+	snd_util_memblk_t *block;	/* allocated data block */
+	struct snd_sf_sample *next;
+} snd_sf_sample_t;
+
+/*
+ * This represents all the information relating to a soundfont.
+ */
+typedef struct snd_soundfont {
+	struct snd_soundfont *next;	/* Link to next */
+	/*struct snd_soundfont *prev;*/	/* Link to previous */
+	short  id;		/* file id */
+	short  type;		/* font type */
+	unsigned char name[SNDRV_SFNT_PATCH_NAME_LEN];	/* identifier */
+	snd_sf_zone_t *zones; /* Font information */
+	snd_sf_sample_t *samples; /* The sample headers */
+} snd_soundfont_t;
+
+/*
+ * Type of the sample access callback
+ */
+typedef int (*snd_sf_sample_new_t)(void *private_data, snd_sf_sample_t *sp,
+				   snd_util_memhdr_t *hdr, const void *buf, long count);
+typedef int (*snd_sf_sample_free_t)(void *private_data, snd_sf_sample_t *sp,
+				    snd_util_memhdr_t *hdr);
+typedef void (*snd_sf_sample_reset_t)(void *private);
+
+typedef struct snd_sf_callback {
+	void *private_data;
+	snd_sf_sample_new_t sample_new;
+	snd_sf_sample_free_t sample_free;
+	snd_sf_sample_reset_t sample_reset;
+} snd_sf_callback_t;
+
+/*
+ * List of soundfonts.
+ */
+typedef struct snd_sf_list {
+	snd_soundfont_t *currsf; /* The currently open soundfont */
+	int open_client;	/* client pointer for lock */
+	int mem_used;		/* used memory size */
+	snd_sf_zone_t *presets[SF_MAX_PRESETS];
+	snd_soundfont_t *fonts; /* The list of soundfonts */
+	int fonts_size;	/* number of fonts allocated */
+	int zone_counter;	/* last allocated time for zone */
+	int sample_counter;	/* last allocated time for sample */
+	int zone_locked;	/* locked time for zone */
+	int sample_locked;	/* locked time for sample */
+	snd_sf_callback_t callback;	/* callback functions */
+	char sf_locked;		/* font lock flag */
+	struct semaphore presets_mutex;
+	spinlock_t lock;
+	snd_util_memhdr_t *memhdr;
+} snd_sf_list_t;
+
+/* Prototypes for soundfont.c */
+int snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client);
+int snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data,
+				long count, int client);
+int snd_soundfont_close_check(snd_sf_list_t *sflist, int client);
+
+snd_sf_list_t *snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr);
+void snd_sf_free(snd_sf_list_t *sflist);
+
+int snd_soundfont_remove_samples(snd_sf_list_t *sflist);
+int snd_soundfont_remove_unlocked(snd_sf_list_t *sflist);
+int snd_soundfont_mem_used(snd_sf_list_t *sflist);
+
+int snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel,
+			      int preset, int bank,
+			      int def_preset, int def_bank,
+			      snd_sf_zone_t **table, int max_layers);
+
+/* Parameter conversions */
+int snd_sf_calc_parm_hold(int msec);
+int snd_sf_calc_parm_attack(int msec);
+int snd_sf_calc_parm_decay(int msec);
+#define snd_sf_calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725);
+extern int snd_sf_vol_table[128];
+
+
+#endif /* __SOUND_SOUNDFONT_H */
diff -Nru linux/include/sound/soundmem.h linux-2.4.19-pre5-mjc/include/sound/soundmem.h
--- linux/include/sound/soundmem.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/soundmem.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,8 @@
+/*
+ *  Onboard memory management
+ */
+
+struct SNDRV_STRU_BANK_INFO {
+	unsigned int address;
+	unsigned int size;
+};
diff -Nru linux/include/sound/tea6330t.h linux-2.4.19-pre5-mjc/include/sound/tea6330t.h
--- linux/include/sound/tea6330t.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/tea6330t.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,42 @@
+#ifndef __SOUND_TEA6330T_H
+#define __SOUND_TEA6330T_H
+
+/*
+ *  Routines for control of TEA6330T circuit.
+ *  Sound fader control circuit for car radios.
+ *
+ *   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
+ *
+ *
+ */
+
+#include "control.h"
+#include "i2c.h"		/* generic i2c support */
+
+typedef struct {
+	snd_i2c_device_t *device;
+	snd_i2c_bus_t *bus;
+	int equalizer;
+	int fader;
+	unsigned char regs[8];
+	unsigned char mleft, mright;
+	unsigned char bass, treble;
+	unsigned char max_bass, max_treble;
+} tea6330t_t;
+
+extern int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer);
+extern int snd_tea6330t_update_mixer(snd_card_t * card, snd_i2c_bus_t * bus, int equalizer, int fader);
+
+#endif /* __SOUND_TEA6330T_H */
diff -Nru linux/include/sound/timer.h linux-2.4.19-pre5-mjc/include/sound/timer.h
--- linux/include/sound/timer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/timer.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,137 @@
+#ifndef __SOUND_TIMER_H
+#define __SOUND_TIMER_H
+
+/*
+ *  Timer abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/asound.h>
+
+typedef enum sndrv_timer_class snd_timer_class_t;
+typedef enum sndrv_timer_slave_class snd_timer_slave_class_t;
+typedef enum sndrv_timer_global snd_timer_global_t;
+typedef struct sndrv_timer_id snd_timer_id_t;
+typedef struct sndrv_timer_select snd_timer_select_t;
+typedef struct sndrv_timer_info snd_timer_info_t;
+typedef struct sndrv_timer_params snd_timer_params_t;
+typedef struct sndrv_timer_status snd_timer_status_t;
+typedef struct sndrv_timer_read snd_timer_read_t;
+
+#define _snd_timer_chip(timer) ((timer)->private_data)
+#define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO)
+
+#define SNDRV_TIMER_DEVICES	16
+
+#define SNDRV_TIMER_DEV_FLG_PCM	0x10000000
+
+#define SNDRV_TIMER_HW_AUTO	0x00000001	/* auto trigger is supported */
+#define SNDRV_TIMER_HW_STOP	0x00000002	/* call stop before start */
+#define SNDRV_TIMER_HW_SLAVE	0x00000004	/* only slave timer (variable resolution) */
+#define SNDRV_TIMER_HW_FIRST	0x00000008	/* first tick can be incomplete */
+
+#define SNDRV_TIMER_IFLG_SLAVE	0x00000001
+#define SNDRV_TIMER_IFLG_RUNNING	0x00000002
+#define SNDRV_TIMER_IFLG_START	0x00000004
+#define SNDRV_TIMER_IFLG_AUTO	0x00000008	/* auto restart */
+
+#define SNDRV_TIMER_FLG_SYSTEM	0x00000001	/* system timer */
+#define SNDRV_TIMER_FLG_CHANGE	0x00000002
+#define SNDRV_TIMER_FLG_RESCHED	0x00000004	/* need reschedule */
+
+typedef void (*snd_timer_callback_t) (snd_timer_instance_t * timeri, unsigned long ticks, unsigned long resolution, void *data);
+
+struct _snd_timer_hardware {
+	/* -- must be filled with low-level driver */
+	unsigned int flags;		/* various flags */
+	unsigned long resolution;	/* average timer resolution for one tick in nsec */
+	unsigned long ticks;		/* max timer ticks per interrupt */
+	/* -- low-level functions -- */
+	int (*open) (snd_timer_t * timer);
+	int (*close) (snd_timer_t * timer);
+	unsigned long (*c_resolution) (snd_timer_t * timer);
+	int (*start) (snd_timer_t * timer);
+	int (*stop) (snd_timer_t * timer);
+};
+
+struct _snd_timer {
+	snd_timer_class_t tmr_class;
+	snd_card_t *card;
+	int tmr_device;
+	int tmr_subdevice;
+	char id[64];
+	char name[80];
+	unsigned int flags;
+	int running;			/* running instances */
+	unsigned long sticks;		/* schedule ticks */
+	void *private_data;
+	void (*private_free) (snd_timer_t *timer);
+	struct _snd_timer_hardware hw;
+	spinlock_t lock;
+	struct list_head device_list;
+	struct list_head open_list_head;
+	struct list_head active_list_head;
+};
+
+struct _snd_timer_instance {
+	snd_timer_t * timer;
+	char *owner;
+	unsigned int flags;
+	void *private_data;
+	void (*private_free) (snd_timer_instance_t *ti);
+	snd_timer_callback_t callback;
+	void *callback_data;
+	unsigned long ticks;
+	unsigned long cticks;
+	unsigned long lost;		/* lost ticks */
+	snd_timer_slave_class_t slave_class;
+	unsigned int slave_id;
+	struct list_head open_list;
+	struct list_head active_list;
+	struct list_head slave_list_head;
+	struct list_head slave_active_head;
+	snd_timer_instance_t *master;
+	atomic_t in_use;		/* don't free */
+};
+
+/*
+ *  Registering
+ */
+
+extern int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer);
+extern int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer);
+extern int snd_timer_global_free(snd_timer_t *timer);
+extern int snd_timer_global_register(snd_timer_t *timer);
+extern int snd_timer_global_unregister(snd_timer_t *timer);
+
+extern snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid, unsigned int slave_id);
+extern int snd_timer_close(snd_timer_instance_t * timeri);
+extern int snd_timer_set_owner(snd_timer_instance_t * timeri, pid_t pid, gid_t gid);
+extern int snd_timer_reset_owner(snd_timer_instance_t * timeri);
+extern int snd_timer_set_resolution(snd_timer_instance_t * timeri, unsigned long resolution);
+extern unsigned long snd_timer_resolution(snd_timer_instance_t * timeri);
+extern int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks);
+extern int snd_timer_stop(snd_timer_instance_t * timeri);
+extern int snd_timer_continue(snd_timer_instance_t * timeri);
+
+extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left);
+
+extern unsigned int snd_timer_system_resolution(void);
+
+#endif /* __SOUND_TIMER_H */
diff -Nru linux/include/sound/trident.h linux-2.4.19-pre5-mjc/include/sound/trident.h
--- linux/include/sound/trident.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/trident.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,476 @@
+#ifndef __SOUND_TRIDENT_H
+#define __SOUND_TRIDENT_H
+
+/*
+ *  audio@tridentmicro.com
+ *  Fri Feb 19 15:55:28 MST 1999
+ *  Definitions for Trident 4DWave DX/NX chips
+ *
+ *
+ *   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
+ *
+ */
+
+#include "pcm.h"
+#include "mpu401.h"
+#include "ac97_codec.h"
+#include "seq_midi_emul.h"
+#include "seq_device.h"
+#include "util_mem.h"
+//#include "ainstr_iw.h"
+//#include "ainstr_gf1.h"
+#include "ainstr_simple.h"
+
+#ifndef PCI_VENDOR_ID_TRIDENT
+#define PCI_VENDOR_ID_TRIDENT		0x1023
+#endif
+#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX	0x2000
+#endif
+#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX	0x2001
+#endif
+
+#ifndef PCI_VENDOR_ID_SI
+#define PCI_VENDOR_ID_SI		0x1039
+#endif
+#ifndef PCI_DEVICE_ID_SI_7018
+#define PCI_DEVICE_ID_SI_7018		0x7018
+#endif
+
+#define TRIDENT_DEVICE_ID_DX		((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)
+#define TRIDENT_DEVICE_ID_NX		((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)
+#define TRIDENT_DEVICE_ID_SI7018	((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018)
+
+/* Trident chipsets have 1GB memory limit */
+#ifdef __alpha__
+#define TRIDENT_DMA_TYPE        SNDRV_DMA_TYPE_PCI_16MB
+#define TRIDENT_GFP_FLAGS	GFP_DMA
+#else
+#define TRIDENT_DMA_TYPE        SNDRV_DMA_TYPE_PCI
+#if defined(__i386__) && !defined(CONFIG_1GB)
+#define TRIDENT_GFP_FLAGS	GFP_DMA
+#else
+#define TRIDENT_GFP_FLAGS	0
+#endif
+#endif
+
+#define SNDRV_SEQ_DEV_ID_TRIDENT			"trident-synth"
+
+#define SNDRV_TRIDENT_VOICE_TYPE_PCM		0
+#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH		1
+#define SNDRV_TRIDENT_VOICE_TYPE_MIDI		2
+
+#define SNDRV_TRIDENT_VFLG_RUNNING		(1<<0)
+
+/* TLB code constants */
+#define SNDRV_TRIDENT_PAGE_SIZE			4096
+#define SNDRV_TRIDENT_PAGE_SHIFT			12
+#define SNDRV_TRIDENT_PAGE_MASK			((1<<SNDRV_TRIDENT_PAGE_SHIFT)-1)
+#define SNDRV_TRIDENT_MAX_PAGES			4096
+
+/*
+ * Direct registers
+ */
+
+#define TRID_REG(trident, x) ((trident)->port + (x))
+
+#define ID_4DWAVE_DX        0x2000
+#define ID_4DWAVE_NX        0x2001
+
+/* Bank definitions */
+
+#define T4D_BANK_A	0
+#define T4D_BANK_B	1
+#define T4D_NUM_BANKS	2
+
+/* Register definitions */
+
+/* Global registers */
+
+enum global_control_bits {
+	CHANNEL_IDX = 0x0000003f, OVERRUN_IE = 0x00000400,
+	UNDERRUN_IE = 0x00000800, ENDLP_IE   = 0x00001000,
+	MIDLP_IE    = 0x00002000, ETOG_IE    = 0x00004000,
+	EDROP_IE    = 0x00008000, BANK_B_EN  = 0x00010000
+};
+
+enum miscint_bits {
+	PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002,
+	SB_IRQ		= 0x00000004, MPU401_IRQ      = 0x00000008,
+	OPL3_IRQ        = 0x00000010, ADDRESS_IRQ     = 0x00000020,
+	ENVELOPE_IRQ    = 0x00000040, PB_UNDERRUN     = 0x00000100,
+	REC_OVERRUN	= 0x00000200, MIXER_UNDERFLOW = 0x00000400,
+	MIXER_OVERFLOW  = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000,
+        ST_TARGET_REACHED = 0x00008000,
+	PB_24K_MODE     = 0x00010000, ST_IRQ_EN       = 0x00800000,
+	ACGPIO_IRQ	= 0x01000000
+};
+
+/* T2 legacy dma control registers. */
+#define LEGACY_DMAR0                0x00  // ADR0
+#define LEGACY_DMAR4                0x04  // CNT0
+#define LEGACY_DMAR6		    0x06  // CNT0 - High bits
+#define LEGACY_DMAR11               0x0b  // MOD 
+#define LEGACY_DMAR15               0x0f  // MMR 
+
+#define T4D_START_A		     0x80
+#define T4D_STOP_A		     0x84
+#define T4D_DLY_A		     0x88
+#define T4D_SIGN_CSO_A		     0x8c
+#define T4D_CSPF_A		     0x90
+#define T4D_CSPF_B		     0xbc
+#define T4D_CEBC_A		     0x94
+#define T4D_AINT_A		     0x98
+#define T4D_AINTEN_A		     0x9c
+#define T4D_LFO_GC_CIR               0xa0
+#define T4D_MUSICVOL_WAVEVOL         0xa8
+#define T4D_SBDELTA_DELTA_R          0xac
+#define T4D_MISCINT                  0xb0
+#define T4D_START_B                  0xb4
+#define T4D_STOP_B                   0xb8
+#define T4D_SBBL_SBCL                0xc0
+#define T4D_SBCTRL_SBE2R_SBDD        0xc4
+#define T4D_STIMER		     0xc8
+#define T4D_AINT_B                   0xd8
+#define T4D_AINTEN_B                 0xdc
+#define T4D_RCI                      0x70
+
+/* MPU-401 UART */
+#define T4D_MPU401_BASE             0x20
+#define T4D_MPUR0                   0x20
+#define T4D_MPUR1                   0x21
+#define T4D_MPUR2                   0x22
+#define T4D_MPUR3                   0x23
+
+/* S/PDIF Registers */
+#define NX_SPCTRL_SPCSO             0x24
+#define NX_SPLBA                    0x28
+#define NX_SPESO                    0x2c
+#define NX_SPCSTATUS                0x64
+
+/* NX Specific Registers */
+#define NX_TLBC                     0x6c
+
+/* Channel Registers */
+
+#define CH_START		    0xe0
+
+#define CH_DX_CSO_ALPHA_FMS         0xe0
+#define CH_DX_ESO_DELTA             0xe8
+#define CH_DX_FMC_RVOL_CVOL         0xec
+
+#define CH_NX_DELTA_CSO             0xe0
+#define CH_NX_DELTA_ESO             0xe8
+#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec
+
+#define CH_LBA                      0xe4
+#define CH_GVSEL_PAN_VOL_CTRL_EC    0xf0
+#define CH_EBUF1                    0xf4
+#define CH_EBUF2                    0xf8
+
+/* AC-97 Registers */
+
+#define DX_ACR0_AC97_W              0x40
+#define DX_ACR1_AC97_R              0x44
+#define DX_ACR2_AC97_COM_STAT       0x48
+
+#define NX_ACR0_AC97_COM_STAT       0x40
+#define NX_ACR1_AC97_W              0x44
+#define NX_ACR2_AC97_R_PRIMARY      0x48
+#define NX_ACR3_AC97_R_SECONDARY    0x4c
+
+#define SI_AC97_WRITE		    0x40
+#define SI_AC97_READ		    0x44
+#define SI_SERIAL_INTF_CTRL	    0x48
+#define SI_AC97_GPIO		    0x4c
+#define SI_ASR0			    0x50
+#define SI_SPDIF_CS		    0x70
+#define SI_GPIO			    0x7c
+
+enum trident_nx_ac97_bits {
+	/* ACR1-3 */
+	NX_AC97_BUSY_WRITE 	= 0x0800,
+	NX_AC97_BUSY_READ	= 0x0800,
+	NX_AC97_BUSY_DATA 	= 0x0400,
+	NX_AC97_WRITE_SECONDARY = 0x0100,
+	/* ACR0 */
+	NX_AC97_SECONDARY_READY = 0x0040,
+	NX_AC97_SECONDARY_RECORD = 0x0020,
+	NX_AC97_SURROUND_OUTPUT = 0x0010,
+	NX_AC97_PRIMARY_READY	= 0x0008,
+	NX_AC97_PRIMARY_RECORD	= 0x0004,
+	NX_AC97_PCM_OUTPUT	= 0x0002,
+	NX_AC97_WARM_RESET	= 0x0001
+};
+
+enum trident_dx_ac97_bits {
+	DX_AC97_BUSY_WRITE	= 0x8000,
+	DX_AC97_BUSY_READ	= 0x8000,
+	DX_AC97_READY		= 0x0010,
+	DX_AC97_RECORD		= 0x0008,
+	DX_AC97_PLAYBACK	= 0x0002
+};
+
+enum sis7018_ac97_bits {
+	SI_AC97_BUSY_WRITE =	0x00008000,
+	SI_AC97_AUDIO_BUSY =	0x00004000,
+	SI_AC97_MODEM_BUSY =	0x00002000,
+	SI_AC97_BUSY_READ =	0x00008000,
+	SI_AC97_SECONDARY =	0x00000080,
+};
+
+enum serial_intf_ctrl_bits {
+	WARM_RESET	= 0x00000001,
+	COLD_RESET	= 0x00000002,
+	I2S_CLOCK	= 0x00000004,
+	PCM_SEC_AC97	= 0x00000008,
+	AC97_DBL_RATE	= 0x00000010,
+	SPDIF_EN	= 0x00000020,
+	I2S_OUTPUT_EN	= 0x00000040,
+	I2S_INPUT_EN	= 0x00000080,
+	PCMIN		= 0x00000100,
+	LINE1IN		= 0x00000200,
+	MICIN		= 0x00000400,
+	LINE2IN		= 0x00000800,
+	HEAD_SET_IN	= 0x00001000,
+	GPIOIN		= 0x00002000,
+	/* 7018 spec says id = 01 but the demo board routed to 10
+	   SECONDARY_ID= 0x00004000, */
+	SECONDARY_ID	= 0x00004000,
+	PCMOUT		= 0x00010000,
+	SURROUT		= 0x00020000,
+	CENTEROUT	= 0x00040000,
+	LFEOUT		= 0x00080000,
+	LINE1OUT	= 0x00100000,
+	LINE2OUT	= 0x00200000,
+	GPIOOUT		= 0x00400000,
+	SI_AC97_PRIMARY_READY = 0x01000000,
+	SI_AC97_SECONDARY_READY = 0x02000000,
+	SI_AC97_POWERDOWN = 0x04000000,
+};
+                                                                                                                                   
+/* PCM defaults */
+
+#define T4D_DEFAULT_PCM_VOL	10	/* 0 - 255 */
+#define T4D_DEFAULT_PCM_PAN	0	/* 0 - 127 */
+#define T4D_DEFAULT_PCM_RVOL	127	/* 0 - 127 */
+#define T4D_DEFAULT_PCM_CVOL	127	/* 0 - 127 */
+
+typedef struct _snd_trident trident_t;
+typedef struct _snd_trident_voice snd_trident_voice_t;
+typedef struct _snd_trident_pcm_mixer snd_trident_pcm_mixer_t;
+
+typedef struct {
+	void (*sample_start)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_position_t position);
+	void (*sample_stop)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_stop_mode_t mode);
+	void (*sample_freq)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_frequency_t freq);
+	void (*sample_volume)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_ev_volume_t *volume);
+	void (*sample_loop)(trident_t *card, snd_trident_voice_t *voice, snd_seq_ev_loop_t *loop);
+	void (*sample_pos)(trident_t *card, snd_trident_voice_t *voice, snd_seq_position_t position);
+	void (*sample_private1)(trident_t *card, snd_trident_voice_t *voice, unsigned char *data);
+} snd_trident_sample_ops_t;
+
+typedef struct {
+	snd_midi_channel_set_t * chset;
+	trident_t * trident;
+	int mode;		/* operation mode */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	int midi_has_voices: 1;
+} snd_trident_port_t;
+
+typedef struct snd_trident_memblk_arg {
+	short first_page, last_page;
+} snd_trident_memblk_arg_t;
+
+typedef struct {
+	unsigned int * entries;		/* 16k-aligned TLB table */
+	dma_addr_t entries_dmaaddr;	/* 16k-aligned PCI address to TLB table */
+	unsigned long * shadow_entries;	/* shadow entries with virtual addresses */
+	void * buffer;			/* pointer for table calloc */
+	dma_addr_t buffer_dmaaddr;	/* not accessible PCI BUS physical address */
+	snd_util_memhdr_t * memhdr;	/* page allocation list */
+	void * silent_page;		/* silent page */
+	dma_addr_t silent_page_dmaaddr; /* not accessible PCI BUS physical address */
+} snd_trident_tlb_t;
+
+struct _snd_trident_voice {
+	unsigned int number;
+	int use: 1,
+	    pcm: 1,
+	    synth:1,
+	    midi: 1;
+	unsigned int flags;
+	unsigned char client;
+	unsigned char port;
+	unsigned char index;
+
+	snd_seq_instr_t instr;
+	snd_trident_sample_ops_t *sample_ops;
+
+	/* channel parameters */
+	unsigned int CSO;		/* 24 bits (16 on DX) */
+	unsigned int ESO;		/* 24 bits (16 on DX) */
+	unsigned int LBA;		/* 30 bits */
+	unsigned short EC;		/* 12 bits */
+	unsigned short Alpha;		/* 12 bits */
+	unsigned short Delta;		/* 16 bits */
+	unsigned short Attribute;	/* 16 bits - SiS 7018 */
+	unsigned short Vol;		/* 12 bits (6.6) */
+	unsigned char Pan;		/* 7 bits (1.4.2) */
+	unsigned char GVSel;		/* 1 bit */
+	unsigned char RVol;		/* 7 bits (5.2) */
+	unsigned char CVol;		/* 7 bits (5.2) */
+	unsigned char FMC;		/* 2 bits */
+	unsigned char CTRL;		/* 4 bits */
+	unsigned char FMS;		/* 4 bits */
+	unsigned char LFO;		/* 8 bits */
+
+	unsigned int negCSO;	/* nonzero - use negative CSO */
+
+	snd_util_memblk_t *memblk;	/* memory block if TLB enabled */
+
+	/* PCM data */
+
+	trident_t *trident;
+	snd_pcm_substream_t *substream;
+	snd_trident_voice_t *extra;	/* extra PCM voice (acts as interrupt generator) */
+	int running: 1,
+            capture: 1,
+            spdif: 1,
+            foldback: 1;
+	int foldback_chan;		/* foldback subdevice number */
+	unsigned int stimer;		/* global sample timer (to detect spurious interrupts) */
+	unsigned int spurious_threshold; /* spurious threshold */
+
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(snd_trident_voice_t *voice);
+};
+
+struct _snd_4dwave {
+	int seq_client;
+
+	snd_trident_port_t seq_ports[4];
+	snd_simple_ops_t simple_ops;
+	snd_seq_kinstr_list_t *ilist;
+
+	snd_trident_voice_t voices[64];	
+
+	int ChanSynthCount;		/* number of allocated synth channels */
+	int max_size;			/* maximum synth memory size in bytes */
+	int current_size;		/* current allocated synth mem in bytes */
+};
+
+struct _snd_trident_pcm_mixer {
+	snd_trident_voice_t *voice;	/* active voice */
+	unsigned short vol;		/* front volume */
+	unsigned char pan;		/* pan control */
+	unsigned char rvol;		/* rear volume */
+	unsigned char cvol;		/* center volume */
+	unsigned char pad;
+	snd_kcontrol_t *ctl_vol;	/* front volume */
+	snd_kcontrol_t *ctl_pan;	/* pan */
+	snd_kcontrol_t *ctl_rvol;	/* rear volume */
+	snd_kcontrol_t *ctl_cvol;	/* center volume */
+};
+
+struct _snd_trident {
+	int irq;
+
+	unsigned int device;	/* device ID */
+
+        unsigned char  bDMAStart;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long midi_port;
+
+	unsigned int spurious_irq_count;
+	unsigned int spurious_irq_max_delta;
+
+        snd_trident_tlb_t tlb;	/* TLB entries for NX cards */
+
+	unsigned char spdif_ctrl;
+	unsigned char spdif_pcm_ctrl;
+	unsigned int spdif_bits;
+	unsigned int spdif_pcm_bits;
+	snd_kcontrol_t *spdif_pcm_ctl;	/* S/PDIF settings */
+	unsigned int ac97_ctrl;
+        
+        unsigned int ChanMap[2];	/* allocation map for hardware channels */
+        
+        int ChanPCM;			/* max number of PCM channels */
+	int ChanPCMcnt;			/* actual number of PCM channels */
+
+	struct _snd_4dwave synth;	/* synth specific variables */
+
+	spinlock_t event_lock;
+	spinlock_t voice_alloc;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;		/* ADC/DAC PCM */
+	snd_pcm_t *foldback;	/* Foldback PCM */
+	snd_pcm_t *spdif;	/* SPDIF PCM */
+	snd_rawmidi_t *rmidi;
+	snd_seq_device_t *seq_dev;
+
+	ac97_t *ac97;
+
+	unsigned int musicvol_wavevol;
+	snd_trident_pcm_mixer_t pcm_mixer[32];
+
+	spinlock_t reg_lock;
+	snd_info_entry_t *proc_entry;
+};
+
+int snd_trident_create(snd_card_t * card,
+		       struct pci_dev *pci,
+		       int pcm_streams,
+		       int pcm_spdif_device,
+		       int max_wavetable_size,
+		       trident_t ** rtrident);
+int snd_trident_free(trident_t *trident);
+
+int snd_trident_pcm(trident_t * trident, int device, snd_pcm_t **rpcm);
+int snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t **rpcm);
+int snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t **rpcm);
+int snd_trident_attach_synthesizer(trident_t * trident);
+int snd_trident_detach_synthesizer(trident_t * trident);
+snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port);
+void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice);
+void snd_trident_start_voice(trident_t * trident, unsigned int voice);
+void snd_trident_stop_voice(trident_t * trident, unsigned int voice);
+void snd_trident_write_voice_regs(trident_t * trident, snd_trident_voice_t *voice);
+void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max);
+
+/* TLB memory allocation */
+snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size);
+int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk);
+snd_util_memblk_t *snd_trident_synth_alloc(trident_t *trident, unsigned int size);
+int snd_trident_synth_free(trident_t *trident, snd_util_memblk_t *blk);
+int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size);
+int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size);
+
+/* Power Management */
+#ifdef CONFIG_PM
+void snd_trident_suspend(trident_t *trident);
+void snd_trident_resume(trident_t *trident);
+#endif
+
+#endif /* __SOUND_TRIDENT_H */
diff -Nru linux/include/sound/util_mem.h linux-2.4.19-pre5-mjc/include/sound/util_mem.h
--- linux/include/sound/util_mem.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/util_mem.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,64 @@
+#ifndef __SOUND_UTIL_MEM_H
+#define __SOUND_UTIL_MEM_H
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Generic memory management routines for soundcard memory allocation
+ *
+ *   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
+ */
+
+typedef struct snd_util_memblk snd_util_memblk_t;
+typedef struct snd_util_memhdr snd_util_memhdr_t;
+typedef unsigned int snd_util_unit_t;
+
+/*
+ * memory block
+ */
+struct snd_util_memblk {
+	snd_util_unit_t size;		/* size of this block */
+	snd_util_unit_t offset;		/* zero-offset of this block */
+	struct list_head list;		/* link */
+};
+
+#define snd_util_memblk_argptr(blk)	(void*)((char*)(blk) + sizeof(snd_util_memblk_t))
+
+/*
+ * memory management information
+ */
+struct snd_util_memhdr {
+	snd_util_unit_t size;		/* size of whole data */
+	struct list_head block;		/* block linked-list header */
+	int nblocks;			/* # of allocated blocks */
+	snd_util_unit_t used;		/* used memory size */
+	int block_extra_size;		/* extra data size of chunk */
+	struct semaphore block_mutex;	/* lock */
+};
+
+/*
+ * prototypes
+ */
+snd_util_memhdr_t *snd_util_memhdr_new(int memsize);
+void snd_util_memhdr_free(snd_util_memhdr_t *hdr);
+snd_util_memblk_t *snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size);
+int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk);
+int snd_util_mem_avail(snd_util_memhdr_t *hdr);
+
+/* functions without mutex */
+snd_util_memblk_t *__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size);
+void __snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk);
+snd_util_memblk_t *__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, struct list_head *prev);
+
+#endif /* __SOUND_UTIL_MEM_H */
diff -Nru linux/include/sound/version.h linux-2.4.19-pre5-mjc/include/sound/version.h
--- linux/include/sound/version.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/version.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3 @@
+/* include/version.h.  Generated automatically by configure.  */
+#define CONFIG_SND_VERSION "0.9.0beta12"
+#define CONFIG_SND_DATE " (Wed Mar 06 07:56:20 2002 UTC)"
diff -Nru linux/include/sound/wavefront.h linux-2.4.19-pre5-mjc/include/sound/wavefront.h
--- linux/include/sound/wavefront.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/wavefront.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,695 @@
+#ifndef __SOUND_WAVEFRONT_H__
+#define __SOUND_WAVEFRONT_H__
+
+/*
+ *  Driver for Turtle Beach Wavefront cards (Maui,Tropez,Tropez+)
+ *
+ *  Copyright (c) by Paul Barton-Davis <pbd@op.net>
+ *
+ *   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
+ */
+
+#if (!defined(__GNUC__) && !defined(__GNUG__))
+
+     You will not be able to compile this file correctly without gcc, because
+     it is necessary to pack the "wavefront_alias" structure to a size
+     of 22 bytes, corresponding to 16-bit alignment (as would have been
+     the case on the original platform, MS-DOS). If this is not done,
+     then WavePatch-format files cannot be read/written correctly.
+     The method used to do this here ("__attribute__((packed)") is
+     completely compiler dependent.
+     
+     All other wavefront_* types end up aligned to 32 bit values and
+     still have the same (correct) size.
+
+#else
+
+     /* However, note that as of G++ 2.7.3.2, g++ was unable to
+	correctly parse *type* __attribute__ tags. It will do the
+	right thing if we use the "packed" attribute on each struct
+	member, which has the same semantics anyway. 
+     */
+
+#endif /* __GNUC__ */
+
+/***************************** WARNING ********************************
+  PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO 
+  BE USED WITH EITHER C *OR* C++.
+ **********************************************************************/
+
+#ifndef NUM_MIDIKEYS 
+#define NUM_MIDIKEYS 128
+#endif  /* NUM_MIDIKEYS */
+
+#ifndef NUM_MIDICHANNELS
+#define NUM_MIDICHANNELS 16
+#endif  /* NUM_MIDICHANNELS */
+
+/* These are very useful/important. the original wavefront interface
+   was developed on a 16 bit system, where sizeof(int) = 2
+   bytes. Defining things like this makes the code much more portable, and
+   easier to understand without having to toggle back and forth
+   between a 16-bit view of the world and a 32-bit one. 
+ */   
+
+#ifndef __KERNEL__
+/* keep them for compatibility */
+typedef short s16;
+typedef unsigned short u16;
+typedef int s32;
+typedef unsigned int u32;
+typedef char s8;
+typedef unsigned char u8;
+typedef s16 INT16;
+typedef u16 UINT16;
+typedef s32 INT32;
+typedef u32 UINT32;
+typedef s8 CHAR8;
+typedef u8 UCHAR8;
+#endif
+
+/* Pseudo-commands not part of the WaveFront command set.
+   These are used for various driver controls and direct
+   hardware control.
+ */
+
+#define WFC_DEBUG_DRIVER                0
+#define WFC_FX_IOCTL                    1
+#define WFC_PATCH_STATUS                2
+#define WFC_PROGRAM_STATUS              3
+#define WFC_SAMPLE_STATUS               4
+#define WFC_DISABLE_INTERRUPTS          5
+#define WFC_ENABLE_INTERRUPTS           6
+#define WFC_INTERRUPT_STATUS            7
+#define WFC_ROMSAMPLES_RDONLY           8
+#define WFC_IDENTIFY_SLOT_TYPE          9
+
+/* Wavefront synth commands
+ */
+
+#define WFC_DOWNLOAD_SAMPLE		0x80
+#define WFC_DOWNLOAD_BLOCK		0x81
+#define WFC_DOWNLOAD_MULTISAMPLE	0x82
+#define WFC_DOWNLOAD_SAMPLE_ALIAS	0x83
+#define WFC_DELETE_SAMPLE		0x84
+#define WFC_REPORT_FREE_MEMORY		0x85
+#define WFC_DOWNLOAD_PATCH		0x86
+#define WFC_DOWNLOAD_PROGRAM		0x87
+#define WFC_SET_SYNTHVOL		0x89
+#define WFC_SET_NVOICES			0x8B
+#define WFC_DOWNLOAD_DRUM		0x90
+#define WFC_GET_SYNTHVOL		0x92
+#define WFC_GET_NVOICES			0x94
+#define WFC_DISABLE_CHANNEL		0x9A
+#define WFC_ENABLE_CHANNEL		0x9B
+#define WFC_MISYNTH_OFF			0x9D
+#define WFC_MISYNTH_ON			0x9E
+#define WFC_FIRMWARE_VERSION		0x9F
+#define WFC_GET_NSAMPLES		0xA0
+#define WFC_DISABLE_DRUM_PROGRAM	0xA2
+#define WFC_UPLOAD_PATCH		0xA3
+#define WFC_UPLOAD_PROGRAM		0xA4
+#define WFC_SET_TUNING			0xA6
+#define WFC_GET_TUNING			0xA7
+#define WFC_VMIDI_ON			0xA8
+#define WFC_VMIDI_OFF			0xA9
+#define WFC_MIDI_STATUS			0xAA
+#define WFC_GET_CHANNEL_STATUS		0xAB
+#define WFC_DOWNLOAD_SAMPLE_HEADER	0xAC
+#define WFC_UPLOAD_SAMPLE_HEADER	0xAD
+#define WFC_UPLOAD_MULTISAMPLE		0xAE
+#define WFC_UPLOAD_SAMPLE_ALIAS		0xAF
+#define WFC_IDENTIFY_SAMPLE_TYPE	0xB0
+#define WFC_DOWNLOAD_EDRUM_PROGRAM	0xB1
+#define WFC_UPLOAD_EDRUM_PROGRAM	0xB2
+#define WFC_SET_EDRUM_CHANNEL		0xB3
+#define WFC_INSTOUT_LEVELS		0xB4
+#define WFC_PEAKOUT_LEVELS		0xB5
+#define WFC_REPORT_CHANNEL_PROGRAMS	0xB6
+#define WFC_HARDWARE_VERSION		0xCF
+#define WFC_UPLOAD_SAMPLE_PARAMS	0xD7
+#define WFC_DOWNLOAD_OS			0xF1
+#define WFC_NOOP                        0xFF
+
+#define WF_MAX_SAMPLE   512
+#define WF_MAX_PATCH    256
+#define WF_MAX_PROGRAM  128
+
+#define WF_SECTION_MAX  44   /* longest OS section length */
+
+/* # of bytes we send to the board when sending it various kinds of
+   substantive data, such as samples, patches and programs.
+*/
+
+#define WF_PROGRAM_BYTES 32
+#define WF_PATCH_BYTES 132
+#define WF_SAMPLE_BYTES 27
+#define WF_SAMPLE_HDR_BYTES 25
+#define WF_ALIAS_BYTES 25
+#define WF_DRUM_BYTES 9
+#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */
+
+#define WF_ACK     0x80
+#define WF_DMA_ACK 0x81
+
+/* OR-values for MIDI status bits */
+
+#define WF_MIDI_VIRTUAL_ENABLED 0x1
+#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2
+#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4
+
+/* slot indexes for struct address_info: makes code a little more mnemonic */
+
+#define WF_SYNTH_SLOT         0
+#define WF_INTERNAL_MIDI_SLOT 1
+#define WF_EXTERNAL_MIDI_SLOT 2
+
+/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401
+   emulation. Note these NEVER show up in output from the device and
+   should NEVER be used in input unless Virtual MIDI mode has been 
+   disabled. If they do show up as input, the results are unpredictable.
+*/
+
+#define WF_EXTERNAL_SWITCH  0xFD
+#define WF_INTERNAL_SWITCH  0xF9
+
+/* Debugging flags */
+
+#define WF_DEBUG_CMD 0x1
+#define WF_DEBUG_DATA 0x2
+#define WF_DEBUG_LOAD_PATCH 0x4
+#define WF_DEBUG_IO 0x8
+
+/* WavePatch file format stuff */
+
+#define WF_WAVEPATCH_VERSION     120;  /*  Current version number (1.2)  */
+#define WF_MAX_COMMENT           64    /*  Comment length */
+#define WF_NUM_LAYERS            4
+#define WF_NAME_LENGTH           32
+#define WF_SOURCE_LENGTH         260
+
+#define BankFileID     "Bank"
+#define DrumkitFileID  "DrumKit"
+#define ProgramFileID  "Program"
+
+struct wf_envelope
+{
+    u8 attack_time:7;
+    u8 Unused1:1;
+
+    u8 decay1_time:7;
+    u8 Unused2:1;
+
+    u8 decay2_time:7;
+    u8 Unused3:1;
+
+    u8 sustain_time:7;
+    u8 Unused4:1;
+
+    u8 release_time:7;
+    u8 Unused5:1;
+
+    u8 release2_time:7;
+    u8 Unused6:1;
+
+    s8 attack_level;
+    s8 decay1_level;
+    s8 decay2_level;
+    s8 sustain_level;
+    s8 release_level;
+
+    u8 attack_velocity:7;
+    u8 Unused7:1;
+
+    u8 volume_velocity:7;
+    u8 Unused8:1;
+
+    u8 keyboard_scaling:7;
+    u8 Unused9:1;
+};
+typedef struct wf_envelope wavefront_envelope;
+
+struct wf_lfo
+{
+    u8 sample_number;
+
+    u8 frequency:7;
+    u8 Unused1:1;
+
+    u8 am_src:4;
+    u8 fm_src:4;
+
+    s8 fm_amount;
+    s8 am_amount;
+    s8 start_level;
+    s8 end_level;
+
+    u8 ramp_delay:7;
+    u8 wave_restart:1; /* for LFO2 only */
+
+    u8 ramp_time:7;
+    u8 Unused2:1;
+};
+typedef struct wf_lfo wavefront_lfo;
+
+struct wf_patch
+{
+    s16  frequency_bias;         /*  ** THIS IS IN MOTOROLA FORMAT!! ** */
+
+    u8 amplitude_bias:7;
+    u8 Unused1:1;
+
+    u8 portamento:7;
+    u8 Unused2:1;
+
+    u8 sample_number;
+
+    u8 pitch_bend:4;
+    u8 sample_msb:1;
+    u8 Unused3:3;
+
+    u8 mono:1;
+    u8 retrigger:1;
+    u8 nohold:1;
+    u8 restart:1;
+    u8 filterconfig:2; /* SDK says "not used" */
+    u8 reuse:1;
+    u8 reset_lfo:1;    
+
+    u8 fm_src2:4;
+    u8 fm_src1:4;   
+
+    s8 fm_amount1;
+    s8 fm_amount2;
+
+    u8 am_src:4;
+    u8 Unused4:4;
+
+    s8 am_amount;
+
+    u8 fc1_mode:4;
+    u8 fc2_mode:4;
+
+    s8 fc1_mod_amount;
+    s8 fc1_keyboard_scaling;
+    s8 fc1_bias;
+    s8 fc2_mod_amount;
+    s8 fc2_keyboard_scaling;
+    s8 fc2_bias;
+
+    u8 randomizer:7;
+    u8 Unused5:1;
+
+    struct wf_envelope envelope1;
+    struct wf_envelope envelope2;
+    struct wf_lfo lfo1;
+    struct wf_lfo lfo2;
+};
+typedef struct wf_patch wavefront_patch;
+
+struct wf_layer
+{
+    u8 patch_number;
+
+    u8 mix_level:7;
+    u8 mute:1;
+
+    u8 split_point:7;
+    u8 play_below:1;
+
+    u8 pan_mod_src:2;
+    u8 pan_or_mod:1;
+    u8 pan:4;
+    u8 split_type:1;
+};
+typedef struct wf_layer wavefront_layer;
+
+struct wf_program
+{
+    struct wf_layer layer[WF_NUM_LAYERS];
+};
+typedef struct wf_program wavefront_program;
+
+struct wf_sample_offset
+{
+    s32 Fraction:4;
+    s32 Integer:20;
+    s32 Unused:8;
+};
+typedef struct wf_sample_offset wavefront_sample_offset;          
+     
+/* Sample slot types */
+
+#define WF_ST_SAMPLE      0
+#define WF_ST_MULTISAMPLE 1
+#define WF_ST_ALIAS       2
+#define WF_ST_EMPTY       3
+
+/* pseudo's */
+
+#define WF_ST_DRUM        4
+#define WF_ST_PROGRAM     5
+#define WF_ST_PATCH       6
+#define WF_ST_SAMPLEHDR   7
+
+#define WF_ST_MASK        0xf
+
+/* Flags for slot status. These occupy the upper bits of the same byte
+   as a sample type.
+*/
+
+#define WF_SLOT_USED      0x80   /* XXX don't rely on this being accurate */
+#define WF_SLOT_FILLED    0x40
+#define WF_SLOT_ROM       0x20
+
+#define WF_SLOT_MASK      0xf0
+
+/* channel constants */
+
+#define WF_CH_MONO  0
+#define WF_CH_LEFT  1
+#define WF_CH_RIGHT 2
+
+/* Sample formats */
+
+#define LINEAR_16BIT 0
+#define WHITE_NOISE  1
+#define LINEAR_8BIT  2
+#define MULAW_8BIT   3
+
+#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2)
+
+
+/* 
+
+  Because most/all of the sample data we pass in via pointers has
+  never been copied (just mmap-ed into user space straight from the
+  disk), it would be nice to allow handling of multi-channel sample
+  data without forcing user-level extraction of the relevant bytes.
+  
+  So, we need a way of specifying which channel to use (the WaveFront
+  only handles mono samples in a given slot), and the only way to do
+  this without using some struct other than wavefront_sample as the
+  interface is the awful hack of using the unused bits in a
+  wavefront_sample:
+  
+  Val      Meaning
+  ---      -------
+  0        no channel selection (use channel 1, sample is MONO)
+  1        use first channel, and skip one
+  2        use second channel, and skip one
+  3        use third channel, and skip two
+  4        use fourth channel, skip three
+  5        use fifth channel, skip four
+  6        use six channel, skip five
+
+
+  This can handle up to 4 channels, and anyone downloading >4 channels
+  of sample data just to select one of them needs to find some tools
+  like sox ...
+
+  NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is 
+  important.
+
+*/
+
+#define WF_SET_CHANNEL(samp,chn) \
+ (samp)->Unused1 = chn & 0x1; \
+ (samp)->Unused2 = chn & 0x2; \
+ (samp)->Unused3 = chn & 0x4 
+  
+#define WF_GET_CHANNEL(samp) \
+  (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1)
+  
+typedef struct wf_sample {
+    struct wf_sample_offset sampleStartOffset;
+    struct wf_sample_offset loopStartOffset;
+    struct wf_sample_offset loopEndOffset;
+    struct wf_sample_offset sampleEndOffset;
+    s16 FrequencyBias;
+    u8 SampleResolution:2;  /* sample_format */
+    u8 Unused1:1;
+    u8 Loop:1;
+    u8 Bidirectional:1;
+    u8 Unused2:1;
+    u8 Reverse:1;
+    u8 Unused3:1;
+} wavefront_sample;
+
+typedef struct wf_multisample {
+    s16 NumberOfSamples;   /* log2 of the number of samples */
+    s16 SampleNumber[NUM_MIDIKEYS];
+} wavefront_multisample;
+
+typedef struct wf_alias {
+    s16 OriginalSample __attribute__ ((packed));
+
+    struct wf_sample_offset sampleStartOffset __attribute__ ((packed));
+    struct wf_sample_offset loopStartOffset __attribute__ ((packed));
+    struct wf_sample_offset sampleEndOffset __attribute__ ((packed));
+    struct wf_sample_offset loopEndOffset __attribute__ ((packed));
+
+    s16  FrequencyBias __attribute__ ((packed));
+
+    u8 SampleResolution:2  __attribute__ ((packed));
+    u8 Unused1:1  __attribute__ ((packed));
+    u8 Loop:1 __attribute__ ((packed));
+    u8 Bidirectional:1  __attribute__ ((packed));
+    u8 Unused2:1 __attribute__ ((packed));
+    u8 Reverse:1 __attribute__ ((packed));
+    u8 Unused3:1 __attribute__ ((packed)); 
+    
+    /* This structure is meant to be padded only to 16 bits on their
+       original. Of course, whoever wrote their documentation didn't
+       realize that sizeof(struct) can be >=
+       sum(sizeof(struct-fields)) and so thought that giving a C level
+       description of the structs used in WavePatch files was
+       sufficient. I suppose it was, as long as you remember the 
+       standard 16->32 bit issues.
+    */
+
+    u8 sixteen_bit_padding __attribute__ ((packed));
+} wavefront_alias;
+
+typedef struct wf_drum {
+    u8 PatchNumber;
+    u8 MixLevel:7;
+    u8 Unmute:1;
+    u8 Group:4;
+    u8 Unused1:4;
+    u8 PanModSource:2;
+    u8 PanModulated:1;
+    u8 PanAmount:4;
+    u8 Unused2:1;
+} wavefront_drum;
+
+typedef struct wf_drumkit {
+    struct wf_drum drum[NUM_MIDIKEYS];
+} wavefront_drumkit;
+
+typedef struct wf_channel_programs {
+    u8 Program[NUM_MIDICHANNELS];
+} wavefront_channel_programs;
+
+/* How to get MIDI channel status from the data returned by
+   a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs)
+*/
+
+#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7))
+
+typedef union wf_any {
+    wavefront_sample s;
+    wavefront_multisample ms;
+    wavefront_alias a;
+    wavefront_program pr;
+    wavefront_patch p;
+    wavefront_drum d;
+} wavefront_any;
+
+/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h
+   might work for other wave-table based patch loading situations.
+   Alas, his fears were correct. The WaveFront doesn't even come with
+   just "patches", but several different kind of structures that
+   control the sound generation process.
+ */
+
+typedef struct wf_patch_info {
+    
+    /* the first two fields are used by the OSS "patch loading" interface
+       only, and are unused by the current user-level library.
+    */
+
+    s16   key;               /* Use WAVEFRONT_PATCH here */
+    u16  devno;             /* fill in when sending */
+    u8  subkey;            /* WF_ST_{SAMPLE,ALIAS,etc.} */
+
+#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999
+
+    u16  number;            /* patch/sample/prog number */
+
+    u32  size;              /* size of any data included in 
+				  one of the fields in `hdrptr', or
+				  as `dataptr'.
+
+				  NOTE: for actual samples, this is
+				  the size of the *SELECTED CHANNEL*
+				  even if more data is actually available.
+				  
+				  So, a stereo sample (2 channels) of
+				  6000 bytes total has `size' = 3000.
+
+				  See the macros and comments for
+				  WF_{GET,SET}_CHANNEL above.
+
+			       */
+    wavefront_any *hdrptr;      /* user-space ptr to hdr bytes */
+    u16 *dataptr;            /* actual sample data */
+
+    wavefront_any hdr;          /* kernel-space copy of hdr bytes */         
+} wavefront_patch_info;
+
+/* The maximum number of bytes we will ever move to or from user space
+   in response to a WFC_* command.  This obviously doesn't cover
+   actual sample data.
+*/
+
+#define WF_MAX_READ sizeof(wavefront_multisample)
+#define WF_MAX_WRITE sizeof(wavefront_multisample)
+
+/*
+   This allows us to execute any WF command except the download/upload
+   ones, which are handled differently due to copyin/copyout issues as
+   well as data-nybbling to/from the card.
+ */
+
+typedef struct wavefront_control {
+    int cmd;                           /* WFC_* */
+    char status;                       /* return status to user-space */
+    unsigned char rbuf[WF_MAX_READ];   /* bytes read from card */
+    unsigned char wbuf[WF_MAX_WRITE];  /* bytes written to card */
+} wavefront_control;
+
+#define WFCTL_WFCMD    0x1
+#define WFCTL_LOAD_SPP 0x2
+
+/* Modulator table */
+
+#define WF_MOD_LFO1      0
+#define WF_MOD_LFO2      1
+#define WF_MOD_ENV1      2
+#define WF_MOD_ENV2      3
+#define WF_MOD_KEYBOARD  4
+#define WF_MOD_LOGKEY    5
+#define WF_MOD_VELOCITY  6
+#define WF_MOD_LOGVEL    7
+#define WF_MOD_RANDOM    8
+#define WF_MOD_PRESSURE  9
+#define WF_MOD_MOD_WHEEL 10
+#define WF_MOD_1         WF_MOD_MOD_WHEEL 
+#define WF_MOD_BREATH    11
+#define WF_MOD_2         WF_MOD_BREATH
+#define WF_MOD_FOOT      12
+#define WF_MOD_4         WF_MOD_FOOT
+#define WF_MOD_VOLUME    13
+#define WF_MOD_7         WF_MOD_VOLUME
+#define WF_MOD_PAN       14
+#define WF_MOD_10        WF_MOD_PAN
+#define WF_MOD_EXPR      15
+#define WF_MOD_11        WF_MOD_EXPR
+
+/* FX-related material */
+
+typedef struct wf_fx_info {
+    int request;             /* see list below */
+    long data[4];             /* we don't need much */
+} wavefront_fx_info;
+
+/* support for each of these will be forthcoming once I or someone 
+   else has figured out which of the addresses on page 6 and page 7 of 
+   the YSS225 control each parameter. Incidentally, these come from
+   the Windows driver interface, but again, Turtle Beach didn't
+   document the API to use them.
+*/
+
+#define WFFX_SETOUTGAIN		        0
+#define WFFX_SETSTEREOOUTGAIN		1
+#define WFFX_SETREVERBIN1GAIN		2
+#define WFFX_SETREVERBIN2GAIN		3
+#define WFFX_SETREVERBIN3GAIN		4
+#define WFFX_SETCHORUSINPORT		5
+#define WFFX_SETREVERBIN1PORT		6
+#define WFFX_SETREVERBIN2PORT		7
+#define WFFX_SETREVERBIN3PORT		8
+#define WFFX_SETEFFECTPORT		9
+#define WFFX_SETAUXPORT		        10
+#define WFFX_SETREVERBTYPE		11
+#define WFFX_SETREVERBDELAY		12
+#define WFFX_SETCHORUSLFO		13
+#define WFFX_SETCHORUSPMD		14
+#define WFFX_SETCHORUSAMD		15
+#define WFFX_SETEFFECT		        16
+#define WFFX_SETBASEALL		        17
+#define WFFX_SETREVERBALL		18
+#define WFFX_SETCHORUSALL		20
+#define WFFX_SETREVERBDEF		22
+#define WFFX_SETCHORUSDEF		23
+#define WFFX_DELAYSETINGAIN		24
+#define WFFX_DELAYSETFBGAIN	        25
+#define WFFX_DELAYSETFBLPF		26
+#define WFFX_DELAYSETGAIN		27
+#define WFFX_DELAYSETTIME		28
+#define WFFX_DELAYSETFBTIME		29
+#define WFFX_DELAYSETALL		30
+#define WFFX_DELAYSETDEF		32
+#define WFFX_SDELAYSETINGAIN		33
+#define WFFX_SDELAYSETFBGAIN		34
+#define WFFX_SDELAYSETFBLPF		35
+#define WFFX_SDELAYSETGAIN		36
+#define WFFX_SDELAYSETTIME		37
+#define WFFX_SDELAYSETFBTIME		38
+#define WFFX_SDELAYSETALL		39
+#define WFFX_SDELAYSETDEF		41
+#define WFFX_DEQSETINGAIN		42
+#define WFFX_DEQSETFILTER		43
+#define WFFX_DEQSETALL		        44
+#define WFFX_DEQSETDEF		        46
+#define WFFX_MUTE		        47
+#define WFFX_FLANGESETBALANCE	        48	
+#define WFFX_FLANGESETDELAY		49
+#define WFFX_FLANGESETDWFFX_TH		50
+#define WFFX_FLANGESETFBGAIN		51
+#define WFFX_FLANGESETINGAIN		52
+#define WFFX_FLANGESETLFO		53
+#define WFFX_FLANGESETALL		54
+#define WFFX_FLANGESETDEF		56
+#define WFFX_PITCHSETSHIFT		57
+#define WFFX_PITCHSETBALANCE		58
+#define WFFX_PITCHSETALL		59
+#define WFFX_PITCHSETDEF		61
+#define WFFX_SRSSETINGAIN		62
+#define WFFX_SRSSETSPACE		63
+#define WFFX_SRSSETCENTER		64
+#define WFFX_SRSSETGAIN		        65
+#define WFFX_SRSSETMODE	        	66
+#define WFFX_SRSSETDEF		        68
+
+/* Allow direct user-space control over FX memory/coefficient data.
+   In theory this could be used to download the FX microprogram,
+   but it would be a little slower, and involve some wierd code.
+ */
+
+#define WFFX_MEMSET              69
+
+#endif /* __SOUND_WAVEFRONT_H__ */
diff -Nru linux/include/sound/wavefront_fx.h linux-2.4.19-pre5-mjc/include/sound/wavefront_fx.h
--- linux/include/sound/wavefront_fx.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/wavefront_fx.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,9 @@
+#ifndef __SOUND_WAVEFRONT_FX_H
+#define __SOUND_WAVEFRONT_FX_H
+
+extern int  snd_wavefront_fx_detect (snd_wavefront_t *);
+extern void snd_wavefront_fx_ioctl  (snd_synth_t *sdev, 
+				     unsigned int cmd, 
+				     unsigned long arg);
+
+#endif  __SOUND_WAVEFRONT_FX_H
diff -Nru linux/include/sound/ymfpci.h linux-2.4.19-pre5-mjc/include/sound/ymfpci.h
--- linux/include/sound/ymfpci.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/ymfpci.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,369 @@
+#ifndef __SOUND_YMFPCI_H
+#define __SOUND_YMFPCI_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for Yahama YMF724/740/744/754 chips
+ *
+ *
+ *   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
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "ac97_codec.h"
+
+#ifndef PCI_VENDOR_ID_YAMAHA
+#define PCI_VENDOR_ID_YAMAHA            0x1073
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_724
+#define PCI_DEVICE_ID_YAMAHA_724	0x0004
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_724F
+#define PCI_DEVICE_ID_YAMAHA_724F	0x000d
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_740
+#define PCI_DEVICE_ID_YAMAHA_740	0x000a
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_740C
+#define PCI_DEVICE_ID_YAMAHA_740C	0x000c
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_744
+#define PCI_DEVICE_ID_YAMAHA_744	0x0010
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_754
+#define PCI_DEVICE_ID_YAMAHA_754	0x0012
+#endif
+
+/*
+ *  Direct registers
+ */
+
+#define YMFREG(chip, reg)		(chip->port + YDSXGR_##reg)
+
+#define	YDSXGR_INTFLAG			0x0004
+#define	YDSXGR_ACTIVITY			0x0006
+#define	YDSXGR_GLOBALCTRL		0x0008
+#define	YDSXGR_ZVCTRL			0x000A
+#define	YDSXGR_TIMERCTRL		0x0010
+#define	YDSXGR_TIMERCOUNT		0x0012
+#define	YDSXGR_SPDIFOUTCTRL		0x0018
+#define	YDSXGR_SPDIFOUTSTATUS		0x001C
+#define	YDSXGR_EEPROMCTRL		0x0020
+#define	YDSXGR_SPDIFINCTRL		0x0034
+#define	YDSXGR_SPDIFINSTATUS		0x0038
+#define	YDSXGR_DSPPROGRAMDL		0x0048
+#define	YDSXGR_DLCNTRL			0x004C
+#define	YDSXGR_GPIOININTFLAG		0x0050
+#define	YDSXGR_GPIOININTENABLE		0x0052
+#define	YDSXGR_GPIOINSTATUS		0x0054
+#define	YDSXGR_GPIOOUTCTRL		0x0056
+#define	YDSXGR_GPIOFUNCENABLE		0x0058
+#define	YDSXGR_GPIOTYPECONFIG		0x005A
+#define	YDSXGR_AC97CMDDATA		0x0060
+#define	YDSXGR_AC97CMDADR		0x0062
+#define	YDSXGR_PRISTATUSDATA		0x0064
+#define	YDSXGR_PRISTATUSADR		0x0066
+#define	YDSXGR_SECSTATUSDATA		0x0068
+#define	YDSXGR_SECSTATUSADR		0x006A
+#define	YDSXGR_SECCONFIG		0x0070
+#define	YDSXGR_LEGACYOUTVOL		0x0080
+#define	YDSXGR_LEGACYOUTVOLL		0x0080
+#define	YDSXGR_LEGACYOUTVOLR		0x0082
+#define	YDSXGR_NATIVEDACOUTVOL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLR		0x0086
+#define	YDSXGR_ZVOUTVOL			0x0088
+#define	YDSXGR_ZVOUTVOLL		0x0088
+#define	YDSXGR_ZVOUTVOLR		0x008A
+#define	YDSXGR_SECADCOUTVOL		0x008C
+#define	YDSXGR_SECADCOUTVOLL		0x008C
+#define	YDSXGR_SECADCOUTVOLR		0x008E
+#define	YDSXGR_PRIADCOUTVOL		0x0090
+#define	YDSXGR_PRIADCOUTVOLL		0x0090
+#define	YDSXGR_PRIADCOUTVOLR		0x0092
+#define	YDSXGR_LEGACYLOOPVOL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLR		0x0096
+#define	YDSXGR_NATIVEDACLOOPVOL		0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLL	0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLR	0x009A
+#define	YDSXGR_ZVLOOPVOL		0x009C
+#define	YDSXGR_ZVLOOPVOLL		0x009E
+#define	YDSXGR_ZVLOOPVOLR		0x009E
+#define	YDSXGR_SECADCLOOPVOL		0x00A0
+#define	YDSXGR_SECADCLOOPVOLL		0x00A0
+#define	YDSXGR_SECADCLOOPVOLR		0x00A2
+#define	YDSXGR_PRIADCLOOPVOL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLR		0x00A6
+#define	YDSXGR_NATIVEADCINVOL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLR		0x00AA
+#define	YDSXGR_NATIVEDACINVOL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLR		0x00AE
+#define	YDSXGR_BUF441OUTVOL		0x00B0
+#define	YDSXGR_BUF441OUTVOLL		0x00B0
+#define	YDSXGR_BUF441OUTVOLR		0x00B2
+#define	YDSXGR_BUF441LOOPVOL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLR		0x00B6
+#define	YDSXGR_SPDIFOUTVOL		0x00B8
+#define	YDSXGR_SPDIFOUTVOLL		0x00B8
+#define	YDSXGR_SPDIFOUTVOLR		0x00BA
+#define	YDSXGR_SPDIFLOOPVOL		0x00BC
+#define	YDSXGR_SPDIFLOOPVOLL		0x00BC
+#define	YDSXGR_SPDIFLOOPVOLR		0x00BE
+#define	YDSXGR_ADCSLOTSR		0x00C0
+#define	YDSXGR_RECSLOTSR		0x00C4
+#define	YDSXGR_ADCFORMAT		0x00C8
+#define	YDSXGR_RECFORMAT		0x00CC
+#define	YDSXGR_P44SLOTSR		0x00D0
+#define	YDSXGR_STATUS			0x0100
+#define	YDSXGR_CTRLSELECT		0x0104
+#define	YDSXGR_MODE			0x0108
+#define	YDSXGR_SAMPLECOUNT		0x010C
+#define	YDSXGR_NUMOFSAMPLES		0x0110
+#define	YDSXGR_CONFIG			0x0114
+#define	YDSXGR_PLAYCTRLSIZE		0x0140
+#define	YDSXGR_RECCTRLSIZE		0x0144
+#define	YDSXGR_EFFCTRLSIZE		0x0148
+#define	YDSXGR_WORKSIZE			0x014C
+#define	YDSXGR_MAPOFREC			0x0150
+#define	YDSXGR_MAPOFEFFECT		0x0154
+#define	YDSXGR_PLAYCTRLBASE		0x0158
+#define	YDSXGR_RECCTRLBASE		0x015C
+#define	YDSXGR_EFFCTRLBASE		0x0160
+#define	YDSXGR_WORKBASE			0x0164
+#define	YDSXGR_DSPINSTRAM		0x1000
+#define	YDSXGR_CTRLINSTRAM		0x4000
+
+#define YDSXG_AC97READCMD		0x8000
+#define YDSXG_AC97WRITECMD		0x0000
+
+#define PCIR_DSXG_LEGACY		0x40
+#define PCIR_DSXG_ELEGACY		0x42
+#define PCIR_DSXG_CTRL			0x48
+#define PCIR_DSXG_PWRCTRL1		0x4a
+#define PCIR_DSXG_PWRCTRL2		0x4e
+#define PCIR_DSXG_FMBASE		0x60
+#define PCIR_DSXG_SBBASE		0x62
+#define PCIR_DSXG_MPU401BASE		0x64
+#define PCIR_DSXG_JOYBASE		0x66
+
+#define YDSXG_DSPLENGTH			0x0080
+#define YDSXG_CTRLLENGTH		0x3000
+
+#define YDSXG_DEFAULT_WORK_SIZE		0x0400
+
+#define YDSXG_PLAYBACK_VOICES		64
+#define YDSXG_CAPTURE_VOICES		2
+#define YDSXG_EFFECT_VOICES		5
+
+/*
+ *
+ */
+
+typedef struct _snd_ymfpci_playback_bank {
+	u32 format;
+	u32 loop_default;
+	u32 base;			/* 32-bit address */
+	u32 loop_start;			/* 32-bit offset */
+	u32 loop_end;			/* 32-bit offset */
+	u32 loop_frac;			/* 8-bit fraction - loop_start */
+	u32 delta_end;			/* pitch delta end */
+	u32 lpfK_end;
+	u32 eg_gain_end;
+	u32 left_gain_end;
+	u32 right_gain_end;
+	u32 eff1_gain_end;
+	u32 eff2_gain_end;
+	u32 eff3_gain_end;
+	u32 lpfQ;
+	u32 status;
+	u32 num_of_frames;
+	u32 loop_count;
+	u32 start;
+	u32 start_frac;
+	u32 delta;
+	u32 lpfK;
+	u32 eg_gain;
+	u32 left_gain;
+	u32 right_gain;
+	u32 eff1_gain;
+	u32 eff2_gain;
+	u32 eff3_gain;
+	u32 lpfD1;
+	u32 lpfD2;
+} snd_ymfpci_playback_bank_t;
+
+typedef struct _snd_ymfpci_capture_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 num_of_loops;		/* counter */
+} snd_ymfpci_capture_bank_t;
+
+typedef struct _snd_ymfpci_effect_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 temp;
+} snd_ymfpci_effect_bank_t;
+
+typedef struct _snd_ymfpci_voice ymfpci_voice_t;
+typedef struct _snd_ymfpci_pcm ymfpci_pcm_t;
+typedef struct _snd_ymfpci ymfpci_t;
+
+typedef enum {
+	YMFPCI_PCM,
+	YMFPCI_SYNTH,
+	YMFPCI_MIDI
+} ymfpci_voice_type_t;
+
+struct _snd_ymfpci_voice {
+	ymfpci_t *chip;
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth: 1,
+	    midi: 1;
+	snd_ymfpci_playback_bank_t *bank;
+	dma_addr_t bank_addr;
+	void (*interrupt)(ymfpci_t *chip, ymfpci_voice_t *voice);
+	ymfpci_pcm_t *ypcm;
+};
+
+typedef enum {
+	PLAYBACK_VOICE,
+	CAPTURE_REC,
+	CAPTURE_AC97,
+	EFFECT_DRY_LEFT,
+	EFFECT_DRY_RIGHT,
+	EFFECT_EFF1,
+	EFFECT_EFF2,
+	EFFECT_EFF3
+} snd_ymfpci_pcm_type_t;
+
+struct _snd_ymfpci_pcm {
+	ymfpci_t *chip;
+	snd_ymfpci_pcm_type_t type;
+	snd_pcm_substream_t *substream;
+	ymfpci_voice_t *voices[2];	/* playback only */
+	int running: 1,
+	    spdif: 1,
+	    mode4ch : 1;	
+	u32 period_size;		/* cached from runtime->period_size */
+	u32 buffer_size;		/* cached from runtime->buffer_size */
+	u32 period_pos;
+	u32 last_pos;
+	u32 capture_bank_number;
+	u32 shift;
+};
+
+struct _snd_ymfpci {
+	int irq;
+
+	unsigned int device_id;	/* PCI device ID */
+	unsigned int rev;	/* PCI revision */
+	unsigned long reg_area_phys;
+	unsigned long reg_area_virt;
+	struct resource *res_reg_area;
+
+	unsigned short old_legacy_ctrl;
+	unsigned int joystick_port;
+
+	void *work_ptr;
+	dma_addr_t work_ptr_addr;
+	unsigned long work_ptr_size;
+
+	unsigned int bank_size_playback;
+	unsigned int bank_size_capture;
+	unsigned int bank_size_effect;
+	unsigned int work_size;
+
+	void *bank_base_playback;
+	void *bank_base_capture;
+	void *bank_base_effect;
+	void *work_base;
+	dma_addr_t bank_base_playback_addr;
+	dma_addr_t bank_base_capture_addr;
+	dma_addr_t bank_base_effect_addr;
+	dma_addr_t work_base_addr;
+	void *ac3_tmp_base;
+	dma_addr_t ac3_tmp_base_addr;
+
+	u32 *ctrl_playback;
+	snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
+	snd_ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2];
+	snd_ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2];
+
+	int start_count;
+
+	u32 active_bank;
+	ymfpci_voice_t voices[64];
+
+	ac97_t *ac97;
+	snd_rawmidi_t *rawmidi;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm2;
+	snd_pcm_t *pcm_spdif;
+	snd_pcm_t *pcm_4ch;
+	snd_pcm_substream_t *capture_substream[YDSXG_CAPTURE_VOICES];
+	snd_pcm_substream_t *effect_substream[YDSXG_EFFECT_VOICES];
+	snd_kcontrol_t *ctl_vol_recsrc;
+	snd_kcontrol_t *ctl_vol_adcrec;
+	snd_kcontrol_t *ctl_vol_spdifrec;
+	unsigned short spdif_bits, spdif_pcm_bits;
+	snd_kcontrol_t *spdif_pcm_ctl;
+
+	spinlock_t reg_lock;
+	spinlock_t voice_lock;
+	wait_queue_head_t interrupt_sleep;
+	atomic_t interrupt_sleep_count;
+	snd_info_entry_t *proc_entry;
+
+#ifdef CONFIG_PM
+	u32 *saved_regs;
+	u32 saved_ydsxgr_mode;
+#endif
+};
+
+int snd_ymfpci_create(snd_card_t * card,
+		      struct pci_dev *pci,
+		      unsigned short old_legacy_ctrl,
+		      ymfpci_t ** rcodec);
+
+int snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_mixer(ymfpci_t *chip);
+int snd_ymfpci_joystick(ymfpci_t *chip);
+
+int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice);
+int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice);
+
+#ifdef CONFIG_PM
+void snd_ymfpci_suspend(ymfpci_t *chip);
+void snd_ymfpci_resume(ymfpci_t *chip);
+#endif
+
+#endif /* __SOUND_YMFPCI_H */
diff -Nru linux/include/sound/yss225.h linux-2.4.19-pre5-mjc/include/sound/yss225.h
--- linux/include/sound/yss225.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/include/sound/yss225.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,23 @@
+#ifndef __SOUND_YSS225_H
+#define __SOUND_YSS225_H
+
+extern unsigned char page_zero[256];
+extern unsigned char page_one[256];
+extern unsigned char page_two[128];
+extern unsigned char page_three[128];
+extern unsigned char page_four[128];
+extern unsigned char page_six[192];
+extern unsigned char page_seven[256];
+extern unsigned char page_zero_v2[96];
+extern unsigned char page_one_v2[96];
+extern unsigned char page_two_v2[48];
+extern unsigned char page_three_v2[48];
+extern unsigned char page_four_v2[48];
+extern unsigned char page_seven_v2[96];
+extern unsigned char mod_v2[304];
+extern unsigned char coefficients[364];
+extern unsigned char coefficients2[56];
+extern unsigned char coefficients3[404];
+
+
+#endif /* __SOUND_YSS225_H  */
diff -Nru linux/sound/Config.help linux-2.4.19-pre5-mjc/sound/Config.help
--- linux/sound/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/Config.help	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,7 @@
+CONFIG_SOUND_ALSA_PRIME
+  Say 'Y' or 'M' to enable Open Sound System drivers.
+
+CONFIG_SOUND_ALSA_SND
+  Say 'Y' or 'M' to enable Advanced Linux Sound Architecture (ALSA) drivers.
+  You need to install also alsa-lib and alsa-utils packages available on
+  the ALSA website at <http://www.alsa-project.org>.
diff -Nru linux/sound/Config.in linux-2.4.19-pre5-mjc/sound/Config.in
--- linux/sound/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/Config.in	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,32 @@
+# sound/Config.in
+#
+
+mainmenu_option next_comment
+comment 'Open Sound System'
+
+dep_tristate 'Open Sound System' CONFIG_SOUND_ALSA_PRIME $CONFIG_SOUND_ALSA
+if [ "$CONFIG_SOUND_ALSA_PRIME" != "n" ]; then
+  source sound/oss/Config.in
+fi
+
+endmenu
+
+mainmenu_option next_comment
+comment 'Advanced Linux Sound Architecture'
+
+dep_tristate 'Advanced Linux Sound Architecture' CONFIG_SND $CONFIG_SOUND_ALSA
+if [ "$CONFIG_SND" != "n" ]; then
+  source sound/core/Config.in
+  source sound/drivers/Config.in
+fi
+if [ "$CONFIG_SND" != "n" -a "$CONFIG_ISA" = "y" ]; then
+  source sound/isa/Config.in
+fi
+if [ "$CONFIG_SND" != "n" -a "$CONFIG_PCI" = "y" ]; then
+  source sound/pci/Config.in
+fi
+if [ "$CONFIG_SND" != "n" -a "$CONFIG_PPC" = "y" ]; then
+  source sound/ppc/Config.in
+fi
+
+endmenu
diff -Nru linux/sound/Makefile linux-2.4.19-pre5-mjc/sound/Makefile
--- linux/sound/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,61 @@
+# Makefile for the Linux sound card driver
+#
+
+O_TARGET := sound.o
+
+export-objs     := sound_core.o
+
+obj-$(CONFIG_SOUND_ALSA) += soundcore.o
+
+subdir-$(CONFIG_SOUND_ALSA_PRIME) += oss
+
+ifeq ($(CONFIG_SOUND_ALSA_PRIME),y)
+  subdir-m += oss
+  obj-y += oss/sounddrivers.o
+endif
+
+_subdirs := core i2c drivers isa pci ppc synth
+
+subdir-$(CONFIG_SND) += $(_subdirs)
+
+ifeq ($(CONFIG_SND),y)
+  subdir-m += $(_subdirs)
+  obj-y += core/core.o i2c/_i2c.o
+  obj-y += drivers/drivers.o \
+	   drivers/mpu401/_mpu401.o \
+	   drivers/opl3/_opl3.o
+  obj-y += isa/isa.o \
+	   isa/ad1816a/_ad1816a.o \
+           isa/ad1848/_ad1848.o \
+	   isa/cs423x/_cs423x.o \
+	   isa/es1688/_es1688.o \
+	   isa/gus/_gus.o \
+	   isa/opti9xx/_opti9xx.o \
+	   isa/sb/_sb.o \
+	   isa/wavefront/_wavefront.o
+  obj-y += pci/pci.o \
+	   pci/ac97/_ac97.o \
+	   pci/ali5451/_ali5451.o \
+	   pci/cs46xx/_cs46xx.o \
+	   pci/emu10k1/_emu10k1.o \
+	   pci/korg1212/_korg1212.o \
+	   pci/nm256/_nm256.o \
+	   pci/rme9652/_rme9652.o \
+	   pci/trident/_trident.o \
+	   pci/ymfpci/_ymfpci.o
+  obj-y += ppc/ppc.o
+  obj-y += synth/synth.o \
+	   synth/emux/_emux.o
+  obj-y += last.o
+endif
+
+list-multi      := soundcore.o
+
+soundcore-objs  := sound_core.o sound_firmware.o
+
+
+include $(TOPDIR)/Rules.make
+
+
+soundcore.o: $(soundcore-objs)
+	$(LD) -r -o $@ $(soundcore-objs)
diff -Nru linux/sound/core/Config.help linux-2.4.19-pre5-mjc/sound/core/Config.help
--- linux/sound/core/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/Config.help	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,40 @@
+CONFIG_SND_SEQUENCER
+  Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature
+  allows routing and enqueing MIDI events. Events can be processed at given
+  time.
+
+CONFIG_SND_SEQ_DUMMY
+  Say 'Y' or 'M' to enable dummy sequencer client. This client is a simple
+  midi-through client. All normal input events are redirected to output port
+  immediately.
+
+CONFIG_SND_OSSEMUL
+  Say 'Y' to enable OSS (Open Sound System) API emulation code.
+
+CONFIG_SND_MIXER_OSS
+  Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*).
+
+CONFIG_SND_PCM_OSS
+  Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*).
+
+CONFIG_SND_SEQUENCER_OSS
+  Say 'Y' or 'M' to enable OSS sequencer emulation (both /dev/sequencer and
+  /dev/music interfaces).
+
+CONFIG_SND_RTCTIMER
+  Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC
+  timer as precise timing source and maps the RTC timer to the ALSA's timer
+  interface. ALSA sequencer code can also use this timing source.
+
+CONFIG_SND_VERBOSE_PRINTK
+  Say 'Y' to enable verbose log messages. These messages will help to
+  identify source file and position containing printed messages.
+
+CONFIG_SND_DEBUG
+  Say 'Y' to enable ALSA debug code.
+
+CONFIG_SND_DEBUG_MEMORY
+  Say 'Y' to enable debugging of memory allocation.
+
+CONFIG_SND_DEBUG_DETECTION
+  Say 'Y' to enable debugging of hardware detection.
diff -Nru linux/sound/core/Config.in linux-2.4.19-pre5-mjc/sound/core/Config.in
--- linux/sound/core/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/Config.in	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,30 @@
+# ALSA soundcard-configuration
+
+if [ "$CONFIG_X86_64" = "y" -a "$CONFIG_IA32_EMULATION" = "y" ]; then
+  tristate '  Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL
+fi
+if [ "$CONFIG_PPC64" = "y" ]; then
+  tristate '  Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL
+fi
+if [ "$CONFIG_SPARC64" = "y" ]; then
+  tristate '  Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL
+fi
+dep_tristate '  Sequencer support' CONFIG_SND_SEQUENCER $CONFIG_SND
+if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then
+  dep_tristate '  Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER
+fi
+bool '  OSS API emulation' CONFIG_SND_OSSEMUL $CONFIG_SND
+if [ "$CONFIG_SND_OSSEMUL" = "y" ]; then
+  dep_tristate '    OSS Mixer API' CONFIG_SND_MIXER_OSS $CONFIG_SND
+  dep_tristate '    OSS PCM API' CONFIG_SND_PCM_OSS $CONFIG_SND
+  if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then
+    dep_tristate '    OSS Sequencer API' CONFIG_SND_SEQUENCER_OSS $CONFIG_SND_SEQUENCER
+  fi
+fi
+dep_tristate '  RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND
+bool '  Verbose printk' CONFIG_SND_VERBOSE_PRINTK
+bool '  Debug' CONFIG_SND_DEBUG
+if [ "$CONFIG_SND_DEBUG" = "y" ]; then
+  bool '    Debug memory' CONFIG_SND_DEBUG_MEMORY
+  bool '    Debug detection' CONFIG_SND_DEBUG_DETECT
+fi
diff -Nru linux/sound/core/Makefile linux-2.4.19-pre5-mjc/sound/core/Makefile
--- linux/sound/core/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,131 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := core.o
+
+export-objs  := sound.o pcm.o pcm_lib.o rawmidi.o timer.o rtctimer.o hwdep.o
+
+list-multi   := snd.o snd-pcm.o snd-rawmidi.o snd-timer.o snd-rtctimer.o snd-hwdep.o
+
+snd-objs     := sound.o init.o isadma.o memory.o info.o control.o misc.o \
+                device.o wrappers.o
+ifeq ($(CONFIG_SND_OSSEMUL),y)
+snd-objs     += sound_oss.o info_oss.o
+endif
+
+snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
+		pcm_memory.o
+
+snd-rawmidi-objs  := rawmidi.o
+snd-timer-objs    := timer.o
+snd-rtctimer-objs := rtctimer.o
+snd-hwdep-objs    := hwdep.o
+
+obj-$(CONFIG_SND) += snd.o
+ifeq ($(CONFIG_RTC),y)
+  obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o
+  obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
+endif
+obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
+
+subdir-$(CONFIG_SND_MIXER_OSS) += oss
+subdir-$(CONFIG_SND_PCM_OSS) += oss
+ifeq ($(filter $(subdir-y),oss),oss)
+  subdir-m += oss
+  obj-y += oss/oss.o
+endif
+
+subdir-$(CONFIG_SND_SEQUENCER) += seq
+ifeq ($(CONFIG_SND_SEQUENCER),y)
+  subdir-m += seq
+  obj-y += seq/sq.o
+endif
+
+obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o
+
+subdir-$(CONFIG_SND_BIT32_EMUL) += ioctl32
+ifeq ($(CONFIG_SND_BIT32_EMUL),y)
+  obj-y += ioctl32/_ioctl32.o
+endif
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o
+obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o
+obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o
+obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_DT0197H) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o
+obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_VIA686) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_VIA8233) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o
+obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
+obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
+obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o
+ifeq ($(CONFIG_SND_SB16_CSP),y)
+  obj-$(CONFIG_SND_SB16) += snd-hwdep.o
+  obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd.o: $(snd-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-objs)
+
+snd-pcm.o: $(snd-pcm-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-objs)
+
+snd-rawmidi.o: $(snd-rawmidi-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-rawmidi-objs)
+
+snd-timer.o: $(snd-timer-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-timer-objs)
+
+snd-rtctimer.o: $(snd-rtctimer-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-rtctimer-objs)
+
+snd-hwdep.o: $(snd-hwdep-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-hwdep-objs)
diff -Nru linux/sound/core/control.c linux-2.4.19-pre5-mjc/sound/core/control.c
--- linux/sound/core/control.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/control.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,815 @@
+/*
+ *  Routines for driver control interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/control.h>
+
+typedef struct _snd_kctl_ioctl {
+	struct list_head list;		/* list of all ioctls */
+	snd_kctl_ioctl_func_t fioctl;
+} snd_kctl_ioctl_t;
+
+#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list)
+
+static rwlock_t snd_ioctl_rwlock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(snd_control_ioctls);
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+static int snd_ctl_open(struct inode *inode, struct file *file)
+{
+	int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev));
+	unsigned long flags;
+	snd_card_t *card;
+	snd_ctl_file_t *ctl;
+	int err;
+
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	card = snd_cards[cardnum];
+	if (!card) {
+		err = -ENODEV;
+		goto __error1;
+	}
+	if (!try_inc_mod_count(card->module)) {
+		err = -EFAULT;
+		goto __error1;
+	}
+	ctl = snd_magic_kcalloc(snd_ctl_file_t, 0, GFP_KERNEL);
+	if (ctl == NULL) {
+		err = -ENOMEM;
+		goto __error;
+	}
+	INIT_LIST_HEAD(&ctl->events);
+	init_waitqueue_head(&ctl->change_sleep);
+	spin_lock_init(&ctl->read_lock);
+	ctl->card = card;
+	ctl->pid = current->pid;
+	file->private_data = ctl;
+	write_lock_irqsave(&card->control_rwlock, flags);
+	list_add_tail(&ctl->list, &card->ctl_files);
+	write_unlock_irqrestore(&card->control_rwlock, flags);
+	return 0;
+
+      __error:
+      	dec_mod_count(card->module);
+      __error1:
+#ifdef LINUX_2_2
+      	MOD_DEC_USE_COUNT;
+#endif
+      	return err;
+}
+
+static void snd_ctl_empty_read_queue(snd_ctl_file_t * ctl)
+{
+	snd_kctl_event_t *cread;
+	
+	spin_lock(&ctl->read_lock);
+	while (!list_empty(&ctl->events)) {
+		cread = snd_kctl_event(ctl->events.next);
+		list_del(&cread->list);
+		kfree(cread);
+	}
+	spin_unlock(&ctl->read_lock);
+}
+
+static int snd_ctl_release(struct inode *inode, struct file *file)
+{
+	unsigned long flags;
+	struct list_head *list;
+	snd_card_t *card;
+	snd_ctl_file_t *ctl;
+	snd_kcontrol_t *control;
+
+	ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO);
+	fasync_helper(-1, file, 0, &ctl->fasync);
+	file->private_data = NULL;
+	card = ctl->card;
+	write_lock_irqsave(&card->control_rwlock, flags);
+	list_del(&ctl->list);
+	write_unlock_irqrestore(&card->control_rwlock, flags);
+	write_lock(&card->control_owner_lock);
+	list_for_each(list, &card->controls) {
+		control = snd_kcontrol(list);
+		if (control->owner == ctl)
+			control->owner = NULL;
+	}
+	write_unlock(&card->control_owner_lock);
+	snd_ctl_empty_read_queue(ctl);
+	snd_magic_kfree(ctl);
+	dec_mod_count(card->module);
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return 0;
+}
+
+void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id)
+{
+	unsigned long flags;
+	struct list_head *flist;
+	snd_ctl_file_t *ctl;
+	snd_kctl_event_t *ev;
+	
+	snd_runtime_check(card != NULL && id != NULL, return);
+	read_lock_irqsave(&card->control_rwlock, flags);
+#ifdef CONFIG_SND_OSSEMUL
+	card->mixer_oss_change_count++;
+#endif
+	list_for_each(flist, &card->ctl_files) {
+		struct list_head *elist;
+		ctl = snd_ctl_file(flist);
+		if (!ctl->subscribed)
+			continue;
+		spin_lock(&ctl->read_lock);
+		list_for_each(elist, &ctl->events) {
+			ev = snd_kctl_event(elist);
+			if (ev->id.numid == id->numid) {
+				ev->mask |= mask;
+				goto _found;
+			}
+		}
+		ev = snd_kcalloc(sizeof(*ev), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+		if (ev) {
+			ev->id = *id;
+			ev->mask = mask;
+			list_add_tail(&ev->list, &ctl->events);
+		} else {
+			snd_printk(KERN_ERR "No memory available to allocate event\n");
+		}
+	_found:
+		wake_up(&ctl->change_sleep);
+		kill_fasync(&ctl->fasync, SIGIO, POLL_IN);
+		spin_unlock(&ctl->read_lock);
+	}
+	read_unlock_irqrestore(&card->control_rwlock, flags);
+}
+
+snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control)
+{
+	snd_kcontrol_t *kctl;
+	
+	snd_runtime_check(control != NULL, return NULL);
+	kctl = (snd_kcontrol_t *)snd_magic_kmalloc(snd_kcontrol_t, 0, GFP_KERNEL);
+	if (kctl == NULL)
+		return NULL;
+	*kctl = *control;
+	return kctl;
+}
+
+snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data)
+{
+	snd_kcontrol_t kctl;
+	
+	snd_runtime_check(ncontrol != NULL, return NULL);
+	snd_assert(ncontrol->info != NULL, return NULL);
+	memset(&kctl, 0, sizeof(kctl));
+	kctl.id.iface = ncontrol->iface;
+	kctl.id.device = ncontrol->device;
+	kctl.id.subdevice = ncontrol->subdevice;
+	strncpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)-1);
+	kctl.id.index = ncontrol->index;
+	kctl.access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
+		      (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|SNDRV_CTL_ELEM_ACCESS_INDIRECT));
+	kctl.info = ncontrol->info;
+	kctl.get = ncontrol->get;
+	kctl.put = ncontrol->put;
+	kctl.private_value = ncontrol->private_value;
+	kctl.private_data = private_data;
+	return snd_ctl_new(&kctl);
+}
+
+void snd_ctl_free_one(snd_kcontrol_t * kcontrol)
+{
+	if (kcontrol) {
+		if (kcontrol->private_free)
+			kcontrol->private_free(kcontrol);
+		snd_magic_kfree(kcontrol);
+	}
+}
+
+int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol)
+{
+	snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL);
+	snd_assert(kcontrol->info != NULL, return -EINVAL);
+	snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_READ) || kcontrol->get != NULL, return -EINVAL);
+	snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kcontrol->put != NULL, return -EINVAL);
+	write_lock(&card->control_rwlock);
+	list_add_tail(&kcontrol->list, &card->controls);
+	card->controls_count++;
+	kcontrol->id.numid = ++card->last_numid;
+	write_unlock(&card->control_rwlock);
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &kcontrol->id);
+	return 0;
+}
+
+int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol)
+{
+	snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL);
+	write_lock(&card->control_rwlock);
+	list_del(&kcontrol->list);
+	card->controls_count--;
+	write_unlock(&card->control_rwlock);
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &kcontrol->id);
+	snd_ctl_free_one(kcontrol);
+	return 0;
+}
+
+int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id)
+{
+	snd_kcontrol_t *kctl;
+
+	kctl = snd_ctl_find_id(card, id);
+	if (kctl == NULL)
+		return -ENOENT;
+	return snd_ctl_remove(card, kctl);
+}
+
+int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id)
+{
+	snd_kcontrol_t *kctl;
+
+	kctl = snd_ctl_find_id(card, src_id);
+	if (kctl == NULL)
+		return -ENOENT;
+	write_lock(&card->control_rwlock);
+	kctl->id = *dst_id;
+	kctl->id.numid = ++card->last_numid;
+	write_unlock(&card->control_rwlock);
+	return 0;
+}
+
+snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid)
+{
+	struct list_head *list;
+	snd_kcontrol_t *kctl;
+
+	snd_runtime_check(card != NULL && numid != 0, return NULL);
+	read_lock(&card->control_rwlock);
+	list_for_each(list, &card->controls) {
+		kctl = snd_kcontrol(list);
+		if (kctl->id.numid == numid) {
+			read_unlock(&card->control_rwlock);
+			return kctl;
+		}
+	}
+	read_unlock(&card->control_rwlock);
+	return NULL;
+}
+
+snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id)
+{
+	struct list_head *list;
+	snd_kcontrol_t *kctl;
+
+	snd_runtime_check(card != NULL && id != NULL, return NULL);
+	if (id->numid != 0)
+		return snd_ctl_find_numid(card, id->numid);
+	read_lock(&card->control_rwlock);
+	list_for_each(list, &card->controls) {
+		kctl = snd_kcontrol(list);
+		if (kctl->id.iface != id->iface)
+			continue;
+		if (kctl->id.device != id->device)
+			continue;
+		if (kctl->id.subdevice != id->subdevice)
+			continue;
+		if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
+			continue;
+		if (kctl->id.index != id->index)
+			continue;
+		read_unlock(&card->control_rwlock);
+		return kctl;
+	}
+	read_unlock(&card->control_rwlock);
+	return NULL;
+}
+
+static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl,
+			     unsigned int cmd, unsigned long arg)
+{
+	snd_ctl_card_info_t info;
+
+	memset(&info, 0, sizeof(info));
+	read_lock(&snd_ioctl_rwlock);
+	info.card = card->number;
+	strncpy(info.id, card->id, sizeof(info.id) - 1);
+	strncpy(info.driver, card->driver, sizeof(info.driver) - 1);
+	strncpy(info.name, card->shortname, sizeof(info.name) - 1);
+	strncpy(info.longname, card->longname, sizeof(info.longname) - 1);
+	strncpy(info.mixername, card->mixername, sizeof(info.mixername) - 1);
+	strncpy(info.components, card->components, sizeof(info.components) - 1);
+	read_unlock(&snd_ioctl_rwlock);
+	if (copy_to_user((void *) arg, &info, sizeof(snd_ctl_card_info_t)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t *_list)
+{
+	struct list_head *plist;
+	snd_ctl_elem_list_t list;
+	snd_kcontrol_t *kctl;
+	snd_ctl_elem_id_t *dst, *id;
+	int offset, space;
+	
+	if (copy_from_user(&list, _list, sizeof(list)))
+		return -EFAULT;
+	offset = list.offset;
+	space = list.space;
+	/* try limit maximum space */
+	if (space > 16384)
+		return -ENOMEM;
+	if (space > 0) {
+		/* allocate temporary buffer for atomic operation */
+		dst = vmalloc(space * sizeof(snd_ctl_elem_id_t));
+		if (dst == NULL)
+			return -ENOMEM;
+		read_lock(&card->control_rwlock);
+		list.count = card->controls_count;
+		plist = card->controls.next;
+		while (offset-- > 0 && plist != &card->controls)
+			plist = plist->next;
+		list.used = 0;
+		id = dst;
+		while (space > 0 && plist != &card->controls) {
+			kctl = snd_kcontrol(plist);
+			memcpy(id, &kctl->id, sizeof(snd_ctl_elem_id_t));
+			id++;
+			plist = plist->next;
+			space--;
+			list.used++;
+		}
+		read_unlock(&card->control_rwlock);
+		if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(snd_ctl_elem_id_t)))
+			return -EFAULT;
+		vfree(dst);
+	} else {
+		read_lock(&card->control_rwlock);
+		list.count = card->controls_count;
+		read_unlock(&card->control_rwlock);
+	}
+	if (copy_to_user(_list, &list, sizeof(list)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *_info)
+{
+	snd_card_t *card = ctl->card;
+	snd_ctl_elem_info_t info;
+	snd_kcontrol_t *kctl;
+	int result;
+	
+	if (copy_from_user(&info, _info, sizeof(info)))
+		return -EFAULT;
+	read_lock(&card->control_rwlock);
+	kctl = snd_ctl_find_id(card, &info.id);
+	if (kctl == NULL) {
+		read_unlock(&card->control_rwlock);
+		return -ENOENT;
+	}
+#ifdef CONFIG_SND_DEBUG
+	info.access = 0;
+#endif
+	result = kctl->info(kctl, &info);
+	if (result >= 0) {
+		snd_assert(info.access == 0, );
+		info.id = kctl->id;
+		info.access = kctl->access;
+		if (kctl->owner) {
+			info.access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
+			if (kctl->owner == ctl)
+				info.access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
+			info.owner = kctl->owner_pid;
+		} else {
+			info.owner = -1;
+		}
+	}
+	read_unlock(&card->control_rwlock);
+	if (result >= 0)
+		if (copy_to_user(_info, &info, sizeof(info)))
+			return -EFAULT;
+	return result;
+}
+
+static int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *_control)
+{
+	snd_ctl_elem_value_t control;
+	snd_kcontrol_t *kctl;
+	int result, indirect;
+	
+	if (copy_from_user(&control, _control, sizeof(control)))
+		return -EFAULT;
+	read_lock(&card->control_rwlock);
+	kctl = snd_ctl_find_id(card, &control.id);
+	if (kctl == NULL) {
+		result = -ENOENT;
+	} else {
+		indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+		if (control.indirect != indirect) {
+			result = -EACCES;
+		} else {
+			if ((kctl->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
+				result = kctl->get(kctl, &control);
+				if (result >= 0)
+					control.id = kctl->id;
+			} else
+				result = -EPERM;
+		}
+	}
+	read_unlock(&card->control_rwlock);
+	if (result >= 0)
+		if (copy_to_user(_control, &control, sizeof(control)))
+			return -EFAULT;
+	return result;
+}
+
+static int snd_ctl_elem_write(snd_ctl_file_t *file, snd_ctl_elem_value_t *_control)
+{
+	snd_card_t *card = file->card;
+	snd_ctl_elem_value_t control;
+	snd_kcontrol_t *kctl;
+	int result, indirect;
+	
+	if (copy_from_user(&control, _control, sizeof(control)))
+		return -EFAULT;
+	read_lock(&card->control_rwlock);
+	kctl = snd_ctl_find_id(card, &control.id);
+	if (kctl == NULL) {
+		result = -ENOENT;
+	} else {
+		indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+		if (control.indirect != indirect) {
+			result = -EACCES;
+		} else {
+			read_lock(&card->control_owner_lock);
+			if (!(kctl->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
+			    kctl->put == NULL ||
+			    (kctl->owner != NULL && kctl->owner != file)) {
+				result = -EPERM;
+			} else {
+				result = kctl->put(kctl, &control);
+				if (result >= 0)
+					control.id = kctl->id;
+			}
+			read_unlock(&card->control_owner_lock);
+			if (result > 0) {
+				result = 0;
+				snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+			}
+		}
+	}
+	read_unlock(&card->control_rwlock);
+	if (result >= 0)
+		if (copy_to_user(_control, &control, sizeof(control)))
+			return -EFAULT;
+	return result;
+}
+
+static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id)
+{
+	snd_card_t *card = file->card;
+	snd_ctl_elem_id_t id;
+	snd_kcontrol_t *kctl;
+	int result;
+	
+	if (copy_from_user(&id, _id, sizeof(id)))
+		return -EFAULT;
+	read_lock(&card->control_rwlock);
+	kctl = snd_ctl_find_id(card, &id);
+	if (kctl == NULL) {
+		result = -ENOENT;
+	} else {
+		write_lock(&card->control_owner_lock);
+		if (kctl->owner != NULL)
+			result = -EBUSY;
+		else {
+			kctl->owner = file;
+			kctl->owner_pid = current->pid;
+			result = 0;
+		}
+		write_unlock(&card->control_owner_lock);
+	}
+	read_unlock(&card->control_rwlock);
+	return result;
+}
+
+static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id)
+{
+	snd_card_t *card = file->card;
+	snd_ctl_elem_id_t id;
+	snd_kcontrol_t *kctl;
+	int result;
+	
+	if (copy_from_user(&id, _id, sizeof(id)))
+		return -EFAULT;
+	read_lock(&card->control_rwlock);
+	kctl = snd_ctl_find_id(card, &id);
+	if (kctl == NULL) {
+		result = -ENOENT;
+	} else {
+		write_lock(&card->control_owner_lock);
+		if (kctl->owner == NULL)
+			result = -EINVAL;
+		else if (kctl->owner != file)
+			result = -EPERM;
+		else {
+			kctl->owner = NULL;
+			kctl->owner_pid = 0;
+			result = 0;
+		}
+		write_unlock(&card->control_owner_lock);
+	}
+	read_unlock(&card->control_rwlock);
+	return result;
+}
+
+static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int *ptr)
+{
+	int subscribe;
+	if (get_user(subscribe, ptr))
+		return -EFAULT;
+	if (subscribe < 0) {
+		subscribe = file->subscribed;
+		if (put_user(subscribe, ptr))
+			return -EFAULT;
+		return 0;
+	}
+	if (subscribe) {
+		file->subscribed = 1;
+		return 0;
+	} else if (file->subscribed) {
+		snd_ctl_empty_read_queue(file);
+		file->subscribed = 0;
+	}
+	return 0;
+}
+
+static int snd_ctl_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	snd_ctl_file_t *ctl;
+	snd_card_t *card;
+	struct list_head *list;
+	snd_kctl_ioctl_t *p;
+	int err;
+
+	ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO);
+	card = ctl->card;
+	snd_assert(card != NULL, return -ENXIO);
+	switch (cmd) {
+	case SNDRV_CTL_IOCTL_PVERSION:
+		return put_user(SNDRV_CTL_VERSION, (int *)arg) ? -EFAULT : 0;
+	case SNDRV_CTL_IOCTL_CARD_INFO:
+		return snd_ctl_card_info(card, ctl, cmd, arg);
+	case SNDRV_CTL_IOCTL_ELEM_LIST:
+		return snd_ctl_elem_list(ctl->card, (snd_ctl_elem_list_t *) arg);
+	case SNDRV_CTL_IOCTL_ELEM_INFO:
+		return snd_ctl_elem_info(ctl, (snd_ctl_elem_info_t *) arg);
+	case SNDRV_CTL_IOCTL_ELEM_READ:
+		return snd_ctl_elem_read(ctl->card, (snd_ctl_elem_value_t *) arg);
+	case SNDRV_CTL_IOCTL_ELEM_WRITE:
+		return snd_ctl_elem_write(ctl, (snd_ctl_elem_value_t *) arg);
+	case SNDRV_CTL_IOCTL_ELEM_LOCK:
+		return snd_ctl_elem_lock(ctl, (snd_ctl_elem_id_t *) arg);
+	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+		return snd_ctl_elem_unlock(ctl, (snd_ctl_elem_id_t *) arg);
+	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
+		return snd_ctl_subscribe_events(ctl, (int *) arg);
+	case SNDRV_CTL_IOCTL_POWER:
+		if (get_user(err, (int *)arg))
+			return -EFAULT;
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+#ifdef CONFIG_PM
+		if (card->set_power_state == NULL)
+			return -ENOPROTOOPT;
+		return card->set_power_state(card, err);
+#else
+		return -ENOPROTOOPT;
+#endif
+	case SNDRV_CTL_IOCTL_POWER_STATE:
+#ifdef CONFIG_PM
+		return put_user(card->power_state, (int *)arg) ? -EFAULT : 0;
+#else
+		return put_user(SNDRV_CTL_POWER_D0, (int *)arg) ? -EFAULT : 0;
+#endif
+	}
+	read_lock(&snd_ioctl_rwlock);
+	list_for_each(list, &snd_control_ioctls) {
+		p = list_entry(list, snd_kctl_ioctl_t, list);
+		err = p->fioctl(card, ctl, cmd, arg);
+		if (err != -ENOIOCTLCMD) {
+			read_unlock(&snd_ioctl_rwlock);
+			return err;
+		}
+	}
+	read_unlock(&snd_ioctl_rwlock);
+	snd_printd("unknown ioctl = 0x%x\n", cmd);
+	return -ENOTTY;
+}
+
+static ssize_t snd_ctl_read(struct file *file, char *buffer, size_t count, loff_t * offset)
+{
+	snd_ctl_file_t *ctl;
+	int err = 0;
+	ssize_t result = 0;
+
+	ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO);
+	snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);
+	if (!ctl->subscribed)
+		return -EBADFD;
+	if (count < sizeof(snd_ctl_event_t))
+		return -EINVAL;
+	spin_lock_irq(&ctl->read_lock);
+	while (count >= sizeof(snd_ctl_event_t)) {
+		snd_ctl_event_t ev;
+		snd_kctl_event_t *kev;
+		while (list_empty(&ctl->events)) {
+			wait_queue_t wait;
+			if (file->f_flags & O_NONBLOCK) {
+				err = -EAGAIN;
+				goto __end;
+			}
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&ctl->change_sleep, &wait);
+			spin_unlock_irq(&ctl->read_lock);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&ctl->change_sleep, &wait);
+			if (signal_pending(current))
+				return result > 0 ? result : -ERESTARTSYS;
+			spin_lock_irq(&ctl->read_lock);
+		}
+		kev = snd_kctl_event(ctl->events.next);
+		ev.type = SNDRV_CTL_EVENT_ELEM;
+		ev.data.elem.mask = kev->mask;
+		ev.data.elem.id = kev->id;
+		list_del(&kev->list);
+		spin_unlock_irq(&ctl->read_lock);
+		kfree(kev);
+		if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) {
+			err = -EFAULT;
+			goto __end;
+		}
+		spin_lock_irq(&ctl->read_lock);
+		buffer += sizeof(snd_ctl_event_t);
+		count -= sizeof(snd_ctl_event_t);
+		result += sizeof(snd_ctl_event_t);
+	}
+      __end:
+	spin_unlock_irq(&ctl->read_lock);
+      	return result > 0 ? result : err;
+}
+
+static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask;
+	snd_ctl_file_t *ctl;
+
+	ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return 0);
+	if (!ctl->subscribed)
+		return 0;
+	poll_wait(file, &ctl->change_sleep, wait);
+
+	mask = 0;
+	if (!list_empty(&ctl->events))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
+{
+	snd_kctl_ioctl_t *pn;
+
+	pn = (snd_kctl_ioctl_t *)
+		snd_kcalloc(sizeof(snd_kctl_ioctl_t), GFP_KERNEL);
+	if (pn == NULL)
+		return -ENOMEM;
+	pn->fioctl = fcn;
+	write_lock(&snd_ioctl_rwlock);
+	list_add_tail(&pn->list, &snd_control_ioctls);
+	write_unlock(&snd_ioctl_rwlock);
+	return 0;
+}
+
+int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
+{
+	struct list_head *list;
+	snd_kctl_ioctl_t *p;
+
+	snd_runtime_check(fcn != NULL, return -EINVAL);
+	write_lock(&snd_ioctl_rwlock);
+	list_for_each(list, &snd_control_ioctls) {
+		p = list_entry(list, snd_kctl_ioctl_t, list);
+		if (p->fioctl == fcn) {
+			list_del(&p->list);
+			write_unlock(&snd_ioctl_rwlock);
+			kfree(p);
+			return 0;
+		}
+	}
+	write_unlock(&snd_ioctl_rwlock);
+	snd_BUG();
+	return -EINVAL;
+}
+
+static int snd_ctl_fasync(int fd, struct file * file, int on)
+{
+	snd_ctl_file_t *ctl;
+	int err;
+	ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO);
+	err = fasync_helper(fd, file, on, &ctl->fasync);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *  INIT PART
+ */
+
+static struct file_operations snd_ctl_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		snd_ctl_read,
+	open:		snd_ctl_open,
+	release:	snd_ctl_release,
+	poll:		snd_ctl_poll,
+	ioctl:		snd_ctl_ioctl,
+	fasync:		snd_ctl_fasync,
+};
+
+static snd_minor_t snd_ctl_reg =
+{
+	comment:	"ctl",
+	f_ops:		&snd_ctl_f_ops,
+};
+
+int snd_ctl_register(snd_card_t *card)
+{
+	int err, cardnum;
+	char name[16];
+
+	snd_assert(card != NULL, return -ENXIO);
+	cardnum = card->number;
+	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+	sprintf(name, "controlC%i", cardnum);
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL,
+					card, 0, &snd_ctl_reg, name)) < 0)
+		return err;
+	return 0;
+}
+
+int snd_ctl_unregister(snd_card_t *card)
+{
+	int err, cardnum;
+	snd_kcontrol_t *control;
+
+	snd_assert(card != NULL, return -ENXIO);
+	cardnum = card->number;
+	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+	if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0)
+		return err;
+	while (!list_empty(&card->controls)) {
+		control = snd_kcontrol(card->controls.next);
+		snd_ctl_remove(card, control);
+	}
+	return 0;
+}
diff -Nru linux/sound/core/device.c linux-2.4.19-pre5-mjc/sound/core/device.c
--- linux/sound/core/device.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/device.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,135 @@
+/*
+ *  Device management routines
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+
+int snd_device_new(snd_card_t *card, snd_device_type_t type,
+		   void *device_data, snd_device_ops_t *ops)
+{
+	snd_device_t *dev;
+
+	snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO);
+	dev = (snd_device_t *) snd_magic_kcalloc(snd_device_t, 0, GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	dev->card = card;
+	dev->type = type;
+	dev->state = SNDRV_DEV_BUILD;
+	dev->device_data = device_data;
+	dev->ops = ops;
+	list_add(&dev->list, &card->devices);	/* add to the head of list */
+	return 0;
+}
+
+int snd_device_free(snd_card_t *card, void *device_data)
+{
+	struct list_head *list;
+	snd_device_t *dev;
+	
+	snd_assert(card != NULL, return -ENXIO);
+	snd_assert(device_data != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->device_data != device_data)
+			continue;
+		/* unlink */
+		list_del(&dev->list);
+		if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_unregister) {
+			if (dev->ops->dev_unregister(dev))
+				snd_printk(KERN_ERR "device unregister failure\n");
+		} else {
+			if (dev->ops->dev_free) {
+				if (dev->ops->dev_free(dev))
+					snd_printk(KERN_ERR "device free failure\n");
+			}
+		}
+		snd_magic_kfree(dev);
+		return 0;
+	}
+	snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0));
+	return -ENXIO;
+}
+
+int snd_device_register(snd_card_t *card, void *device_data)
+{
+	struct list_head *list;
+	snd_device_t *dev;
+	int err;
+	
+	snd_assert(card != NULL && device_data != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->device_data != device_data)
+			continue;
+		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
+			if ((err = dev->ops->dev_register(dev)) < 0)
+				return err;
+			dev->state = SNDRV_DEV_REGISTERED;
+			return 0;
+		}
+		return -EBUSY;
+	}
+	snd_BUG();
+	return -ENXIO;
+}
+
+int snd_device_register_all(snd_card_t *card)
+{
+	struct list_head *list;
+	snd_device_t *dev;
+	int err;
+	
+	snd_assert(card != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
+			if ((err = dev->ops->dev_register(dev)) < 0)
+				return err;
+			dev->state = SNDRV_DEV_REGISTERED;
+		}
+	}
+	return 0;
+}
+
+int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd)
+{
+	snd_device_t *dev;
+	struct list_head *list;
+	int err, range_low, range_high;
+
+	snd_assert(card != NULL, return -ENXIO);
+	range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
+	range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
+      __again:
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);		
+		if (dev->type >= range_low && dev->type <= range_high) {
+			if ((err = snd_device_free(card, dev->device_data)) < 0)
+				return err;
+			goto __again;
+		}
+	}
+	return 0;
+}
diff -Nru linux/sound/core/hwdep.c linux-2.4.19-pre5-mjc/sound/core/hwdep.c
--- linux/sound/core/hwdep.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/hwdep.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,436 @@
+/*
+ *  Hardware dependent layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Hardware dependent layer");
+MODULE_LICENSE("GPL");
+
+snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS];
+
+static DECLARE_MUTEX(register_mutex);
+
+static int snd_hwdep_free(snd_hwdep_t *hwdep);
+static int snd_hwdep_dev_free(snd_device_t *device);
+static int snd_hwdep_dev_register(snd_device_t *device);
+static int snd_hwdep_dev_unregister(snd_device_t *device);
+
+/*
+
+ */
+
+static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
+{
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO);
+	if (hw->ops.llseek)
+		return hw->ops.llseek(hw, file, offset, orig);
+	return -ENXIO;
+}
+
+static ssize_t snd_hwdep_read(struct file * file, char *buf, size_t count, loff_t *offset)
+{
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO);
+	if (hw->ops.read)
+		return hw->ops.read(hw, buf, count, offset);
+	return -ENXIO;	
+}
+
+static ssize_t snd_hwdep_write(struct file * file, const char *buf, size_t count, loff_t *offset)
+{
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO);
+	if (hw->ops.write)
+		return hw->ops.write(hw, buf, count, offset);
+	return -ENXIO;	
+}
+
+static int snd_hwdep_open(struct inode *inode, struct file * file)
+{
+	int major = major(inode->i_rdev);
+	int cardnum;
+	int device;
+	snd_hwdep_t *hw;
+	int err;
+	wait_queue_t wait;
+
+	switch (major) {
+	case CONFIG_SND_MAJOR:
+		cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev));
+		device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_HWDEP;
+		break;
+#ifdef CONFIG_SND_OSSEMUL
+	case SOUND_MAJOR:
+		cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev));
+		device = 0;
+		break;
+#endif
+	default:
+		return -ENXIO;
+	}
+	cardnum %= SNDRV_CARDS;
+	device %= SNDRV_MINOR_HWDEPS;
+	hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device];
+
+	snd_assert(hw != NULL, return -ENODEV);
+	if (!hw->ops.open)
+		return -ENXIO;
+#ifdef CONFIG_SND_OSSEMUL
+	if (major == SOUND_MAJOR && hw->oss_type < 0)
+		return -ENXIO;
+#endif
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&hw->open_wait, &wait);
+	while (1) {
+		err = hw->ops.open(hw, file);
+		if (err >= 0)
+			break;
+		if (err == -EAGAIN) {
+			if (file->f_flags & O_NONBLOCK) {
+				err = -EBUSY;
+				break;
+			}
+		} else
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&hw->open_wait, &wait);
+	if (err >= 0)
+		file->private_data = hw;
+	return err;
+}
+
+static int snd_hwdep_release(struct inode *inode, struct file * file)
+{
+	int err;
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO);
+	if (hw->ops.release) {
+		err = hw->ops.release(hw, file);
+		wake_up(&hw->open_wait);
+		return err;
+	}
+	return -ENXIO;	
+}
+
+static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
+{
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return 0);
+	if (hw->ops.poll)
+		return hw->ops.poll(hw, file, wait);
+	return 0;
+}
+
+static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t *_info)
+{
+	snd_hwdep_info_t info;
+	
+	memset(&info, 0, sizeof(info));
+	info.card = hw->card->number;
+	strncpy(info.id, hw->id, sizeof(info.id) - 1);	
+	strncpy(info.name, hw->name, sizeof(info.name) - 1);
+	info.iface = hw->iface;
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_hwdep_ioctl(struct inode *inode, struct file * file,
+			   unsigned int cmd, unsigned long arg)
+{
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO);
+	if (cmd == SNDRV_HWDEP_IOCTL_PVERSION)
+		return put_user(SNDRV_HWDEP_VERSION, (int *)arg);
+	if (cmd == SNDRV_HWDEP_IOCTL_INFO)
+		return snd_hwdep_info(hw, (snd_hwdep_info_t *)arg);
+	if (hw->ops.ioctl)
+		return hw->ops.ioctl(hw, file, cmd, arg);
+	return -ENOTTY;
+}
+
+static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO);
+	if (hw->ops.mmap)
+		return hw->ops.mmap(hw, file, vma);
+	return -ENXIO;
+}
+
+static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
+				   unsigned int cmd, unsigned long arg)
+{
+	unsigned int tmp;
+	
+	tmp = card->number * SNDRV_MINOR_HWDEPS;
+	switch (cmd) {
+	case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
+		{
+			int device;
+
+			if (get_user(device, (int *)arg))
+				return -EFAULT;
+			device = device < 0 ? 0 : device + 1;
+			while (device < SNDRV_MINOR_HWDEPS) {
+				if (snd_hwdep_devices[tmp + device])
+					break;
+				device++;
+			}
+			if (device >= SNDRV_MINOR_HWDEPS)
+				device = -1;
+			if (put_user(device, (int *)arg))
+				return -EFAULT;
+			return 0;
+		}
+	case SNDRV_CTL_IOCTL_HWDEP_INFO:
+		{
+			snd_hwdep_info_t *info = (snd_hwdep_info_t *)arg;
+			int device;
+			snd_hwdep_t *hwdep;
+
+			if (get_user(device, &info->device))
+				return -EFAULT;
+			if (device < 0 || device >= SNDRV_MINOR_HWDEPS)
+				return -ENXIO;
+			hwdep = snd_hwdep_devices[tmp + device];
+			if (hwdep == NULL)
+				return -ENXIO;
+			return snd_hwdep_info(hwdep, info);
+		}
+	}
+	return -ENOIOCTLCMD;
+}
+
+/*
+
+ */
+
+static struct file_operations snd_hwdep_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	llseek:		snd_hwdep_llseek,
+	read:		snd_hwdep_read,
+	write:		snd_hwdep_write,
+	open:		snd_hwdep_open,
+	release:	snd_hwdep_release,
+	poll:		snd_hwdep_poll,
+	ioctl:		snd_hwdep_ioctl,
+	mmap:		snd_hwdep_mmap,
+};
+
+static snd_minor_t snd_hwdep_reg =
+{
+	comment:	"hardware dependent",
+	f_ops:		&snd_hwdep_f_ops,
+};
+
+int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep)
+{
+	snd_hwdep_t *hwdep;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_hwdep_dev_free,
+		dev_register:	snd_hwdep_dev_register,
+		dev_unregister:	snd_hwdep_dev_unregister
+	};
+
+	snd_assert(rhwdep != NULL, return -EINVAL);
+	*rhwdep = NULL;
+	snd_assert(card != NULL, return -ENXIO);
+	hwdep = snd_magic_kcalloc(snd_hwdep_t, 0, GFP_KERNEL);
+	if (hwdep == NULL)
+		return -ENOMEM;
+	hwdep->card = card;
+	hwdep->device = device;
+	if (id) {
+		strncpy(hwdep->id, id, sizeof(hwdep->id) - 1);
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	hwdep->oss_type = -1;
+#endif
+	if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
+		snd_hwdep_free(hwdep);
+		return err;
+	}
+	init_waitqueue_head(&hwdep->open_wait);
+	*rhwdep = hwdep;
+	return 0;
+}
+
+static int snd_hwdep_free(snd_hwdep_t *hwdep)
+{
+	snd_assert(hwdep != NULL, return -ENXIO);
+	if (hwdep->private_free)
+		hwdep->private_free(hwdep);
+	snd_magic_kfree(hwdep);
+	return 0;
+}
+
+static int snd_hwdep_dev_free(snd_device_t *device)
+{
+	snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO);
+	return snd_hwdep_free(hwdep);
+}
+
+static int snd_hwdep_dev_register(snd_device_t *device)
+{
+	snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO);
+	int idx, err;
+	char name[32];
+
+	down(&register_mutex);
+	idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
+	if (snd_hwdep_devices[idx]) {
+		up(&register_mutex);
+		return -EBUSY;
+	}
+	snd_hwdep_devices[idx] = hwdep;
+	sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
+				       hwdep->card, hwdep->device,
+				       &snd_hwdep_reg, name)) < 0) {
+		snd_printk(KERN_ERR "unable to register hardware dependant device %i:%i\n",
+			   hwdep->card->number, hwdep->device);
+		snd_hwdep_devices[idx] = NULL;
+		up(&register_mutex);
+		return err;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	hwdep->ossreg = 0;
+	if (hwdep->oss_type >= 0) {
+		if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
+			snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
+		} else {
+			if (snd_register_oss_device(hwdep->oss_type,
+						    hwdep->card, hwdep->device,
+						    &snd_hwdep_reg, hwdep->oss_dev) < 0) {
+				snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
+					   hwdep->card->number, hwdep->device);
+			} else {
+				snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number, hwdep->name);
+				hwdep->ossreg = 1;
+			}
+		}
+	}
+#endif
+	up(&register_mutex);
+	return 0;
+}
+
+static int snd_hwdep_dev_unregister(snd_device_t *device)
+{
+	snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO);
+	int idx;
+
+	snd_assert(hwdep != NULL, return -ENXIO);
+	down(&register_mutex);
+	idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
+	if (snd_hwdep_devices[idx] != hwdep) {
+		up(&register_mutex);
+		return -EINVAL;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	if (hwdep->ossreg) {
+		snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
+		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number);
+	}
+#endif
+	snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
+	snd_hwdep_devices[idx] = NULL;
+	up(&register_mutex);
+	return snd_hwdep_free(hwdep);
+}
+
+/*
+ *  Info interface
+ */
+
+static void snd_hwdep_proc_read(snd_info_entry_t *entry,
+				snd_info_buffer_t * buffer)
+{
+	int idx;
+	snd_hwdep_t *hwdep;
+
+	down(&register_mutex);
+	for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) {
+		hwdep = snd_hwdep_devices[idx];
+		if (hwdep == NULL)
+			continue;
+		snd_iprintf(buffer, "%02i-%02i: %s\n",
+					idx / SNDRV_MINOR_HWDEPS,
+					idx % SNDRV_MINOR_HWDEPS,
+					hwdep->name);
+	}
+	up(&register_mutex);
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static snd_info_entry_t *snd_hwdep_proc_entry = NULL;
+
+static int __init alsa_hwdep_init(void)
+{
+	snd_info_entry_t *entry;
+
+	memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices));
+	if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = snd_hwdep_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_hwdep_proc_entry = entry;
+	snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
+	return 0;
+}
+
+static void __exit alsa_hwdep_exit(void)
+{
+	snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
+	if (snd_hwdep_proc_entry) {
+		snd_info_unregister(snd_hwdep_proc_entry);
+		snd_hwdep_proc_entry = NULL;
+	}
+}
+
+module_init(alsa_hwdep_init)
+module_exit(alsa_hwdep_exit)
+
+EXPORT_SYMBOL(snd_hwdep_new);
diff -Nru linux/sound/core/info.c linux-2.4.19-pre5-mjc/sound/core/info.c
--- linux/sound/core/info.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/info.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1074 @@
+/*
+ *  Information interface for ALSA driver
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/smp_lock.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <linux/proc_fs.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+#include <stdarg.h>
+
+/*
+ *
+ */
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+int snd_info_check_reserved_words(const char *str)
+{
+	static char *reserved[] =
+	{
+		"dev",
+		"version",
+		"meminfo",
+		"memdebug",
+		"detect",
+		"devices",
+		"oss",
+		"cards",
+		"timers",
+		"synth",
+		"pcm",
+		"seq",
+		NULL
+	};
+	char **xstr = reserved;
+
+	while (*xstr) {
+		if (!strcmp(*xstr, str))
+			return 0;
+		xstr++;
+	}
+	if (!strncmp(str, "card", 4))
+		return 0;
+	return 1;
+}
+
+#ifdef CONFIG_PROC_FS
+
+extern int snd_major;
+extern struct file_operations snd_fops;
+
+static DECLARE_MUTEX(info_mutex);
+
+typedef struct _snd_info_private_data {
+	snd_info_buffer_t *rbuffer;
+	snd_info_buffer_t *wbuffer;
+	snd_info_entry_t *entry;
+	void *file_private_data;
+} snd_info_private_data_t;
+
+static int snd_info_version_init(void);
+static int snd_info_version_done(void);
+
+/*
+
+ */
+
+int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...)
+{
+	va_list args;
+	int res;
+	char sbuffer[512];
+
+	if (buffer->stop || buffer->error)
+		return 0;
+	va_start(args, fmt);
+	res = vsprintf(sbuffer, fmt, args);
+	va_end(args);
+	if (buffer->size + res >= buffer->len) {
+		buffer->stop = 1;
+		return 0;
+	}
+	strcpy(buffer->curr, sbuffer);
+	buffer->curr += res;
+	buffer->size += res;
+	return res;
+}
+
+/*
+
+ */
+
+struct proc_dir_entry *snd_proc_root = NULL;
+struct proc_dir_entry *snd_proc_dev = NULL;
+snd_info_entry_t *snd_seq_root = NULL;
+#ifdef CONFIG_SND_OSSEMUL
+snd_info_entry_t *snd_oss_root = NULL;
+#endif
+
+#ifdef LINUX_2_2
+static void snd_info_fill_inode(struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+}
+
+static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
+{
+	de->fill_inode = snd_info_fill_inode;
+}
+
+void snd_remove_proc_entry(struct proc_dir_entry *parent,
+			   struct proc_dir_entry *de)
+{
+	if (parent && de)
+		proc_unregister(parent, de->low_ino);
+}
+#else
+static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
+{
+	de->owner = THIS_MODULE;
+}
+
+void snd_remove_proc_entry(struct proc_dir_entry *parent,
+			   struct proc_dir_entry *de)
+{
+	if (de)
+		remove_proc_entry(de->name, parent);
+}
+#endif
+
+static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
+{
+	snd_info_private_data_t *data;
+	struct snd_info_entry *entry;
+	loff_t ret;
+
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	entry = data->entry;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3)
+	lock_kernel();
+#endif
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_TEXT:
+		switch (orig) {
+		case 0:	/* SEEK_SET */
+			file->f_pos = offset;
+			ret = file->f_pos;
+			goto out;
+		case 1:	/* SEEK_CUR */
+			file->f_pos += offset;
+			ret = file->f_pos;
+			goto out;
+		case 2:	/* SEEK_END */
+		default:
+			ret = -EINVAL;
+			goto out;
+		}
+		break;
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->llseek) {
+			ret = entry->c.ops->llseek(entry,
+						    data->file_private_data,
+						    file, offset, orig);
+			goto out;
+		}
+		break;
+	}
+	ret = -ENXIO;
+out:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3)
+	unlock_kernel();
+#endif
+	return ret;
+}
+
+static ssize_t snd_info_entry_read(struct file *file, char *buffer,
+				   size_t count, loff_t * offset)
+{
+	snd_info_private_data_t *data;
+	struct snd_info_entry *entry;
+	snd_info_buffer_t *buf;
+	long size = 0, size1;
+
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	snd_assert(data != NULL, return -ENXIO);
+	entry = data->entry;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_TEXT:
+		buf = data->rbuffer;
+		if (buf == NULL)
+			return -EIO;
+		if (file->f_pos >= buf->size)
+			return 0;
+		size = buf->size < count ? buf->size : count;
+		size1 = buf->size - file->f_pos;
+		if (size1 < size)
+			size = size1;
+		if (copy_to_user(buffer, buf->buffer + file->f_pos, size))
+			return -EFAULT;
+		file->f_pos += size;
+		break;
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->read)
+			return entry->c.ops->read(entry,
+						  data->file_private_data,
+						  file, buffer, count);
+		if (size > 0)
+			file->f_pos += size;
+		break;
+	}
+	return size;
+}
+
+static ssize_t snd_info_entry_write(struct file *file, const char *buffer,
+				    size_t count, loff_t * offset)
+{
+	snd_info_private_data_t *data;
+	struct snd_info_entry *entry;
+	snd_info_buffer_t *buf;
+	long size = 0, size1;
+
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	snd_assert(data != NULL, return -ENXIO);
+	entry = data->entry;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_TEXT:
+		buf = data->wbuffer;
+		if (buf == NULL)
+			return -EIO;
+		if (file->f_pos < 0)
+			return -EINVAL;
+		if (file->f_pos >= buf->len)
+			return -ENOMEM;
+		size = buf->len < count ? buf->len : count;
+		size1 = buf->len - file->f_pos;
+		if (size1 < size)
+			size = size1;
+		if (copy_from_user(buf->buffer + file->f_pos, buffer, size))
+			return -EFAULT;
+		if (buf->size < file->f_pos + size)
+			buf->size = file->f_pos + size;
+		file->f_pos += size;
+		break;
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->write)
+			return entry->c.ops->write(entry,
+						   data->file_private_data,
+						   file, buffer, count);
+		if (size > 0)
+			file->f_pos += size;
+		break;
+	}
+	return size;
+}
+
+static int snd_info_entry_open(struct inode *inode, struct file *file)
+{
+	snd_info_entry_t *entry;
+	snd_info_private_data_t *data;
+	snd_info_buffer_t *buffer;
+	struct proc_dir_entry *p;
+	int mode, err;
+
+	down(&info_mutex);
+	p = PDE(inode);
+	entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;
+	if (entry == NULL) {
+		up(&info_mutex);
+		return -ENODEV;
+	}
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	if (entry->module && !try_inc_mod_count(entry->module)) {
+		err = -EFAULT;
+		goto __error1;
+	}
+	mode = file->f_flags & O_ACCMODE;
+	if (mode == O_RDONLY || mode == O_RDWR) {
+		if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
+		     !entry->c.text.read_size) ||
+		    (entry->content == SNDRV_INFO_CONTENT_DATA &&
+		     entry->c.ops->read == NULL) ||
+		    entry->content == SNDRV_INFO_CONTENT_DEVICE) {
+		    	err = -ENODEV;
+		    	goto __error;
+		}
+	}
+	if (mode == O_WRONLY || mode == O_RDWR) {
+		if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
+					!entry->c.text.write_size) ||
+		    (entry->content == SNDRV_INFO_CONTENT_DATA &&
+		    			entry->c.ops->write == NULL) ||
+		    entry->content == SNDRV_INFO_CONTENT_DEVICE) {
+		    	err = -ENODEV;
+		    	goto __error;
+		}
+	}
+	data = snd_magic_kcalloc(snd_info_private_data_t, 0, GFP_KERNEL);
+	if (data == NULL) {
+		err = -ENOMEM;
+		goto __error;
+	}
+	data->entry = entry;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_TEXT:
+		if (mode == O_RDONLY || mode == O_RDWR) {
+			buffer = (snd_info_buffer_t *)
+				 	snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL);
+			if (buffer == NULL) {
+				snd_magic_kfree(data);
+				err = -ENOMEM;
+				goto __error;
+			}
+			buffer->len = (entry->c.text.read_size +
+				      (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+			buffer->buffer = vmalloc(buffer->len);
+			if (buffer->buffer == NULL) {
+				kfree(buffer);
+				snd_magic_kfree(data);
+				err = -ENOMEM;
+				goto __error;
+			}
+			buffer->curr = buffer->buffer;
+			data->rbuffer = buffer;
+		}
+		if (mode == O_WRONLY || mode == O_RDWR) {
+			buffer = (snd_info_buffer_t *)
+					snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL);
+			if (buffer == NULL) {
+				if (mode == O_RDWR) {
+					vfree(data->rbuffer->buffer);
+					kfree(data->rbuffer);
+				}
+				snd_magic_kfree(data);
+				err = -ENOMEM;
+				goto __error;
+			}
+			buffer->len = (entry->c.text.write_size +
+				      (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+			buffer->buffer = vmalloc(buffer->len);
+			if (buffer->buffer == NULL) {
+				if (mode == O_RDWR) {
+					vfree(data->rbuffer->buffer);
+					kfree(data->rbuffer);
+				}
+				kfree(buffer);
+				snd_magic_kfree(data);
+				err = -ENOMEM;
+				goto __error;
+			}
+			buffer->curr = buffer->buffer;
+			data->wbuffer = buffer;
+		}
+		break;
+	case SNDRV_INFO_CONTENT_DATA:	/* data */
+		if (entry->c.ops->open) {
+			if ((err = entry->c.ops->open(entry, mode,
+						      &data->file_private_data)) < 0) {
+				snd_magic_kfree(data);
+				goto __error;
+			}
+		}
+		break;
+	}
+	file->private_data = data;
+	up(&info_mutex);
+	if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
+	    (mode == O_RDONLY || mode == O_RDWR)) {
+		if (entry->c.text.read) {
+			down(&entry->access);
+			entry->c.text.read(entry, data->rbuffer);
+			up(&entry->access);
+		}
+	}
+	return 0;
+
+      __error:
+	dec_mod_count(entry->module);
+      __error1:
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	up(&info_mutex);
+	return err;
+}
+
+static int snd_info_entry_release(struct inode *inode, struct file *file)
+{
+	snd_info_entry_t *entry;
+	snd_info_private_data_t *data;
+	int mode;
+
+	mode = file->f_flags & O_ACCMODE;
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	entry = data->entry;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_TEXT:
+		if (mode == O_RDONLY || mode == O_RDWR) {
+			vfree(data->rbuffer->buffer);
+			kfree(data->rbuffer);
+		}
+		if (mode == O_WRONLY || mode == O_RDWR) {
+			if (entry->c.text.write) {
+				entry->c.text.write(entry, data->wbuffer);
+				if (data->wbuffer->error) {
+					snd_printk(KERN_WARNING "data write error to %s (%i)\n",
+						entry->name,
+						data->wbuffer->error);
+				}
+			}
+			vfree(data->wbuffer->buffer);
+			kfree(data->wbuffer);
+		}
+		break;
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->release)
+			entry->c.ops->release(entry, mode,
+					      data->file_private_data);
+		break;
+	}
+	dec_mod_count(entry->module);
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	snd_magic_kfree(data);
+	return 0;
+}
+
+static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
+{
+	snd_info_private_data_t *data;
+	struct snd_info_entry *entry;
+	unsigned int mask;
+
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	if (data == NULL)
+		return 0;
+	entry = data->entry;
+	mask = 0;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->poll)
+			return entry->c.ops->poll(entry,
+						  data->file_private_data,
+						  file, wait);
+		if (entry->c.ops->read)
+			mask |= POLLIN | POLLRDNORM;
+		if (entry->c.ops->write)
+			mask |= POLLOUT | POLLWRNORM;
+		break;
+	}
+	return mask;
+}
+
+static int snd_info_entry_ioctl(struct inode *inode, struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	snd_info_private_data_t *data;
+	struct snd_info_entry *entry;
+
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	if (data == NULL)
+		return 0;
+	entry = data->entry;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->ioctl)
+			return entry->c.ops->ioctl(entry,
+						   data->file_private_data,
+						   file, cmd, arg);
+		break;
+	}
+	return -ENOTTY;
+}
+
+static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	snd_info_private_data_t *data;
+	struct snd_info_entry *entry;
+
+	data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
+	if (data == NULL)
+		return 0;
+	entry = data->entry;
+	switch (entry->content) {
+	case SNDRV_INFO_CONTENT_DATA:
+		if (entry->c.ops->mmap)
+			return entry->c.ops->mmap(entry,
+						  data->file_private_data,
+						  inode, file, vma);
+		break;
+	}
+	return -ENXIO;
+}
+
+static struct file_operations snd_info_entry_operations =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	llseek:		snd_info_entry_llseek,
+	read:		snd_info_entry_read,
+	write:		snd_info_entry_write,
+	poll:		snd_info_entry_poll,
+	ioctl:		snd_info_entry_ioctl,
+	mmap:		snd_info_entry_mmap,
+	open:		snd_info_entry_open,
+	release:	snd_info_entry_release,
+};
+
+#ifdef LINUX_2_2
+static struct inode_operations snd_info_entry_inode_operations =
+{
+	&snd_info_entry_operations,	/* default sound info directory file-ops */
+};
+
+static struct inode_operations snd_info_device_inode_operations =
+{
+	&snd_fops,		/* default sound info directory file-ops */
+};
+#endif	/* LINUX_2_2 */
+
+static int snd_info_card_readlink(struct dentry *dentry,
+				  char *buffer, int buflen)
+{
+        char *s = PDE(dentry->d_inode)->data;
+#ifndef LINUX_2_2
+	return vfs_readlink(dentry, buffer, buflen, s);
+#else
+	int len;
+	
+	if (s == NULL)
+		return -EIO;
+	len = strlen(s);
+	if (len > buflen)
+		len = buflen;
+	if (copy_to_user(buffer, s, len))
+		return -EFAULT;
+	return len;
+#endif
+}
+
+#ifndef LINUX_2_2
+static int snd_info_card_followlink(struct dentry *dentry,
+				    struct nameidata *nd)
+{
+        char *s = PDE(dentry->d_inode)->data;
+        return vfs_follow_link(nd, s);
+}
+#else
+static struct dentry *snd_info_card_followlink(struct dentry *dentry,
+					       struct dentry *base,
+					       unsigned int follow)
+{
+	char *s = PDE(dentry->d_inode)->data;
+	return lookup_dentry(s, base, follow);
+}
+#endif
+
+#ifdef LINUX_2_2
+static struct file_operations snd_info_card_link_operations =
+{
+	NULL
+};
+#endif
+
+struct inode_operations snd_info_card_link_inode_operations =
+{
+#ifdef LINUX_2_2
+	default_file_ops:	&snd_info_card_link_operations,
+#endif
+	readlink:		snd_info_card_readlink,
+	follow_link:		snd_info_card_followlink,
+};
+
+struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
+					     struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *p;
+	p = create_proc_entry(name, mode, parent);
+	if (p)
+		snd_info_entry_prepare(p);
+	return p;
+}
+
+int __init snd_info_init(void)
+{
+	struct proc_dir_entry *p;
+
+	p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
+	if (p == NULL)
+		return -ENOMEM;
+	snd_proc_root = p;
+	p = snd_create_proc_entry("dev", S_IFDIR | S_IRUGO | S_IXUGO, snd_proc_root);
+	if (p == NULL)
+		return -ENOMEM;
+	snd_proc_dev = p;
+#ifdef CONFIG_SND_OSSEMUL
+	{
+		snd_info_entry_t *entry;
+		if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
+			return -ENOMEM;
+		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			return -ENOMEM;
+		}
+		snd_oss_root = entry;
+	}
+#endif
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	{
+		snd_info_entry_t *entry;
+		if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
+			return -ENOMEM;
+		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			return -ENOMEM;
+		}
+		snd_seq_root = entry;
+	}
+#endif
+	snd_info_version_init();
+#ifdef CONFIG_SND_DEBUG_MEMORY
+	snd_memory_info_init();
+#endif
+	snd_minor_info_init();
+#ifdef CONFIG_SND_OSSEMUL
+	snd_minor_info_oss_init();
+#endif
+	snd_card_info_init();
+	return 0;
+}
+
+int __exit snd_info_done(void)
+{
+	snd_card_info_done();
+#ifdef CONFIG_SND_OSSEMUL
+	snd_minor_info_oss_done();
+#endif
+	snd_minor_info_done();
+#ifdef CONFIG_SND_DEBUG_MEMORY
+	snd_memory_info_done();
+#endif
+	snd_info_version_done();
+	if (snd_proc_root) {
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+		if (snd_seq_root)
+			snd_info_unregister(snd_seq_root);
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+		if (snd_oss_root)
+			snd_info_unregister(snd_oss_root);
+#endif
+		snd_remove_proc_entry(snd_proc_root, snd_proc_dev);
+		snd_remove_proc_entry(&proc_root, snd_proc_root);
+	}
+	return 0;
+}
+
+/*
+
+ */
+
+
+int snd_info_card_register(snd_card_t * card)
+{
+	char str[8];
+	char *s;
+	snd_info_entry_t *entry;
+	struct proc_dir_entry *p;
+
+	snd_assert(card != NULL, return -ENXIO);
+
+	sprintf(str, "card%i", card->number);
+	if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
+		return -ENOMEM;
+	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	card->proc_root = entry;
+
+	if (!strcmp(card->id, str))
+		return 0;
+
+	s = snd_kmalloc_strdup(str, GFP_KERNEL);
+	if (s == NULL)
+		return -ENOMEM;
+	p = snd_create_proc_entry(card->id, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, snd_proc_root);
+	if (p == NULL)
+		return -ENOMEM;
+	p->data = s;
+#ifndef LINUX_2_2
+	p->owner = card->module;
+	p->proc_iops = &snd_info_card_link_inode_operations;
+#else
+	p->ops = &snd_info_card_link_inode_operations;
+#endif
+	card->proc_root_link = p;
+	return 0;
+}
+
+int snd_info_card_unregister(snd_card_t * card)
+{
+	void *data;
+
+	snd_assert(card != NULL, return -ENXIO);
+	if (card->proc_root_link) {
+		data = card->proc_root_link->data;
+		card->proc_root_link->data = NULL;
+		kfree(data);
+		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+		card->proc_root_link = NULL;
+	}
+	if (card->proc_root) {
+		snd_info_unregister(card->proc_root);
+		card->proc_root = NULL;
+	}
+	return 0;
+}
+
+/*
+
+ */
+
+int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len)
+{
+	int c = -1;
+
+	if (len <= 0 || buffer->stop || buffer->error)
+		return 1;
+	while (--len > 0) {
+		c = *buffer->curr++;
+		if (c == '\n') {
+			if ((buffer->curr - buffer->buffer) >= buffer->size) {
+				buffer->stop = 1;
+			}
+			break;
+		}
+		*line++ = c;
+		if ((buffer->curr - buffer->buffer) >= buffer->size) {
+			buffer->stop = 1;
+			break;
+		}
+	}
+	while (c != '\n' && !buffer->stop) {
+		c = *buffer->curr++;
+		if ((buffer->curr - buffer->buffer) >= buffer->size) {
+			buffer->stop = 1;
+		}
+	}
+	*line = '\0';
+	return 0;
+}
+
+char *snd_info_get_str(char *dest, char *src, int len)
+{
+	int c;
+
+	while (*src == ' ' || *src == '\t')
+		src++;
+	if (*src == '"' || *src == '\'') {
+		c = *src++;
+		while (--len > 0 && *src && *src != c) {
+			*dest++ = *src++;
+		}
+		if (*src == c)
+			src++;
+	} else {
+		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
+			*dest++ = *src++;
+		}
+	}
+	*dest = 0;
+	while (*src == ' ' || *src == '\t')
+		src++;
+	return src;
+}
+
+static snd_info_entry_t *snd_info_create_entry(const char *name)
+{
+	snd_info_entry_t *entry;
+	entry = (snd_info_entry_t *) snd_kcalloc(sizeof(snd_info_entry_t), GFP_KERNEL);
+	if (entry == NULL)
+		return NULL;
+	entry->name = snd_kmalloc_strdup(name, GFP_KERNEL);
+	if (entry->name == NULL) {
+		kfree(entry);
+		return NULL;
+	}
+	entry->mode = S_IFREG | S_IRUGO;
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	init_MUTEX(&entry->access);
+	return entry;
+}
+
+snd_info_entry_t *snd_info_create_module_entry(struct module * module,
+					       const char *name,
+					       snd_info_entry_t *parent)
+{
+	snd_info_entry_t *entry = snd_info_create_entry(name);
+	if (entry) {
+		entry->module = module;
+		entry->parent = parent;
+	}
+	return entry;
+}
+
+snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card,
+					     const char *name,
+					     snd_info_entry_t * parent)
+{
+	snd_info_entry_t *entry = snd_info_create_entry(name);
+	if (entry) {
+		entry->module = card->module;
+		entry->card = card;
+		entry->parent = parent;
+	}
+	return entry;
+}
+
+void snd_info_free_entry(snd_info_entry_t * entry)
+{
+	if (entry == NULL)
+		return;
+	if (entry->name)
+		kfree((char *)entry->name);
+	if (entry->private_free)
+		entry->private_free(entry);
+	kfree(entry);
+}
+
+#ifdef LINUX_2_2
+static void snd_info_device_fill_inode(struct inode *inode, int fill)
+{
+	struct proc_dir_entry *de;
+	snd_info_entry_t *entry;
+
+	if (!fill) {
+		MOD_DEC_USE_COUNT;
+		return;
+	}
+	MOD_INC_USE_COUNT;
+	de = PDE(inode);
+	if (de == NULL)
+		return;
+	entry = (snd_info_entry_t *) de->data;
+	if (entry == NULL)
+		return;
+	inode->i_gid = snd_device_gid;
+	inode->i_uid = snd_device_uid;
+	inode->i_rdev = MKDEV(entry->c.device.major, entry->c.device.minor);
+}
+
+static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry)
+{
+	de->fill_inode = snd_info_device_fill_inode;
+}
+#else
+static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry)
+{
+	de->rdev = mk_kdev(entry->c.device.major, entry->c.device.minor);
+	de->owner = THIS_MODULE;
+}
+#endif /* LINUX_2_2 */
+
+snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode)
+{
+#ifdef CONFIG_DEVFS_FS
+	char dname[32];
+#endif
+	unsigned short major = number >> 16;
+	unsigned short minor = (unsigned short) number;
+	snd_info_entry_t *entry;
+	struct proc_dir_entry *p = NULL;
+
+	if (!major)
+		major = snd_major;
+	if (!mode)
+		mode = S_IFCHR | S_IRUGO | S_IWUGO;
+	mode &= (snd_device_mode & (S_IRUGO | S_IWUGO)) | S_IFCHR | S_IFBLK;
+	entry = snd_info_create_module_entry(THIS_MODULE, name, NULL);
+	if (entry == NULL)
+		return NULL;
+	entry->content = SNDRV_INFO_CONTENT_DEVICE;
+	entry->mode = mode;
+	entry->c.device.major = major;
+	entry->c.device.minor = minor;
+	down(&info_mutex);
+	p = create_proc_entry(entry->name, entry->mode, snd_proc_dev);
+	if (p) {
+		snd_info_device_entry_prepare(p, entry);
+#ifndef LINUX_2_2
+		/* we should not set this - at least on 2.4.14 or later it causes
+		   problems! */
+		/* p->proc_fops = &snd_fops; */
+#else
+		p->ops = &snd_info_device_inode_operations;
+#endif
+	} else {
+		up(&info_mutex);
+		snd_info_free_entry(entry);
+		return NULL;
+	}
+	p->gid = snd_device_gid;
+	p->uid = snd_device_uid;
+	p->data = (void *) entry;
+	entry->p = p;
+	up(&info_mutex);
+#ifdef CONFIG_DEVFS_FS
+	if (strncmp(name, "controlC", 8)) {	/* created in sound.c */
+		sprintf(dname, "snd/%s", name);
+		devfs_register(NULL, dname, DEVFS_FL_DEFAULT,
+				major, minor, mode,
+				&snd_fops, NULL);
+	}
+#endif
+	return entry;
+}
+
+void snd_info_free_device(snd_info_entry_t * entry)
+{
+#ifdef CONFIG_DEVFS_FS
+	char dname[32];
+	devfs_handle_t master;
+#endif
+
+	snd_runtime_check(entry, return);
+	down(&info_mutex);
+	snd_remove_proc_entry(snd_proc_dev, entry->p);
+	up(&info_mutex);
+#ifdef CONFIG_DEVFS_FS
+	if (entry->p && strncmp(entry->name, "controlC", 8)) {
+		sprintf(dname, "snd/%s", entry->name);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+		master = devfs_find_handle(NULL, dname, strlen(dname), 0, 0, DEVFS_SPECIAL_CHR, 0);
+#else
+		master = devfs_find_handle(NULL, dname, 0, 0, DEVFS_SPECIAL_CHR, 0);
+#endif
+		devfs_unregister(master);
+	}
+#endif
+	snd_info_free_entry(entry);
+}
+
+int snd_info_register(snd_info_entry_t * entry)
+{
+	struct proc_dir_entry *root, *p = NULL;
+
+	snd_assert(entry != NULL, return -ENXIO);
+	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+	down(&info_mutex);
+	p = snd_create_proc_entry(entry->name, entry->mode, root);
+	if (!p) {
+		up(&info_mutex);
+		return -ENOMEM;
+	}
+#ifndef LINUX_2_2
+	p->owner = entry->module;
+#endif
+	if (!S_ISDIR(entry->mode)) {
+#ifndef LINUX_2_2
+		p->proc_fops = &snd_info_entry_operations;
+#else
+		p->ops = &snd_info_entry_inode_operations;
+#endif
+	}
+	p->size = entry->size;
+	p->data = entry;
+	entry->p = p;
+	up(&info_mutex);
+	return 0;
+}
+
+int snd_info_unregister(snd_info_entry_t * entry)
+{
+	struct proc_dir_entry *root;
+
+	snd_assert(entry != NULL && entry->p != NULL, return -ENXIO);
+	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+	snd_assert(root, return -ENXIO);
+	down(&info_mutex);
+	snd_remove_proc_entry(root, entry->p);
+	up(&info_mutex);
+	snd_info_free_entry(entry);
+	return 0;
+}
+
+/*
+
+ */
+
+static snd_info_entry_t *snd_info_version_entry = NULL;
+
+static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	static char *kernel_version = UTS_RELEASE;
+
+	snd_iprintf(buffer,
+		    "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
+		    "Compiled on " __DATE__ " for kernel %s"
+#ifdef __SMP__
+		    " (SMP)"
+#endif
+#ifdef MODVERSIONS
+		    " with versioned symbols"
+#endif
+		    ".\n", kernel_version);
+}
+
+static int __init snd_info_version_init(void)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
+	if (entry == NULL)
+		return -ENOMEM;
+	entry->c.text.read_size = 256;
+	entry->c.text.read = snd_info_version_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	snd_info_version_entry = entry;
+	return 0;
+}
+
+static int __exit snd_info_version_done(void)
+{
+	if (snd_info_version_entry)
+		snd_info_unregister(snd_info_version_entry);
+	return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
diff -Nru linux/sound/core/info_oss.c linux-2.4.19-pre5-mjc/sound/core/info_oss.c
--- linux/sound/core/info_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/info_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,151 @@
+/*
+ *  Information interface for ALSA driver
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <linux/utsname.h>
+
+#ifdef CONFIG_SND_OSSEMUL
+
+/*
+ *  OSS compatible part
+ */
+
+static DECLARE_MUTEX(strings);
+static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
+static snd_info_entry_t *snd_sndstat_proc_entry;
+
+int snd_oss_info_register(int dev, int num, char *string)
+{
+	char *x;
+
+	snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO);
+	snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO);
+	down(&strings);
+	if (string == NULL) {
+		if ((x = snd_sndstat_strings[num][dev]) != NULL) {
+			kfree(x);
+			x = NULL;
+		}
+	} else {
+		x = snd_kmalloc_strdup(string, GFP_KERNEL);
+		if (x == NULL) {
+			up(&strings);
+			return -ENOMEM;
+		}
+	}
+	snd_sndstat_strings[num][dev] = x;
+	up(&strings);
+	return 0;
+}
+
+extern void snd_card_info_read_oss(snd_info_buffer_t * buffer);
+
+static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev)
+{
+	int idx, ok = -1;
+	char *str;
+
+	snd_iprintf(buf, "\n%s:", id);
+	down(&strings);
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		str = snd_sndstat_strings[idx][dev];
+		if (str) {
+			if (ok < 0) {
+				snd_iprintf(buf, "\n");
+				ok++;
+			}
+			snd_iprintf(buf, "%i: %s\n", idx, str);
+		}
+	}
+	up(&strings);
+	if (ok < 0)
+		snd_iprintf(buf, " NOT ENABLED IN CONFIG\n");
+	return ok;
+}
+
+static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
+	snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
+		    system_utsname.sysname,
+		    system_utsname.nodename,
+		    system_utsname.release,
+		    system_utsname.version,
+		    system_utsname.machine);
+	snd_iprintf(buffer, "Config options: 0\n");
+	snd_iprintf(buffer, "\nInstalled drivers: \n");
+	snd_iprintf(buffer, "Type 10: ALSA emulation\n");
+	snd_iprintf(buffer, "\nCard config: \n");
+	snd_card_info_read_oss(buffer);
+	snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO);
+	snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH);
+	snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI);
+	snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS);
+	snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
+}
+
+int snd_info_minor_register(void)
+{
+	snd_info_entry_t *entry;
+
+	memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
+	if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 2048;
+		entry->c.text.read = snd_sndstat_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_sndstat_proc_entry = entry;
+	return 0;
+}
+
+int snd_info_minor_unregister(void)
+{
+	if (snd_sndstat_proc_entry) {
+		snd_info_unregister(snd_sndstat_proc_entry);
+		snd_sndstat_proc_entry = NULL;
+	}
+	return 0;
+}
+
+#else
+
+int snd_info_minor_register(void)
+{
+	return 0;
+}
+
+int snd_info_minor_unregister(void)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SND_OSSEMUL */
diff -Nru linux/sound/core/init.c linux-2.4.19-pre5-mjc/sound/core/init.c
--- linux/sound/core/init.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/init.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,295 @@
+/*
+ *  Initialization routines
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+int snd_cards_count = 0;
+static unsigned int snd_cards_lock = 0;	/* locked for registering/using */
+snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL};
+rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED;
+
+#ifdef CONFIG_SND_OSSEMUL
+int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag);
+#endif
+
+static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	snd_iprintf(buffer, "%s\n", entry->card->id);
+}
+
+snd_card_t *snd_card_new(int idx, const char *xid,
+			 struct module *module, int extra_size)
+{
+	snd_card_t *card;
+	snd_info_entry_t *entry;
+	int err;
+
+	if (extra_size < 0)
+		extra_size = 0;
+	card = (snd_card_t *) snd_kcalloc(sizeof(snd_card_t) + extra_size, GFP_KERNEL);
+	if (card == NULL)
+		return NULL;
+	if (xid) {
+		if (!snd_info_check_reserved_words(xid))
+			goto __error;
+		strncpy(card->id, xid, sizeof(card->id) - 1);
+	}
+	write_lock(&snd_card_rwlock);
+	if (idx < 0) {
+		int idx2;
+		for (idx2 = 0; idx2 < snd_ecards_limit; idx2++)
+			if (!(snd_cards_lock & (1 << idx2))) {
+				idx = idx2;
+				break;
+			}
+	} else if (idx < snd_ecards_limit) {
+		if (snd_cards_lock & (1 << idx))
+			idx = -1;	/* invalid */
+	}
+	if (idx < 0 || idx >= snd_ecards_limit) {
+		write_unlock(&snd_card_rwlock);
+		if (idx >= snd_ecards_limit)
+			snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1);
+		goto __error;
+	}
+	snd_cards_lock |= 1 << idx;		/* lock it */
+	write_unlock(&snd_card_rwlock);
+	card->number = idx;
+	if (!card->id[0])
+		sprintf(card->id, "card%i", card->number);
+	card->module = module;
+	INIT_LIST_HEAD(&card->devices);
+	rwlock_init(&card->control_rwlock);
+	rwlock_init(&card->control_owner_lock);
+	INIT_LIST_HEAD(&card->controls);
+	INIT_LIST_HEAD(&card->ctl_files);
+#ifdef CONFIG_PM
+	init_MUTEX(&card->power_lock);
+	init_waitqueue_head(&card->power_sleep);
+#endif
+	/* the control interface cannot be accessed from the user space until */
+	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
+	if ((err = snd_ctl_register(card)) < 0) {
+		snd_printd("unable to register control minors\n");
+		goto __error;
+	}
+	if ((err = snd_info_card_register(card)) < 0) {
+		snd_printd("unable to register card info\n");
+		goto __error_ctl;
+	}
+	if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
+		snd_printd("unable to create card entry\n");
+		goto __error_info;
+	}
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->c.text.read_size = PAGE_SIZE;
+	entry->c.text.read = snd_card_id_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		goto __error_info;
+	}
+	card->proc_id = entry;
+	if (extra_size > 0)
+		card->private_data = (char *)card + sizeof(snd_card_t);
+	return card;
+
+      __error_info:
+      	snd_info_card_unregister(card);
+      __error_ctl:
+	snd_ctl_unregister(card);
+      __error:
+	kfree(card);
+      	return NULL;
+}
+
+int snd_card_free(snd_card_t * card)
+{
+	if (card == NULL)
+		return -EINVAL;
+	write_lock(&snd_card_rwlock);
+	snd_cards[card->number] = NULL;
+	snd_cards_count--;
+	write_unlock(&snd_card_rwlock);
+#ifdef CONFIG_SND_OSSEMUL
+	if (snd_mixer_oss_notify_callback)
+		snd_mixer_oss_notify_callback(card, 1);
+#endif
+	if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
+		snd_printk(KERN_ERR "unable to free all devices (pre)\n");
+		/* Fatal, but this situation should never occur */
+	}
+	if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
+		snd_printk(KERN_ERR "unable to free all devices (normal)\n");
+		/* Fatal, but this situation should never occur */
+	}
+	if (snd_ctl_unregister(card) < 0) {
+		snd_printk(KERN_ERR "unable to unregister control minors\n");
+		/* Not fatal error */
+	}
+	if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
+		snd_printk(KERN_ERR "unable to free all devices (post)\n");
+		/* Fatal, but this situation should never occur */
+	}
+	if (card->private_free)
+		card->private_free(card);
+	snd_info_free_entry(card->proc_id);
+	if (snd_info_card_unregister(card) < 0) {
+		snd_printk(KERN_WARNING "unable to unregister card info\n");
+		/* Not fatal error */
+	}
+	write_lock(&snd_card_rwlock);
+	snd_cards_lock &= ~(1 << card->number);
+	write_unlock(&snd_card_rwlock);
+	kfree(card);
+	return 0;
+}
+
+int snd_card_register(snd_card_t * card)
+{
+	int err;
+
+	snd_runtime_check(card != NULL, return -EINVAL);
+	if ((err = snd_device_register_all(card)) < 0)
+		return err;
+	write_lock(&snd_card_rwlock);
+	snd_cards[card->number] = card;
+	snd_cards_count++;
+	write_unlock(&snd_card_rwlock);
+#ifdef CONFIG_SND_OSSEMUL
+	if (snd_mixer_oss_notify_callback)
+		snd_mixer_oss_notify_callback(card, 0);
+#endif
+	return 0;
+}
+
+static snd_info_entry_t *snd_card_info_entry = NULL;
+
+static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	int idx, count;
+	snd_card_t *card;
+
+	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
+		read_lock(&snd_card_rwlock);
+		if ((card = snd_cards[idx]) != NULL) {
+			count++;
+			snd_iprintf(buffer, "%i [%-15s]: %s - %s\n",
+					idx,
+					card->id,
+					card->driver,
+					card->shortname);
+			snd_iprintf(buffer, "                     %s\n",
+					card->longname);
+		}
+		read_unlock(&snd_card_rwlock);
+	}
+	if (!count)
+		snd_iprintf(buffer, "--- no soundcards ---\n");
+}
+
+#ifdef CONFIG_SND_OSSEMUL
+
+void snd_card_info_read_oss(snd_info_buffer_t * buffer)
+{
+	int idx, count;
+	snd_card_t *card;
+
+	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
+		read_lock(&snd_card_rwlock);
+		if ((card = snd_cards[idx]) != NULL) {
+			count++;
+			snd_iprintf(buffer, "%s\n", card->longname);
+		}
+		read_unlock(&snd_card_rwlock);
+	}
+	if (!count) {
+		snd_iprintf(buffer, "--- no soundcards ---\n");
+	}
+}
+
+#endif
+
+int __init snd_card_info_init(void)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
+	snd_runtime_check(entry != NULL, return -ENOMEM);
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->c.text.read_size = PAGE_SIZE;
+	entry->c.text.read = snd_card_info_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	snd_card_info_entry = entry;
+	
+	return 0;
+}
+
+int __exit snd_card_info_done(void)
+{
+	if (snd_card_info_entry)
+		snd_info_unregister(snd_card_info_entry);
+	return 0;
+}
+
+int snd_component_add(snd_card_t *card, const char *component)
+{
+	char *ptr;
+	int len = strlen(component);
+
+	ptr = strstr(card->components, component);
+	if (ptr != NULL) {
+		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
+			return 1;
+	}
+	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
+		snd_BUG();
+		return -ENOMEM;
+	}
+	if (card->components[0] != '\0')
+		strcat(card->components, " ");
+	strcat(card->components, component);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/* the power lock must be active before call */
+void snd_power_wait(snd_card_t *card)
+{
+	wait_queue_t wait;
+
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&card->power_sleep, &wait);
+	snd_power_unlock(card);
+	schedule_timeout(30 * HZ);
+	remove_wait_queue(&card->power_sleep, &wait);
+	snd_power_lock(card);
+}
+#endif /* CONFIG_PM */
diff -Nru linux/sound/core/ioctl32/Makefile linux-2.4.19-pre5-mjc/sound/core/ioctl32/Makefile
--- linux/sound/core/ioctl32/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,17 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _ioctl32.o
+
+list-multi   := snd-ioctl32.o
+
+snd-ioctl32-objs := ioctl32.o pcm32.o rawmidi32.o timer32.o hwdep32.o
+
+obj-$(CONFIG_SND_BIT32_EMUL) += snd-ioctl32.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ioctl32.o: $(snd-ioctl32-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ioctl32-objs)
diff -Nru linux/sound/core/ioctl32/hwdep32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/hwdep32.c
--- linux/sound/core/ioctl32/hwdep32.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/hwdep32.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,37 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for timer API
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+#include <asm/uaccess.h>
+#include "ioctl32.h"
+
+#define AP(x) snd_ioctl32_##x
+
+struct ioctl32_mapper hwdep_mappers[] = {
+	{ SNDRV_HWDEP_IOCTL_PVERSION, NULL },
+	{ SNDRV_HWDEP_IOCTL_INFO, NULL },
+	{ SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL },
+	{ SNDRV_CTL_IOCTL_HWDEP_INFO, NULL },
+	{ 0 },
+};
diff -Nru linux/sound/core/ioctl32/ioctl32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.c
--- linux/sound/core/ioctl32/ioctl32.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,357 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for control API
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/smp_lock.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <asm/uaccess.h>
+#include "ioctl32.h"
+
+/*
+ * register/unregister mappers
+ * exported for other modules
+ */
+
+int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
+int unregister_ioctl32_conversion(unsigned int cmd);
+
+
+int snd_ioctl32_register(struct ioctl32_mapper *mappers)
+{
+	int err;
+	struct ioctl32_mapper *m;
+
+	lock_kernel();
+	for (m = mappers; m->cmd; m++) {
+		err = register_ioctl32_conversion(m->cmd, m->handler);
+		if (err < 0) {
+			unlock_kernel();
+			return err;
+		}
+		m->registered++;
+	}
+	return 0;
+}
+
+void snd_ioctl32_unregister(struct ioctl32_mapper *mappers)
+{
+	struct ioctl32_mapper *m;
+
+	lock_kernel();
+	for (m = mappers; m->cmd; m++) {
+		if (m->registered) {
+			unregister_ioctl32_conversion(m->cmd);
+			m->registered = 0;
+		}
+	}
+	unlock_kernel();
+}
+
+
+/*
+ * Controls
+ */
+
+struct sndrv_ctl_elem_list32 {
+	u32 offset;
+	u32 space;
+	u32 used;
+	u32 count;
+	u32 pids;
+	unsigned char reserved[50];
+};
+
+#define CVT_sndrv_ctl_elem_list()\
+{\
+	COPY(offset);\
+	COPY(space);\
+	COPY(used);\
+	COPY(count);\
+	CPTR(pids);\
+}
+
+DEFINE_ALSA_IOCTL(ctl_elem_list);
+
+
+/*
+ * control element info
+ * it uses union, so the things are not easy..
+ */
+
+struct sndrv_ctl_elem_info32 {
+	struct sndrv_ctl_elem_id id; // the size of struct is same
+	s32 type;
+	u32 access;
+	u32 count;
+	s32 owner;
+	union {
+		struct {
+			s32 min;
+			s32 max;
+			s32 step;
+		} integer;
+		struct {
+			u32 items;
+			u32 item;
+			char name[64];
+		} enumerated;
+		unsigned char reserved[128];
+	} value;
+	unsigned char reserved[64];
+};
+
+static int snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
+{
+	struct sndrv_ctl_elem_info data;
+	struct sndrv_ctl_elem_info32 data32;
+	int err;
+
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
+	memset(&data, 0, sizeof(data));
+	data.id = data32.id;
+	err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data);
+	if (err < 0)
+		return err;
+	/* restore info to 32bit */
+	data32.type = data.type;
+	data32.access = data.access;
+	data32.count = data.count;
+	data32.owner = data.owner;
+	switch (data.type) {
+	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+	case SNDRV_CTL_ELEM_TYPE_INTEGER:
+		data32.value.integer.min = data.value.integer.min;
+		data32.value.integer.max = data.value.integer.min;
+		data32.value.integer.step = data.value.integer.step;
+		break;
+	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+		data32.value.enumerated.items = data.value.enumerated.items;
+		data32.value.enumerated.item = data.value.enumerated.item;
+		memcpy(data32.value.enumerated.name, data.value.enumerated.name,
+		       sizeof(data.value.enumerated.name));
+		break;
+	default:
+		break;
+	}
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return -EFAULT;
+	return err;
+}
+
+
+struct sndrv_ctl_elem_value32 {
+	struct sndrv_ctl_elem_id id;
+	unsigned int indirect: 1;
+        union {
+		union {
+			s32 value[128];
+			u32 value_ptr;
+		} integer;
+		union {
+			u32 item[128];
+			u32 item_ptr;
+		} enumerated;
+		union {
+			unsigned char data[512];
+			u32 data_ptr;
+		} bytes;
+		struct sndrv_aes_iec958 iec958;
+        } value;
+        unsigned char reserved[128];
+};
+
+
+/* hmm, it's so hard to retrieve the value type from the control id.. */
+static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id)
+{
+	snd_ctl_file_t *ctl;
+	snd_kcontrol_t *kctl;
+	snd_ctl_elem_info_t info;
+	int err;
+
+	ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO);
+
+	read_lock(&ctl->card->control_rwlock);
+	kctl = snd_ctl_find_id(ctl->card, id);
+	if (! kctl) {
+		read_unlock(&ctl->card->control_rwlock);
+		return -ENXIO;
+	}
+	info.id = *id;
+	err = kctl->info(kctl, &info);
+	if (err >= 0)
+		err = info.type;
+	read_unlock(&ctl->card->control_rwlock);
+	return err;
+}
+
+
+static int snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
+{
+	// too big?
+	struct sndrv_ctl_elem_value data;
+	struct sndrv_ctl_elem_value32 data32;
+	int err, i;
+	int type;
+	/* FIXME: check the sane ioctl.. */
+
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
+	memset(&data, 0, sizeof(data));
+	data.id = data32.id;
+	data.indirect = data32.indirect;
+	if (data.indirect) /* FIXME: this is not correct for long arrays */
+		data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr);
+	type = get_ctl_type(file, &data.id);
+	if (type < 0)
+		return type;
+	if (! data.indirect) {
+		switch (type) {
+		case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+		case SNDRV_CTL_ELEM_TYPE_INTEGER:
+			for (i = 0; i < 128; i++)
+				data.value.integer.value[i] = data32.value.integer.value[i];
+			break;
+		case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+			for (i = 0; i < 128; i++)
+				data.value.enumerated.item[i] = data32.value.enumerated.item[i];
+			break;
+		case SNDRV_CTL_ELEM_TYPE_BYTES:
+			memcpy(data.value.bytes.data, data32.value.bytes.data,
+			       sizeof(data.value.bytes.data));
+			break;
+		case SNDRV_CTL_ELEM_TYPE_IEC958:
+			data.value.iec958 = data32.value.iec958;
+			break;
+		default:
+			break;
+		}
+	}
+
+	err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data);
+	if (err < 0)
+		return err;
+	/* restore info to 32bit */
+	if (! data.indirect) {
+		switch (type) {
+		case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+		case SNDRV_CTL_ELEM_TYPE_INTEGER:
+			for (i = 0; i < 128; i++)
+				data.value.integer.value[i] = data32.value.integer.value[i];
+			break;
+		case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+			for (i = 0; i < 128; i++)
+				data.value.enumerated.item[i] = data32.value.enumerated.item[i];
+			break;
+		case SNDRV_CTL_ELEM_TYPE_BYTES:
+			memcpy(data.value.bytes.data, data32.value.bytes.data,
+			       sizeof(data.value.bytes.data));
+			break;
+		case SNDRV_CTL_ELEM_TYPE_IEC958:
+			data.value.iec958 = data32.value.iec958;
+			break;
+		default:
+			break;
+		}
+	}
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return -EFAULT;
+	return err;
+}
+
+
+/*
+ */
+
+#define AP(x) snd_ioctl32_##x
+
+static struct ioctl32_mapper control_mappers[] = {
+	/* controls (without rawmidi, hwdep, timer releated ones) */
+	{ SNDRV_CTL_IOCTL_PVERSION, NULL },
+	{ SNDRV_CTL_IOCTL_CARD_INFO , NULL },
+	{ SNDRV_CTL_IOCTL_ELEM_LIST, AP(ctl_elem_list) },
+	{ SNDRV_CTL_IOCTL_ELEM_INFO, AP(ctl_elem_info) },
+	{ SNDRV_CTL_IOCTL_ELEM_READ, AP(ctl_elem_value) },
+	{ SNDRV_CTL_IOCTL_ELEM_WRITE, AP(ctl_elem_value) },
+	{ SNDRV_CTL_IOCTL_ELEM_LOCK, NULL },
+	{ SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL },
+	{ SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL },
+	{ SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL },
+	{ SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL },
+	{ SNDRV_CTL_IOCTL_PCM_INFO, NULL },
+	{ SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL },
+	{ SNDRV_CTL_IOCTL_POWER, NULL },
+	{ SNDRV_CTL_IOCTL_POWER_STATE, NULL },
+	{ 0 }
+};
+
+
+/*
+ */
+
+extern struct ioctl32_mapper pcm_mappers[];
+extern struct ioctl32_mapper rawmidi_mappers[];
+extern struct ioctl32_mapper timer_mappers[];
+extern struct ioctl32_mapper hwdep_mappers[];
+
+static void snd_ioctl32_done(void)
+{
+	snd_ioctl32_unregister(hwdep_mappers);
+	snd_ioctl32_unregister(timer_mappers);
+	snd_ioctl32_unregister(rawmidi_mappers);
+	snd_ioctl32_unregister(pcm_mappers);
+	snd_ioctl32_unregister(control_mappers);
+}
+
+static int __init snd_ioctl32_init(void)
+{
+	int err;
+	
+	err = snd_ioctl32_register(control_mappers);
+	if (err < 0)
+		return err;
+	err = snd_ioctl32_register(pcm_mappers);
+	if (err < 0) {
+		snd_ioctl32_done();
+		return err;
+	}
+	err = snd_ioctl32_register(rawmidi_mappers);
+	if (err < 0) {
+		snd_ioctl32_done();
+		return err;
+	}
+	err = snd_ioctl32_register(timer_mappers);
+	if (err < 0) {
+		snd_ioctl32_done();
+		return err;
+	}
+	err = snd_ioctl32_register(hwdep_mappers);
+	if (err < 0) {
+		snd_ioctl32_done();
+		return err;
+	}
+}
+
+module_init(snd_ioctl32_init)
+module_exit(snd_ioctl32_done)
diff -Nru linux/sound/core/ioctl32/ioctl32.h linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.h
--- linux/sound/core/ioctl32/ioctl32.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,79 @@
+/*
+ *   32bit -> 64bit ioctl helpers
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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 file registers the converters from 32-bit ioctls to 64-bit ones.
+ * The converter assumes that a 32-bit user-pointer can be casted by A(x)
+ * macro to a valid 64-bit pointer which is accessible via copy_from/to_user.
+ *
+ */
+
+#ifndef __ALSA_IOCTL32_H
+#define __ALSA_IOCTL32_H
+
+#define TO_PTR(x)  A(x)
+
+#define COPY(x)  (dst->x = src->x)
+#define CPTR(x)	 (dst->x = (typeof(dst->x))A(src->x))
+
+#define convert_from_32(type, dstp, srcp)\
+{\
+	struct sndrv_##type *dst = dstp;\
+	struct sndrv_##type##32 *src = srcp;\
+	CVT_##sndrv_##type();\
+}
+
+#define convert_to_32(type, dstp, srcp)\
+{\
+	struct sndrv_##type *src = srcp;\
+	struct sndrv_##type##32 *dst = dstp;\
+	CVT_##sndrv_##type();\
+}
+
+
+#define DEFINE_ALSA_IOCTL(type) \
+static int snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)\
+{\
+	struct sndrv_##type##32 data32;\
+	struct sndrv_##type data;\
+	int err;\
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\
+		return -EFAULT;\
+	memset(&data, 0, sizeof(data));\
+	convert_from_32(type, &data, &data32);\
+	err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data);\
+	if (err < 0)\
+		return err;\
+	if (cmd & (_IOC_READ << _IOC_DIRSHIFT)) {\
+		convert_to_32(type, &data32, &data);\
+		if (copy_to_user((void*)arg, &data32, sizeof(data32)))\
+			return -EFAULT;\
+	}\
+	return err;\
+}
+
+struct ioctl32_mapper {
+	unsigned int cmd;
+	int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
+	int registered;
+};
+
+int snd_ioctl32_register(struct ioctl32_mapper *mappers);
+void snd_ioctl32_unregister(struct ioctl32_mapper *mappers);
+
+#endif /* __ALSA_IOCTL32_H */
diff -Nru linux/sound/core/ioctl32/pcm32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/pcm32.c
--- linux/sound/core/ioctl32/pcm32.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/pcm32.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,296 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for PCM API
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "ioctl32.h"
+
+
+/* wrapper for sndrv_pcm_[us]frames */
+struct sndrv_pcm_sframes_str {
+	sndrv_pcm_sframes_t val;
+};
+struct sndrv_pcm_sframes_str32 {
+	s32 val;
+};
+struct sndrv_pcm_uframes_str {
+	sndrv_pcm_uframes_t val;
+};
+struct sndrv_pcm_uframes_str32 {
+	u32 val;
+};
+
+#define CVT_sndrv_pcm_sframes_str() { COPY(val); }
+#define CVT_sndrv_pcm_uframes_str() { COPY(val); }
+
+
+struct sndrv_interval32 {
+	u32 min, max;
+	unsigned int openmin:1,
+		     openmax:1,
+		     integer:1,
+		     empty:1;
+};
+
+struct sndrv_pcm_hw_params32 {
+	u32 flags;
+	u32 masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
+	struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+	u32 rmask;
+	u32 cmask;
+	u32 info;
+	u32 msbits;
+	u32 rate_num;
+	u32 rate_den;
+	u32 fifo_size;
+	unsigned char reserved[64];
+};
+
+#define numberof(array)  (sizeof(array)/sizeof(array[0]))
+
+#define CVT_sndrv_pcm_hw_params()\
+{\
+	int i;\
+	COPY(flags);\
+	for (i = 0; i < numberof(dst->masks); i++)\
+		COPY(masks[i]);\
+	for (i = 0; i < numberof(dst->intervals); i++) {\
+		COPY(intervals[i].min);\
+		COPY(intervals[i].max);\
+		COPY(intervals[i].openmin);\
+		COPY(intervals[i].openmax);\
+		COPY(intervals[i].integer);\
+		COPY(intervals[i].empty);\
+	}\
+	COPY(rmask);\
+	COPY(cmask);\
+	COPY(info);\
+	COPY(msbits);\
+	COPY(rate_num);\
+	COPY(rate_den);\
+	COPY(fifo_size);\
+}
+
+struct sndrv_pcm_sw_params32 {
+	s32 tstamp_mode;
+	u32 period_step;
+	u32 sleep_min;
+	u32 avail_min;
+	u32 xfer_align;
+	u32 start_threshold;
+	u32 stop_threshold;
+	u32 silence_threshold;
+	u32 silence_size;
+	u32 boundary;
+	unsigned char reserved[64];
+};
+
+#define CVT_sndrv_pcm_sw_params()\
+{\
+	COPY(tstamp_mode);\
+	COPY(period_step);\
+	COPY(sleep_min);\
+	COPY(avail_min);\
+	COPY(xfer_align);\
+	COPY(start_threshold);\
+	COPY(stop_threshold);\
+	COPY(silence_threshold);\
+	COPY(silence_size);\
+	COPY(boundary);\
+}
+
+struct sndrv_pcm_channel_info32 {
+	u32 channel;
+	u32 offset;
+	u32 first;
+	u32 step;
+};
+
+#define CVT_sndrv_pcm_channel_info()\
+{\
+	COPY(channel);\
+	COPY(offset);\
+	COPY(first);\
+	COPY(step);\
+}
+
+struct timeval32 {
+	s32 tv_sec;
+	s32 tv_usec;
+};
+
+struct sndrv_pcm_status32 {
+	s32 state;
+	struct timeval32 trigger_tstamp;
+	struct timeval32 tstamp;
+	u32 appl_ptr;
+	u32 hw_ptr;
+	s32 delay;
+	u32 avail;
+	u32 avail_max;
+	u32 overrange;
+	s32 suspended_state;
+	unsigned char reserved[60];
+};
+
+#define CVT_sndrv_pcm_status()\
+{\
+	COPY(state);\
+	COPY(trigger_tstamp.tv_sec);\
+	COPY(trigger_tstamp.tv_usec);\
+	COPY(tstamp.tv_sec);\
+	COPY(tstamp.tv_usec);\
+	COPY(appl_ptr);\
+	COPY(hw_ptr);\
+	COPY(delay);\
+	COPY(avail);\
+	COPY(avail_max);\
+	COPY(overrange);\
+	COPY(suspended_state);\
+}
+
+struct sndrv_xferi32 {
+	s32 result;
+	u32 buf;
+	u32 frames;
+};
+
+#define CVT_sndrv_xferi()\
+{\
+	COPY(result);\
+	CPTR(buf);\
+	COPY(frames);\
+}
+
+DEFINE_ALSA_IOCTL(pcm_uframes_str);
+DEFINE_ALSA_IOCTL(pcm_sframes_str);
+DEFINE_ALSA_IOCTL(pcm_hw_params);
+DEFINE_ALSA_IOCTL(pcm_sw_params);
+DEFINE_ALSA_IOCTL(pcm_channel_info);
+DEFINE_ALSA_IOCTL(pcm_status);
+DEFINE_ALSA_IOCTL(xferi);
+
+/* snd_xfern needs remapping of bufs */
+struct sndrv_xfern32 {
+	s32 result;
+	u32 bufs;  /* this is void **; */
+	u32 frames;
+};
+
+/*
+ * xfern ioctl nees to copy (up to) 128 pointers on stack.
+ * although we may pass the copied pointers through f_op->ioctl, but the ioctl
+ * handler there expands again the same 128 pointers on stack, so it is better
+ * to handle the function (calling pcm_readv/writev) directly in this handler.
+ */
+static int snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	struct sndrv_xfern32 data32, *srcptr = (struct sndrv_xfern32*)arg;
+	void *bufs[128];
+	int err = 0, ch, i;
+	u32 *bufptr;
+
+	/* FIXME: need to check whether fop->ioctl is sane */
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL && substream->runtime, return -ENXIO);
+
+	/* check validty of the command */
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
+		if (substream->stream  != SNDRV_PCM_STREAM_PLAYBACK)
+			return -EINVAL;
+		if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
+	case SNDRV_PCM_IOCTL_READN_FRAMES:
+		if (substream->stream  != SNDRV_PCM_STREAM_CAPTURE)
+			return -EINVAL;
+		break;
+	}
+	if ((ch = substream->runtime->channels) > 128)
+		return -EINVAL;
+	if (get_user(data32.frames, &srcptr->frames))
+		return -EFAULT;
+	__get_user(data32.bufs, &srcptr->bufs);
+	bufptr = (u32*)TO_PTR(data32.bufs);
+	for (i = 0; i < ch; i++) {
+		u32 ptr;
+		if (get_user(ptr, bufptr))
+			return -EFAULT;
+		bufs[ch] = (void*)TO_PTR(ptr);
+		bufptr++;
+	}
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
+		err = snd_pcm_lib_writev(substream, bufs, data32.frames);
+		break;
+	case SNDRV_PCM_IOCTL_READN_FRAMES:
+		err = snd_pcm_lib_readv(substream, bufs, data32.frames);
+		break;
+	}
+	
+	if (err < 0)
+		return err;
+	if (put_user(err, &srcptr->result))
+		return -EFAULT;
+	return err < 0 ? err : 0;
+}
+
+
+#define AP(x) snd_ioctl32_##x
+
+struct ioctl32_mapper pcm_mappers[] = {
+	{ SNDRV_PCM_IOCTL_PVERSION, NULL },
+	{ SNDRV_PCM_IOCTL_INFO, NULL },
+	{ SNDRV_PCM_IOCTL_HW_REFINE, AP(pcm_hw_params) },
+	{ SNDRV_PCM_IOCTL_HW_PARAMS, AP(pcm_hw_params) },
+	{ SNDRV_PCM_IOCTL_HW_FREE, NULL },
+	{ SNDRV_PCM_IOCTL_SW_PARAMS, AP(pcm_sw_params) },
+	{ SNDRV_PCM_IOCTL_STATUS, AP(pcm_status) },
+	{ SNDRV_PCM_IOCTL_DELAY, AP(pcm_sframes_str) },
+	{ SNDRV_PCM_IOCTL_CHANNEL_INFO, AP(pcm_channel_info) },
+	{ SNDRV_PCM_IOCTL_PREPARE, NULL },
+	{ SNDRV_PCM_IOCTL_RESET, NULL },
+	{ SNDRV_PCM_IOCTL_START, NULL },
+	{ SNDRV_PCM_IOCTL_DROP, NULL },
+	{ SNDRV_PCM_IOCTL_DRAIN, NULL },
+	{ SNDRV_PCM_IOCTL_PAUSE, NULL },
+	{ SNDRV_PCM_IOCTL_REWIND, AP(pcm_uframes_str) },
+	{ SNDRV_PCM_IOCTL_RESUME, NULL },
+	{ SNDRV_PCM_IOCTL_XRUN, NULL },
+	{ SNDRV_PCM_IOCTL_WRITEI_FRAMES, AP(xferi) },
+	{ SNDRV_PCM_IOCTL_READI_FRAMES, AP(xferi) },
+	{ SNDRV_PCM_IOCTL_WRITEN_FRAMES, AP(xfern) },
+	{ SNDRV_PCM_IOCTL_READN_FRAMES, AP(xfern) },
+	{ SNDRV_PCM_IOCTL_LINK, NULL },
+	{ SNDRV_PCM_IOCTL_UNLINK, NULL },
+
+	{ SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL },
+	{ SNDRV_CTL_IOCTL_PCM_INFO, NULL },
+	{ SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL },
+
+	{ 0 },
+};
diff -Nru linux/sound/core/ioctl32/rawmidi32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/rawmidi32.c
--- linux/sound/core/ioctl32/rawmidi32.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/rawmidi32.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,86 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for raw MIDI API
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <asm/uaccess.h>
+#include "ioctl32.h"
+
+struct sndrv_rawmidi_params32 {
+	s32 stream;
+	u32 buffer_size;
+	u32 avail_min;
+	unsigned int no_active_sensing: 1;
+	unsigned char reserved[16];
+};
+
+#define CVT_sndrv_rawmidi_params()\
+{\
+	COPY(stream);\
+	COPY(buffer_size);\
+	COPY(avail_min);\
+	COPY(no_active_sensing);\
+}
+
+struct timeval32 {
+	s32 tv_sec;
+	s32 tv_usec;
+};
+
+struct sndrv_rawmidi_status32 {
+	s32 stream;
+	struct timeval32 tstamp;
+	u32 avail;
+	u32 xruns;
+	unsigned char reserved[16];
+};
+
+#define CVT_sndrv_rawmidi_status()\
+{\
+	COPY(stream);\
+	COPY(tstamp.tv_sec);\
+	COPY(tstamp.tv_usec);\
+	COPY(avail);\
+	COPY(xruns);\
+}
+
+DEFINE_ALSA_IOCTL(rawmidi_params);
+DEFINE_ALSA_IOCTL(rawmidi_status);
+
+
+#define AP(x) snd_ioctl32_##x
+
+struct ioctl32_mapper rawmidi_mappers[] = {
+	{ SNDRV_RAWMIDI_IOCTL_PVERSION, NULL },
+	{ SNDRV_RAWMIDI_IOCTL_INFO, NULL },
+	{ SNDRV_RAWMIDI_IOCTL_PARAMS, AP(rawmidi_params) },
+	{ SNDRV_RAWMIDI_IOCTL_STATUS, AP(rawmidi_status) },
+	{ SNDRV_RAWMIDI_IOCTL_DROP, NULL },
+	{ SNDRV_RAWMIDI_IOCTL_DRAIN, NULL },
+
+	{ SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, NULL },
+	{ SNDRV_CTL_IOCTL_RAWMIDI_INFO, NULL },
+	{ SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, NULL },
+
+	{ 0 },
+};
diff -Nru linux/sound/core/ioctl32/timer32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/timer32.c
--- linux/sound/core/ioctl32/timer32.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/timer32.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,93 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for timer API
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+#include <asm/uaccess.h>
+#include "ioctl32.h"
+
+struct sndrv_timer_info32 {
+	u32 flags;
+	s32 card;
+	unsigned char id[64];
+	unsigned char name[80];
+	u32 ticks;
+	u32 resolution;
+	unsigned char reserved[64];
+};
+
+#define CVT_sndrv_timer_info()\
+{\
+	COPY(flags);\
+	COPY(card);\
+	memcpy(dst->id, src->id, sizeof(src->id));\
+	memcpy(dst->name, src->name, sizeof(src->name));\
+	COPY(ticks);\
+	COPY(resolution);\
+}
+
+struct timeval32 {
+	s32 tv_sec;
+	s32 tv_usec;
+};
+
+struct sndrv_timer_status32 {
+	struct timeval32 tstamp;
+	u32 resolution;
+	u32 lost;
+	u32 overrun;
+	u32 queue;
+	unsigned char reserved[64];
+};
+
+#define CVT_sndrv_timer_status()\
+{\
+	COPY(tstamp.tv_sec);\
+	COPY(tstamp.tv_usec);\
+	COPY(resolution);\
+	COPY(lost);\
+	COPY(overrun);\
+	COPY(queue);\
+}
+
+DEFINE_ALSA_IOCTL(timer_info);
+DEFINE_ALSA_IOCTL(timer_status);
+
+
+/*
+ */
+
+#define AP(x) snd_ioctl32_##x
+
+struct ioctl32_mapper timer_mappers[] = {
+	{ SNDRV_TIMER_IOCTL_PVERSION, NULL },
+	{ SNDRV_TIMER_IOCTL_NEXT_DEVICE, NULL },
+	{ SNDRV_TIMER_IOCTL_SELECT, NULL },
+	{ SNDRV_TIMER_IOCTL_INFO, AP(timer_info) },
+	{ SNDRV_TIMER_IOCTL_PARAMS, NULL },
+	{ SNDRV_TIMER_IOCTL_STATUS, AP(timer_status) },
+	{ SNDRV_TIMER_IOCTL_START, NULL },
+	{ SNDRV_TIMER_IOCTL_STOP, NULL },
+	{ SNDRV_TIMER_IOCTL_CONTINUE, NULL },
+	{ 0 },
+};
diff -Nru linux/sound/core/isadma.c linux-2.4.19-pre5-mjc/sound/core/isadma.c
--- linux/sound/core/isadma.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/isadma.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,83 @@
+/*
+ *  ISA DMA support functions
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+/*
+ * Defining following add some delay. Maybe this helps for some broken
+ * ISA DMA controllers.
+ */
+
+#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <asm/dma.h>
+
+#ifdef CONFIG_ISA
+
+/*
+ *
+ */
+ 
+void snd_dma_program(unsigned long dma,
+		     unsigned long addr, unsigned int size,
+                     unsigned short mode)
+{
+	unsigned long flags;
+
+	flags = claim_dma_lock();
+	disable_dma(dma);
+	clear_dma_ff(dma);
+	set_dma_mode(dma, mode);
+	set_dma_addr(dma, addr);
+	set_dma_count(dma, size);
+	if (!(mode & DMA_MODE_NO_ENABLE))
+		enable_dma(dma);
+	release_dma_lock(flags);
+}
+
+void snd_dma_disable(unsigned long dma)
+{
+	unsigned long flags;
+
+	flags = claim_dma_lock();
+	clear_dma_ff(dma);
+	disable_dma(dma);
+	release_dma_lock(flags);
+}
+
+unsigned int snd_dma_residue(unsigned long dma)
+{
+	unsigned long flags;
+	unsigned int result;
+
+	flags = claim_dma_lock();
+	clear_dma_ff(dma);
+	if (!isa_dma_bridge_buggy)
+		disable_dma(dma);
+	result = get_dma_residue(dma);
+	if (!isa_dma_bridge_buggy)
+		enable_dma(dma);
+	release_dma_lock(flags);
+	return result;
+}
+
+#endif /* CONFIG_ISA */
diff -Nru linux/sound/core/memory.c linux-2.4.19-pre5-mjc/sound/core/memory.c
--- linux/sound/core/memory.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/memory.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,552 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Memory allocation routines.
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/info.h>
+
+/*
+ *  memory allocation helpers and debug routines
+ */
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+
+struct snd_alloc_track {
+	unsigned long magic;
+	void *caller;
+	size_t size;
+	struct list_head list;
+	long data[0];
+};
+
+#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data)
+
+static long snd_alloc_pages;
+static long snd_alloc_kmalloc;
+static long snd_alloc_vmalloc;
+static LIST_HEAD(snd_alloc_kmalloc_list);
+static LIST_HEAD(snd_alloc_vmalloc_list);
+static spinlock_t snd_alloc_kmalloc_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t snd_alloc_vmalloc_lock = SPIN_LOCK_UNLOCKED;
+#define KMALLOC_MAGIC 0x87654321
+#define VMALLOC_MAGIC 0x87654320
+static snd_info_entry_t *snd_memory_info_entry;
+
+void snd_memory_init(void)
+{
+	snd_alloc_pages = 0;
+	snd_alloc_kmalloc = 0;
+	snd_alloc_vmalloc = 0;
+}
+
+void snd_memory_done(void)
+{
+	struct list_head *head;
+	struct snd_alloc_track *t;
+	if (snd_alloc_pages > 0)
+		snd_printk(KERN_ERR "Not freed snd_alloc_pages = %li\n", snd_alloc_pages);
+	if (snd_alloc_kmalloc > 0)
+		snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc);
+	if (snd_alloc_vmalloc > 0)
+		snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc);
+	for (head = snd_alloc_kmalloc_list.prev;
+	     head != &snd_alloc_kmalloc_list; head = head->prev) {
+		t = list_entry(head, struct snd_alloc_track, list);
+		if (t->magic != KMALLOC_MAGIC) {
+			snd_printk(KERN_ERR "Corrupted kmalloc\n");
+			break;
+		}
+		snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
+	}
+	for (head = snd_alloc_vmalloc_list.prev;
+	     head != &snd_alloc_vmalloc_list; head = head->prev) {
+		t = list_entry(head, struct snd_alloc_track, list);
+		if (t->magic != VMALLOC_MAGIC) {
+			snd_printk(KERN_ERR "Corrupted vmalloc\n");
+			break;
+		}
+		snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
+	}
+}
+
+void *__snd_kmalloc(size_t size, int flags, void *caller)
+{
+	unsigned long cpu_flags;
+	struct snd_alloc_track *t;
+	void *ptr;
+	
+	ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags);
+	if (ptr != NULL) {
+		t = (struct snd_alloc_track *)ptr;
+		t->magic = KMALLOC_MAGIC;
+		t->caller = caller;
+		spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags);
+		list_add_tail(&t->list, &snd_alloc_kmalloc_list);
+		spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags);
+		t->size = size;
+		snd_alloc_kmalloc += size;
+		ptr = t->data;
+	}
+	return ptr;
+}
+
+#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0));
+void *snd_hidden_kmalloc(size_t size, int flags)
+{
+	return _snd_kmalloc(size, flags);
+}
+
+void snd_hidden_kfree(const void *obj)
+{
+	unsigned long flags;
+	struct snd_alloc_track *t;
+	if (obj == NULL) {
+		snd_printk(KERN_WARNING "null kfree (called from %p)\n", __builtin_return_address(0));
+		return;
+	}
+	t = snd_alloc_track_entry(obj);
+	if (t->magic != KMALLOC_MAGIC) {
+		snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0));
+		return;
+	}
+	spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags);
+	list_del(&t->list);
+	spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags);
+	t->magic = 0;
+	snd_alloc_kmalloc -= t->size;
+	obj = t;
+	snd_wrapper_kfree(obj);
+}
+
+void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags)
+{
+	unsigned long *ptr;
+	ptr = _snd_kmalloc(size + sizeof(unsigned long), flags);
+	if (ptr) {
+		*ptr++ = magic;
+		memset(ptr, 0, size);
+	}
+	return ptr;
+}
+
+void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags)
+{
+	unsigned long *ptr;
+	ptr = _snd_kmalloc(size + sizeof(unsigned long), flags);
+	if (ptr)
+		*ptr++ = magic;
+	return ptr;
+}
+
+void snd_magic_kfree(void *_ptr)
+{
+	unsigned long *ptr = _ptr;
+	if (ptr == NULL) {
+		snd_printk(KERN_WARNING "null snd_magic_kfree (called from %p)\n", __builtin_return_address(0));
+		return;
+	}
+	*--ptr = 0;
+	{
+		struct snd_alloc_track *t;
+		t = snd_alloc_track_entry(ptr);
+		if (t->magic != KMALLOC_MAGIC) {
+			snd_printk(KERN_ERR "bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0));
+			return;
+		}
+	}
+	snd_hidden_kfree(ptr);
+	return;
+}
+
+void *snd_hidden_vmalloc(unsigned long size)
+{
+	void *ptr;
+	ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track));
+	if (ptr) {
+		struct snd_alloc_track *t = (struct snd_alloc_track *)ptr;
+		t->magic = VMALLOC_MAGIC;
+		t->caller = __builtin_return_address(0);
+		spin_lock(&snd_alloc_vmalloc_lock);
+		list_add_tail(&t->list, &snd_alloc_vmalloc_list);
+		spin_unlock(&snd_alloc_vmalloc_lock);
+		t->size = size;
+		snd_alloc_vmalloc += size;
+		ptr = t->data;
+	}
+	return ptr;
+}
+
+void snd_hidden_vfree(void *obj)
+{
+	struct snd_alloc_track *t;
+	if (obj == NULL) {
+		snd_printk(KERN_WARNING "null vfree (called from %p)\n", __builtin_return_address(0));
+		return;
+	}
+	t = snd_alloc_track_entry(obj);
+	if (t->magic != VMALLOC_MAGIC) {
+		snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0));
+		return;
+	}
+	spin_lock(&snd_alloc_vmalloc_lock);
+	list_del(&t->list);
+	spin_unlock(&snd_alloc_vmalloc_lock);
+	t->magic = 0;
+	snd_alloc_vmalloc -= t->size;
+	obj = t;
+	snd_wrapper_vfree(obj);
+}
+
+static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	long pages = snd_alloc_pages >> (PAGE_SHIFT-12);
+	snd_iprintf(buffer, "pages  : %li bytes (%li pages per %likB)\n", pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
+	snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc);
+	snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc);
+}
+
+int __init snd_memory_info_init(void)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_memory_info_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_memory_info_entry = entry;
+	return 0;
+}
+
+int __exit snd_memory_info_done(void)
+{
+	if (snd_memory_info_entry)
+		snd_info_unregister(snd_memory_info_entry);
+	return 0;
+}
+
+#else
+
+#define _snd_kmalloc kmalloc
+
+#endif /* CONFIG_SND_DEBUG_MEMORY */
+
+
+
+void *snd_malloc_pages(unsigned long size, unsigned int dma_flags)
+{
+	int pg;
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(dma_flags != 0, return NULL);
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	if ((res = (void *) __get_free_pages(dma_flags, pg)) != NULL) {
+		mem_map_t *page = virt_to_page(res);
+		mem_map_t *last_page = page + (1 << pg);
+		while (page < last_page)
+			SetPageReserved(page++);
+#ifdef CONFIG_SND_DEBUG_MEMORY
+		snd_alloc_pages += 1 << pg;
+#endif
+	}
+	return res;
+}
+
+void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size)
+{
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((res = snd_malloc_pages(size, dma_flags)) != NULL) {
+			*res_size = size;
+			return res;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+}
+
+void snd_free_pages(void *ptr, unsigned long size)
+{
+	int pg;
+	mem_map_t *page, *last_page;
+
+	if (ptr == NULL)
+		return;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	page = virt_to_page(ptr);
+	last_page = page + (1 << pg);
+	while (page < last_page)
+		ClearPageReserved(page++);
+	free_pages((unsigned long) ptr, pg);
+#ifdef CONFIG_SND_DEBUG_MEMORY
+	snd_alloc_pages -= 1 << pg;
+#endif
+}
+
+#ifdef CONFIG_ISA
+
+#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT
+#ifdef CONFIG_PCI
+#define CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT
+#endif
+#endif
+
+void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr)
+{
+	void *dma_area;
+
+#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT
+	dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA);
+	*dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL;
+#else
+	{
+		int pg;
+		for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+		dma_area = pci_alloc_consistent(NULL, size, dma_addr);
+		if (dma_area != NULL) {
+			mem_map_t *page = virt_to_page(dma_area);
+			mem_map_t *last_page = page + (1 << pg);
+			while (page < last_page)
+				SetPageReserved(page++);
+#ifdef CONFIG_SND_DEBUG_MEMORY
+			snd_alloc_pages += 1 << pg;
+#endif
+		}
+	}
+#endif
+	return dma_area;
+}
+
+void *snd_malloc_isa_pages_fallback(unsigned long size,
+				    dma_addr_t *dma_addr,
+				    unsigned long *res_size)
+{
+	void *dma_area;
+
+#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT
+	dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size);
+	*dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL;
+	return dma_area;
+#else
+	snd_assert(size > 0, return NULL);
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((dma_area = snd_malloc_isa_pages(size, dma_addr)) != NULL) {
+			*res_size = size;
+			return dma_area;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+#endif
+}
+
+#endif
+
+#ifdef CONFIG_PCI
+
+void *snd_malloc_pci_pages(struct pci_dev *pci,
+			   unsigned long size,
+			   dma_addr_t *dma_addr)
+{
+	int pg;
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(dma_addr != NULL, return NULL);
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr);
+	if (res != NULL) {
+		mem_map_t *page = virt_to_page(res);
+		mem_map_t *last_page = page + (1 << pg);
+		while (page < last_page)
+			SetPageReserved(page++);
+#ifdef CONFIG_SND_DEBUG_MEMORY
+		snd_alloc_pages += 1 << pg;
+#endif
+	}
+	return res;
+}
+
+void *snd_malloc_pci_pages_fallback(struct pci_dev *pci,
+				    unsigned long size,
+				    dma_addr_t *dma_addr,
+				    unsigned long *res_size)
+{
+	void *res;
+
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) {
+			*res_size = size;
+			return res;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+}
+
+void snd_free_pci_pages(struct pci_dev *pci,
+			unsigned long size,
+			void *ptr,
+			dma_addr_t dma_addr)
+{
+	int pg;
+	mem_map_t *page, *last_page;
+
+	if (ptr == NULL)
+		return;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	page = virt_to_page(ptr);
+	last_page = page + (1 << pg);
+	while (page < last_page)
+		ClearPageReserved(page++);
+	pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr);
+#ifdef CONFIG_SND_DEBUG_MEMORY
+	snd_alloc_pages -= 1 << pg;
+#endif
+}
+
+#endif /* CONFIG_PCI */
+
+void *snd_kcalloc(size_t size, int flags)
+{
+	void *ptr;
+	
+	ptr = _snd_kmalloc(size, flags);
+	if (ptr)
+		memset(ptr, 0, size);
+	return ptr;
+}
+
+char *snd_kmalloc_strdup(const char *string, int flags)
+{
+	size_t len;
+	char *ptr;
+
+	if (!string)
+		return NULL;
+	len = strlen(string) + 1;
+	ptr = _snd_kmalloc(len, flags);
+	if (ptr)
+		memcpy(ptr, string, len);
+	return ptr;
+}
+
+int copy_to_user_fromio(void *dst, unsigned long src, size_t count)
+{
+#if defined(__i386_) || defined(CONFIG_SPARC32)
+	return copy_to_user(dst, (const void*) src, count);
+#else
+	char buf[1024];
+	while (count) {
+		size_t c = count;
+		int err;
+		if (c > sizeof(buf))
+			c = sizeof(buf);
+		memcpy_fromio(buf, src, c);
+		err = copy_to_user(dst, buf, c);
+		if (err)
+			return err;
+		count -= c;
+		dst += c;
+		src += c;
+	}
+	return 0;
+#endif
+}
+
+int copy_from_user_toio(unsigned long dst, const void *src, size_t count)
+{
+#if defined(__i386_) || defined(CONFIG_SPARC32)
+	return copy_from_user((void*)dst, src, count);
+#else
+	char buf[1024];
+	while (count) {
+		size_t c = count;
+		int err;
+		if (c > sizeof(buf))
+			c = sizeof(buf);
+		err = copy_from_user(buf, src, c);
+		if (err)
+			return err;
+		memcpy_toio(dst, buf, c);
+		count -= c;
+		dst += c;
+		src += c;
+	}
+	return 0;
+#endif
+}
+
+#ifdef HACK_PCI_ALLOC_CONSISTENT
+/*
+ * A dirty hack... when the kernel code is fixed this should be removed.
+ *
+ * since pci_alloc_consistent always tries GFP_DMA when the requested
+ * pci memory region is below 32bit, it happens quite often that even
+ * 2 order or pages cannot be allocated.
+ *
+ * so in the following, GFP_DMA is used only when the first allocation
+ * doesn't match the requested region.
+ */
+#ifdef __i386__
+#define get_phys_addr(x) virt_to_phys(x)
+#else /* ppc */
+#define get_phys_addr(x) virt_to_bus(x)
+#endif
+void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+				    dma_addr_t *dma_handle)
+{
+	void *ret;
+	int gfp = GFP_ATOMIC;
+
+	if (hwdev == NULL)
+		gfp |= GFP_DMA;
+	ret = (void *)__get_free_pages(gfp, get_order(size));
+	if (ret) {
+		if (hwdev && ((get_phys_addr(ret) + size - 1) & ~hwdev->dma_mask)) {
+			free_pages((unsigned long)ret, get_order(size));
+			ret = (void *)__get_free_pages(gfp | GFP_DMA, get_order(size));
+		}
+	}
+	if (ret) {
+		memset(ret, 0, size);
+		*dma_handle = get_phys_addr(ret);
+	}
+	return ret;
+}
+#endif /* hack */
diff -Nru linux/sound/core/misc.c linux-2.4.19-pre5-mjc/sound/core/misc.c
--- linux/sound/core/misc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/misc.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,53 @@
+/*
+ *  Misc and compatibility things
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <sound/core.h>
+
+int snd_task_name(struct task_struct *task, char *name, size_t size)
+{
+	unsigned int idx;
+
+	snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
+	for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
+		name[idx] = task->comm[idx];
+	name[idx] = '\0';
+	return 0;
+}
+
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+int snd_verbose_printk(const char *file, int line, const char *format)
+{
+	if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+		char tmp[] = "<0>ALSA %s:%d: ";
+		tmp[1] = format[1];
+		printk("%sALSA %s:%d: ", tmp, file, line);
+		return 1;
+	} else {
+		printk("ALSA %s:%d: ", file, line);
+		return 0;
+	}
+}
+#endif
diff -Nru linux/sound/core/oss/Makefile linux-2.4.19-pre5-mjc/sound/core/oss/Makefile
--- linux/sound/core/oss/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,26 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := oss.o
+
+export-objs  := mixer_oss.o
+
+list-multi   := snd-mixer-oss.o snd-pcm-oss.o
+
+snd-mixer-oss-objs := mixer_oss.o
+
+snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
+		    io.o copy.o linear.o mulaw.o route.o rate.o
+
+obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
+obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o
+
+include $(TOPDIR)/Rules.make
+
+snd-mixer-oss.o: $(snd-mixer-oss-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-mixer-oss-objs)
+
+snd-pcm-oss.o: $(snd-pcm-oss-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-oss-objs)
diff -Nru linux/sound/core/oss/copy.c linux-2.4.19-pre5-mjc/sound/core/oss/copy.c
--- linux/sound/core/oss/copy.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/copy.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,88 @@
+/*
+ *  Linear conversion Plug-In
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin,
+			     const snd_pcm_plugin_channel_t *src_channels,
+			     snd_pcm_plugin_channel_t *dst_channels,
+			     snd_pcm_uframes_t frames)
+{
+	unsigned int channel;
+	unsigned int nchannels;
+
+	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (frames == 0)
+		return 0;
+	nchannels = plugin->src_format.channels;
+	for (channel = 0; channel < nchannels; channel++) {
+		snd_assert(src_channels->area.first % 8 == 0 &&
+			   src_channels->area.step % 8 == 0,
+			   return -ENXIO);
+		snd_assert(dst_channels->area.first % 8 == 0 &&
+			   dst_channels->area.step % 8 == 0,
+			   return -ENXIO);
+		if (!src_channels->enabled) {
+			if (dst_channels->wanted)
+				snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
+			dst_channels->enabled = 0;
+			continue;
+		}
+		dst_channels->enabled = 1;
+		snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format);
+		src_channels++;
+		dst_channels++;
+	}
+	return frames;
+}
+
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
+			      snd_pcm_plugin_format_t *src_format,
+			      snd_pcm_plugin_format_t *dst_format,
+			      snd_pcm_plugin_t **r_plugin)
+{
+	int err;
+	snd_pcm_plugin_t *plugin;
+	int width;
+
+	snd_assert(r_plugin != NULL, return -ENXIO);
+	*r_plugin = NULL;
+
+	snd_assert(src_format->format == dst_format->format, return -ENXIO);
+	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+
+	width = snd_pcm_format_physical_width(src_format->format);
+	snd_assert(width > 0, return -ENXIO);
+
+	err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
+				   0, &plugin);
+	if (err < 0)
+		return err;
+	plugin->transfer = copy_transfer;
+	*r_plugin = plugin;
+	return 0;
+}
diff -Nru linux/sound/core/oss/io.c linux-2.4.19-pre5-mjc/sound/core/oss/io.c
--- linux/sound/core/oss/io.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/io.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,134 @@
+/*
+ *  PCM I/O Plug-In Interface
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+  
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
+#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
+#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
+#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
+
+/*
+ *  Basic io plugin
+ */
+ 
+static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin,
+				    const snd_pcm_plugin_channel_t *src_channels,
+				    snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
+				    snd_pcm_uframes_t frames)
+{
+	snd_assert(plugin != NULL, return -ENXIO);
+	snd_assert(src_channels != NULL, return -ENXIO);
+	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+		return pcm_write(plugin->plug, src_channels->area.addr, frames);
+	} else {
+		int channel, channels = plugin->dst_format.channels;
+		void **bufs = (void**)plugin->extra_data;
+		snd_assert(bufs != NULL, return -ENXIO);
+		for (channel = 0; channel < channels; channel++) {
+			if (src_channels[channel].enabled)
+				bufs[channel] = src_channels[channel].area.addr;
+			else
+				bufs[channel] = NULL;
+		}
+		return pcm_writev(plugin->plug, bufs, frames);
+	}
+}
+ 
+static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin,
+				   const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
+				   snd_pcm_plugin_channel_t *dst_channels,
+				   snd_pcm_uframes_t frames)
+{
+	snd_assert(plugin != NULL, return -ENXIO);
+	snd_assert(dst_channels != NULL, return -ENXIO);
+	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+		return pcm_read(plugin->plug, dst_channels->area.addr, frames);
+	} else {
+		int channel, channels = plugin->dst_format.channels;
+		void **bufs = (void**)plugin->extra_data;
+		snd_assert(bufs != NULL, return -ENXIO);
+		for (channel = 0; channel < channels; channel++) {
+			if (dst_channels[channel].enabled)
+				bufs[channel] = dst_channels[channel].area.addr;
+			else
+				bufs[channel] = NULL;
+		}
+		return pcm_readv(plugin->plug, bufs, frames);
+	}
+	return 0;
+}
+ 
+static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin,
+			     snd_pcm_uframes_t frames,
+			     snd_pcm_plugin_channel_t **channels)
+{
+	int err;
+	unsigned int channel;
+	snd_pcm_plugin_channel_t *v;
+	err = snd_pcm_plugin_client_channels(plugin, frames, &v);
+	if (err < 0)
+		return err;
+	*channels = v;
+	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+		for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v)
+			v->wanted = 1;
+	}
+	return frames;
+}
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
+			    snd_pcm_hw_params_t *params,
+			    snd_pcm_plugin_t **r_plugin)
+{
+	int err;
+	snd_pcm_plugin_format_t format;
+	snd_pcm_plugin_t *plugin;
+
+	snd_assert(r_plugin != NULL, return -ENXIO);
+	*r_plugin = NULL;
+	snd_assert(plug != NULL && params != NULL, return -ENXIO);
+	format.format = params_format(params);
+	format.rate = params_rate(params);
+	format.channels = params_channels(params);
+	err = snd_pcm_plugin_build(plug, "I/O io",
+				   &format, &format,
+				   sizeof(void *) * format.channels,
+				   &plugin);
+	if (err < 0)
+		return err;
+	plugin->access = params_access(params);
+	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
+		plugin->transfer = io_playback_transfer;
+		if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+			plugin->client_channels = io_src_channels;
+	} else {
+		plugin->transfer = io_capture_transfer;
+	}
+
+	*r_plugin = plugin;
+	return 0;
+}
diff -Nru linux/sound/core/oss/linear.c linux-2.4.19-pre5-mjc/sound/core/oss/linear.c
--- linux/sound/core/oss/linear.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/linear.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,159 @@
+/*
+ *  Linear conversion Plug-In
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
+ *			  Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+/*
+ *  Basic linear conversion plugin
+ */
+ 
+typedef struct linear_private_data {
+	int conv;
+} linear_t;
+
+static void convert(snd_pcm_plugin_t *plugin,
+		    const snd_pcm_plugin_channel_t *src_channels,
+		    snd_pcm_plugin_channel_t *dst_channels,
+		    snd_pcm_uframes_t frames)
+{
+#define CONV_LABELS
+#include "plugin_ops.h"
+#undef CONV_LABELS
+	linear_t *data = (linear_t *)plugin->extra_data;
+	void *conv = conv_labels[data->conv];
+	int channel;
+	int nchannels = plugin->src_format.channels;
+	for (channel = 0; channel < nchannels; ++channel) {
+		char *src;
+		char *dst;
+		int src_step, dst_step;
+		snd_pcm_uframes_t frames1;
+		if (!src_channels[channel].enabled) {
+			if (dst_channels[channel].wanted)
+				snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+			dst_channels[channel].enabled = 0;
+			continue;
+		}
+		dst_channels[channel].enabled = 1;
+		src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+		dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+		src_step = src_channels[channel].area.step / 8;
+		dst_step = dst_channels[channel].area.step / 8;
+		frames1 = frames;
+		while (frames1-- > 0) {
+			goto *conv;
+#define CONV_END after
+#include "plugin_ops.h"
+#undef CONV_END
+		after:
+			src += src_step;
+			dst += dst_step;
+		}
+	}
+}
+
+static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin,
+			       const snd_pcm_plugin_channel_t *src_channels,
+			       snd_pcm_plugin_channel_t *dst_channels,
+			       snd_pcm_uframes_t frames)
+{
+	linear_t *data;
+
+	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	data = (linear_t *)plugin->extra_data;
+	if (frames == 0)
+		return 0;
+#ifdef CONFIG_SND_DEBUG
+	{
+		unsigned int channel;
+		for (channel = 0; channel < plugin->src_format.channels; channel++) {
+			snd_assert(src_channels[channel].area.first % 8 == 0 &&
+				   src_channels[channel].area.step % 8 == 0,
+				   return -ENXIO);
+			snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+				   dst_channels[channel].area.step % 8 == 0,
+				   return -ENXIO);
+		}
+	}
+#endif
+	convert(plugin, src_channels, dst_channels, frames);
+	return frames;
+}
+
+int conv_index(int src_format, int dst_format)
+{
+	int src_endian, dst_endian, sign, src_width, dst_width;
+
+	sign = (snd_pcm_format_signed(src_format) !=
+		snd_pcm_format_signed(dst_format));
+#ifdef SNDRV_LITTLE_ENDIAN
+	src_endian = snd_pcm_format_big_endian(src_format);
+	dst_endian = snd_pcm_format_big_endian(dst_format);
+#else
+	src_endian = snd_pcm_format_little_endian(src_format);
+	dst_endian = snd_pcm_format_little_endian(dst_format);
+#endif
+
+	if (src_endian < 0)
+		src_endian = 0;
+	if (dst_endian < 0)
+		dst_endian = 0;
+
+	src_width = snd_pcm_format_width(src_format) / 8 - 1;
+	dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
+
+	return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
+}
+
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug,
+				snd_pcm_plugin_format_t *src_format,
+				snd_pcm_plugin_format_t *dst_format,
+				snd_pcm_plugin_t **r_plugin)
+{
+	int err;
+	struct linear_private_data *data;
+	snd_pcm_plugin_t *plugin;
+
+	snd_assert(r_plugin != NULL, return -ENXIO);
+	*r_plugin = NULL;
+
+	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+	snd_assert(snd_pcm_format_linear(src_format->format) &&
+		   snd_pcm_format_linear(dst_format->format), return -ENXIO);
+
+	err = snd_pcm_plugin_build(plug, "linear format conversion",
+				   src_format, dst_format,
+				   sizeof(linear_t), &plugin);
+	if (err < 0)
+		return err;
+	data = (linear_t *)plugin->extra_data;
+	data->conv = conv_index(src_format->format, dst_format->format);
+	plugin->transfer = linear_transfer;
+	*r_plugin = plugin;
+	return 0;
+}
diff -Nru linux/sound/core/oss/mixer_oss.c linux-2.4.19-pre5-mjc/sound/core/oss/mixer_oss.c
--- linux/sound/core/oss/mixer_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/mixer_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1045 @@
+/*
+ *  OSS emulation layer for the mixer interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/mixer_oss.h>
+#include <linux/soundcard.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");
+MODULE_LICENSE("GPL");
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+static int snd_mixer_oss_open(struct inode *inode, struct file *file)
+{
+	int cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev));
+	snd_card_t *card;
+	snd_mixer_oss_file_t *fmixer;
+
+	if ((card = snd_cards[cardnum]) == NULL)
+		return -ENODEV;
+	if (card->mixer_oss == NULL)
+		return -ENODEV;
+	fmixer = (snd_mixer_oss_file_t *)snd_kcalloc(sizeof(*fmixer), GFP_KERNEL);
+	if (fmixer == NULL)
+		return -ENOMEM;
+	fmixer->card = card;
+	fmixer->mixer = card->mixer_oss;
+	file->private_data = fmixer;
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	if (!try_inc_mod_count(card->module)) {
+		kfree(fmixer);
+#ifdef LINUX_2_2
+		MOD_DEC_USE_COUNT;
+#endif
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int snd_mixer_oss_release(struct inode *inode, struct file *file)
+{
+	snd_mixer_oss_file_t *fmixer;
+
+	if (file->private_data) {
+		fmixer = (snd_mixer_oss_file_t *) file->private_data;
+		dec_mod_count(fmixer->card->module);
+#ifdef LINUX_2_2
+		MOD_DEC_USE_COUNT;
+#endif
+		kfree(fmixer);
+	}
+	return 0;
+}
+
+static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer,
+			      mixer_info *_info)
+{
+	snd_card_t *card = fmixer->card;
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	struct mixer_info info;
+	
+	memset(&info, 0, sizeof(info));
+	strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1);
+	strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1);
+	info.modify_counter = card->mixer_oss_change_count;
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer,
+				       _old_mixer_info *_info)
+{
+	snd_card_t *card = fmixer->card;
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	_old_mixer_info info;
+	
+	memset(&info, 0, sizeof(info));
+	strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1);
+	strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1);
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	int result = 0;
+
+	if (mixer == NULL)
+		return -EIO;
+	if (mixer->get_recsrc && mixer->put_recsrc)
+		result |= SOUND_CAP_EXCL_INPUT;
+	return result;
+}
+
+static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	snd_mixer_oss_slot_t *pslot;
+	int result = 0, chn;
+
+	if (mixer == NULL)
+		return -EIO;
+	for (chn = 0; chn < 31; chn++) {
+		pslot = &mixer->slots[chn];
+		if (pslot->put_volume || pslot->put_recsrc)
+			result |= 1 << chn;
+	}
+	return result;
+}
+
+static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	snd_mixer_oss_slot_t *pslot;
+	int result = 0, chn;
+
+	if (mixer == NULL)
+		return -EIO;
+	for (chn = 0; chn < 31; chn++) {
+		pslot = &mixer->slots[chn];
+		if (pslot->put_volume && pslot->stereo)
+			result |= 1 << chn;
+	}
+	return result;
+}
+
+static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	int result = 0;
+
+	if (mixer == NULL)
+		return -EIO;
+	if (mixer->put_recsrc && mixer->get_recsrc) {	/* exclusive */
+		result = mixer->mask_recsrc;
+	} else {
+		snd_mixer_oss_slot_t *pslot;
+		int chn;
+		for (chn = 0; chn < 31; chn++) {
+			pslot = &mixer->slots[chn];
+			if (pslot->put_recsrc)
+				result |= 1 << chn;
+		}
+	}
+	return result;
+}
+
+static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	int result = 0;
+
+	if (mixer == NULL)
+		return -EIO;
+	if (mixer->put_recsrc && mixer->get_recsrc) {	/* exclusive */
+		int err;
+		if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
+			return err;
+		result = 1 << result;
+	} else {
+		snd_mixer_oss_slot_t *pslot;
+		int chn;
+		for (chn = 0; chn < 31; chn++) {
+			pslot = &mixer->slots[chn];
+			if (pslot->get_recsrc) {
+				int active = 0;
+				pslot->get_recsrc(fmixer, pslot, &active);
+				if (active)
+					result |= 1 << chn;
+			}
+		}
+	}
+	return mixer->oss_recsrc = result;
+}
+
+static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	int result = 0;
+
+	if (mixer == NULL)
+		return -EIO;
+	if (mixer->get_recsrc && mixer->put_recsrc) {	/* exclusive input */
+		if (recsrc & ~mixer->oss_recsrc)
+			recsrc &= ~mixer->oss_recsrc;
+		mixer->put_recsrc(fmixer, ffz(~recsrc));
+		mixer->get_recsrc(fmixer, &result);
+		result = 1 << result;
+	} else {
+		snd_mixer_oss_slot_t *pslot;
+		int chn, active;
+		for (chn = 0; chn < 31; chn++) {
+			pslot = &mixer->slots[chn];
+			if (pslot->put_recsrc) {
+				active = (recsrc & (1 << chn)) ? 1 : 0;
+				pslot->put_recsrc(fmixer, pslot, active);
+			}
+		}
+		for (chn = 0; chn < 31; chn++) {
+			pslot = &mixer->slots[chn];
+			if (pslot->get_recsrc) {
+				active = 0;
+				pslot->get_recsrc(fmixer, pslot, &active);
+				if (active)
+					result |= 1 << chn;
+			}
+		}
+	}
+	return result;
+}
+
+static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	snd_mixer_oss_slot_t *pslot;
+	int result = 0, left, right;
+
+	if (mixer == NULL || slot > 30)
+		return -EIO;
+	pslot = &mixer->slots[slot];
+	left = fmixer->volume[slot][0];
+	right = fmixer->volume[slot][1];
+	if (pslot->get_volume)
+		result = pslot->get_volume(fmixer, pslot, &left, &right);
+	if (!pslot->stereo)
+		right = left;
+	snd_assert(left >= 0 && left <= 100, return -EIO);
+	snd_assert(right >= 0 && right <= 100, return -EIO);
+	if (result >= 0) {
+		fmixer->volume[slot][0] = left;
+		fmixer->volume[slot][1] = right;
+	 	result = (left & 0xff) | ((right & 0xff) << 8);
+	}
+	return result;
+}
+
+static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer,
+				    int slot, int volume)
+{
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	snd_mixer_oss_slot_t *pslot;
+	int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff;
+
+	if (mixer == NULL || slot > 30)
+		return -EIO;
+	pslot = &mixer->slots[slot];
+	if (left > 100)
+		left = 100;
+	if (right > 100)
+		right = 100;
+	if (!pslot->stereo)
+		left = right = left;
+	if (pslot->put_volume)
+		result = pslot->put_volume(fmixer, pslot, left, right);
+	if (result < 0)
+		return result;
+	fmixer->volume[slot][0] = left;
+	fmixer->volume[slot][1] = right;
+ 	return (left & 0xff) | ((right & 0xff) << 8);
+}
+
+static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg)
+{
+	int tmp;
+
+	snd_assert(fmixer != NULL, return -ENXIO);
+	if (((cmd >> 8) & 0xff) == 'M') {
+		switch (cmd) {
+		case SOUND_MIXER_INFO:
+			return snd_mixer_oss_info(fmixer, (mixer_info *)arg);
+		case SOUND_OLD_MIXER_INFO:
+ 			return snd_mixer_oss_info_obsolete(fmixer, (_old_mixer_info *)arg);
+		case SOUND_MIXER_WRITE_RECSRC:
+			if (get_user(tmp, (int *)arg))
+				return -EFAULT;
+			tmp = snd_mixer_oss_set_recsrc(fmixer, tmp);
+			if (tmp < 0)
+				return tmp;
+			return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+		case OSS_GETVERSION:
+			return put_user(SNDRV_OSS_VERSION, (int *) arg);
+		case SOUND_MIXER_READ_DEVMASK:
+			tmp = snd_mixer_oss_devmask(fmixer);
+			if (tmp < 0)
+				return tmp;
+			return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+		case SOUND_MIXER_READ_STEREODEVS:
+			tmp = snd_mixer_oss_stereodevs(fmixer);
+			if (tmp < 0)
+				return tmp;
+			return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+		case SOUND_MIXER_READ_RECMASK:
+			tmp = snd_mixer_oss_recmask(fmixer);
+			if (tmp < 0)
+				return tmp;
+			return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+		case SOUND_MIXER_READ_CAPS:
+			tmp = snd_mixer_oss_caps(fmixer);
+			if (tmp < 0)
+				return tmp;
+			return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+		case SOUND_MIXER_READ_RECSRC:
+			tmp = snd_mixer_oss_get_recsrc(fmixer);
+			if (tmp < 0)
+				return tmp;
+			return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+		}
+	}
+	if (cmd & IOC_IN) {
+		if (get_user(tmp, (int *)arg))
+			return -EFAULT;
+		tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp);
+		if (tmp < 0)
+			return tmp;
+		return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+	} else if (cmd & IOC_OUT) {
+		tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff);
+		if (tmp < 0)
+			return tmp;
+		return put_user(tmp, (int *)arg) ? -EFAULT : 0;
+	}
+	return -ENXIO;
+}
+
+int snd_mixer_oss_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg);
+}
+
+int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg)
+{
+	snd_mixer_oss_file_t fmixer;
+	
+	snd_assert(card != NULL, return -ENXIO);
+	if (card->mixer_oss == NULL)
+		return -ENXIO;
+	memset(&fmixer, 0, sizeof(fmixer));
+	fmixer.card = card;
+	fmixer.mixer = card->mixer_oss;
+	return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
+}
+
+/*
+ *  REGISTRATION PART
+ */
+
+static struct file_operations snd_mixer_oss_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	open:		snd_mixer_oss_open,
+	release:	snd_mixer_oss_release,
+	ioctl:		snd_mixer_oss_ioctl,
+};
+
+static snd_minor_t snd_mixer_oss_reg =
+{
+	comment:	"mixer",
+	f_ops:		&snd_mixer_oss_f_ops,
+};
+
+/*
+ *  utilities
+ */
+
+static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax)
+{
+	long orange = omax - omin, nrange = nmax - nmin;
+	
+	if (orange == 0)
+		return 0;
+	return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
+}
+
+static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
+{
+	if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
+		return *old;
+	return snd_mixer_oss_conv(val, min, max, 0, 100);
+}
+
+static long snd_mixer_oss_conv2(long val, long min, long max)
+{
+	return snd_mixer_oss_conv(val, 0, 100, min, max);
+}
+
+#if 0
+static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot)
+{
+	snd_mixer_oss_t *mixer = card->mixer_oss;
+	if (mixer)
+		mixer->mask_recsrc |= 1 << slot;
+}
+
+static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot)
+{
+	snd_mixer_oss_t *mixer = card->mixer_oss;
+	if (mixer && (mixer->mask_recsrc & (1 << slot)))
+		return 1;
+	return 0;
+}
+#endif
+
+#define SNDRV_MIXER_OSS_SIGNATURE		0x65999250
+
+#define SNDRV_MIXER_OSS_ITEM_GLOBAL	0
+#define SNDRV_MIXER_OSS_ITEM_GSWITCH	1
+#define SNDRV_MIXER_OSS_ITEM_GROUTE	2
+#define SNDRV_MIXER_OSS_ITEM_GVOLUME	3
+#define SNDRV_MIXER_OSS_ITEM_PSWITCH	4
+#define SNDRV_MIXER_OSS_ITEM_PROUTE	5
+#define SNDRV_MIXER_OSS_ITEM_PVOLUME	6
+#define SNDRV_MIXER_OSS_ITEM_CSWITCH	7
+#define SNDRV_MIXER_OSS_ITEM_CROUTE	8
+#define SNDRV_MIXER_OSS_ITEM_CVOLUME	9
+#define SNDRV_MIXER_OSS_ITEM_CAPTURE	10
+
+#define SNDRV_MIXER_OSS_ITEM_COUNT	11
+
+#define SNDRV_MIXER_OSS_PRESENT_GLOBAL	(1<<0)
+#define SNDRV_MIXER_OSS_PRESENT_GSWITCH	(1<<1)
+#define SNDRV_MIXER_OSS_PRESENT_GROUTE	(1<<2)
+#define SNDRV_MIXER_OSS_PRESENT_GVOLUME	(1<<3)
+#define SNDRV_MIXER_OSS_PRESENT_PSWITCH	(1<<4)
+#define SNDRV_MIXER_OSS_PRESENT_PROUTE	(1<<5)
+#define SNDRV_MIXER_OSS_PRESENT_PVOLUME	(1<<6)
+#define SNDRV_MIXER_OSS_PRESENT_CSWITCH	(1<<7)
+#define SNDRV_MIXER_OSS_PRESENT_CROUTE	(1<<8)
+#define SNDRV_MIXER_OSS_PRESENT_CVOLUME	(1<<9)
+#define SNDRV_MIXER_OSS_PRESENT_CAPTURE	(1<<10)
+
+struct slot {
+	unsigned int signature;
+	unsigned int present;
+	int channels;
+	snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT];
+	unsigned int capture_item;
+};
+
+static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
+{
+	snd_card_t * card = mixer->card;
+	snd_ctl_elem_id_t id;
+	
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, name);
+	id.index = index;
+	return snd_ctl_find_id(card, &id);
+}
+
+static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer,
+					  snd_mixer_oss_slot_t *pslot,
+					  snd_kcontrol_t *kctl,
+					  int *left, int *right)
+{
+	snd_ctl_elem_info_t uinfo;
+	snd_ctl_elem_value_t uctl;
+
+	snd_runtime_check(kctl != NULL, return);
+	memset(&uinfo, 0, sizeof(uinfo));
+	memset(&uctl, 0, sizeof(uctl));
+	snd_runtime_check(!kctl->info(kctl, &uinfo), return);
+	snd_runtime_check(!kctl->get(kctl, &uctl), return);
+	snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return);
+	*left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][0]);
+	if (uinfo.count > 1)
+		*right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][1]);
+}
+
+static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer,
+					 snd_mixer_oss_slot_t *pslot,
+					 snd_kcontrol_t *kctl,
+					 int *left, int *right,
+					 int route)
+{
+	snd_ctl_elem_info_t uinfo;
+	snd_ctl_elem_value_t uctl;
+
+	snd_runtime_check(kctl != NULL, return);
+	memset(&uinfo, 0, sizeof(uinfo));
+	memset(&uctl, 0, sizeof(uctl));
+	snd_runtime_check(!kctl->info(kctl, &uinfo), return);
+	snd_runtime_check(!kctl->get(kctl, &uctl), return);
+	if (!uctl.value.integer.value[0]) {
+		*left = 0;
+		if (uinfo.count == 1)
+			*right = 0;
+	}
+	if (uinfo.count > 1 && !uctl.value.integer.value[route ? 3 : 1])
+		*right = 0;
+}
+
+static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer,
+				     snd_mixer_oss_slot_t *pslot,
+				     int *left, int *right)
+{
+	snd_card_t *card = fmixer->card;
+	struct slot *slot = (struct slot *)pslot->private_data;
+	
+	*left = *right = 100;
+	read_lock(&card->control_rwlock);
+	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
+		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
+		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
+		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
+	}
+	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
+		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
+		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
+		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
+		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+	}
+	read_unlock(&card->control_rwlock);
+	return 0;
+}
+
+static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer,
+					  snd_mixer_oss_slot_t *pslot,
+					  snd_kcontrol_t *kctl,
+					  int left, int right)
+{
+	snd_ctl_elem_info_t uinfo;
+	snd_ctl_elem_value_t uctl;
+	int res;
+
+	snd_runtime_check(kctl != NULL, return);
+	memset(&uinfo, 0, sizeof(uinfo));
+	memset(&uctl, 0, sizeof(uctl));
+	snd_runtime_check(!kctl->info(kctl, &uinfo), return);
+	snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return);
+	uctl.value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo.value.integer.min, uinfo.value.integer.max);
+	if (uinfo.count > 1)
+		uctl.value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo.value.integer.min, uinfo.value.integer.max);
+	snd_runtime_check((res = kctl->put(kctl, &uctl)) >= 0, return);
+	if (res > 0)
+		snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+}
+
+static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer,
+					 snd_mixer_oss_slot_t *pslot,
+					 snd_kcontrol_t *kctl,
+					 int left, int right,
+					 int route)
+{
+	snd_ctl_elem_info_t uinfo;
+	snd_ctl_elem_value_t uctl;
+	int res;
+
+	snd_runtime_check(kctl != NULL, return);
+	memset(&uinfo, 0, sizeof(uinfo));
+	memset(&uctl, 0, sizeof(uctl));
+	snd_runtime_check(!kctl->info(kctl, &uinfo), return);
+	if (uinfo.count > 1) {
+		uctl.value.integer.value[0] = left > 0 ? 1 : 0;
+		uctl.value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
+		if (route) {
+			uctl.value.integer.value[1] =
+			uctl.value.integer.value[2] = 0;
+		}
+	} else {
+		uctl.value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
+	}
+	snd_runtime_check((res = kctl->put(kctl, &uctl)) >= 0, return);
+	if (res > 0)
+		snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+}
+
+static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer,
+				     snd_mixer_oss_slot_t *pslot,
+				     int left, int right)
+{
+	snd_card_t *card = fmixer->card;
+	struct slot *slot = (struct slot *)pslot->private_data;
+	
+	read_lock(&card->control_rwlock);
+	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
+		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
+		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
+			snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
+		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
+	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
+		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
+	}
+	if (left || right) {
+		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+		if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+		if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+	} else {
+		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
+		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
+		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
+		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
+			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
+		}
+	}
+	read_unlock(&card->control_rwlock);
+	return 0;
+}
+
+static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
+					snd_mixer_oss_slot_t *pslot,
+					int *active)
+{
+	snd_card_t *card = fmixer->card;
+	struct slot *slot = (struct slot *)pslot->private_data;
+	int left, right;
+	
+	left = right = 1;
+	read_lock(&card->control_rwlock);
+	snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);
+	read_unlock(&card->control_rwlock);
+	*active = (left || right) ? 1 : 0;
+	return 0;
+}
+
+static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer,
+					   snd_mixer_oss_slot_t *pslot,
+					   int *active)
+{
+	snd_card_t *card = fmixer->card;
+	struct slot *slot = (struct slot *)pslot->private_data;
+	int left, right;
+	
+	left = right = 1;
+	read_lock(&card->control_rwlock);
+	snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);
+	read_unlock(&card->control_rwlock);
+	*active = (left || right) ? 1 : 0;
+	return 0;
+}
+
+static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer,
+					snd_mixer_oss_slot_t *pslot,
+					int active)
+{
+	snd_card_t *card = fmixer->card;
+	struct slot *slot = (struct slot *)pslot->private_data;
+	
+	read_lock(&card->control_rwlock);
+	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
+	read_unlock(&card->control_rwlock);
+	return 0;
+}
+
+static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer,
+					   snd_mixer_oss_slot_t *pslot,
+					   int active)
+{
+	snd_card_t *card = fmixer->card;
+	struct slot *slot = (struct slot *)pslot->private_data;
+	
+	read_lock(&card->control_rwlock);
+	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
+	read_unlock(&card->control_rwlock);
+	return 0;
+}
+
+static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, int *active_index)
+{
+	snd_card_t *card = fmixer->card;
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	snd_kcontrol_t *kctl;
+	snd_mixer_oss_slot_t *pslot;
+	struct slot *slot;
+	snd_ctl_elem_info_t uinfo;
+	snd_ctl_elem_value_t uctl;
+	int err, idx;
+	
+	read_lock(&card->control_rwlock);
+	kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+	snd_runtime_check(kctl != NULL, return -ENOENT);
+	memset(&uinfo, 0, sizeof(uinfo));
+	memset(&uctl, 0, sizeof(uctl));
+	snd_runtime_check(!(err = kctl->info(kctl, &uinfo)), read_unlock(&card->control_rwlock); return err);
+	snd_runtime_check(!(err = kctl->get(kctl, &uctl)), read_unlock(&card->control_rwlock); return err);
+	read_unlock(&card->control_rwlock);
+	for (idx = 0; idx < 32; idx++) {
+		if (!(mixer->mask_recsrc & (1 << idx)))
+			continue;
+		pslot = &fmixer->mixer->slots[idx];
+		slot = (struct slot *)pslot->private_data;
+		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
+			continue;
+		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
+			continue;
+		if (slot->capture_item == uctl.value.enumerated.item[0]) {
+			*active_index = idx;
+			break;
+		}
+	}
+	return 0;
+}
+
+static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, int active_index)
+{
+	snd_card_t *card = fmixer->card;
+	snd_mixer_oss_t *mixer = fmixer->mixer;
+	snd_kcontrol_t *kctl;
+	snd_mixer_oss_slot_t *pslot;
+	struct slot *slot = NULL;
+	snd_ctl_elem_info_t uinfo;
+	snd_ctl_elem_value_t uctl;
+	int err, idx;
+
+	read_lock(&card->control_rwlock);
+	kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+	snd_runtime_check(kctl != NULL, read_unlock(&card->control_rwlock); return -ENOENT);
+	memset(&uinfo, 0, sizeof(uinfo));
+	memset(&uctl, 0, sizeof(uctl));
+	snd_runtime_check(!(err = kctl->info(kctl, &uinfo)), read_unlock(&card->control_rwlock); return err);
+	for (idx = 0; idx < 32; idx++) {
+		if (!(mixer->mask_recsrc & (1 << idx)))
+			continue;
+		pslot = &fmixer->mixer->slots[idx];
+		slot = (struct slot *)pslot->private_data;
+		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
+			continue;
+		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
+			continue;
+		if (idx == active_index)
+			break;
+		slot = NULL;
+	}
+	snd_runtime_check(slot != NULL, goto __unlock);
+	for (idx = 0; idx < uinfo.count; idx++)
+		uctl.value.enumerated.item[idx] = slot->capture_item;
+	snd_runtime_check((err = kctl->put(kctl, &uctl)) >= 0, );
+	if (err > 0)
+		snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+      __unlock:
+	read_unlock(&card->control_rwlock);
+	return 0;
+}
+
+struct snd_mixer_oss_assign_table {
+	int oss_id;
+	const char *name;
+	int index;
+};
+
+static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item)
+{
+	snd_ctl_elem_info_t info;
+	snd_kcontrol_t *kcontrol;
+	int err;
+
+	kcontrol = snd_mixer_oss_test_id(mixer, name, index);
+	if (kcontrol == NULL)
+		return 0;
+	snd_runtime_check((err = kcontrol->info(kcontrol, &info)) >= 0, return err);
+	slot->kcontrol[item] = kcontrol;
+	if (info.count > slot->channels)
+		slot->channels = info.count;
+	slot->present |= 1 << item;
+	return 0;
+}
+
+static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
+{
+	kfree(chn->private_data);
+}
+
+static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr)
+{
+	struct slot slot;
+	struct slot *pslot;
+	snd_kcontrol_t *kctl;
+	snd_mixer_oss_slot_t *rslot;
+	char str[64];	
+	
+	memset(&slot, 0, sizeof(slot));
+	if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_GLOBAL))
+		return;
+	sprintf(str, "%s Switch", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_GSWITCH))
+		return;
+	sprintf(str, "%s Route", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_GROUTE))
+		return;
+	sprintf(str, "%s Volume", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_GVOLUME))
+		return;
+	sprintf(str, "%s Playback Switch", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_PSWITCH))
+		return;
+	sprintf(str, "%s Playback Route", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_PROUTE))
+		return;
+	sprintf(str, "%s Playback Volume", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_PVOLUME))
+		return;
+	sprintf(str, "%s Capture Switch", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_CSWITCH))
+		return;
+	sprintf(str, "%s Capture Route", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_CROUTE))
+		return;
+	sprintf(str, "%s Capture Volume", ptr->name);
+	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
+				     SNDRV_MIXER_OSS_ITEM_CVOLUME))
+		return;
+	if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
+		snd_ctl_elem_info_t uinfo;
+
+		memset(&uinfo, 0, sizeof(uinfo));
+		if (kctl->info(kctl, &uinfo))
+			return;
+		strcpy(str, ptr->name);
+		if (!strcmp(str, "Master"))
+			strcpy(str, "Mix");
+		if (!strcmp(str, "Master Mono"))
+			strcpy(str, "Mix Mono");
+		slot.capture_item = 0;
+		if (!strcmp(uinfo.value.enumerated.name, str)) {
+			slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
+		} else {
+			for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) {
+				uinfo.value.enumerated.item = slot.capture_item;
+				if (kctl->info(kctl, &uinfo))
+					return;
+				if (!strcmp(uinfo.value.enumerated.name, str)) {
+					slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
+					break;
+				}
+			}
+		}
+	}
+	if (slot.present != 0) {
+		pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
+		snd_runtime_check(pslot != NULL, return);
+		*pslot = slot;
+		pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
+		rslot = &mixer->slots[ptr->oss_id];
+		rslot->stereo = slot.channels > 1 ? 1 : 0;
+		rslot->get_volume = snd_mixer_oss_get_volume1;
+		rslot->put_volume = snd_mixer_oss_put_volume1;
+		/* note: ES18xx have both Capture Source and XX Capture Volume !!! */
+		if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
+			rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;
+			rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;
+		} else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
+			rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;
+			rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;
+		} else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {
+			mixer->mask_recsrc |= 1 << ptr->oss_id;
+		}
+		rslot->private_data = pslot;
+		rslot->private_free = snd_mixer_oss_slot_free;
+	}
+}
+
+static void snd_mixer_oss_build(snd_mixer_oss_t *mixer)
+{
+	static struct snd_mixer_oss_assign_table table[] = {
+		{ SOUND_MIXER_VOLUME, 	"Master",		0 },
+		{ SOUND_MIXER_BASS,	"Tone Control - Bass",	0 },
+		{ SOUND_MIXER_TREBLE,	"Tone Control - Treble", 0 },
+		{ SOUND_MIXER_SYNTH,	"Synth",		0 },
+		{ SOUND_MIXER_PCM,	"PCM",			0 },
+		{ SOUND_MIXER_SPEAKER,	"PC Speaker", 		0 },
+		{ SOUND_MIXER_LINE,	"Line", 		0 },
+		{ SOUND_MIXER_MIC,	"Mic", 			0 },
+		{ SOUND_MIXER_CD,	"CD", 			0 },
+		{ SOUND_MIXER_IMIX,	"Monitor Mix", 		0 },
+		{ SOUND_MIXER_ALTPCM,	"PCM",			1 },
+		{ SOUND_MIXER_RECLEV,	"-- nothing --",	0 },
+		{ SOUND_MIXER_IGAIN,	"Capture",		0 },
+		{ SOUND_MIXER_OGAIN,	"Playback",		0 },
+		{ SOUND_MIXER_LINE1,	"Aux",			0 },
+		{ SOUND_MIXER_LINE2,	"Aux",			1 },
+		{ SOUND_MIXER_LINE3,	"Aux",			2 },
+		{ SOUND_MIXER_DIGITAL1,	"Digital",		0 },
+		{ SOUND_MIXER_DIGITAL2,	"Digital",		1 },
+		{ SOUND_MIXER_DIGITAL3,	"Digital",		2 },
+		{ SOUND_MIXER_PHONEIN,	"Phone",		0 },
+		{ SOUND_MIXER_PHONEOUT,	"Phone",		1 },
+		{ SOUND_MIXER_VIDEO,	"Video",		0 },
+		{ SOUND_MIXER_RADIO,	"Radio",		0 },
+		{ SOUND_MIXER_MONITOR,	"Monitor",		0 }
+	};
+	static struct snd_mixer_oss_assign_table fm_table = {
+		SOUND_MIXER_SYNTH,	"FM",			0
+	};
+	int idx;
+	
+	for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++)
+		snd_mixer_oss_build_input(mixer, &table[idx]);
+	if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL)
+		snd_mixer_oss_build_input(mixer, &fm_table);
+	if (mixer->mask_recsrc) {
+		mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
+		mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
+	}
+}
+
+/*
+ *
+ */
+
+static int snd_mixer_oss_free1(void *private)
+{
+	snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, private, return -ENXIO);
+	snd_card_t * card;
+	int idx;
+ 
+	snd_assert(mixer != NULL, return -ENXIO);
+	card = mixer->card;
+	snd_assert(mixer == card->mixer_oss, return -ENXIO);
+	card->mixer_oss = NULL;
+	for (idx = 0; idx < 31; idx++) {
+		snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
+		if (chn->private_free)
+			chn->private_free(chn);
+	}
+	snd_magic_kfree(mixer);
+	return 0;
+}
+
+static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag)
+{
+	if (!free_flag) {
+		snd_mixer_oss_t *mixer;
+		char name[128];
+		int idx, err;
+
+		mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL);
+		if (mixer == NULL)
+			return -ENOMEM;
+		sprintf(name, "mixer%i%i", card->number, 0);
+		if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
+						   card, 0,
+						   &snd_mixer_oss_reg,
+						   name)) < 0) {
+			snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0);
+			snd_magic_kfree(mixer);
+			return err;
+		}
+		mixer->card = card;
+		strcpy(mixer->name, name);
+		snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
+				      card->number,
+				      name);
+		for (idx = 0; idx < 31; idx++)
+			mixer->slots[idx].number = idx;
+		card->mixer_oss = mixer;
+		snd_mixer_oss_build(mixer);
+	} else {
+		snd_mixer_oss_t *mixer = card->mixer_oss;
+		if (mixer == NULL)
+			return 0;
+		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
+		snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+		return snd_mixer_oss_free1(mixer);
+	}
+	return 0;
+}
+
+static int __init alsa_mixer_oss_init(void)
+{
+	int idx;
+	
+	snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		if (snd_cards[idx])
+			snd_mixer_oss_notify_handler(snd_cards[idx], 0);
+	}
+	return 0;
+}
+
+static void __exit alsa_mixer_oss_exit(void)
+{
+	int idx;
+
+	snd_mixer_oss_notify_callback = NULL;
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		if (snd_cards[idx])
+			snd_mixer_oss_notify_handler(snd_cards[idx], 1);
+	}
+}
+
+module_init(alsa_mixer_oss_init)
+module_exit(alsa_mixer_oss_exit)
+
+EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
diff -Nru linux/sound/core/oss/mulaw.c linux-2.4.19-pre5-mjc/sound/core/oss/mulaw.c
--- linux/sound/core/oss/mulaw.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/mulaw.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,308 @@
+/*
+ *  Mu-Law conversion Plug-In Interface
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *                        Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Based on reference implementation by Sun Microsystems, Inc.
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+  
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define	SIGN_BIT	(0x80)		/* Sign bit for a u-law byte. */
+#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
+#define	NSEGS		(8)		/* Number of u-law segments. */
+#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
+#define	SEG_MASK	(0x70)		/* Segment field mask. */
+
+static inline int val_seg(int val)
+{
+	int r = 0;
+	val >>= 7;
+	if (val & 0xf0) {
+		val >>= 4;
+		r += 4;
+	}
+	if (val & 0x0c) {
+		val >>= 2;
+		r += 2;
+	}
+	if (val & 0x02)
+		r += 1;
+	return r;
+}
+
+#define	BIAS		(0x84)		/* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *	Biased Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	00000001wxyza			000wxyz
+ *	0000001wxyzab			001wxyz
+ *	000001wxyzabc			010wxyz
+ *	00001wxyzabcd			011wxyz
+ *	0001wxyzabcde			100wxyz
+ *	001wxyzabcdef			101wxyz
+ *	01wxyzabcdefg			110wxyz
+ *	1wxyzabcdefgh			111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char linear2ulaw(int pcm_val)	/* 2's complement (16-bit range) */
+{
+	int mask;
+	int seg;
+	unsigned char uval;
+
+	/* Get the sign and the magnitude of the value. */
+	if (pcm_val < 0) {
+		pcm_val = BIAS - pcm_val;
+		mask = 0x7F;
+	} else {
+		pcm_val += BIAS;
+		mask = 0xFF;
+	}
+	if (pcm_val > 0x7FFF)
+		pcm_val = 0x7FFF;
+
+	/* Convert the scaled magnitude to segment number. */
+	seg = val_seg(pcm_val);
+
+	/*
+	 * Combine the sign, segment, quantization bits;
+	 * and complement the code word.
+	 */
+	uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+	return uval ^ mask;
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static int ulaw2linear(unsigned char u_val)
+{
+	int t;
+
+	/* Complement to obtain normal u-law value. */
+	u_val = ~u_val;
+
+	/*
+	 * Extract and bias the quantization bits. Then
+	 * shift up by the segment number and subtract out the bias.
+	 */
+	t = ((u_val & QUANT_MASK) << 3) + BIAS;
+	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/*
+ *  Basic Mu-Law plugin
+ */
+
+typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
+			const snd_pcm_plugin_channel_t *src_channels,
+			snd_pcm_plugin_channel_t *dst_channels,
+			snd_pcm_uframes_t frames);
+
+typedef struct mulaw_private_data {
+	mulaw_f func;
+	int conv;
+} mulaw_t;
+
+static void mulaw_decode(snd_pcm_plugin_t *plugin,
+			const snd_pcm_plugin_channel_t *src_channels,
+			snd_pcm_plugin_channel_t *dst_channels,
+			snd_pcm_uframes_t frames)
+{
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef PUT_S16_LABELS
+	mulaw_t *data = (mulaw_t *)plugin->extra_data;
+	void *put = put_s16_labels[data->conv];
+	int channel;
+	int nchannels = plugin->src_format.channels;
+	for (channel = 0; channel < nchannels; ++channel) {
+		char *src;
+		char *dst;
+		int src_step, dst_step;
+		snd_pcm_uframes_t frames1;
+		if (!src_channels[channel].enabled) {
+			if (dst_channels[channel].wanted)
+				snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+			dst_channels[channel].enabled = 0;
+			continue;
+		}
+		dst_channels[channel].enabled = 1;
+		src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+		dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+		src_step = src_channels[channel].area.step / 8;
+		dst_step = dst_channels[channel].area.step / 8;
+		frames1 = frames;
+		while (frames1-- > 0) {
+			signed short sample = ulaw2linear(*src);
+			goto *put;
+#define PUT_S16_END after
+#include "plugin_ops.h"
+#undef PUT_S16_END
+		after:
+			src += src_step;
+			dst += dst_step;
+		}
+	}
+}
+
+static void mulaw_encode(snd_pcm_plugin_t *plugin,
+			const snd_pcm_plugin_channel_t *src_channels,
+			snd_pcm_plugin_channel_t *dst_channels,
+			snd_pcm_uframes_t frames)
+{
+#define GET_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+	mulaw_t *data = (mulaw_t *)plugin->extra_data;
+	void *get = get_s16_labels[data->conv];
+	int channel;
+	int nchannels = plugin->src_format.channels;
+	signed short sample = 0;
+	for (channel = 0; channel < nchannels; ++channel) {
+		char *src;
+		char *dst;
+		int src_step, dst_step;
+		snd_pcm_uframes_t frames1;
+		if (!src_channels[channel].enabled) {
+			if (dst_channels[channel].wanted)
+				snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
+			dst_channels[channel].enabled = 0;
+			continue;
+		}
+		dst_channels[channel].enabled = 1;
+		src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+		dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+		src_step = src_channels[channel].area.step / 8;
+		dst_step = dst_channels[channel].area.step / 8;
+		frames1 = frames;
+		while (frames1-- > 0) {
+			goto *get;
+#define GET_S16_END after
+#include "plugin_ops.h"
+#undef GET_S16_END
+		after:
+			*dst = linear2ulaw(sample);
+			src += src_step;
+			dst += dst_step;
+		}
+	}
+}
+
+static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin,
+			      const snd_pcm_plugin_channel_t *src_channels,
+			      snd_pcm_plugin_channel_t *dst_channels,
+			      snd_pcm_uframes_t frames)
+{
+	mulaw_t *data;
+
+	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (frames == 0)
+		return 0;
+#ifdef CONFIG_SND_DEBUG
+	{
+		unsigned int channel;
+		for (channel = 0; channel < plugin->src_format.channels; channel++) {
+			snd_assert(src_channels[channel].area.first % 8 == 0 &&
+				   src_channels[channel].area.step % 8 == 0,
+				   return -ENXIO);
+			snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+				   dst_channels[channel].area.step % 8 == 0,
+				   return -ENXIO);
+		}
+	}
+#endif
+	data = (mulaw_t *)plugin->extra_data;
+	data->func(plugin, src_channels, dst_channels, frames);
+	return frames;
+}
+
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug,
+			       snd_pcm_plugin_format_t *src_format,
+			       snd_pcm_plugin_format_t *dst_format,
+			       snd_pcm_plugin_t **r_plugin)
+{
+	int err;
+	mulaw_t *data;
+	snd_pcm_plugin_t *plugin;
+	snd_pcm_plugin_format_t *format;
+	mulaw_f func;
+
+	snd_assert(r_plugin != NULL, return -ENXIO);
+	*r_plugin = NULL;
+
+	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+
+	if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
+		format = src_format;
+		func = mulaw_encode;
+	}
+	else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
+		format = dst_format;
+		func = mulaw_decode;
+	}
+	else {
+		snd_BUG();
+		return -EINVAL;
+	}
+	snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
+
+	err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
+				   src_format, dst_format,
+				   sizeof(mulaw_t), &plugin);
+	if (err < 0)
+		return err;
+	data = (mulaw_t*)plugin->extra_data;
+	data->func = func;
+	data->conv = getput_index(format->format);
+	plugin->transfer = mulaw_transfer;
+	*r_plugin = plugin;
+	return 0;
+}
diff -Nru linux/sound/core/oss/pcm_oss.c linux-2.4.19-pre5-mjc/sound/core/oss/pcm_oss.c
--- linux/sound/core/oss/pcm_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/pcm_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2195 @@
+/*
+ *  Digital Audio (PCM) abstract layer / OSS compatible
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#if 0
+#define PLUGIN_DEBUG
+#endif
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "pcm_plugin.h"
+#include <sound/info.h>
+#include <linux/soundcard.h>
+#include <sound/initval.h>
+
+static int snd_dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int snd_adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
+static int snd_nonblock_open = 0;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
+MODULE_LICENSE("GPL");
+MODULE_PARM(snd_dsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dsp_map, "PCM device number assigned to 1st OSS device.");
+MODULE_PARM_SYNTAX(snd_dsp_map, "default:0,skill:advanced");
+MODULE_PARM(snd_adsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_adsp_map, "PCM device number assigned to 2nd OSS device.");
+MODULE_PARM_SYNTAX(snd_adsp_map, "default:1,skill:advanced");
+MODULE_PARM(snd_nonblock_open, "i");
+MODULE_PARM_DESC(snd_nonblock_open, "Don't block opening busy PCM devices.");
+MODULE_PARM_SYNTAX(snd_nonblock_open, "default:0,skill:advanced");
+
+extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg);
+static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file);
+static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file);
+static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file);
+
+EXPORT_NO_SYMBOLS;
+
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_plugin_t *plugin, *next;
+	
+	plugin = runtime->oss.plugin_first;
+	while (plugin) {
+		next = plugin->next;
+		snd_pcm_plugin_free(plugin);
+		plugin = next;
+	}
+	runtime->oss.plugin_first = runtime->oss.plugin_last = NULL;
+	return 0;
+}
+
+int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin)
+{
+	snd_pcm_runtime_t *runtime = plugin->plug->runtime;
+	plugin->next = runtime->oss.plugin_first;
+	plugin->prev = NULL;
+	if (runtime->oss.plugin_first) {
+		runtime->oss.plugin_first->prev = plugin;
+		runtime->oss.plugin_first = plugin;
+	} else {
+		runtime->oss.plugin_last =
+		runtime->oss.plugin_first = plugin;
+	}
+	return 0;
+}
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
+{
+	snd_pcm_runtime_t *runtime = plugin->plug->runtime;
+	plugin->next = NULL;
+	plugin->prev = runtime->oss.plugin_last;
+	if (runtime->oss.plugin_last) {
+		runtime->oss.plugin_last->next = plugin;
+		runtime->oss.plugin_last = plugin;
+	} else {
+		runtime->oss.plugin_last =
+		runtime->oss.plugin_first = plugin;
+	}
+	return 0;
+}
+
+static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->period_size == runtime->oss.period_bytes)
+		return frames;
+	if (runtime->period_size < runtime->oss.period_bytes)
+		return frames * (runtime->oss.period_bytes / runtime->period_size);
+	return frames / (runtime->period_size / runtime->oss.period_bytes);
+}
+
+static int snd_pcm_oss_format_from(int format)
+{
+	switch (format) {
+	case AFMT_MU_LAW:	return SNDRV_PCM_FORMAT_MU_LAW;
+	case AFMT_A_LAW:	return SNDRV_PCM_FORMAT_A_LAW;
+	case AFMT_IMA_ADPCM:	return SNDRV_PCM_FORMAT_IMA_ADPCM;
+	case AFMT_U8:		return SNDRV_PCM_FORMAT_U8;
+	case AFMT_S16_LE:	return SNDRV_PCM_FORMAT_S16_LE;
+	case AFMT_S16_BE:	return SNDRV_PCM_FORMAT_S16_BE;
+	case AFMT_S8:		return SNDRV_PCM_FORMAT_S8;
+	case AFMT_U16_LE:	return SNDRV_PCM_FORMAT_U16_LE;
+	case AFMT_U16_BE:	return SNDRV_PCM_FORMAT_U16_BE;
+	case AFMT_MPEG:		return SNDRV_PCM_FORMAT_MPEG;
+	default:			return SNDRV_PCM_FORMAT_U8;
+	}
+}
+
+static int snd_pcm_oss_format_to(int format)
+{
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:	return AFMT_MU_LAW;
+	case SNDRV_PCM_FORMAT_A_LAW:	return AFMT_A_LAW;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:	return AFMT_IMA_ADPCM;
+	case SNDRV_PCM_FORMAT_U8:		return AFMT_U8;
+	case SNDRV_PCM_FORMAT_S16_LE:	return AFMT_S16_LE;
+	case SNDRV_PCM_FORMAT_S16_BE:	return AFMT_S16_BE;
+	case SNDRV_PCM_FORMAT_S8:		return AFMT_S8;
+	case SNDRV_PCM_FORMAT_U16_LE:	return AFMT_U16_LE;
+	case SNDRV_PCM_FORMAT_U16_BE:	return AFMT_U16_BE;
+	case SNDRV_PCM_FORMAT_MPEG:		return AFMT_MPEG;
+	default:			return -EINVAL;
+	}
+}
+
+static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, 
+				   snd_pcm_hw_params_t *oss_params,
+				   snd_pcm_hw_params_t *slave_params)
+{
+	size_t s;
+	size_t oss_buffer_size, oss_period_size, oss_periods;
+	size_t min_period_size, max_period_size;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	size_t oss_frame_size;
+
+	oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
+			 params_channels(oss_params) / 8;
+
+	oss_buffer_size = snd_pcm_plug_client_size(substream,
+						   snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0)) * oss_frame_size;
+	oss_buffer_size = 1 << ld2(oss_buffer_size);
+	if (atomic_read(&runtime->mmap_count)) {
+		if (oss_buffer_size > runtime->oss.mmap_bytes)
+			oss_buffer_size = runtime->oss.mmap_bytes;
+	}
+
+	if (substream->oss.setup &&
+	    substream->oss.setup->period_size > 16)
+		oss_period_size = substream->oss.setup->period_size;
+	else if (runtime->oss.fragshift) {
+		oss_period_size = 1 << runtime->oss.fragshift;
+		if (oss_period_size > oss_buffer_size / 2)
+			oss_period_size = oss_buffer_size / 2;
+	} else {
+		int sd;
+		size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8;
+
+		oss_period_size = oss_buffer_size;
+		do {
+			oss_period_size /= 2;
+		} while (oss_period_size > bytes_per_sec);
+		if (runtime->oss.subdivision == 0) {
+			sd = 4;
+			if (oss_period_size / sd > 4096)
+				sd *= 2;
+			if (oss_period_size / sd < 4096)
+				sd = 1;
+		} else
+			sd = runtime->oss.subdivision;
+		oss_period_size /= sd;
+		if (oss_period_size < 16)
+			oss_period_size = 16;
+	}
+
+	min_period_size = snd_pcm_plug_client_size(substream,
+						   snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0));
+	min_period_size *= oss_frame_size;
+	min_period_size = 1 << (ld2(min_period_size - 1) + 1);
+	if (oss_period_size < min_period_size)
+		oss_period_size = min_period_size;
+
+	max_period_size = snd_pcm_plug_client_size(substream,
+						   snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0));
+	max_period_size *= oss_frame_size;
+	max_period_size = 1 << ld2(max_period_size);
+	if (oss_period_size > max_period_size)
+		oss_period_size = max_period_size;
+
+	oss_periods = oss_buffer_size / oss_period_size;
+
+	if (substream->oss.setup) {
+		if (substream->oss.setup->periods > 1)
+			oss_periods = substream->oss.setup->periods;
+	}
+
+	s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0);
+	if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
+		s = runtime->oss.maxfrags;
+	if (oss_periods > s)
+		oss_periods = s;
+
+	s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0);
+	if (s < 2)
+		s = 2;
+	if (oss_periods < s)
+		oss_periods = s;
+
+	while (oss_period_size * oss_periods > oss_buffer_size)
+		oss_period_size /= 2;
+
+	snd_assert(oss_period_size >= 16, return -EINVAL);
+	runtime->oss.period_bytes = oss_period_size;
+	runtime->oss.periods = oss_periods;
+	return 0;
+}
+
+static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_hw_params_t params, sparams;
+	snd_pcm_sw_params_t sw_params;
+	ssize_t oss_buffer_size, oss_period_size;
+	size_t oss_frame_size;
+	int err;
+	int direct;
+	int format, sformat, n;
+	unsigned int sformat_mask;
+	unsigned int mask;
+
+	if (atomic_read(&runtime->mmap_count)) {
+		direct = 1;
+	} else {
+		snd_pcm_oss_setup_t *setup = substream->oss.setup;
+		direct = (setup != NULL && setup->direct);
+	}
+
+	_snd_pcm_hw_params_any(&sparams);
+	_snd_pcm_hw_param_setinteger(&sparams, SNDRV_PCM_HW_PARAM_PERIODS);
+	_snd_pcm_hw_param_min(&sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
+	if (atomic_read(&runtime->mmap_count))
+		mask = 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+	else {
+		mask = 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+		if (!direct)
+			mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+	}
+	err = snd_pcm_hw_param_mask(substream, &sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
+	if (err < 0) {
+		snd_printd("No usable accesses\n");
+		return -EINVAL;
+	}
+	snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_RATE, runtime->oss.rate, 0);
+	snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0);
+
+	format = snd_pcm_oss_format_from(runtime->oss.format);
+
+	sformat_mask = *hw_param_mask(&sparams, SNDRV_PCM_HW_PARAM_FORMAT);
+	if (direct)
+		sformat = format;
+	else
+		sformat = snd_pcm_plug_slave_format(format, sformat_mask);
+
+	if (sformat < 0 || !(sformat_mask & (1 << sformat))) {
+		for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
+			if ((sformat_mask & (1 << sformat)) &&
+			    snd_pcm_oss_format_to(sformat) >= 0)
+				break;
+		}
+		if (sformat > SNDRV_PCM_FORMAT_LAST) {
+			snd_printd("Cannot find a format!!!\n");
+			return -EINVAL;
+		}
+	}
+	err = _snd_pcm_hw_param_set(&sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
+	snd_assert(err >= 0, return err);
+
+	if (direct) {
+		params = sparams;
+	} else {
+		_snd_pcm_hw_params_any(&params);
+		_snd_pcm_hw_param_set(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+				      SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+		_snd_pcm_hw_param_set(&params, SNDRV_PCM_HW_PARAM_FORMAT,
+				      snd_pcm_oss_format_from(runtime->oss.format), 0);
+		_snd_pcm_hw_param_set(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
+				      runtime->oss.channels, 0);
+		_snd_pcm_hw_param_set(&params, SNDRV_PCM_HW_PARAM_RATE,
+				      runtime->oss.rate, 0);
+		pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",
+			 params_access(&params), params_format(&params),
+			 params_channels(&params), params_rate(&params));
+	}
+	pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",
+		 params_access(&sparams), params_format(&sparams),
+		 params_channels(&sparams), params_rate(&sparams));
+
+	oss_frame_size = snd_pcm_format_physical_width(params_format(&params)) *
+			 params_channels(&params) / 8;
+
+	snd_pcm_oss_plugin_clear(substream);
+	if (!direct) {
+		/* add necessary plugins */
+		snd_pcm_oss_plugin_clear(substream);
+		if ((err = snd_pcm_plug_format_plugins(substream,
+						       &params, 
+						       &sparams)) < 0) {
+			snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
+			snd_pcm_oss_plugin_clear(substream);
+			return err;
+		}
+		if (runtime->oss.plugin_first) {
+			snd_pcm_plugin_t *plugin;
+			if ((err = snd_pcm_plugin_build_io(substream, &sparams, &plugin)) < 0) {
+				snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
+				snd_pcm_oss_plugin_clear(substream);
+				return err;
+			}
+			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+				err = snd_pcm_plugin_append(plugin);
+			} else {
+				err = snd_pcm_plugin_insert(plugin);
+			}
+			if (err < 0) {
+				snd_pcm_oss_plugin_clear(substream);
+				return err;
+			}
+		}
+	}
+
+	err = snd_pcm_oss_period_size(substream, &params, &sparams);
+	if (err < 0)
+		return err;
+
+	n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
+	err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, 0);
+	snd_assert(err >= 0, return err);
+
+	err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIODS,
+				     runtime->oss.periods, 0);
+	snd_assert(err >= 0, return err);
+
+	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0);
+
+	if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, &sparams)) < 0) {
+		snd_printd("HW_PARAMS failed: %i\n", err);
+		return err;
+	}
+
+	memset(&sw_params, 0, sizeof(sw_params));
+	if (runtime->oss.trigger) {
+		sw_params.start_threshold = 1;
+	} else {
+		sw_params.start_threshold = runtime->boundary;
+	}
+	if (atomic_read(&runtime->mmap_count))
+		sw_params.stop_threshold = runtime->boundary;
+	else
+		sw_params.stop_threshold = runtime->buffer_size;
+	sw_params.tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+	sw_params.period_step = 1;
+	sw_params.sleep_min = 0;
+	sw_params.avail_min = runtime->period_size;
+	sw_params.xfer_align = 1;
+	sw_params.silence_threshold = 0;
+	sw_params.silence_size = 0;
+
+	if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params)) < 0) {
+		snd_printd("SW_PARAMS failed: %i\n", err);
+		return err;
+	}
+	runtime->control->avail_min = runtime->period_size;
+
+	runtime->oss.periods = params_periods(&sparams);
+	oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(&sparams));
+	snd_assert(oss_period_size >= 0, return -EINVAL);
+	if (runtime->oss.plugin_first) {
+		err = snd_pcm_plug_alloc(substream, oss_period_size);
+		if (err < 0)
+			return err;
+	}
+	oss_period_size *= oss_frame_size;
+
+	oss_buffer_size = oss_period_size * runtime->oss.periods;
+	snd_assert(oss_buffer_size >= 0, return -EINVAL);
+
+	runtime->oss.period_bytes = oss_period_size;
+	runtime->oss.buffer_bytes = oss_buffer_size;
+
+	pdprintf("oss: period bytes = %i, buffer bytes = %i\n",
+		 runtime->oss.period_bytes,
+		 runtime->oss.buffer_bytes);
+	pdprintf("slave: period_size = %i, buffer_size = %i\n",
+		 params_period_size(&sparams),
+		 params_buffer_size(&sparams));
+
+	runtime->oss.format = snd_pcm_oss_format_to(params_format(&params));
+	runtime->oss.channels = params_channels(&params);
+	runtime->oss.rate = params_rate(&params);
+
+	runtime->oss.params = 0;
+	runtime->oss.prepare = 1;
+	if (runtime->oss.buffer != NULL)
+		vfree(runtime->oss.buffer);
+	runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+	runtime->oss.buffer_used = 0;
+	snd_assert(runtime->dma_area != NULL, return -EIO);
+	snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
+	return 0;
+}
+
+static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream)
+{
+	int idx, err;
+	snd_pcm_substream_t *asubstream = NULL, *substream;
+
+	for (idx = 0; idx < 2; idx++) {
+		substream = pcm_oss_file->streams[idx];
+		if (substream == NULL)
+			continue;
+		if (asubstream == NULL)
+			asubstream = substream;
+		if (substream->runtime->oss.params) {
+			err = snd_pcm_oss_change_params(substream);
+			if (err < 0)
+				return err;
+		}
+	}
+	snd_assert(asubstream != NULL, return -EIO);
+	if (r_substream)
+		*r_substream = asubstream;
+	return 0;
+}
+
+static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream)
+{
+	int err;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, 0);
+	if (err < 0) {
+		snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
+		return err;
+	}
+	runtime->oss.prepare = 0;
+	runtime->oss.prev_hw_ptr_interrupt = 0;
+
+	return 0;
+}
+
+static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime;
+	int err;
+
+	if (substream == NULL)
+		return 0;
+	runtime = substream->runtime;
+	if (runtime->oss.params) {
+		err = snd_pcm_oss_change_params(substream);
+		if (err < 0)
+			return err;
+	}
+	if (runtime->oss.prepare) {
+		err = snd_pcm_oss_prepare(substream);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret;
+	while (1) {
+		if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+		    runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+			ret = snd_pcm_oss_prepare(substream);
+			if (ret < 0)
+				break;
+		}
+		if (in_kernel) {
+			mm_segment_t fs;
+			fs = snd_enter_user();
+			ret = snd_pcm_lib_write(substream, ptr, frames);
+			snd_leave_user(fs);
+		} else {
+			ret = snd_pcm_lib_write(substream, ptr, frames);
+		}
+		if (ret != -EPIPE && ret != -ESTRPIPE)
+			break;
+		/* test, if we can't store new data, because the stream */
+		/* has not been started */
+		if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+			return -EAGAIN;
+	}
+	return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret;
+	while (1) {
+		if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+		    runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+			ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0);
+			if (ret < 0)
+				break;
+		} else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+			ret = snd_pcm_oss_prepare(substream);
+			if (ret < 0)
+				break;
+		}
+		ret = snd_pcm_lib_read(substream, ptr, frames);
+		if (in_kernel) {
+			mm_segment_t fs;
+			fs = snd_enter_user();
+			ret = snd_pcm_lib_read(substream, ptr, frames);
+			snd_leave_user(fs);
+		} else {
+			ret = snd_pcm_lib_read(substream, ptr, frames);
+		}
+		if (ret != -EPIPE && ret != -ESTRPIPE)
+			break;
+	}
+	return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret;
+	while (1) {
+		if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+		    runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+			ret = snd_pcm_oss_prepare(substream);
+			if (ret < 0)
+				break;
+		}
+		if (in_kernel) {
+			mm_segment_t fs;
+			fs = snd_enter_user();
+			ret = snd_pcm_lib_writev(substream, bufs, frames);
+			snd_leave_user(fs);
+		} else {
+			ret = snd_pcm_lib_writev(substream, bufs, frames);
+		}
+		if (ret != -EPIPE && ret != -ESTRPIPE)
+			break;
+
+		/* test, if we can't store new data, because the stream */
+		/* has not been started */
+		if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+			return -EAGAIN;
+	}
+	return ret;
+}
+	
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret;
+	while (1) {
+		if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+		    runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+			ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0);
+			if (ret < 0)
+				break;
+		} else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+			ret = snd_pcm_oss_prepare(substream);
+			if (ret < 0)
+				break;
+		}
+		if (in_kernel) {
+			mm_segment_t fs;
+			fs = snd_enter_user();
+			ret = snd_pcm_lib_readv(substream, bufs, frames);
+			snd_leave_user(fs);
+		} else {
+			ret = snd_pcm_lib_readv(substream, bufs, frames);
+		}
+		if (ret != -EPIPE && ret != -ESTRPIPE)
+			break;
+	}
+	return ret;
+}
+
+static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t frames, frames1;
+	if (runtime->oss.plugin_first) {
+		snd_pcm_plugin_channel_t *channels;
+		size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
+		if (!in_kernel) {
+			if (copy_from_user(runtime->oss.buffer, buf, bytes))
+				return -EFAULT;
+			buf = runtime->oss.buffer;
+		}
+		frames = bytes / oss_frame_bytes;
+		frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels);
+		if (frames1 < 0)
+			return frames1;
+		frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1);
+		if (frames1 <= 0)
+			return frames1;
+		bytes = frames1 * oss_frame_bytes;
+	} else {
+		frames = bytes_to_frames(runtime, bytes);
+		frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel);
+		if (frames1 <= 0)
+			return frames1;
+		bytes = frames_to_bytes(runtime, frames1);
+	}
+	return bytes;
+}
+
+static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char *buf, size_t bytes)
+{
+	size_t xfer = 0;
+	ssize_t tmp;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (atomic_read(&runtime->mmap_count))
+		return -ENXIO;
+
+	if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
+		return tmp;
+	while (bytes > 0) {
+		if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
+			tmp = bytes;
+			if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
+				tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;
+			if (tmp > 0) {
+				if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp))
+					return xfer > 0 ? xfer : -EFAULT;
+			}
+			runtime->oss.buffer_used += tmp;
+			buf += tmp;
+			bytes -= tmp;
+			xfer += tmp;
+			if (runtime->oss.buffer_used == runtime->oss.period_bytes) {
+				tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
+				if (tmp <= 0)
+					return xfer > 0 ? xfer : tmp;
+				runtime->oss.bytes += tmp;
+				runtime->oss.buffer_used = 0;
+			}
+		} else {
+			tmp = snd_pcm_oss_write2(substream, (char *)buf, runtime->oss.period_bytes, 0);
+			if (tmp <= 0)
+				return xfer > 0 ? xfer : tmp;
+			runtime->oss.bytes += tmp;
+			buf += tmp;
+			bytes -= tmp;
+			xfer += tmp;
+		}
+	}
+	return xfer;
+}
+
+static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t frames, frames1;
+	char *final_dst = buf;
+	if (runtime->oss.plugin_first) {
+		snd_pcm_plugin_channel_t *channels;
+		size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
+		if (!in_kernel)
+			buf = runtime->oss.buffer;
+		frames = bytes / oss_frame_bytes;
+		frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels);
+		if (frames1 < 0)
+			return frames1;
+		frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1);
+		if (frames1 <= 0)
+			return frames1;
+		bytes = frames1 * oss_frame_bytes;
+		if (!in_kernel && copy_to_user(final_dst, buf, bytes))
+			return -EFAULT;
+	} else {
+		frames = bytes_to_frames(runtime, bytes);
+		frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel);
+		if (frames1 <= 0)
+			return frames1;
+		bytes = frames_to_bytes(runtime, frames1);
+	}
+	return bytes;
+}
+
+static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char *buf, size_t bytes)
+{
+	size_t xfer = 0;
+	ssize_t tmp;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (atomic_read(&runtime->mmap_count))
+		return -ENXIO;
+
+	if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
+		return tmp;
+	while (bytes > 0) {
+		if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
+			if (runtime->oss.buffer_used == 0) {
+				tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
+				if (tmp <= 0)
+					return xfer > 0 ? xfer : tmp;
+				runtime->oss.bytes += tmp;
+				runtime->oss.buffer_used = runtime->oss.period_bytes;
+			}
+			tmp = bytes;
+			if ((size_t) tmp > runtime->oss.buffer_used)
+				tmp = runtime->oss.buffer_used;
+			if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_bytes - runtime->oss.buffer_used), tmp))
+				return xfer > 0 ? xfer : -EFAULT;
+			buf += tmp;
+			bytes -= tmp;
+			xfer += tmp;
+			runtime->oss.buffer_used -= tmp;
+		} else {
+			tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0);
+			if (tmp <= 0)
+				return xfer > 0 ? xfer : tmp;
+			runtime->oss.bytes += tmp;
+			buf += tmp;
+			bytes -= tmp;
+			xfer += tmp;
+		}
+	}
+	return xfer;
+}
+
+static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (substream != NULL) {
+		snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0);
+		substream->runtime->oss.prepare = 1;
+	}
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+	if (substream != NULL) {
+		snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0);
+		substream->runtime->oss.prepare = 1;
+	}
+	return 0;
+}
+
+static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	int err = 0;
+	unsigned int saved_f_flags;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	ssize_t result;
+	wait_queue_t wait;
+
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (substream != NULL) {
+		if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+			return err;
+		
+		runtime = substream->runtime;
+		if (runtime->oss.buffer_used > 0) {
+			snd_pcm_format_set_silence(runtime->format,
+						   runtime->oss.buffer + runtime->oss.buffer_used,
+						   bytes_to_samples(runtime, runtime->oss.period_bytes - runtime->oss.buffer_used));
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&runtime->sleep, &wait);
+			while (1) {
+				result = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
+				if (result > 0) {
+					runtime->oss.buffer_used = 0;
+					break;
+				}
+				if (result != 0 && result != -EAGAIN)
+					break;
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule();
+				if (signal_pending(current)) {
+					result = -ERESTARTSYS;
+					break;
+				}
+			}
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&runtime->sleep, &wait);
+			if (result < 0)
+				return result;
+		}
+		saved_f_flags = substream->ffile->f_flags;
+		substream->ffile->f_flags &= ~O_NONBLOCK;
+		err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0);
+		substream->ffile->f_flags = saved_f_flags;
+		if (err < 0)
+			return err;
+		runtime->oss.prepare = 1;
+	}
+
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+	if (substream != NULL) {
+		if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+			return err;
+		runtime = substream->runtime;
+		err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0);
+		if (err < 0)
+			return err;
+		runtime->oss.buffer_used = 0;
+		runtime->oss.prepare = 1;
+	}
+	return 0;
+}
+
+static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate)
+{
+	int idx;
+
+	for (idx = 1; idx >= 0; --idx) {
+		snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+		snd_pcm_runtime_t *runtime;
+		if (substream == NULL)
+			continue;
+		runtime = substream->runtime;
+		if (rate < 1000)
+			rate = 1000;
+		else if (rate > 48000)
+			rate = 48000;
+		if (runtime->oss.rate != rate) {
+			runtime->oss.params = 1;
+			runtime->oss.rate = rate;
+		}
+	}
+	return snd_pcm_oss_get_rate(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+	int err;
+	
+	if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+		return err;
+	return substream->runtime->oss.rate;
+}
+
+static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, int channels)
+{
+	int idx;
+	if (channels < 1)
+		channels = 1;
+	if (channels > 128)
+		return -EINVAL;
+	for (idx = 1; idx >= 0; --idx) {
+		snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+		snd_pcm_runtime_t *runtime;
+		if (substream == NULL)
+			continue;
+		runtime = substream->runtime;
+		if (runtime->oss.channels != channels) {
+			runtime->oss.params = 1;
+			runtime->oss.channels = channels;
+		}
+	}
+	return snd_pcm_oss_get_channels(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+	int err;
+	
+	if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+		return err;
+	return substream->runtime->oss.channels;
+}
+
+static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+	int err;
+	
+	if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+		return err;
+	return substream->runtime->oss.period_bytes;
+}
+
+static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+	int err;
+	int direct;
+	snd_pcm_hw_params_t params;
+	unsigned int formats = 0;
+	unsigned int format_mask;
+	int fmt;
+	if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+		return err;
+	if (atomic_read(&substream->runtime->mmap_count)) {
+		direct = 1;
+	} else {
+		snd_pcm_oss_setup_t *setup = substream->oss.setup;
+		direct = (setup != NULL && setup->direct);
+	}
+	if (!direct)
+		return AFMT_MU_LAW | AFMT_U8 |
+		       AFMT_S16_LE | AFMT_S16_BE |
+		       AFMT_S8 | AFMT_U16_LE |
+		       AFMT_U16_BE;
+	_snd_pcm_hw_params_any(&params);
+	err = snd_pcm_hw_refine(substream, &params);
+	snd_assert(err >= 0, return err);
+	format_mask = *hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT); 
+	for (fmt = 0; fmt < 32; ++fmt) {
+		if (format_mask & (1 << fmt)) {
+			int f = snd_pcm_oss_format_to(fmt);
+			if (f >= 0)
+				formats |= f;
+		}
+	}
+	return formats;
+}
+
+static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format)
+{
+	int formats, idx;
+	
+	if (format != AFMT_QUERY) {
+		formats = snd_pcm_oss_get_formats(pcm_oss_file);
+		if (!(formats & format))
+			format = AFMT_U8;
+		for (idx = 1; idx >= 0; --idx) {
+			snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+			snd_pcm_runtime_t *runtime;
+			if (substream == NULL)
+				continue;
+			runtime = substream->runtime;
+			if (runtime->oss.format != format) {
+				runtime->oss.params = 1;
+				runtime->oss.format = format;
+			}
+		}
+	}
+	return snd_pcm_oss_get_format(pcm_oss_file);
+}
+
+static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+	int err;
+	
+	if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+		return err;
+	return substream->runtime->oss.format;
+}
+
+static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide)
+{
+	snd_pcm_runtime_t *runtime;
+
+	if (substream == NULL)
+		return 0;
+	runtime = substream->runtime;
+	if (subdivide == 0) {
+		subdivide = runtime->oss.subdivision;
+		if (subdivide == 0)
+			subdivide = 1;
+		return subdivide;
+	}
+	if (runtime->oss.subdivision || runtime->oss.fragshift)
+		return -EINVAL;
+	if (subdivide != 1 && subdivide != 2 && subdivide != 4 &&
+	    subdivide != 8 && subdivide != 16)
+		return -EINVAL;
+	runtime->oss.subdivision = subdivide;
+	runtime->oss.params = 1;
+	return subdivide;
+}
+
+static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide)
+{
+	int err = -EINVAL, idx;
+
+	for (idx = 1; idx >= 0; --idx) {
+		snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+		if (substream == NULL)
+			continue;
+		if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
+			return err;
+	}
+	return err;
+}
+
+static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val)
+{
+	snd_pcm_runtime_t *runtime;
+
+	if (substream == NULL)
+		return 0;
+	runtime = substream->runtime;
+	if (runtime->oss.subdivision || runtime->oss.fragshift)
+		return -EINVAL;
+	runtime->oss.fragshift = val & 0xffff;
+	runtime->oss.maxfrags = (val >> 16) & 0xffff;
+	if (runtime->oss.fragshift < 4)		/* < 16 */
+		runtime->oss.fragshift = 4;
+	if (runtime->oss.maxfrags < 2)
+		runtime->oss.maxfrags = 2;
+	runtime->oss.params = 1;
+	return 0;
+}
+
+static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val)
+{
+	int err = -EINVAL, idx;
+
+	for (idx = 1; idx >= 0; --idx) {
+		snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+		if (substream == NULL)
+			continue;
+		if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
+			return err;
+	}
+	return err;
+}
+
+static int snd_pcm_oss_nonblock(struct file * file)
+{
+	file->f_flags |= O_NONBLOCK;
+	return 0;
+}
+
+static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res)
+{
+
+	if (substream == NULL) {
+		res &= ~DSP_CAP_DUPLEX;
+		return res;
+	}
+#ifdef DSP_CAP_MULTI
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		if (substream->pstr->substream_count > 1)
+			res |= DSP_CAP_MULTI;
+#endif
+	/* DSP_CAP_REALTIME is set all times: */
+	/* all ALSA drivers can return actual pointer in ring buffer */
+#if defined(DSP_CAP_REALTIME) && 0
+	{
+		snd_pcm_runtime_t *runtime = substream->runtime;
+		if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))
+			res &= ~DSP_CAP_REALTIME;
+	}
+#endif
+	return res;
+}
+
+static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	int result, idx;
+	
+	result = DSP_CAP_TRIGGER | DSP_CAP_MMAP	| DSP_CAP_DUPLEX | DSP_CAP_REALTIME;
+	for (idx = 0; idx < 2; idx++) {
+		snd_pcm_substream_t *substream = pcm_oss_file->streams[idx];
+		result = snd_pcm_oss_get_caps1(substream, result);
+	}
+	result |= 0x0001;	/* revision - same as SB AWE 64 */
+	return result;
+}
+
+static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t appl_ptr;
+	appl_ptr = runtime->hw_ptr_interrupt + runtime->buffer_size;
+	appl_ptr %= runtime->boundary;
+	runtime->control->appl_ptr = appl_ptr;
+}
+
+static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger)
+{
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+	int err, cmd;
+	
+	psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+	if (psubstream) {
+		if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
+			return err;
+		if (atomic_read(&psubstream->runtime->mmap_count))
+			snd_pcm_oss_simulate_fill(psubstream);
+	}
+	if (csubstream) {
+		if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
+			return err;
+	}
+      	if (psubstream) {
+      		runtime = psubstream->runtime;
+		if (trigger & PCM_ENABLE_OUTPUT) {
+			if (runtime->oss.trigger)
+				goto _skip1;
+			runtime->oss.trigger = 1;
+			runtime->start_threshold = 1;
+			cmd = SNDRV_PCM_IOCTL_START;
+		} else {
+			if (!runtime->oss.trigger)
+				goto _skip1;
+			runtime->oss.trigger = 0;
+			runtime->start_threshold = runtime->boundary;
+			cmd = SNDRV_PCM_IOCTL_DROP;
+			runtime->oss.prepare = 1;
+		}
+		err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, 0);
+		if (err < 0)
+			return err;
+	}
+ _skip1:
+	if (csubstream) {
+      		runtime = csubstream->runtime;
+		if (trigger & PCM_ENABLE_INPUT) {
+			if (runtime->oss.trigger)
+				goto _skip2;
+			runtime->oss.trigger = 1;
+			runtime->start_threshold = 1;
+			cmd = SNDRV_PCM_IOCTL_START;
+		} else {
+			if (!runtime->oss.trigger)
+				goto _skip2;
+			runtime->oss.trigger = 0;
+			runtime->start_threshold = runtime->boundary;
+			cmd = SNDRV_PCM_IOCTL_DROP;
+			runtime->oss.prepare = 1;
+		}
+		err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, 0);
+		if (err < 0)
+			return err;
+	}
+ _skip2:
+	return 0;
+}
+
+static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+	int result = 0;
+
+	psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+	if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger)
+		result |= PCM_ENABLE_OUTPUT;
+	if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger)
+		result |= PCM_ENABLE_INPUT;
+	return result;
+}
+
+static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_sframes_t delay;
+	int err;
+
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (substream == NULL)
+		return -EINVAL;
+	if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+		return err;
+	runtime = substream->runtime;
+	if (runtime->oss.params || runtime->oss.prepare)
+		return 0;
+	err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+	if (err == -EPIPE)
+		delay = 0;	/* hack for broken OSS applications */
+	else if (err < 0)
+		return err;
+	return snd_pcm_oss_bytes(substream, delay);
+}
+
+static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info * _info)
+{	
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_status_t status;
+	struct count_info info;
+	int err;
+
+	if (_info == NULL)
+		return -EFAULT;
+	substream = pcm_oss_file->streams[stream];
+	if (substream == NULL)
+		return -EINVAL;
+	if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+		return err;
+	runtime = substream->runtime;
+	if (runtime->oss.params || runtime->oss.prepare) {
+		memset(&info, 0, sizeof(info));
+		if (copy_to_user(_info, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	memset(&status, 0, sizeof(status));
+	err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status);
+	if (err < 0)
+		return err;
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		info.bytes = runtime->oss.bytes - snd_pcm_oss_bytes(substream, runtime->buffer_size - status.avail);
+	} else {
+		info.bytes = runtime->oss.bytes + snd_pcm_oss_bytes(substream, status.avail);
+	}
+	info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
+	if (atomic_read(&runtime->mmap_count)) {
+		snd_pcm_sframes_t n;
+		n = runtime->hw_ptr_interrupt - runtime->oss.prev_hw_ptr_interrupt;
+		if (n < 0)
+			n += runtime->boundary;
+		info.blocks = n / runtime->period_size;
+		runtime->oss.prev_hw_ptr_interrupt = runtime->hw_ptr_interrupt;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			snd_pcm_oss_simulate_fill(substream);
+	} else {
+		if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+			info.blocks = (runtime->buffer_size - status.avail) / runtime->period_size;
+		else
+			info.blocks = status.avail / runtime->period_size;
+	}
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info *_info)
+{
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_status_t status;
+	struct audio_buf_info info;
+	int err;
+
+	if (_info == NULL)
+		return -EFAULT;
+	substream = pcm_oss_file->streams[stream];
+	if (substream == NULL)
+		return -EINVAL;
+	runtime = substream->runtime;
+
+	if (runtime->oss.params &&
+	    (err = snd_pcm_oss_change_params(substream)) < 0)
+		return err;
+
+	info.fragsize = runtime->oss.period_bytes;
+	info.fragstotal = runtime->periods;
+	memset(&status, 0, sizeof(status));
+	if (runtime->oss.prepare) {
+		if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			info.bytes = runtime->oss.period_bytes * runtime->periods;
+			info.fragments = runtime->periods;
+		} else {
+			info.bytes = 0;
+			info.fragments = 0;
+		}
+	} else {
+		err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status);
+		if (err < 0)
+			return err;
+		info.bytes = snd_pcm_oss_bytes(substream, status.avail);
+		info.fragments = status.avail / runtime->period_size;
+	}
+
+#if 0
+	/* very experimental stuff to get Quake2 working */
+	runtime->oss.period = (info.periods - 1) << 16;
+	for (tmp = info.fragsize; tmp > 1; tmp >>= 1)
+		runtime->oss.period++;
+	runtime->oss.subdivision = 1;	/* disable SUBDIVIDE */
+#endif
+	// printk("space: bytes = %i, periods = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.periods, info.fragstotal, info.fragsize);
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc * _info)
+{
+	// it won't be probably implemented
+	// snd_printd("TODO: snd_pcm_oss_get_mapbuf\n");
+	return -EINVAL;
+}
+
+static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name)
+{
+	const char *ptr, *ptrl;
+	snd_pcm_oss_setup_t *setup;
+
+	down(&pcm->streams[stream].oss.setup_mutex);
+	for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
+		if (!strcmp(setup->task_name, task_name)) {
+			up(&pcm->streams[stream].oss.setup_mutex);
+			return setup;
+		}
+	}
+	ptr = ptrl = task_name;
+	while (*ptr) {
+		if (*ptr == '/')
+			ptrl = ptr + 1;
+		ptr++;
+	}
+	if (ptrl == task_name) {
+		goto __not_found;
+		return NULL;
+	}
+	for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
+		if (!strcmp(setup->task_name, ptrl)) {
+			up(&pcm->streams[stream].oss.setup_mutex);
+			return setup;
+		}
+	}
+      __not_found:
+	up(&pcm->streams[stream].oss.setup_mutex);
+	return NULL;
+}
+
+static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream,
+				       snd_pcm_oss_setup_t *setup,
+				       int minor)
+{
+	snd_pcm_runtime_t *runtime;
+
+	substream->oss.oss = 1;
+	substream->oss.setup = setup;
+	runtime = substream->runtime;
+	runtime->oss.params = 1;
+	runtime->oss.trigger = 1;
+	runtime->oss.rate = 8000;
+	switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
+	case SNDRV_MINOR_OSS_PCM_8:
+		runtime->oss.format = AFMT_U8;
+		break;
+	case SNDRV_MINOR_OSS_PCM_16:
+		runtime->oss.format = AFMT_S16_LE;
+		break;
+	default:
+		runtime->oss.format = AFMT_MU_LAW;
+	}
+	runtime->oss.channels = 1;
+	runtime->oss.fragshift = 0;
+	runtime->oss.maxfrags = 0;
+	runtime->oss.subdivision = 0;
+}
+
+static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime;
+	runtime = substream->runtime;
+	if (runtime->oss.buffer)
+		vfree(runtime->oss.buffer);
+	snd_pcm_oss_plugin_clear(substream);
+	substream->oss.file = NULL;
+	substream->oss.oss = 0;
+}
+
+static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file)
+{
+	int cidx;
+	snd_assert(pcm_oss_file != NULL, return -ENXIO);
+	for (cidx = 0; cidx < 2; ++cidx) {
+		snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx];
+		snd_pcm_runtime_t *runtime;
+		if (substream == NULL)
+			continue;
+		runtime = substream->runtime;
+		
+		spin_lock_irq(&runtime->lock);
+		if (snd_pcm_running(substream))
+			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+		spin_unlock_irq(&runtime->lock);
+		if (substream->ops->hw_free != NULL)
+			substream->ops->hw_free(substream);
+		substream->ops->close(substream);
+		substream->ffile = NULL;
+		snd_pcm_oss_release_substream(substream);
+		snd_pcm_release_substream(substream);
+	}
+	snd_magic_kfree(pcm_oss_file);
+	return 0;
+}
+
+static int snd_pcm_oss_open_file(struct file *file,
+				 snd_pcm_t *pcm,
+				 snd_pcm_oss_file_t **rpcm_oss_file,
+				 int minor,
+				 snd_pcm_oss_setup_t *psetup,
+				 snd_pcm_oss_setup_t *csetup)
+{
+	int err = 0;
+	snd_pcm_oss_file_t *pcm_oss_file;
+	snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+	unsigned int f_mode = file->f_mode;
+
+	snd_assert(rpcm_oss_file != NULL, return -EINVAL);
+	*rpcm_oss_file = NULL;
+
+	pcm_oss_file = snd_magic_kcalloc(snd_pcm_oss_file_t, 0, GFP_KERNEL);
+	if (pcm_oss_file == NULL)
+		return -ENOMEM;
+
+	if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&
+	    (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
+		f_mode = FMODE_WRITE;
+	if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) {
+		if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					       &psubstream)) < 0) {
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream;
+	}
+	if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) {
+		if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, 
+					       &csubstream)) < 0) {
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream;
+	}
+	
+	if (psubstream == NULL && csubstream == NULL) {
+		snd_pcm_oss_release_file(pcm_oss_file);
+		return -EINVAL;
+	}
+	if (psubstream != NULL) {
+		psubstream->oss.file = pcm_oss_file;
+		err = snd_pcm_hw_constraints_init(psubstream);
+		if (err < 0) {
+			snd_printd("snd_pcm_hw_constraint_init failed\n");
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		if ((err = psubstream->ops->open(psubstream)) < 0) {
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		err = snd_pcm_hw_constraints_complete(psubstream);
+		if (err < 0) {
+			snd_printd("snd_pcm_hw_constraint_complete failed\n");
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		psubstream->ffile = file;
+		snd_pcm_oss_init_substream(psubstream, psetup, minor);
+	}
+	if (csubstream != NULL) {
+		csubstream->oss.file = pcm_oss_file;
+		err = snd_pcm_hw_constraints_init(csubstream);
+		if (err < 0) {
+			snd_printd("snd_pcm_hw_constraint_init failed\n");
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		if ((err = csubstream->ops->open(csubstream)) < 0) {
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		err = snd_pcm_hw_constraints_complete(csubstream);
+		if (err < 0) {
+			snd_printd("snd_pcm_hw_constraint_complete failed\n");
+			snd_pcm_oss_release_file(pcm_oss_file);
+			return err;
+		}
+		csubstream->ffile = file;
+		snd_pcm_oss_init_substream(csubstream, csetup, minor);
+	}
+
+	file->private_data = pcm_oss_file;
+	*rpcm_oss_file = pcm_oss_file;
+	return 0;
+}
+
+
+static int snd_pcm_oss_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	int cardnum = SNDRV_MINOR_OSS_CARD(minor);
+	int device;
+	int err;
+	char task_name[32];
+	snd_pcm_t *pcm;
+	snd_pcm_oss_file_t *pcm_oss_file;
+	snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL;
+	int nonblock;
+	wait_queue_t wait;
+
+	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+	device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ?
+		snd_adsp_map[cardnum] : snd_dsp_map[cardnum];
+
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device];
+	if (pcm == NULL) {
+		err = -ENODEV;
+		goto __error1;
+	}
+	if (!try_inc_mod_count(pcm->card->module)) {
+		err = -EFAULT;
+		goto __error1;
+	}
+	if (snd_task_name(current, task_name, sizeof(task_name)) < 0) {
+		err = -EFAULT;
+		goto __error1;
+	}
+	if (file->f_mode & FMODE_WRITE)
+		psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name);
+	if (file->f_mode & FMODE_READ)
+		csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name);
+
+	nonblock = !!(file->f_flags & O_NONBLOCK);
+	if (psetup && !psetup->disable) {
+		if (psetup->nonblock)
+			nonblock = 1;
+		else if (psetup->block)
+			nonblock = 0;
+		else if (!nonblock)
+			nonblock = snd_nonblock_open;
+	} else if (csetup && !csetup->disable) {
+		if (csetup->nonblock)
+			nonblock = 1;
+		else if (csetup->block)
+			nonblock = 0;
+		else if (!nonblock)
+			nonblock = snd_nonblock_open;
+	}
+
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&pcm->open_wait, &wait);
+	while (1) {
+		down(&pcm->open_mutex);
+		err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,
+					    minor, psetup, csetup);
+		if (err >= 0)
+			break;
+		up(&pcm->open_mutex);
+		if (err == -EAGAIN) {
+			if (nonblock) {
+				err = -EBUSY;
+				break;
+			}
+		} else
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&pcm->open_wait, &wait);
+	if (err < 0)
+		goto __error;
+	up(&pcm->open_mutex);
+	return err;
+
+      __error:
+      	dec_mod_count(pcm->card->module);
+      __error1:
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return err;
+}
+
+static int snd_pcm_oss_release(struct inode *inode, struct file *file)
+{
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *substream;
+	snd_pcm_oss_file_t *pcm_oss_file;
+
+	pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO);
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (substream == NULL)
+		substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+	snd_assert(substream != NULL, return -ENXIO);
+	pcm = substream->pcm;
+	snd_pcm_oss_sync(pcm_oss_file);
+	down(&pcm->open_mutex);
+	snd_pcm_oss_release_file(pcm_oss_file);
+	up(&pcm->open_mutex);
+	wake_up(&pcm->open_wait);
+	dec_mod_count(pcm->card->module);
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+	snd_pcm_oss_file_t *pcm_oss_file;
+	int res;
+
+	pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO);
+	if (cmd == OSS_GETVERSION)
+		return put_user(SNDRV_OSS_VERSION, (int *)arg) ? -EFAULT : 0;
+	if (((cmd >> 8) & 0xff) == 'M')	{	/* mixer ioctl - for OSS (grrr) compatibility */
+		snd_pcm_substream_t *substream;
+		int idx;
+		for (idx = 0; idx < 2; ++idx) {
+			substream = pcm_oss_file->streams[idx];
+			if (substream != NULL)
+				break;
+		}
+		snd_assert(substream != NULL, return -ENXIO);
+		return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
+	}
+	if (((cmd >> 8) & 0xff) != 'P')
+		return -EINVAL;
+	switch (cmd) {
+	case SNDCTL_DSP_RESET:
+		return snd_pcm_oss_reset(pcm_oss_file);
+	case SNDCTL_DSP_SYNC:
+		return snd_pcm_oss_sync(pcm_oss_file);
+	case SNDCTL_DSP_SPEED:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SOUND_PCM_READ_RATE:
+		res = snd_pcm_oss_get_rate(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_STEREO:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		res = res > 0 ? 2 : 1;
+		if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
+			return res;
+		return put_user(--res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_GETBLKSIZE:
+		res = snd_pcm_oss_get_block_size(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		res = snd_pcm_oss_set_format(pcm_oss_file, res);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SOUND_PCM_READ_BITS:
+		res = snd_pcm_oss_get_format(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		res = snd_pcm_oss_set_channels(pcm_oss_file, res);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SOUND_PCM_READ_CHANNELS:
+		res = snd_pcm_oss_get_channels(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SOUND_PCM_WRITE_FILTER:
+	case SOUND_PCM_READ_FILTER:
+		return -EIO;
+	case SNDCTL_DSP_POST:	/* to do */
+		return 0;
+	case SNDCTL_DSP_SUBDIVIDE:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		res = snd_pcm_oss_set_subdivide(pcm_oss_file, res);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		return snd_pcm_oss_set_fragment(pcm_oss_file, res);
+	case SNDCTL_DSP_GETFMTS:
+		res = snd_pcm_oss_get_formats(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_GETOSPACE:
+	case SNDCTL_DSP_GETISPACE:
+		return snd_pcm_oss_get_space(pcm_oss_file,
+			cmd == SNDCTL_DSP_GETISPACE ?
+				SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+			(struct audio_buf_info *) arg);
+	case SNDCTL_DSP_NONBLOCK:
+		return snd_pcm_oss_nonblock(file);
+	case SNDCTL_DSP_GETCAPS:
+		res = snd_pcm_oss_get_caps(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_GETTRIGGER:
+		res = snd_pcm_oss_get_trigger(pcm_oss_file);
+		if (res < 0)
+			return res;
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(res, (int *)arg))
+			return -EFAULT;
+		return snd_pcm_oss_set_trigger(pcm_oss_file, res);
+	case SNDCTL_DSP_GETIPTR:
+	case SNDCTL_DSP_GETOPTR:
+		return snd_pcm_oss_get_ptr(pcm_oss_file,
+			cmd == SNDCTL_DSP_GETIPTR ?
+				SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+			(struct count_info *) arg);
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+		return snd_pcm_oss_get_mapbuf(pcm_oss_file,
+			cmd == SNDCTL_DSP_MAPINBUF ?
+				SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,
+			(struct buffmem_desc *) arg);
+	case SNDCTL_DSP_SETSYNCRO:
+		/* stop DMA now.. */
+		return 0;
+	case SNDCTL_DSP_SETDUPLEX:
+		if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX)
+			return 0;
+		return -EIO;
+	case SNDCTL_DSP_GETODELAY:
+		res = snd_pcm_oss_get_odelay(pcm_oss_file);
+		if (res < 0) {
+			/* it's for sure, some broken apps don't check for error codes */
+			put_user(0, (int *)arg);
+			return res;
+		}
+		return put_user(res, (int *)arg) ? -EFAULT : 0;
+	case SNDCTL_DSP_PROFILE:
+		return 0;	/* silently ignore */
+	default:
+		snd_printd("pcm_oss: unknown command = 0x%x\n", cmd);
+	}
+	return -EINVAL;
+}
+
+static ssize_t snd_pcm_oss_read(struct file *file, char *buf, size_t count, loff_t *offset)
+{
+	snd_pcm_oss_file_t *pcm_oss_file;
+	snd_pcm_substream_t *substream;
+
+	pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO);
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+	if (substream == NULL)
+		return -ENXIO;
+	return snd_pcm_oss_read1(substream, buf, count);
+}
+
+static ssize_t snd_pcm_oss_write(struct file *file, const char *buf, size_t count, loff_t *offset)
+{
+	snd_pcm_oss_file_t *pcm_oss_file;
+	snd_pcm_substream_t *substream;
+	long result;
+
+	pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO);
+	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (substream == NULL)
+		return -ENXIO;
+	up(&file->f_dentry->d_inode->i_sem);
+	result = snd_pcm_oss_write1(substream, buf, count);
+	down(&file->f_dentry->d_inode->i_sem);
+	return result;
+}
+
+static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (atomic_read(&runtime->mmap_count))
+		return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+	else
+		return snd_pcm_playback_ready(substream);
+}
+
+static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (atomic_read(&runtime->mmap_count))
+		return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+	else
+		return snd_pcm_capture_ready(substream);
+}
+
+static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
+{
+	snd_pcm_oss_file_t *pcm_oss_file;
+	unsigned int mask;
+	snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL;
+	
+	pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return 0);
+
+	psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+	mask = 0;
+	if (psubstream != NULL) {
+		snd_pcm_runtime_t *runtime = psubstream->runtime;
+		spin_lock_irq(&runtime->lock);
+		poll_wait(file, &runtime->sleep, wait);
+		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
+		    (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
+		     snd_pcm_oss_playback_ready(psubstream)))
+			mask |= POLLOUT | POLLWRNORM;
+		spin_unlock_irq(&runtime->lock);
+	}
+	if (csubstream != NULL) {
+		snd_pcm_runtime_t *runtime = csubstream->runtime;
+		spin_lock_irq(&runtime->lock);
+		poll_wait(file, &runtime->sleep, wait);
+		if (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
+		    snd_pcm_oss_capture_ready(csubstream))
+			mask |= POLLIN | POLLRDNORM;
+		spin_unlock_irq(&runtime->lock);
+	}
+
+	return mask;
+}
+
+static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
+{
+	snd_pcm_oss_file_t *pcm_oss_file;
+	snd_pcm_substream_t *substream = NULL;
+	snd_pcm_runtime_t *runtime;
+	int err;
+
+	pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO);
+	switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
+	case VM_READ | VM_WRITE:
+		substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+		if (substream)
+			break;
+		/* Fall through */
+	case VM_READ:
+		substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
+		break;
+	case VM_WRITE:
+		substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* set VM_READ access as well to fix memset() routines that do
+	   reads before writes (to improve performance) */
+	area->vm_flags |= VM_READ;
+	if (substream == NULL)
+		return -ENXIO;
+	runtime = substream->runtime;
+	if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID))
+		return -EIO;
+	if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED)
+		runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+	else
+		return -EIO;
+	
+	if (runtime->oss.params) {
+		if ((err = snd_pcm_oss_change_params(substream)) < 0)
+			return err;
+	}
+	if (runtime->oss.plugin_first != NULL)
+		return -EIO;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25)
+	if (area->vm_pgoff != 0)
+#else
+	if (area->vm_offset != 0)
+#endif
+		return -EINVAL;
+
+	err = snd_pcm_mmap_data(substream, file, area);
+	if (err < 0)
+		return err;
+	runtime->oss.mmap_bytes = area->vm_end - area->vm_start;
+	/* In mmap mode we never stop */
+	runtime->stop_threshold = runtime->boundary;
+
+	return 0;
+}
+
+/*
+ *  /proc interface
+ */
+
+static void snd_pcm_oss_proc_read(snd_info_entry_t *entry,
+				  snd_info_buffer_t * buffer)
+{
+	snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+	snd_pcm_oss_setup_t *setup = pstr->oss.setup_list;
+	down(&pstr->oss.setup_mutex);
+	while (setup) {
+		snd_iprintf(buffer, "%s %u %u%s%s%s%s\n",
+			    setup->task_name,
+			    setup->periods,
+			    setup->period_size,
+			    setup->disable ? " disable" : "",
+			    setup->direct ? " direct" : "",
+			    setup->block ? " block" : "",
+			    setup->nonblock ? " non-block" : "");
+		setup = setup->next;
+	}
+	up(&pstr->oss.setup_mutex);
+}
+
+static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr)
+{
+	unsigned int idx;
+	snd_pcm_substream_t *substream;
+	snd_pcm_oss_setup_t *setup, *setupn;
+
+	for (idx = 0, substream = pstr->substream;
+	     idx < pstr->substream_count; idx++, substream = substream->next)
+		substream->oss.setup = NULL;
+	for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;
+	     setup; setup = setupn) {
+		setupn = setup->next;
+		kfree(setup->task_name);
+		kfree(setup);
+	}
+	pstr->oss.setup_list = NULL;
+}
+
+static void snd_pcm_oss_proc_write(snd_info_entry_t *entry,
+				   snd_info_buffer_t * buffer)
+{
+	snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+	char line[512], str[32], task_name[32], *ptr;
+	int idx1;
+	snd_pcm_oss_setup_t *setup, *setup1, template;
+
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		down(&pstr->oss.setup_mutex);
+		memset(&template, 0, sizeof(template));
+		ptr = snd_info_get_str(task_name, line, sizeof(task_name));
+		if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
+			snd_pcm_oss_proc_free_setup_list(pstr);
+			up(&pstr->oss.setup_mutex);
+			continue;
+		}
+		for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
+			if (!strcmp(setup->task_name, task_name)) {
+				template = *setup;
+				break;
+			}
+		}
+		ptr = snd_info_get_str(str, ptr, sizeof(str));
+		template.periods = simple_strtoul(str, NULL, 10);
+		ptr = snd_info_get_str(str, ptr, sizeof(str));
+		template.period_size = simple_strtoul(str, NULL, 10);
+		for (idx1 = 31; idx1 >= 0; idx1--)
+			if (template.period_size & (1 << idx1))
+				break;
+		for (idx1--; idx1 >= 0; idx1--)
+			template.period_size &= ~(1 << idx1);
+		do {
+			ptr = snd_info_get_str(str, ptr, sizeof(str));
+			if (!strcmp(str, "disable")) {
+				template.disable = 1;
+			} else if (!strcmp(str, "direct")) {
+				template.direct = 1;
+			} else if (!strcmp(str, "block")) {
+				template.block = 1;
+			} else if (!strcmp(str, "non-block")) {
+				template.nonblock = 1;
+			}
+		} while (*str);
+		if (setup == NULL) {
+			setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL);
+			if (setup) {
+				if (pstr->oss.setup_list == NULL) {
+					pstr->oss.setup_list = setup;
+				} else {
+					for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next);
+					setup1->next = setup;
+				}
+				template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL);
+			} else {
+				buffer->error = -ENOMEM;
+			}
+		}
+		if (setup)
+			*setup = template;
+		up(&pstr->oss.setup_mutex);
+	}
+}
+
+static void snd_pcm_oss_proc_init(snd_pcm_t *pcm)
+{
+	int stream;
+	for (stream = 0; stream < 2; ++stream) {
+		snd_info_entry_t *entry;
+		snd_pcm_str_t *pstr = &pcm->streams[stream];
+		if (pstr->substream_count == 0)
+			continue;
+		if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
+			entry->content = SNDRV_INFO_CONTENT_TEXT;
+			entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+			entry->c.text.read_size = 8192;
+			entry->c.text.read = snd_pcm_oss_proc_read;
+			entry->c.text.write_size = 8192;
+			entry->c.text.write = snd_pcm_oss_proc_write;
+			entry->private_data = pstr;
+			if (snd_info_register(entry) < 0) {
+				snd_info_free_entry(entry);
+				entry = NULL;
+			}
+		}
+		pstr->oss.proc_entry = entry;
+	}
+}
+
+static void snd_pcm_oss_proc_done(snd_pcm_t *pcm)
+{
+	int stream;
+	for (stream = 0; stream < 2; ++stream) {
+		snd_pcm_str_t *pstr = &pcm->streams[stream];
+		if (pstr->oss.proc_entry) {
+			snd_info_unregister(pstr->oss.proc_entry);
+			pstr->oss.proc_entry = NULL;
+			snd_pcm_oss_proc_free_setup_list(pstr);
+		}
+	}
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static struct file_operations snd_pcm_oss_f_reg =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		snd_pcm_oss_read,
+	write:		snd_pcm_oss_write,
+	open:		snd_pcm_oss_open,
+	release:	snd_pcm_oss_release,
+	poll:		snd_pcm_oss_poll,
+	ioctl:		snd_pcm_oss_ioctl,
+	mmap:		snd_pcm_oss_mmap,
+};
+
+static snd_minor_t snd_pcm_oss_reg =
+{
+	comment:	"digital audio",
+	f_ops:		&snd_pcm_oss_f_reg,
+};
+
+static void register_oss_dsp(unsigned short native_minor, snd_pcm_t *pcm, int index)
+{
+	char name[128];
+	sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
+	if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+				    pcm->card, index, &snd_pcm_oss_reg,
+				    name) < 0) {
+		snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device);
+	}
+}
+
+static int snd_pcm_oss_register_minor(unsigned short native_minor,
+				      snd_pcm_t * pcm)
+{
+	pcm->oss.reg = 0;
+	if (snd_dsp_map[pcm->card->number] == pcm->device) {
+		char name[128];
+		int duplex;
+		register_oss_dsp(native_minor, pcm, 0);
+		duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 && 
+			      pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count && 
+			      !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX));
+		sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : "");
+		snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO,
+				      pcm->card->number,
+				      name);
+		pcm->oss.reg++;
+	}
+	if (snd_adsp_map[pcm->card->number] == pcm->device) {
+		register_oss_dsp(native_minor, pcm, 1);
+		pcm->oss.reg++;
+	}
+
+	if (pcm->oss.reg)
+		snd_pcm_oss_proc_init(pcm);
+
+	return 0;
+}
+
+static int snd_pcm_oss_unregister_minor(unsigned short native_minor,
+				        snd_pcm_t * pcm)
+{
+	if (pcm->oss.reg) {
+		if (snd_dsp_map[pcm->card->number] == pcm->device) {
+			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+						  pcm->card, 0);
+			snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
+		}
+		if (snd_adsp_map[pcm->card->number] == pcm->device)
+			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
+						  pcm->card, 1);
+		pcm->oss.reg = 0;
+		snd_pcm_oss_proc_done(pcm);
+	}
+	return 0;
+}
+
+static snd_pcm_notify_t snd_pcm_oss_notify =
+{
+	n_register:	snd_pcm_oss_register_minor,
+	n_unregister:	snd_pcm_oss_unregister_minor,
+};
+
+static int __init alsa_pcm_oss_init(void)
+{
+	int i;
+	int err;
+
+	if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
+		return err;
+	/* check device map table */
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (snd_dsp_map[i] < 0 || snd_dsp_map[i] >= SNDRV_PCM_DEVICES) {
+			snd_printk("invalid dsp_map[%d] = %d\n", i, snd_dsp_map[i]);
+			snd_dsp_map[i] = 0;
+		}
+		if (snd_adsp_map[i] < 0 || snd_adsp_map[i] >= SNDRV_PCM_DEVICES) {
+			snd_printk("invalid adsp_map[%d] = %d\n", i, snd_adsp_map[i]);
+			snd_adsp_map[i] = 1;
+		}
+	}
+	return 0;
+}
+
+static void __exit alsa_pcm_oss_exit(void)
+{
+	snd_pcm_notify(&snd_pcm_oss_notify, 1);
+}
+
+module_init(alsa_pcm_oss_init)
+module_exit(alsa_pcm_oss_exit)
diff -Nru linux/sound/core/oss/pcm_plugin.c linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.c
--- linux/sound/core/oss/pcm_plugin.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1089 @@
+/*
+ *  PCM Plug-In shared (kernel/library) code
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+  
+#if 0
+#define PLUGIN_DEBUG
+#endif
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
+#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
+
+static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin,
+					    bitset_t *dst_vmask,
+					    bitset_t **src_vmask)
+{
+	bitset_t *vmask = plugin->src_vmask;
+	bitset_copy(vmask, dst_vmask, plugin->src_format.channels);
+	*src_vmask = vmask;
+	return 0;
+}
+
+static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin,
+					    bitset_t *src_vmask,
+					    bitset_t **dst_vmask)
+{
+	bitset_t *vmask = plugin->dst_vmask;
+	bitset_copy(vmask, src_vmask, plugin->dst_format.channels);
+	*dst_vmask = vmask;
+	return 0;
+}
+
+static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+	snd_pcm_plugin_format_t *format;
+	ssize_t width;
+	size_t size;
+	unsigned int channel;
+	snd_pcm_plugin_channel_t *c;
+
+	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		format = &plugin->src_format;
+	} else {
+		format = &plugin->dst_format;
+	}
+	if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+		return width;
+	size = frames * format->channels * width;
+	snd_assert((size % 8) == 0, return -ENXIO);
+	size /= 8;
+	if (plugin->buf_frames < frames) {
+		if (plugin->buf)
+			vfree(plugin->buf);
+		plugin->buf = vmalloc(size);
+		plugin->buf_frames = frames;
+	}
+	if (!plugin->buf)
+		return -ENOMEM;
+	c = plugin->buf_channels;
+	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
+		for (channel = 0; channel < format->channels; channel++, c++) {
+			c->enabled = 1;
+			c->wanted = 0;
+			c->area.addr = plugin->buf;
+			c->area.first = channel * width;
+			c->area.step = format->channels * width;
+		}
+	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
+		snd_assert((size % format->channels) == 0,);
+		size /= format->channels;
+		for (channel = 0; channel < format->channels; channel++, c++) {
+			c->enabled = 1;
+			c->wanted = 0;
+			c->area.addr = plugin->buf + (channel * size);
+			c->area.first = 0;
+			c->area.step = width;
+		}
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames)
+{
+	int err;
+	snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
+	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
+		snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+		while (plugin->next) {
+			if (plugin->dst_frames)
+				frames = plugin->dst_frames(plugin, frames);
+			snd_assert(frames > 0, return -ENXIO);
+			plugin = plugin->next;
+			err = snd_pcm_plugin_alloc(plugin, frames);
+			if (err < 0)
+				return err;
+		}
+	} else {
+		snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+		while (plugin->prev) {
+			if (plugin->src_frames)
+				frames = plugin->src_frames(plugin, frames);
+			snd_assert(frames > 0, return -ENXIO);
+			plugin = plugin->prev;
+			err = snd_pcm_plugin_alloc(plugin, frames);
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+				       snd_pcm_uframes_t frames,
+				       snd_pcm_plugin_channel_t **channels)
+{
+	*channels = plugin->buf_channels;
+	return frames;
+}
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *plug,
+			 const char *name,
+			 snd_pcm_plugin_format_t *src_format,
+			 snd_pcm_plugin_format_t *dst_format,
+			 size_t extra,
+			 snd_pcm_plugin_t **ret)
+{
+	snd_pcm_plugin_t *plugin;
+	unsigned int channels;
+	
+	snd_assert(plug != NULL, return -ENXIO);
+	snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
+	plugin = (snd_pcm_plugin_t *)snd_kcalloc(sizeof(*plugin) + extra, GFP_KERNEL);
+	if (plugin == NULL)
+		return -ENOMEM;
+	plugin->name = name;
+	plugin->plug = plug;
+	plugin->stream = snd_pcm_plug_stream(plug);
+	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+	plugin->src_format = *src_format;
+	plugin->src_width = snd_pcm_format_physical_width(src_format->format);
+	snd_assert(plugin->src_width > 0, );
+	plugin->dst_format = *dst_format;
+	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
+	snd_assert(plugin->dst_width > 0, );
+	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		channels = src_format->channels;
+	else
+		channels = dst_format->channels;
+	plugin->buf_channels = snd_kcalloc(channels * sizeof(*plugin->buf_channels), GFP_KERNEL);
+	if (plugin->buf_channels == NULL) {
+		snd_pcm_plugin_free(plugin);
+		return -ENOMEM;
+	}
+	plugin->src_vmask = bitset_alloc(src_format->channels);
+	if (plugin->src_vmask == NULL) {
+		snd_pcm_plugin_free(plugin);
+		return -ENOMEM;
+	}
+	plugin->dst_vmask = bitset_alloc(dst_format->channels);
+	if (plugin->dst_vmask == NULL) {
+		snd_pcm_plugin_free(plugin);
+		return -ENOMEM;
+	}
+	plugin->client_channels = snd_pcm_plugin_client_channels;
+	plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask;
+	plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask;
+	*ret = plugin;
+	return 0;
+}
+
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
+{
+	if (! plugin)
+		return 0;
+	if (plugin->private_free)
+		plugin->private_free(plugin);
+	if (plugin->buf_channels)
+		kfree(plugin->buf_channels);
+	if (plugin->buf)
+		vfree(plugin->buf);
+	if (plugin->src_vmask)
+		kfree(plugin->src_vmask);
+	if (plugin->dst_vmask)
+		kfree(plugin->dst_vmask);
+	kfree(plugin);
+	return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames)
+{
+	snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+	int stream = snd_pcm_plug_stream(plug);
+
+	snd_assert(plug != NULL, return -ENXIO);
+	if (drv_frames == 0)
+		return 0;
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		plugin = snd_pcm_plug_last(plug);
+		while (plugin && drv_frames > 0) {
+			plugin_prev = plugin->prev;
+			if (plugin->src_frames)
+				drv_frames = plugin->src_frames(plugin, drv_frames);
+			plugin = plugin_prev;
+		}
+	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+		plugin = snd_pcm_plug_first(plug);
+		while (plugin && drv_frames > 0) {
+			plugin_next = plugin->next;
+			if (plugin->dst_frames)
+				drv_frames = plugin->dst_frames(plugin, drv_frames);
+			plugin = plugin_next;
+		}
+	} else
+		snd_BUG();
+	return drv_frames;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames)
+{
+	snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+	snd_pcm_sframes_t frames;
+	int stream = snd_pcm_plug_stream(plug);
+	
+	snd_assert(plug != NULL, return -ENXIO);
+	if (clt_frames == 0)
+		return 0;
+	frames = clt_frames;
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		plugin = snd_pcm_plug_first(plug);
+		while (plugin && frames > 0) {
+			plugin_next = plugin->next;
+			if (plugin->dst_frames) {
+				frames = plugin->dst_frames(plugin, frames);
+				if (frames < 0)
+					return frames;
+			}
+			plugin = plugin_next;
+		}
+	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+		plugin = snd_pcm_plug_last(plug);
+		while (plugin) {
+			plugin_prev = plugin->prev;
+			if (plugin->src_frames) {
+				frames = plugin->src_frames(plugin, frames);
+				if (frames < 0)
+					return frames;
+			}
+			plugin = plugin_prev;
+		}
+	} else
+		snd_BUG();
+	return frames;
+}
+
+unsigned int snd_pcm_plug_formats(unsigned int formats)
+{
+	int linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+		       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+		       SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+		       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
+		       SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
+		       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+		       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
+	formats |= SNDRV_PCM_FMTBIT_MU_LAW;
+	
+	if (formats & linfmts)
+		formats |= linfmts;
+	return formats;
+}
+
+static int preferred_formats[] = {
+	SNDRV_PCM_FORMAT_S16_LE,
+	SNDRV_PCM_FORMAT_S16_BE,
+	SNDRV_PCM_FORMAT_U16_LE,
+	SNDRV_PCM_FORMAT_U16_BE,
+	SNDRV_PCM_FORMAT_S24_LE,
+	SNDRV_PCM_FORMAT_S24_BE,
+	SNDRV_PCM_FORMAT_U24_LE,
+	SNDRV_PCM_FORMAT_U24_BE,
+	SNDRV_PCM_FORMAT_S32_LE,
+	SNDRV_PCM_FORMAT_S32_BE,
+	SNDRV_PCM_FORMAT_U32_LE,
+	SNDRV_PCM_FORMAT_U32_BE,
+	SNDRV_PCM_FORMAT_S8,
+	SNDRV_PCM_FORMAT_U8
+};
+
+int snd_pcm_plug_slave_format(int format, unsigned int format_mask)
+{
+	if (format_mask & (1 << format))
+		return format;
+	if ((snd_pcm_plug_formats(format_mask) & (1 << format)) == 0)
+		return -EINVAL;
+	if (snd_pcm_format_linear(format)) {
+		int width = snd_pcm_format_width(format);
+		int unsignd = snd_pcm_format_unsigned(format);
+		int big = snd_pcm_format_big_endian(format);
+		int format1;
+		int wid, width1=width;
+		int dwidth1 = 8;
+		for (wid = 0; wid < 4; ++wid) {
+			int end, big1 = big;
+			for (end = 0; end < 2; ++end) {
+				int sgn, unsignd1 = unsignd;
+				for (sgn = 0; sgn < 2; ++sgn) {
+					format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
+					if (format1 >= 0 &&
+					    format_mask & (1 << format1))
+						goto _found;
+					unsignd1 = !unsignd1;
+				}
+				big1 = !big1;
+			}
+			if (width1 == 32) {
+				dwidth1 = -dwidth1;
+				width1 = width;
+			}
+			width1 += dwidth1;
+		}
+		return -EINVAL;
+	_found:
+		return format1;
+	} else {
+		unsigned int i;
+		switch (format) {
+		case SNDRV_PCM_FORMAT_MU_LAW:
+			for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
+				int format1 = preferred_formats[i];
+				if (format_mask & (1 << format1))
+					return format1;
+			}
+		default:
+			return -EINVAL;
+		}
+	}
+}
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug,
+				snd_pcm_hw_params_t *params,
+				snd_pcm_hw_params_t *slave_params)
+{
+	snd_pcm_plugin_format_t tmpformat;
+	snd_pcm_plugin_format_t dstformat;
+	snd_pcm_plugin_format_t srcformat;
+	int src_access, dst_access;
+	snd_pcm_plugin_t *plugin = NULL;
+	int err, first;
+	int stream = snd_pcm_plug_stream(plug);
+	int slave_interleaved = (params_channels(slave_params) == 1 ||
+				 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+	switch (stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		dstformat.format = params_format(slave_params);
+		dstformat.rate = params_rate(slave_params);
+		dstformat.channels = params_channels(slave_params);
+		srcformat.format = params_format(params);
+		srcformat.rate = params_rate(params);
+		srcformat.channels = params_channels(params);
+		src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+		dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
+						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		dstformat.format = params_format(params);
+		dstformat.rate = params_rate(params);
+		dstformat.channels = params_channels(params);
+		srcformat.format = params_format(slave_params);
+		srcformat.rate = params_rate(slave_params);
+		srcformat.channels = params_channels(slave_params);
+		src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
+						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+		dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	tmpformat = srcformat;
+		
+	pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", 
+		 srcformat.format,
+		 srcformat.rate,
+		 srcformat.channels);
+	pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", 
+		 dstformat.format,
+		 dstformat.rate,
+		 dstformat.channels);
+
+	/* Format change (linearization) */
+	if ((srcformat.format != dstformat.format ||
+	     srcformat.rate != dstformat.rate ||
+	     srcformat.channels != dstformat.channels) &&
+	    !snd_pcm_format_linear(srcformat.format)) {
+		if (snd_pcm_format_linear(dstformat.format))
+			tmpformat.format = dstformat.format;
+		else
+			tmpformat.format = SNDRV_PCM_FORMAT_S16;
+		first = plugin == NULL;
+		switch (srcformat.format) {
+		case SNDRV_PCM_FORMAT_MU_LAW:
+			err = snd_pcm_plugin_build_mulaw(plug,
+							 &srcformat, &tmpformat,
+							 &plugin);
+			break;
+		default:
+			return -EINVAL;
+		}
+		pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
+		if (err < 0)
+			return err;
+		err = snd_pcm_plugin_append(plugin);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+		srcformat = tmpformat;
+		src_access = dst_access;
+	}
+
+	/* channels reduction */
+	if (srcformat.channels > dstformat.channels) {
+		int sv = srcformat.channels;
+		int dv = dstformat.channels;
+		route_ttable_entry_t *ttable = snd_kcalloc(dv*sv*sizeof(*ttable), GFP_KERNEL);
+		if (ttable == NULL)
+			return -ENOMEM;
+#if 1
+		if (sv == 2 && dv == 1) {
+			ttable[0] = HALF;
+			ttable[1] = HALF;
+		} else
+#endif
+		{
+			int v;
+			for (v = 0; v < dv; ++v)
+				ttable[v * sv + v] = FULL;
+		}
+		tmpformat.channels = dstformat.channels;
+		if (srcformat.rate == dstformat.rate &&
+		    snd_pcm_format_linear(dstformat.format))
+			tmpformat.format = dstformat.format;
+		err = snd_pcm_plugin_build_route(plug,
+						 &srcformat, &tmpformat,
+						 ttable, &plugin);
+		kfree(ttable);
+		pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+		err = snd_pcm_plugin_append(plugin);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+		srcformat = tmpformat;
+		src_access = dst_access;
+	}
+
+	/* rate resampling */
+	if (srcformat.rate != dstformat.rate) {
+		tmpformat.rate = dstformat.rate;
+		if (srcformat.channels == dstformat.channels &&
+		    snd_pcm_format_linear(dstformat.format))
+			tmpformat.format = dstformat.format;
+        	err = snd_pcm_plugin_build_rate(plug,
+        					&srcformat, &tmpformat,
+						&plugin);
+		pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}      					    
+		err = snd_pcm_plugin_append(plugin);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+		srcformat = tmpformat;
+		src_access = dst_access;
+        }
+
+	/* channels extension  */
+	if (srcformat.channels < dstformat.channels) {
+		int sv = srcformat.channels;
+		int dv = dstformat.channels;
+		route_ttable_entry_t *ttable = snd_kcalloc(dv * sv * sizeof(*ttable), GFP_KERNEL);
+		if (ttable == NULL)
+			return -ENOMEM;
+#if 0
+		{
+			int v;
+			for (v = 0; v < sv; ++v)
+				ttable[v * sv + v] = FULL;
+		}
+#else
+		{
+			/* Playback is spreaded on all channels */
+			int vd, vs;
+			for (vd = 0, vs = 0; vd < dv; ++vd) {
+				ttable[vd * sv + vs] = FULL;
+				vs++;
+				if (vs == sv)
+					vs = 0;
+			}
+		}
+#endif
+		tmpformat.channels = dstformat.channels;
+		if (snd_pcm_format_linear(dstformat.format))
+			tmpformat.format = dstformat.format;
+		err = snd_pcm_plugin_build_route(plug,
+						 &srcformat, &tmpformat,
+						 ttable, &plugin);
+		kfree(ttable);
+		pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}      					    
+		err = snd_pcm_plugin_append(plugin);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+		srcformat = tmpformat;
+		src_access = dst_access;
+	}
+
+	/* format change */
+	if (srcformat.format != dstformat.format) {
+		tmpformat.format = dstformat.format;
+		if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
+			err = snd_pcm_plugin_build_mulaw(plug,
+							 &srcformat, &tmpformat,
+							 &plugin);
+		}
+		else if (snd_pcm_format_linear(srcformat.format) &&
+			 snd_pcm_format_linear(tmpformat.format)) {
+			err = snd_pcm_plugin_build_linear(plug,
+							  &srcformat, &tmpformat,
+							  &plugin);
+		}
+		else
+			return -EINVAL;
+		pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
+		if (err < 0)
+			return err;
+		err = snd_pcm_plugin_append(plugin);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+		srcformat = tmpformat;
+		src_access = dst_access;
+	}
+
+	/* de-interleave */
+	if (src_access != dst_access) {
+		err = snd_pcm_plugin_build_copy(plug,
+						&srcformat,
+						&tmpformat,
+						&plugin);
+		pdprintf("interleave change (copy: returns %i)\n", err);
+		if (err < 0)
+			return err;
+		err = snd_pcm_plugin_append(plugin);
+		if (err < 0) {
+			snd_pcm_plugin_free(plugin);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug,
+					 char *buf,
+					 snd_pcm_uframes_t count,
+					 snd_pcm_plugin_channel_t **channels)
+{
+	snd_pcm_plugin_t *plugin;
+	snd_pcm_plugin_channel_t *v;
+	snd_pcm_plugin_format_t *format;
+	int width, nchannels, channel;
+	int stream = snd_pcm_plug_stream(plug);
+
+	snd_assert(buf != NULL, return -ENXIO);
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		plugin = snd_pcm_plug_first(plug);
+		format = &plugin->src_format;
+	} else {
+		plugin = snd_pcm_plug_last(plug);
+		format = &plugin->dst_format;
+	}
+	v = plugin->buf_channels;
+	*channels = v;
+	if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+		return width;
+	nchannels = format->channels;
+	snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
+	for (channel = 0; channel < nchannels; channel++, v++) {
+		v->enabled = 1;
+		v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
+		v->area.addr = buf;
+		v->area.first = channel * width;
+		v->area.step = nchannels * width;
+	}
+	return count;
+}
+
+int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
+					bitset_t *client_vmask)
+{
+	snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+	if (plugin == NULL) {
+		return 0;
+	} else {
+		int schannels = plugin->dst_format.channels;
+		bitset_t bs[bitset_size(schannels)];
+		bitset_t *srcmask;
+		bitset_t *dstmask = bs;
+		int err;
+		bitset_one(dstmask, schannels);
+		if (plugin == NULL) {
+			bitset_and(client_vmask, dstmask, schannels);
+			return 0;
+		}
+		while (1) {
+			err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
+			if (err < 0)
+				return err;
+			dstmask = srcmask;
+			if (plugin->prev == NULL)
+				break;
+			plugin = plugin->prev;
+		}
+		bitset_and(client_vmask, dstmask, plugin->src_format.channels);
+		return 0;
+	}
+}
+
+int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug,
+				       bitset_t *client_vmask)
+{
+	snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+	if (plugin == NULL) {
+		return 0;
+	} else {
+		int schannels = plugin->src_format.channels;
+		bitset_t bs[bitset_size(schannels)];
+		bitset_t *srcmask = bs;
+		bitset_t *dstmask;
+		int err;
+		bitset_one(srcmask, schannels);
+		while (1) {
+			err = plugin->dst_channels_mask(plugin, srcmask, &dstmask);
+			if (err < 0)
+				return err;
+			srcmask = dstmask;
+			if (plugin->next == NULL)
+				break;
+			plugin = plugin->next;
+		}
+		bitset_and(client_vmask, srcmask, plugin->dst_format.channels);
+		return 0;
+	}
+}
+
+static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug,
+							  snd_pcm_plugin_channel_t *src_channels)
+{
+	snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
+	unsigned int nchannels = plugin->src_format.channels;
+	bitset_t bs[bitset_size(nchannels)];
+	bitset_t *srcmask = bs;
+	int err;
+	unsigned int channel;
+	for (channel = 0; channel < nchannels; channel++) {
+		if (src_channels[channel].enabled)
+			bitset_set(srcmask, channel);
+		else
+			bitset_reset(srcmask, channel);
+	}
+	err = snd_pcm_plug_playback_channels_mask(plug, srcmask);
+	if (err < 0)
+		return err;
+	for (channel = 0; channel < nchannels; channel++) {
+		if (!bitset_get(srcmask, channel))
+			src_channels[channel].enabled = 0;
+	}
+	return 0;
+}
+
+static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug,
+							 snd_pcm_plugin_channel_t *src_channels,
+							 snd_pcm_plugin_channel_t *client_channels)
+{
+	snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
+	unsigned int nchannels = plugin->dst_format.channels;
+	bitset_t bs[bitset_size(nchannels)];
+	bitset_t *dstmask = bs;
+	bitset_t *srcmask;
+	int err;
+	unsigned int channel;
+	for (channel = 0; channel < nchannels; channel++) {
+		if (client_channels[channel].enabled)
+			bitset_set(dstmask, channel);
+		else
+			bitset_reset(dstmask, channel);
+	}
+	while (plugin) {
+		err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
+		if (err < 0)
+			return err;
+		dstmask = srcmask;
+		plugin = plugin->prev;
+	}
+	plugin = snd_pcm_plug_first(plug);
+	nchannels = plugin->src_format.channels;
+	for (channel = 0; channel < nchannels; channel++) {
+		if (!bitset_get(dstmask, channel))
+			src_channels[channel].enabled = 0;
+	}
+	return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size)
+{
+	snd_pcm_plugin_t *plugin, *next;
+	snd_pcm_plugin_channel_t *dst_channels;
+	int err;
+	snd_pcm_sframes_t frames = size;
+
+	if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0)
+		return err;
+	
+	plugin = snd_pcm_plug_first(plug);
+	while (plugin && frames > 0) {
+		if ((next = plugin->next) != NULL) {
+			snd_pcm_sframes_t frames1 = frames;
+			if (plugin->dst_frames)
+				frames1 = plugin->dst_frames(plugin, frames);
+			if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
+				return err;
+			}
+			if (err != frames1) {
+				frames = err;
+				if (plugin->src_frames)
+					frames = plugin->src_frames(plugin, frames1);
+			}
+		} else
+			dst_channels = 0;
+		pdprintf("write plugin: %s, %li\n", plugin->name, frames);
+		if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+			return frames;
+		src_channels = dst_channels;
+		plugin = next;
+	}
+	return snd_pcm_plug_client_size(plug, frames);
+}
+
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size)
+{
+	snd_pcm_plugin_t *plugin, *next;
+	snd_pcm_plugin_channel_t *src_channels, *dst_channels;
+	snd_pcm_sframes_t frames = size;
+	int err;
+
+	frames = snd_pcm_plug_slave_size(plug, frames);
+	if (frames < 0)
+		return frames;
+
+	src_channels = 0;
+	plugin = snd_pcm_plug_first(plug);
+	while (plugin && frames > 0) {
+		if ((next = plugin->next) != NULL) {
+			if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
+				return err;
+			}
+			frames = err;
+			if (!plugin->prev) {
+				if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final) < 0))
+					return err;
+			}
+		} else {
+			dst_channels = dst_channels_final;
+		}
+		pdprintf("read plugin: %s, %li\n", plugin->name, frames);
+		if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+			return frames;
+		plugin = next;
+		src_channels = dst_channels;
+	}
+	return frames;
+}
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
+			 size_t samples, int format)
+{
+	/* FIXME: sub byte resolution and odd dst_offset */
+	char *dst;
+	unsigned int dst_step;
+	int width;
+	u_int64_t silence;
+	if (!dst_area->addr)
+		return 0;
+	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
+	width = snd_pcm_format_physical_width(format);
+	silence = snd_pcm_format_silence_64(format);
+	if (dst_area->step == (unsigned int) width) {
+		size_t dwords = samples * width / 64;
+		samples -= dwords * 64 / width;
+		while (dwords-- > 0)
+			*((u_int64_t*)dst)++ = silence;
+		if (samples == 0)
+			return 0;
+	}
+	dst_step = dst_area->step / 8;
+	switch (width) {
+	case 4: {
+		u_int8_t s0 = silence & 0xf0;
+		u_int8_t s1 = silence & 0x0f;
+		int dstbit = dst_area->first % 8;
+		int dstbit_step = dst_area->step % 8;
+		while (samples-- > 0) {
+			if (dstbit) {
+				*dst &= 0xf0;
+				*dst |= s1;
+			} else {
+				*dst &= 0x0f;
+				*dst |= s0;
+			}
+			dst += dst_step;
+			dstbit += dstbit_step;
+			if (dstbit == 8) {
+				dst++;
+				dstbit = 0;
+			}
+		}
+		break;
+	}
+	case 8: {
+		u_int8_t sil = silence;
+		while (samples-- > 0) {
+			*dst = sil;
+			dst += dst_step;
+		}
+		break;
+	}
+	case 16: {
+		u_int16_t sil = silence;
+		while (samples-- > 0) {
+			*(u_int16_t*)dst = sil;
+			dst += dst_step;
+		}
+		break;
+	}
+	case 32: {
+		u_int32_t sil = silence;
+		while (samples-- > 0) {
+			*(u_int32_t*)dst = sil;
+			dst += dst_step;
+		}
+		break;
+	}
+	case 64: {
+		while (samples-- > 0) {
+			*(u_int64_t*)dst = silence;
+			dst += dst_step;
+		}
+		break;
+	}
+	default:
+		snd_BUG();
+	}
+	return 0;
+}
+
+int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
+			  unsigned int channels, snd_pcm_uframes_t frames, int format)
+{
+	int width = snd_pcm_format_physical_width(format);
+	while (channels > 0) {
+		void *addr = dst_areas->addr;
+		unsigned int step = dst_areas->step;
+		const snd_pcm_channel_area_t *begin = dst_areas;
+		int vc = channels;
+		unsigned int v = 0;
+		int err;
+		while (1) {
+			vc--;
+			v++;
+			dst_areas++;
+			if (vc == 0 ||
+			    dst_areas->addr != addr ||
+			    dst_areas->step != step ||
+			    dst_areas->first != dst_areas[-1].first + width)
+				break;
+		}
+		if (v > 1 && v * width == step) {
+			/* Collapse the areas */
+			snd_pcm_channel_area_t d;
+			d.addr = begin->addr;
+			d.first = begin->first;
+			d.step = width;
+			err = snd_pcm_area_silence(&d, dst_offset * v, frames * v, format);
+			channels -= v;
+		} else {
+			err = snd_pcm_area_silence(begin, dst_offset, frames, format);
+			dst_areas = begin + 1;
+			channels--;
+		}
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset,
+		      const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
+		      size_t samples, int format)
+{
+	/* FIXME: sub byte resolution and odd dst_offset */
+	char *src, *dst;
+	int width;
+	int src_step, dst_step;
+	src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
+	if (!src_area->addr)
+		return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
+	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
+	if (!dst_area->addr)
+		return 0;
+	width = snd_pcm_format_physical_width(format);
+	if (src_area->step == (unsigned int) width &&
+	    dst_area->step == (unsigned int) width) {
+		size_t bytes = samples * width / 8;
+		samples -= bytes * 8 / width;
+		memcpy(dst, src, bytes);
+		if (samples == 0)
+			return 0;
+	}
+	src_step = src_area->step / 8;
+	dst_step = dst_area->step / 8;
+	switch (width) {
+	case 4: {
+		int srcbit = src_area->first % 8;
+		int srcbit_step = src_area->step % 8;
+		int dstbit = dst_area->first % 8;
+		int dstbit_step = dst_area->step % 8;
+		while (samples-- > 0) {
+			unsigned char srcval;
+			if (srcbit)
+				srcval = *src & 0x0f;
+			else
+				srcval = *src & 0xf0;
+			if (dstbit)
+				*dst &= 0xf0;
+			else
+				*dst &= 0x0f;
+			*dst |= srcval;
+			src += src_step;
+			srcbit += srcbit_step;
+			if (srcbit == 8) {
+				src++;
+				srcbit = 0;
+			}
+			dst += dst_step;
+			dstbit += dstbit_step;
+			if (dstbit == 8) {
+				dst++;
+				dstbit = 0;
+			}
+		}
+		break;
+	}
+	case 8: {
+		while (samples-- > 0) {
+			*dst = *src;
+			src += src_step;
+			dst += dst_step;
+		}
+		break;
+	}
+	case 16: {
+		while (samples-- > 0) {
+			*(u_int16_t*)dst = *(u_int16_t*)src;
+			src += src_step;
+			dst += dst_step;
+		}
+		break;
+	}
+	case 32: {
+		while (samples-- > 0) {
+			*(u_int32_t*)dst = *(u_int32_t*)src;
+			src += src_step;
+			dst += dst_step;
+		}
+		break;
+	}
+	case 64: {
+		while (samples-- > 0) {
+			*(u_int64_t*)dst = *(u_int64_t*)src;
+			src += src_step;
+			dst += dst_step;
+		}
+		break;
+	}
+	default:
+		snd_BUG();
+	}
+	return 0;
+}
+
+int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
+		       const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
+		       unsigned int channels, snd_pcm_uframes_t frames, int format)
+{
+	int width = snd_pcm_format_physical_width(format);
+	while (channels > 0) {
+		unsigned int step = src_areas->step;
+		void *src_addr = src_areas->addr;
+		const snd_pcm_channel_area_t *src_start = src_areas;
+		void *dst_addr = dst_areas->addr;
+		const snd_pcm_channel_area_t *dst_start = dst_areas;
+		int vc = channels;
+		unsigned int v = 0;
+		while (dst_areas->step == step) {
+			vc--;
+			v++;
+			src_areas++;
+			dst_areas++;
+			if (vc == 0 ||
+			    src_areas->step != step ||
+			    src_areas->addr != src_addr ||
+			    dst_areas->addr != dst_addr ||
+			    src_areas->first != src_areas[-1].first + width ||
+			    dst_areas->first != dst_areas[-1].first + width)
+				break;
+		}
+		if (v > 1 && v * width == step) {
+			/* Collapse the areas */
+			snd_pcm_channel_area_t s, d;
+			s.addr = src_start->addr;
+			s.first = src_start->first;
+			s.step = width;
+			d.addr = dst_start->addr;
+			d.first = dst_start->first;
+			d.step = width;
+			snd_pcm_area_copy(&s, src_offset * v, &d, dst_offset * v, frames * v, format);
+			channels -= v;
+		} else {
+			snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format);
+			src_areas = src_start + 1;
+			dst_areas = dst_start + 1;
+			channels--;
+		}
+	}
+	return 0;
+}
diff -Nru linux/sound/core/oss/pcm_plugin.h linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.h
--- linux/sound/core/oss/pcm_plugin.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,257 @@
+#ifndef __PCM_PLUGIN_H
+#define __PCM_PLUGIN_H
+
+/*
+ *  Digital Audio (Plugin interface) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+typedef unsigned int bitset_t;
+
+static inline size_t bitset_size(int nbits)
+{
+	return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
+}
+
+static inline bitset_t *bitset_alloc(int nbits)
+{
+	return snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL);
+}
+	
+static inline void bitset_set(bitset_t *bitmap, unsigned int pos)
+{
+	size_t bits = sizeof(*bitmap) * 8;
+	bitmap[pos / bits] |= 1 << (pos % bits);
+}
+
+static inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
+{
+	size_t bits = sizeof(*bitmap) * 8;
+	bitmap[pos / bits] &= ~(1 << (pos % bits));
+}
+
+static inline int bitset_get(bitset_t *bitmap, unsigned int pos)
+{
+	size_t bits = sizeof(*bitmap) * 8;
+	return !!(bitmap[pos / bits] & (1 << (pos % bits)));
+}
+
+static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
+{
+	memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
+}
+
+static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ &= *bs++;
+}
+
+static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ |= *bs++;
+}
+
+static inline void bitset_zero(bitset_t *dst, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ = 0;
+}
+
+static inline void bitset_one(bitset_t *dst, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ = ~(bitset_t)0;
+}
+
+#define snd_pcm_plug_t snd_pcm_substream_t
+#define snd_pcm_plug_stream(plug) ((plug)->stream)
+
+typedef enum {
+	INIT = 0,
+	PREPARE = 1,
+} snd_pcm_plugin_action_t;
+
+typedef struct _snd_pcm_channel_area {
+	void *addr;			/* base address of channel samples */
+	unsigned int first;		/* offset to first sample in bits */
+	unsigned int step;		/* samples distance in bits */
+} snd_pcm_channel_area_t;
+
+typedef struct _snd_pcm_plugin_channel {
+	void *aptr;			/* pointer to the allocated area */
+	snd_pcm_channel_area_t area;
+	unsigned int enabled:1;		/* channel need to be processed */
+	unsigned int wanted:1;		/* channel is wanted */
+} snd_pcm_plugin_channel_t;
+
+typedef struct _snd_pcm_plugin_format {
+	int format;
+	unsigned int rate;
+	unsigned int channels;
+} snd_pcm_plugin_format_t;
+
+struct _snd_pcm_plugin {
+	const char *name;		/* plug-in name */
+	int stream;
+	snd_pcm_plugin_format_t src_format;	/* source format */
+	snd_pcm_plugin_format_t dst_format;	/* destination format */
+	int src_width;			/* sample width in bits */
+	int dst_width;			/* sample width in bits */
+	int access;
+	snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames);
+	snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames);
+	snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin,
+				 snd_pcm_uframes_t frames,
+				 snd_pcm_plugin_channel_t **channels);
+	int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
+			       bitset_t *dst_vmask,
+			       bitset_t **src_vmask);
+	int (*dst_channels_mask)(snd_pcm_plugin_t *plugin,
+			       bitset_t *src_vmask,
+			       bitset_t **dst_vmask);
+	snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin,
+			    const snd_pcm_plugin_channel_t *src_channels,
+			    snd_pcm_plugin_channel_t *dst_channels,
+			    snd_pcm_uframes_t frames);
+	int (*action)(snd_pcm_plugin_t *plugin,
+		      snd_pcm_plugin_action_t action,
+		      unsigned long data);
+	snd_pcm_plugin_t *prev;
+	snd_pcm_plugin_t *next;
+	snd_pcm_plug_t *plug;
+	void *private_data;
+	void (*private_free)(snd_pcm_plugin_t *plugin);
+	char *buf;
+	snd_pcm_uframes_t buf_frames;
+	snd_pcm_plugin_channel_t *buf_channels;
+	bitset_t *src_vmask;
+	bitset_t *dst_vmask;
+	char extra_data[0];
+};
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *handle,
+                         const char *name,
+                         snd_pcm_plugin_format_t *src_format,
+                         snd_pcm_plugin_format_t *dst_format,
+                         size_t extra,
+                         snd_pcm_plugin_t **ret);
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
+int snd_pcm_plugin_clear(snd_pcm_plugin_t **first);
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size);
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size);
+
+#define ROUTE_PLUGIN_USE_FLOAT 0
+#define FULL ROUTE_PLUGIN_RESOLUTION
+#define HALF ROUTE_PLUGIN_RESOLUTION / 2
+typedef int route_ttable_entry_t;
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle,
+			    snd_pcm_hw_params_t *params,
+			    snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle,
+				snd_pcm_plugin_format_t *src_format,
+				snd_pcm_plugin_format_t *dst_format,
+				snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle,
+			       snd_pcm_plugin_format_t *src_format,
+			       snd_pcm_plugin_format_t *dst_format,
+			       snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle,
+			      snd_pcm_plugin_format_t *src_format,
+			      snd_pcm_plugin_format_t *dst_format,
+			      snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle,
+			       snd_pcm_plugin_format_t *src_format,
+			       snd_pcm_plugin_format_t *dst_format,
+			       route_ttable_entry_t *ttable,
+		               snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle,
+			      snd_pcm_plugin_format_t *src_format,
+			      snd_pcm_plugin_format_t *dst_format,
+			      snd_pcm_plugin_t **r_plugin);
+
+unsigned int snd_pcm_plug_formats(unsigned int formats);
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream,
+				snd_pcm_hw_params_t *params,
+				snd_pcm_hw_params_t *slave_params);
+
+int snd_pcm_plug_slave_format(int format, unsigned int format_mask);
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size);
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size);
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle,
+					 char *buf, snd_pcm_uframes_t count,
+					 snd_pcm_plugin_channel_t **channels);
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+				       snd_pcm_uframes_t frames,
+				       snd_pcm_plugin_channel_t **channels);
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+			 size_t samples, int format);
+int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset,
+			  unsigned int channels, snd_pcm_uframes_t frames, int format);
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
+		      const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+		      size_t samples, int format);
+int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset,
+		       const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset,
+		       unsigned int channels, snd_pcm_uframes_t frames, int format);
+
+void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size);
+void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr);
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+
+
+
+#define ROUTE_PLUGIN_RESOLUTION 16
+
+int getput_index(int format);
+int copy_index(int format);
+int conv_index(int src_format, int dst_format);
+
+void zero_channel(snd_pcm_plugin_t *plugin,
+		  const snd_pcm_plugin_channel_t *dst_channel,
+		  size_t samples);
+
+#ifdef PLUGIN_DEBUG
+#define pdprintf( args... ) printk( "plugin: " ##args)
+#else
+#define pdprintf( args... ) { ; }
+#endif
+
+#endif				/* __PCM_PLUGIN_H */
diff -Nru linux/sound/core/oss/plugin_ops.h linux-2.4.19-pre5-mjc/sound/core/oss/plugin_ops.h
--- linux/sound/core/oss/plugin_ops.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/plugin_ops.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,536 @@
+/*
+ *  Plugin sample operators with fast switch
+ *  Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+
+#define as_u8(ptr) (*(u_int8_t*)(ptr))
+#define as_u16(ptr) (*(u_int16_t*)(ptr))
+#define as_u32(ptr) (*(u_int32_t*)(ptr))
+#define as_u64(ptr) (*(u_int64_t*)(ptr))
+#define as_s8(ptr) (*(int8_t*)(ptr))
+#define as_s16(ptr) (*(int16_t*)(ptr))
+#define as_s32(ptr) (*(int32_t*)(ptr))
+#define as_s64(ptr) (*(int64_t*)(ptr))
+
+#ifdef COPY_LABELS
+static void *copy_labels[4] = {
+	&&copy_8,
+	&&copy_16,
+	&&copy_32,
+	&&copy_64
+};
+#endif
+
+#ifdef COPY_END
+while(0) {
+copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
+copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
+copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
+copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
+}
+#endif
+
+#ifdef CONV_LABELS
+/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
+static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
+	&&conv_xxx1_xxx1,	 /*  8h ->  8h */
+	&&conv_xxx1_xxx1,	 /*  8h ->  8s */
+	&&conv_xxx1_xx10,	 /*  8h -> 16h */
+	&&conv_xxx1_xx01,	 /*  8h -> 16s */
+	&&conv_xxx1_x100,	 /*  8h -> 24h */
+	&&conv_xxx1_001x,	 /*  8h -> 24s */
+	&&conv_xxx1_1000,	 /*  8h -> 32h */
+	&&conv_xxx1_0001,	 /*  8h -> 32s */
+	&&conv_xxx1_xxx9,	 /*  8h ^>  8h */
+	&&conv_xxx1_xxx9,	 /*  8h ^>  8s */
+	&&conv_xxx1_xx90,	 /*  8h ^> 16h */
+	&&conv_xxx1_xx09,	 /*  8h ^> 16s */
+	&&conv_xxx1_x900,	 /*  8h ^> 24h */
+	&&conv_xxx1_009x,	 /*  8h ^> 24s */
+	&&conv_xxx1_9000,	 /*  8h ^> 32h */
+	&&conv_xxx1_0009,	 /*  8h ^> 32s */
+	&&conv_xxx1_xxx1,	 /*  8s ->  8h */
+	&&conv_xxx1_xxx1,	 /*  8s ->  8s */
+	&&conv_xxx1_xx10,	 /*  8s -> 16h */
+	&&conv_xxx1_xx01,	 /*  8s -> 16s */
+	&&conv_xxx1_x100,	 /*  8s -> 24h */
+	&&conv_xxx1_001x,	 /*  8s -> 24s */
+	&&conv_xxx1_1000,	 /*  8s -> 32h */
+	&&conv_xxx1_0001,	 /*  8s -> 32s */
+	&&conv_xxx1_xxx9,	 /*  8s ^>  8h */
+	&&conv_xxx1_xxx9,	 /*  8s ^>  8s */
+	&&conv_xxx1_xx90,	 /*  8s ^> 16h */
+	&&conv_xxx1_xx09,	 /*  8s ^> 16s */
+	&&conv_xxx1_x900,	 /*  8s ^> 24h */
+	&&conv_xxx1_009x,	 /*  8s ^> 24s */
+	&&conv_xxx1_9000,	 /*  8s ^> 32h */
+	&&conv_xxx1_0009,	 /*  8s ^> 32s */
+	&&conv_xx12_xxx1,	 /* 16h ->  8h */
+	&&conv_xx12_xxx1,	 /* 16h ->  8s */
+	&&conv_xx12_xx12,	 /* 16h -> 16h */
+	&&conv_xx12_xx21,	 /* 16h -> 16s */
+	&&conv_xx12_x120,	 /* 16h -> 24h */
+	&&conv_xx12_021x,	 /* 16h -> 24s */
+	&&conv_xx12_1200,	 /* 16h -> 32h */
+	&&conv_xx12_0021,	 /* 16h -> 32s */
+	&&conv_xx12_xxx9,	 /* 16h ^>  8h */
+	&&conv_xx12_xxx9,	 /* 16h ^>  8s */
+	&&conv_xx12_xx92,	 /* 16h ^> 16h */
+	&&conv_xx12_xx29,	 /* 16h ^> 16s */
+	&&conv_xx12_x920,	 /* 16h ^> 24h */
+	&&conv_xx12_029x,	 /* 16h ^> 24s */
+	&&conv_xx12_9200,	 /* 16h ^> 32h */
+	&&conv_xx12_0029,	 /* 16h ^> 32s */
+	&&conv_xx12_xxx2,	 /* 16s ->  8h */
+	&&conv_xx12_xxx2,	 /* 16s ->  8s */
+	&&conv_xx12_xx21,	 /* 16s -> 16h */
+	&&conv_xx12_xx12,	 /* 16s -> 16s */
+	&&conv_xx12_x210,	 /* 16s -> 24h */
+	&&conv_xx12_012x,	 /* 16s -> 24s */
+	&&conv_xx12_2100,	 /* 16s -> 32h */
+	&&conv_xx12_0012,	 /* 16s -> 32s */
+	&&conv_xx12_xxxA,	 /* 16s ^>  8h */
+	&&conv_xx12_xxxA,	 /* 16s ^>  8s */
+	&&conv_xx12_xxA1,	 /* 16s ^> 16h */
+	&&conv_xx12_xx1A,	 /* 16s ^> 16s */
+	&&conv_xx12_xA10,	 /* 16s ^> 24h */
+	&&conv_xx12_01Ax,	 /* 16s ^> 24s */
+	&&conv_xx12_A100,	 /* 16s ^> 32h */
+	&&conv_xx12_001A,	 /* 16s ^> 32s */
+	&&conv_x123_xxx1,	 /* 24h ->  8h */
+	&&conv_x123_xxx1,	 /* 24h ->  8s */
+	&&conv_x123_xx12,	 /* 24h -> 16h */
+	&&conv_x123_xx21,	 /* 24h -> 16s */
+	&&conv_x123_x123,	 /* 24h -> 24h */
+	&&conv_x123_321x,	 /* 24h -> 24s */
+	&&conv_x123_1230,	 /* 24h -> 32h */
+	&&conv_x123_0321,	 /* 24h -> 32s */
+	&&conv_x123_xxx9,	 /* 24h ^>  8h */
+	&&conv_x123_xxx9,	 /* 24h ^>  8s */
+	&&conv_x123_xx92,	 /* 24h ^> 16h */
+	&&conv_x123_xx29,	 /* 24h ^> 16s */
+	&&conv_x123_x923,	 /* 24h ^> 24h */
+	&&conv_x123_329x,	 /* 24h ^> 24s */
+	&&conv_x123_9230,	 /* 24h ^> 32h */
+	&&conv_x123_0329,	 /* 24h ^> 32s */
+	&&conv_123x_xxx3,	 /* 24s ->  8h */
+	&&conv_123x_xxx3,	 /* 24s ->  8s */
+	&&conv_123x_xx32,	 /* 24s -> 16h */
+	&&conv_123x_xx23,	 /* 24s -> 16s */
+	&&conv_123x_x321,	 /* 24s -> 24h */
+	&&conv_123x_123x,	 /* 24s -> 24s */
+	&&conv_123x_3210,	 /* 24s -> 32h */
+	&&conv_123x_0123,	 /* 24s -> 32s */
+	&&conv_123x_xxxB,	 /* 24s ^>  8h */
+	&&conv_123x_xxxB,	 /* 24s ^>  8s */
+	&&conv_123x_xxB2,	 /* 24s ^> 16h */
+	&&conv_123x_xx2B,	 /* 24s ^> 16s */
+	&&conv_123x_xB21,	 /* 24s ^> 24h */
+	&&conv_123x_12Bx,	 /* 24s ^> 24s */
+	&&conv_123x_B210,	 /* 24s ^> 32h */
+	&&conv_123x_012B,	 /* 24s ^> 32s */
+	&&conv_1234_xxx1,	 /* 32h ->  8h */
+	&&conv_1234_xxx1,	 /* 32h ->  8s */
+	&&conv_1234_xx12,	 /* 32h -> 16h */
+	&&conv_1234_xx21,	 /* 32h -> 16s */
+	&&conv_1234_x123,	 /* 32h -> 24h */
+	&&conv_1234_321x,	 /* 32h -> 24s */
+	&&conv_1234_1234,	 /* 32h -> 32h */
+	&&conv_1234_4321,	 /* 32h -> 32s */
+	&&conv_1234_xxx9,	 /* 32h ^>  8h */
+	&&conv_1234_xxx9,	 /* 32h ^>  8s */
+	&&conv_1234_xx92,	 /* 32h ^> 16h */
+	&&conv_1234_xx29,	 /* 32h ^> 16s */
+	&&conv_1234_x923,	 /* 32h ^> 24h */
+	&&conv_1234_329x,	 /* 32h ^> 24s */
+	&&conv_1234_9234,	 /* 32h ^> 32h */
+	&&conv_1234_4329,	 /* 32h ^> 32s */
+	&&conv_1234_xxx4,	 /* 32s ->  8h */
+	&&conv_1234_xxx4,	 /* 32s ->  8s */
+	&&conv_1234_xx43,	 /* 32s -> 16h */
+	&&conv_1234_xx34,	 /* 32s -> 16s */
+	&&conv_1234_x432,	 /* 32s -> 24h */
+	&&conv_1234_234x,	 /* 32s -> 24s */
+	&&conv_1234_4321,	 /* 32s -> 32h */
+	&&conv_1234_1234,	 /* 32s -> 32s */
+	&&conv_1234_xxxC,	 /* 32s ^>  8h */
+	&&conv_1234_xxxC,	 /* 32s ^>  8s */
+	&&conv_1234_xxC3,	 /* 32s ^> 16h */
+	&&conv_1234_xx3C,	 /* 32s ^> 16s */
+	&&conv_1234_xC32,	 /* 32s ^> 24h */
+	&&conv_1234_23Cx,	 /* 32s ^> 24s */
+	&&conv_1234_C321,	 /* 32s ^> 32h */
+	&&conv_1234_123C,	 /* 32s ^> 32s */
+};
+#endif
+
+#ifdef CONV_END
+while(0) {
+conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
+conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
+conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
+conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
+conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
+conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
+conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
+conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
+conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
+conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
+conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END;
+conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
+conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END;
+conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
+conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
+conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END;
+conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
+conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
+conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END;
+conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
+conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END;
+conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; 
+conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
+conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END;
+conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
+conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
+conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
+conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
+conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
+conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
+conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
+conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END;
+conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
+conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
+conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
+conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
+conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
+conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
+conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
+conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
+conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
+conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END;
+conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
+conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
+conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
+conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END;
+conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
+conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
+conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
+conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
+conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
+conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
+conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END;
+conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
+conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END;
+conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
+conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
+conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
+conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
+conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
+conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
+}
+#endif
+
+#ifdef GET_S16_LABELS
+/* src_wid src_endswap unsigned */
+static void *get_s16_labels[4 * 2 * 2] = {
+	&&get_s16_xxx1_xx10,	 /*  8h -> 16h */
+	&&get_s16_xxx1_xx90,	 /*  8h ^> 16h */
+	&&get_s16_xxx1_xx10,	 /*  8s -> 16h */
+	&&get_s16_xxx1_xx90,	 /*  8s ^> 16h */
+	&&get_s16_xx12_xx12,	 /* 16h -> 16h */
+	&&get_s16_xx12_xx92,	 /* 16h ^> 16h */
+	&&get_s16_xx12_xx21,	 /* 16s -> 16h */
+	&&get_s16_xx12_xxA1,	 /* 16s ^> 16h */
+	&&get_s16_x123_xx12,	 /* 24h -> 16h */
+	&&get_s16_x123_xx92,	 /* 24h ^> 16h */
+	&&get_s16_123x_xx32,	 /* 24s -> 16h */
+	&&get_s16_123x_xxB2,	 /* 24s ^> 16h */
+	&&get_s16_1234_xx12,	 /* 32h -> 16h */
+	&&get_s16_1234_xx92,	 /* 32h ^> 16h */
+	&&get_s16_1234_xx43,	 /* 32s -> 16h */
+	&&get_s16_1234_xxC3,	 /* 32s ^> 16h */
+};
+#endif
+
+#ifdef GET_S16_END
+while(0) {
+get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END;
+get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END;
+get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END;
+get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END;
+get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END;
+get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END;
+get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END;
+get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END;
+get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END;
+get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END;
+get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END;
+get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END;
+get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END;
+get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END;
+}
+#endif
+
+#ifdef PUT_S16_LABELS
+/* dst_wid dst_endswap unsigned */
+static void *put_s16_labels[4 * 2 * 2 * 4 * 2] = {
+	&&put_s16_xx12_xxx1,	 /* 16h ->  8h */
+	&&put_s16_xx12_xxx9,	 /* 16h ^>  8h */
+	&&put_s16_xx12_xxx1,	 /* 16h ->  8s */
+	&&put_s16_xx12_xxx9,	 /* 16h ^>  8s */
+	&&put_s16_xx12_xx12,	 /* 16h -> 16h */
+	&&put_s16_xx12_xx92,	 /* 16h ^> 16h */
+	&&put_s16_xx12_xx21,	 /* 16h -> 16s */
+	&&put_s16_xx12_xx29,	 /* 16h ^> 16s */
+	&&put_s16_xx12_x120,	 /* 16h -> 24h */
+	&&put_s16_xx12_x920,	 /* 16h ^> 24h */
+	&&put_s16_xx12_021x,	 /* 16h -> 24s */
+	&&put_s16_xx12_029x,	 /* 16h ^> 24s */
+	&&put_s16_xx12_1200,	 /* 16h -> 32h */
+	&&put_s16_xx12_9200,	 /* 16h ^> 32h */
+	&&put_s16_xx12_0021,	 /* 16h -> 32s */
+	&&put_s16_xx12_0029,	 /* 16h ^> 32s */
+};
+#endif
+
+#ifdef PUT_S16_END
+while (0) {
+put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END;
+put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END;
+put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END;
+put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END;
+put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END;
+put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END;
+put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END;
+put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END;
+put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END;
+put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END;
+put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END;
+put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END;
+put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END;
+put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END;
+}
+#endif
+
+#if 0
+#ifdef GET32_LABELS
+/* src_wid src_endswap unsigned */
+static void *get32_labels[4 * 2 * 2] = {
+	&&get32_xxx1_1000,	 /*  8h -> 32h */
+	&&get32_xxx1_9000,	 /*  8h ^> 32h */
+	&&get32_xxx1_1000,	 /*  8s -> 32h */
+	&&get32_xxx1_9000,	 /*  8s ^> 32h */
+	&&get32_xx12_1200,	 /* 16h -> 32h */
+	&&get32_xx12_9200,	 /* 16h ^> 32h */
+	&&get32_xx12_2100,	 /* 16s -> 32h */
+	&&get32_xx12_A100,	 /* 16s ^> 32h */
+	&&get32_x123_1230,	 /* 24h -> 32h */
+	&&get32_x123_9230,	 /* 24h ^> 32h */
+	&&get32_123x_3210,	 /* 24s -> 32h */
+	&&get32_123x_B210,	 /* 24s ^> 32h */
+	&&get32_1234_1234,	 /* 32h -> 32h */
+	&&get32_1234_9234,	 /* 32h ^> 32h */
+	&&get32_1234_4321,	 /* 32s -> 32h */
+	&&get32_1234_C321,	 /* 32s ^> 32h */
+};
+#endif
+
+#ifdef GET32_END
+while (0) {
+get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END;
+get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END;
+get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END;
+get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END;
+get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END;
+get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END;
+get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END;
+get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END;
+get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END;
+get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END;
+get32_1234_1234: sample = as_u32(src); goto GET32_END;
+get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END;
+get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END;
+get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END;
+}
+#endif
+#endif
+
+#ifdef PUT_U32_LABELS
+/* dst_wid dst_endswap unsigned */
+static void *put_u32_labels[4 * 2 * 2] = {
+	&&put_u32_1234_xxx9,	 /* u32h ->  s8h */
+	&&put_u32_1234_xxx1,	 /* u32h ->  u8h */
+	&&put_u32_1234_xxx9,	 /* u32h ->  s8s */
+	&&put_u32_1234_xxx1,	 /* u32h ->  u8s */
+	&&put_u32_1234_xx92,	 /* u32h -> s16h */
+	&&put_u32_1234_xx12,	 /* u32h -> u16h */
+	&&put_u32_1234_xx29,	 /* u32h -> s16s */
+	&&put_u32_1234_xx21,	 /* u32h -> u16s */
+	&&put_u32_1234_x923,	 /* u32h -> s24h */
+	&&put_u32_1234_x123,	 /* u32h -> u24h */
+	&&put_u32_1234_329x,	 /* u32h -> s24s */
+	&&put_u32_1234_321x,	 /* u32h -> u24s */
+	&&put_u32_1234_9234,	 /* u32h -> s32h */
+	&&put_u32_1234_1234,	 /* u32h -> u32h */
+	&&put_u32_1234_4329,	 /* u32h -> s32s */
+	&&put_u32_1234_4321,	 /* u32h -> u32s */
+};
+#endif
+
+#ifdef PUT_U32_END
+while (0) {
+put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END;
+put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END;
+put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END;
+put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END;
+put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END;
+put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END;
+put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END;
+put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END;
+put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END;
+put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END;
+put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END;
+put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END;
+put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END;
+put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END;
+}
+#endif
+
+#ifdef GET_U_LABELS
+/* width endswap unsigned*/
+static void *get_u_labels[4 * 2 * 2] = {
+	&&get_u_s8,	/* s8  ->  u8  */
+	&&get_u_u8,	/* u8  ->  u8  */
+	&&get_u_s8,	/* s8  ->  u8  */
+	&&get_u_u8,	/* u8  ->  u8  */
+	&&get_u_s16h,	/* s16h -> u16h */
+	&&get_u_u16h,	/* u16h -> u16h */
+	&&get_u_s16s,	/* s16s -> u16h */
+	&&get_u_u16s,	/* u16s -> u16h */
+	&&get_u_s24h,	/* s24h -> u32h */
+	&&get_u_u24h,	/* u24h -> u32h */
+	&&get_u_s24s,	/* s24s -> u32h */
+	&&get_u_u24s,	/* u24s -> u32h */
+	&&get_u_s32h,	/* s32h -> u32h */
+	&&get_u_u32h,	/* u32h -> u32h */
+	&&get_u_s32s,	/* s32s -> u32h */
+	&&get_u_u32s,	/* u32s -> u32h */
+};
+#endif
+
+#ifdef GET_U_END
+while (0) {
+get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END;
+get_u_u8: sample = as_u8(src); goto GET_U_END;
+get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END;
+get_u_u16h: sample = as_u16(src); goto GET_U_END;
+get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END;
+get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END;
+get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END;
+get_u_u24h: sample = as_u32(src); goto GET_U_END;
+get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END;
+get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END;
+get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END;
+get_u_u32h: sample = as_u32(src); goto GET_U_END;
+get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END;
+get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END;
+}
+#endif
+
+#if 0
+#ifdef PUT_LABELS
+/* width endswap unsigned */
+static void *put_labels[4 * 2 * 2] = {
+	&&put_s8,	/* s8  ->  s8  */
+	&&put_u8,	/* u8  ->  s8  */
+	&&put_s8,	/* s8  ->  s8  */
+	&&put_u8,	/* u8  ->  s8  */
+	&&put_s16h,	/* s16h -> s16h */
+	&&put_u16h,	/* u16h -> s16h */
+	&&put_s16s,	/* s16s -> s16h */
+	&&put_u16s,	/* u16s -> s16h */
+	&&put_s24h,	/* s24h -> s32h */
+	&&put_u24h,	/* u24h -> s32h */
+	&&put_s24s,	/* s24s -> s32h */
+	&&put_u24s,	/* u24s -> s32h */
+	&&put_s32h,	/* s32h -> s32h */
+	&&put_u32h,	/* u32h -> s32h */
+	&&put_s32s,	/* s32s -> s32h */
+	&&put_u32s,	/* u32s -> s32h */
+};
+#endif
+
+#ifdef PUT_END
+put_s8: as_s8(dst) = sample; goto PUT_END;
+put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END;
+put_s16h: as_s16(dst) = sample; goto PUT_END;
+put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END;
+put_s16s: as_s16(dst) = swab16(sample); goto PUT_END;
+put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END;
+put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END;
+put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END;
+put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END;
+put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END;
+put_s32h: as_s32(dst) = sample; goto PUT_END;
+put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END;
+put_s32s: as_s32(dst) = swab32(sample); goto PUT_END;
+put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END;
+#endif
+#endif
+
+#undef as_u8
+#undef as_u16
+#undef as_u32
+#undef as_s8
+#undef as_s16
+#undef as_s32
diff -Nru linux/sound/core/oss/rate.c linux-2.4.19-pre5-mjc/sound/core/oss/rate.c
--- linux/sound/core/oss/rate.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/rate.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,387 @@
+/*
+ *  Rate conversion Plug-In
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+  
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+#define SHIFT	11
+#define BITS	(1<<SHIFT)
+#define R_MASK	(BITS-1)
+
+/*
+ *  Basic rate conversion plugin
+ */
+
+typedef struct {
+	signed short last_S1;
+	signed short last_S2;
+} rate_channel_t;
+ 
+typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
+		       const snd_pcm_plugin_channel_t *src_channels,
+		       snd_pcm_plugin_channel_t *dst_channels,
+		       int src_frames, int dst_frames);
+
+typedef struct rate_private_data {
+	unsigned int pitch;
+	unsigned int pos;
+	rate_f func;
+	int get, put;
+	snd_pcm_sframes_t old_src_frames, old_dst_frames;
+	rate_channel_t channels[0];
+} rate_t;
+
+static void rate_init(snd_pcm_plugin_t *plugin)
+{
+	unsigned int channel;
+	rate_t *data = (rate_t *)plugin->extra_data;
+	data->pos = 0;
+	for (channel = 0; channel < plugin->src_format.channels; channel++) {
+		data->channels[channel].last_S1 = 0;
+		data->channels[channel].last_S2 = 0;
+	}
+}
+
+static void resample_expand(snd_pcm_plugin_t *plugin,
+			    const snd_pcm_plugin_channel_t *src_channels,
+			    snd_pcm_plugin_channel_t *dst_channels,
+			    int src_frames, int dst_frames)
+{
+	unsigned int pos = 0;
+	signed int val;
+	signed short S1, S2;
+	char *src, *dst;
+	unsigned int channel;
+	int src_step, dst_step;
+	int src_frames1, dst_frames1;
+	rate_t *data = (rate_t *)plugin->extra_data;
+	rate_channel_t *rchannels = data->channels;
+
+#define GET_S16_LABELS
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+#undef PUT_S16_LABELS
+	void *get = get_s16_labels[data->get];
+	void *put = put_s16_labels[data->put];
+	void *get_s16_end = 0;
+	signed short sample = 0;
+#define GET_S16_END *get_s16_end
+#include "plugin_ops.h"
+#undef GET_S16_END
+	
+	for (channel = 0; channel < plugin->src_format.channels; channel++) {
+		pos = data->pos;
+		S1 = rchannels->last_S1;
+		S2 = rchannels->last_S2;
+		if (!src_channels[channel].enabled) {
+			if (dst_channels[channel].wanted)
+				snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
+			dst_channels[channel].enabled = 0;
+			continue;
+		}
+		dst_channels[channel].enabled = 1;
+		src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+		dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+		src_step = src_channels[channel].area.step / 8;
+		dst_step = dst_channels[channel].area.step / 8;
+		src_frames1 = src_frames;
+		dst_frames1 = dst_frames;
+		if (pos & ~R_MASK) {
+			get_s16_end = &&after_get1;
+			goto *get;
+		after_get1:
+			pos &= R_MASK;
+			S1 = S2;
+			S2 = sample;
+			src += src_step;
+			src_frames1--;
+		}
+		while (dst_frames1-- > 0) {
+			if (pos & ~R_MASK) {
+				pos &= R_MASK;
+				S1 = S2;
+				if (src_frames1-- > 0) {
+					get_s16_end = &&after_get2;
+					goto *get;
+				after_get2:
+					S2 = sample;
+					src += src_step;
+				}
+			}
+			val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+			if (val < -32768)
+				val = -32768;
+			else if (val > 32767)
+				val = 32767;
+			sample = val;
+			goto *put;
+#define PUT_S16_END after_put
+#include "plugin_ops.h"
+#undef PUT_S16_END
+		after_put:
+			dst += dst_step;
+			pos += data->pitch;
+		}
+		rchannels->last_S1 = S1;
+		rchannels->last_S2 = S2;
+		rchannels++;
+	}
+	data->pos = pos;
+}
+
+static void resample_shrink(snd_pcm_plugin_t *plugin,
+			    const snd_pcm_plugin_channel_t *src_channels,
+			    snd_pcm_plugin_channel_t *dst_channels,
+			    int src_frames, int dst_frames)
+{
+	unsigned int pos = 0;
+	signed int val;
+	signed short S1, S2;
+	char *src, *dst;
+	unsigned int channel;
+	int src_step, dst_step;
+	int src_frames1, dst_frames1;
+	rate_t *data = (rate_t *)plugin->extra_data;
+	rate_channel_t *rchannels = data->channels;
+	
+#define GET_S16_LABELS
+#define PUT_S16_LABELS
+#include "plugin_ops.h"
+#undef GET_S16_LABELS
+#undef PUT_S16_LABELS
+	void *get = get_s16_labels[data->get];
+	void *put = put_s16_labels[data->put];
+	signed short sample = 0;
+
+	for (channel = 0; channel < plugin->src_format.channels; ++channel) {
+		pos = data->pos;
+		S1 = rchannels->last_S1;
+		S2 = rchannels->last_S2;
+		if (!src_channels[channel].enabled) {
+			if (dst_channels[channel].wanted)
+				snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
+			dst_channels[channel].enabled = 0;
+			continue;
+		}
+		dst_channels[channel].enabled = 1;
+		src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
+		dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
+		src_step = src_channels[channel].area.step / 8;
+		dst_step = dst_channels[channel].area.step / 8;
+		src_frames1 = src_frames;
+		dst_frames1 = dst_frames;
+		while (dst_frames1 > 0) {
+			S1 = S2;
+			if (src_frames1-- > 0) {
+				goto *get;
+#define GET_S16_END after_get
+#include "plugin_ops.h"
+#undef GET_S16_END
+			after_get:
+				S2 = sample;
+				src += src_step;
+			}
+			if (pos & ~R_MASK) {
+				pos &= R_MASK;
+				val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+				if (val < -32768)
+					val = -32768;
+				else if (val > 32767)
+					val = 32767;
+				sample = val;
+				goto *put;
+#define PUT_S16_END after_put
+#include "plugin_ops.h"
+#undef PUT_S16_END
+			after_put:
+				dst += dst_step;
+				dst_frames1--;
+			}
+			pos += data->pitch;
+		}
+		rchannels->last_S1 = S1;
+		rchannels->last_S2 = S2;
+		rchannels++;
+	}
+	data->pos = pos;
+}
+
+static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+	rate_t *data;
+	snd_pcm_sframes_t res;
+
+	snd_assert(plugin != NULL, return -ENXIO);
+	if (frames == 0)
+		return 0;
+	data = (rate_t *)plugin->extra_data;
+	if (plugin->src_format.rate < plugin->dst_format.rate) {
+		res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
+	} else {
+		res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);		
+	}
+	if (data->old_src_frames > 0) {
+		snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
+		while (data->old_src_frames < frames1) {
+			frames1 >>= 1;
+			res1 <<= 1;
+		}
+		while (data->old_src_frames > frames1) {
+			frames1 <<= 1;
+			res1 >>= 1;
+		}
+		if (data->old_src_frames == frames1)
+			return res1;
+	}
+	data->old_src_frames = frames;
+	data->old_dst_frames = res;
+	return res;
+}
+
+static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
+{
+	rate_t *data;
+	snd_pcm_sframes_t res;
+
+	snd_assert(plugin != NULL, return -ENXIO);
+	if (frames == 0)
+		return 0;
+	data = (rate_t *)plugin->extra_data;
+	if (plugin->src_format.rate < plugin->dst_format.rate) {
+		res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+	} else {
+		res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
+	}
+	if (data->old_dst_frames > 0) {
+		snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames;
+		while (data->old_dst_frames < frames1) {
+			frames1 >>= 1;
+			res1 <<= 1;
+		}
+		while (data->old_dst_frames > frames1) {
+			frames1 <<= 1;
+			res1 >>= 1;
+		}
+		if (data->old_dst_frames == frames1)
+			return res1;
+	}
+	data->old_dst_frames = frames;
+	data->old_src_frames = res;
+	return res;
+}
+
+static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin,
+			     const snd_pcm_plugin_channel_t *src_channels,
+			     snd_pcm_plugin_channel_t *dst_channels,
+			     snd_pcm_uframes_t frames)
+{
+	snd_pcm_uframes_t dst_frames;
+	rate_t *data;
+
+	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (frames == 0)
+		return 0;
+#ifdef CONFIG_SND_DEBUG
+	{
+		unsigned int channel;
+		for (channel = 0; channel < plugin->src_format.channels; channel++) {
+			snd_assert(src_channels[channel].area.first % 8 == 0 &&
+				   src_channels[channel].area.step % 8 == 0,
+				   return -ENXIO);
+			snd_assert(dst_channels[channel].area.first % 8 == 0 &&
+				   dst_channels[channel].area.step % 8 == 0,
+				   return -ENXIO);
+		}
+	}
+#endif
+
+	dst_frames = rate_dst_frames(plugin, frames);
+	data = (rate_t *)plugin->extra_data;
+	data->func(plugin, src_channels, dst_channels, frames, dst_frames);
+	return dst_frames;
+}
+
+static int rate_action(snd_pcm_plugin_t *plugin,
+		       snd_pcm_plugin_action_t action,
+		       unsigned long udata ATTRIBUTE_UNUSED)
+{
+	snd_assert(plugin != NULL, return -ENXIO);
+	switch (action) {
+	case INIT:
+	case PREPARE:
+		rate_init(plugin);
+		break;
+	default:
+		break;
+	}
+	return 0;	/* silenty ignore other actions */
+}
+
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug,
+			      snd_pcm_plugin_format_t *src_format,
+			      snd_pcm_plugin_format_t *dst_format,
+			      snd_pcm_plugin_t **r_plugin)
+{
+	int err;
+	rate_t *data;
+	snd_pcm_plugin_t *plugin;
+
+	snd_assert(r_plugin != NULL, return -ENXIO);
+	*r_plugin = NULL;
+
+	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+	snd_assert(src_format->channels > 0, return -ENXIO);
+	snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO);
+	snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO);
+	snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
+
+	err = snd_pcm_plugin_build(plug, "rate conversion",
+				   src_format, dst_format,
+				   sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t),
+				   &plugin);
+	if (err < 0)
+		return err;
+	data = (rate_t *)plugin->extra_data;
+	data->get = getput_index(src_format->format);
+	data->put = getput_index(dst_format->format);
+
+	if (src_format->rate < dst_format->rate) {
+		data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
+		data->func = resample_expand;
+	} else {
+		data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
+		data->func = resample_shrink;
+	}
+	data->pos = 0;
+	rate_init(plugin);
+	data->old_src_frames = data->old_dst_frames = 0;
+	plugin->transfer = rate_transfer;
+	plugin->src_frames = rate_src_frames;
+	plugin->dst_frames = rate_dst_frames;
+	plugin->action = rate_action;
+	*r_plugin = plugin;
+	return 0;
+}
diff -Nru linux/sound/core/oss/route.c linux-2.4.19-pre5-mjc/sound/core/oss/route.c
--- linux/sound/core/oss/route.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/oss/route.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,581 @@
+/*
+ *  Attenuated route Plug-In
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "pcm_plugin.h"
+
+/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
+#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
+#define div(a) a /= ROUTE_PLUGIN_RESOLUTION
+#elif ROUTE_PLUGIN_RESOLUTION == 16
+#define div(a) a >>= 4
+#else
+#error "Add some code here"
+#endif
+
+typedef struct ttable_dst ttable_dst_t;
+typedef struct route_private_data route_t;
+
+typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin,
+			      const snd_pcm_plugin_channel_t *src_channels,
+			      snd_pcm_plugin_channel_t *dst_channel,
+			      ttable_dst_t* ttable, snd_pcm_uframes_t frames);
+
+typedef struct {
+	int channel;
+	int as_int;
+#if ROUTE_PLUGIN_USE_FLOAT
+	float as_float;
+#endif
+} ttable_src_t;
+
+struct ttable_dst {
+	int att;	/* Attenuated */
+	unsigned int nsrcs;
+	ttable_src_t* srcs;
+	route_channel_f func;
+};
+
+struct route_private_data {
+	enum {R_UINT32=0, R_UINT64=1, R_FLOAT=2} sum_type;
+	int get, put;
+	int conv;
+	int src_sample_size;
+	ttable_dst_t ttable[0];
+};
+
+typedef union {
+	u_int32_t as_uint32;
+	u_int64_t as_uint64;
+#if ROUTE_PLUGIN_USE_FLOAT
+	float as_float;
+#endif
+} sum_t;
+
+
+static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin,
+				     const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
+				     snd_pcm_plugin_channel_t *dst_channel,
+				     ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames)
+{
+	if (dst_channel->wanted)
+		snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format);
+	dst_channel->enabled = 0;
+}
+
+static void route_to_channel_from_one(snd_pcm_plugin_t *plugin,
+				    const snd_pcm_plugin_channel_t *src_channels,
+				    snd_pcm_plugin_channel_t *dst_channel,
+				    ttable_dst_t* ttable, snd_pcm_uframes_t frames)
+{
+#define CONV_LABELS
+#include "plugin_ops.h"
+#undef CONV_LABELS
+	route_t *data = (route_t *)plugin->extra_data;
+	void *conv;
+	const snd_pcm_plugin_channel_t *src_channel = 0;
+	unsigned int srcidx;
+	char *src, *dst;
+	int src_step, dst_step;
+	for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
+		src_channel = &src_channels[ttable->srcs[srcidx].channel];
+		if (src_channel->area.addr != NULL)
+			break;
+	}
+	if (srcidx == ttable->nsrcs) {
+		route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
+		return;
+	}
+
+	dst_channel->enabled = 1;
+	conv = conv_labels[data->conv];
+	src = src_channel->area.addr + src_channel->area.first / 8;
+	src_step = src_channel->area.step / 8;
+	dst = dst_channel->area.addr + dst_channel->area.first / 8;
+	dst_step = dst_channel->area.step / 8;
+	while (frames-- > 0) {
+		goto *conv;
+#define CONV_END after
+#include "plugin_ops.h"
+#undef CONV_END
+	after:
+		src += src_step;
+		dst += dst_step;
+	}
+}
+
+static void route_to_channel(snd_pcm_plugin_t *plugin,
+			   const snd_pcm_plugin_channel_t *src_channels,
+			   snd_pcm_plugin_channel_t *dst_channel,
+			   ttable_dst_t* ttable, snd_pcm_uframes_t frames)
+{
+#define GET_U_LABELS
+#define PUT_U32_LABELS
+#include "plugin_ops.h"
+#undef GET_U_LABELS
+#undef PUT_U32_LABELS
+	static void *zero_labels[3] = { &&zero_int32, &&zero_int64,
+#if ROUTE_PLUGIN_USE_FLOAT
+				 &&zero_float
+#endif
+	};
+	/* sum_type att */
+	static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att,
+				    &&add_int64_noatt, &&add_int64_att,
+#if ROUTE_PLUGIN_USE_FLOAT
+				    &&add_float_noatt, &&add_float_att
+#endif
+	};
+	/* sum_type att shift */
+	static void *norm_labels[3 * 2 * 4] = { 0,
+					 &&norm_int32_8_noatt,
+					 &&norm_int32_16_noatt,
+					 &&norm_int32_24_noatt,
+					 0,
+					 &&norm_int32_8_att,
+					 &&norm_int32_16_att,
+					 &&norm_int32_24_att,
+					 &&norm_int64_0_noatt,
+					 &&norm_int64_8_noatt,
+					 &&norm_int64_16_noatt,
+					 &&norm_int64_24_noatt,
+					 &&norm_int64_0_att,
+					 &&norm_int64_8_att,
+					 &&norm_int64_16_att,
+					 &&norm_int64_24_att,
+#if ROUTE_PLUGIN_USE_FLOAT
+					 &&norm_float_0,
+					 &&norm_float_8,
+					 &&norm_float_16,
+					 &&norm_float_24,
+					 &&norm_float_0,
+					 &&norm_float_8,
+					 &&norm_float_16,
+					 &&norm_float_24,
+#endif
+	};
+	route_t *data = (route_t *)plugin->extra_data;
+	void *zero, *get, *add, *norm, *put_u32;
+	int nsrcs = ttable->nsrcs;
+	char *dst;
+	int dst_step;
+	char *srcs[nsrcs];
+	int src_steps[nsrcs];
+	ttable_src_t src_tt[nsrcs];
+	u_int32_t sample = 0;
+	int srcidx, srcidx1 = 0;
+	for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
+		const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel];
+		if (!src_channel->enabled)
+			continue;
+		srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8;
+		src_steps[srcidx1] = src_channel->area.step / 8;
+		src_tt[srcidx1] = ttable->srcs[srcidx];
+		srcidx1++;
+	}
+	nsrcs = srcidx1;
+	if (nsrcs == 0) {
+		route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
+		return;
+	} else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
+		route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames);
+		return;
+	}
+
+	dst_channel->enabled = 1;
+	zero = zero_labels[data->sum_type];
+	get = get_u_labels[data->get];
+	add = add_labels[data->sum_type * 2 + ttable->att];
+	norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size];
+	put_u32 = put_u32_labels[data->put];
+	dst = dst_channel->area.addr + dst_channel->area.first / 8;
+	dst_step = dst_channel->area.step / 8;
+
+	while (frames-- > 0) {
+		ttable_src_t *ttp = src_tt;
+		sum_t sum;
+
+		/* Zero sum */
+		goto *zero;
+	zero_int32:
+		sum.as_uint32 = 0;
+		goto zero_end;
+	zero_int64: 
+		sum.as_uint64 = 0;
+		goto zero_end;
+#if ROUTE_PLUGIN_USE_FLOAT
+	zero_float:
+		sum.as_float = 0.0;
+		goto zero_end;
+#endif
+	zero_end:
+		for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
+			char *src = srcs[srcidx];
+			
+			/* Get sample */
+			goto *get;
+#define GET_U_END after_get
+#include "plugin_ops.h"
+#undef GET_U_END
+		after_get:
+
+			/* Sum */
+			goto *add;
+		add_int32_att:
+			sum.as_uint32 += sample * ttp->as_int;
+			goto after_sum;
+		add_int32_noatt:
+			if (ttp->as_int)
+				sum.as_uint32 += sample;
+			goto after_sum;
+		add_int64_att:
+			sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
+			goto after_sum;
+		add_int64_noatt:
+			if (ttp->as_int)
+				sum.as_uint64 += sample;
+			goto after_sum;
+#if ROUTE_PLUGIN_USE_FLOAT
+		add_float_att:
+			sum.as_float += sample * ttp->as_float;
+			goto after_sum;
+		add_float_noatt:
+			if (ttp->as_int)
+				sum.as_float += sample;
+			goto after_sum;
+#endif
+		after_sum:
+			srcs[srcidx] += src_steps[srcidx];
+			ttp++;
+		}
+		
+		/* Normalization */
+		goto *norm;
+	norm_int32_8_att:
+		sum.as_uint64 = sum.as_uint32;
+	norm_int64_8_att:
+		sum.as_uint64 <<= 8;
+	norm_int64_0_att:
+		div(sum.as_uint64);
+		goto norm_int;
+
+	norm_int32_16_att:
+		sum.as_uint64 = sum.as_uint32;
+	norm_int64_16_att:
+		sum.as_uint64 <<= 16;
+		div(sum.as_uint64);
+		goto norm_int;
+
+	norm_int32_24_att:
+		sum.as_uint64 = sum.as_uint32;
+	norm_int64_24_att:
+		sum.as_uint64 <<= 24;
+		div(sum.as_uint64);
+		goto norm_int;
+
+	norm_int32_8_noatt:
+		sum.as_uint64 = sum.as_uint32;
+	norm_int64_8_noatt:
+		sum.as_uint64 <<= 8;
+		goto norm_int;
+
+	norm_int32_16_noatt:
+		sum.as_uint64 = sum.as_uint32;
+	norm_int64_16_noatt:
+		sum.as_uint64 <<= 16;
+		goto norm_int;
+
+	norm_int32_24_noatt:
+		sum.as_uint64 = sum.as_uint32;
+	norm_int64_24_noatt:
+		sum.as_uint64 <<= 24;
+		goto norm_int;
+
+	norm_int64_0_noatt:
+	norm_int:
+		if (sum.as_uint64 > (u_int32_t)0xffffffff)
+			sample = (u_int32_t)0xffffffff;
+		else
+			sample = sum.as_uint64;
+		goto after_norm;
+
+#if ROUTE_PLUGIN_USE_FLOAT
+	norm_float_8:
+		sum.as_float *= 1 << 8;
+		goto norm_float;
+	norm_float_16:
+		sum.as_float *= 1 << 16;
+		goto norm_float;
+	norm_float_24:
+		sum.as_float *= 1 << 24;
+		goto norm_float;
+	norm_float_0:
+	norm_float:
+		sum.as_float = floor(sum.as_float + 0.5);
+		if (sum.as_float > (u_int32_t)0xffffffff)
+			sample = (u_int32_t)0xffffffff;
+		else
+			sample = sum.as_float;
+		goto after_norm;
+#endif
+	after_norm:
+		
+		/* Put sample */
+		goto *put_u32;
+#define PUT_U32_END after_put_u32
+#include "plugin_ops.h"
+#undef PUT_U32_END
+	after_put_u32:
+		
+		dst += dst_step;
+	}
+}
+
+int route_src_channels_mask(snd_pcm_plugin_t *plugin,
+			  bitset_t *dst_vmask,
+			  bitset_t **src_vmask)
+{
+	route_t *data = (route_t *)plugin->extra_data;
+	int schannels = plugin->src_format.channels;
+	int dchannels = plugin->dst_format.channels;
+	bitset_t *vmask = plugin->src_vmask;
+	int channel;
+	ttable_dst_t *dp = data->ttable;
+	bitset_zero(vmask, schannels);
+	for (channel = 0; channel < dchannels; channel++, dp++) {
+		unsigned int src;
+		ttable_src_t *sp;
+		if (!bitset_get(dst_vmask, channel))
+			continue;
+		sp = dp->srcs;
+		for (src = 0; src < dp->nsrcs; src++, sp++)
+			bitset_set(vmask, sp->channel);
+	}
+	*src_vmask = vmask;
+	return 0;
+}
+
+int route_dst_channels_mask(snd_pcm_plugin_t *plugin,
+			  bitset_t *src_vmask,
+			  bitset_t **dst_vmask)
+{
+	route_t *data = (route_t *)plugin->extra_data;
+	int dchannels = plugin->dst_format.channels;
+	bitset_t *vmask = plugin->dst_vmask;
+	int channel;
+	ttable_dst_t *dp = data->ttable;
+	bitset_zero(vmask, dchannels);
+	for (channel = 0; channel < dchannels; channel++, dp++) {
+		unsigned int src;
+		ttable_src_t *sp;
+		sp = dp->srcs;
+		for (src = 0; src < dp->nsrcs; src++, sp++) {
+			if (bitset_get(src_vmask, sp->channel)) {
+				bitset_set(vmask, channel);
+				break;
+			}
+		}
+	}
+	*dst_vmask = vmask;
+	return 0;
+}
+
+static void route_free(snd_pcm_plugin_t *plugin)
+{
+	route_t *data = (route_t *)plugin->extra_data;
+	unsigned int dst_channel;
+	for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
+		if (data->ttable[dst_channel].srcs != NULL)
+			kfree(data->ttable[dst_channel].srcs);
+	}
+}
+
+static int route_load_ttable(snd_pcm_plugin_t *plugin, 
+			     const route_ttable_entry_t* src_ttable)
+{
+	route_t *data;
+	unsigned int src_channel, dst_channel;
+	const route_ttable_entry_t *sptr;
+	ttable_dst_t *dptr;
+	if (src_ttable == NULL)
+		return 0;
+	data = (route_t *)plugin->extra_data;
+	dptr = data->ttable;
+	sptr = src_ttable;
+	plugin->private_free = route_free;
+	for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
+		route_ttable_entry_t t = 0;
+		int att = 0;
+		int nsrcs = 0;
+		ttable_src_t srcs[plugin->src_format.channels];
+		for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) {
+			snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO);
+			if (*sptr != 0) {
+				srcs[nsrcs].channel = src_channel;
+#if ROUTE_PLUGIN_USE_FLOAT
+				/* Also in user space for non attenuated */
+				srcs[nsrcs].as_int = (*sptr == FULL ? ROUTE_PLUGIN_RESOLUTION : 0);
+				srcs[nsrcs].as_float = *sptr;
+#else
+				srcs[nsrcs].as_int = *sptr;
+#endif
+				if (*sptr != FULL)
+					att = 1;
+				t += *sptr;
+				nsrcs++;
+			}
+			sptr++;
+		}
+		dptr->att = att;
+		dptr->nsrcs = nsrcs;
+		if (nsrcs == 0)
+			dptr->func = route_to_channel_from_zero;
+		else if (nsrcs == 1 && !att)
+			dptr->func = route_to_channel_from_one;
+		else
+			dptr->func = route_to_channel;
+		if (nsrcs > 0) {
+                        int srcidx;
+			dptr->srcs = snd_kcalloc(nsrcs * sizeof(*srcs), GFP_KERNEL);
+                        for(srcidx = 0; srcidx < nsrcs; srcidx++)
+				dptr->srcs[srcidx] = srcs[srcidx];
+		} else
+			dptr->srcs = 0;
+		dptr++;
+	}
+	return 0;
+}
+
+static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin,
+			      const snd_pcm_plugin_channel_t *src_channels,
+			      snd_pcm_plugin_channel_t *dst_channels,
+			      snd_pcm_uframes_t frames)
+{
+	route_t *data;
+	int src_nchannels, dst_nchannels;
+	int dst_channel;
+	ttable_dst_t *ttp;
+	snd_pcm_plugin_channel_t *dvp;
+
+	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (frames == 0)
+		return 0;
+	data = (route_t *)plugin->extra_data;
+
+	src_nchannels = plugin->src_format.channels;
+	dst_nchannels = plugin->dst_format.channels;
+
+#ifdef CONFIG_SND_DEBUG
+	{
+		int src_channel;
+		for (src_channel = 0; src_channel < src_nchannels; ++src_channel) {
+			snd_assert(src_channels[src_channel].area.first % 8 == 0 ||
+				   src_channels[src_channel].area.step % 8 == 0,
+				   return -ENXIO);
+		}
+		for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
+			snd_assert(dst_channels[dst_channel].area.first % 8 == 0 ||
+				   dst_channels[dst_channel].area.step % 8 == 0,
+				   return -ENXIO);
+		}
+	}
+#endif
+
+	ttp = data->ttable;
+	dvp = dst_channels;
+	for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
+		ttp->func(plugin, src_channels, dvp, ttp, frames);
+		dvp++;
+		ttp++;
+	}
+	return frames;
+}
+
+int getput_index(int format)
+{
+	int sign, width, endian;
+	sign = !snd_pcm_format_signed(format);
+	width = snd_pcm_format_width(format) / 8 - 1;
+#ifdef SNDRV_LITTLE_ENDIAN
+	endian = snd_pcm_format_big_endian(format);
+#else
+	endian = snd_pcm_format_little_endian(format);
+#endif
+	if (endian < 0)
+		endian = 0;
+	return width * 4 + endian * 2 + sign;
+}
+
+int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug,
+			       snd_pcm_plugin_format_t *src_format,
+			       snd_pcm_plugin_format_t *dst_format,
+			       route_ttable_entry_t *ttable,
+			       snd_pcm_plugin_t **r_plugin)
+{
+	route_t *data;
+	snd_pcm_plugin_t *plugin;
+	int err;
+
+	snd_assert(r_plugin != NULL, return -ENXIO);
+	*r_plugin = NULL;
+	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
+	snd_assert(snd_pcm_format_linear(src_format->format) != 0 &&
+		   snd_pcm_format_linear(dst_format->format) != 0,
+		   return -ENXIO);
+
+	err = snd_pcm_plugin_build(plug, "attenuated route conversion",
+				   src_format, dst_format,
+				   sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels,
+				   &plugin);
+	if (err < 0)
+		return err;
+
+	data = (route_t *) plugin->extra_data;
+
+	data->get = getput_index(src_format->format);
+	data->put = getput_index(dst_format->format);
+	data->conv = conv_index(src_format->format, dst_format->format);
+
+#if ROUTE_PLUGIN_USE_FLOAT
+	data->sum_type = R_FLOAT;
+#else
+	if (snd_pcm_format_width(src_format->format) == 32)
+		data->sum_type = R_UINT64;
+	else
+		data->sum_type = R_UINT32;
+#endif
+	data->src_sample_size = snd_pcm_format_width(src_format->format) / 8;
+
+	if ((err = route_load_ttable(plugin, ttable)) < 0) {
+		snd_pcm_plugin_free(plugin);
+		return err;
+	}
+	plugin->transfer = route_transfer;
+	plugin->src_channels_mask = route_src_channels_mask;
+	plugin->dst_channels_mask = route_dst_channels_mask;
+	*r_plugin = plugin;
+	return 0;
+}
diff -Nru linux/sound/core/pcm.c linux-2.4.19-pre5-mjc/sound/core/pcm.c
--- linux/sound/core/pcm.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/pcm.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,994 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
+MODULE_LICENSE("GPL");
+
+snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES];
+static LIST_HEAD(snd_pcm_notify_list);
+static DECLARE_MUTEX(register_mutex);
+
+int snd_pcm_free(snd_pcm_t *pcm);
+static int snd_pcm_dev_free(snd_device_t *device);
+static int snd_pcm_dev_register(snd_device_t *device);
+static int snd_pcm_dev_unregister(snd_device_t *device);
+
+void snd_pcm_lock(int xup)
+{
+	if (!xup) {
+		down(&register_mutex);
+	} else {
+		up(&register_mutex);
+	}
+}
+
+static int snd_pcm_control_ioctl(snd_card_t * card,
+				 snd_ctl_file_t * control,
+				 unsigned int cmd, unsigned long arg)
+{
+	unsigned int tmp;
+
+	tmp = card->number * SNDRV_PCM_DEVICES;
+	switch (cmd) {
+	case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
+		{
+			int device;
+
+			if (get_user(device, (int *)arg))
+				return -EFAULT;
+			device = device < 0 ? 0 : device + 1;
+			while (device < SNDRV_PCM_DEVICES) {
+				if (snd_pcm_devices[tmp + device])
+					break;
+				device++;
+			}
+			if (device == SNDRV_PCM_DEVICES)
+				device = -1;
+			if (put_user(device, (int *)arg))
+				return -EFAULT;
+			return 0;
+		}
+	case SNDRV_CTL_IOCTL_PCM_INFO:
+		{
+			snd_pcm_info_t *info = (snd_pcm_info_t *)arg;
+			unsigned int device, subdevice;
+			snd_pcm_stream_t stream;
+			snd_pcm_t *pcm;
+			snd_pcm_str_t *pstr;
+			snd_pcm_substream_t *substream;
+			if (get_user(device, &info->device))
+				return -EFAULT;
+			if (device >= SNDRV_PCM_DEVICES)
+				return -ENXIO;
+			pcm = snd_pcm_devices[tmp + device];
+			if (pcm == NULL)
+				return -ENXIO;
+			if (get_user(stream, &info->stream))
+				return -EFAULT;
+			if (stream < 0 || stream > 1)
+				return -EINVAL;
+			pstr = &pcm->streams[stream];
+			if (pstr->substream_count == 0)
+				return -ENOENT;
+			if (get_user(subdevice, &info->subdevice))
+				return -EFAULT;
+			if (subdevice >= pstr->substream_count)
+				return -ENXIO;
+			for (substream = pstr->substream; substream; substream = substream->next)
+				if (substream->number == subdevice)
+					break;
+			if (substream == NULL)
+				return -ENXIO;
+			return snd_pcm_info_user(substream, info);
+		}
+	case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
+		{
+			int val;
+			
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			control->prefer_pcm_subdevice = val;
+			return 0;
+		}
+	}
+	return -ENOIOCTLCMD;
+}
+#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
+#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
+#define READY(v) [SNDRV_PCM_READY_##v] = #v
+#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
+#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
+#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
+#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
+#define START(v) [SNDRV_PCM_START_##v] = #v
+#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
+#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 
+
+char *snd_pcm_stream_names[] = {
+	STREAM(PLAYBACK),
+	STREAM(CAPTURE),
+};
+
+char *snd_pcm_state_names[] = {
+	STATE(OPEN),
+	STATE(SETUP),
+	STATE(PREPARED),
+	STATE(RUNNING),
+	STATE(XRUN),
+	STATE(PAUSED),
+};
+
+char *snd_pcm_access_names[] = {
+	ACCESS(MMAP_INTERLEAVED), 
+	ACCESS(MMAP_NONINTERLEAVED),
+	ACCESS(MMAP_COMPLEX),
+	ACCESS(RW_INTERLEAVED),
+	ACCESS(RW_NONINTERLEAVED),
+};
+
+char *snd_pcm_format_names[] = {
+	FORMAT(S8),
+	FORMAT(U8),
+	FORMAT(S16_LE),
+	FORMAT(S16_BE),
+	FORMAT(U16_LE),
+	FORMAT(U16_BE),
+	FORMAT(S24_LE),
+	FORMAT(S24_BE),
+	FORMAT(U24_LE),
+	FORMAT(U24_BE),
+	FORMAT(S32_LE),
+	FORMAT(S32_BE),
+	FORMAT(U32_LE),
+	FORMAT(U32_BE),
+	FORMAT(FLOAT_LE),
+	FORMAT(FLOAT_BE),
+	FORMAT(FLOAT64_LE),
+	FORMAT(FLOAT64_BE),
+	FORMAT(IEC958_SUBFRAME_LE),
+	FORMAT(IEC958_SUBFRAME_BE),
+	FORMAT(MU_LAW),
+	FORMAT(A_LAW),
+	FORMAT(IMA_ADPCM),
+	FORMAT(MPEG),
+	FORMAT(GSM),
+	FORMAT(SPECIAL),
+};
+
+char *snd_pcm_subformat_names[] = {
+	SUBFORMAT(STD), 
+};
+
+char *snd_pcm_tstamp_mode_names[] = {
+	TSTAMP(NONE),
+	TSTAMP(MMAP),
+};
+
+const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
+{
+	snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return 0);
+	return snd_pcm_stream_names[stream];
+}
+
+const char *snd_pcm_access_name(snd_pcm_access_t access)
+{
+	snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return 0);
+	return snd_pcm_access_names[access];
+}
+
+const char *snd_pcm_format_name(snd_pcm_format_t format)
+{
+	snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return 0);
+	return snd_pcm_format_names[format];
+}
+
+const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
+{
+	snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return 0);
+	return snd_pcm_subformat_names[subformat];
+}
+
+const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode)
+{
+	snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return 0);
+	return snd_pcm_tstamp_mode_names[mode];
+}
+
+const char *snd_pcm_state_name(snd_pcm_state_t state)
+{
+	snd_assert(state <= SNDRV_PCM_STATE_LAST, return 0);
+	return snd_pcm_state_names[state];
+}
+
+#ifdef CONFIG_SND_OSSEMUL
+#include <linux/soundcard.h>
+const char *snd_pcm_oss_format_name(int format)
+{
+	switch (format) {
+	case AFMT_MU_LAW:
+		return "MU_LAW";
+	case AFMT_A_LAW:
+		return "A_LAW";
+	case AFMT_IMA_ADPCM:
+		return "IMA_ADPCM";
+	case AFMT_U8:
+		return "U8";
+	case AFMT_S16_LE:
+		return "S16_LE";
+	case AFMT_S16_BE:
+		return "S16_BE";
+	case AFMT_S8:
+		return "S8";
+	case AFMT_U16_LE:
+		return "U16_LE";
+	case AFMT_U16_BE:
+		return "U16_BE";
+	case AFMT_MPEG:
+		return "MPEG";
+	default:
+		return "unknown";
+	}
+}
+#endif
+
+
+static void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer)
+{
+	snd_pcm_info_t info;
+	int err;
+	snd_runtime_check(substream, return);
+	err = snd_pcm_info(substream, &info);
+	if (err < 0) {
+		snd_iprintf(buffer, "error %d\n", err);
+		return;
+	}
+	snd_iprintf(buffer, "card: %d\n", info.card);
+	snd_iprintf(buffer, "device: %d\n", info.device);
+	snd_iprintf(buffer, "subdevice: %d\n", info.subdevice);
+	snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info.stream));
+	snd_iprintf(buffer, "id: %s\n", info.id);
+	snd_iprintf(buffer, "name: %s\n", info.name);
+	snd_iprintf(buffer, "subname: %s\n", info.subname);
+	snd_iprintf(buffer, "class: %d\n", info.dev_class);
+	snd_iprintf(buffer, "subclass: %d\n", info.dev_subclass);
+	snd_iprintf(buffer, "subdevices_count: %d\n", info.subdevices_count);
+	snd_iprintf(buffer, "subdevices_avail: %d\n", info.subdevices_avail);
+}
+
+static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer);
+}
+
+static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer);
+}
+
+static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (!runtime) {
+		snd_iprintf(buffer, "closed\n");
+		return;
+	}
+	spin_lock_irq(&runtime->lock);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		snd_iprintf(buffer, "no setup\n");
+		spin_unlock_irq(&runtime->lock);
+		return;
+	}
+	snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
+	snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
+	snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat));
+	snd_iprintf(buffer, "channels: %u\n", runtime->channels);	
+	snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);	
+	snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);	
+	snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);	
+	snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);
+#ifdef CONFIG_SND_OSSEMUL
+	if (substream->oss.oss) {
+		snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
+		snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);	
+		snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate);
+		snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);	
+		snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods);
+	}
+#endif
+	spin_unlock_irq(&runtime->lock);
+}
+
+static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (!runtime) {
+		snd_iprintf(buffer, "closed\n");
+		return;
+	}
+	spin_lock_irq(&runtime->lock);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		snd_iprintf(buffer, "no setup\n");
+		spin_unlock_irq(&runtime->lock);
+		return;
+	}
+	snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
+	snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
+	snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min);
+	snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
+	snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
+	snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
+	snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
+	snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
+	snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
+	snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
+	spin_unlock_irq(&runtime->lock);
+}
+
+static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_status_t status;
+	int err;
+	if (!runtime) {
+		snd_iprintf(buffer, "closed\n");
+		return;
+	}
+	err = snd_pcm_status(substream, &status);
+	if (err < 0) {
+		snd_iprintf(buffer, "error %d\n", err);
+		return;
+	}
+	snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+	snd_iprintf(buffer, "trigger_time: %ld.%06ld\n",
+		status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_usec);
+	snd_iprintf(buffer, "tstamp      : %ld.%06ld\n",
+		status.tstamp.tv_sec, status.tstamp.tv_usec);
+	snd_iprintf(buffer, "delay       : %ld\n", status.delay);
+	snd_iprintf(buffer, "avail       : %ld\n", status.avail);
+	snd_iprintf(buffer, "avail_max   : %ld\n", status.avail_max);
+	snd_iprintf(buffer, "-----\n");
+	snd_iprintf(buffer, "hw_ptr      : %ld\n", runtime->status->hw_ptr);
+	snd_iprintf(buffer, "appl_ptr    : %ld\n", runtime->control->appl_ptr);
+}
+
+static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr)
+{
+	snd_pcm_t *pcm = pstr->pcm;
+	snd_info_entry_t *entry;
+	char name[16];
+
+	sprintf(name, "pcm%i%c", pcm->device, 
+		pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
+	if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL)
+		return -ENOMEM;
+	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	pstr->proc_root = entry;
+
+	if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->mode = S_IFREG | S_IRUGO;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_pcm_stream_proc_info_read;
+		entry->private_data = pstr;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	pstr->proc_info_entry = entry;
+
+	return 0;
+}
+
+static int snd_pcm_stream_proc_done(snd_pcm_str_t *pstr)
+{
+	if (pstr->proc_info_entry) {
+		snd_info_unregister(pstr->proc_info_entry);
+		pstr->proc_info_entry = NULL;
+	}
+	if (pstr->proc_root) {
+		snd_info_unregister(pstr->proc_root);
+		pstr->proc_root = NULL;
+	}
+	return 0;
+}
+
+static int snd_pcm_substream_proc_init(snd_pcm_substream_t *substream)
+{
+	snd_info_entry_t *entry;
+	snd_card_t *card;
+	char name[16];
+
+	card = substream->pcm->card;
+
+	sprintf(name, "sub%i", substream->number);
+	if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL)
+		return -ENOMEM;
+	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	substream->proc_root = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->mode = S_IFREG | S_IRUGO;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_pcm_substream_proc_info_read;
+		entry->private_data = substream;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	substream->proc_info_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->mode = S_IFREG | S_IRUGO;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_pcm_substream_proc_hw_params_read;
+		entry->private_data = substream;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	substream->proc_hw_params_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->mode = S_IFREG | S_IRUGO;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_pcm_substream_proc_sw_params_read;
+		entry->private_data = substream;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	substream->proc_sw_params_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->mode = S_IFREG | S_IRUGO;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_pcm_substream_proc_status_read;
+		entry->private_data = substream;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	substream->proc_status_entry = entry;
+
+	return 0;
+}
+		
+static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream)
+{
+	if (substream->proc_info_entry) {
+		snd_info_unregister(substream->proc_info_entry);
+		substream->proc_info_entry = 0;
+	}
+	if (substream->proc_hw_params_entry) {
+		snd_info_unregister(substream->proc_hw_params_entry);
+		substream->proc_hw_params_entry = 0;
+	}
+	if (substream->proc_sw_params_entry) {
+		snd_info_unregister(substream->proc_sw_params_entry);
+		substream->proc_sw_params_entry = 0;
+	}
+	if (substream->proc_status_entry) {
+		snd_info_unregister(substream->proc_status_entry);
+		substream->proc_status_entry = 0;
+	}
+	if (substream->proc_root) {
+		snd_info_unregister(substream->proc_root);
+		substream->proc_root = 0;
+	}
+	return 0;
+}
+
+static int snd_pcm_new_stream(snd_pcm_t *pcm,
+			      snd_pcm_str_t *pstr,
+			      int substream_count,
+			      int stream)
+{
+	int idx, err;
+	snd_pcm_substream_t *substream, *prev;
+
+#ifdef CONFIG_SND_OSSEMUL
+	init_MUTEX(&pstr->oss.setup_mutex);
+#endif
+	pstr->stream = stream;
+	pstr->pcm = pcm;
+	pstr->substream_count = substream_count;
+	pstr->reg = &snd_pcm_reg[stream];
+	if (substream_count > 0) {
+		err = snd_pcm_stream_proc_init(pstr);
+		if (err < 0)
+			return err;
+	}
+	prev = NULL;
+	for (idx = 0, prev = NULL; idx < substream_count; idx++) {
+		substream = snd_magic_kcalloc(snd_pcm_substream_t, 0, GFP_KERNEL);
+		if (substream == NULL)
+			return -ENOMEM;
+		substream->pcm = pcm;
+		substream->pstr = pstr;
+		substream->number = idx;
+		substream->stream = stream;
+		sprintf(substream->name, "subdevice #%i", idx);
+		substream->buffer_bytes_max = UINT_MAX;
+		if (prev == NULL)
+			pstr->substream = substream;
+		else
+			prev->next = substream;
+		substream->link_next = substream;
+		substream->link_prev = substream;
+		err = snd_pcm_substream_proc_init(substream);
+		if (err < 0) {
+			snd_magic_kfree(substream);
+			return err;
+		}
+		substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA;
+		substream->dma_private = NULL;
+		spin_lock_init(&substream->timer_lock);
+		prev = substream;
+	}
+	return 0;
+}				
+
+int snd_pcm_new(snd_card_t * card, char *id, int device,
+	        int playback_count, int capture_count,
+	        snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_pcm_dev_free,
+		dev_register:	snd_pcm_dev_register,
+		dev_unregister:	snd_pcm_dev_unregister
+	};
+
+	snd_assert(rpcm != NULL, return -EINVAL);
+	*rpcm = NULL;
+	snd_assert(card != NULL, return -ENXIO);
+	pcm = snd_magic_kcalloc(snd_pcm_t, 0, GFP_KERNEL);
+	if (pcm == NULL)
+		return -ENOMEM;
+	pcm->card = card;
+	pcm->device = device;
+	if (id) {
+		strncpy(pcm->id, id, sizeof(pcm->id) - 1);
+	}
+	if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK], playback_count, SNDRV_PCM_STREAM_PLAYBACK)) < 0) {
+		snd_pcm_free(pcm);
+		return err;
+	}
+	if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE], capture_count, SNDRV_PCM_STREAM_CAPTURE)) < 0) {
+		snd_pcm_free(pcm);
+		return err;
+	}
+	init_MUTEX(&pcm->open_mutex);
+	init_waitqueue_head(&pcm->open_wait);
+	if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
+		snd_pcm_free(pcm);
+		return err;
+	}
+	*rpcm = pcm;
+	return 0;
+}
+
+static void snd_pcm_free_stream(snd_pcm_str_t * pstr)
+{
+	snd_pcm_substream_t *substream, *substream_next;
+#ifdef CONFIG_SND_OSSEMUL
+	snd_pcm_oss_setup_t *setup, *setupn;
+#endif
+	substream = pstr->substream;
+	while (substream) {
+		substream_next = substream->next;
+		snd_pcm_substream_proc_done(substream);
+		snd_magic_kfree(substream);
+		substream = substream_next;
+	}
+	snd_pcm_stream_proc_done(pstr);
+#ifdef CONFIG_SND_OSSEMUL
+	for (setup = pstr->oss.setup_list; setup; setup = setupn) {
+		setupn = setup->next;
+		kfree(setup->task_name);
+		kfree(setup);
+	}
+#endif
+}
+
+int snd_pcm_free(snd_pcm_t *pcm)
+{
+	snd_assert(pcm != NULL, return -ENXIO);
+	if (pcm->private_free)
+		pcm->private_free(pcm);
+	snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]);
+	snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]);
+	snd_magic_kfree(pcm);
+	return 0;
+}
+
+int snd_pcm_dev_free(snd_device_t *device)
+{
+	snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO);
+	return snd_pcm_free(pcm);
+}
+
+static void snd_pcm_tick_timer_func(unsigned long data)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t*) data;
+	snd_pcm_tick_elapsed(substream);
+}
+
+int snd_pcm_open_substream(snd_pcm_t *pcm, int stream,
+			   snd_pcm_substream_t **rsubstream)
+{
+	snd_pcm_str_t * pstr;
+	snd_pcm_substream_t * substream;
+	snd_pcm_runtime_t * runtime;
+	snd_ctl_file_t *kctl;
+	snd_card_t *card;
+	struct list_head *list;
+	int prefer_subdevice = -1;
+	size_t size;
+
+	snd_assert(rsubstream != NULL, return -EINVAL);
+	*rsubstream = NULL;
+	snd_assert(pcm != NULL, return -ENXIO);
+	pstr = &pcm->streams[stream];
+	if (pstr->substream == NULL)
+		return -ENODEV;
+
+	card = pcm->card;
+	read_lock(&card->control_rwlock);
+	list_for_each(list, &card->ctl_files) {
+		kctl = snd_ctl_file(list);
+		if (kctl->pid == current->pid) {
+			prefer_subdevice = kctl->prefer_pcm_subdevice;
+			break;
+		}
+	}
+	read_unlock(&card->control_rwlock);
+
+	if (pstr->substream_count == 0)
+		return -ENODEV;
+	switch (stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
+			for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) {
+				if (SUBSTREAM_BUSY(substream))
+					return -EAGAIN;
+			}
+		}
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
+			for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) {
+				if (SUBSTREAM_BUSY(substream))
+					return -EAGAIN;
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (prefer_subdevice >= 0) {
+		for (substream = pstr->substream; substream; substream = substream->next)
+			if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
+				goto __ok;
+	}
+	for (substream = pstr->substream; substream; substream = substream->next)
+		if (!SUBSTREAM_BUSY(substream))
+			break;
+      __ok:
+	if (substream == NULL)
+		return -EAGAIN;
+
+	runtime = snd_kcalloc(sizeof(snd_pcm_runtime_t), GFP_KERNEL);
+	if (runtime == NULL)
+		return -ENOMEM;
+
+	size = PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t));
+	runtime->status = snd_malloc_pages(size, GFP_KERNEL);
+	if (runtime->status == NULL) {
+		kfree(runtime);
+		return -ENOMEM;
+	}
+
+	size = PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t));
+	runtime->control = snd_malloc_pages(size, GFP_KERNEL);
+	if (runtime->control == NULL) {
+		kfree((void *)runtime->status);
+		kfree(runtime);
+		return -ENOMEM;
+	}
+
+	memset((void*)runtime->status, 0, size);
+	memset((void*)runtime->control, 0, size);
+
+	init_waitqueue_head(&runtime->sleep);
+	spin_lock_init(&runtime->lock);
+	atomic_set(&runtime->mmap_count, 0);
+	init_timer(&runtime->tick_timer);
+	runtime->tick_timer.function = snd_pcm_tick_timer_func;
+	runtime->tick_timer.data = (unsigned long) substream;
+
+	runtime->status->state = SNDRV_PCM_STATE_OPEN;
+
+	substream->runtime = runtime;
+	pstr->substream_opened++;
+	*rsubstream = substream;
+	return 0;
+}
+
+void snd_pcm_release_substream(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t * runtime;
+	substream->file = NULL;
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return);
+	if (runtime->private_free != NULL)
+		runtime->private_free(runtime);
+	snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)));
+	snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)));
+	kfree(runtime->hw_constraints.rules);
+	kfree(runtime);
+	substream->runtime = NULL;
+	substream->pstr->substream_opened--;
+}
+
+int snd_pcm_dev_register(snd_device_t *device)
+{
+	int idx, cidx, err;
+	unsigned short minor;
+	snd_pcm_substream_t *substream;
+	struct list_head *list;
+	char str[16];
+	snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO);
+
+	snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+	snd_pcm_lock(0);
+	idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
+	if (snd_pcm_devices[idx]) {
+		snd_pcm_lock(1);
+		return -EBUSY;
+	}
+	snd_pcm_devices[idx] = pcm;
+	for (cidx = 0; cidx < 2; cidx++) {
+		int devtype = -1;
+		if (pcm->streams[cidx].substream == NULL)
+			continue;
+		switch (cidx) {
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
+			minor = SNDRV_MINOR_PCM_PLAYBACK + idx;
+			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
+			break;
+		case SNDRV_PCM_STREAM_CAPTURE:
+			sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
+			minor = SNDRV_MINOR_PCM_CAPTURE + idx;
+			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
+			break;
+		}
+		if ((err = snd_register_device(devtype, pcm->card, pcm->device, pcm->streams[cidx].reg, str)) < 0) {
+			snd_pcm_devices[idx] = NULL;
+			snd_pcm_lock(1);
+			return err;
+		}
+		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
+			snd_pcm_timer_init(substream);
+	}
+	list_for_each(list, &snd_pcm_notify_list) {
+		snd_pcm_notify_t *notify;
+		notify = list_entry(list, snd_pcm_notify_t, list);
+		if (notify->n_register)
+			notify->n_register(-1 /* idx + SNDRV_MINOR_PCM */, pcm);
+	}
+	snd_pcm_lock(1);
+	return 0;
+}
+
+static int snd_pcm_dev_unregister(snd_device_t *device)
+{
+	int idx, cidx, devtype;
+	snd_pcm_substream_t *substream;
+	struct list_head *list;
+	snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO);
+
+	snd_assert(pcm != NULL, return -ENXIO);
+	snd_pcm_lock(0);
+	idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
+	if (snd_pcm_devices[idx] != pcm) {
+		snd_pcm_lock(1);
+		return -EINVAL;
+	}
+	for (cidx = 0; cidx < 2; cidx++) {
+		devtype = -1;
+		switch (cidx) {
+		case SNDRV_PCM_STREAM_PLAYBACK:
+			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
+			break;
+		case SNDRV_PCM_STREAM_CAPTURE:
+			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
+			break;
+		}
+		snd_unregister_device(devtype, pcm->card, pcm->device);
+		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
+			snd_pcm_timer_done(substream);
+	}
+	list_for_each(list, &snd_pcm_notify_list) {
+		snd_pcm_notify_t *notify;
+		notify = list_entry(list, snd_pcm_notify_t, list);
+		if (notify->n_unregister)
+			notify->n_unregister(-1 /* SNDRV_MINOR_PCM + idx */, pcm);
+	}
+	snd_pcm_devices[idx] = NULL;
+	snd_pcm_lock(1);
+	return snd_pcm_free(pcm);
+}
+
+int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree)
+{
+	int idx;
+
+	snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
+	snd_pcm_lock(0);
+	if (nfree) {
+		list_del(&notify->list);
+		for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+			if (snd_pcm_devices[idx] == NULL)
+				continue;
+			notify->n_unregister(-1 /* idx + SNDRV_MINOR_PCM */,
+				             snd_pcm_devices[idx]);
+		}
+	} else {
+		list_add_tail(&notify->list, &snd_pcm_notify_list);
+		for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+			if (snd_pcm_devices[idx] == NULL)
+				continue;
+			notify->n_register(-1 /* idx + SNDRV_MINOR_PCM */,
+				           snd_pcm_devices[idx]);
+		}
+	}
+	snd_pcm_lock(1);
+	return 0;
+}
+
+/*
+ *  Info interface
+ */
+
+static void snd_pcm_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	int idx;
+	snd_pcm_t *pcm;
+
+	down(&register_mutex);
+	for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+		pcm = snd_pcm_devices[idx];
+		if (pcm == NULL)
+			continue;
+		snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES,
+			    idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name);
+		if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+			snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count);
+		if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+			snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
+		snd_iprintf(buffer, "\n");
+	}
+	up(&register_mutex);
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static snd_info_entry_t *snd_pcm_proc_entry = NULL;
+
+static int __init alsa_pcm_init(void)
+{
+	snd_info_entry_t *entry;
+
+	snd_ctl_register_ioctl(snd_pcm_control_ioctl);
+	if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = SNDRV_CARDS * SNDRV_PCM_DEVICES * 128;
+		entry->c.text.read = snd_pcm_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_pcm_proc_entry = entry;
+	return 0;
+}
+
+static void __exit alsa_pcm_exit(void)
+{
+	snd_ctl_unregister_ioctl(snd_pcm_control_ioctl);
+	if (snd_pcm_proc_entry) {
+		snd_info_unregister(snd_pcm_proc_entry);
+		snd_pcm_proc_entry = NULL;
+	}
+}
+
+module_init(alsa_pcm_init)
+module_exit(alsa_pcm_exit)
+
+EXPORT_SYMBOL(snd_pcm_lock);
+EXPORT_SYMBOL(snd_pcm_devices);
+EXPORT_SYMBOL(snd_pcm_new);
+EXPORT_SYMBOL(snd_pcm_notify);
+EXPORT_SYMBOL(snd_pcm_open_substream);
+EXPORT_SYMBOL(snd_pcm_release_substream);
+  /* pcm_native.c */
+EXPORT_SYMBOL(snd_pcm_start);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_pcm_suspend);
+EXPORT_SYMBOL(snd_pcm_suspend_all);
+#endif
+EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl);
+EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl);
+EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
+EXPORT_SYMBOL(snd_pcm_open);
+EXPORT_SYMBOL(snd_pcm_release);
+EXPORT_SYMBOL(snd_pcm_playback_poll);
+EXPORT_SYMBOL(snd_pcm_capture_poll);
+EXPORT_SYMBOL(snd_pcm_mmap_data);
+ /* pcm_misc.c */
+EXPORT_SYMBOL(snd_pcm_format_signed);
+EXPORT_SYMBOL(snd_pcm_format_unsigned);
+EXPORT_SYMBOL(snd_pcm_format_linear);
+EXPORT_SYMBOL(snd_pcm_format_little_endian);
+EXPORT_SYMBOL(snd_pcm_format_big_endian);
+EXPORT_SYMBOL(snd_pcm_format_width);
+EXPORT_SYMBOL(snd_pcm_format_physical_width);
+EXPORT_SYMBOL(snd_pcm_format_size);
+EXPORT_SYMBOL(snd_pcm_format_silence_64);
+EXPORT_SYMBOL(snd_pcm_format_set_silence);
+EXPORT_SYMBOL(snd_pcm_build_linear_format);
diff -Nru linux/sound/core/pcm_lib.c linux-2.4.19-pre5-mjc/sound/core/pcm_lib.c
--- linux/sound/core/pcm_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/pcm_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2376 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/timer.h>
+
+void snd_pcm_playback_silence(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t frames, ofs;
+	snd_pcm_sframes_t noise_dist;
+	if (runtime->silenced_start != runtime->control->appl_ptr) {
+		snd_pcm_sframes_t n = runtime->control->appl_ptr - runtime->silenced_start;
+		if (n < 0)
+			n += runtime->boundary;
+		if ((snd_pcm_uframes_t)n < runtime->silenced_size)
+			runtime->silenced_size -= n;
+		else
+			runtime->silenced_size = 0;
+		runtime->silenced_start = runtime->control->appl_ptr;
+	}
+	if (runtime->silenced_size == runtime->buffer_size)
+		return;
+	snd_assert(runtime->silenced_size <= runtime->buffer_size, return);
+	noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size;
+	if (noise_dist > (snd_pcm_sframes_t) runtime->silence_threshold)
+		return;
+	frames = runtime->silence_threshold - noise_dist;
+	if (frames < runtime->silence_size)
+		frames = runtime->silence_size;
+	if (runtime->silenced_size + frames > runtime->buffer_size)
+		frames = runtime->buffer_size - runtime->silenced_size;
+	ofs = runtime->silenced_start % runtime->buffer_size + runtime->silenced_size;
+	if (ofs >= runtime->buffer_size)
+		ofs -= runtime->buffer_size;
+	if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+	    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+		if (substream->ops->silence) {
+			int err;
+			err = substream->ops->silence(substream, -1, ofs, frames);
+			snd_assert(err >= 0, );
+		} else {
+			char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
+			snd_pcm_format_set_silence(runtime->format, hwbuf, frames * runtime->channels);
+		}
+	} else {
+		unsigned int c;
+		unsigned int channels = runtime->channels;
+		if (substream->ops->silence) {
+			for (c = 0; c < channels; ++c) {
+				int err;
+				err = substream->ops->silence(substream, c, ofs, frames);
+				snd_assert(err >= 0, );
+			}
+		} else {
+			size_t dma_csize = runtime->dma_bytes / channels;
+			for (c = 0; c < channels; ++c) {
+				char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
+				snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
+			}
+		}
+	}
+	runtime->silenced_size += frames;
+}
+
+int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt;
+	snd_pcm_uframes_t avail;
+	snd_pcm_sframes_t delta;
+
+	old_hw_ptr = runtime->status->hw_ptr;
+	pos = substream->ops->pointer(substream);
+	if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+		snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp);
+#ifdef CONFIG_SND_DEBUG
+	if (pos > runtime->buffer_size) {
+		snd_printk(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
+	} else
+#endif
+	snd_runtime_check(pos <= runtime->buffer_size, return 0);
+	  
+	pos -= pos % runtime->min_align;
+	new_hw_ptr = runtime->hw_ptr_base + pos;
+
+	hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
+
+	delta = hw_ptr_interrupt - new_hw_ptr;
+	if (delta > 0) {
+		if (delta < runtime->buffer_size / 2) {
+			snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
+			return 0;
+		}
+		runtime->hw_ptr_base += runtime->buffer_size;
+		if (runtime->hw_ptr_base == runtime->boundary)
+			runtime->hw_ptr_base = 0;
+		new_hw_ptr = runtime->hw_ptr_base + pos;
+	}
+	runtime->status->hw_ptr = new_hw_ptr;
+	runtime->hw_ptr_interrupt = new_hw_ptr - (runtime->hw_ptr_base + pos) % runtime->period_size;
+
+#if 0
+	if (hw_ptr_interrupt == runtime->boundary)
+		hw_ptr_interrupt = 0;
+
+	if (runtime->hw_ptr_interrupt != hw_ptr_interrupt)
+		snd_printd("Lost interrupt: hw_ptr = %d expected %d\n", runtime->hw_ptr_interrupt, hw_ptr_interrupt);
+#endif
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		avail = snd_pcm_playback_avail(runtime);
+	else
+		avail = snd_pcm_capture_avail(runtime);
+	if (avail > runtime->avail_max)
+		runtime->avail_max = avail;
+	if (avail >= runtime->stop_threshold) {
+		snd_pcm_stop(substream,
+			     runtime->status->state == SNDRV_PCM_STATE_DRAINING ?
+			     SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN);
+		return -EPIPE;
+	}
+	if (avail >= runtime->control->avail_min)
+		wake_up(&runtime->sleep);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+	    runtime->silence_size > 0)
+		snd_pcm_playback_silence(substream);
+	return 0;
+}
+
+/* CAUTION: call it with irq disabled */
+int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t pos;
+	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr;
+	snd_pcm_uframes_t avail;
+	snd_pcm_sframes_t delta;
+	old_hw_ptr = runtime->status->hw_ptr;
+	pos = substream->ops->pointer(substream);
+	if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+		snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp);
+#ifdef CONFIG_SND_DEBUG
+	if (pos > runtime->buffer_size) {
+		snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
+	} else
+#endif
+	snd_runtime_check(pos <= runtime->buffer_size, return 0);
+	  
+	pos -= pos % runtime->min_align;
+	new_hw_ptr = runtime->hw_ptr_base + pos;
+
+	delta = old_hw_ptr - new_hw_ptr;
+	if (delta > 0) {
+		if (delta < runtime->buffer_size / 2) {
+			snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
+			return 0;
+		}
+		runtime->hw_ptr_base += runtime->buffer_size;
+		if (runtime->hw_ptr_base == runtime->boundary)
+			runtime->hw_ptr_base = 0;
+		new_hw_ptr = runtime->hw_ptr_base + pos;
+	}
+	runtime->status->hw_ptr = new_hw_ptr;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		avail = snd_pcm_playback_avail(runtime);
+	else
+		avail = snd_pcm_capture_avail(runtime);
+	if (avail > runtime->avail_max)
+		runtime->avail_max = avail;
+	if (avail >= runtime->stop_threshold) {
+		snd_pcm_stop(substream,
+			     runtime->status->state == SNDRV_PCM_STATE_DRAINING ?
+			     SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN);
+		return -EPIPE;
+	}
+	if (avail >= runtime->control->avail_min)
+		wake_up(&runtime->sleep);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+	    runtime->silence_size > 0)
+		snd_pcm_playback_silence(substream);
+	return 0;
+}
+
+/*
+ *  Operations
+ */
+
+void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops)
+{
+	snd_pcm_str_t *stream = &pcm->streams[direction];
+	snd_pcm_substream_t *substream;
+	
+	for (substream = stream->substream; substream != NULL; substream = substream->next)
+		substream->ops = ops;
+}
+
+/*
+ *  Sync
+ */
+ 
+void snd_pcm_set_sync(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	
+	runtime->sync.id32[0] = substream->pcm->card->number;
+	runtime->sync.id32[1] = -1;
+	runtime->sync.id32[2] = -1;
+	runtime->sync.id32[3] = -1;
+}
+
+/*
+ *  Standard ioctl routine
+ */
+
+/* Code taken from alsa-lib */
+#define assert(a) snd_assert((a), return -EINVAL)
+
+static inline unsigned int div32(unsigned int a, unsigned int b, 
+				 unsigned int *r)
+{
+	if (b == 0) {
+		*r = 0;
+		return UINT_MAX;
+	}
+	*r = a % b;
+	return a / b;
+}
+
+static inline unsigned int div_down(unsigned int a, unsigned int b)
+{
+	if (b == 0)
+		return UINT_MAX;
+	return a / b;
+}
+
+static inline unsigned int div_up(unsigned int a, unsigned int b)
+{
+	unsigned int r;
+	unsigned int q;
+	if (b == 0)
+		return UINT_MAX;
+	q = div32(a, b, &r);
+	if (r)
+		++q;
+	return q;
+}
+
+static inline unsigned int mul(unsigned int a, unsigned int b)
+{
+	if (a == 0)
+		return 0;
+	if (div_down(UINT_MAX, a) < b)
+		return UINT_MAX;
+	return a * b;
+}
+
+static inline unsigned int muldiv32(unsigned int a, unsigned int b,
+				    unsigned int c, unsigned int *r)
+{
+	u_int64_t n = (u_int64_t) a * b;
+	if (c == 0) {
+		snd_assert(n > 0, );
+		*r = 0;
+		return UINT_MAX;
+	}
+	div64_32(&n, c, r);
+	if (n >= UINT_MAX) {
+		*r = 0;
+		return UINT_MAX;
+	}
+	return n;
+}
+
+int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin)
+{
+	int changed = 0;
+	assert(!snd_interval_empty(i));
+	if (i->min < min) {
+		i->min = min;
+		i->openmin = openmin;
+		changed = 1;
+	} else if (i->min == min && !i->openmin && openmin) {
+		i->openmin = 1;
+		changed = 1;
+	}
+	if (i->integer) {
+		if (i->openmin) {
+			i->min++;
+			i->openmin = 0;
+		}
+	}
+	if (snd_interval_checkempty(i)) {
+		snd_interval_none(i);
+		return -EINVAL;
+	}
+	return changed;
+}
+
+int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax)
+{
+	int changed = 0;
+	assert(!snd_interval_empty(i));
+	if (i->max > max) {
+		i->max = max;
+		i->openmax = openmax;
+		changed = 1;
+	} else if (i->max == max && !i->openmax && openmax) {
+		i->openmax = 1;
+		changed = 1;
+	}
+	if (i->integer) {
+		if (i->openmax) {
+			i->max--;
+			i->openmax = 0;
+		}
+	}
+	if (snd_interval_checkempty(i)) {
+		snd_interval_none(i);
+		return -EINVAL;
+	}
+	return changed;
+}
+
+/* r <- v */
+int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v)
+{
+	int changed = 0;
+	assert(!snd_interval_empty(i));
+	if (i->min < v->min) {
+		i->min = v->min;
+		i->openmin = v->openmin;
+		changed = 1;
+	} else if (i->min == v->min && !i->openmin && v->openmin) {
+		i->openmin = 1;
+		changed = 1;
+	}
+	if (i->max > v->max) {
+		i->max = v->max;
+		i->openmax = v->openmax;
+		changed = 1;
+	} else if (i->max == v->max && !i->openmax && v->openmax) {
+		i->openmax = 1;
+		changed = 1;
+	}
+	if (!i->integer && v->integer) {
+		i->integer = 1;
+		changed = 1;
+	}
+	if (i->integer) {
+		if (i->openmin) {
+			i->min++;
+			i->openmin = 0;
+		}
+		if (i->openmax) {
+			i->max--;
+			i->openmax = 0;
+		}
+	} else if (!i->openmin && !i->openmax && i->min == i->max)
+		i->integer = 1;
+	if (snd_interval_checkempty(i)) {
+		snd_interval_none(i);
+		return -EINVAL;
+	}
+	return changed;
+}
+
+int snd_interval_refine_first(snd_interval_t *i)
+{
+	assert(!snd_interval_empty(i));
+	if (snd_interval_single(i))
+		return 0;
+	i->max = i->min;
+	i->openmax = i->openmin;
+	if (i->openmax)
+		i->max++;
+	return 1;
+}
+
+int snd_interval_refine_last(snd_interval_t *i)
+{
+	assert(!snd_interval_empty(i));
+	if (snd_interval_single(i))
+		return 0;
+	i->min = i->max;
+	i->openmin = i->openmax;
+	if (i->openmin)
+		i->min--;
+	return 1;
+}
+
+int snd_interval_refine_set(snd_interval_t *i, unsigned int val)
+{
+	snd_interval_t t;
+	t.empty = 0;
+	t.min = t.max = val;
+	t.openmin = t.openmax = 0;
+	t.integer = 1;
+	return snd_interval_refine(i, &t);
+}
+
+void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c)
+{
+	if (a->empty || b->empty) {
+		snd_interval_none(c);
+		return;
+	}
+	c->empty = 0;
+	c->min = mul(a->min, b->min);
+	c->openmin = (a->openmin || b->openmin);
+	c->max = mul(a->max,  b->max);
+	c->openmax = (a->openmax || b->openmax);
+	c->integer = (a->integer && b->integer);
+}
+
+void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c)
+{
+	unsigned int r;
+	if (a->empty || b->empty) {
+		snd_interval_none(c);
+		return;
+	}
+	c->empty = 0;
+	c->min = div32(a->min, b->max, &r);
+	c->openmin = (r || a->openmin || b->openmax);
+	if (b->min > 0) {
+		c->max = div32(a->max, b->min, &r);
+		if (r) {
+			c->max++;
+			c->openmax = 1;
+		} else
+			c->openmax = (a->openmax || b->openmin);
+	} else {
+		c->max = UINT_MAX;
+		c->openmax = 0;
+	}
+	c->integer = 0;
+}
+
+/* a * b / k */
+void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b,
+		      unsigned int k, snd_interval_t *c)
+{
+	unsigned int r;
+	if (a->empty || b->empty) {
+		snd_interval_none(c);
+		return;
+	}
+	c->empty = 0;
+	c->min = muldiv32(a->min, b->min, k, &r);
+	c->openmin = (r || a->openmin || b->openmin);
+	c->max = muldiv32(a->max, b->max, k, &r);
+	if (r) {
+		c->max++;
+		c->openmax = 1;
+	} else
+		c->openmax = (a->openmax || b->openmax);
+	c->integer = 0;
+}
+
+/* a * k / b */
+void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k,
+		      const snd_interval_t *b, snd_interval_t *c)
+{
+	unsigned int r;
+	if (a->empty || b->empty) {
+		snd_interval_none(c);
+		return;
+	}
+	c->empty = 0;
+	c->min = muldiv32(a->min, k, b->max, &r);
+	c->openmin = (r || a->openmin || b->openmax);
+	if (b->min > 0) {
+		c->max = muldiv32(a->max, k, b->min, &r);
+		if (r) {
+			c->max++;
+			c->openmax = 1;
+		} else
+			c->openmax = (a->openmax || b->openmin);
+	} else {
+		c->max = UINT_MAX;
+		c->openmax = 0;
+	}
+	c->integer = 0;
+}
+
+#undef assert
+/* ---- */
+
+
+int snd_interval_ratnum(snd_interval_t *i,
+		    unsigned int rats_count, ratnum_t *rats,
+		    unsigned int *nump, unsigned int *denp)
+{
+	unsigned int best_num, best_diff, best_den;
+	unsigned int k;
+	snd_interval_t t;
+	int err;
+
+	best_num = best_den = best_diff = 0;
+	for (k = 0; k < rats_count; ++k) {
+		unsigned int num = rats[k].num;
+		unsigned int den;
+		unsigned int q = i->min;
+		int diff;
+		if (q == 0)
+			q = 1;
+		den = div_down(num, q);
+		if (den < rats[k].den_min)
+			continue;
+		if (den > rats[k].den_max)
+			den = rats[k].den_max;
+		else {
+			unsigned int r;
+			r = (den - rats[k].den_min) % rats[k].den_step;
+			if (r != 0)
+				den -= r;
+		}
+		diff = num - q * den;
+		if (best_num == 0 ||
+		    diff * best_den < best_diff * den) {
+			best_diff = diff;
+			best_den = den;
+			best_num = num;
+		}
+	}
+	if (best_den == 0) {
+		i->empty = 1;
+		return -EINVAL;
+	}
+	t.min = div_down(best_num, best_den);
+	t.openmin = !!(best_num % best_den);
+	
+	best_num = best_den = best_diff = 0;
+	for (k = 0; k < rats_count; ++k) {
+		unsigned int num = rats[k].num;
+		unsigned int den;
+		unsigned int q = i->max;
+		int diff;
+		if (q == 0) {
+			i->empty = 1;
+			return -EINVAL;
+		}
+		den = div_up(num, q);
+		if (den > rats[k].den_max)
+			continue;
+		if (den < rats[k].den_min)
+			den = rats[k].den_min;
+		else {
+			unsigned int r;
+			r = (den - rats[k].den_min) % rats[k].den_step;
+			if (r != 0)
+				den += rats[k].den_step - r;
+		}
+		diff = q * den - num;
+		if (best_num == 0 ||
+		    diff * best_den < best_diff * den) {
+			best_diff = diff;
+			best_den = den;
+			best_num = num;
+		}
+	}
+	if (best_den == 0) {
+		i->empty = 1;
+		return -EINVAL;
+	}
+	t.max = div_up(best_num, best_den);
+	t.openmax = !!(best_num % best_den);
+	t.integer = 0;
+	err = snd_interval_refine(i, &t);
+	if (err < 0)
+		return err;
+
+	if (snd_interval_single(i)) {
+		if (nump)
+			*nump = best_num;
+		if (denp)
+			*denp = best_den;
+	}
+	return err;
+}
+
+int snd_interval_ratden(snd_interval_t *i,
+		    unsigned int rats_count, ratden_t *rats,
+		    unsigned int *nump, unsigned int *denp)
+{
+	unsigned int best_num, best_diff, best_den;
+	unsigned int k;
+	snd_interval_t t;
+	int err;
+
+	best_num = best_den = best_diff = 0;
+	for (k = 0; k < rats_count; ++k) {
+		unsigned int num;
+		unsigned int den = rats[k].den;
+		unsigned int q = i->min;
+		int diff;
+		num = mul(q, den);
+		if (num > rats[k].num_max)
+			continue;
+		if (num < rats[k].num_min)
+			num = rats[k].num_max;
+		else {
+			unsigned int r;
+			r = (num - rats[k].num_min) % rats[k].num_step;
+			if (r != 0)
+				num += rats[k].num_step - r;
+		}
+		diff = num - q * den;
+		if (best_num == 0 ||
+		    diff * best_den < best_diff * den) {
+			best_diff = diff;
+			best_den = den;
+			best_num = num;
+		}
+	}
+	if (best_den == 0) {
+		i->empty = 1;
+		return -EINVAL;
+	}
+	t.min = div_down(best_num, best_den);
+	t.openmin = !!(best_num % best_den);
+	
+	best_num = best_den = best_diff = 0;
+	for (k = 0; k < rats_count; ++k) {
+		unsigned int num;
+		unsigned int den = rats[k].den;
+		unsigned int q = i->max;
+		int diff;
+		num = mul(q, den);
+		if (num < rats[k].num_min)
+			continue;
+		if (num > rats[k].num_max)
+			num = rats[k].num_max;
+		else {
+			unsigned int r;
+			r = (num - rats[k].num_min) % rats[k].num_step;
+			if (r != 0)
+				num -= r;
+		}
+		diff = q * den - num;
+		if (best_num == 0 ||
+		    diff * best_den < best_diff * den) {
+			best_diff = diff;
+			best_den = den;
+			best_num = num;
+		}
+	}
+	if (best_den == 0) {
+		i->empty = 1;
+		return -EINVAL;
+	}
+	t.max = div_up(best_num, best_den);
+	t.openmax = !!(best_num % best_den);
+	t.integer = 0;
+	err = snd_interval_refine(i, &t);
+	if (err < 0)
+		return err;
+
+	if (snd_interval_single(i)) {
+		if (nump)
+			*nump = best_num;
+		if (denp)
+			*denp = best_den;
+	}
+	return err;
+}
+
+int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask)
+{
+        unsigned int k;
+	int changed = 0;
+        for (k = 0; k < count; k++) {
+		if (mask && !(mask & (1 << k)))
+			continue;
+                if (i->min == list[k] && !i->openmin)
+                        goto _l1;
+                if (i->min < list[k]) {
+                        i->min = list[k];
+			i->openmin = 0;
+			changed = 1;
+                        goto _l1;
+                }
+        }
+        i->empty = 1;
+        return -EINVAL;
+ _l1:
+        for (k = count; k-- > 0;) {
+		if (mask && !(mask & (1 << k)))
+			continue;
+                if (i->max == list[k] && !i->openmax)
+                        goto _l2;
+                if (i->max > list[k]) {
+                        i->max = list[k];
+			i->openmax = 0;
+			changed = 1;
+                        goto _l2;
+                }
+        }
+        i->empty = 1;
+        return -EINVAL;
+ _l2:
+	if (snd_interval_checkempty(i)) {
+		i->empty = 1;
+		return -EINVAL;
+	}
+        return changed;
+}
+
+int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step)
+{
+	unsigned int n;
+	int changed = 0;
+	n = (i->min - min) % step;
+	if (n != 0 || i->openmin) {
+		i->min += step - n;
+		changed = 1;
+	}
+	n = (i->max - min) % step;
+	if (n != 0 || i->openmax) {
+		i->max -= n;
+		changed = 1;
+	}
+	if (snd_interval_checkempty(i)) {
+		i->empty = 1;
+		return -EINVAL;
+	}
+	return changed;
+}
+
+/* Info constraints helpers */
+
+int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond,
+			int var,
+			snd_pcm_hw_rule_func_t func, void *private,
+			int dep, ...)
+{
+	snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+	snd_pcm_hw_rule_t *c;
+	unsigned int k;
+	va_list args;
+	va_start(args, dep);
+	if (constrs->rules_num >= constrs->rules_all) {
+		snd_pcm_hw_rule_t *old = constrs->rules;
+		if (constrs->rules_all == 0)
+			constrs->rules_all = 32;
+		else {
+			old = constrs->rules;
+			constrs->rules_all += 10;
+		}
+		constrs->rules = snd_kcalloc(constrs->rules_all * sizeof(*c),
+					     GFP_KERNEL);
+		if (!constrs->rules)
+			return -ENOMEM;
+		if (old) {
+			memcpy(constrs->rules, old,
+			       constrs->rules_num * sizeof(*c));
+			kfree(old);
+		}
+	}
+	c = &constrs->rules[constrs->rules_num];
+	c->cond = cond;
+	c->func = func;
+	c->var = var;
+	c->private = private;
+	k = 0;
+	while (1) {
+		snd_assert(k < sizeof(c->deps) / sizeof(c->deps[0]), return -EINVAL);
+		c->deps[k++] = dep;
+		if (dep < 0)
+			break;
+		dep = va_arg(args, int);
+	}
+	constrs->rules_num++;
+	va_end(args);
+	return 0;
+}				    
+
+int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+			       unsigned int mask)
+{
+	snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+	unsigned int *maskp = constrs_mask(constrs, var);
+	*maskp &= mask;
+	if (*maskp == 0)
+		return -EINVAL;
+	return 0;
+}
+
+int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var)
+{
+	snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+	return snd_interval_setinteger(constrs_interval(constrs, var));
+}
+
+int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+				 unsigned int min, unsigned int max)
+{
+	snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+	snd_interval_t t;
+	t.min = min;
+	t.max = max;
+	t.openmin = t.openmax = 0;
+	t.integer = 0;
+	return snd_interval_refine(constrs_interval(constrs, var), &t);
+}
+
+static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params,
+				snd_pcm_hw_rule_t *rule)
+{
+	snd_pcm_hw_constraint_list_t *list = rule->private;
+	return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask);
+}		
+
+
+int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime,
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var,
+			       snd_pcm_hw_constraint_list_t *l)
+{
+	return snd_pcm_hw_rule_add(runtime, cond, var,
+				   snd_pcm_hw_rule_list, l,
+				   var, -1);
+}
+
+static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params,
+				   snd_pcm_hw_rule_t *rule)
+{
+	snd_pcm_hw_constraint_ratnums_t *r = rule->private;
+	unsigned int num = 0, den = 0;
+	int err;
+	err = snd_interval_ratnum(hw_param_interval(params, rule->var),
+				  r->nrats, r->rats, &num, &den);
+	if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+		params->rate_num = num;
+		params->rate_den = den;
+	}
+	return err;
+}
+
+int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, 
+				  unsigned int cond,
+				  snd_pcm_hw_param_t var,
+				  snd_pcm_hw_constraint_ratnums_t *r)
+{
+	return snd_pcm_hw_rule_add(runtime, cond, var,
+				   snd_pcm_hw_rule_ratnums, r,
+				   var, -1);
+}
+
+static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params,
+				   snd_pcm_hw_rule_t *rule)
+{
+	snd_pcm_hw_constraint_ratdens_t *r = rule->private;
+	unsigned int num = 0, den = 0;
+	int err = snd_interval_ratden(hw_param_interval(params, rule->var),
+				  r->nrats, r->rats, &num, &den);
+	if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+		params->rate_num = num;
+		params->rate_den = den;
+	}
+	return err;
+}
+
+int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, 
+				  unsigned int cond,
+				  snd_pcm_hw_param_t var,
+				  snd_pcm_hw_constraint_ratdens_t *r)
+{
+	return snd_pcm_hw_rule_add(runtime, cond, var,
+				   snd_pcm_hw_rule_ratdens, r,
+				   var, -1);
+}
+
+static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params,
+				  snd_pcm_hw_rule_t *rule)
+{
+	unsigned int l = (unsigned long) rule->private;
+	unsigned int width = l & 0xffff;
+	unsigned int msbits = l >> 16;
+	snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+	if (snd_interval_single(i) && snd_interval_value(i) == width)
+		params->msbits = msbits;
+	return 0;
+}
+
+int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, 
+				 unsigned int cond,
+				 unsigned int width,
+				 unsigned int msbits)
+{
+	unsigned long l = (msbits << 16) | width;
+	return snd_pcm_hw_rule_add(runtime, cond, -1,
+				    snd_pcm_hw_rule_msbits,
+				    (void*) l,
+				    SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+}
+
+static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params,
+				snd_pcm_hw_rule_t *rule)
+{
+	unsigned long step = (unsigned long) rule->private;
+	return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
+}
+
+int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime,
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var,
+			       unsigned long step)
+{
+	return snd_pcm_hw_rule_add(runtime, cond, var, 
+				   snd_pcm_hw_rule_step, (void *) step,
+				   var, -1);
+}
+
+
+/* To use the same code we have in alsa-lib */
+#define snd_pcm_t snd_pcm_substream_t
+#define assert(i) snd_assert((i), return -EINVAL)
+#ifndef INT_MIN
+#define INT_MIN ((int)((unsigned int)INT_MAX+1))
+#endif
+
+void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
+{
+	if (hw_is_mask(var)) {
+		snd_mask_any(hw_param_mask(params, var));
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+		return;
+	}
+	if (hw_is_interval(var)) {
+		snd_interval_any(hw_param_interval(params, var));
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+		return;
+	}
+	snd_BUG();
+}
+
+int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+			 snd_pcm_hw_param_t var)
+{
+	_snd_pcm_hw_param_any(params, var);
+	return snd_pcm_hw_refine(pcm, params);
+}
+
+void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
+{
+	unsigned int k;
+	memset(params, 0, sizeof(*params));
+	for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST; k++)
+		_snd_pcm_hw_param_any(params, k);
+	params->info = ~0U;
+}
+
+/* Fill PARAMS with full configuration space boundaries */
+int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+	_snd_pcm_hw_params_any(params);
+	return snd_pcm_hw_refine(pcm, params);
+}
+
+/* Return the value for field PAR if it's fixed in configuration space 
+   defined by PARAMS. Return -EINVAL otherwise
+*/
+int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params,
+			   snd_pcm_hw_param_t var, int *dir)
+{
+	if (hw_is_mask(var)) {
+		const snd_mask_t *mask = hw_param_mask_c(params, var);
+		if (!snd_mask_single(mask))
+			return -EINVAL;
+		if (dir)
+			*dir = 0;
+		return snd_mask_value(mask);
+	}
+	if (hw_is_interval(var)) {
+		const snd_interval_t *i = hw_param_interval_c(params, var);
+		if (!snd_interval_single(i))
+			return -EINVAL;
+		if (dir)
+			*dir = i->openmin;
+		return snd_interval_value(i);
+	}
+	assert(0);
+	return -EINVAL;
+}
+
+/* Return the minimum value for field PAR. */
+unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params,
+					snd_pcm_hw_param_t var, int *dir)
+{
+	if (hw_is_mask(var)) {
+		if (dir)
+			*dir = 0;
+		return snd_mask_min(hw_param_mask_c(params, var));
+	}
+	if (hw_is_interval(var)) {
+		const snd_interval_t *i = hw_param_interval_c(params, var);
+		if (dir)
+			*dir = i->openmin;
+		return snd_interval_min(i);
+	}
+	assert(0);
+	return -EINVAL;
+}
+
+/* Return the maximum value for field PAR. */
+unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params,
+					snd_pcm_hw_param_t var, int *dir)
+{
+	if (hw_is_mask(var)) {
+		if (dir)
+			*dir = 0;
+		return snd_mask_max(hw_param_mask_c(params, var));
+	}
+	if (hw_is_interval(var)) {
+		const snd_interval_t *i = hw_param_interval_c(params, var);
+		if (dir)
+			*dir = - (int) i->openmax;
+		return snd_interval_max(i);
+	}
+	assert(0);
+	return -EINVAL;
+}
+
+void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params,
+				snd_pcm_hw_param_t var)
+{
+	if (hw_is_mask(var)) {
+		snd_mask_none(hw_param_mask(params, var));
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	} else if (hw_is_interval(var)) {
+		snd_interval_none(hw_param_interval(params, var));
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	} else {
+		snd_BUG();
+	}
+}
+
+int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var)
+{
+	int changed;
+	assert(hw_is_interval(var));
+	changed = snd_interval_setinteger(hw_param_interval(params, var));
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+	
+/* Inside configuration space defined by PARAMS remove from PAR all 
+   non integer values. Reduce configuration space accordingly.
+   Return -EINVAL if the configuration space is empty
+*/
+int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, 
+				snd_pcm_hw_params_t *params,
+				snd_pcm_hw_param_t var)
+{
+	int changed = _snd_pcm_hw_param_setinteger(params, var);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
+			    snd_pcm_hw_param_t var)
+{
+	int changed;
+	if (hw_is_mask(var))
+		changed = snd_mask_refine_first(hw_param_mask(params, var));
+	else if (hw_is_interval(var))
+		changed = snd_interval_refine_first(hw_param_interval(params, var));
+	else {
+		assert(0);
+		return -EINVAL;
+	}
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+
+
+/* Inside configuration space defined by PARAMS remove from PAR all 
+   values > minimum. Reduce configuration space accordingly.
+   Return the minimum.
+*/
+int snd_pcm_hw_param_first(snd_pcm_t *pcm, 
+			   snd_pcm_hw_params_t *params, 
+			   snd_pcm_hw_param_t var, int *dir)
+{
+	int changed = _snd_pcm_hw_param_first(params, var);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		assert(err >= 0);
+	}
+	return snd_pcm_hw_param_value(params, var, dir);
+}
+
+int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
+			   snd_pcm_hw_param_t var)
+{
+	int changed;
+	if (hw_is_mask(var))
+		changed = snd_mask_refine_last(hw_param_mask(params, var));
+	else if (hw_is_interval(var))
+		changed = snd_interval_refine_last(hw_param_interval(params, var));
+	else {
+		assert(0);
+		return -EINVAL;
+	}
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+
+
+/* Inside configuration space defined by PARAMS remove from PAR all 
+   values < maximum. Reduce configuration space accordingly.
+   Return the maximum.
+*/
+int snd_pcm_hw_param_last(snd_pcm_t *pcm, 
+			  snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, int *dir)
+{
+	int changed = _snd_pcm_hw_param_last(params, var);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		assert(err >= 0);
+	}
+	return snd_pcm_hw_param_value(params, var, dir);
+}
+
+int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+	int changed;
+	int open = 0;
+	if (dir) {
+		if (dir > 0) {
+			open = 1;
+		} else if (dir < 0) {
+			if (val > 0) {
+				open = 1;
+				val--;
+			}
+		}
+	}
+	if (hw_is_mask(var))
+		changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open);
+	else if (hw_is_interval(var))
+		changed = snd_interval_refine_min(hw_param_interval(params, var), val, open);
+	else {
+		assert(0);
+		return -EINVAL;
+	}
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+
+/* Inside configuration space defined by PARAMS remove from PAR all 
+   values < VAL. Reduce configuration space accordingly.
+   Return new minimum or -EINVAL if the configuration space is empty
+*/
+int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+			 snd_pcm_hw_param_t var, unsigned int val, int *dir)
+{
+	int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		if (err < 0)
+			return err;
+	}
+	return snd_pcm_hw_param_value_min(params, var, dir);
+}
+
+int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
+			   snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+	int changed;
+	int open = 0;
+	if (dir) {
+		if (dir < 0) {
+			open = 1;
+		} else if (dir > 0) {
+			open = 1;
+			val++;
+		}
+	}
+	if (hw_is_mask(var)) {
+		if (val == 0 && open) {
+			snd_mask_none(hw_param_mask(params, var));
+			changed = -EINVAL;
+		} else
+			changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open);
+	} else if (hw_is_interval(var))
+		changed = snd_interval_refine_max(hw_param_interval(params, var), val, open);
+	else {
+		assert(0);
+		return -EINVAL;
+	}
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+
+/* Inside configuration space defined by PARAMS remove from PAR all 
+   values >= VAL + 1. Reduce configuration space accordingly.
+   Return new maximum or -EINVAL if the configuration space is empty
+*/
+int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, unsigned int val, int *dir)
+{
+	int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		if (err < 0)
+			return err;
+	}
+	return snd_pcm_hw_param_value_max(params, var, dir);
+}
+
+int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+	int changed;
+	if (hw_is_mask(var)) {
+		snd_mask_t *m = hw_param_mask(params, var);
+		if (val == 0 && dir < 0) {
+			changed = -EINVAL;
+			snd_mask_none(m);
+		} else {
+			if (dir > 0)
+				val++;
+			else if (dir < 0)
+				val--;
+			changed = snd_mask_refine_set(hw_param_mask(params, var), val);
+		}
+	} else if (hw_is_interval(var)) {
+		snd_interval_t *i = hw_param_interval(params, var);
+		if (val == 0 && dir < 0) {
+			changed = -EINVAL;
+			snd_interval_none(i);
+		} else if (dir == 0)
+			changed = snd_interval_refine_set(i, val);
+		else {
+			snd_interval_t t;
+			t.openmin = 1;
+			t.openmax = 1;
+			t.empty = 0;
+			t.integer = 0;
+			if (dir < 0) {
+				t.min = val - 1;
+				t.max = val;
+			} else {
+				t.min = val;
+				t.max = val+1;
+			}
+			changed = snd_interval_refine(i, &t);
+		}
+	} else {
+		assert(0);
+		return -EINVAL;
+	}
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+
+/* Inside configuration space defined by PARAMS remove from PAR all 
+   values != VAL. Reduce configuration space accordingly.
+   Return VAL or -EINVAL if the configuration space is empty
+*/
+int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+			 snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+	int changed = _snd_pcm_hw_param_set(params, var, val, dir);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		if (err < 0)
+			return err;
+	}
+	return snd_pcm_hw_param_value(params, var, 0);
+}
+
+int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params,
+			   snd_pcm_hw_param_t var, const snd_mask_t *val)
+{
+	int changed;
+	assert(hw_is_mask(var));
+	changed = snd_mask_refine(hw_param_mask(params, var), val);
+	if (changed) {
+		params->cmask |= 1 << var;
+		params->rmask |= 1 << var;
+	}
+	return changed;
+}
+
+/* Inside configuration space defined by PARAMS remove from PAR all values
+   not contained in MASK. Reduce configuration space accordingly.
+   This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS,
+   SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
+   Return 0 on success or -EINVAL
+   if the configuration space is empty
+*/
+int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, const snd_mask_t *val)
+{
+	int changed = _snd_pcm_hw_param_mask(params, var, val);
+	if (changed < 0)
+		return changed;
+	if (params->rmask) {
+		int err = snd_pcm_hw_refine(pcm, params);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int boundary_sub(int a, int adir,
+			int b, int bdir,
+			int *c, int *cdir)
+{
+	adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
+	bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
+	*c = a - b;
+	*cdir = adir - bdir;
+	if (*cdir == -2) {
+		assert(*c > INT_MIN);
+		(*c)--;
+	} else if (*cdir == 2) {
+		assert(*c < INT_MAX);
+		(*c)++;
+	}
+	return 0;
+}
+
+static int boundary_lt(unsigned int a, int adir,
+		       unsigned int b, int bdir)
+{
+	assert(a > 0 || adir >= 0);
+	assert(b > 0 || bdir >= 0);
+	if (adir < 0) {
+		a--;
+		adir = 1;
+	} else if (adir > 0)
+		adir = 1;
+	if (bdir < 0) {
+		b--;
+		bdir = 1;
+	} else if (bdir > 0)
+		bdir = 1;
+	return a < b || (a == b && adir < bdir);
+}
+
+/* Return 1 if min is nearer to best than max */
+static int boundary_nearer(int min, int mindir,
+			   int best, int bestdir,
+			   int max, int maxdir)
+{
+	int dmin, dmindir;
+	int dmax, dmaxdir;
+	boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
+	boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
+	return boundary_lt(dmin, dmindir, dmax, dmaxdir);
+}
+
+/* Inside configuration space defined by PARAMS set PAR to the available value
+   nearest to VAL. Reduce configuration space accordingly.
+   This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS,
+   SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
+   Return the value found.
+ */
+int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+			  snd_pcm_hw_param_t var, unsigned int best, int *dir)
+{
+	snd_pcm_hw_params_t save;
+	int v;
+	unsigned int saved_min;
+	int last = 0;
+	int min, max;
+	int mindir, maxdir;
+	int valdir = dir ? *dir : 0;
+	/* FIXME */
+	if (best > INT_MAX)
+		best = INT_MAX;
+	min = max = best;
+	mindir = maxdir = valdir;
+	if (maxdir > 0)
+		maxdir = 0;
+	else if (maxdir == 0)
+		maxdir = -1;
+	else {
+		maxdir = 1;
+		max--;
+	}
+	save = *params;
+	saved_min = min;
+	min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
+	if (min >= 0) {
+		snd_pcm_hw_params_t params1;
+		if (max < 0)
+			goto _end;
+		if ((unsigned int)min == saved_min && mindir == valdir)
+			goto _end;
+		params1 = save;
+		max = snd_pcm_hw_param_max(pcm, &params1, var, max, &maxdir);
+		if (max < 0)
+			goto _end;
+		if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
+			*params = params1;
+			last = 1;
+		}
+	} else {
+		*params = save;
+		max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
+		assert(max >= 0);
+		last = 1;
+	}
+ _end:
+	if (last)
+		v = snd_pcm_hw_param_last(pcm, params, var, dir);
+	else
+		v = snd_pcm_hw_param_first(pcm, params, var, dir);
+	assert(v >= 0);
+	return v;
+}
+
+/* Choose one configuration from configuration space defined by PARAMS
+   The configuration choosen is that obtained fixing in this order:
+   first access
+   first format
+   first subformat
+   min channels
+   min rate
+   min period time
+   max buffer size
+   min tick time
+*/
+int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+	int err;
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0);
+	assert(err >= 0);
+
+	err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, 0);
+	assert(err >= 0);
+
+	return 0;
+}
+
+#undef snd_pcm_t
+#undef assert
+
+static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream,
+				   void *arg)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (snd_pcm_running(substream) &&
+	    snd_pcm_update_hw_ptr(substream) >= 0) {
+		runtime->status->hw_ptr %= runtime->buffer_size;
+		return 0;
+	}
+	runtime->status->hw_ptr = 0;
+	return 0;
+}
+
+static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream,
+					  void *arg)
+{
+	snd_pcm_channel_info_t *info = arg;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int width;
+	if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) {
+		info->offset = -1;
+		return 0;
+	}
+	width = snd_pcm_format_physical_width(runtime->format);
+	if (width < 0)
+		return width;
+	info->offset = 0;
+	switch (runtime->access) {
+	case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+	case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+		info->first = info->channel * width;
+		info->step = runtime->channels * width;
+		break;
+	case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED:
+	case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED:
+	{
+		size_t size = runtime->dma_bytes / runtime->channels;
+		info->first = info->channel * size * 8;
+		info->step = width;
+		break;
+	}
+	default:
+		snd_BUG();
+		break;
+	}
+	return 0;
+}
+
+int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream,
+		      unsigned int cmd, void *arg)
+{
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_INFO:
+		return 0;
+	case SNDRV_PCM_IOCTL1_RESET:
+		return snd_pcm_lib_ioctl_reset(substream, arg);
+	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+		return snd_pcm_lib_ioctl_channel_info(substream, arg);
+	}
+	return -ENXIO;
+}
+
+/*
+ *  Conditions
+ */
+
+int snd_pcm_playback_ready(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min;
+}
+
+int snd_pcm_capture_ready(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min;
+}
+
+int snd_pcm_playback_data(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return snd_pcm_playback_avail(runtime) < runtime->buffer_size;
+}
+
+int snd_pcm_playback_empty(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return snd_pcm_playback_avail(runtime) >= runtime->buffer_size;
+}
+
+int snd_pcm_capture_empty(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return snd_pcm_capture_avail(runtime) == 0;
+}
+
+static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream, 
+				    unsigned long ticks)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (ticks == 0)
+		del_timer(&runtime->tick_timer);
+	else {
+		ticks /= (1000000 / HZ);
+		if (ticks % (1000000 / HZ))
+			ticks++;
+		mod_timer(&runtime->tick_timer, jiffies + ticks);
+	}
+}
+
+/* Temporary alias */
+void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks)
+{
+	snd_pcm_system_tick_set(substream, ticks);
+}
+
+void snd_pcm_tick_prepare(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t frames = ULONG_MAX;
+	snd_pcm_uframes_t avail, dist;
+	unsigned int ticks;
+	u_int64_t n;
+	u_int32_t r;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (runtime->silence_size > 0 &&
+		    runtime->silenced_size < runtime->buffer_size) {
+			snd_pcm_sframes_t noise_dist;
+			noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size;
+			snd_assert(noise_dist <= runtime->silence_threshold, );
+			frames = noise_dist - runtime->silence_threshold;
+		}
+		avail = snd_pcm_playback_avail(runtime);
+	} else {
+		avail = snd_pcm_capture_avail(runtime);
+	}
+	if (avail < runtime->control->avail_min) {
+		snd_pcm_sframes_t n = runtime->control->avail_min - avail;
+		if (n > 0 && frames > n)
+			frames = n;
+	}
+	if (avail < runtime->buffer_size) {
+		snd_pcm_sframes_t n = runtime->buffer_size - avail;
+		if (n > 0 && frames > n)
+			frames = n;
+	}
+	if (frames == ULONG_MAX) {
+		snd_pcm_tick_set(substream, 0);
+		return;
+	}
+	dist = runtime->status->hw_ptr - runtime->hw_ptr_base;
+	/* Distance to next interrupt */
+	dist = runtime->period_size - dist % runtime->period_size;
+	if (dist <= frames) {
+		snd_pcm_tick_set(substream, 0);
+		return;
+	}
+	/* the base time is us */
+	n = frames;
+	n *= 1000000;
+	div64_32(&n, runtime->tick_time * runtime->rate, &r);
+	ticks = n + (r > 0 ? 1 : 0);
+	if (ticks < runtime->sleep_min)
+		ticks = runtime->sleep_min;
+	snd_pcm_tick_set(substream, (unsigned long) ticks);
+}
+
+void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime;
+	snd_assert(substream != NULL, return);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return);
+
+	spin_lock_irq(&runtime->lock);
+	if (!snd_pcm_running(substream) ||
+	    snd_pcm_update_hw_ptr(substream) < 0)
+		goto _end;
+	if (runtime->sleep_min)
+		snd_pcm_tick_prepare(substream);
+ _end:
+	spin_unlock_irq(&runtime->lock);
+}
+
+void snd_pcm_period_elapsed(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime;
+	snd_assert(substream != NULL, return);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return);
+
+	if (runtime->transfer_ack_begin)
+		runtime->transfer_ack_begin(substream);
+
+	spin_lock(&runtime->lock);
+	if (!snd_pcm_running(substream) ||
+	    snd_pcm_update_hw_ptr_interrupt(substream) < 0)
+		goto _end;
+
+	if (substream->timer_running)
+		snd_timer_interrupt(substream->timer, 1);
+	if (runtime->sleep_min)
+		snd_pcm_tick_prepare(substream);
+ _end:
+	spin_unlock(&runtime->lock);
+	if (runtime->transfer_ack_end)
+		runtime->transfer_ack_end(substream);
+	kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+}
+
+static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream,
+				      unsigned int hwoff,
+				      void *data, unsigned int off,
+				      snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	char *buf = (char *) data + frames_to_bytes(runtime, off);
+	if (substream->ops->copy) {
+		if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
+			return err;
+	} else {
+		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+		snd_assert(runtime->dma_area, return -EFAULT);
+		if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
+			return -EFAULT;
+	}
+	return 0;
+}
+ 
+typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff,
+			  void *data, unsigned int off, snd_pcm_uframes_t size);
+
+static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, 
+					    const void *data, snd_pcm_uframes_t size,
+					    int nonblock,
+					    transfer_f transfer)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t xfer = 0;
+	snd_pcm_uframes_t offset = 0;
+	int err = 0;
+
+	if (size == 0)
+		return 0;
+	if (size > runtime->xfer_align)
+		size -= size % runtime->xfer_align;
+
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_PREPARED:
+	case SNDRV_PCM_STATE_RUNNING:
+	case SNDRV_PCM_STATE_PAUSED:
+		break;
+	case SNDRV_PCM_STATE_XRUN:
+		err = -EPIPE;
+		goto _end_unlock;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		err = -ESTRPIPE;
+		goto _end_unlock;
+	default:
+		err = -EBADFD;
+		goto _end_unlock;
+	}
+
+	while (size > 0) {
+		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
+		snd_pcm_uframes_t avail;
+		snd_pcm_uframes_t cont;
+		if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+			snd_pcm_update_hw_ptr(substream);
+		avail = snd_pcm_playback_avail(runtime);
+		if (runtime->status->state == SNDRV_PCM_STATE_PAUSED ||
+		    runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+			if (avail < runtime->xfer_align) {
+				err = -EPIPE;
+				goto _end_unlock;
+			}
+		} else if (((avail < runtime->control->avail_min && size > avail) ||
+			    (size >= runtime->xfer_align && avail < runtime->xfer_align))) {
+			wait_queue_t wait;
+			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state;
+			if (nonblock) {
+				err = -EAGAIN;
+				goto _end_unlock;
+			}
+
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&runtime->sleep, &wait);
+			while (1) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				if (signal_pending(current)) {
+					state = SIGNALED;
+					break;
+				}
+				spin_unlock_irq(&runtime->lock);
+				if (schedule_timeout(10 * HZ) == 0) {
+					spin_lock_irq(&runtime->lock);
+					state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+					break;
+				}
+				spin_lock_irq(&runtime->lock);
+				switch (runtime->status->state) {
+				case SNDRV_PCM_STATE_XRUN:
+					state = ERROR;
+					goto _end_loop;
+				case SNDRV_PCM_STATE_SUSPENDED:
+					state = SUSPENDED;
+					goto _end_loop;
+				default:
+					break;
+				}
+				avail = snd_pcm_playback_avail(runtime);
+				if (avail >= runtime->control->avail_min) {
+					state = READY;
+					break;
+				}
+			}
+		       _end_loop:
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&runtime->sleep, &wait);
+
+			switch (state) {
+			case ERROR:
+				err = -EPIPE;
+				goto _end_unlock;
+			case SUSPENDED:
+				err = -ESTRPIPE;
+				goto _end_unlock;
+			case SIGNALED:
+				err = -ERESTARTSYS;
+				goto _end_unlock;
+			case EXPIRED:
+				snd_printd("playback write error (DMA or IRQ trouble?)\n");
+				err = -EIO;
+				goto _end_unlock;
+			default:
+				break;
+			}
+		}
+		if (avail > runtime->xfer_align)
+			avail -= avail % runtime->xfer_align;
+		frames = size > avail ? avail : size;
+		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+		if (frames > cont)
+			frames = cont;
+		if (frames == 0 && runtime->status->state == SNDRV_PCM_STATE_PAUSED) {
+			err = -EPIPE;
+			goto _end;
+		}
+		snd_assert(frames != 0,
+			   spin_unlock_irq(&runtime->lock);
+			   return -EINVAL);
+		appl_ptr = runtime->control->appl_ptr;
+		appl_ofs = appl_ptr % runtime->buffer_size;
+		spin_unlock_irq(&runtime->lock);
+		if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0)
+			goto _end;
+		spin_lock_irq(&runtime->lock);
+		switch (runtime->status->state) {
+		case SNDRV_PCM_STATE_XRUN:
+			err = -EPIPE;
+			goto _end_unlock;
+		case SNDRV_PCM_STATE_SUSPENDED:
+			err = -ESTRPIPE;
+			goto _end_unlock;
+		default:
+			break;
+		}
+		appl_ptr += frames;
+		if (appl_ptr >= runtime->boundary) {
+			runtime->control->appl_ptr = 0;
+		} else {
+			runtime->control->appl_ptr = appl_ptr;
+		}
+
+		offset += frames;
+		size -= frames;
+		xfer += frames;
+		if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
+		    snd_pcm_playback_hw_avail(runtime) >= runtime->start_threshold) {
+			err = snd_pcm_start(substream);
+			if (err < 0)
+				goto _end_unlock;
+		}
+		if (runtime->sleep_min &&
+		    runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+			snd_pcm_tick_prepare(substream);
+	}
+ _end_unlock:
+	spin_unlock_irq(&runtime->lock);
+ _end:
+	return xfer > 0 ? xfer : err;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void *buf, snd_pcm_uframes_t size)
+{
+	snd_pcm_runtime_t *runtime;
+	int nonblock;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+
+	snd_assert(substream->ffile != NULL, return -ENXIO);
+	nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#ifdef CONFIG_SND_OSSEMUL
+	if (substream->oss.oss) {
+		snd_pcm_oss_setup_t *setup = substream->oss.setup;
+		if (setup != NULL) {
+			if (setup->nonblock)
+				nonblock = 1;
+			else if (setup->block)
+				nonblock = 0;
+		}
+	}
+#endif
+
+	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+		return -EINVAL;
+	return snd_pcm_lib_write1(substream, buf, size, nonblock,
+				  snd_pcm_lib_write_transfer);
+}
+
+static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream,
+				       unsigned int hwoff,
+				       void *data, unsigned int off,
+				       snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	void **bufs = data;
+	int channels = runtime->channels;
+	int c;
+	if (substream->ops->copy) {
+		snd_assert(substream->ops->silence != NULL, return -EINVAL);
+		for (c = 0; c < channels; ++c, ++bufs) {
+			if (*bufs == NULL) {
+				if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
+					return err;
+			} else {
+				char *buf = *bufs + samples_to_bytes(runtime, off);
+				if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
+					return err;
+			}
+		}
+	} else {
+		/* default transfer behaviour */
+		size_t dma_csize = runtime->dma_bytes / channels;
+		snd_assert(runtime->dma_area, return -EFAULT);
+		for (c = 0; c < channels; ++c, ++bufs) {
+			char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
+			if (*bufs == NULL) {
+				snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
+			} else {
+				char *buf = *bufs + samples_to_bytes(runtime, off);
+				if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
+					return -EFAULT;
+			}
+		}
+	}
+	return 0;
+}
+ 
+snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, void **bufs,
+				     snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime;
+	int nonblock;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+
+	snd_assert(substream->ffile != NULL, return -ENXIO);
+	nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#ifdef CONFIG_SND_OSSEMUL
+	if (substream->oss.oss) {
+		snd_pcm_oss_setup_t *setup = substream->oss.setup;
+		if (setup != NULL) {
+			if (setup->nonblock)
+				nonblock = 1;
+			else if (setup->block)
+				nonblock = 0;
+		}
+	}
+#endif
+
+	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+		return -EINVAL;
+	return snd_pcm_lib_write1(substream, bufs, frames, nonblock,
+				  snd_pcm_lib_writev_transfer);
+}
+
+static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream, 
+				     unsigned int hwoff,
+				     void *data, unsigned int off,
+				     snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	char *buf = (char *) data + frames_to_bytes(runtime, off);
+	if (substream->ops->copy) {
+		if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
+			return err;
+	} else {
+		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+		snd_assert(runtime->dma_area, return -EFAULT);
+		if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void *data, snd_pcm_uframes_t size, int nonblock,
+					   transfer_f transfer)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t xfer = 0;
+	snd_pcm_uframes_t offset = 0;
+	int err = 0;
+
+	if (size == 0)
+		return 0;
+	if (size > runtime->xfer_align)
+		size -= size % runtime->xfer_align;
+
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_PREPARED:
+		if (size >= runtime->start_threshold) {
+			err = snd_pcm_start(substream);
+			if (err < 0)
+				goto _end_unlock;
+		}
+		break;
+	case SNDRV_PCM_STATE_DRAINING:
+	case SNDRV_PCM_STATE_RUNNING:
+	case SNDRV_PCM_STATE_PAUSED:
+		break;
+	case SNDRV_PCM_STATE_XRUN:
+		err = -EPIPE;
+		goto _end_unlock;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		err = -ESTRPIPE;
+		goto _end_unlock;
+	default:
+		err = -EBADFD;
+		goto _end_unlock;
+	}
+
+	while (size > 0) {
+		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
+		snd_pcm_uframes_t avail;
+		snd_pcm_uframes_t cont;
+		if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+			snd_pcm_update_hw_ptr(substream);
+		avail = snd_pcm_capture_avail(runtime);
+		if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) {
+			if (avail < runtime->xfer_align) {
+				err = -EPIPE;
+				goto _end_unlock;
+			}
+		} else if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+			if (avail < runtime->xfer_align) {
+				runtime->status->state = SNDRV_PCM_STATE_SETUP;
+				err = -EPIPE;
+				goto _end_unlock;
+			}
+		} else if ((avail < runtime->control->avail_min && size > avail) ||
+			   (size >= runtime->xfer_align && avail < runtime->xfer_align)) {
+			wait_queue_t wait;
+			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state;
+			if (nonblock) {
+				err = -EAGAIN;
+				goto _end_unlock;
+			}
+
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&runtime->sleep, &wait);
+			while (1) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				if (signal_pending(current)) {
+					state = SIGNALED;
+					break;
+				}
+				spin_unlock_irq(&runtime->lock);
+				if (schedule_timeout(10 * HZ) == 0) {
+					spin_lock_irq(&runtime->lock);
+					state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+					break;
+				}
+				spin_lock_irq(&runtime->lock);
+				switch (runtime->status->state) {
+				case SNDRV_PCM_STATE_XRUN:
+					state = ERROR;
+					goto _end_loop;
+				case SNDRV_PCM_STATE_SUSPENDED:
+					state = SUSPENDED;
+					goto _end_loop;
+				default:
+					break;
+				}
+				avail = snd_pcm_capture_avail(runtime);
+				if (avail >= runtime->control->avail_min) {
+					state = READY;
+					break;
+				}
+			}
+		       _end_loop:
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&runtime->sleep, &wait);
+
+			switch (state) {
+			case ERROR:
+				err = -EPIPE;
+				goto _end_unlock;
+			case SUSPENDED:
+				err = -ESTRPIPE;
+				goto _end_unlock;
+			case SIGNALED:
+				err = -ERESTARTSYS;
+				goto _end_unlock;
+			case EXPIRED:
+				snd_printd("capture read error (DMA or IRQ trouble?)\n");
+				err = -EIO;
+				goto _end_unlock;
+			default:
+				break;
+			}
+		}
+		if (avail > runtime->xfer_align)
+			avail -= avail % runtime->xfer_align;
+		frames = size > avail ? avail : size;
+		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+		if (frames > cont)
+			frames = cont;
+		snd_assert(frames != 0,
+			   spin_unlock_irq(&runtime->lock);
+			   return -EINVAL);
+		appl_ptr = runtime->control->appl_ptr;
+		appl_ofs = appl_ptr % runtime->buffer_size;
+		spin_unlock_irq(&runtime->lock);
+		if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0)
+			goto _end;
+		spin_lock_irq(&runtime->lock);
+		switch (runtime->status->state) {
+		case SNDRV_PCM_STATE_XRUN:
+			err = -EPIPE;
+			goto _end_unlock;
+		case SNDRV_PCM_STATE_SUSPENDED:
+			err = -ESTRPIPE;
+			goto _end_unlock;
+		default:
+			break;
+		}
+		appl_ptr += frames;
+		if (appl_ptr >= runtime->boundary) {
+			runtime->control->appl_ptr = 0;
+		} else {
+			runtime->control->appl_ptr = appl_ptr;
+		}
+
+		offset += frames;
+		size -= frames;
+		xfer += frames;
+		if (runtime->sleep_min &&
+		    runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+			snd_pcm_tick_prepare(substream);
+	}
+ _end_unlock:
+	spin_unlock_irq(&runtime->lock);
+ _end:
+	return xfer > 0 ? xfer : err;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void *buf, snd_pcm_uframes_t size)
+{
+	snd_pcm_runtime_t *runtime;
+	int nonblock;
+	
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+
+	snd_assert(substream->ffile != NULL, return -ENXIO);
+	nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#ifdef CONFIG_SND_OSSEMUL
+	if (substream->oss.oss) {
+		snd_pcm_oss_setup_t *setup = substream->oss.setup;
+		if (setup != NULL) {
+			if (setup->nonblock)
+				nonblock = 1;
+			else if (setup->block)
+				nonblock = 0;
+		}
+	}
+#endif
+	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+		return -EINVAL;
+	return snd_pcm_lib_read1(substream, buf, size, nonblock, snd_pcm_lib_read_transfer);
+}
+
+static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream,
+				      unsigned int hwoff,
+				      void *data, unsigned int off,
+				      snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+	void **bufs = data;
+	int channels = runtime->channels;
+	int c;
+	if (substream->ops->copy) {
+		for (c = 0; c < channels; ++c, ++bufs) {
+			char *buf;
+			if (*bufs == NULL)
+				continue;
+			buf = *bufs + samples_to_bytes(runtime, off);
+			if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
+				return err;
+		}
+	} else {
+		snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
+		snd_assert(runtime->dma_area, return -EFAULT);
+		for (c = 0; c < channels; ++c, ++bufs) {
+			char *hwbuf, *buf;
+			if (*bufs == NULL)
+				continue;
+
+			hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
+			buf = *bufs + samples_to_bytes(runtime, off);
+			if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
+				return -EFAULT;
+		}
+	}
+	return 0;
+}
+ 
+snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs,
+				    snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime;
+	int nonblock;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+
+	snd_assert(substream->ffile != NULL, return -ENXIO);
+	nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#ifdef CONFIG_SND_OSSEMUL
+	if (substream->oss.oss) {
+		snd_pcm_oss_setup_t *setup = substream->oss.setup;
+		if (setup != NULL) {
+			if (setup->nonblock)
+				nonblock = 1;
+			else if (setup->block)
+				nonblock = 0;
+		}
+	}
+#endif
+
+	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+		return -EINVAL;
+	return snd_pcm_lib_read1(substream, bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
+}
+
+/*
+ *  Exported symbols
+ */
+
+EXPORT_SYMBOL(snd_interval_refine);
+EXPORT_SYMBOL(snd_interval_list);
+EXPORT_SYMBOL(snd_interval_ratnum);
+EXPORT_SYMBOL(snd_interval_ratden);
+EXPORT_SYMBOL(snd_interval_muldivk);
+EXPORT_SYMBOL(snd_interval_mulkdiv);
+EXPORT_SYMBOL(snd_interval_div);
+EXPORT_SYMBOL(_snd_pcm_hw_params_any);
+EXPORT_SYMBOL(_snd_pcm_hw_param_min);
+EXPORT_SYMBOL(_snd_pcm_hw_param_set);
+EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
+EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger);
+EXPORT_SYMBOL(snd_pcm_hw_param_value_min);
+EXPORT_SYMBOL(snd_pcm_hw_param_value_max);
+EXPORT_SYMBOL(snd_pcm_hw_param_mask);
+EXPORT_SYMBOL(snd_pcm_hw_param_first);
+EXPORT_SYMBOL(snd_pcm_hw_param_last);
+EXPORT_SYMBOL(snd_pcm_hw_param_near);
+EXPORT_SYMBOL(snd_pcm_hw_refine);
+EXPORT_SYMBOL(snd_pcm_hw_constraints_init);
+EXPORT_SYMBOL(snd_pcm_hw_constraints_complete);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
+EXPORT_SYMBOL(snd_pcm_hw_rule_add);
+EXPORT_SYMBOL(snd_pcm_set_ops);
+EXPORT_SYMBOL(snd_pcm_set_sync);
+EXPORT_SYMBOL(snd_pcm_lib_ioctl);
+EXPORT_SYMBOL(snd_pcm_playback_ready);
+EXPORT_SYMBOL(snd_pcm_capture_ready);
+EXPORT_SYMBOL(snd_pcm_playback_data);
+EXPORT_SYMBOL(snd_pcm_capture_empty);
+EXPORT_SYMBOL(snd_pcm_stop);
+EXPORT_SYMBOL(snd_pcm_period_elapsed);
+EXPORT_SYMBOL(snd_pcm_lib_write);
+EXPORT_SYMBOL(snd_pcm_lib_read);
+EXPORT_SYMBOL(snd_pcm_lib_writev);
+EXPORT_SYMBOL(snd_pcm_lib_readv);
+EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes);
+EXPORT_SYMBOL(snd_pcm_lib_period_bytes);
+/* pcm_memory.c */
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_free);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
+EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
+EXPORT_SYMBOL(snd_pcm_lib_free_pages);
+#ifdef CONFIG_ISA
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all);
+#endif
diff -Nru linux/sound/core/pcm_memory.c linux-2.4.19-pre5-mjc/sound/core/pcm_memory.c
--- linux/sound/core/pcm_memory.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/pcm_memory.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,357 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+
+static int snd_preallocate_dma = 1;
+MODULE_PARM(snd_preallocate_dma, "i");
+MODULE_PARM_DESC(snd_preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized.");
+MODULE_PARM_SYNTAX(snd_preallocate_dma, SNDRV_BOOLEAN_TRUE_DESC);
+
+static int snd_maximum_substreams = 4;
+MODULE_PARM(snd_maximum_substreams, "i");
+MODULE_PARM_DESC(snd_maximum_substreams, "Maximum substreams with preallocated DMA memory.");
+MODULE_PARM_SYNTAX(snd_maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC);
+
+static int snd_minimum_buffer = 16384;
+
+
+static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream)
+{
+	if (substream->dma_area == NULL)
+		return;
+	switch (substream->dma_type) {
+	case SNDRV_PCM_DMA_TYPE_CONTINUOUS:
+		snd_free_pages(substream->dma_area, substream->dma_bytes);
+		break;
+#ifdef CONFIG_ISA
+	case SNDRV_PCM_DMA_TYPE_ISA:
+		snd_free_isa_pages(substream->dma_bytes, substream->dma_area, substream->dma_addr);
+		break;
+#endif
+#ifdef CONFIG_PCI
+	case SNDRV_PCM_DMA_TYPE_PCI:
+		snd_free_pci_pages((struct pci_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr);
+		break;
+#endif
+	}
+	substream->dma_area = NULL;
+}
+
+int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream)
+{
+	snd_pcm_lib_preallocate_dma_free(substream);
+	if (substream->proc_prealloc_entry) {
+		snd_info_unregister(substream->proc_prealloc_entry);
+		substream->proc_prealloc_entry = NULL;
+	}
+	return 0;
+}
+
+int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm)
+{
+	snd_pcm_substream_t *substream;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++)
+		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
+			snd_pcm_lib_preallocate_free(substream);
+	return 0;
+}
+
+static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry,
+					      snd_info_buffer_t *buffer)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+	snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_bytes / 1024);
+}
+
+static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry,
+					       snd_info_buffer_t *buffer)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+	char line[64], str[64];
+	size_t size;
+	void *dma_area;
+	dma_addr_t dma_addr;
+
+	if (substream->runtime) {
+		buffer->error = -EBUSY;
+		return;
+	}
+	if (!snd_info_get_line(buffer, line, sizeof(line))) {
+		snd_info_get_str(str, line, sizeof(str));
+		size = simple_strtoul(str, NULL, 10) * 1024;
+		if ((size != 0 && size < 8192) || size > substream->dma_max) {
+			buffer->error = -EINVAL;
+			return;
+		}
+		if (substream->dma_bytes == size)
+			return;
+		if (size > 0) {
+			switch (substream->dma_type) {
+			case SNDRV_PCM_DMA_TYPE_CONTINUOUS:
+				dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff));
+				dma_addr = 0UL;		/* not valid */
+				break;
+#ifdef CONFIG_ISA
+			case SNDRV_PCM_DMA_TYPE_ISA:
+				dma_area = snd_malloc_isa_pages(size, &dma_addr);
+				break;
+#endif
+#ifdef CONFIG_PCI
+			case SNDRV_PCM_DMA_TYPE_PCI:
+				dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr);
+				break;
+#endif
+			default:
+				dma_area = NULL;
+				dma_addr = 0UL;
+			}
+			if (dma_area == NULL) {
+				buffer->error = -ENOMEM;
+				return;
+			}
+			substream->buffer_bytes_max = size;
+		} else {
+			dma_area = NULL;
+			substream->buffer_bytes_max = UINT_MAX;
+		}
+		snd_pcm_lib_preallocate_dma_free(substream);
+		substream->dma_area = dma_area;
+		substream->dma_addr = dma_addr;
+		substream->dma_bytes = size;
+	} else {
+		buffer->error = -EINVAL;
+	}
+}
+
+static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream,
+					  size_t size, size_t max)
+{
+	unsigned long rsize = 0;
+	void *dma_area = NULL;
+	dma_addr_t dma_addr = 0UL;
+	snd_info_entry_t *entry;
+
+	if (!size || !snd_preallocate_dma || substream->number >= snd_maximum_substreams) {
+		size = 0;
+	} else {
+		switch (substream->dma_type) {
+		case SNDRV_PCM_DMA_TYPE_CONTINUOUS:
+			dma_area = snd_malloc_pages_fallback(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff), &rsize);
+			dma_addr = 0UL;		/* not valid */
+			break;
+#ifdef CONFIG_ISA
+		case SNDRV_PCM_DMA_TYPE_ISA:
+			dma_area = snd_malloc_isa_pages_fallback(size, &dma_addr, &rsize);
+			break;
+#endif
+#ifdef CONFIG_PCI
+		case SNDRV_PCM_DMA_TYPE_PCI:
+			dma_area = snd_malloc_pci_pages_fallback((struct pci_dev *)substream->dma_private, size, &dma_addr, &rsize);
+			break;
+#endif
+		default:
+			size = 0;
+		}
+		if (rsize < snd_minimum_buffer) {
+			snd_pcm_lib_preallocate_dma_free(substream);
+			size = 0;
+		}
+	}
+	substream->dma_area = dma_area;
+	substream->dma_addr = dma_addr;
+	substream->dma_bytes = rsize;
+	if (rsize > 0)
+		substream->buffer_bytes_max = rsize;
+	substream->dma_max = max;
+	if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
+		entry->c.text.read_size = 64;
+		entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
+		entry->c.text.write_size = 64;
+		entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
+		entry->private_data = substream;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	substream->proc_prealloc_entry = entry;
+	return 0;
+}
+
+int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream,
+				      size_t size, size_t max,
+				      unsigned int flags)
+{
+	substream->dma_type = SNDRV_PCM_DMA_TYPE_CONTINUOUS;
+	substream->dma_private = (void *)(unsigned long)flags;
+	return snd_pcm_lib_preallocate_pages1(substream, size, max);
+}
+
+int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm,
+					  size_t size, size_t max,
+					  unsigned int flags)
+{
+	snd_pcm_substream_t *substream;
+	int stream, err;
+
+	for (stream = 0; stream < 2; stream++)
+		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
+			if ((err = snd_pcm_lib_preallocate_pages(substream, size, max, flags)) < 0)
+				return err;
+	return 0;
+}
+
+#ifdef CONFIG_ISA
+int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream,
+				      size_t size, size_t max)
+{
+	substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA;
+	substream->dma_private = NULL;
+	return snd_pcm_lib_preallocate_pages1(substream, size, max);
+}
+
+int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm,
+					      size_t size, size_t max)
+{
+	snd_pcm_substream_t *substream;
+	int stream, err;
+
+	for (stream = 0; stream < 2; stream++)
+		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
+			if ((err = snd_pcm_lib_preallocate_isa_pages(substream, size, max)) < 0)
+				return err;
+	return 0;
+}
+#endif /* CONFIG_ISA */
+
+int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size)
+{
+	snd_pcm_runtime_t *runtime;
+	void *dma_area = NULL;
+	dma_addr_t dma_addr = 0UL;
+
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);	
+	if (runtime->dma_area != NULL) {
+		/* perphaps, we might free the large DMA memory region
+		   to save some space here, but the actual solution
+		   costs us less time */
+		if (runtime->dma_bytes >= size)
+			return 0;	/* ok, do not change */
+      		snd_pcm_lib_free_pages(substream);
+	}
+	if (substream->dma_area != NULL && substream->dma_bytes >= size) {
+		dma_area = substream->dma_area;
+		dma_addr = substream->dma_addr;
+	} else {
+		switch (substream->dma_type) {
+		case SNDRV_PCM_DMA_TYPE_CONTINUOUS:
+			dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff));
+			dma_addr = 0UL;		/* not valid */
+			break;
+#ifdef CONFIG_ISA
+		case SNDRV_PCM_DMA_TYPE_ISA:
+			dma_area = snd_malloc_isa_pages(size, &dma_addr); 
+			break;
+#endif
+#ifdef CONFIG_PCI
+		case SNDRV_PCM_DMA_TYPE_PCI:
+			dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr);
+			break;
+#endif
+		default:
+			return -ENXIO;
+		}
+	}
+	if (! dma_area)
+		return -ENOMEM;
+	runtime->dma_area = dma_area;
+	runtime->dma_addr = dma_addr;
+	runtime->dma_bytes = size;
+	return 1;			/* area was changed */
+}
+
+int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime;
+
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+	if (runtime->dma_area == NULL)
+		return 0;
+	if (runtime->dma_area != substream->dma_area) {
+		switch (substream->dma_type) {
+#ifdef CONFIG_ISA
+		case SNDRV_PCM_DMA_TYPE_ISA:
+			snd_free_isa_pages(runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+			break;
+#endif
+#ifdef CONFIG_PCI
+		case SNDRV_PCM_DMA_TYPE_PCI:
+			snd_free_pci_pages((struct pci_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+			break;
+#endif
+		}
+	}
+	runtime->dma_area = NULL;
+	runtime->dma_addr = 0UL;
+	runtime->dma_bytes = 0;
+	return 0;
+}
+
+#ifdef CONFIG_PCI
+
+int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci,
+				      snd_pcm_substream_t *substream,
+				      size_t size, size_t max)
+{
+	substream->dma_type = SNDRV_PCM_DMA_TYPE_PCI;
+	substream->dma_private = pci;
+	return snd_pcm_lib_preallocate_pages1(substream, size, max);
+}
+
+int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci,
+					      snd_pcm_t *pcm,
+					      size_t size, size_t max)
+{
+	snd_pcm_substream_t *substream;
+	int stream, err;
+
+	for (stream = 0; stream < 2; stream++)
+		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
+			if ((err = snd_pcm_lib_preallocate_pci_pages(pci, substream, size, max)) < 0)
+				return err;
+	return 0;
+}
+
+#endif /* CONFIG_PCI */
diff -Nru linux/sound/core/pcm_misc.c linux-2.4.19-pre5-mjc/sound/core/pcm_misc.c
--- linux/sound/core/pcm_misc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/pcm_misc.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,442 @@
+/*
+ *  PCM Interface - misc routines
+ *  Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+  
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#define bswap_16 swab16
+#define bswap_32 swab32
+#define bswap_64 swab64
+#define SND_PCM_FORMAT_UNKNOWN (-1)
+#define snd_enum_to_int(v) (v)
+#define snd_int_to_enum(v) (v)
+
+int snd_pcm_format_signed(snd_pcm_format_t format)
+{
+	switch (snd_enum_to_int(format)) {
+	case SNDRV_PCM_FORMAT_S8:
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_BE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32_BE:
+		return 1;
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_U24_BE:
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+int snd_pcm_format_unsigned(snd_pcm_format_t format)
+{
+	int val;
+
+	val = snd_pcm_format_signed(format);
+	if (val < 0)
+		return val;
+	return !val;
+}
+
+int snd_pcm_format_linear(snd_pcm_format_t format)
+{
+	return snd_pcm_format_signed(format) >= 0;
+}
+
+int snd_pcm_format_little_endian(snd_pcm_format_t format)
+{
+	switch (snd_enum_to_int(format)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+	case SNDRV_PCM_FORMAT_FLOAT64_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		return 1;
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+	case SNDRV_PCM_FORMAT_S24_BE:
+	case SNDRV_PCM_FORMAT_U24_BE:
+	case SNDRV_PCM_FORMAT_S32_BE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+	case SNDRV_PCM_FORMAT_FLOAT_BE:
+	case SNDRV_PCM_FORMAT_FLOAT64_BE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+int snd_pcm_format_big_endian(snd_pcm_format_t format)
+{
+	int val;
+
+	val = snd_pcm_format_little_endian(format);
+	if (val < 0)
+		return val;
+	return !val;
+}
+
+int snd_pcm_format_cpu_endian(snd_pcm_format_t format)
+{
+#ifdef SNDRV_LITTLE_ENDIAN
+	return snd_pcm_format_little_endian(format);
+#else
+	return snd_pcm_format_big_endian(format);
+#endif
+}
+
+int snd_pcm_format_width(snd_pcm_format_t format)
+{
+	switch (snd_enum_to_int(format)) {
+	case SNDRV_PCM_FORMAT_S8:
+	case SNDRV_PCM_FORMAT_U8:
+		return 8;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+		return 16;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_BE:
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_U24_BE:
+		return 24;
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32_BE:
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+	case SNDRV_PCM_FORMAT_FLOAT_BE:
+		return 32;
+	case SNDRV_PCM_FORMAT_FLOAT64_LE:
+	case SNDRV_PCM_FORMAT_FLOAT64_BE:
+		return 64;
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE:
+		return 24;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+	case SNDRV_PCM_FORMAT_A_LAW:
+		return 8;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:
+		return 4;
+	default:
+		return -EINVAL;
+	}
+}
+
+int snd_pcm_format_physical_width(snd_pcm_format_t format)
+{
+	switch (snd_enum_to_int(format)) {
+	case SNDRV_PCM_FORMAT_S8:
+	case SNDRV_PCM_FORMAT_U8:
+		return 8;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+		return 16;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_BE:
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_U24_BE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32_BE:
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+	case SNDRV_PCM_FORMAT_FLOAT_BE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE:
+		return 32;
+	case SNDRV_PCM_FORMAT_FLOAT64_LE:
+	case SNDRV_PCM_FORMAT_FLOAT64_BE:
+		return 64;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+	case SNDRV_PCM_FORMAT_A_LAW:
+		return 8;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:
+		return 4;
+	default:
+		return -EINVAL;
+	}
+}
+
+ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
+{
+	switch (snd_enum_to_int(format)) {
+	case SNDRV_PCM_FORMAT_S8:
+	case SNDRV_PCM_FORMAT_U8:
+		return samples;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_U16_BE:
+		return samples * 2;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_BE:
+	case SNDRV_PCM_FORMAT_U24_LE:
+	case SNDRV_PCM_FORMAT_U24_BE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32_BE:
+	case SNDRV_PCM_FORMAT_U32_LE:
+	case SNDRV_PCM_FORMAT_U32_BE:
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+	case SNDRV_PCM_FORMAT_FLOAT_BE:
+		return samples * 4;
+	case SNDRV_PCM_FORMAT_FLOAT64_LE:
+	case SNDRV_PCM_FORMAT_FLOAT64_BE:
+		return samples * 8;
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE:
+		return samples * 4;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+	case SNDRV_PCM_FORMAT_A_LAW:
+		return samples;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:
+		if (samples & 1)
+			return -EINVAL;
+		return samples / 2;
+	default:
+		return -EINVAL;
+	}
+}
+
+u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format)
+{
+	switch (snd_enum_to_int(format)) {
+	case SNDRV_PCM_FORMAT_S8:
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S16_BE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_BE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_S32_BE:
+		return 0;
+	case SNDRV_PCM_FORMAT_U8:
+		return 0x8080808080808080ULL;
+#ifdef SNDRV_LITTLE_ENDIAN
+	case SNDRV_PCM_FORMAT_U16_LE:
+		return 0x8000800080008000ULL;
+	case SNDRV_PCM_FORMAT_U24_LE:
+		return 0x0080000000800000ULL;
+	case SNDRV_PCM_FORMAT_U32_LE:
+		return 0x8000000080000000ULL;
+	case SNDRV_PCM_FORMAT_U16_BE:
+		return 0x0080008000800080ULL;
+	case SNDRV_PCM_FORMAT_U24_BE:
+		return 0x0000800000008000ULL;
+	case SNDRV_PCM_FORMAT_U32_BE:
+		return 0x0000008000000080ULL;
+#else
+	case SNDRV_PCM_FORMAT_U16_LE:
+		return 0x0080008000800080ULL;
+	case SNDRV_PCM_FORMAT_U24_LE:
+		return 0x0000800000008000ULL;
+	case SNDRV_PCM_FORMAT_U32_LE:
+		return 0x0000008000000080ULL;
+	case SNDRV_PCM_FORMAT_U16_BE:
+		return 0x8000800080008000ULL;
+	case SNDRV_PCM_FORMAT_U24_BE:
+		return 0x0080000000800000ULL;
+	case SNDRV_PCM_FORMAT_U32_BE:
+		return 0x8000000080000000ULL;
+#endif
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+	{
+		union {
+			float f;
+			u_int32_t i;
+		} u;
+		u.f = 0.0;
+#ifdef SNDRV_LITTLE_ENDIAN
+		return u.i;
+#else
+		return bswap_32(u.i);
+#endif
+	}
+	case SNDRV_PCM_FORMAT_FLOAT64_LE:
+	{
+		union {
+			double f;
+			u_int64_t i;
+		} u;
+		u.f = 0.0;
+#ifdef SNDRV_LITTLE_ENDIAN
+		return u.i;
+#else
+		return bswap_64(u.i);
+#endif
+	}
+	case SNDRV_PCM_FORMAT_FLOAT_BE:		
+	{
+		union {
+			float f;
+			u_int32_t i;
+		} u;
+		u.f = 0.0;
+#ifdef SNDRV_LITTLE_ENDIAN
+		return bswap_32(u.i);
+#else
+		return u.i;
+#endif
+	}
+	case SNDRV_PCM_FORMAT_FLOAT64_BE:
+	{
+		union {
+			double f;
+			u_int64_t i;
+		} u;
+		u.f = 0.0;
+#ifdef SNDRV_LITTLE_ENDIAN
+		return bswap_64(u.i);
+#else
+		return u.i;
+#endif
+	}
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE:
+		return 0;	
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		return 0x7f7f7f7f7f7f7f7fULL;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		return 0x5555555555555555ULL;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:	/* special case */
+	case SNDRV_PCM_FORMAT_MPEG:
+	case SNDRV_PCM_FORMAT_GSM:
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+u_int32_t snd_pcm_format_silence_32(snd_pcm_format_t format)
+{
+	return (u_int32_t)snd_pcm_format_silence_64(format);
+}
+
+u_int16_t snd_pcm_format_silence_16(snd_pcm_format_t format)
+{
+	return (u_int16_t)snd_pcm_format_silence_64(format);
+}
+
+u_int8_t snd_pcm_format_silence(snd_pcm_format_t format)
+{
+	return (u_int8_t)snd_pcm_format_silence_64(format);
+}
+
+int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
+{
+	if (samples == 0)
+		return 0;
+	switch (snd_pcm_format_width(format)) {
+	case 4: {
+		u_int8_t silence = snd_pcm_format_silence_64(format);
+		unsigned int samples1;
+		if (samples % 2 != 0)
+			return -EINVAL;
+		samples1 = samples / 2;
+		memset(data, silence, samples1);
+		break;
+	}
+	case 8: {
+		u_int8_t silence = snd_pcm_format_silence_64(format);
+		memset(data, silence, samples);
+		break;
+	}
+	case 16: {
+		u_int16_t silence = snd_pcm_format_silence_64(format);
+		while (samples-- > 0)
+			*((u_int16_t *)data)++ = silence;
+		break;
+	}
+	case 32: {
+		u_int32_t silence = snd_pcm_format_silence_64(format);
+		while (samples-- > 0)
+			*((u_int32_t *)data)++ = silence;
+		break;
+	}
+	case 64: {
+		u_int64_t silence = snd_pcm_format_silence_64(format);
+		while (samples-- > 0)
+			*((u_int64_t *)data)++ = silence;
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int linear_formats[4*2*2] = {
+	SNDRV_PCM_FORMAT_S8,
+	SNDRV_PCM_FORMAT_S8,
+	SNDRV_PCM_FORMAT_U8,
+	SNDRV_PCM_FORMAT_U8,
+	SNDRV_PCM_FORMAT_S16_LE,
+	SNDRV_PCM_FORMAT_S16_BE,
+	SNDRV_PCM_FORMAT_U16_LE,
+	SNDRV_PCM_FORMAT_U16_BE,
+	SNDRV_PCM_FORMAT_S24_LE,
+	SNDRV_PCM_FORMAT_S24_BE,
+	SNDRV_PCM_FORMAT_U24_LE,
+	SNDRV_PCM_FORMAT_U24_BE,
+	SNDRV_PCM_FORMAT_S32_LE,
+	SNDRV_PCM_FORMAT_S32_BE,
+	SNDRV_PCM_FORMAT_U32_LE,
+	SNDRV_PCM_FORMAT_U32_BE
+};
+
+snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian)
+{
+	switch (width) {
+	case 8:
+		width = 0;
+		break;
+	case 16:
+		width = 1;
+		break;
+	case 24:
+		width = 2;
+		break;
+	case 32:
+		width = 3;
+		break;
+	default:
+		return SND_PCM_FORMAT_UNKNOWN;
+	}
+	return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]);
+}
diff -Nru linux/sound/core/pcm_native.c linux-2.4.19-pre5-mjc/sound/core/pcm_native.c
--- linux/sound/core/pcm_native.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/pcm_native.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2762 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/minors.h>
+
+spinlock_t pcm_link_lock = SPIN_LOCK_UNLOCKED;
+
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info)
+{
+	snd_pcm_runtime_t * runtime;
+	snd_pcm_t *pcm = substream->pcm;
+	snd_pcm_str_t *pstr = substream->pstr;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	memset(info, 0, sizeof(*info));
+	info->card = pcm->card->number;
+	info->device = pcm->device;
+	info->stream = substream->stream;
+	info->subdevice = substream->number;
+	strncpy(info->id, pcm->id, sizeof(info->id)-1);
+	strncpy(info->name, pcm->name, sizeof(info->name)-1);
+	info->dev_class = pcm->dev_class;
+	info->dev_subclass = pcm->dev_subclass;
+	info->subdevices_count = pstr->substream_count;
+	info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
+	strncpy(info->subname, substream->name, sizeof(info->subname)-1);
+	runtime = substream->runtime;
+	/* AB: FIXME!!! This is definitely nonsense */
+	if (runtime) {
+		info->sync = runtime->sync;
+		substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
+	}
+	return 0;
+}
+
+int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t * _info)
+{
+	snd_pcm_info_t info;
+	int err = snd_pcm_info(substream, &info);
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return err;
+}
+
+#undef RULES_DEBUG
+
+#ifdef RULES_DEBUG
+#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
+char *snd_pcm_hw_param_names[] = {
+	HW_PARAM(ACCESS),
+	HW_PARAM(FORMAT),
+	HW_PARAM(SUBFORMAT),
+	HW_PARAM(SAMPLE_BITS),
+	HW_PARAM(FRAME_BITS),
+	HW_PARAM(CHANNELS),
+	HW_PARAM(RATE),
+	HW_PARAM(PERIOD_TIME),
+	HW_PARAM(PERIOD_SIZE),
+	HW_PARAM(PERIOD_BYTES),
+	HW_PARAM(PERIODS),
+	HW_PARAM(BUFFER_TIME),
+	HW_PARAM(BUFFER_SIZE),
+	HW_PARAM(BUFFER_BYTES),
+	HW_PARAM(TICK_TIME),
+};
+#endif
+
+int snd_pcm_hw_refine(snd_pcm_substream_t *substream, 
+		      snd_pcm_hw_params_t *params)
+{
+	unsigned int k;
+	snd_pcm_hardware_t *hw;
+	snd_interval_t *i = NULL;
+	snd_mask_t *m = NULL;
+	snd_pcm_hw_constraints_t *constrs = &substream->runtime->hw_constraints;
+	unsigned int rstamps[constrs->rules_num];
+	unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST + 1];
+	unsigned int stamp = 2;
+	int changed, again;
+
+	params->info = 0;
+	params->fifo_size = 0;
+	if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
+		params->msbits = 0;
+	if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
+		params->rate_num = 0;
+		params->rate_den = 0;
+	}
+
+	for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
+		m = hw_param_mask(params, k);
+		if (snd_mask_empty(m))
+			return -EINVAL;
+		if (!(params->rmask & (1 << k)))
+			continue;
+#ifdef RULES_DEBUG
+		printk("%s = ", snd_pcm_hw_param_names[k]);
+		printk("%x -> ", *m);
+#endif
+		changed = snd_mask_refine(m, constrs_mask(constrs, k));
+#ifdef RULES_DEBUG
+		printk("%x\n", *m);
+#endif
+		if (changed)
+			params->cmask |= 1 << k;
+		if (changed < 0)
+			return changed;
+	}
+
+	for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
+		i = hw_param_interval(params, k);
+		if (snd_interval_empty(i))
+			return -EINVAL;
+		if (!(params->rmask & (1 << k)))
+			continue;
+#ifdef RULES_DEBUG
+		printk("%s = ", snd_pcm_hw_param_names[k]);
+		if (i->empty)
+			printk("empty");
+		else
+			printk("%c%u %u%c", 
+			       i->openmin ? '(' : '[', i->min,
+			       i->max, i->openmax ? ')' : ']');
+		printk(" -> ");
+#endif
+		changed = snd_interval_refine(i, constrs_interval(constrs, k));
+#ifdef RULES_DEBUG
+		if (i->empty)
+			printk("empty\n");
+		else 
+			printk("%c%u %u%c\n", 
+			       i->openmin ? '(' : '[', i->min,
+			       i->max, i->openmax ? ')' : ']');
+#endif
+		if (changed)
+			params->cmask |= 1 << k;
+		if (changed < 0)
+			return changed;
+	}
+
+	for (k = 0; k < constrs->rules_num; k++)
+		rstamps[k] = 0;
+	for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST; k++) 
+		vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
+	do {
+		again = 0;
+		for (k = 0; k < constrs->rules_num; k++) {
+			snd_pcm_hw_rule_t *r = &constrs->rules[k];
+			unsigned int d;
+			int doit = 0;
+			if (r->cond && !(r->cond & params->flags))
+				continue;
+			for (d = 0; r->deps[d] >= 0; d++) {
+				if (vstamps[r->deps[d]] > rstamps[k]) {
+					doit = 1;
+					break;
+				}
+			}
+			if (!doit)
+				continue;
+#ifdef RULES_DEBUG
+			printk("Rule %d [%p]: ", k, r->func);
+			if (r->var >= 0) {
+				printk("%s = ", snd_pcm_hw_param_names[r->var]);
+				if (hw_is_mask(r->var)) {
+					m = hw_param_mask(params, r->var);
+					printk("%x", *m);
+				} else {
+					i = hw_param_interval(params, r->var);
+					if (i->empty)
+						printk("empty");
+					else
+						printk("%c%u %u%c", 
+						       i->openmin ? '(' : '[', i->min,
+						       i->max, i->openmax ? ')' : ']');
+				}
+			}
+#endif
+			changed = r->func(params, r);
+#ifdef RULES_DEBUG
+			if (r->var >= 0) {
+				printk(" -> ");
+				if (hw_is_mask(r->var))
+					printk("%x", *m);
+				else {
+					if (i->empty)
+						printk("empty");
+					else
+						printk("%c%u %u%c", 
+						       i->openmin ? '(' : '[', i->min,
+						       i->max, i->openmax ? ')' : ']');
+				}
+			}
+			printk("\n");
+#endif
+			rstamps[k] = stamp;
+			if (changed && r->var >= 0) {
+				params->cmask |= (1 << r->var);
+				vstamps[r->var] = stamp;
+				again = 1;
+			}
+			if (changed < 0)
+				return changed;
+			stamp++;
+		}
+	} while (again);
+	if (!params->msbits) {
+		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+		if (snd_interval_single(i))
+			params->msbits = snd_interval_value(i);
+	}
+
+	if (!params->rate_den) {
+		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+		if (snd_interval_single(i)) {
+			params->rate_num = snd_interval_value(i);
+			params->rate_den = 1;
+		}
+	}
+
+	hw = &substream->runtime->hw;
+	if (!params->info)
+		params->info = hw->info;
+	if (!params->fifo_size)
+		params->fifo_size = hw->fifo_size;
+	params->rmask = 0;
+	return 0;
+}
+
+static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params)
+{
+	snd_pcm_hw_params_t params;
+	int err;
+	if (copy_from_user(&params, _params, sizeof(params)))
+		return -EFAULT;
+	err = snd_pcm_hw_refine(substream, &params);
+	if (copy_to_user(_params, &params, sizeof(params)))
+		return -EFAULT;
+	return err;
+}
+
+static int snd_pcm_hw_params(snd_pcm_substream_t *substream,
+			     snd_pcm_hw_params_t *params)
+{
+	snd_pcm_runtime_t *runtime;
+	int err;
+	unsigned int bits;
+	snd_pcm_uframes_t frames;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_OPEN:
+	case SNDRV_PCM_STATE_SETUP:
+	case SNDRV_PCM_STATE_PREPARED:
+		break;
+	default:
+		return -EBADFD;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	if (!substream->oss.oss)
+#endif
+		if (atomic_read(&runtime->mmap_count))
+			return -EBADFD;
+
+	params->rmask = ~0U;
+	err = snd_pcm_hw_refine(substream, params);
+	if (err < 0)
+		goto _error;
+
+	err = snd_pcm_hw_params_choose(substream, params);
+	if (err < 0)
+		goto _error;
+
+	if (substream->ops->hw_params != NULL) {
+		err = substream->ops->hw_params(substream, params);
+		if (err < 0)
+			goto _error;
+	}
+
+	runtime->access = params_access(params);
+	runtime->format = params_format(params);
+	runtime->subformat = params_subformat(params);
+	runtime->channels = params_channels(params);
+	runtime->rate = params_rate(params);
+	runtime->period_size = params_period_size(params);
+	runtime->periods = params_periods(params);
+	runtime->buffer_size = params_buffer_size(params);
+	runtime->tick_time = params_tick_time(params);
+	runtime->info = params->info;
+	runtime->rate_num = params->rate_num;
+	runtime->rate_den = params->rate_den;
+
+	bits = snd_pcm_format_physical_width(runtime->format);
+	runtime->sample_bits = bits;
+	bits *= runtime->channels;
+	runtime->frame_bits = bits;
+	frames = 1;
+	while (bits % 8 != 0) {
+		bits *= 2;
+		frames *= 2;
+	}
+	runtime->byte_align = bits / 8;
+	runtime->min_align = frames;
+
+	/* Default sw params */
+	runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+	runtime->period_step = 1;
+	runtime->sleep_min = 0;
+	runtime->control->avail_min = runtime->period_size;
+	runtime->xfer_align = runtime->period_size;
+	runtime->start_threshold = 1;
+	runtime->stop_threshold = runtime->buffer_size;
+	runtime->silence_threshold = 0;
+	runtime->silence_size = 0;
+	runtime->boundary = runtime->buffer_size;
+	while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
+		runtime->boundary *= 2;
+
+	snd_pcm_timer_resolution_change(substream);
+	runtime->status->state = SNDRV_PCM_STATE_SETUP;
+	return 0;
+ _error:
+	/* hardware might be unuseable from this time,
+	   so we force application to retry to set
+	   the correct hardware parameter settings */
+	runtime->status->state = SNDRV_PCM_STATE_OPEN;
+	if (substream->ops->hw_free != NULL)
+		substream->ops->hw_free(substream);
+	return err;
+}
+
+static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params)
+{
+	snd_pcm_hw_params_t params;
+	int err;
+	if (copy_from_user(&params, _params, sizeof(params)))
+		return -EFAULT;
+	err = snd_pcm_hw_params(substream, &params);
+	if (copy_to_user(_params, &params, sizeof(params)))
+		return -EFAULT;
+	return err;
+}
+
+static int snd_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime;
+	int result;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_SETUP:
+	case SNDRV_PCM_STATE_PREPARED:
+		break;
+	default:
+		return -EBADFD;
+	}
+	if (atomic_read(&runtime->mmap_count))
+		return -EBADFD;
+	if (substream->ops->hw_free == NULL)
+		return 0;
+	result = substream->ops->hw_free(substream);
+	runtime->status->state = SNDRV_PCM_STATE_OPEN;
+	return result;
+}
+
+static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params)
+{
+	snd_pcm_runtime_t *runtime;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -ENXIO);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+
+	if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+		return -EINVAL;
+	if (params->avail_min == 0)
+		return -EINVAL;
+	if (params->xfer_align == 0 ||
+	    params->xfer_align % runtime->min_align != 0)
+		return -EINVAL;
+	if (params->silence_threshold + params->silence_size > runtime->buffer_size)
+		return -EINVAL;
+	runtime->tstamp_mode = params->tstamp_mode;
+	runtime->sleep_min = params->sleep_min;
+	runtime->period_step = params->period_step;
+	runtime->control->avail_min = params->avail_min;
+	runtime->start_threshold = params->start_threshold;
+	runtime->stop_threshold = params->stop_threshold;
+	runtime->silence_threshold = params->silence_threshold;
+	runtime->silence_size = params->silence_size;
+	runtime->xfer_align = params->xfer_align;
+        params->boundary = runtime->boundary;
+	spin_lock_irq(&runtime->lock);
+	if (snd_pcm_running(substream)) {
+		if (runtime->sleep_min)
+			snd_pcm_tick_prepare(substream);
+		else
+			snd_pcm_tick_set(substream, 0);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+		    runtime->silence_size > 0)
+			snd_pcm_playback_silence(substream);
+		wake_up(&runtime->sleep);
+	}
+	spin_unlock_irq(&runtime->lock);
+	return 0;
+}
+
+static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t * _params)
+{
+	snd_pcm_sw_params_t params;
+	int err;
+	if (copy_from_user(&params, _params, sizeof(params)))
+		return -EFAULT;
+	err = snd_pcm_sw_params(substream, &params);
+	if (copy_to_user(_params, &params, sizeof(params)))
+		return -EFAULT;
+	return err;
+}
+
+int snd_pcm_status(snd_pcm_substream_t *substream,
+		   snd_pcm_status_t *status)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irq(&runtime->lock);
+	status->state = runtime->status->state;
+	status->suspended_state = runtime->status->suspended_state;
+	if (status->state == SNDRV_PCM_STATE_OPEN)
+		goto _end;
+	status->trigger_tstamp = runtime->trigger_tstamp;
+	if (snd_pcm_running(substream)) {
+		snd_pcm_update_hw_ptr(substream);
+		if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+			status->tstamp = runtime->status->tstamp;
+		else
+			snd_timestamp_now(&status->tstamp);
+	} else
+		snd_timestamp_now(&status->tstamp);
+	status->appl_ptr = runtime->control->appl_ptr;
+	status->hw_ptr = runtime->status->hw_ptr;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		status->avail = snd_pcm_playback_avail(runtime);
+		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
+		    runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+			status->delay = runtime->buffer_size - status->avail;
+	} else {
+		status->avail = snd_pcm_capture_avail(runtime);
+		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+			status->delay = status->avail;
+	}
+	status->avail_max = runtime->avail_max;
+	status->overrange = runtime->overrange;
+	runtime->avail_max = 0;
+	runtime->overrange = 0;
+ _end:
+	spin_unlock_irq(&runtime->lock);	
+	return 0;
+}
+
+static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t * _status)
+{
+	snd_pcm_status_t status;
+	snd_pcm_runtime_t *runtime;
+	int res;
+	
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	memset(&status, 0, sizeof(status));
+	res = snd_pcm_status(substream, &status);
+	if (res < 0)
+		return res;
+	if (copy_to_user(_status, &status, sizeof(status)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t * _info)
+{
+	snd_pcm_channel_info_t info;
+	snd_pcm_runtime_t *runtime;
+	int res;
+	unsigned int channel;
+	
+	snd_assert(substream != NULL, return -ENXIO);
+	if (copy_from_user(&info, _info, sizeof(info)))
+		return -EFAULT;
+	channel = info.channel;
+	runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	if (channel >= runtime->channels)
+		return -EINVAL;
+	memset(&info, 0, sizeof(info));
+	info.channel = channel;
+	res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info);
+	if (res < 0)
+		return res;
+	if (copy_to_user(_info, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static void snd_pcm_trigger_time(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->trigger_master == NULL)
+		return;
+	if (runtime->trigger_master == substream) {
+		snd_timestamp_now(&runtime->trigger_tstamp);
+	} else {
+		snd_pcm_trigger_time(runtime->trigger_master);
+		runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
+	}
+	runtime->trigger_master = NULL;
+}
+
+#define _SND_PCM_ACTION(aname, substream, state, res, check_master) { \
+	snd_pcm_substream_t *s; \
+	res = 0; \
+	spin_lock(&pcm_link_lock); \
+	s = substream; \
+	do { \
+		if (s != substream) \
+			spin_lock(&s->runtime->lock); \
+		res = snd_pcm_pre_##aname(s, state); \
+		if (res < 0) \
+			break; \
+		s = s->link_next; \
+	} while (s != substream); \
+	if (res < 0) { \
+		/* Clean all spin_lock */ \
+		while (s != substream) { \
+			spin_unlock(&s->runtime->lock); \
+			s = s->link_prev; \
+		} \
+		goto _end; \
+	} \
+	s = substream; \
+	do { \
+		snd_pcm_runtime_t *runtime = s->runtime; \
+		int err; \
+		if (check_master && runtime->trigger_master != s) \
+			goto _done; \
+		err = snd_pcm_do_##aname(s, state); \
+		if (err < 0) { \
+			if (res == 0) \
+				res = err; \
+		} else { \
+			_done: \
+			snd_pcm_post_##aname(s, state); \
+		} \
+		if (s != substream) \
+			spin_unlock(&runtime->lock); \
+		s = s->link_next; \
+	} while (s != substream); \
+ _end: \
+	spin_unlock(&pcm_link_lock); \
+}
+
+#define SND_PCM_ACTION(aname, substream, state) { \
+	int res; \
+	_SND_PCM_ACTION(aname, substream, state, res, 1); \
+	return res; \
+}
+
+static inline int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
+		return -EBADFD;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+	    !snd_pcm_playback_data(substream))
+		return -EPIPE;
+	runtime->trigger_master = substream;
+	return 0;
+}
+
+static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state)
+{
+        return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+}
+
+static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_trigger_time(substream);
+	runtime->status->state = SNDRV_PCM_STATE_RUNNING;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+	    runtime->silence_size > 0)
+		snd_pcm_playback_silence(substream);
+	if (runtime->sleep_min)
+		snd_pcm_tick_prepare(substream);
+}
+
+int snd_pcm_start(snd_pcm_substream_t *substream)
+{
+	SND_PCM_ACTION(start, substream, 0);
+}
+
+static inline int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (!snd_pcm_running(substream))
+		return -EBADFD;
+	runtime->trigger_master = substream;
+	return 0;
+}
+
+static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state)
+{
+	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+}
+
+static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_trigger_time(substream);
+	runtime->status->state = state;
+	snd_pcm_tick_set(substream, 0);
+	wake_up(&runtime->sleep);
+}
+
+int snd_pcm_stop(snd_pcm_substream_t *substream, int state)
+{
+	SND_PCM_ACTION(stop, substream, state);
+}
+
+static inline int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))
+		return -ENOSYS;
+	if (push) {
+		if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)
+			return -EBADFD;
+	} else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)
+		return -EBADFD;
+	runtime->trigger_master = substream;
+	return 0;
+}
+
+static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push)
+{
+	return substream->ops->trigger(substream,
+				       push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
+					      SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+}
+
+static inline void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_trigger_time(substream);
+	if (push) {
+		runtime->status->state = SNDRV_PCM_STATE_PAUSED;
+		snd_pcm_tick_set(substream, 0);
+		wake_up(&runtime->sleep);
+	} else {
+		runtime->status->state = SNDRV_PCM_STATE_RUNNING;
+		if (runtime->sleep_min)
+			snd_pcm_tick_prepare(substream);
+	}
+}
+
+static int snd_pcm_pause(snd_pcm_substream_t *substream, int push)
+{
+	SND_PCM_ACTION(pause, substream, push);
+}
+
+#ifdef CONFIG_PM
+/* suspend */
+
+static inline int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+		return -EBUSY;
+	runtime->status->suspended_state = runtime->status->state;
+	runtime->trigger_master = substream;
+	return 0;
+}
+
+static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING)
+		return 0;
+	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+}
+
+static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_trigger_time(substream);
+	runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
+	snd_pcm_tick_set(substream, 0);
+	wake_up(&runtime->sleep);
+}
+
+int snd_pcm_suspend(snd_pcm_substream_t *substream)
+{
+	SND_PCM_ACTION(suspend, substream, 0);
+}
+
+int snd_pcm_suspend_all(snd_pcm_t *pcm)
+{
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	int stream, err;
+
+	for (stream = 0; stream < 2; stream++) {
+		for (substream = pcm->streams[stream].substream; substream; substream = substream->next) {
+			/* FIXME: the open/close code should lock this as well */
+			if ((runtime = substream->runtime) == NULL)
+				continue;
+			spin_lock(&runtime->lock);
+			if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+				spin_unlock(&runtime->lock);
+				continue;
+			}
+			if ((err = snd_pcm_suspend(substream)) < 0) {
+				spin_unlock(&runtime->lock);
+				return err;
+			}
+			spin_unlock(&runtime->lock);
+		}
+	}
+	return 0;
+}
+
+/* resume */
+
+static inline int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
+		return -ENOSYS;
+	runtime->trigger_master = substream;
+	return 0;
+}
+
+static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING)
+		return 0;
+	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
+}
+
+static inline void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_trigger_time(substream);
+	runtime->status->state = runtime->status->suspended_state;
+	if (runtime->sleep_min)
+		snd_pcm_tick_prepare(substream);
+}
+
+static int snd_pcm_resume(snd_pcm_substream_t *substream)
+{
+	snd_card_t *card = substream->pcm->card;
+	int res;
+
+	snd_power_lock(card);
+	while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+		if (substream->ffile->f_flags & O_NONBLOCK) {
+			res = -EAGAIN;
+			goto _power_unlock;
+		}
+		snd_power_wait(card);
+	}
+
+	_SND_PCM_ACTION(resume, substream, 0, res, 1);
+
+       _power_unlock:
+	snd_power_unlock(card);
+	return res;
+}
+
+#else
+
+static int snd_pcm_resume(snd_pcm_substream_t *substream)
+{
+	return -ENOSYS;
+}
+
+#endif /* CONFIG_PM */
+
+static int snd_pcm_xrun(snd_pcm_substream_t *substream)
+{
+	snd_card_t *card = substream->pcm->card;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int result;
+
+	snd_power_lock(card);
+	spin_lock_irq(&runtime->lock);
+       _xrun_recovery:
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_XRUN:
+		result = 0;	/* already there */
+		break;
+	case SNDRV_PCM_STATE_RUNNING:
+		result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+			if (substream->ffile->f_flags & O_NONBLOCK) {
+				result = -EAGAIN;
+				goto _end;
+			}
+			spin_unlock_irq(&runtime->lock);
+			snd_power_wait(card);
+			spin_lock_irq(&runtime->lock);
+		}
+		goto _xrun_recovery;
+	default:
+		result = -EBADFD;
+	}
+       _end:
+	spin_unlock_irq(&runtime->lock);
+	snd_power_unlock(card);
+	return result;
+}
+
+static inline int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_RUNNING:
+	case SNDRV_PCM_STATE_PREPARED:
+	case SNDRV_PCM_STATE_PAUSED:
+	case SNDRV_PCM_STATE_SUSPENDED:
+		return 0;
+	default:
+		return -EBADFD;
+	}
+}
+
+static inline int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, 0);
+	if (err < 0)
+		return err;
+	snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );
+	runtime->hw_ptr_base = 0;
+	runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size;
+	return 0;
+}
+
+static inline void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	runtime->control->appl_ptr = runtime->status->hw_ptr;
+	
+}
+
+static int snd_pcm_reset(snd_pcm_substream_t *substream)
+{
+	int res;
+	_SND_PCM_ACTION(reset, substream, 0, res, 0);
+	return res;
+}
+
+static inline int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_OPEN:
+		return -EBADFD;
+	case SNDRV_PCM_STATE_RUNNING:
+		return -EBUSY;
+	default:
+		return 0;
+	}
+}
+
+static inline int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state)
+{
+	int err;
+	err = substream->ops->prepare(substream);
+	if (err < 0)
+		return err;
+	return snd_pcm_do_reset(substream, 0);
+}
+
+static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	runtime->control->appl_ptr = runtime->status->hw_ptr;
+	runtime->status->state = SNDRV_PCM_STATE_PREPARED;
+}
+
+int snd_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	int res;
+	snd_card_t *card = substream->pcm->card;
+	snd_power_lock(card);
+	while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+		if (substream->ffile->f_flags & O_NONBLOCK) {
+			res = -EAGAIN;
+			goto _power_unlock;
+		}
+		snd_power_wait(card);
+	}
+
+	_SND_PCM_ACTION(prepare, substream, 0, res, 0);
+
+       _power_unlock:
+	snd_power_unlock(card);
+	return res;
+}
+
+static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state)
+{
+	snd_pcm_substream_t *s;
+	spin_lock(&pcm_link_lock);
+	s = substream->link_next;
+	while (s != substream) {
+		spin_lock(&s->runtime->lock);
+		s = s->link_next;
+	}
+	s = substream;
+	do {
+		snd_pcm_runtime_t *runtime = s->runtime;
+		runtime->status->state = state;
+		if (s != substream)
+			spin_unlock(&runtime->lock);
+		s = s->link_next;
+	} while (s != substream);
+	spin_unlock(&pcm_link_lock);
+}
+
+static int snd_pcm_playback_drop(snd_pcm_substream_t *substream);
+
+static int snd_pcm_playback_drain(snd_pcm_substream_t * substream)
+{
+	snd_card_t *card;
+	snd_pcm_runtime_t *runtime;
+	int err, result = 0;
+	wait_queue_t wait;
+	enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY;
+
+	snd_assert(substream != NULL, return -ENXIO);
+	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+	runtime = substream->runtime;
+	card = substream->pcm->card;
+
+	snd_power_lock(card);
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_PAUSED:
+		snd_pcm_pause(substream, 0);
+		/* Fall through */
+	case SNDRV_PCM_STATE_RUNNING:
+	case SNDRV_PCM_STATE_DRAINING:
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+			if (substream->ffile->f_flags & O_NONBLOCK) {
+				result = -EAGAIN;
+				goto _end;
+			}
+			spin_unlock_irq(&runtime->lock);
+			snd_power_wait(card);
+			spin_lock_irq(&runtime->lock);
+		}
+		goto _xrun_recovery;
+	case SNDRV_PCM_STATE_OPEN:
+		result = -EBADFD;
+		goto _end;
+	case SNDRV_PCM_STATE_PREPARED:
+		if (!snd_pcm_playback_empty(substream)) {
+			err = snd_pcm_start(substream);
+			if (err < 0) {
+				result = err;
+				goto _end;
+			}
+			break;
+		}
+		/* Fall through */
+	case SNDRV_PCM_STATE_XRUN:
+	       _xrun_recovery:
+		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
+		/* Fall through */
+	case SNDRV_PCM_STATE_SETUP:
+		goto _end;
+	}
+
+	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
+		if (snd_pcm_playback_empty(substream)) {
+			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+			goto _end;
+		}
+		snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING);
+	}
+
+	if (substream->ffile->f_flags & O_NONBLOCK) {
+		result = -EAGAIN;
+		goto _end;
+	}
+
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&runtime->sleep, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (signal_pending(current)) {
+			state = SIGNALED;
+			break;
+		}
+		spin_unlock_irq(&runtime->lock);
+		if (schedule_timeout(10 * HZ) == 0) {
+			spin_lock_irq(&runtime->lock);
+			state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+			break;
+		}
+		spin_lock_irq(&runtime->lock);
+		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
+			state = READY;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&runtime->sleep, &wait);
+
+	switch (state) {
+	case SIGNALED:
+		result = -ERESTARTSYS;
+		goto _end;
+	case SUSPENDED:
+		result = -ESTRPIPE;
+		goto _end;
+	case EXPIRED:
+		snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+		result = -EIO;
+		goto _end;
+	default:
+		break;
+	}
+
+      _end:
+	spin_unlock_irq(&runtime->lock);
+	snd_power_unlock(card);
+	if (state == EXPIRED)
+		snd_pcm_playback_drop(substream);
+
+	return result;
+}
+
+static int snd_pcm_playback_drop(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_t *card = substream->pcm->card;
+	int res = 0;
+	snd_power_lock(card);
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_OPEN:
+		res = -EBADFD;
+		break;
+	case SNDRV_PCM_STATE_SETUP:
+		break;
+	case SNDRV_PCM_STATE_PAUSED:
+		snd_pcm_pause(substream, 0);
+		/* Fall through */
+	case SNDRV_PCM_STATE_RUNNING:
+	case SNDRV_PCM_STATE_DRAINING:
+		if (snd_pcm_update_hw_ptr(substream) >= 0) {
+			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+			break;
+		}
+		/* Fall through */
+	case SNDRV_PCM_STATE_PREPARED:
+	case SNDRV_PCM_STATE_XRUN:
+	       _xrun_recovery:
+		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+			if (substream->ffile->f_flags & O_NONBLOCK) {
+				res = -EAGAIN;
+				goto _end;
+			}
+			spin_unlock_irq(&runtime->lock);
+			snd_power_wait(card);
+			spin_lock_irq(&runtime->lock);
+		}
+		goto _xrun_recovery;
+	}
+	runtime->control->appl_ptr = runtime->status->hw_ptr;
+       _end:
+	spin_unlock_irq(&runtime->lock);
+	snd_power_unlock(card);
+	return res;
+}
+
+static int snd_pcm_capture_drain(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_t *card = substream->pcm->card;
+	int res = 0;
+	snd_power_lock(card);
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_OPEN:
+		res = -EBADFD;
+		break;
+	case SNDRV_PCM_STATE_PREPARED:
+		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
+		break;
+	case SNDRV_PCM_STATE_SETUP:
+	case SNDRV_PCM_STATE_DRAINING:
+		break;
+	case SNDRV_PCM_STATE_PAUSED:
+		snd_pcm_pause(substream, 0);
+		/* Fall through */
+	case SNDRV_PCM_STATE_RUNNING:
+		if (snd_pcm_update_hw_ptr(substream) >= 0) {
+			snd_pcm_stop(substream, 
+				     snd_pcm_capture_avail(runtime) > 0 ?
+				     SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP);
+			break;
+		}
+		/* Fall through */
+	case SNDRV_PCM_STATE_XRUN:
+	       _xrun_recovery:
+		snd_pcm_change_state(substream, 
+				     snd_pcm_capture_avail(runtime) > 0 ?
+				     SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP);
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+			if (substream->ffile->f_flags & O_NONBLOCK) {
+				res = -EAGAIN;
+				goto _end;
+			}
+			spin_unlock_irq(&runtime->lock);
+			snd_power_wait(card);
+			spin_lock_irq(&runtime->lock);
+		}
+		goto _xrun_recovery;
+	}
+       _end:
+	spin_unlock_irq(&runtime->lock);
+	snd_power_unlock(card);
+	return res;
+}
+
+static int snd_pcm_capture_drop(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_t *card = substream->pcm->card;
+	int res = 0;
+	snd_power_lock(card);
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_OPEN:
+		res = -EBADFD;
+		break;
+	case SNDRV_PCM_STATE_SETUP:
+		break;
+	case SNDRV_PCM_STATE_PAUSED:
+		snd_pcm_pause(substream, 0);
+		/* Fall through */
+	case SNDRV_PCM_STATE_RUNNING:
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+			if (substream->ffile->f_flags & O_NONBLOCK) {
+				res = -EAGAIN;
+				goto _end;
+			}
+			spin_unlock_irq(&runtime->lock);
+			snd_power_wait(card);
+			spin_lock_irq(&runtime->lock);
+		}
+		/* Fall through */
+	case SNDRV_PCM_STATE_PREPARED:
+	case SNDRV_PCM_STATE_DRAINING:
+	case SNDRV_PCM_STATE_XRUN:
+		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);
+		break;
+	}
+	runtime->control->appl_ptr = runtime->status->hw_ptr;
+       _end: 
+	spin_unlock_irq(&runtime->lock);
+	snd_power_unlock(card);
+	return res;
+}
+
+/* WARNING: Don't forget to fput back the file */
+static struct file *snd_pcm_file_fd(int fd)
+{
+	struct file *file;
+	struct inode *inode;
+	unsigned short minor;
+	file = fget(fd);
+	if (!file)
+		return 0;
+	inode = file->f_dentry->d_inode;
+	if (!S_ISCHR(inode->i_mode) ||
+	    major(inode->i_rdev) != CONFIG_SND_MAJOR) {
+		fput(file);
+		return 0;
+	}
+	minor = minor(inode->i_rdev);
+	if (minor >= 256 || 
+	    minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) {
+		fput(file);
+		return 0;
+	}
+	return file;
+}
+
+static int snd_pcm_link(snd_pcm_substream_t *substream, int fd)
+{
+	int res = 0;
+	struct file *file;
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *s, *substream1;
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	file = snd_pcm_file_fd(fd);
+	if (!file)
+		return -EBADFD;
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream1 = pcm_file->substream;
+	spin_lock_irq(&pcm_link_lock);
+	if (substream->runtime->status->state != substream1->runtime->status->state) {
+		res = -EBADFD;
+		goto _end;
+	}
+	s = substream;
+	do {
+		if (s == substream1) {
+			res = -EALREADY;
+			goto _end;
+		}
+		s = s->link_next;
+	} while (s != substream);
+	substream1->link_prev->link_next = substream->link_next;
+	substream->link_next->link_prev = substream1->link_prev;
+	substream->link_next = substream1;
+	substream1->link_prev = substream;
+ _end:
+	spin_unlock_irq(&pcm_link_lock);
+	fput(file);
+	return res;
+}
+
+static int snd_pcm_unlink(snd_pcm_substream_t *substream)
+{
+	spin_lock_irq(&pcm_link_lock);
+	substream->link_prev->link_next = substream->link_next;
+	substream->link_next->link_prev = substream->link_prev;
+	substream->link_prev = substream;
+	substream->link_next = substream;
+	spin_unlock_irq(&pcm_link_lock);
+	return 0;
+}
+
+
+static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params,
+			       snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t t;
+	snd_interval_mul(hw_param_interval_c(params, rule->deps[0]),
+		     hw_param_interval_c(params, rule->deps[1]), &t);
+	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params,
+			       snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t t;
+	snd_interval_div(hw_param_interval_c(params, rule->deps[0]),
+		     hw_param_interval_c(params, rule->deps[1]), &t);
+	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params,
+				   snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t t;
+	snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]),
+			 hw_param_interval_c(params, rule->deps[1]),
+			 (unsigned long) rule->private, &t);
+	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params,
+				   snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t t;
+	snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]),
+			 (unsigned long) rule->private,
+			 hw_param_interval_c(params, rule->deps[1]), &t);
+	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params,
+				  snd_pcm_hw_rule_t *rule)
+{
+	unsigned int k;
+	snd_interval_t *i = hw_param_interval(params, rule->deps[0]);
+	unsigned int m = ~0U;
+	unsigned int *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+		int bits;
+		if (!(*mask & (1U << k)))
+			continue;
+		bits = snd_pcm_format_physical_width(k);
+		snd_assert(bits > 0, continue);
+		if ((unsigned)bits < i->min || (unsigned)bits > i->max)
+			m &= ~(1U << k);
+	}
+	return snd_mask_refine(mask, &m);
+}
+
+static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params,
+				       snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t t;
+	unsigned int k;
+	t.min = UINT_MAX;
+	t.max = 0;
+	t.openmin = 0;
+	t.openmax = 0;
+	for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+		int bits;
+		if (!(*hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT) & (1U << k)))
+			continue;
+		bits = snd_pcm_format_physical_width(k);
+		snd_assert(bits > 0, continue);
+		if (t.min > (unsigned)bits)
+			t.min = bits;
+		if (t.max < (unsigned)bits)
+			t.max = bits;
+	}
+	t.integer = 1;
+	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#error "Change this table"
+#endif
+
+static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
+                                 48000, 64000, 88200, 96000, 176400, 192000 };
+
+#define RATES (sizeof(rates) / sizeof(rates[0]))
+
+static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params,
+				snd_pcm_hw_rule_t *rule)
+{
+	snd_pcm_hardware_t *hw = rule->private;
+	return snd_interval_list(hw_param_interval(params, rule->var), RATES, rates, hw->rates);
+}		
+
+static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params,
+					    snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t t;
+	snd_pcm_substream_t *substream = rule->private;
+	t.min = 0;
+	t.max = substream->buffer_bytes_max;
+	t.openmin = 0;
+	t.openmax = 0;
+	t.integer = 1;
+	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}		
+
+int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+	int k, err;
+
+	for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
+		snd_mask_any(constrs_mask(constrs, k));
+	}
+
+	for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
+		snd_interval_any(constrs_interval(constrs, k));
+	}
+
+	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS));
+	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE));
+	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));
+	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));
+	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));
+
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+				   snd_pcm_hw_rule_format, 0,
+				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 
+				  snd_pcm_hw_rule_sample_bits, 0,
+				  SNDRV_PCM_HW_PARAM_FORMAT, 
+				  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 
+				  snd_pcm_hw_rule_div, 0,
+				  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, 
+				  snd_pcm_hw_rule_mul, 0,
+				  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
+				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
+				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 
+				  snd_pcm_hw_rule_div, 0,
+				  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 
+				  snd_pcm_hw_rule_div, 0,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 
+				  snd_pcm_hw_rule_div, 0,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
+				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 
+				  snd_pcm_hw_rule_muldivk, (void*) 1000000,
+				  SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 
+				  snd_pcm_hw_rule_mul, 0,
+				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 8,
+				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 
+				  snd_pcm_hw_rule_muldivk, (void*) 1000000,
+				  SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 
+				  snd_pcm_hw_rule_muldivk, (void*) 8,
+				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 
+				  snd_pcm_hw_rule_muldivk, (void*) 8,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 
+				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_hardware_t *hw = &runtime->hw;
+	int err;
+	unsigned int mask = 0;
+
+        if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
+		mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+        if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
+		mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+	if (hw->info & SNDRV_PCM_INFO_MMAP) {
+		if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
+			mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+		if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
+			mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
+		if (hw->info & SNDRV_PCM_INFO_COMPLEX)
+			mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
+	}
+	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+					   hw->channels_min, hw->channels_max);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+					   hw->rate_min, hw->rate_max);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+					   hw->period_bytes_min, hw->period_bytes_max);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
+					   hw->periods_min, hw->periods_max);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+					   hw->period_bytes_min, hw->buffer_bytes_max);
+	snd_assert(err >= 0, return -EINVAL);
+
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 
+				  snd_pcm_hw_rule_buffer_bytes_max, substream,
+				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+	if (err < 0)
+		return err;
+
+	/* FIXME: remove */
+	if (runtime->dma_bytes) {
+		err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
+		snd_assert(err >= 0, return -EINVAL);
+	}
+
+	if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
+		err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 
+					  snd_pcm_hw_rule_rate, hw,
+					  SNDRV_PCM_HW_PARAM_RATE, -1);
+		if (err < 0)
+			return err;
+	}
+
+	/* FIXME: this belong to lowlevel */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME,
+				     1000000 / HZ, 1000000 / HZ);
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+
+	return 0;
+}
+
+static void snd_pcm_add_file(snd_pcm_str_t *str,
+			     snd_pcm_file_t *pcm_file)
+{
+	pcm_file->next = str->files;
+	str->files = pcm_file;
+}
+
+static void snd_pcm_remove_file(snd_pcm_str_t *str,
+				snd_pcm_file_t *pcm_file)
+{
+	snd_pcm_file_t * pcm_file1;
+	if (str->files == pcm_file) {
+		str->files = pcm_file->next;
+	} else {
+		pcm_file1 = str->files;
+		while (pcm_file1 && pcm_file1->next != pcm_file)
+			pcm_file1 = pcm_file1->next;
+		if (pcm_file1 != NULL)
+			pcm_file1->next = pcm_file->next;
+	}
+}
+
+static int snd_pcm_release_file(snd_pcm_file_t * pcm_file)
+{
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_str_t * str;
+
+	snd_assert(pcm_file != NULL, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	str = substream->pstr;
+	snd_pcm_unlink(substream);
+	if (substream->ops->hw_free != NULL)
+		substream->ops->hw_free(substream);
+	substream->ops->close(substream);
+	substream->ffile = NULL;
+	snd_pcm_remove_file(str, pcm_file);
+	snd_pcm_release_substream(substream);
+	snd_magic_kfree(pcm_file);
+	return 0;
+}
+
+static int snd_pcm_open_file(struct file *file,
+			     snd_pcm_t *pcm,
+			     int stream,
+			     snd_pcm_file_t **rpcm_file)
+{
+	int err = 0;
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_str_t *str;
+
+	snd_assert(rpcm_file != NULL, return -EINVAL);
+	*rpcm_file = NULL;
+
+	pcm_file = snd_magic_kcalloc(snd_pcm_file_t, 0, GFP_KERNEL);
+	if (pcm_file == NULL) {
+		return -ENOMEM;
+	}
+
+	if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) {
+		snd_magic_kfree(pcm_file);
+		return err;
+	}
+
+	str = substream->pstr;
+	substream->file = pcm_file;
+
+	pcm_file->substream = substream;
+
+	snd_pcm_add_file(str, pcm_file);
+
+	err = snd_pcm_hw_constraints_init(substream);
+	if (err < 0) {
+		snd_printd("snd_pcm_hw_constraints_init failed\n");
+		snd_pcm_release_file(pcm_file);
+		return err;
+	}
+
+	if ((err = substream->ops->open(substream)) < 0) {
+		snd_pcm_release_file(pcm_file);
+		return err;
+	}
+
+	err = snd_pcm_hw_constraints_complete(substream);
+	if (err < 0) {
+		snd_printd("snd_pcm_hw_constraints_complete failed\n");
+		substream->ops->close(substream);
+		snd_pcm_release_file(pcm_file);
+		return err;
+	}
+
+	substream->ffile = file;
+
+	file->private_data = pcm_file;
+	*rpcm_file = pcm_file;
+	return 0;
+}
+
+int snd_pcm_open(struct inode *inode, struct file *file)
+{
+	int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev));
+	int device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev));
+	int err;
+	snd_pcm_t *pcm;
+	snd_pcm_file_t *pcm_file;
+	wait_queue_t wait;
+
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO);
+	pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)];
+	if (pcm == NULL) {
+		err = -ENODEV;
+		goto __error1;
+	}
+	if (!try_inc_mod_count(pcm->card->module)) {
+		err = -EFAULT;
+		goto __error1;
+	}
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&pcm->open_wait, &wait);
+	while (1) {
+		down(&pcm->open_mutex);
+		err = snd_pcm_open_file(file, pcm, device >= SNDRV_MINOR_PCM_CAPTURE ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, &pcm_file);
+		if (err >= 0)
+			break;
+		up(&pcm->open_mutex);
+		if (err == -EAGAIN) {
+			if (file->f_flags & O_NONBLOCK) {
+				err = -EBUSY;
+				break;
+			}
+		} else
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&pcm->open_wait, &wait);
+	if (err < 0)
+		goto __error;
+	up(&pcm->open_mutex);
+	return err;
+
+      __error:
+	dec_mod_count(pcm->card->module);
+      __error1:
+#ifdef LINUX_2_2
+      	MOD_DEC_USE_COUNT;
+#endif
+      	return err;
+}
+
+int snd_pcm_release(struct inode *inode, struct file *file)
+{
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *substream;
+	snd_pcm_file_t *pcm_file;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	snd_assert(!atomic_read(&substream->runtime->mmap_count), );
+	pcm = substream->pcm;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if ((substream->ffile->f_flags & O_NONBLOCK) ||
+		    snd_pcm_playback_drain(substream) == -ERESTARTSYS)
+			snd_pcm_playback_drop(substream);
+	} else
+		snd_pcm_capture_drop(substream);
+	fasync_helper(-1, file, 0, &substream->runtime->fasync);
+	down(&pcm->open_mutex);
+	snd_pcm_release_file(pcm_file);
+	up(&pcm->open_mutex);
+	wake_up(&pcm->open_wait);
+	dec_mod_count(pcm->card->module);
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_playback_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t appl_ptr;
+	snd_pcm_sframes_t ret;
+	snd_pcm_sframes_t hw_avail;
+
+	if (frames == 0)
+		return 0;
+
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_PREPARED:
+		break;
+	case SNDRV_PCM_STATE_DRAINING:
+	case SNDRV_PCM_STATE_RUNNING:
+		if (snd_pcm_update_hw_ptr(substream) >= 0)
+			break;
+		/* Fall through */
+	case SNDRV_PCM_STATE_XRUN:
+		ret = -EPIPE;
+		goto __end;
+	default:
+		ret = -EBADFD;
+		goto __end;
+	}
+
+	hw_avail = snd_pcm_playback_hw_avail(runtime);
+	if (hw_avail <= 0) {
+		ret = 0;
+		goto __end;
+	}
+	if (frames > hw_avail)
+		frames = hw_avail;
+	else
+		frames -= frames % runtime->xfer_align;
+	appl_ptr = runtime->control->appl_ptr - frames;
+	if (appl_ptr < 0)
+		appl_ptr += runtime->boundary;
+	runtime->control->appl_ptr = appl_ptr;
+	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+	    runtime->sleep_min)
+		snd_pcm_tick_prepare(substream);
+	ret = frames;
+ __end:
+	spin_unlock_irq(&runtime->lock);
+	return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t appl_ptr;
+	snd_pcm_sframes_t ret;
+	snd_pcm_sframes_t hw_avail;
+
+	if (frames == 0)
+		return 0;
+
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_PREPARED:
+	case SNDRV_PCM_STATE_DRAINING:
+		break;
+	case SNDRV_PCM_STATE_RUNNING:
+		if (snd_pcm_update_hw_ptr(substream) >= 0)
+			break;
+		/* Fall through */
+	case SNDRV_PCM_STATE_XRUN:
+		ret = -EPIPE;
+		goto __end;
+	default:
+		ret = -EBADFD;
+		goto __end;
+	}
+
+	hw_avail = snd_pcm_capture_hw_avail(runtime);
+	if (hw_avail <= 0) {
+		ret = 0;
+		goto __end;
+	}
+	if (frames > hw_avail)
+		frames = hw_avail;
+	else
+		frames -= frames % runtime->xfer_align;
+	appl_ptr = runtime->control->appl_ptr - frames;
+	if (appl_ptr < 0)
+		appl_ptr += runtime->boundary;
+	runtime->control->appl_ptr = appl_ptr;
+	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+	    runtime->sleep_min)
+		snd_pcm_tick_prepare(substream);
+	ret = frames;
+ __end:
+	spin_unlock_irq(&runtime->lock);
+	return ret;
+}
+
+static int snd_pcm_playback_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err = 0;
+	snd_pcm_sframes_t n;
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_RUNNING:
+	case SNDRV_PCM_STATE_DRAINING:
+		if (snd_pcm_update_hw_ptr(substream) >= 0) {
+			n = snd_pcm_playback_hw_avail(runtime);
+			if (put_user(n, res))
+				err = -EFAULT;
+			break;
+		} else {
+			err = SNDRV_PCM_STATE_RUNNING ? -EPIPE : -EBADFD;
+		}
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) {
+			n = snd_pcm_playback_hw_avail(runtime);
+			if (put_user(n, res))
+				err = -EFAULT;
+		} else {
+			err = -EBADFD;
+		}
+		break;
+	default:
+		err = -EBADFD;
+		break;
+	}
+	spin_unlock_irq(&runtime->lock);
+	return err;
+}
+		
+static int snd_pcm_capture_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err = 0;
+	snd_pcm_sframes_t n;
+	spin_lock_irq(&runtime->lock);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_RUNNING:
+		if (snd_pcm_update_hw_ptr(substream) >= 0) {
+			n = snd_pcm_capture_avail(runtime);
+			if (put_user(n, res))
+				err = -EFAULT;
+			break;
+		}
+		/* Fall through */
+	case SNDRV_PCM_STATE_XRUN:
+		err = -EPIPE;
+		break;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) {
+			n = snd_pcm_capture_avail(runtime);
+			if (put_user(n, res))
+				err = -EFAULT;
+		} else {
+			err = -EBADFD;
+		}
+		break;
+	default:
+		err = -EBADFD;
+		break;
+	}
+	spin_unlock_irq(&runtime->lock);
+	return err;
+}
+
+static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream,
+				   unsigned int cmd, void *arg);
+static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream,
+				  unsigned int cmd, void *arg);
+
+static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream,
+				 unsigned int cmd, void *arg)
+{
+	snd_assert(substream != NULL, return -ENXIO);
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_PVERSION:
+		return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0;
+	case SNDRV_PCM_IOCTL_INFO:
+		return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg);
+	case SNDRV_PCM_IOCTL_HW_REFINE:
+		return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg);
+	case SNDRV_PCM_IOCTL_HW_PARAMS:
+		return snd_pcm_hw_params_user(substream, (snd_pcm_hw_params_t *) arg);
+	case SNDRV_PCM_IOCTL_HW_FREE:
+		return snd_pcm_hw_free(substream);
+	case SNDRV_PCM_IOCTL_SW_PARAMS:
+		return snd_pcm_sw_params_user(substream, (snd_pcm_sw_params_t *) arg);
+	case SNDRV_PCM_IOCTL_STATUS:
+		return snd_pcm_status_user(substream, (snd_pcm_status_t *) arg);
+	case SNDRV_PCM_IOCTL_CHANNEL_INFO:
+		return snd_pcm_channel_info(substream, (snd_pcm_channel_info_t *) arg);
+	case SNDRV_PCM_IOCTL_PREPARE:
+	{
+		int res;
+		spin_lock_irq(&substream->runtime->lock);
+		res = snd_pcm_prepare(substream);
+		spin_unlock_irq(&substream->runtime->lock);
+		return res;
+	}
+	case SNDRV_PCM_IOCTL_RESET:
+	{
+		int res;
+		spin_lock_irq(&substream->runtime->lock);
+		res = snd_pcm_reset(substream);
+		spin_unlock_irq(&substream->runtime->lock);
+		return res;
+	}
+	case SNDRV_PCM_IOCTL_START:
+	{
+		int res;
+		spin_lock_irq(&substream->runtime->lock);
+		res = snd_pcm_start(substream);
+		spin_unlock_irq(&substream->runtime->lock);
+		return res;
+	}
+	case SNDRV_PCM_IOCTL_LINK:
+		return snd_pcm_link(substream, (long) arg);
+	case SNDRV_PCM_IOCTL_UNLINK:
+		return snd_pcm_unlink(substream);
+	case SNDRV_PCM_IOCTL_RESUME:
+		return snd_pcm_resume(substream);
+	case SNDRV_PCM_IOCTL_XRUN:
+		return snd_pcm_xrun(substream);
+	}
+	snd_printd("unknown ioctl = 0x%x\n", cmd);
+	return -ENOTTY;
+}
+
+static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream,
+				   unsigned int cmd, void *arg)
+{
+	snd_assert(substream != NULL, return -ENXIO);
+	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
+	{
+		snd_xferi_t xferi, *_xferi = arg;
+		snd_pcm_runtime_t *runtime = substream->runtime;
+		snd_pcm_sframes_t result;
+		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
+		if (put_user(0, &_xferi->result))
+			return -EFAULT;
+		if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+			return -EFAULT;
+		result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
+		__put_user(result, &_xferi->result);
+		return result < 0 ? result : 0;
+	}
+	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
+	{
+		snd_xfern_t xfern, *_xfern = arg;
+		snd_pcm_runtime_t *runtime = substream->runtime;
+		void *bufs[128];
+		snd_pcm_sframes_t result;
+		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
+		if (runtime->channels > 128)
+			return -EINVAL;
+		if (put_user(0, &_xfern->result))
+			return -EFAULT;
+		if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+			return -EFAULT;
+		if (copy_from_user(bufs, xfern.bufs, sizeof(*bufs) * runtime->channels))
+			return -EFAULT;
+		result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
+		__put_user(result, &_xfern->result);
+		return result < 0 ? result : 0;
+	}
+	case SNDRV_PCM_IOCTL_REWIND:
+	{
+		snd_pcm_uframes_t frames, *_frames = arg;
+		snd_pcm_sframes_t result;
+		if (get_user(frames, _frames))
+			return -EFAULT;
+		if (put_user(0, _frames))
+			return -EFAULT;
+		result = snd_pcm_playback_rewind(substream, frames);
+		__put_user(result, _frames);
+		return result < 0 ? result : 0;
+	}
+	case SNDRV_PCM_IOCTL_PAUSE:
+	{
+		int res;
+		spin_lock_irq(&substream->runtime->lock);
+		res = snd_pcm_pause(substream, (long) arg);
+		spin_unlock_irq(&substream->runtime->lock);
+		return res;
+	}
+	case SNDRV_PCM_IOCTL_DRAIN:
+		return snd_pcm_playback_drain(substream);
+	case SNDRV_PCM_IOCTL_DROP:
+		return snd_pcm_playback_drop(substream);
+	case SNDRV_PCM_IOCTL_DELAY:
+		return snd_pcm_playback_delay(substream, (snd_pcm_sframes_t*) arg);
+	}
+	return snd_pcm_common_ioctl1(substream, cmd, arg);
+}
+
+static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream,
+				  unsigned int cmd, void *arg)
+{
+	snd_assert(substream != NULL, return -ENXIO);
+	snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL);
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL_READI_FRAMES:
+	{
+		snd_xferi_t xferi, *_xferi = arg;
+		snd_pcm_runtime_t *runtime = substream->runtime;
+		snd_pcm_sframes_t result;
+		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
+		if (put_user(0, &_xferi->result))
+			return -EFAULT;
+		if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+			return -EFAULT;
+		result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
+		__put_user(result, &_xferi->result);
+		return result < 0 ? result : 0;
+	}
+	case SNDRV_PCM_IOCTL_READN_FRAMES:
+	{
+		snd_xfern_t xfern, *_xfern = arg;
+		snd_pcm_runtime_t *runtime = substream->runtime;
+		void *bufs[128];
+		snd_pcm_sframes_t result;
+		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
+		if (runtime->channels > 128)
+			return -EINVAL;
+		if (put_user(0, &_xfern->result))
+			return -EFAULT;
+		if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+			return -EFAULT;
+		if (copy_from_user(bufs, xfern.bufs, sizeof(*bufs) * runtime->channels))
+			return -EFAULT;
+		result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
+		__put_user(result, &_xfern->result);
+		return result < 0 ? result : 0;
+	}
+	case SNDRV_PCM_IOCTL_REWIND:
+	{
+		snd_pcm_uframes_t frames, *_frames = arg;
+		snd_pcm_sframes_t result;
+		if (get_user(frames, _frames))
+			return -EFAULT;
+		if (put_user(0, _frames))
+			return -EFAULT;
+		result = snd_pcm_capture_rewind(substream, frames);
+		__put_user(result, _frames);
+		return result < 0 ? result : 0;
+	}
+	case SNDRV_PCM_IOCTL_DRAIN:
+		return snd_pcm_capture_drain(substream);
+	case SNDRV_PCM_IOCTL_DROP:
+		return snd_pcm_capture_drop(substream);
+	case SNDRV_PCM_IOCTL_DELAY:
+		return snd_pcm_capture_delay(substream, (snd_pcm_sframes_t*) arg);
+	}
+	return snd_pcm_common_ioctl1(substream, cmd, arg);
+}
+
+static int snd_pcm_playback_ioctl(struct inode *inode, struct file *file,
+				  unsigned int cmd, unsigned long arg)
+{
+	snd_pcm_file_t *pcm_file;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+
+	if (((cmd >> 8) & 0xff) != 'A')
+		return -ENOTTY;
+
+	return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void *) arg);
+}
+
+static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file,
+				 unsigned int cmd, unsigned long arg)
+{
+	snd_pcm_file_t *pcm_file;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+
+	if (((cmd >> 8) & 0xff) != 'A')
+		return -ENOTTY;
+
+	return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void *) arg);
+}
+
+int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream,
+				  unsigned int cmd, void *arg)
+{
+	mm_segment_t fs;
+	int result;
+	
+	fs = snd_enter_user();
+	result = snd_pcm_playback_ioctl1(substream, cmd, arg);
+	snd_leave_user(fs);
+	return result;
+}
+
+int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream,
+				 unsigned int cmd, void *arg)
+{
+	mm_segment_t fs;
+	int result;
+	
+	fs = snd_enter_user();
+	result = snd_pcm_capture_ioctl1(substream, cmd, arg);
+	snd_leave_user(fs);
+	return result;
+}
+
+int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream,
+			 unsigned int cmd, void *arg)
+{
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		return snd_pcm_kernel_playback_ioctl(substream, cmd, arg);
+	case SNDRV_PCM_STREAM_CAPTURE:
+		return snd_pcm_kernel_capture_ioctl(substream, cmd, arg);
+	default:
+		return -EINVAL;
+	}
+}
+
+static ssize_t snd_pcm_read(struct file *file, char *buf, size_t count, loff_t * offset)
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_sframes_t result;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	if (!frame_aligned(runtime, count))
+		return -EINVAL;
+	count = bytes_to_frames(runtime, count);
+	result = snd_pcm_lib_read(substream, buf, count);
+	if (result > 0)
+		result = frames_to_bytes(runtime, result);
+	return result;
+}
+
+static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, loff_t * offset)
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_sframes_t result;
+
+	up(&file->f_dentry->d_inode->i_sem);
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, result = -ENXIO; goto end);
+	runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		result = -EBADFD;
+		goto end;
+	}
+	if (!frame_aligned(runtime, count)) {
+		result = -EINVAL;
+		goto end;
+	}
+	count = bytes_to_frames(runtime, count);
+	result = snd_pcm_lib_write(substream, buf, count);
+	if (result > 0)
+		result = frames_to_bytes(runtime, result);
+ end:
+	down(&file->f_dentry->d_inode->i_sem);
+	return result;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44)
+static ssize_t snd_pcm_readv(struct file *file, const struct iovec *_vector,
+			     unsigned long count, loff_t * offset)
+
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_sframes_t result;
+	unsigned long i;
+	void *bufs[128];
+	snd_pcm_uframes_t frames;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	if (count > 128 || count != runtime->channels)
+		return -EINVAL;
+	if (!frame_aligned(runtime, _vector->iov_len))
+		return -EINVAL;
+	frames = bytes_to_samples(runtime, _vector->iov_len);
+	for (i = 0; i < count; ++i)
+		bufs[i] = _vector[i].iov_base;
+	result = snd_pcm_lib_readv(substream, bufs, frames);
+	if (result > 0)
+		result = frames_to_bytes(runtime, result);
+	return result;
+}
+
+static ssize_t snd_pcm_writev(struct file *file, const struct iovec *_vector,
+			      unsigned long count, loff_t * offset)
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	snd_pcm_sframes_t result;
+	unsigned long i;
+	void *bufs[128];
+	snd_pcm_uframes_t frames;
+
+	up(&file->f_dentry->d_inode->i_sem);
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, result = -ENXIO; goto end);
+	runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		result = -EBADFD;
+		goto end;
+	}
+	if (count > 128 || count != runtime->channels ||
+	    !frame_aligned(runtime, _vector->iov_len)) {
+		result = -EINVAL;
+		goto end;
+	}
+	frames = bytes_to_samples(runtime, _vector->iov_len);
+	for (i = 0; i < count; ++i)
+		bufs[i] = _vector[i].iov_base;
+	result = snd_pcm_lib_writev(substream, bufs, frames);
+	if (result > 0)
+		result = frames_to_bytes(runtime, result);
+ end:
+	down(&file->f_dentry->d_inode->i_sem);
+	return result;
+}
+#endif
+
+unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+        unsigned int mask;
+	snd_pcm_uframes_t avail;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0);
+
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+
+	poll_wait(file, &runtime->sleep, wait);
+
+	spin_lock_irq(&runtime->lock);
+	avail = snd_pcm_playback_avail(runtime);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_RUNNING:
+		if (avail >= runtime->control->avail_min) {
+			mask = POLLOUT | POLLWRNORM;
+			break;
+		}
+		/* Fall through */
+	case SNDRV_PCM_STATE_DRAINING:
+		mask = 0;
+		break;
+	case SNDRV_PCM_STATE_PREPARED:
+		if (avail > 0) {
+			mask = POLLOUT | POLLWRNORM;
+			break;
+		}
+		/* Fall through */
+	default:
+		mask = POLLOUT | POLLWRNORM | POLLERR;
+		break;
+	}
+	spin_unlock_irq(&runtime->lock);
+	return mask;
+}
+
+unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
+{
+	snd_pcm_file_t *pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+        unsigned int mask;
+	snd_pcm_uframes_t avail;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0);
+
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+
+	poll_wait(file, &runtime->sleep, wait);
+
+	spin_lock_irq(&runtime->lock);
+	avail = snd_pcm_capture_avail(runtime);
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_RUNNING:
+		if (avail >= runtime->control->avail_min) {
+			mask = POLLIN | POLLRDNORM;
+			break;
+		}
+		mask = 0;
+		break;
+	case SNDRV_PCM_STATE_DRAINING:
+		if (avail > 0) {
+			mask = POLLIN | POLLRDNORM;
+			break;
+		}
+		/* Fall through */
+	default:
+		mask = POLLIN | POLLRDNORM | POLLERR;
+		break;
+	}
+	spin_unlock_irq(&runtime->lock);
+	return mask;
+}
+
+#ifndef VM_RESERVED
+#ifndef LINUX_2_2
+static int snd_pcm_mmap_swapout(struct page * page, struct file * file)
+#else
+static int snd_pcm_mmap_swapout(struct vm_area_struct * area, struct page * page)
+#endif
+{
+	return 0;
+}
+#endif
+
+#ifndef LINUX_2_2
+static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share)
+#else
+static unsigned long snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share)
+#endif
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+	snd_pcm_runtime_t *runtime;
+	struct page * page;
+	
+	if (substream == NULL)
+		return NOPAGE_OOM;
+	runtime = substream->runtime;
+	page = virt_to_page(runtime->status);
+	get_page(page);
+#ifndef LINUX_2_2
+	return page;
+#else
+	return page_address(page);
+#endif
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_status =
+{
+	nopage:		snd_pcm_mmap_status_nopage,
+#ifndef VM_RESERVED
+	swapout:	snd_pcm_mmap_swapout,
+#endif
+};
+
+int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file,
+			struct vm_area_struct *area)
+{
+	snd_pcm_runtime_t *runtime;
+	long size;
+	if (!(area->vm_flags & VM_READ))
+		return -EINVAL;
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EAGAIN);
+	size = area->vm_end - area->vm_start;
+	if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)))
+		return -EINVAL;
+	area->vm_ops = &snd_pcm_vm_ops_status;
+#ifndef LINUX_2_2
+	area->vm_private_data = substream;
+#else
+	area->vm_private_data = (long)substream;	
+#endif
+#ifdef VM_RESERVED
+	area->vm_flags |= VM_RESERVED;
+#endif
+	return 0;
+}
+
+#ifndef LINUX_2_2
+static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share)
+#else
+static unsigned long snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share)
+#endif
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+	snd_pcm_runtime_t *runtime;
+	struct page * page;
+	
+	if (substream == NULL)
+		return NOPAGE_OOM;
+	runtime = substream->runtime;
+	page = virt_to_page(runtime->control);
+	get_page(page);
+#ifndef LINUX_2_2
+	return page;
+#else
+	return page_address(page);
+#endif
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_control =
+{
+	nopage:		snd_pcm_mmap_control_nopage,
+#ifndef VM_RESERVED
+	swapout:	snd_pcm_mmap_swapout,
+#endif
+};
+
+static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file,
+				struct vm_area_struct *area)
+{
+	snd_pcm_runtime_t *runtime;
+	long size;
+	if (!(area->vm_flags & VM_READ))
+		return -EINVAL;
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EAGAIN);
+	size = area->vm_end - area->vm_start;
+	if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)))
+		return -EINVAL;
+	area->vm_ops = &snd_pcm_vm_ops_control;
+#ifndef LINUX_2_2
+	area->vm_private_data = substream;
+#else
+	area->vm_private_data = (long)substream;	
+#endif
+#ifdef VM_RESERVED
+	area->vm_flags |= VM_RESERVED;
+#endif
+	return 0;
+}
+
+static void snd_pcm_mmap_data_open(struct vm_area_struct *area)
+{
+#if 0
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+	atomic_inc(&substream->runtime->mmap_count);
+#endif
+}
+
+static void snd_pcm_mmap_data_close(struct vm_area_struct *area)
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+	atomic_dec(&substream->runtime->mmap_count);
+}
+
+#ifndef LINUX_2_2
+static struct page * snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share)
+#else
+static unsigned long snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share)
+#endif
+{
+	snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+	snd_pcm_runtime_t *runtime;
+	unsigned long offset;
+	struct page * page;
+	size_t dma_bytes;
+	
+	if (substream == NULL)
+		return NOPAGE_OOM;
+	runtime = substream->runtime;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25)
+	offset = area->vm_pgoff << PAGE_SHIFT;
+#else
+	offset = area->vm_offset;
+#endif
+	offset += address - area->vm_start;
+	snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
+	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+	if (offset > dma_bytes - PAGE_SIZE)
+		return NOPAGE_SIGBUS;
+	page = virt_to_page(runtime->dma_area + offset);
+	get_page(page);
+#ifndef LINUX_2_2
+	return page;
+#else
+	return page_address(page);
+#endif
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_data =
+{
+	open:		snd_pcm_mmap_data_open,
+	close:		snd_pcm_mmap_data_close,
+	nopage:		snd_pcm_mmap_data_nopage,
+#ifndef VM_RESERVED
+	swapout:	snd_pcm_mmap_swapout,
+#endif
+};
+
+int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file,
+		      struct vm_area_struct *area)
+{
+	snd_pcm_runtime_t *runtime;
+	long size;
+	unsigned long offset;
+	size_t dma_bytes;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (!(area->vm_flags & (VM_WRITE|VM_READ)))
+			return -EINVAL;
+	} else {
+		if (!(area->vm_flags & VM_READ))
+			return -EINVAL;
+	}
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EAGAIN);
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
+		return -ENXIO;
+	if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+	    runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+		return -EINVAL;
+	size = area->vm_end - area->vm_start;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25)
+	offset = area->vm_pgoff << PAGE_SHIFT;
+#else
+	offset = area->vm_offset;
+#endif
+	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+	if (size > dma_bytes)
+		return -EINVAL;
+	if (offset > dma_bytes - size)
+		return -EINVAL;
+
+	area->vm_ops = &snd_pcm_vm_ops_data;
+#ifndef LINUX_2_2
+	area->vm_private_data = substream;
+#else
+	area->vm_private_data = (long)substream;
+#endif
+#ifdef VM_RESERVED
+	area->vm_flags |= VM_RESERVED;
+#endif
+	atomic_inc(&runtime->mmap_count);
+	return 0;
+}
+
+static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
+{
+	snd_pcm_file_t * pcm_file;
+	snd_pcm_substream_t *substream;	
+	unsigned long offset;
+	
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25)
+	offset = area->vm_pgoff << PAGE_SHIFT;
+#else
+	offset = area->vm_offset;
+#endif
+	switch (offset) {
+	case SNDRV_PCM_MMAP_OFFSET_STATUS:
+		return snd_pcm_mmap_status(substream, file, area);
+	case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+		return snd_pcm_mmap_control(substream, file, area);
+	default:
+		return snd_pcm_mmap_data(substream, file, area);
+	}
+	return 0;
+}
+
+static int snd_pcm_fasync(int fd, struct file * file, int on)
+{
+	snd_pcm_file_t * pcm_file;
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	int err;
+
+	pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO);
+	substream = pcm_file->substream;
+	snd_assert(substream != NULL, return -ENXIO);
+	runtime = substream->runtime;
+
+	err = fasync_helper(fd, file, on, &runtime->fasync);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *  Register section
+ */
+
+static struct file_operations snd_pcm_f_ops_playback = {
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	write:		snd_pcm_write,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44)
+	writev:		snd_pcm_writev,
+#endif
+	open:		snd_pcm_open,
+	release:	snd_pcm_release,
+	poll:		snd_pcm_playback_poll,
+	ioctl:		snd_pcm_playback_ioctl,
+	mmap:		snd_pcm_mmap,
+	fasync:		snd_pcm_fasync,
+};
+
+static struct file_operations snd_pcm_f_ops_capture = {
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		snd_pcm_read,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44)
+	readv:		snd_pcm_readv,
+#endif
+	open:		snd_pcm_open,
+	release:	snd_pcm_release,
+	poll:		snd_pcm_capture_poll,
+	ioctl:		snd_pcm_capture_ioctl,
+	mmap:		snd_pcm_mmap,
+	fasync:		snd_pcm_fasync,
+};
+
+snd_minor_t snd_pcm_reg[2] =
+{
+	{
+		comment:	"digital audio playback",
+		f_ops:		&snd_pcm_f_ops_playback,
+	},
+	{
+		comment:	"digital audio capture",
+		f_ops:		&snd_pcm_f_ops_capture,
+	}
+};
diff -Nru linux/sound/core/pcm_timer.c linux-2.4.19-pre5-mjc/sound/core/pcm_timer.c
--- linux/sound/core/pcm_timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/pcm_timer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,159 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/timer.h>
+
+#define chip_t snd_pcm_substream_t
+
+/*
+ *  Timer functions
+ */
+
+/* Greatest common divisor */
+static int gcd(int a, int b)
+{
+	int r;
+	if (a < b) {
+		r = a;
+		a = b;
+		b = r;
+	}
+	while ((r = a % b) != 0) {
+		a = b;
+		b = r;
+	}
+	return b;
+}
+
+void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream)
+{
+	unsigned int rate, mult, fsize, l;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	
+        mult = 1000000000;
+	rate = runtime->rate;
+	snd_assert(rate != 0, return);
+	l = gcd(mult, rate);
+	mult /= l;
+	rate /= l;
+	fsize = runtime->period_size;
+	snd_assert(fsize != 0, return);
+	l = gcd(rate, fsize);
+	rate /= l;
+	fsize /= l;
+	while ((mult * fsize) / fsize != mult) {
+		mult /= 2;
+		rate /= 2;
+	}
+	snd_assert(rate != 0, return);
+	runtime->timer_resolution = mult * fsize / rate;
+}
+
+static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer)
+{
+	snd_pcm_substream_t * substream;
+	
+	substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return -ENXIO);
+	return substream->runtime ? substream->runtime->timer_resolution : 0;
+}
+
+static int snd_pcm_timer_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	snd_pcm_substream_t * substream;
+	
+	substream = snd_timer_chip(timer);
+	spin_lock_irqsave(&substream->timer_lock, flags);
+	substream->timer_running = 1;
+	spin_unlock_irqrestore(&substream->timer_lock, flags);
+	return 0;
+}
+
+static int snd_pcm_timer_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	snd_pcm_substream_t * substream;
+	
+	substream = snd_timer_chip(timer);
+	spin_lock_irqsave(&substream->timer_lock, flags);
+	substream->timer_running = 0;
+	spin_unlock_irqrestore(&substream->timer_lock, flags);
+	return 0;
+}
+
+static struct _snd_timer_hardware snd_pcm_timer =
+{
+	flags:		SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
+	resolution:	0,
+	ticks:		1,
+	c_resolution:	snd_pcm_timer_resolution,
+	start:		snd_pcm_timer_start,
+	stop:		snd_pcm_timer_stop,
+};
+
+/*
+ *  Init functions
+ */
+
+static void snd_pcm_timer_free(snd_timer_t *timer)
+{
+	snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return);
+	substream->timer = NULL;
+}
+
+void snd_pcm_timer_init(snd_pcm_substream_t *substream)
+{
+	snd_timer_id_t tid;
+	snd_timer_t *timer;
+	
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.dev_class = SNDRV_TIMER_CLASS_PCM;
+	tid.card = substream->pcm->card->number;
+	tid.device = substream->pcm->device;
+	tid.subdevice = (substream->number << 1) | (substream->stream & 1);
+	if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0)
+		return;
+	sprintf(timer->name, "PCM %s %i-%i-%i",
+			substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
+				"capture" : "playback",
+			tid.card, tid.device, tid.subdevice);
+	timer->hw = snd_pcm_timer;
+	if (snd_device_register(timer->card, timer) < 0) {
+		snd_device_free(timer->card, timer);
+		return;
+	}
+	timer->private_data = substream;
+	timer->private_free = snd_pcm_timer_free;
+	substream->timer = timer;
+}
+
+void snd_pcm_timer_done(snd_pcm_substream_t *substream)
+{
+	if (substream->timer) {
+		snd_device_free(substream->pcm->card, substream->timer);
+		substream->timer = NULL;
+	}
+}
diff -Nru linux/sound/core/rawmidi.c linux-2.4.19-pre5-mjc/sound/core/rawmidi.c
--- linux/sound/core/rawmidi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/rawmidi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1574 @@
+/*
+ *  Abstract layer for MIDI v1.0 stream
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_SND_OSSEMUL
+static int snd_midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int snd_amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
+MODULE_PARM(snd_midi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_midi_map, "Raw MIDI device number assigned to 1st OSS device.");
+MODULE_PARM_SYNTAX(snd_midi_map, "default:0,skill:advanced");
+MODULE_PARM(snd_amidi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
+MODULE_PARM_SYNTAX(snd_amidi_map, "default:1,skill:advanced");
+#endif /* CONFIG_SND_OSSEMUL */
+
+static int snd_rawmidi_free(snd_rawmidi_t *rawmidi);
+static int snd_rawmidi_dev_free(snd_device_t *device);
+static int snd_rawmidi_dev_register(snd_device_t *device);
+static int snd_rawmidi_dev_unregister(snd_device_t *device);
+
+snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES];
+
+static DECLARE_MUTEX(register_mutex);
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+static inline unsigned short snd_rawmidi_file_flags(struct file *file)
+{
+	switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
+	case FMODE_WRITE:
+		return SNDRV_RAWMIDI_LFLG_OUTPUT;
+	case FMODE_READ:
+		return SNDRV_RAWMIDI_LFLG_INPUT;
+	default:
+		return SNDRV_RAWMIDI_LFLG_OPEN;
+	}
+}
+
+static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	return runtime->avail >= runtime->avail_min;
+}
+
+static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	return runtime->avail >= runtime->avail_min && runtime->avail >= count;
+}
+
+static int snd_rawmidi_init(snd_rawmidi_substream_t *substream)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	spin_lock_init(&runtime->lock);
+	init_waitqueue_head(&runtime->sleep);
+	runtime->event = NULL;
+	runtime->buffer_size = PAGE_SIZE;
+	runtime->avail_min = 1;
+	if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
+		runtime->avail = 0;
+	else
+		runtime->avail = runtime->buffer_size;
+	if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	runtime->appl_ptr = runtime->hw_ptr = 0;
+	return 0;
+}
+
+static int snd_rawmidi_done_buffer(snd_rawmidi_runtime_t *runtime)
+{
+	if (runtime->buffer) {
+		kfree(runtime->buffer);
+		runtime->buffer = NULL;
+	}
+	return 0;
+}
+
+int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	substream->ops->trigger(substream, 0);
+	runtime->trigger = 0;
+	runtime->drain = 0;
+	/* interrupts are not enabled at this moment,
+	   so spinlock is not required */
+	runtime->appl_ptr = runtime->hw_ptr = 0;
+	runtime->avail = runtime->buffer_size;
+	return 0;
+}
+
+int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream)
+{
+	int err;
+	long timeout;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	err = 0;
+	runtime->drain = 1;
+	while (runtime->avail < runtime->buffer_size) {
+		timeout = interruptible_sleep_on_timeout(&runtime->sleep, 10 * HZ);
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+		if (runtime->avail < runtime->buffer_size && !timeout) {
+			err = -EIO;
+			break;
+		}
+	}
+	runtime->drain = 0;
+	if (err != -ERESTARTSYS) {
+		/* we need wait a while to make sure that Tx FIFOs are empty */
+		if (substream->ops->drain)
+			substream->ops->drain(substream);
+		else {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ / 20);
+		}
+		snd_rawmidi_drop_output(substream);
+	}
+	return err;
+}
+
+int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	substream->ops->trigger(substream, 0);
+	runtime->trigger = 0;
+	runtime->drain = 0;
+	/* interrupts aren't enabled at this moment, so spinlock isn't needed */
+	runtime->appl_ptr = runtime->hw_ptr = 0;
+	runtime->avail = 0;
+	return 0;
+}
+
+int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice,
+			    int mode, snd_rawmidi_file_t * rfile)
+{
+	snd_rawmidi_t *rmidi;
+	struct list_head *list1, *list2;
+	snd_rawmidi_substream_t *sinput, *soutput;
+	snd_rawmidi_runtime_t *input = NULL, *output = NULL;
+	int err;
+
+	if (rfile)
+		rfile->input = rfile->output = NULL;
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device];
+	if (rmidi == NULL) {
+		err = -ENODEV;
+		goto __error1;
+	}
+	if (!try_inc_mod_count(rmidi->card->module)) {
+		err = -EFAULT;
+		goto __error1;
+	}
+	down(&rmidi->open_mutex);
+	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+		if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {
+			err = -ENXIO;
+			goto __error;
+		}
+		if (subdevice >= 0 && subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
+			err = -ENODEV;
+			goto __error;
+		}
+		if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=
+		    rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
+			err = -EAGAIN;
+			goto __error;
+		}
+	}
+	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+		if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {
+			err = -ENXIO;
+			goto __error;
+		}
+		if (subdevice >= 0 && subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
+			err = -ENODEV;
+			goto __error;
+		}
+		if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=
+		    rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
+			err = -EAGAIN;
+			goto __error;
+		}
+	}
+	list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
+	while (1) {
+		if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+			sinput = NULL;
+			if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+				err = -EAGAIN;
+				goto __error;
+			}
+			break;
+		}
+		sinput = list_entry(list1, snd_rawmidi_substream_t, list);
+		if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)
+			goto __nexti;
+		if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))
+			break;
+	      __nexti:
+		list1 = list1->next;
+	}
+	list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;
+	while (1) {
+		if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+			soutput = NULL;
+			if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+				err = -EAGAIN;
+				goto __error;
+			}
+			break;
+		}
+		soutput = list_entry(list2, snd_rawmidi_substream_t, list);
+		if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+			if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {
+				if (soutput->opened && !soutput->append)
+					goto __nexto;
+			} else {
+				if (soutput->opened)
+					goto __nexto;
+			}
+		}
+		if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))
+			break;
+	      __nexto:
+		list2 = list2->next;
+	}
+	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+		input = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL);
+		if (input == NULL) {
+			err = -ENOMEM;
+			goto __error;
+		}
+		sinput->runtime = input;
+		if (snd_rawmidi_init(sinput) < 0) {
+			err = -ENOMEM;
+			goto __error;
+		}
+		if ((err = sinput->ops->open(sinput)) < 0) {
+			sinput->runtime = NULL;
+			goto __error;
+		}
+		sinput->opened = 1;
+		rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
+	} else {
+		sinput = NULL;
+	}
+	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+		if (soutput->opened)
+			goto __skip_output;
+		output = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL);
+		if (output == NULL) {
+			err = -ENOMEM;
+			goto __error;
+		}
+		soutput->runtime = output;
+		if (snd_rawmidi_init(soutput) < 0) {
+			if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+				sinput->ops->close(sinput);
+				sinput->runtime = NULL;
+			}
+			err = -ENOMEM;
+			goto __error;
+		}
+		if ((err = soutput->ops->open(soutput)) < 0) {
+			if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+				sinput->ops->close(sinput);
+				sinput->runtime = NULL;
+			}
+			soutput->runtime = NULL;
+			goto __error;
+		}
+	      __skip_output:
+		soutput->opened = 1;
+		if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
+			soutput->append = 1;
+	      	if (soutput->use_count++ == 0)
+			soutput->active_sensing = 1;
+		rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;
+	} else {
+		soutput = NULL;
+	}
+	up(&rmidi->open_mutex);
+	if (rfile) {
+		rfile->rmidi = rmidi;
+		rfile->input = sinput;
+		rfile->output = soutput;
+	}
+	return 0;
+
+      __error:
+	if (input != NULL)
+		kfree(input);
+	if (output != NULL)
+		kfree(output);
+	dec_mod_count(rmidi->card->module);
+	up(&rmidi->open_mutex);
+      __error1:
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return err;
+}
+
+static int snd_rawmidi_open(struct inode *inode, struct file *file)
+{
+	int maj = major(inode->i_rdev);
+	int cardnum;
+	snd_card_t *card;
+	int device, subdevice;
+	unsigned short fflags;
+	int err;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_file_t *rawmidi_file;
+	wait_queue_t wait;
+	struct list_head *list;
+	snd_ctl_file_t *kctl;
+
+	switch (maj) {
+	case CONFIG_SND_MAJOR:
+		cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev));
+		cardnum %= SNDRV_CARDS;
+		device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_RAWMIDI;
+		device %= SNDRV_MINOR_RAWMIDIS;
+		break;
+#ifdef CONFIG_SND_OSSEMUL
+	case SOUND_MAJOR:
+		cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev));
+		cardnum %= SNDRV_CARDS;
+		device = SNDRV_MINOR_OSS_DEVICE(minor(inode->i_rdev)) == SNDRV_MINOR_OSS_MIDI ?
+			snd_midi_map[cardnum] : snd_amidi_map[cardnum];
+		break;
+#endif
+	default:
+		return -ENXIO;
+	}
+
+	rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device];
+	if (rmidi == NULL)
+		return -ENODEV;
+	card = rmidi->card;
+#ifdef CONFIG_SND_OSSEMUL
+	if (maj == SOUND_MAJOR && !rmidi->ossreg)
+		return -ENXIO;
+#endif
+	fflags = snd_rawmidi_file_flags(file);
+	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
+		return -EINVAL;		/* invalid combination */
+	if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */
+		fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
+	rawmidi_file = snd_magic_kmalloc(snd_rawmidi_file_t, 0, GFP_KERNEL);
+	if (rawmidi_file == NULL)
+		return -ENOMEM;
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&rmidi->open_wait, &wait);
+	while (1) {
+		subdevice = -1;
+		read_lock(&card->control_rwlock);
+		list_for_each(list, &card->ctl_files) {
+			kctl = snd_ctl_file(list);
+			if (kctl->pid == current->pid) {
+				subdevice = kctl->prefer_rawmidi_subdevice;
+				break;
+			}
+		}
+		read_unlock(&card->control_rwlock);
+		err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file);
+		if (err >= 0)
+			break;
+		if (err == -EAGAIN) {
+			if (file->f_flags & O_NONBLOCK) {
+				err = -EBUSY;
+				break;
+			}
+		} else
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	if (rawmidi_file->input && rawmidi_file->input->runtime)
+		rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
+	if (rawmidi_file->output && rawmidi_file->output->runtime)
+		rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
+#endif
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rmidi->open_wait, &wait);
+	if (err >= 0) {
+		file->private_data = rawmidi_file;
+	} else {
+		snd_magic_kfree(rawmidi_file);
+	}
+	return err;
+}
+
+int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile)
+{
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *substream;
+	snd_rawmidi_runtime_t *runtime;
+
+	snd_assert(rfile != NULL, return -ENXIO);
+	snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO);
+	rmidi = rfile->rmidi;
+	down(&rmidi->open_mutex);
+	if (rfile->input != NULL) {
+		substream = rfile->input;
+		rfile->input = NULL;
+		runtime = substream->runtime;
+		runtime->trigger = 0;
+		substream->ops->trigger(substream, 0);
+		substream->ops->close(substream);
+		snd_rawmidi_done_buffer(runtime);
+		if (runtime->private_free != NULL)
+			runtime->private_free(substream);
+		kfree(runtime);
+		substream->runtime = NULL;
+		substream->opened = 0;
+		rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--;
+	}
+	if (rfile->output != NULL) {
+		substream = rfile->output;
+		rfile->output = NULL;
+		if (--substream->use_count == 0) {
+			runtime = substream->runtime;
+			if (substream->active_sensing) {
+				unsigned char buf = 0xfe;
+				/* sending single active sensing message to shut the device up */
+				snd_rawmidi_kernel_write(substream, &buf, 1);
+			}
+			if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
+				substream->ops->trigger(substream, 0);
+			substream->ops->close(substream);
+			snd_rawmidi_done_buffer(runtime);
+			if (runtime->private_free != NULL)
+				runtime->private_free(substream);
+			kfree(runtime);
+			substream->runtime = NULL;
+			substream->opened = 0;
+			substream->append = 0;
+		}
+		rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;
+	}
+	up(&rmidi->open_mutex);
+	dec_mod_count(rmidi->card->module);
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static int snd_rawmidi_release(struct inode *inode, struct file *file)
+{
+	snd_rawmidi_file_t *rfile;
+	int err;
+
+	rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO);
+	err = snd_rawmidi_kernel_release(rfile);
+	wake_up(&rfile->rmidi->open_wait);
+	snd_magic_kfree(rfile);
+	return err;
+}
+
+int snd_rawmidi_info(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t *info)
+{
+	snd_rawmidi_t *rmidi = substream->rmidi;
+	memset(info, 0, sizeof(*info));
+	info->card = rmidi->card->number;
+	info->device = rmidi->device;
+	info->subdevice = substream->number;
+	info->stream = substream->stream;
+	info->flags = rmidi->info_flags;
+	strcpy(info->id, rmidi->id);
+	strcpy(info->name, rmidi->name);
+	strcpy(info->subname, substream->name);
+	info->subdevices_count = substream->pstr->substream_count;
+	info->subdevices_avail = (substream->pstr->substream_count -
+				  substream->pstr->substream_opened);
+	return 0;
+}
+
+static int snd_rawmidi_info_user(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t * _info)
+{
+	snd_rawmidi_info_t info;
+	int err;
+	if ((err = snd_rawmidi_info(substream, &info)) < 0)
+		return err;
+	if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t)))
+		return -EFAULT;
+	return 0;
+}
+
+int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info)
+{
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_str_t *pstr;
+	snd_rawmidi_substream_t *substream;
+	struct list_head *list;
+	if (info->device >= SNDRV_RAWMIDI_DEVICES)
+		return -ENXIO;
+	rmidi = snd_rawmidi_devices[card->number * SNDRV_RAWMIDI_DEVICES + info->device];
+	if (info->stream < 0 || info->stream > 1)
+		return -EINVAL;
+	pstr = &rmidi->streams[info->stream];
+	if (pstr->substream_count == 0)
+		return -ENOENT;
+	if (info->subdevice >= pstr->substream_count)
+		return -ENXIO;
+	list_for_each(list, &pstr->substreams) {
+		substream = list_entry(list, snd_rawmidi_substream_t, list);
+		if (substream->number == info->subdevice)
+			return snd_rawmidi_info(substream, info);
+	}
+	return -ENXIO;
+}
+
+static int snd_rawmidi_info_select_user(snd_card_t *card,
+					snd_rawmidi_info_t *_info)
+{
+	int err;
+	snd_rawmidi_info_t info;
+	if (get_user(info.device, &_info->device))
+		return -EFAULT;
+	if (get_user(info.stream, &_info->stream))
+		return -EFAULT;
+	if (get_user(info.subdevice, &_info->subdevice))
+		return -EFAULT;
+	if ((err = snd_rawmidi_info_select(card, &info)) < 0)
+		return err;
+	if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t)))
+		return -EFAULT;
+	return 0;
+}
+
+int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream,
+			      snd_rawmidi_params_t * params)
+{
+	char *newbuf;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	
+	if (substream->append && substream->use_count > 1)
+		return -EBUSY;
+	snd_rawmidi_drain_output(substream);
+	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
+		return -EINVAL;
+	}
+	if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
+		return -EINVAL;
+	}
+	if (params->buffer_size != runtime->buffer_size) {
+		if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		kfree(runtime->buffer);
+		runtime->buffer = newbuf;
+		runtime->buffer_size = params->buffer_size;
+	}
+	runtime->avail_min = params->avail_min;
+	substream->active_sensing = !params->no_active_sensing;
+	return 0;
+}
+
+int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream,
+			     snd_rawmidi_params_t * params)
+{
+	char *newbuf;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	snd_rawmidi_drain_input(substream);
+	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
+		return -EINVAL;
+	}
+	if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
+		return -EINVAL;
+	}
+	if (params->buffer_size != runtime->buffer_size) {
+		if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		kfree(runtime->buffer);
+		runtime->buffer = newbuf;
+		runtime->buffer_size = params->buffer_size;
+	}
+	runtime->avail_min = params->avail_min;
+	return 0;
+}
+
+static int snd_rawmidi_output_status(snd_rawmidi_substream_t * substream,
+				     snd_rawmidi_status_t * status)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	memset(status, 0, sizeof(*status));
+	status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+	spin_lock_irq(&runtime->lock);
+	status->avail = runtime->avail;
+	spin_unlock_irq(&runtime->lock);
+	return 0;
+}
+
+static int snd_rawmidi_input_status(snd_rawmidi_substream_t * substream,
+				    snd_rawmidi_status_t * status)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	memset(status, 0, sizeof(*status));
+	status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
+	spin_lock_irq(&runtime->lock);
+	status->avail = runtime->avail;
+	status->xruns = runtime->xruns;
+	runtime->xruns = 0;
+	spin_unlock_irq(&runtime->lock);
+	return 0;
+}
+
+static int snd_rawmidi_ioctl(struct inode *inode, struct file *file,
+			     unsigned int cmd, unsigned long arg)
+{
+	snd_rawmidi_file_t *rfile;
+
+	rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO);
+	if (((cmd >> 8) & 0xff) != 'W')
+		return -ENOTTY;
+	switch (cmd) {
+	case SNDRV_RAWMIDI_IOCTL_PVERSION:
+		return put_user(SNDRV_RAWMIDI_VERSION, (int *)arg) ? -EFAULT : 0;
+	case SNDRV_RAWMIDI_IOCTL_INFO:
+	{
+		snd_rawmidi_stream_t stream;
+		snd_rawmidi_info_t *info = (snd_rawmidi_info_t *) arg;
+		if (get_user(stream, &info->stream))
+			return -EFAULT;
+		switch (stream) {
+		case SNDRV_RAWMIDI_STREAM_INPUT:
+			return snd_rawmidi_info_user(rfile->input, info);
+		case SNDRV_RAWMIDI_STREAM_OUTPUT:
+			return snd_rawmidi_info_user(rfile->output, info);
+		default:
+			return -EINVAL;
+		}
+	}
+	case SNDRV_RAWMIDI_IOCTL_PARAMS:
+	{
+		snd_rawmidi_params_t params;
+		int err;
+		if (copy_from_user(&params, (snd_rawmidi_params_t *) arg, sizeof(snd_rawmidi_params_t)))
+			return -EFAULT;
+		switch (params.stream) {
+		case SNDRV_RAWMIDI_STREAM_OUTPUT:
+			if (rfile->output == NULL)
+				return -EINVAL;
+			return snd_rawmidi_output_params(rfile->output, &params);
+		case SNDRV_RAWMIDI_STREAM_INPUT:
+			if (rfile->input == NULL)
+				return -EINVAL;
+			return snd_rawmidi_input_params(rfile->input, &params);
+		default:
+			return -EINVAL;
+		}
+		if (copy_to_user((snd_rawmidi_params_t *) arg, &params, sizeof(snd_rawmidi_params_t)))
+			return -EFAULT;
+		return err;
+	}
+	case SNDRV_RAWMIDI_IOCTL_STATUS:
+	{
+		int err = 0;
+		snd_rawmidi_status_t status;
+		if (copy_from_user(&status, (snd_rawmidi_status_t *) arg, sizeof(snd_rawmidi_status_t)))
+			return -EFAULT;
+		switch (status.stream) {
+		case SNDRV_RAWMIDI_STREAM_OUTPUT:
+			if (rfile->output == NULL)
+				return -EINVAL;
+			err = snd_rawmidi_output_status(rfile->output, &status);
+			break;
+		case SNDRV_RAWMIDI_STREAM_INPUT:
+			if (rfile->input == NULL)
+				return -EINVAL;
+			err = snd_rawmidi_input_status(rfile->input, &status);
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (err < 0)
+			return err;
+		if (copy_to_user((snd_rawmidi_status_t *) arg, &status, sizeof(snd_rawmidi_status_t)))
+			return -EFAULT;
+		return 0;
+	}
+	case SNDRV_RAWMIDI_IOCTL_DROP:
+	{
+		int val;
+		if (get_user(val, (long *) arg))
+			return -EFAULT;
+		switch (val) {
+		case SNDRV_RAWMIDI_STREAM_OUTPUT:
+			if (rfile->output == NULL)
+				return -EINVAL;
+			return snd_rawmidi_drop_output(rfile->output);
+		default:
+			return -EINVAL;
+		}
+	}
+	case SNDRV_RAWMIDI_IOCTL_DRAIN:
+	{
+		int val;
+		if (get_user(val, (long *) arg))
+			return -EFAULT;
+		switch (val) {
+		case SNDRV_RAWMIDI_STREAM_OUTPUT:
+			if (rfile->output == NULL)
+				return -EINVAL;
+			return snd_rawmidi_drain_output(rfile->output);
+		case SNDRV_RAWMIDI_STREAM_INPUT:
+			if (rfile->input == NULL)
+				return -EINVAL;
+			return snd_rawmidi_drain_input(rfile->input);
+		default:
+			return -EINVAL;
+		}
+	}
+#ifdef CONFIG_SND_DEBUG
+	default:
+		snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd);
+#endif
+	}
+	return -ENOTTY;
+}
+
+int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
+			      unsigned int cmd, unsigned long arg)
+{
+	unsigned int tmp;
+
+	tmp = card->number * SNDRV_RAWMIDI_DEVICES;
+	switch (cmd) {
+	case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
+	{
+		int device;
+		
+		if (get_user(device, (int *)arg))
+			return -EFAULT;
+		device = device < 0 ? 0 : device + 1;
+		while (device < SNDRV_RAWMIDI_DEVICES) {
+			if (snd_rawmidi_devices[tmp + device])
+				break;
+			device++;
+		}
+		if (device == SNDRV_RAWMIDI_DEVICES)
+			device = -1;
+		if (put_user(device, (int *)arg))
+			return -EFAULT;
+		return 0;
+	}
+	case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
+	{
+		int val;
+		
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		control->prefer_rawmidi_subdevice = val;
+		return 0;
+	}
+	case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
+		return snd_rawmidi_info_select_user(card, (snd_rawmidi_info_t *)arg);
+	}
+	return -ENOIOCTLCMD;
+}
+
+void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream)
+{
+	/* TODO: reset current state */
+}
+
+int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count)
+{
+	unsigned long flags;
+	int result = 0, count1;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	if (runtime->buffer == NULL) {
+		snd_printd("snd_rawmidi_receive: input is not active!!!\n");
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&runtime->lock, flags);
+	if (count == 1) {	/* special case, faster code */
+		substream->bytes++;
+		if (runtime->avail < runtime->buffer_size) {
+			runtime->buffer[runtime->hw_ptr++] = buffer[0];
+			runtime->hw_ptr %= runtime->buffer_size;
+			runtime->avail++;
+			result++;
+		} else {
+			runtime->xruns++;
+		}
+	} else {
+		substream->bytes += count;
+		count1 = runtime->buffer_size - runtime->hw_ptr;
+		if (count1 > count)
+			count1 = count;
+		if (count1 > runtime->buffer_size - runtime->avail)
+			count1 = runtime->buffer_size - runtime->avail;
+		memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
+		runtime->hw_ptr += count1;
+		runtime->hw_ptr %= runtime->buffer_size;
+		runtime->avail += count1;
+		count -= count1;
+		result += count1;
+		if (count > 0) {
+			buffer += count1;
+			count1 = count;
+			if (count1 > runtime->buffer_size - runtime->avail) {
+				count1 = runtime->buffer_size - runtime->avail;
+				runtime->xruns = count - count1;
+			}
+			if (count1 > 0) {
+				memcpy(runtime->buffer, buffer, count1);
+				runtime->hw_ptr = count1;
+				runtime->avail += count1;
+				result += count1;
+			}
+		}
+	}
+	if (result > 0 && runtime->event == NULL) {
+		if (snd_rawmidi_ready(substream))
+			wake_up(&runtime->sleep);
+	}
+	spin_unlock_irqrestore(&runtime->lock, flags);
+	if (result > 0 && runtime->event)
+		runtime->event(substream);
+	return result;
+}
+
+static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream,
+				     unsigned char *buf, long count, int kernel)
+{
+	unsigned long flags;
+	long result = 0, count1;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	while (count > 0 && runtime->avail) {
+		count1 = runtime->buffer_size - runtime->appl_ptr;
+		if (count1 > count)
+			count1 = count;
+		spin_lock_irqsave(&runtime->lock, flags);
+		if (count1 > runtime->avail)
+			count1 = runtime->avail;
+		if (kernel) {
+			memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1);
+		} else {
+			if (copy_to_user(buf + result, runtime->buffer + runtime->appl_ptr, count1)) {
+				spin_unlock_irqrestore(&runtime->lock, flags);
+				return result > 0 ? result : -EFAULT;
+			}
+		}
+		runtime->appl_ptr += count1;
+		runtime->appl_ptr %= runtime->buffer_size;
+		runtime->avail -= count1;
+		spin_unlock_irqrestore(&runtime->lock, flags);
+		result += count1;
+		count -= count1;
+	}
+	return result;
+}
+
+long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count)
+{
+	substream->runtime->trigger = 1;
+	substream->ops->trigger(substream, 1);
+	return snd_rawmidi_kernel_read1(substream, buf, count, 1);
+}
+
+static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff_t *offset)
+{
+	long result;
+	int count1;
+	snd_rawmidi_file_t *rfile;
+	snd_rawmidi_substream_t *substream;
+	snd_rawmidi_runtime_t *runtime;
+
+	rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO);
+	substream = rfile->input;
+	if (substream == NULL)
+		return -EIO;
+	runtime = substream->runtime;
+	runtime->trigger = 1;
+	substream->ops->trigger(substream, 1);
+	result = 0;
+	while (count > 0) {
+		spin_lock_irq(&runtime->lock);
+		while (!snd_rawmidi_ready(substream)) {
+			wait_queue_t wait;
+			if (file->f_flags & O_NONBLOCK) {
+				spin_unlock_irq(&runtime->lock);
+				return result > 0 ? result : -EAGAIN;
+			}
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&runtime->sleep, &wait);
+			spin_unlock_irq(&runtime->lock);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&runtime->sleep, &wait);
+			if (signal_pending(current))
+				return result > 0 ? result : -ERESTARTSYS;
+			if (!runtime->avail)
+				return result > 0 ? result : -EIO;
+			spin_lock_irq(&runtime->lock);
+		}
+		spin_unlock_irq(&runtime->lock);
+		count1 = snd_rawmidi_kernel_read1(substream, buf, count, 0);
+		result += count1;
+		buf += count1;
+		count -= count1;
+	}
+	return result;
+}
+
+void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream)
+{
+	/* TODO: reset current state */
+}
+
+int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream)
+{
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	int result;
+	unsigned long flags;
+
+	if (runtime->buffer == NULL) {
+		snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n");
+		return 1;
+	}
+	spin_lock_irqsave(&runtime->lock, flags);
+	result = runtime->avail >= runtime->buffer_size;
+	if (result)
+		runtime->trigger = 1;
+	spin_unlock_irqrestore(&runtime->lock, flags);
+	return result;		
+}
+
+int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count)
+{
+	unsigned long flags;
+	int result, count1;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	if (runtime->buffer == NULL) {
+		snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n");
+		return -EINVAL;
+	}
+	result = 0;
+	spin_lock_irqsave(&runtime->lock, flags);
+	if (runtime->avail >= runtime->buffer_size) {
+		/* warning: lowlevel layer MUST trigger down the hardware */
+		runtime->trigger = 0;
+		goto __skip;
+	}
+	if (count == 1) {	/* special case, faster code */
+		*buffer = runtime->buffer[runtime->hw_ptr];
+		result++;
+	} else {
+		count1 = runtime->buffer_size - runtime->hw_ptr;
+		if (count1 > count)
+			count1 = count;
+		if (count1 > runtime->buffer_size - runtime->avail)
+			count1 = runtime->buffer_size - runtime->avail;
+		memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
+		count -= count1;
+		result += count1;
+		if (count > 0)
+			memcpy(buffer + count1, runtime->buffer, count);
+	}
+      __skip:
+	spin_unlock_irqrestore(&runtime->lock, flags);
+	return result;
+}
+
+int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count)
+{
+	unsigned long flags;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	if (runtime->buffer == NULL) {
+		snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n");
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&runtime->lock, flags);
+	snd_assert(runtime->avail + count <= runtime->buffer_size, );
+	runtime->hw_ptr += count;
+	runtime->hw_ptr %= runtime->buffer_size;
+	runtime->avail += count;
+	substream->bytes += count;
+	if (runtime->drain)
+		wake_up(&runtime->sleep);
+	else
+		if (count > 0 && runtime->event == NULL)
+			if (snd_rawmidi_ready(substream))
+				wake_up(&runtime->sleep);
+	spin_unlock_irqrestore(&runtime->lock, flags);
+	if (count > 0 && runtime->event)
+		runtime->event(substream);
+	return count;
+}
+
+int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count)
+{
+	count = snd_rawmidi_transmit_peek(substream, buffer, count);
+	if (count < 0)
+		return count;
+	return snd_rawmidi_transmit_ack(substream, count);
+}
+
+static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count, int kernel)
+{
+	unsigned long flags;
+	long count1, result;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+	snd_assert(buf != NULL, return -EINVAL);
+	snd_assert(runtime->buffer != NULL, return -EINVAL);
+
+	result = 0;
+	spin_lock_irqsave(&runtime->lock, flags);
+	if (substream->append) {
+		if (runtime->avail < count) {
+			spin_unlock_irqrestore(&runtime->lock, flags);
+			return -EAGAIN;
+		}
+	}
+	while (count > 0 && runtime->avail > 0) {
+		count1 = runtime->buffer_size - runtime->appl_ptr;
+		if (count1 > count)
+			count1 = count;
+		if (count1 > runtime->avail)
+			count1 = runtime->avail;
+		if (kernel) {
+			memcpy(runtime->buffer + runtime->appl_ptr, buf, count1);
+		} else {
+			if (copy_from_user(runtime->buffer + runtime->appl_ptr, buf, count1)) {
+				result = result > 0 ? result : -EFAULT;
+				goto __end;
+			}
+		}
+		runtime->appl_ptr += count1;
+		runtime->appl_ptr %= runtime->buffer_size;
+		runtime->avail -= count1;
+		result += count1;
+		buf += count1;
+		count -= count1;
+	}
+      __end:
+	if (result > 0)
+		runtime->trigger = 1;
+	spin_unlock_irqrestore(&runtime->lock, flags);
+	substream->ops->trigger(substream, 1);
+	return result;
+}
+
+long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count)
+{
+	return snd_rawmidi_kernel_write1(substream, buf, count, 1);
+}
+
+static ssize_t snd_rawmidi_write(struct file *file, const char *buf, size_t count, loff_t *offset)
+{
+	long result, timeout;
+	int count1;
+	snd_rawmidi_file_t *rfile;
+	snd_rawmidi_runtime_t *runtime;
+	snd_rawmidi_substream_t *substream;
+
+	rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO);
+	substream = rfile->output;
+	runtime = substream->runtime;
+	/* we cannot put an atomic message to our buffer */
+	if (substream->append && count > runtime->buffer_size)
+		return -EIO;
+	result = 0;
+	while (count > 0) {
+		spin_lock_irq(&runtime->lock);
+		while (!snd_rawmidi_ready_append(substream, count)) {
+			wait_queue_t wait;
+			if (file->f_flags & O_NONBLOCK) {
+				spin_unlock_irq(&runtime->lock);
+				return result > 0 ? result : -EAGAIN;
+			}
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&runtime->sleep, &wait);
+			spin_unlock_irq(&runtime->lock);
+			set_current_state(TASK_INTERRUPTIBLE);
+			timeout = schedule_timeout(30 * HZ);
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&runtime->sleep, &wait);
+			if (signal_pending(current))
+				return result > 0 ? result : -ERESTARTSYS;
+			if (!runtime->avail && !timeout)
+				return result > 0 ? result : -EIO;
+			spin_lock_irq(&runtime->lock);
+		}
+		spin_unlock_irq(&runtime->lock);
+		count1 = snd_rawmidi_kernel_write1(substream, buf, count, 0);
+		if (count1 < 0)
+			continue;
+		result += count1;
+		buf += count1;
+		if (count1 < count && (file->f_flags & O_NONBLOCK))
+			break;
+		count -= count1;
+	}
+	while (file->f_flags & O_SYNC) {
+		spin_lock_irq(&runtime->lock);
+		while (runtime->avail != runtime->buffer_size) {
+			wait_queue_t wait;
+			unsigned int last_avail = runtime->avail;
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&runtime->sleep, &wait);
+			spin_unlock_irq(&runtime->lock);
+			set_current_state(TASK_INTERRUPTIBLE);
+			timeout = schedule_timeout(30 * HZ);
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&runtime->sleep, &wait);
+			if (signal_pending(current))
+				return result > 0 ? result : -ERESTARTSYS;
+			if (runtime->avail == last_avail && !timeout)
+				return result > 0 ? result : -EIO;
+			spin_lock_irq(&runtime->lock);
+		}
+		spin_unlock_irq(&runtime->lock);
+	}
+	return result;
+}
+
+static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
+{
+	snd_rawmidi_file_t *rfile;
+	snd_rawmidi_runtime_t *runtime;
+	unsigned int mask;
+
+	rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return 0);
+	if (rfile->input != NULL) {
+		runtime = rfile->input->runtime;
+		runtime->trigger = 1;
+		rfile->input->ops->trigger(rfile->input, 1);
+		poll_wait(file, &runtime->sleep, wait);
+	}
+	if (rfile->output != NULL) {
+		runtime = rfile->output->runtime;
+		poll_wait(file, &runtime->sleep, wait);
+	}
+	mask = 0;
+	if (rfile->input != NULL) {
+		if (snd_rawmidi_ready(rfile->input))
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (rfile->output != NULL) {
+		if (snd_rawmidi_ready(rfile->output))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	return mask;
+}
+
+/*
+
+ */
+
+static void snd_rawmidi_proc_info_read(snd_info_entry_t *entry,
+				       snd_info_buffer_t * buffer)
+{
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *substream;
+	snd_rawmidi_runtime_t *runtime;
+	struct list_head *list;
+
+	rmidi = snd_magic_cast(snd_rawmidi_t, entry->private_data, return);
+	snd_iprintf(buffer, "%s\n\n", rmidi->name);
+	down(&rmidi->open_mutex);
+	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
+		list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+			substream = list_entry(list, snd_rawmidi_substream_t, list);
+			snd_iprintf(buffer,
+				    "Output %d\n"
+				    "  Tx bytes     : %lu\n",
+				    substream->number,
+				    (unsigned long) substream->bytes);
+			if (substream->opened) {
+				runtime = substream->runtime;
+				snd_iprintf(buffer,
+				    "  Mode         : %s\n"
+				    "  Buffer size  : %lu\n"
+				    "  Avail        : %lu\n",
+				    runtime->oss ? "OSS compatible" : "native",
+				    (unsigned long) runtime->buffer_size,
+				    (unsigned long) runtime->avail);
+			}
+		}
+	}
+	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
+		list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+			substream = list_entry(list, snd_rawmidi_substream_t, list);
+			snd_iprintf(buffer,
+				    "Input %d\n"
+				    "  Rx bytes     : %lu\n",
+				    substream->number,
+				    (unsigned long) substream->bytes);
+			if (substream->opened) {
+				runtime = substream->runtime;
+				snd_iprintf(buffer,
+					    "  Buffer size  : %lu\n"
+					    "  Avail        : %lu\n"
+					    "  Overruns     : %lu\n",
+					    (unsigned long) runtime->buffer_size,
+					    (unsigned long) runtime->avail,
+					    (unsigned long) runtime->xruns);
+			}
+		}
+	}
+	up(&rmidi->open_mutex);
+}
+
+/*
+ *  Register functions
+ */
+
+static struct file_operations snd_rawmidi_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		snd_rawmidi_read,
+	write:		snd_rawmidi_write,
+	open:		snd_rawmidi_open,
+	release:	snd_rawmidi_release,
+	poll:		snd_rawmidi_poll,
+	ioctl:		snd_rawmidi_ioctl,
+};
+
+static snd_minor_t snd_rawmidi_reg =
+{
+	comment:	"raw midi",
+	f_ops:		&snd_rawmidi_f_ops,
+};
+
+static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi,
+					snd_rawmidi_str_t *stream,
+					int direction,
+					int count)
+{
+	snd_rawmidi_substream_t *substream;
+	int idx;
+
+	INIT_LIST_HEAD(&stream->substreams);
+	for (idx = 0; idx < count; idx++) {
+		substream = snd_kcalloc(sizeof(snd_rawmidi_substream_t), GFP_KERNEL);
+		if (substream == NULL)
+			return -ENOMEM;
+		substream->stream = direction;
+		substream->number = idx;
+		substream->rmidi = rmidi;
+		substream->pstr = stream;
+		list_add_tail(&substream->list, &stream->substreams);
+		stream->substream_count++;
+	}
+	return 0;
+}
+
+int snd_rawmidi_new(snd_card_t * card, char *id, int device,
+		    int output_count, int input_count,
+		    snd_rawmidi_t ** rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_rawmidi_dev_free,
+		dev_register:	snd_rawmidi_dev_register,
+		dev_unregister:	snd_rawmidi_dev_unregister
+	};
+
+	snd_assert(rrawmidi != NULL, return -EINVAL);
+	*rrawmidi = NULL;
+	snd_assert(card != NULL, return -ENXIO);
+	rmidi = snd_magic_kcalloc(snd_rawmidi_t, 0, GFP_KERNEL);
+	if (rmidi == NULL)
+		return -ENOMEM;
+	rmidi->card = card;
+	rmidi->device = device;
+	init_MUTEX(&rmidi->open_mutex);
+	init_waitqueue_head(&rmidi->open_wait);
+	if (id != NULL)
+		strncpy(rmidi->id, id, sizeof(rmidi->id) - 1);
+	if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], SNDRV_RAWMIDI_STREAM_INPUT, input_count)) < 0) {
+		snd_rawmidi_free(rmidi);
+		return err;
+	}
+	if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], SNDRV_RAWMIDI_STREAM_OUTPUT, output_count)) < 0) {
+		snd_rawmidi_free(rmidi);
+		return err;
+	}
+	if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) {
+		snd_rawmidi_free(rmidi);
+		return err;
+	}
+	*rrawmidi = rmidi;
+	return 0;
+}
+
+static void snd_rawmidi_free_substreams(snd_rawmidi_str_t *stream)
+{
+	snd_rawmidi_substream_t *substream;
+
+	while (!list_empty(&stream->substreams)) {
+		substream = list_entry(stream->substreams.next, snd_rawmidi_substream_t, list);
+		list_del(&substream->list);
+		kfree(substream);
+	}
+}
+
+static int snd_rawmidi_free(snd_rawmidi_t *rmidi)
+{
+	snd_assert(rmidi != NULL, return -ENXIO);	
+	snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
+	snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
+	if (rmidi->private_free)
+		rmidi->private_free(rmidi);
+	snd_magic_kfree(rmidi);
+	return 0;
+}
+
+static int snd_rawmidi_dev_free(snd_device_t *device)
+{
+	snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO);
+	return snd_rawmidi_free(rmidi);
+}
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device)
+{
+	snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->private_data, return);
+	rmidi->seq_dev = NULL;
+}
+#endif
+
+static int snd_rawmidi_dev_register(snd_device_t *device)
+{
+	int idx, err;
+	snd_info_entry_t *entry;
+	char name[16];
+	snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO);
+
+	if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
+		return -ENOMEM;
+	down(&register_mutex);
+	idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device;
+	if (snd_rawmidi_devices[idx] != NULL) {
+		up(&register_mutex);
+		return -EBUSY;
+	}
+	snd_rawmidi_devices[idx] = rmidi;
+	sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device);
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
+				       rmidi->card, rmidi->device,
+				       &snd_rawmidi_reg, name)) < 0) {
+		snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device);
+		snd_rawmidi_devices[idx] = NULL;
+		up(&register_mutex);
+		return err;
+	}
+	if (rmidi->ops && rmidi->ops->dev_register &&
+	    (err = rmidi->ops->dev_register(rmidi)) < 0) {
+		snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
+		snd_rawmidi_devices[idx] = NULL;
+		up(&register_mutex);
+		return err;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	rmidi->ossreg = 0;
+	if (rmidi->device == snd_midi_map[rmidi->card->number]) {
+		if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
+					    rmidi->card, 0, &snd_rawmidi_reg, name) < 0) {
+			snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0);
+		} else {
+			rmidi->ossreg++;
+			snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name);
+		}
+	}
+	if (rmidi->device == snd_amidi_map[rmidi->card->number]) {
+		if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
+					    rmidi->card, 1, &snd_rawmidi_reg, name) < 0) {
+			snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1);
+		} else {
+			rmidi->ossreg++;
+		}
+	}
+#endif
+	up(&register_mutex);
+	sprintf(name, "midi%d", rmidi->device);
+	entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = rmidi;
+		entry->c.text.read_size = 1024;
+		entry->c.text.read = snd_rawmidi_proc_info_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	rmidi->proc_entry = entry;
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
+		if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
+			rmidi->seq_dev->private_data = rmidi;
+			rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
+			sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device);
+			snd_device_register(rmidi->card, rmidi->seq_dev);
+		}
+	}
+#endif
+	return 0;
+}
+
+static int snd_rawmidi_dev_unregister(snd_device_t *device)
+{
+	int idx;
+	snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO);
+
+	snd_assert(rmidi != NULL, return -ENXIO);
+	down(&register_mutex);
+	idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device;
+	if (snd_rawmidi_devices[idx] != rmidi) {
+		up(&register_mutex);
+		return -EINVAL;
+	}
+	if (rmidi->proc_entry) {
+		snd_info_unregister(rmidi->proc_entry);
+		rmidi->proc_entry = NULL;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	if (rmidi->ossreg) {
+		if (rmidi->device == snd_midi_map[rmidi->card->number]) {
+			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0);
+			snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number);
+		}
+		if (rmidi->device == snd_amidi_map[rmidi->card->number])
+			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1);
+		rmidi->ossreg = 0;
+	}
+#endif
+	if (rmidi->ops && rmidi->ops->dev_unregister)
+		rmidi->ops->dev_unregister(rmidi);
+	snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
+	snd_rawmidi_devices[idx] = NULL;
+	up(&register_mutex);
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (rmidi->seq_dev) {
+		snd_device_free(rmidi->card, rmidi->seq_dev);
+		rmidi->seq_dev = NULL;
+	}
+#endif
+	return snd_rawmidi_free(rmidi);
+}
+
+void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops)
+{
+	struct list_head *list;
+	snd_rawmidi_substream_t *substream;
+	
+	list_for_each(list, &rmidi->streams[stream].substreams) {
+		substream = list_entry(list, snd_rawmidi_substream_t, list);
+		substream->ops = ops;
+	}
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static int __init alsa_rawmidi_init(void)
+{
+	int i;
+
+	snd_ctl_register_ioctl(snd_rawmidi_control_ioctl);
+#ifdef CONFIG_SND_OSSEMUL
+	/* check device map table */
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (snd_midi_map[i] < 0 || snd_midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
+			snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, snd_midi_map[i]);
+			snd_midi_map[i] = 0;
+		}
+		if (snd_amidi_map[i] < 0 || snd_amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
+			snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, snd_amidi_map[i]);
+			snd_amidi_map[i] = 1;
+		}
+	}
+#endif /* CONFIG_SND_OSSEMUL */
+	return 0;
+}
+
+static void __exit alsa_rawmidi_exit(void)
+{
+	snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl);
+}
+
+module_init(alsa_rawmidi_init)
+module_exit(alsa_rawmidi_exit)
+
+EXPORT_SYMBOL(snd_rawmidi_output_params);
+EXPORT_SYMBOL(snd_rawmidi_input_params);
+EXPORT_SYMBOL(snd_rawmidi_drop_output);
+EXPORT_SYMBOL(snd_rawmidi_drain_output);
+EXPORT_SYMBOL(snd_rawmidi_drain_input);
+EXPORT_SYMBOL(snd_rawmidi_receive_reset);
+EXPORT_SYMBOL(snd_rawmidi_receive);
+EXPORT_SYMBOL(snd_rawmidi_transmit_reset);
+EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
+EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
+EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
+EXPORT_SYMBOL(snd_rawmidi_transmit);
+EXPORT_SYMBOL(snd_rawmidi_new);
+EXPORT_SYMBOL(snd_rawmidi_set_ops);
+EXPORT_SYMBOL(snd_rawmidi_info);
+EXPORT_SYMBOL(snd_rawmidi_info_select);
+EXPORT_SYMBOL(snd_rawmidi_kernel_open);
+EXPORT_SYMBOL(snd_rawmidi_kernel_release);
+EXPORT_SYMBOL(snd_rawmidi_kernel_read);
+EXPORT_SYMBOL(snd_rawmidi_kernel_write);
diff -Nru linux/sound/core/rtctimer.c linux-2.4.19-pre5-mjc/sound/core/rtctimer.c
--- linux/sound/core/rtctimer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/rtctimer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,219 @@
+/*
+ *  RTC based high-frequency timer
+ *
+ *  Copyright (C) 2000 Takashi Iwai
+ *	based on rtctimer.c by Steve Ratcliffe
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+#include <sound/info.h>
+
+#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 12)	/* FIXME: which 2.2.x kernel? */
+#include <linux/rtc.h>
+#else
+#include <linux/mc146818rtc.h>
+#endif
+
+/* use tasklet for interrupt handling */
+#define USE_TASKLET
+
+#define RTC_FREQ	1024		/* default frequency */
+#define NANO_SEC	1000000000L	/* 10^9 in sec */
+
+/*
+ * prototypes
+ */
+static int rtctimer_open(snd_timer_t *t);
+static int rtctimer_close(snd_timer_t *t);
+static int rtctimer_start(snd_timer_t *t);
+static int rtctimer_stop(snd_timer_t *t);
+
+
+/*
+ * The hardware dependent description for this timer.
+ */
+static struct _snd_timer_hardware rtc_hw = {
+	flags:		SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
+	ticks:		100000000L,		/* FIXME: XXX */
+	open:		rtctimer_open,
+	close:		rtctimer_close,
+	start:		rtctimer_start,
+	stop:		rtctimer_stop,
+};
+
+int rtctimer_freq = RTC_FREQ;		/* frequency */
+static snd_timer_t *rtctimer;
+static atomic_t rtc_inc = ATOMIC_INIT(0);
+static rtc_task_t rtc_task;
+
+/* tasklet */
+#ifdef USE_TASKLET
+static struct tasklet_struct rtc_tq;
+#endif
+
+static int
+rtctimer_open(snd_timer_t *t)
+{
+	err = rtc_register(&rtc_task);
+	if (err < 0)
+		return err;
+	t->private_data = &rtc_task;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+rtctimer_close(snd_timer_t *t)
+{
+	rtc_task_t *rtc = t->private_data;
+	if (rtc) {
+		rtc_unregister(rtc);
+		t->private_data = NULL;
+	}
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int
+rtctimer_start(snd_timer_t *timer)
+{
+	rtc_task_t *rtc = timer->private_data;
+	snd_assert(rtc != NULL, return -EINVAL);
+	rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
+	rtc_control(rtc, RTC_PIE_ON, 0);
+	atomic_set(&rtc_inc, 0);
+	return 0;
+}
+
+static int
+rtctimer_stop(snd_timer_t *timer)
+{
+	rtc_task_t *rtc = timer->private_data;
+	snd_assert(rtc != NULL, return -EINVAL);
+	rtc_control(rtc, RTC_PIE_OFF, 0);
+	return 0;
+}
+
+/*
+ * interrupt
+ */
+static void rtctimer_interrupt(void *private_data)
+{
+	atomic_inc(&rtc_inc);
+#ifdef USE_TASKLET
+	tasklet_hi_schedule(&rtc_tq);
+#else
+	{
+		int ticks = atomic_read(&rtc_inc);
+		snd_timer_interrupt((snd_timer_t*)private_data, ticks);
+		atomic_sub(ticks, &rtc_inc);
+	}
+#endif /* USE_TASKLET */
+}
+
+#ifdef USE_TASKLET
+static void rtctimer_interrupt2(unsigned long private_data)
+{
+	snd_timer_t *timer = (snd_timer_t *)private_data;
+	int ticks;
+
+	snd_assert(timer != NULL, return);
+	do {
+		ticks = atomic_read(&rtc_inc);
+		snd_timer_interrupt(timer, ticks);
+	} while (!atomic_sub_and_test(ticks, &rtc_inc));
+}
+#endif /* USE_TASKLET */
+
+
+/*
+ *  ENTRY functions
+ */
+static int __init rtctimer_init(void)
+{
+	int order, err;
+	snd_timer_t *timer;
+
+	if (rtctimer_freq < 2 || rtctimer_freq > 8192) {
+		snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq);
+		return -EINVAL;
+	}
+	for (order = 1; rtctimer_freq > order; order <<= 1)
+		;
+	if (rtctimer_freq != order) {
+		snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq);
+		return -EINVAL;
+	}
+
+	/* Create a new timer and set up the fields */
+	err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
+	if (err < 0)
+		return err;
+
+#ifdef USE_TASKLET
+	tasklet_init(&rtc_tq, rtctimer_interrupt2, (unsigned long)timer);
+#endif /* USE_TASKLET */
+
+	strcpy(timer->name, "RTC timer");
+	timer->hw = rtc_hw;
+	timer->hw.resolution = NANO_SEC / rtctimer_freq;
+
+	/* set up RTC callback */
+	rtc_task.func = rtctimer_interrupt;
+	rtc_task.private_data = timer;
+
+	err = snd_timer_global_register(timer);
+	if (err < 0) {
+		snd_timer_global_free(timer);
+		return err;
+	}
+	rtctimer = timer; /* remember this */
+
+	return 0;
+}
+
+static void __exit rtctimer_exit(void)
+{
+	if (rtctimer) {
+		snd_timer_global_unregister(rtctimer);
+		rtctimer = NULL;
+	}
+}
+
+
+/*
+ * exported stuff
+ */
+module_init(rtctimer_init)
+module_exit(rtctimer_exit)
+
+MODULE_PARM(rtctimer_freq, "i");
+MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
+
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
diff -Nru linux/sound/core/seq/Makefile linux-2.4.19-pre5-mjc/sound/core/seq/Makefile
--- linux/sound/core/seq/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,108 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := sq.o
+
+subdir-y     := instr
+subdir-m     := $(subdir-y)
+obj-y        += instr/instr.o
+subdir-$(CONFIG_SND_SEQUENCER_OSS) += oss
+ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
+  obj-y	     += oss/oss.o
+endif
+
+list-multi   := snd-seq-device.o snd-seq.o snd-seq-midi.o snd-seq-midi-emul.o \
+		snd-seq-midi-event.o snd-seq-instr.o snd-seq-dummy.o \
+		snd-seq-virmidi.o
+
+export-objs  := seq_device.o seq.o seq_ports.o seq_instr.o seq_midi_emul.o \
+		seq_midi_event.o seq_virmidi.o
+
+snd-seq-device-objs := seq_device.o
+snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
+                seq_fifo.o seq_prioq.o seq_timer.o \
+                seq_system.o seq_ports.o seq_info.o seq_sync.o \
+		seq_midi_clock.o seq_mtc.o seq_dtl.o
+snd-seq-midi-objs := seq_midi.o
+snd-seq-midi-emul-objs := seq_midi_emul.o
+snd-seq-midi-event-objs := seq_midi_event.o
+snd-seq-instr-objs := seq_instr.o
+snd-seq-dummy-objs := seq_dummy.o
+snd-seq-virmidi-objs := seq_virmidi.o
+
+obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
+obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-midi-event.o
+obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_MTPAV) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_MPU401) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_ALS100) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_AZT2320) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_DT0197H) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_ES18XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_AD1816A) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_CS4232) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_CS4236) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_ES1688) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_GUSCLASSIC) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_GUSMAX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_INTERWAVE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_OPTI93X) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_SB8) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_SB16) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_SBAWE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_ES968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_WAVEFRONT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_ALS4000) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_CMIPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_CS4281) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_ENS1370) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_ENS1371) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_ES1938) += snd-seq-device.o snd-seq-midi-emul.o snd-seq.o snd-seq-instr.o snd-seq-midi.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_ES1968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+
+include $(TOPDIR)/Rules.make
+
+snd-seq-device.o: $(snd-seq-device-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-device-objs)
+
+snd-seq.o: $(snd-seq-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-objs)
+
+snd-seq-midi.o: $(snd-seq-midi-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-objs)
+
+snd-seq-midi-emul.o: $(snd-seq-midi-emul-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-emul-objs)
+
+snd-seq-midi-event.o: $(snd-seq-midi-event-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-event-objs)
+
+snd-seq-instr.o: $(snd-seq-instr-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-instr-objs)
+
+snd-seq-dummy.o: $(snd-seq-dummy-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-dummy-objs)
+
+snd-seq-virmidi.o: $(snd-seq-virmidi-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-virmidi-objs)
diff -Nru linux/sound/core/seq/instr/Makefile linux-2.4.19-pre5-mjc/sound/core/seq/instr/Makefile
--- linux/sound/core/seq/instr/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,61 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := instr.o
+
+list-multi   := snd-ainstr-fm.o snd-ainstr-simple.o snd-ainstr-gf1.o \
+		snd-ainstr-iw.o
+
+export-objs  := ainstr_fm.o ainstr_simple.o ainstr_gf1.o ainstr_iw.o
+
+snd-ainstr-fm-objs := ainstr_fm.o
+snd-ainstr-simple-objs := ainstr_simple.o
+snd-ainstr-gf1-objs := ainstr_gf1.o
+snd-ainstr-iw-objs := ainstr_iw.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS100) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_AZT2320) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_DT0197H) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_ES18XX) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_AD1816A) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_CS4232) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_CS4236) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_ES1688) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_GUSCLASSIC) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o
+obj-$(CONFIG_SND_GUSMAX) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o
+obj-$(CONFIG_SND_INTERWAVE) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_OPTI93X) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_SB8) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_SB16) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_SBAWE) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_WAVEFRONT) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_ALS4000) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_CMIPCI) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_CS4281) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_ES1938) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_FM801) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-ainstr-fm.o
+obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o
+obj-$(CONFIG_SND_YMFPCI) += snd-ainstr-fm.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ainstr-fm.o: $(snd-ainstr-fm-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-fm-objs)
+
+snd-ainstr-simple.o: $(snd-ainstr-simple-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-simple-objs)
+
+snd-ainstr-gf1.o: $(snd-ainstr-gf1-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-gf1-objs)
+
+snd-ainstr-iw.o: $(snd-ainstr-iw-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-iw-objs)
diff -Nru linux/sound/core/seq/instr/ainstr_fm.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_fm.c
--- linux/sound/core/seq/instr/ainstr_fm.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_fm.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,160 @@
+/*
+ *  FM (OPL2/3) Instrument routines
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *   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
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/ainstr_fm.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+char *snd_seq_fm_id = SNDRV_SEQ_INSTR_ID_OPL2_3;
+
+static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr,
+			  char *instr_data, long len, int atomic, int cmd)
+{
+	fm_instrument_t *ip;
+	fm_xinstrument_t ix;
+	int idx;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	/* copy instrument data */
+	if (len < sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != FM_STRU_INSTR)
+		return -EINVAL;
+	ip = (fm_instrument_t *)KINSTR_DATA(instr);
+	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+	ip->type = ix.type;
+	for (idx = 0; idx < 4; idx++) {
+		ip->op[idx].am_vib = ix.op[idx].am_vib;
+		ip->op[idx].ksl_level = ix.op[idx].ksl_level;
+		ip->op[idx].attack_decay = ix.op[idx].attack_decay;
+		ip->op[idx].sustain_release = ix.op[idx].sustain_release;
+		ip->op[idx].wave_select = ix.op[idx].wave_select;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		ip->feedback_connection[idx] = ix.feedback_connection[idx];
+	}
+	ip->echo_delay = ix.echo_delay;
+	ip->echo_atten = ix.echo_atten;
+	ip->chorus_spread = ix.chorus_spread;
+	ip->trnsps = ix.trnsps;
+	ip->fix_dur = ix.fix_dur;
+	ip->modes = ix.modes;
+	ip->fix_key = ix.fix_key;
+	return 0;
+}
+
+static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr,
+			  char *instr_data, long len, int atomic, int cmd)
+{
+	fm_instrument_t *ip;
+	fm_xinstrument_t ix;
+	int idx;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (fm_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = FM_STRU_INSTR;
+	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+	ix.type = ip->type;
+	for (idx = 0; idx < 4; idx++) {
+		ix.op[idx].am_vib = ip->op[idx].am_vib;
+		ix.op[idx].ksl_level = ip->op[idx].ksl_level;
+		ix.op[idx].attack_decay = ip->op[idx].attack_decay;
+		ix.op[idx].sustain_release = ip->op[idx].sustain_release;
+		ix.op[idx].wave_select = ip->op[idx].wave_select;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		ix.feedback_connection[idx] = ip->feedback_connection[idx];
+	}
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	ix.echo_delay = ip->echo_delay;
+	ix.echo_atten = ip->echo_atten;
+	ix.chorus_spread = ip->chorus_spread;
+	ix.trnsps = ip->trnsps;
+	ix.fix_dur = ip->fix_dur;
+	ix.modes = ip->modes;
+	ix.fix_key = ip->fix_key;
+	return 0;
+}
+
+static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr,
+			       long *size)
+{
+	*size = sizeof(fm_xinstrument_t);
+	return 0;
+}
+
+int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops,
+		    snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	// ops->private_data = private_data;
+	ops->add_len = sizeof(fm_instrument_t);
+	ops->instr_type = snd_seq_fm_id;
+	ops->put = snd_seq_fm_put;
+	ops->get = snd_seq_fm_get;
+	ops->get_size = snd_seq_fm_get_size;
+	// ops->remove = snd_seq_fm_remove;
+	// ops->notify = snd_seq_fm_notify;
+	ops->next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_fm_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_fm_exit(void)
+{
+}
+
+module_init(alsa_ainstr_fm_init)
+module_exit(alsa_ainstr_fm_exit)
+
+EXPORT_SYMBOL(snd_seq_fm_id);
+EXPORT_SYMBOL(snd_seq_fm_init);
diff -Nru linux/sound/core/seq/instr/ainstr_gf1.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_gf1.c
--- linux/sound/core/seq/instr/ainstr_gf1.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_gf1.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,361 @@
+/*
+ *   GF1 (GUS) Patch - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_gf1.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+char *snd_seq_gf1_id = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
+
+static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & GF1_WAVE_16BIT)
+		result <<= 1;
+	if (format & GF1_WAVE_STEREO)
+		result <<= 1;
+	return format;
+}
+
+static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops,
+					     gf1_instrument_t *ip,
+					     char **data,
+					     long *len,
+					     int atomic)
+{
+	gf1_wave_t *wp, *prev;
+	gf1_xwave_t xp;
+	int err, gfp_mask;
+	unsigned int real_size;
+	
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	if (*len < sizeof(xp))
+		return -EINVAL;
+	if (copy_from_user(&xp, *data, sizeof(xp)))
+		return -EFAULT;
+	*data += sizeof(xp);
+	*len -= sizeof(xp);
+	wp = (gf1_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask);
+	if (wp == NULL)
+		return -ENOMEM;
+	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+	wp->format = le32_to_cpu(xp.format);
+	wp->size = le32_to_cpu(xp.size);
+	wp->start = le32_to_cpu(xp.start);
+	wp->loop_start = le32_to_cpu(xp.loop_start);
+	wp->loop_end = le32_to_cpu(xp.loop_end);
+	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+	wp->flags = xp.flags;
+	wp->sample_rate = le32_to_cpu(xp.sample_rate);
+	wp->low_frequency = le32_to_cpu(xp.low_frequency);
+	wp->high_frequency = le32_to_cpu(xp.high_frequency);
+	wp->root_frequency = le32_to_cpu(xp.root_frequency);
+	wp->tune = le16_to_cpu(xp.tune);
+	wp->balance = xp.balance;
+	memcpy(wp->envelope_rate, xp.envelope_rate, 6);
+	memcpy(wp->envelope_offset, xp.envelope_offset, 6);
+	wp->tremolo_sweep = xp.tremolo_sweep;
+	wp->tremolo_rate = xp.tremolo_rate;
+	wp->tremolo_depth = xp.tremolo_depth;
+	wp->vibrato_sweep = xp.vibrato_sweep;
+	wp->vibrato_rate = xp.vibrato_rate;
+	wp->vibrato_depth = xp.vibrato_depth;
+	wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
+	wp->scale_factor = le16_to_cpu(xp.scale_factor);
+	real_size = snd_seq_gf1_size(wp->size, wp->format);
+	if (real_size > *len) {
+		kfree(wp);
+		return -ENOMEM;
+	}
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, wp,
+				      *data, real_size, atomic);
+		if (err < 0) {
+			kfree(wp);
+			return err;
+		}
+	}
+	*data += real_size;
+	*len -= real_size;
+	prev = ip->wave;
+	if (prev) {
+		while (prev->next) prev = prev->next;
+		prev->next = wp;
+	} else {
+		ip->wave = wp;
+	}
+	return 0;
+}
+
+static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops,
+				  gf1_wave_t *wave,
+				  int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, wave, atomic);
+	kfree(wave);
+}
+
+static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops,
+				   gf1_instrument_t *ip,
+				   int atomic)
+{
+	gf1_wave_t *wave;
+	
+	while ((wave = ip->wave) != NULL) {
+		ip->wave = wave->next;
+		snd_seq_gf1_wave_free(ops, wave, atomic);
+	}
+}
+
+static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr,
+			   char *instr_data, long len, int atomic, int cmd)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+	gf1_instrument_t *ip;
+	gf1_xinstrument_t ix;
+	int err, gfp_mask;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != GF1_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	ip->exclusion = le16_to_cpu(ix.exclusion);
+	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	/* copy layers */
+	while (len > sizeof(__u32)) {
+		__u32 stype;
+
+		if (copy_from_user(&stype, instr_data, sizeof(stype)))
+			return -EFAULT;
+		if (stype != GF1_STRU_WAVE) {
+			snd_seq_gf1_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		err = snd_seq_gf1_copy_wave_from_stream(ops,
+							ip,
+							&instr_data,
+							&len,
+							atomic);
+		if (err < 0) {
+			snd_seq_gf1_instr_free(ops, ip, atomic);
+			return err;
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops,
+					   gf1_instrument_t *ip,
+					   char **data,
+					   long *len,
+					   int atomic)
+{
+	gf1_wave_t *wp;
+	gf1_xwave_t xp;
+	int err;
+	unsigned int real_size;
+	
+	for (wp = ip->wave; wp; wp = wp->next) {
+		if (*len < sizeof(xp))
+			return -ENOMEM;
+		memset(&xp, 0, sizeof(xp));
+		xp.stype = GF1_STRU_WAVE;
+		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+		xp.format = cpu_to_le32(wp->format);
+		xp.size = cpu_to_le32(wp->size);
+		xp.start = cpu_to_le32(wp->start);
+		xp.loop_start = cpu_to_le32(wp->loop_start);
+		xp.loop_end = cpu_to_le32(wp->loop_end);
+		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+		xp.flags = wp->flags;
+		xp.sample_rate = cpu_to_le32(wp->sample_rate);
+		xp.low_frequency = cpu_to_le32(wp->low_frequency);
+		xp.high_frequency = cpu_to_le32(wp->high_frequency);
+		xp.root_frequency = cpu_to_le32(wp->root_frequency);
+		xp.tune = cpu_to_le16(wp->tune);
+		xp.balance = wp->balance;
+		memcpy(xp.envelope_rate, wp->envelope_rate, 6);
+		memcpy(xp.envelope_offset, wp->envelope_offset, 6);
+		xp.tremolo_sweep = wp->tremolo_sweep;
+		xp.tremolo_rate = wp->tremolo_rate;
+		xp.tremolo_depth = wp->tremolo_depth;
+		xp.vibrato_sweep = wp->vibrato_sweep;
+		xp.vibrato_rate = wp->vibrato_rate;
+		xp.vibrato_depth = wp->vibrato_depth;
+		xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
+		xp.scale_factor = cpu_to_le16(wp->scale_factor);
+		if (copy_to_user(*data, &xp, sizeof(xp)))
+			return -EFAULT;
+		*data += sizeof(xp);
+		*len -= sizeof(xp);
+		real_size = snd_seq_gf1_size(wp->size, wp->format);
+		if (*len < real_size)
+			return -ENOMEM;
+		if (ops->get_sample) {
+			err = ops->get_sample(ops->private_data, wp,
+					      *data, real_size, atomic);
+			if (err < 0)
+				return err;
+		}
+		*data += wp->size;
+		*len -= wp->size;
+	}
+	return 0;
+}
+
+static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr,
+			   char *instr_data, long len, int atomic, int cmd)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+	gf1_instrument_t *ip;
+	gf1_xinstrument_t ix;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = GF1_STRU_INSTR;
+	ix.exclusion = cpu_to_le16(ip->exclusion);
+	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	return snd_seq_gf1_copy_wave_to_stream(ops,
+					       ip,
+					       &instr_data,
+					       &len,
+					       atomic);
+}
+
+static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr,
+				long *size)
+{
+	long result;
+	gf1_instrument_t *ip;
+	gf1_wave_t *wp;
+
+	*size = 0;
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	result = sizeof(gf1_xinstrument_t);
+	for (wp = ip->wave; wp; wp = wp->next) {
+		result += sizeof(gf1_xwave_t);
+		result += wp->size;
+	}
+	*size = result;
+	return 0;
+}
+
+static int snd_seq_gf1_remove(void *private_data,
+			      snd_seq_kinstr_t *instr,
+                              int atomic)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+	gf1_instrument_t *ip;
+
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	snd_seq_gf1_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_gf1_notify(void *private_data,
+			       snd_seq_kinstr_t *instr,
+			       int what)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_gf1_init(snd_gf1_ops_t *ops,
+		     void *private_data,
+		     snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(gf1_instrument_t);
+	ops->kops.instr_type = snd_seq_gf1_id;
+	ops->kops.put = snd_seq_gf1_put;
+	ops->kops.get = snd_seq_gf1_get;
+	ops->kops.get_size = snd_seq_gf1_get_size;
+	ops->kops.remove = snd_seq_gf1_remove;
+	ops->kops.notify = snd_seq_gf1_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_gf1_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_gf1_exit(void)
+{
+}
+
+module_init(alsa_ainstr_gf1_init)
+module_exit(alsa_ainstr_gf1_exit)
+
+EXPORT_SYMBOL(snd_seq_gf1_id);
+EXPORT_SYMBOL(snd_seq_gf1_init);
diff -Nru linux/sound/core/seq/instr/ainstr_iw.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_iw.c
--- linux/sound/core/seq/instr/ainstr_iw.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_iw.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,626 @@
+/*
+ *   IWFFFF - AMD InterWave (tm) - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_iw.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+char *snd_seq_iwffff_id = SNDRV_SEQ_INSTR_ID_INTERWAVE;
+
+static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & IWFFFF_WAVE_16BIT)
+		result <<= 1;
+	if (format & IWFFFF_WAVE_STEREO)
+		result <<= 1;
+	return result;
+}
+
+static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp,
+						iwffff_xlfo_t *fx)
+{
+	fp->freq = le16_to_cpu(fx->freq);
+	fp->depth = le16_to_cpu(fx->depth);
+	fp->sweep = le16_to_cpu(fx->sweep);
+	fp->shape = fx->shape;
+	fp->delay = fx->delay;
+}
+
+static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
+					       iwffff_layer_t *lp,
+					       iwffff_env_t *ep,
+					       iwffff_xenv_t *ex,
+					       char **data,
+					       long *len,
+					       int gfp_mask)
+{
+	__u32 stype;
+	iwffff_env_record_t *rp, *rp_last;
+	iwffff_xenv_record_t rx;
+	iwffff_env_point_t *pp;
+	iwffff_xenv_point_t px;
+	int points_size, idx;
+
+	ep->flags = ex->flags;
+	ep->mode = ex->mode;
+	ep->index = ex->index;
+	rp_last = NULL;
+	while (1) {
+		if (*len < sizeof(__u32))
+			return -EINVAL;
+		if (copy_from_user(&stype, data, sizeof(stype)))
+			return -EFAULT;
+		if (stype == IWFFFF_STRU_WAVE)
+			return 0;
+		if (req_stype != stype) {
+			if (stype == IWFFFF_STRU_ENV_RECP ||
+			    stype == IWFFFF_STRU_ENV_RECV)
+				return 0;
+		}
+		if (*len < sizeof(rx))
+			return -EINVAL;
+		if (copy_from_user(&rx, *data, sizeof(rx)))
+			return -EFAULT;
+		*data += sizeof(rx);
+		*len -= sizeof(rx);
+		points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
+		if (points_size > *len)
+			return -EINVAL;
+		rp = (iwffff_env_record_t *)snd_kcalloc(sizeof(*rp) + points_size, gfp_mask);
+		if (rp == NULL)
+			return -ENOMEM;
+		rp->nattack = le16_to_cpu(rx.nattack);
+		rp->nrelease = le16_to_cpu(rx.nrelease);
+		rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
+		rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
+		rp->release_rate = le16_to_cpu(rx.release_rate);
+		rp->hirange = rx.hirange;
+		pp = (iwffff_env_point_t *)(rp + 1);
+		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+			if (copy_from_user(&px, *data, sizeof(px)))
+				return -EFAULT;
+			*data += sizeof(px);
+			*len -= sizeof(px);
+			pp->offset = le16_to_cpu(px.offset);
+			pp->rate = le16_to_cpu(px.rate);
+		}
+		if (ep->record == NULL) {
+			ep->record = rp;
+		} else {
+			rp_last = rp;
+		}
+		rp_last = rp;
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops,
+						iwffff_layer_t *lp,
+					        char **data,
+					        long *len,
+					        int atomic)
+{
+	iwffff_wave_t *wp, *prev;
+	iwffff_xwave_t xp;
+	int err, gfp_mask;
+	unsigned int real_size;
+	
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	if (*len < sizeof(xp))
+		return -EINVAL;
+	if (copy_from_user(&xp, *data, sizeof(xp)))
+		return -EFAULT;
+	*data += sizeof(xp);
+	*len -= sizeof(xp);
+	wp = (iwffff_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask);
+	if (wp == NULL)
+		return -ENOMEM;
+	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+	wp->format = le32_to_cpu(xp.format);
+	wp->address.memory = le32_to_cpu(xp.offset);
+	wp->size = le32_to_cpu(xp.size);
+	wp->start = le32_to_cpu(xp.start);
+	wp->loop_start = le32_to_cpu(xp.loop_start);
+	wp->loop_end = le32_to_cpu(xp.loop_end);
+	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+	wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
+	wp->attenuation = xp.attenuation;
+	wp->low_note = xp.low_note;
+	wp->high_note = xp.high_note;
+	real_size = snd_seq_iwffff_size(wp->size, wp->format);
+	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+		if (real_size > *len) {
+			kfree(wp);
+			return -ENOMEM;
+		}
+	}
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, wp,
+				      *data, real_size, atomic);
+		if (err < 0) {
+			kfree(wp);
+			return err;
+		}
+	}
+	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+		*data += real_size;
+		*len -= real_size;
+	}
+	prev = lp->wave;
+	if (prev) {
+		while (prev->next) prev = prev->next;
+		prev->next = wp;
+	} else {
+		lp->wave = wp;
+	}
+	return 0;
+}
+
+static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops,
+				    iwffff_env_t *env,
+				    int atomic)
+{
+	iwffff_env_record_t *rec;
+	
+	while ((rec = env->record) != NULL) {
+		env->record = rec->next;
+		kfree(rec);
+	}
+}
+				    
+static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops,
+				     iwffff_wave_t *wave,
+				     int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, wave, atomic);
+	kfree(wave);
+}
+
+static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops,
+                                      iwffff_instrument_t *ip,
+                                      int atomic)
+{
+	iwffff_layer_t *layer;
+	iwffff_wave_t *wave;
+	
+	while ((layer = ip->layer) != NULL) {
+		ip->layer = layer->next;
+		snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
+		snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
+		while ((wave = layer->wave) != NULL) {
+			layer->wave = wave->next;
+			snd_seq_iwffff_wave_free(ops, wave, atomic);
+		}
+		kfree(layer);
+	}
+}
+
+static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr,
+			      char *instr_data, long len, int atomic, int cmd)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+	iwffff_instrument_t *ip;
+	iwffff_xinstrument_t ix;
+	iwffff_layer_t *lp, *prev_lp;
+	iwffff_xlayer_t lx;
+	int err, gfp_mask;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != IWFFFF_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	ip->exclusion = le16_to_cpu(ix.exclusion);
+	ip->layer_type = le16_to_cpu(ix.layer_type);
+	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	/* copy layers */
+	prev_lp = NULL;
+	while (len > 0) {
+		if (len < sizeof(iwffff_xlayer_t)) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		if (copy_from_user(&lx, instr_data, sizeof(lx)))
+			return -EFAULT;
+		instr_data += sizeof(lx);
+		len -= sizeof(lx);
+		if (lx.stype != IWFFFF_STRU_LAYER) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		lp = (iwffff_layer_t *)snd_kcalloc(sizeof(*lp), gfp_mask);
+		if (lp == NULL) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -ENOMEM;
+		}
+		if (prev_lp) {
+			prev_lp->next = lp;
+		} else {
+			ip->layer = lp;
+		}
+		prev_lp = lp;
+		lp->flags = lx.flags;
+		lp->velocity_mode = lx.velocity_mode;
+		lp->layer_event = lx.layer_event;
+		lp->low_range = lx.low_range;
+		lp->high_range = lx.high_range;
+		lp->pan = lx.pan;
+		lp->pan_freq_scale = lx.pan_freq_scale;
+		lp->attenuation = lx.attenuation;
+		snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
+		snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
+		lp->freq_scale = le16_to_cpu(lx.freq_scale);
+		lp->freq_center = lx.freq_center;
+		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
+							  lp,
+							  &lp->penv, &lx.penv,
+						          &instr_data, &len,
+						          gfp_mask);
+		if (err < 0) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return err;
+		}
+		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
+							  lp,
+							  &lp->venv, &lx.venv,
+						          &instr_data, &len,
+						          gfp_mask);
+		if (err < 0) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return err;
+		}
+		while (len > sizeof(__u32)) {
+			__u32 stype;
+
+			if (copy_from_user(&stype, instr_data, sizeof(stype)))
+				return -EFAULT;
+			if (stype != IWFFFF_STRU_WAVE)
+				break;
+			err = snd_seq_iwffff_copy_wave_from_stream(ops,
+								   lp,
+							    	   &instr_data,
+								   &len,
+								   atomic);
+			if (err < 0) {
+				snd_seq_iwffff_instr_free(ops, ip, atomic);
+				return err;
+			}
+		}
+	}
+	return 0;
+}
+
+static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx,
+					      iwffff_lfo_t *fp)
+{
+	fx->freq = cpu_to_le16(fp->freq);
+	fx->depth = cpu_to_le16(fp->depth);
+	fx->sweep = cpu_to_le16(fp->sweep);
+	fp->shape = fx->shape;
+	fp->delay = fx->delay;
+}
+
+static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
+					     iwffff_layer_t *lp,
+					     iwffff_xenv_t *ex,
+					     iwffff_env_t *ep,
+					     char **data,
+					     long *len)
+{
+	iwffff_env_record_t *rp;
+	iwffff_xenv_record_t rx;
+	iwffff_env_point_t *pp;
+	iwffff_xenv_point_t px;
+	int points_size, idx;
+
+	ex->flags = ep->flags;
+	ex->mode = ep->mode;
+	ex->index = ep->index;
+	for (rp = ep->record; rp; rp = rp->next) {
+		if (*len < sizeof(rx))
+			return -ENOMEM;
+		memset(&rx, 0, sizeof(rx));
+		rx.stype = req_stype;
+		rx.nattack = cpu_to_le16(rp->nattack);
+		rx.nrelease = cpu_to_le16(rp->nrelease);
+		rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
+		rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
+		rx.release_rate = cpu_to_le16(rp->release_rate);
+		rx.hirange = cpu_to_le16(rp->hirange);
+		if (copy_to_user(*data, &rx, sizeof(rx)))
+			return -EFAULT;
+		*data += sizeof(rx);
+		*len -= sizeof(rx);
+		points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+		if (*len < points_size)
+			return -ENOMEM;
+		pp = (iwffff_env_point_t *)(rp + 1);
+		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+			px.offset = cpu_to_le16(pp->offset);
+			px.rate = cpu_to_le16(pp->rate);
+			if (copy_to_user(*data, &px, sizeof(px)))
+				return -EFAULT;
+			*data += sizeof(px);
+			*len -= sizeof(px);
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops,
+					      iwffff_layer_t *lp,
+					      char **data,
+					      long *len,
+					      int atomic)
+{
+	iwffff_wave_t *wp;
+	iwffff_xwave_t xp;
+	int err;
+	unsigned int real_size;
+	
+	for (wp = lp->wave; wp; wp = wp->next) {
+		if (*len < sizeof(xp))
+			return -ENOMEM;
+		memset(&xp, 0, sizeof(xp));
+		xp.stype = IWFFFF_STRU_WAVE;
+		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+		xp.format = cpu_to_le32(wp->format);
+		if (wp->format & IWFFFF_WAVE_ROM)
+			xp.offset = cpu_to_le32(wp->address.memory);
+		xp.size = cpu_to_le32(wp->size);
+		xp.start = cpu_to_le32(wp->start);
+		xp.loop_start = cpu_to_le32(wp->loop_start);
+		xp.loop_end = cpu_to_le32(wp->loop_end);
+		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+		xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
+		xp.attenuation = wp->attenuation;
+		xp.low_note = wp->low_note;
+		xp.high_note = wp->high_note;
+		if (copy_to_user(*data, &xp, sizeof(xp)))
+			return -EFAULT;
+		*data += sizeof(xp);
+		*len -= sizeof(xp);
+		real_size = snd_seq_iwffff_size(wp->size, wp->format);
+		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+			if (*len < real_size)
+				return -ENOMEM;
+		}
+		if (ops->get_sample) {
+			err = ops->get_sample(ops->private_data, wp,
+					      *data, real_size, atomic);
+			if (err < 0)
+				return err;
+		}
+		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+			*data += real_size;
+			*len -= real_size;
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr,
+			      char *instr_data, long len, int atomic, int cmd)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+	iwffff_instrument_t *ip;
+	iwffff_xinstrument_t ix;
+	iwffff_layer_t *lp;
+	iwffff_xlayer_t lx;
+	char *layer_instr_data;
+	int err;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = IWFFFF_STRU_INSTR;
+	ix.exclusion = cpu_to_le16(ip->exclusion);
+	ix.layer_type = cpu_to_le16(ip->layer_type);
+	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	for (lp = ip->layer; lp; lp = lp->next) {
+		if (len < sizeof(lx))
+			return -ENOMEM;
+		memset(&lx, 0, sizeof(lx));
+		lx.stype = IWFFFF_STRU_LAYER;
+		lx.flags = lp->flags;
+		lx.velocity_mode = lp->velocity_mode;
+		lx.layer_event = lp->layer_event;
+		lx.low_range = lp->low_range;
+		lx.high_range = lp->high_range;
+		lx.pan = lp->pan;
+		lx.pan_freq_scale = lp->pan_freq_scale;
+		lx.attenuation = lp->attenuation;
+		snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
+		snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
+		layer_instr_data = instr_data;
+		instr_data += sizeof(lx);
+		len -= sizeof(lx);
+		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
+							lp,
+							&lx.penv, &lp->penv,
+						        &instr_data, &len);
+		if (err < 0)
+			return err;
+		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
+							lp,
+							&lx.venv, &lp->venv,
+						        &instr_data, &len);
+		if (err < 0)
+			return err;
+		/* layer structure updating is now finished */
+		if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
+			return -EFAULT;
+		err = snd_seq_iwffff_copy_wave_to_stream(ops,
+							 lp,
+							 &instr_data,
+							 &len,
+							 atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep)
+{
+	long result = 0;
+	iwffff_env_record_t *rp;
+
+	for (rp = ep->record; rp; rp = rp->next) {
+		result += sizeof(iwffff_xenv_record_t);
+		result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+	}
+	return 0;
+}
+
+static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp)
+{
+	long result = 0;
+	iwffff_wave_t *wp;
+	
+	for (wp = lp->wave; wp; wp = wp->next) {
+		result += sizeof(iwffff_xwave_t);
+		if (!(wp->format & IWFFFF_WAVE_ROM))
+			result += wp->size;
+	}
+	return result;
+}
+
+static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr,
+				   long *size)
+{
+	long result;
+	iwffff_instrument_t *ip;
+	iwffff_layer_t *lp;
+
+	*size = 0;
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	result = sizeof(iwffff_xinstrument_t);
+	for (lp = ip->layer; lp; lp = lp->next) {
+		result += sizeof(iwffff_xlayer_t);
+		result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
+		result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
+		result += snd_seq_iwffff_wave_size_in_stream(lp);
+	}
+	*size = result;
+	return 0;
+}
+
+static int snd_seq_iwffff_remove(void *private_data,
+				 snd_seq_kinstr_t *instr,
+                                 int atomic)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+	iwffff_instrument_t *ip;
+
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	snd_seq_iwffff_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_iwffff_notify(void *private_data,
+				  snd_seq_kinstr_t *instr,
+                                  int what)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_iwffff_init(snd_iwffff_ops_t *ops,
+			void *private_data,
+			snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(iwffff_instrument_t);
+	ops->kops.instr_type = snd_seq_iwffff_id;
+	ops->kops.put = snd_seq_iwffff_put;
+	ops->kops.get = snd_seq_iwffff_get;
+	ops->kops.get_size = snd_seq_iwffff_get_size;
+	ops->kops.remove = snd_seq_iwffff_remove;
+	ops->kops.notify = snd_seq_iwffff_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_iw_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_iw_exit(void)
+{
+}
+
+module_init(alsa_ainstr_iw_init)
+module_exit(alsa_ainstr_iw_exit)
+
+EXPORT_SYMBOL(snd_seq_iwffff_id);
+EXPORT_SYMBOL(snd_seq_iwffff_init);
diff -Nru linux/sound/core/seq/instr/ainstr_simple.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_simple.c
--- linux/sound/core/seq/instr/ainstr_simple.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_simple.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,218 @@
+/*
+ *   Simple (MOD player) - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_simple.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+char *snd_seq_simple_id = SNDRV_SEQ_INSTR_ID_SIMPLE;
+
+static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & SIMPLE_WAVE_16BIT)
+		result <<= 1;
+	if (format & SIMPLE_WAVE_STEREO)
+		result <<= 1;
+	return result;
+}
+
+static void snd_seq_simple_instr_free(snd_simple_ops_t *ops,
+				      simple_instrument_t *ip,
+				      int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, ip, atomic);
+}
+
+static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr,
+			      char *instr_data, long len, int atomic, int cmd)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+	simple_instrument_t *ip;
+	simple_xinstrument_t ix;
+	int err, gfp_mask;
+	unsigned int real_size;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != SIMPLE_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+	ip->format = le32_to_cpu(ix.format);
+	ip->size = le32_to_cpu(ix.size);
+	ip->start = le32_to_cpu(ix.start);
+	ip->loop_start = le32_to_cpu(ix.loop_start);
+	ip->loop_end = le32_to_cpu(ix.loop_end);
+	ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	real_size = snd_seq_simple_size(ip->size, ip->format);
+	if (len < real_size)
+		return -EINVAL;
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, ip,
+				      instr_data, real_size, atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr,
+			      char *instr_data, long len, int atomic, int cmd)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+	simple_instrument_t *ip;
+	simple_xinstrument_t ix;
+	int err;
+	unsigned int real_size;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = SIMPLE_STRU_INSTR;
+	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+	ix.format = cpu_to_le32(ip->format);
+	ix.size = cpu_to_le32(ip->size);
+	ix.start = cpu_to_le32(ip->start);
+	ix.loop_start = cpu_to_le32(ip->loop_start);
+	ix.loop_end = cpu_to_le32(ip->loop_end);
+	ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	real_size = snd_seq_simple_size(ip->size, ip->format);
+	if (len < real_size)
+		return -ENOMEM;
+	if (ops->get_sample) {
+		err = ops->get_sample(ops->private_data, ip,
+				      instr_data, real_size, atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr,
+				   long *size)
+{
+	simple_instrument_t *ip;
+
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	*size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format);
+	return 0;
+}
+
+static int snd_seq_simple_remove(void *private_data,
+			         snd_seq_kinstr_t *instr,
+                                 int atomic)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+	simple_instrument_t *ip;
+
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	snd_seq_simple_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_simple_notify(void *private_data,
+			          snd_seq_kinstr_t *instr,
+                                  int what)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_simple_init(snd_simple_ops_t *ops,
+		        void *private_data,
+		        snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(simple_instrument_t);
+	ops->kops.instr_type = snd_seq_simple_id;
+	ops->kops.put = snd_seq_simple_put;
+	ops->kops.get = snd_seq_simple_get;
+	ops->kops.get_size = snd_seq_simple_get_size;
+	ops->kops.remove = snd_seq_simple_remove;
+	ops->kops.notify = snd_seq_simple_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_simple_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_simple_exit(void)
+{
+}
+
+module_init(alsa_ainstr_simple_init)
+module_exit(alsa_ainstr_simple_exit)
+
+EXPORT_SYMBOL(snd_seq_simple_id);
+EXPORT_SYMBOL(snd_seq_simple_init);
diff -Nru linux/sound/core/seq/oss/Makefile linux-2.4.19-pre5-mjc/sound/core/seq/oss/Makefile
--- linux/sound/core/seq/oss/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,19 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := oss.o
+
+list-multi   := snd-seq-oss.o
+
+snd-seq-oss-objs  := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
+		     seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
+		     seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
+
+obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o
+
+include $(TOPDIR)/Rules.make
+
+snd-seq-oss.o: $(snd-seq-oss-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-oss-objs)
diff -Nru linux/sound/core/seq/oss/seq_oss.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss.c
--- linux/sound/core/seq/oss/seq_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,307 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * registration of device and proc
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/initval.h>
+#include "seq_oss_device.h"
+#include "seq_oss_synth.h"
+
+/*
+ * module option
+ */
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("OSS-compatible sequencer module");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+#ifdef SNDRV_SEQ_OSS_DEBUG
+MODULE_PARM(seq_oss_debug, "i");
+MODULE_PARM_DESC(seq_oss_debug, "debug option");
+int seq_oss_debug = 0;
+#endif
+
+
+/*
+ * prototypes
+ */
+static int register_device(void);
+static void unregister_device(void);
+static int register_proc(void);
+static void unregister_proc(void);
+
+static int odev_open(struct inode *inode, struct file *file);
+static int odev_release(struct inode *inode, struct file *file);
+static ssize_t odev_read(struct file *file, char *buf, size_t count, loff_t *offset);
+static ssize_t odev_write(struct file *file, const char *buf, size_t count, loff_t *offset);
+static int odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static unsigned int odev_poll(struct file *file, poll_table * wait);
+#ifdef CONFIG_PROC_FS
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf);
+#endif
+
+
+/*
+ * module interface
+ */
+
+static int __init alsa_seq_oss_init(void)
+{
+	int rc;
+	static snd_seq_dev_ops_t ops = {
+		snd_seq_oss_synth_register,
+		snd_seq_oss_synth_unregister,
+	};
+
+	if ((rc = register_device()) < 0)
+		return rc;
+	if ((rc = register_proc()) < 0) {
+		unregister_device();
+		return rc;
+	}
+	if ((rc = snd_seq_oss_create_client()) < 0) {
+		unregister_proc();
+		unregister_device();
+		return rc;
+	}
+
+	if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
+						 sizeof(snd_seq_oss_reg_t))) < 0) {
+		snd_seq_oss_delete_client();
+		unregister_proc();
+		unregister_device();
+		return rc;
+	}
+
+	/* success */
+	snd_seq_oss_synth_init();
+	return 0;
+}
+
+static void __exit alsa_seq_oss_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
+	snd_seq_oss_delete_client();
+	unregister_proc();
+	unregister_device();
+}
+
+module_init(alsa_seq_oss_init)
+module_exit(alsa_seq_oss_exit)
+
+/*
+ * ALSA minor device interface
+ */
+
+static DECLARE_MUTEX(register_mutex);
+
+static int
+odev_open(struct inode *inode, struct file *file)
+{
+	int level, rc;
+
+	if (minor(inode->i_rdev) == SNDRV_MINOR_OSS_MUSIC)
+		level = SNDRV_SEQ_OSS_MODE_MUSIC;
+	else
+		level = SNDRV_SEQ_OSS_MODE_SYNTH;
+
+	down(&register_mutex);
+	rc = snd_seq_oss_open(file, level);
+	up(&register_mutex);
+
+	return rc;
+}
+
+static int
+odev_release(struct inode *inode, struct file *file)
+{
+	seq_oss_devinfo_t *dp;
+
+	if ((dp = file->private_data) == NULL)
+		return 0;
+
+	snd_seq_oss_drain_write(dp);
+
+	down(&register_mutex);
+	snd_seq_oss_release(dp);
+	up(&register_mutex);
+
+	return 0;
+}
+
+static ssize_t
+odev_read(struct file *file, char *buf, size_t count, loff_t *offset)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return -EIO);
+	return snd_seq_oss_read(dp, buf, count);
+}
+
+
+static ssize_t
+odev_write(struct file *file, const char *buf, size_t count, loff_t *offset)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return -EIO);
+	return snd_seq_oss_write(dp, buf, count, file);
+}
+
+static int
+odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return -EIO);
+	return snd_seq_oss_ioctl(dp, cmd, arg);
+}
+
+
+static unsigned int
+odev_poll(struct file *file, poll_table * wait)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return 0);
+	return snd_seq_oss_poll(dp, file, wait);
+}
+
+/*
+ * registration of sequencer minor device
+ */
+
+static struct file_operations seq_oss_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		odev_read,
+	write:		odev_write,
+	open:		odev_open,
+	release:	odev_release,
+	poll:		odev_poll,
+	ioctl:		odev_ioctl,
+};
+
+static snd_minor_t seq_oss_reg = {
+	comment:	"sequencer",
+	f_ops:		&seq_oss_f_ops,
+};
+
+static int __init
+register_device(void)
+{
+	int rc;
+
+	down(&register_mutex);
+	if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
+					  NULL, 0,
+					  &seq_oss_reg,
+					  SNDRV_SEQ_OSS_DEVNAME)) < 0) {
+		snd_printk(KERN_ERR "can't register device seq\n");
+		up(&register_mutex);
+		return rc;
+	}
+	if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
+					  NULL, 0,
+					  &seq_oss_reg,
+					  SNDRV_SEQ_OSS_DEVNAME)) < 0) {
+		snd_printk(KERN_ERR "can't register device music\n");
+		snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
+		up(&register_mutex);
+		return rc;
+	}
+	debug_printk(("device registered\n"));
+	up(&register_mutex);
+	return 0;
+}
+
+static void
+unregister_device(void)
+{
+	down(&register_mutex);
+	debug_printk(("device unregistered\n"));
+	if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)		
+		snd_printk(KERN_ERR "error unregister device music\n");
+	if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
+		snd_printk(KERN_ERR "error unregister device seq\n");
+	up(&register_mutex);
+}
+
+/*
+ * /proc interface
+ */
+
+#ifdef CONFIG_PROC_FS
+
+static snd_info_entry_t *info_entry;
+
+static void
+info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf)
+{
+	down(&register_mutex);
+	snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
+	snd_seq_oss_system_info_read(buf);
+	snd_seq_oss_synth_info_read(buf);
+	snd_seq_oss_midi_info_read(buf);
+	up(&register_mutex);
+}
+
+#endif /* CONFIG_PROC_FS */
+
+static int __init
+register_proc(void)
+{
+#ifdef CONFIG_PROC_FS
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root);
+	if (entry == NULL)
+		return -ENOMEM;
+
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->private_data = NULL;
+	entry->c.text.read_size = 1024;
+	entry->c.text.read = info_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	info_entry = entry;
+#endif
+	return 0;
+}
+
+static void
+unregister_proc(void)
+{
+#ifdef CONFIG_PROC_FS
+	if (info_entry)
+		snd_info_unregister(info_entry);
+	info_entry = NULL;
+#endif
+}
diff -Nru linux/sound/core/seq/oss/seq_oss_device.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_device.h
--- linux/sound/core/seq/oss/seq_oss_device.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_device.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,199 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_DEVICE_H
+#define __SEQ_OSS_DEVICE_H
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/seq_oss.h>
+#include <sound/rawmidi.h>
+#include <sound/seq_kernel.h>
+#include <sound/info.h>
+
+/* enable debug print */
+#define SNDRV_SEQ_OSS_DEBUG
+
+/* max. applications */
+#define SNDRV_SEQ_OSS_MAX_CLIENTS	16
+#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS	16
+#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS	32
+
+/* version */
+#define SNDRV_SEQ_OSS_MAJOR_VERSION	0
+#define SNDRV_SEQ_OSS_MINOR_VERSION	1
+#define SNDRV_SEQ_OSS_TINY_VERSION	8
+#define SNDRV_SEQ_OSS_VERSION_STR	"0.1.8"
+
+/* device and proc interface name */
+#define SNDRV_SEQ_OSS_DEVNAME		"seq_oss"
+#define SNDRV_SEQ_OSS_PROCNAME		"oss"
+
+
+/*
+ * type definitions
+ */
+
+typedef struct seq_oss_devinfo_t seq_oss_devinfo_t;
+typedef struct seq_oss_writeq_t seq_oss_writeq_t;
+typedef struct seq_oss_readq_t seq_oss_readq_t;
+typedef struct seq_oss_timer_t seq_oss_timer_t;
+typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t;
+typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t;
+typedef struct seq_oss_chinfo_t seq_oss_chinfo_t;
+typedef unsigned int reltime_t;
+typedef unsigned int abstime_t;
+typedef union evrec_t evrec_t;
+
+
+/*
+ * synthesizer channel information
+ */
+struct seq_oss_chinfo_t {
+	int note, vel;
+};
+
+/*
+ * synthesizer information
+ */
+struct seq_oss_synthinfo_t {
+	snd_seq_oss_arg_t arg;
+	seq_oss_chinfo_t *ch;
+	seq_oss_synth_sysex_t *sysex;
+	int nr_voices;
+	int opened;
+	int is_midi;
+	int midi_mapped;
+};
+
+
+/*
+ * sequencer client information
+ */
+
+struct seq_oss_devinfo_t {
+
+	int index;	/* application index */
+	int cseq;	/* sequencer client number */
+	int port;	/* sequencer port number */
+	int queue;	/* sequencer queue number */
+
+	snd_seq_addr_t addr;	/* address of this device */
+
+	int seq_mode;	/* sequencer mode */
+	int file_mode;	/* file access */
+
+	/* midi device table */
+	int max_mididev;
+
+	/* synth device table */
+	int max_synthdev;
+	seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
+	int synth_opened;
+
+	/* output queue */
+	seq_oss_writeq_t *writeq;
+
+	/* midi input queue */
+	seq_oss_readq_t *readq;
+
+	/* timer */
+	seq_oss_timer_t *timer;
+};
+
+
+/*
+ * function prototypes
+ */
+
+/* create/delete OSS sequencer client */
+int snd_seq_oss_create_client(void);
+int snd_seq_oss_delete_client(void);
+
+/* device file interface */
+int snd_seq_oss_open(struct file *file, int level);
+void snd_seq_oss_release(seq_oss_devinfo_t *dp);
+int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg);
+int snd_seq_oss_read(seq_oss_devinfo_t *dev, char *buf, int count);
+int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt);
+unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait);
+
+void snd_seq_oss_reset(seq_oss_devinfo_t *dp);
+void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp);
+
+/* */
+void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time);
+
+
+/* proc interface */
+void snd_seq_oss_system_info_read(snd_info_buffer_t *buf);
+void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf);
+void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf);
+void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf);
+
+/* file mode macros */
+#define is_read_mode(mode)	((mode) & SNDRV_SEQ_OSS_FILE_READ)
+#define is_write_mode(mode)	((mode) & SNDRV_SEQ_OSS_FILE_WRITE)
+#define is_nonblock_mode(mode)	((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK)
+
+/* dispatch event */
+inline static int
+snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop)
+{
+	return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
+}
+
+/* ioctl */
+inline static int
+snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg)
+{
+	return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
+}
+
+/* fill the addresses in header */
+inline static void
+snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev,
+		     int dest_client, int dest_port)
+{
+	ev->queue = dp->queue;
+	ev->source = dp->addr;
+	ev->dest.client = dest_client;
+	ev->dest.port = dest_port;
+}
+
+
+/* misc. functions for proc interface */
+char *enabled_str(int bool);
+char *filemode_str(int fmode);
+
+
+/* for debug */
+#ifdef SNDRV_SEQ_OSS_DEBUG
+extern int seq_oss_debug;
+#define debug_printk(x)	do { if (seq_oss_debug > 0) snd_printk x; } while (0)
+#else
+#define debug_printk(x)	/**/
+#endif
+
+#endif /* __SEQ_OSS_DEVICE_H */
diff -Nru linux/sound/core/seq/oss/seq_oss_event.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.c
--- linux/sound/core/seq/oss/seq_oss_event.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,448 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_device.h"
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "seq_oss_event.h"
+#include "seq_oss_timer.h"
+#include <sound/seq_oss_legacy.h>
+#include "seq_oss_readq.h"
+#include "seq_oss_writeq.h"
+
+
+/*
+ * prototypes
+ */
+static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
+static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
+static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
+static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
+static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev);
+static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev);
+static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev);
+
+
+/*
+ * convert an OSS event to ALSA event
+ * return 0 : enqueued
+ *        non-zero : invalid - ignored
+ */
+
+int
+snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	switch (q->s.code) {
+	case SEQ_EXTENDED:
+		return extended_event(dp, q, ev);
+
+	case EV_CHN_VOICE:
+		return chn_voice_event(dp, q, ev);
+
+	case EV_CHN_COMMON:
+		return chn_common_event(dp, q, ev);
+
+	case EV_TIMING:
+		return timing_event(dp, q, ev);
+
+	case EV_SEQ_LOCAL:
+		return local_event(dp, q, ev);
+
+	case EV_SYSEX:
+		return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);
+
+	case SEQ_MIDIPUTC:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		/* put a midi byte */
+		if (! is_write_mode(dp->file_mode))
+			break;
+		if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
+			break;
+		if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
+			return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
+		break;
+
+	case SEQ_ECHO:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		return set_echo_event(dp, q, ev);
+
+	case SEQ_PRIVATE:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);
+
+	default:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		return old_event(dp, q, ev);
+	}
+	return -EINVAL;
+}
+
+/* old type events: mode1 only */
+static int
+old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	switch (q->s.code) {
+	case SEQ_NOTEOFF:
+		return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
+
+	case SEQ_NOTEON:
+		return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
+
+	case SEQ_WAIT:
+		/* skip */
+		break;
+
+	case SEQ_PGMCHANGE:
+		return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,
+					 q->n.chn, 0, q->n.note, ev);
+
+	case SEQ_SYNCTIMER:
+		return snd_seq_oss_timer_reset(dp->timer);
+	}
+
+	return -EINVAL;
+}
+
+/* 8bytes extended event: mode1 only */
+static int
+extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	int val;
+
+	switch (q->e.cmd) {
+	case SEQ_NOTEOFF:
+		return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
+
+	case SEQ_NOTEON:
+		return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
+
+	case SEQ_PGMCHANGE:
+		return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
+					 q->e.chn, 0, q->e.p1, ev);
+
+	case SEQ_AFTERTOUCH:
+		return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,
+					 q->e.chn, 0, q->e.p1, ev);
+
+	case SEQ_BALANCE:
+		/* convert -128:127 to 0:127 */
+		val = (char)q->e.p1;
+		val = (val + 128) / 2;
+		return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,
+					 q->e.chn, CTL_PAN, val, ev);
+
+	case SEQ_CONTROLLER:
+		val = ((short)q->e.p3 << 8) | (short)q->e.p2;
+		switch (q->e.p1) {
+		case CTRL_PITCH_BENDER: /* SEQ1 V2 control */
+			/* -0x2000:0x1fff */
+			return set_control_event(dp, q->e.dev,
+						 SNDRV_SEQ_EVENT_PITCHBEND,
+						 q->e.chn, 0, val, ev);
+		case CTRL_PITCH_BENDER_RANGE:
+			/* conversion: 100/semitone -> 128/semitone */
+			return set_control_event(dp, q->e.dev,
+						 SNDRV_SEQ_EVENT_REGPARAM,
+						 q->e.chn, 0, val*128/100, ev);
+		default:
+			return set_control_event(dp, q->e.dev,
+						  SNDRV_SEQ_EVENT_CONTROL14,
+						  q->e.chn, q->e.p1, val, ev);
+		}
+
+	case SEQ_VOLMODE:
+		return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);
+
+	}
+	return -EINVAL;
+}
+
+/* channel voice events: mode1 and 2 */
+static int
+chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	if (q->v.chn >= 32)
+		return -EINVAL;
+	switch (q->v.cmd) {
+	case MIDI_NOTEON:
+		return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
+
+	case MIDI_NOTEOFF:
+		return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
+
+	case MIDI_KEY_PRESSURE:
+		return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,
+				       q->v.chn, q->v.note, q->v.parm, ev);
+
+	}
+	return -EINVAL;
+}
+
+/* channel common events: mode1 and 2 */
+static int
+chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	if (q->l.chn >= 32)
+		return -EINVAL;
+	switch (q->l.cmd) {
+	case MIDI_PGM_CHANGE:
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
+					  q->l.chn, 0, q->l.p1, ev);
+
+	case MIDI_CTL_CHANGE:
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,
+					  q->l.chn, q->l.p1, q->l.val, ev);
+
+	case MIDI_PITCH_BEND:
+		/* conversion: 0:0x3fff -> -0x2000:0x1fff */
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,
+					  q->l.chn, 0, q->l.val - 8192, ev);
+		
+	case MIDI_CHN_PRESSURE:
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,
+					  q->l.chn, 0, q->l.val, ev);
+	}
+	return -EINVAL;
+}
+
+/* timer events: mode1 and mode2 */
+static int
+timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	switch (q->t.cmd) {
+	case TMR_ECHO:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return set_echo_event(dp, q, ev);
+		else {
+			evrec_t tmp;
+			memset(&tmp, 0, sizeof(tmp));
+			/* XXX: only for little-endian! */
+			tmp.echo = (q->t.time << 8) | SEQ_ECHO;
+			return set_echo_event(dp, &tmp, ev);
+		} 
+
+	case TMR_STOP:
+		if (dp->seq_mode)
+			return snd_seq_oss_timer_stop(dp->timer);
+		return 0;
+
+	case TMR_CONTINUE:
+		if (dp->seq_mode)
+			return snd_seq_oss_timer_continue(dp->timer);
+		return 0;
+
+	case TMR_TEMPO:
+		if (dp->seq_mode)
+			return snd_seq_oss_timer_tempo(dp->timer, q->t.time);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* local events: mode1 and 2 */
+static int
+local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	return -EINVAL;
+}
+
+/*
+ * process note-on event for OSS synth
+ * three different modes are available:
+ * - SNDRV_SEQ_OSS_PROCESS_EVENTS  (for one-voice per channel mode)
+ *	Accept note 255 as volume change.
+ * - SNDRV_SEQ_OSS_PASS_EVENTS
+ *	Pass all events to lowlevel driver anyway
+ * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS  (mostly for Emu8000)
+ *	Use key-pressure if note >= 128
+ */
+static int
+note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
+{
+	seq_oss_synthinfo_t *info = &dp->synths[dev];
+	switch (info->arg.event_passing) {
+	case SNDRV_SEQ_OSS_PROCESS_EVENTS:
+		if (! info->ch || ch < 0 || ch >= info->nr_voices) {
+			/* pass directly */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+		}
+
+		if (note == 255 && info->ch[ch].note >= 0) {
+			/* volume control */
+			int type;
+			//if (! vel)
+				/* set volume to zero -- note off */
+			//	type = SNDRV_SEQ_EVENT_NOTEOFF;
+			//else
+				if (info->ch[ch].vel)
+				/* sample already started -- volume change */
+				type = SNDRV_SEQ_EVENT_KEYPRESS;
+			else
+				/* sample not started -- start now */
+				type = SNDRV_SEQ_EVENT_NOTEON;
+			info->ch[ch].vel = vel;
+			return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
+		} else if (note >= 128)
+			return -EINVAL; /* invalid */
+
+		if (note != info->ch[ch].note && info->ch[ch].note >= 0)
+			/* note changed - note off at beginning */
+			set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);
+		/* set current status */
+		info->ch[ch].note = note;
+		info->ch[ch].vel = vel;
+		if (vel) /* non-zero velocity - start the note now */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+		return -EINVAL;
+		
+	case SNDRV_SEQ_OSS_PASS_EVENTS:
+		/* pass the event anyway */
+		return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+
+	case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
+		if (note >= 128) /* key pressure: shifted by 128 */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);
+		else /* normal note-on event */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+	}
+	return -EINVAL;
+}
+
+/*
+ * process note-off event for OSS synth
+ */
+static int
+note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
+{
+	seq_oss_synthinfo_t *info = &dp->synths[dev];
+	switch (info->arg.event_passing) {
+	case SNDRV_SEQ_OSS_PROCESS_EVENTS:
+		if (! info->ch || ch < 0 || ch >= info->nr_voices) {
+			/* pass directly */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+		}
+
+		if (info->ch[ch].note >= 0) {
+			note = info->ch[ch].note;
+			info->ch[ch].vel = 0;
+			info->ch[ch].note = -1;
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
+		}
+		return -EINVAL; /* invalid */
+
+	case SNDRV_SEQ_OSS_PASS_EVENTS:
+	case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
+		/* pass the event anyway */
+		return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
+
+	}
+	return -EINVAL;
+}
+
+/*
+ * create a note event
+ */
+static int
+set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -ENXIO;
+	
+	ev->type = type;
+	snd_seq_oss_synth_addr(dp, dev, ev);
+	ev->data.note.channel = ch;
+	ev->data.note.note = note;
+	ev->data.note.velocity = vel;
+
+	return 0;
+}
+
+/*
+ * create a control event
+ */
+static int
+set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -ENXIO;
+	
+	ev->type = type;
+	snd_seq_oss_synth_addr(dp, dev, ev);
+	ev->data.control.channel = ch;
+	ev->data.control.param = param;
+	ev->data.control.value = val;
+
+	return 0;
+}
+
+/*
+ * create an echo event
+ */
+static int
+set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev)
+{
+	ev->type = SNDRV_SEQ_EVENT_ECHO;
+	/* echo back to itself */
+	snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);
+	memcpy(&ev->data, rec, LONG_EVENT_SIZE);
+	return 0;
+}
+
+/*
+ * event input callback from ALSA sequencer:
+ * the echo event is processed here.
+ */
+int
+snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data,
+			int atomic, int hop)
+{
+	seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
+	evrec_t *rec;
+
+	if (ev->type != SNDRV_SEQ_EVENT_ECHO)
+		return snd_seq_oss_midi_input(ev, direct, private_data);
+
+	if (ev->source.client != dp->cseq)
+		return 0; /* ignored */
+
+	rec = (evrec_t*)&ev->data;
+	if (rec->s.code == SEQ_SYNCTIMER) {
+		/* sync echo back */
+		snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);
+		
+	} else {
+		/* echo back event */
+		if (dp->readq == NULL)
+			return 0;
+		snd_seq_oss_readq_put_event(dp->readq, rec);
+	}
+	return 0;
+}
+
diff -Nru linux/sound/core/seq/oss/seq_oss_event.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.h
--- linux/sound/core/seq/oss/seq_oss_event.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,112 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * seq_oss_event.h - OSS event queue record
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_EVENT_H
+#define __SEQ_OSS_EVENT_H
+
+#include "seq_oss_device.h"
+
+#define SHORT_EVENT_SIZE	4
+#define LONG_EVENT_SIZE		8
+
+/* short event (4bytes) */
+typedef struct evrec_short_t {
+	unsigned char code;
+	unsigned char parm1;
+	unsigned char dev;
+	unsigned char parm2;
+} evrec_short_t;
+	
+/* short note events (4bytes) */
+typedef struct evrec_note_t {
+	unsigned char code;
+	unsigned char chn;
+	unsigned char note;
+	unsigned char vel;
+} evrec_note_t;
+	
+/* long timer events (8bytes) */
+typedef struct evrec_timer_t {
+	unsigned char code;
+	unsigned char cmd;
+	unsigned char dummy1, dummy2;
+	unsigned int time;
+} evrec_timer_t;
+
+/* long extended events (8bytes) */
+typedef struct evrec_extended_t {
+	unsigned char code;
+	unsigned char cmd;
+	unsigned char dev;
+	unsigned char chn;
+	unsigned char p1, p2, p3, p4;
+} evrec_extended_t;
+
+/* long channel events (8bytes) */
+typedef struct evrec_long_t {
+	unsigned char code;
+	unsigned char dev;
+	unsigned char cmd;
+	unsigned char chn;
+	unsigned char p1, p2;
+	unsigned short val;
+} evrec_long_t;
+	
+/* channel voice events (8bytes) */
+typedef struct evrec_voice_t {
+	unsigned char code;
+	unsigned char dev;
+	unsigned char cmd;
+	unsigned char chn;
+	unsigned char note, parm;
+	unsigned short dummy;
+} evrec_voice_t;
+
+/* sysex events (8bytes) */
+typedef struct evrec_sysex_t {
+	unsigned char code;
+	unsigned char dev;
+	unsigned char buf[6];
+} evrec_sysex_t;
+
+/* event record */
+union evrec_t {
+	evrec_short_t s;
+	evrec_note_t n;
+	evrec_long_t l;
+	evrec_voice_t v;
+	evrec_timer_t t;
+	evrec_extended_t e;
+	evrec_sysex_t x;
+	unsigned int echo;
+	unsigned char c[LONG_EVENT_SIZE];
+};
+
+#define ev_is_long(ev) ((ev)->s.code >= 128)
+#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE)
+
+int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
+int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q);
+int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop);
+
+
+#endif /* __SEQ_OSS_EVENT_H */
diff -Nru linux/sound/core/seq/oss/seq_oss_init.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_init.c
--- linux/sound/core/seq/oss/seq_oss_init.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_init.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,540 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * open/close and reset interface
+ *
+ * Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_device.h"
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "seq_oss_writeq.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <linux/init.h>
+
+/*
+ * common variables
+ */
+MODULE_PARM(maxqlen, "i");
+MODULE_PARM_DESC(maxqlen, "maximum queue length");
+
+static int system_client = -1; /* ALSA sequencer client number */
+static int system_port = -1;
+
+int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
+static int num_clients = 0;
+static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
+
+
+/*
+ * prototypes
+ */
+static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
+static int translate_mode(struct file *file);
+static int create_port(seq_oss_devinfo_t *dp);
+static int delete_port(seq_oss_devinfo_t *dp);
+static int alloc_seq_queue(seq_oss_devinfo_t *dp);
+static int delete_seq_queue(seq_oss_devinfo_t *dp);
+static void free_devinfo(void *private);
+
+#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
+
+
+/*
+ * create sequencer client for OSS sequencer
+ */
+int __init
+snd_seq_oss_create_client(void)
+{
+	int rc;
+	snd_seq_client_callback_t callback;
+	snd_seq_client_info_t info;
+	snd_seq_port_info_t port;
+	snd_seq_port_callback_t port_callback;
+
+	/* create ALSA client */
+	memset(&callback, 0, sizeof(callback));
+
+	callback.private_data = NULL;
+	callback.allow_input = 1;
+	callback.allow_output = 1;
+
+	rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback);
+	if (rc < 0)
+		return rc;
+
+	system_client = rc;
+	debug_printk(("new client = %d\n", rc));
+
+	/* set client information */
+	memset(&info, 0, sizeof(info));
+	info.client = system_client;
+	info.type = KERNEL_CLIENT;
+	strcpy(info.name, "OSS sequencer");
+
+	rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
+
+	/* look up midi devices */
+	snd_seq_oss_midi_lookup_ports(system_client);
+
+	/* create annoucement receiver port */
+	memset(&port, 0, sizeof(port));
+	strcpy(port.name, "Receiver");
+	port.addr.client = system_client;
+	port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
+	port.type = 0;
+
+	memset(&port_callback, 0, sizeof(port_callback));
+	/* don't set port_callback.owner here. otherwise the module counter
+	 * is incremented and we can no longer release the module..
+	 */
+	port_callback.event_input = receive_announce;
+	port.kernel = &port_callback;
+	
+	call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
+	if ((system_port = port.addr.port) >= 0) {
+		snd_seq_port_subscribe_t subs;
+
+		memset(&subs, 0, sizeof(subs));
+		subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+		subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+		subs.dest.client = system_client;
+		subs.dest.port = system_port;
+		call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
+	}
+
+
+	return 0;
+}
+
+
+/*
+ * receive annoucement from system port, and check the midi device
+ */
+static int
+receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop)
+{
+	snd_seq_port_info_t pinfo;
+
+	if (atomic)
+		return 0; /* it must not happen */
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_PORT_START:
+	case SNDRV_SEQ_EVENT_PORT_CHANGE:
+		if (ev->data.addr.client == system_client)
+			break; /* ignore myself */
+		memset(&pinfo, 0, sizeof(pinfo));
+		pinfo.addr = ev->data.addr;
+		if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
+			snd_seq_oss_midi_check_new_port(&pinfo);
+		break;
+
+	case SNDRV_SEQ_EVENT_PORT_EXIT:
+		if (ev->data.addr.client == system_client)
+			break; /* ignore myself */
+		snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
+						ev->data.addr.port);
+		break;
+	}
+	return 0;
+}
+
+
+/*
+ * delete OSS sequencer client
+ */
+int
+snd_seq_oss_delete_client(void)
+{
+	if (system_client >= 0)
+		snd_seq_delete_kernel_client(system_client);
+
+	snd_seq_oss_midi_clear_all();
+
+	return 0;
+}
+
+
+/*
+ * open sequencer device
+ */
+int
+snd_seq_oss_open(struct file *file, int level)
+{
+	int i, rc;
+	seq_oss_devinfo_t *dp;
+
+	if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc device info\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
+		if (client_table[i] == NULL)
+			break;
+	}
+	if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
+		snd_printk(KERN_ERR "too many applications\n");
+		return -ENOMEM;
+	}
+
+	dp->index = i;
+	dp->cseq = system_client;
+	dp->port = -1;
+	dp->queue = -1;
+	dp->readq = NULL;
+	dp->writeq = NULL;
+
+	/* look up synth and midi devices */
+	snd_seq_oss_synth_setup(dp);
+	snd_seq_oss_midi_setup(dp);
+
+	if (dp->synth_opened == 0 && dp->max_mididev == 0) {
+		snd_printk(KERN_ERR "no device found\n");
+		kfree(dp);
+		return -ENODEV;
+	}
+
+	/* create port */
+	if ((rc = create_port(dp)) < 0) {
+		snd_printk(KERN_ERR "can't create port\n");
+		free_devinfo(dp);
+		return rc;
+	}
+
+	/* allocate queue */
+	if ((rc = alloc_seq_queue(dp)) < 0) {
+		delete_port(dp);
+		return rc;
+	}
+
+	/* set address */
+	dp->addr.client = dp->cseq;
+	dp->addr.port = dp->port;
+	/*dp->addr.queue = dp->queue;*/
+	/*dp->addr.channel = 0;*/
+
+	dp->seq_mode = level;
+
+	/* set up file mode */
+	dp->file_mode = translate_mode(file);
+
+	/* initialize read queue */
+	if (is_read_mode(dp->file_mode)) {
+		if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
+			delete_seq_queue(dp);
+			delete_port(dp);
+			return -ENOMEM;
+		}
+	}
+
+	/* initialize write queue */
+	if (is_write_mode(dp->file_mode)) {
+		dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
+		if (dp->writeq == NULL) {
+			delete_seq_queue(dp);
+			delete_port(dp);
+			return -ENOMEM;
+		}
+	}
+
+	/* initialize timer */
+	if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
+		snd_printk(KERN_ERR "can't alloc timer\n");
+		delete_seq_queue(dp);
+		delete_port(dp);
+		return -ENOMEM;
+	}
+
+	/* set private data pointer */
+	file->private_data = dp;
+
+	/* set up for mode2 */
+	if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
+		snd_seq_oss_synth_setup_midi(dp);
+	else if (is_read_mode(dp->file_mode))
+		snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
+
+	client_table[dp->index] = dp;
+	num_clients++;
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+
+	debug_printk(("open done\n"));
+
+	return 0;
+}
+
+/*
+ * translate file flags to private mode
+ */
+static int
+translate_mode(struct file *file)
+{
+	int file_mode = 0;
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+		file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
+	if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+		file_mode |= SNDRV_SEQ_OSS_FILE_READ;
+	if (file->f_flags & O_NONBLOCK)
+		file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
+	return file_mode;
+}
+
+
+/*
+ * create sequencer port
+ */
+static int
+create_port(seq_oss_devinfo_t *dp)
+{
+	int rc;
+	snd_seq_port_info_t port;
+	snd_seq_port_callback_t callback;
+
+	memset(&port, 0, sizeof(port));
+	port.addr.client = dp->cseq;
+	sprintf(port.name, "Sequencer-%d", dp->index);
+	port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
+	port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
+	port.midi_channels = 128;
+	port.synth_voices = 128;
+
+	memset(&callback, 0, sizeof(callback));
+	callback.owner = THIS_MODULE;
+	callback.private_data = dp;
+	callback.event_input = snd_seq_oss_event_input;
+	callback.private_free = free_devinfo;
+	port.kernel = &callback;
+
+	rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
+	if (rc < 0)
+		return rc;
+
+	dp->port = port.addr.port;
+	debug_printk(("new port = %d\n", port.addr.port));
+
+	return 0;
+}
+
+/*
+ * delete ALSA port
+ */
+static int
+delete_port(seq_oss_devinfo_t *dp)
+{
+	snd_seq_port_info_t port_info;
+
+	if (dp->port < 0)
+		return 0;
+
+	memset(&port_info, 0, sizeof(port_info));
+	port_info.addr.client = dp->cseq;
+	port_info.addr.port = dp->port;
+	return snd_seq_kernel_client_ctl(dp->cseq,
+					 SNDRV_SEQ_IOCTL_DELETE_PORT,
+					 &port_info);
+}
+
+/*
+ * allocate a queue
+ */
+static int
+alloc_seq_queue(seq_oss_devinfo_t *dp)
+{
+	snd_seq_queue_info_t qinfo;
+	int rc;
+
+	memset(&qinfo, 0, sizeof(qinfo));
+	qinfo.owner = system_client;
+	qinfo.locked = 1;
+	strcpy(qinfo.name, "OSS Sequencer Emulation");
+	if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
+		return rc;
+	dp->queue = qinfo.queue;
+	return 0;
+}
+
+/*
+ * release queue
+ */
+static int
+delete_seq_queue(seq_oss_devinfo_t *dp)
+{
+	snd_seq_queue_info_t qinfo;
+
+	if (dp->queue < 0)
+		return 0;
+	memset(&qinfo, 0, sizeof(qinfo));
+	qinfo.queue = dp->queue;
+	return call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
+}
+
+
+/*
+ * free device informations - private_free callback of port
+ */
+static void
+free_devinfo(void *private)
+{
+	seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private;
+
+	if (dp->timer)
+		snd_seq_oss_timer_delete(dp->timer);
+		
+	if (dp->writeq)
+		snd_seq_oss_writeq_delete(dp->writeq);
+
+	if (dp->readq)
+		snd_seq_oss_readq_delete(dp->readq);
+	
+	kfree(dp);
+}
+
+
+/*
+ * close sequencer device
+ */
+void
+snd_seq_oss_release(seq_oss_devinfo_t *dp)
+{
+	client_table[dp->index] = NULL;
+	num_clients--;
+
+	debug_printk(("resetting..\n"));
+	snd_seq_oss_reset(dp);
+
+	debug_printk(("cleaning up..\n"));
+	snd_seq_oss_synth_cleanup(dp);
+	snd_seq_oss_midi_cleanup(dp);
+
+	/* clear slot */
+	debug_printk(("releasing resource..\n"));
+	if (dp->port >= 0)
+		delete_port(dp);
+	if (dp->queue >= 0)
+		delete_seq_queue(dp);
+
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	debug_printk(("release done\n"));
+}
+
+
+/*
+ * Wait until the queue is empty (if we don't have nonblock)
+ */
+void
+snd_seq_oss_drain_write(seq_oss_devinfo_t *dp)
+{
+	if (! dp->timer->running)
+		return;
+	if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
+	    dp->writeq) {
+		debug_printk(("syncing..\n"));
+		while (snd_seq_oss_writeq_sync(dp->writeq))
+			;
+	}
+}
+
+
+/*
+ * reset sequencer devices
+ */
+void
+snd_seq_oss_reset(seq_oss_devinfo_t *dp)
+{
+	int i;
+
+	/* reset all synth devices */
+	for (i = 0; i < dp->max_synthdev; i++)
+		snd_seq_oss_synth_reset(dp, i);
+
+	/* reset all midi devices */
+	if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
+		for (i = 0; i < dp->max_mididev; i++)
+			snd_seq_oss_midi_reset(dp, i);
+	}
+
+	/* remove queues */
+	if (dp->readq)
+		snd_seq_oss_readq_clear(dp->readq);
+	if (dp->writeq)
+		snd_seq_oss_writeq_clear(dp->writeq);
+
+	/* reset timer */
+	snd_seq_oss_timer_stop(dp->timer);
+}
+
+/*
+ * proc interface
+ */
+void
+snd_seq_oss_system_info_read(snd_info_buffer_t *buf)
+{
+	int i;
+	seq_oss_devinfo_t *dp;
+
+	snd_iprintf(buf, "ALSA client number %d\n", system_client);
+	snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
+
+	snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
+	for (i = 0; i < num_clients; i++) {
+		snd_iprintf(buf, "\nApplication %d: ", i);
+		if ((dp = client_table[i]) == NULL) {
+			snd_iprintf(buf, "*empty*\n");
+			continue;
+		}
+		snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
+		snd_iprintf(buf, "  sequencer mode = %s : file open mode = %s\n",
+			    (dp->seq_mode ? "music" : "synth"),
+			    filemode_str(dp->file_mode));
+		if (dp->seq_mode)
+			snd_iprintf(buf, "  timer tempo = %d, timebase = %d\n",
+				    dp->timer->oss_tempo, dp->timer->oss_timebase);
+		snd_iprintf(buf, "  max queue length %d\n", maxqlen);
+		if (is_read_mode(dp->file_mode) && dp->readq)
+			snd_seq_oss_readq_info_read(dp->readq, buf);
+	}
+}
+
+/*
+ * misc. functions for proc interface
+ */
+char *
+enabled_str(int bool)
+{
+	return bool ? "enabled" : "disabled";
+}
+
+char *
+filemode_str(int val)
+{
+	static char *str[] = {
+		"none", "read", "write", "read/write",
+	};
+	return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
+}
+
+
diff -Nru linux/sound/core/seq/oss/seq_oss_ioctl.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_ioctl.c
--- linux/sound/core/seq/oss/seq_oss_ioctl.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_ioctl.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,191 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * OSS compatible i/o control
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_device.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_writeq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "seq_oss_event.h"
+
+int
+snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg)
+{
+	int dev, val;
+	struct synth_info inf;
+	struct midi_info minf;
+	unsigned char ev[8];
+	void *arg = (void*)carg;
+	snd_seq_event_t tmpev;
+
+	switch (cmd) {
+	case SNDCTL_TMR_TIMEBASE:
+	case SNDCTL_TMR_TEMPO:
+	case SNDCTL_TMR_START:
+	case SNDCTL_TMR_STOP:
+	case SNDCTL_TMR_CONTINUE:
+	case SNDCTL_TMR_METRONOME:
+	case SNDCTL_TMR_SOURCE:
+	case SNDCTL_TMR_SELECT:
+	case SNDCTL_SEQ_CTRLRATE:
+		return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
+
+	case SNDCTL_SEQ_PANIC:
+		debug_printk(("panic\n"));
+		snd_seq_oss_reset(dp);
+		return -EINVAL;
+
+	case SNDCTL_SEQ_SYNC:
+		debug_printk(("sync\n"));
+		if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
+			return 0;
+		while (snd_seq_oss_writeq_sync(dp->writeq))
+			;
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		return 0;
+
+	case SNDCTL_SEQ_RESET:
+		debug_printk(("reset\n"));
+		snd_seq_oss_reset(dp);
+		return 0;
+
+	case SNDCTL_SEQ_TESTMIDI:
+		debug_printk(("test midi\n"));
+		if (get_user(dev, (int *)arg))
+			return -EFAULT;
+		return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
+
+	case SNDCTL_SEQ_GETINCOUNT:
+		debug_printk(("get in count\n"));
+		if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
+			return 0;
+		return put_user(dp->readq->qlen, (int *)arg) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_GETOUTCOUNT:
+		debug_printk(("get out count\n"));
+		if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
+			return 0;
+		return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), (int *)arg) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_GETTIME:
+		debug_printk(("get time\n"));
+		return put_user(snd_seq_oss_timer_cur_tick(dp->timer), (int *)arg) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_RESETSAMPLES:
+		debug_printk(("reset samples\n"));
+		if (get_user(dev, (int *)arg))
+			return -EFAULT;
+		return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
+
+	case SNDCTL_SEQ_NRSYNTHS:
+		debug_printk(("nr synths\n"));
+		return put_user(dp->max_synthdev, (int *)arg) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_NRMIDIS:
+		debug_printk(("nr midis\n"));
+		return put_user(dp->max_mididev, (int *)arg) ? -EFAULT : 0;
+
+	case SNDCTL_SYNTH_MEMAVL:
+		debug_printk(("mem avail\n"));
+		if (get_user(dev, (int *)arg))
+			return -EFAULT;
+		val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
+		return put_user(val, (int *)arg) ? -EFAULT : 0;
+
+	case SNDCTL_FM_4OP_ENABLE:
+		debug_printk(("4op\n"));
+		if (get_user(dev, (int *)arg))
+			return -EFAULT;
+		snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
+		return 0;
+
+	case SNDCTL_SYNTH_INFO:
+	case SNDCTL_SYNTH_ID:
+		debug_printk(("synth info\n"));
+		if (copy_from_user(&inf, arg, sizeof(inf)))
+			return -EFAULT;
+		if (snd_seq_oss_synth_make_info(dp, inf.device, &inf) < 0)
+			return -EINVAL;
+		if (copy_to_user(arg, &inf, sizeof(inf)))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_SEQ_OUTOFBAND:
+		debug_printk(("out of bound\n"));
+		if (copy_from_user(ev, arg, 8))
+			return -EFAULT;
+		memset(&tmpev, 0, sizeof(tmpev));
+		snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
+		tmpev.time.tick = 0;
+		if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) {
+			snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
+		}
+		return 0;
+
+	case SNDCTL_MIDI_INFO:
+		debug_printk(("midi info\n"));
+		if (copy_from_user(&minf, arg, sizeof(minf)))
+			return -EFAULT;
+		if (snd_seq_oss_midi_make_info(dp, minf.device, &minf) < 0)
+			return -EINVAL;
+		if (copy_to_user(arg, &minf, sizeof(minf)))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_SEQ_THRESHOLD:
+		debug_printk(("threshold\n"));
+		if (! is_write_mode(dp->file_mode))
+			return 0;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val < 1)
+			val = 1;
+		if (val >= dp->writeq->maxlen)
+			val = dp->writeq->maxlen - 1;
+		snd_seq_oss_writeq_set_output(dp->writeq, val);
+		return 0;
+
+	case SNDCTL_MIDI_PRETIME:
+		debug_printk(("pretime\n"));
+		if (dp->readq == NULL || !is_read_mode(dp->file_mode))
+			return 0;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val <= 0)
+			val = -1;
+		else
+			val = (HZ * val) / 10;
+		dp->readq->pre_event_timeout = val;
+		return put_user(val, (int *)arg) ? -EFAULT : 0;
+
+	default:
+		debug_printk(("others\n"));
+		if (! is_write_mode(dp->file_mode))
+			return -EIO;
+		return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
+	}
+	return 0;
+}
+
diff -Nru linux/sound/core/seq/oss/seq_oss_midi.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.c
--- linux/sound/core/seq/oss/seq_oss_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,697 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * MIDI device handlers
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_midi.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <sound/seq_midi_event.h>
+#include "../seq_lock.h"
+#include <linux/init.h>
+
+
+/*
+ * constants
+ */
+#define SNDRV_SEQ_OSS_MAX_MIDI_NAME	30
+
+/*
+ * definition of midi device record
+ */
+struct seq_oss_midi_t {
+	int seq_device;		/* device number */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	unsigned int flags;	/* port capability */
+	int opened;		/* flag for opening */
+	unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
+	snd_midi_event_t *coder;	/* MIDI event coder */
+	seq_oss_devinfo_t *devinfo;	/* assigned OSSseq device */
+	snd_use_lock_t use_lock;
+};
+
+
+/*
+ * midi device table
+ */
+static int max_midi_devs = 0;
+static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
+
+static spinlock_t register_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * prototypes
+ */
+static seq_oss_midi_t *get_mdev(int dev);
+static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev);
+static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev);
+static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev);
+
+/*
+ * look up the existing ports
+ * this looks a very exhausting job.
+ */
+int __init
+snd_seq_oss_midi_lookup_ports(int client)
+{
+	snd_seq_system_info_t sysinfo;
+	snd_seq_client_info_t clinfo;
+	snd_seq_port_info_t pinfo;
+	int rc;
+
+	rc = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SYSTEM_INFO, &sysinfo);
+	if (rc < 0)
+		return rc;
+	
+	memset(&clinfo, 0, sizeof(clinfo));
+	memset(&pinfo, 0, sizeof(pinfo));
+	clinfo.client = -1;
+	while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, &clinfo) == 0) {
+		if (clinfo.client == client)
+			continue; /* ignore myself */
+		pinfo.addr.client = clinfo.client;
+		pinfo.addr.port = -1;
+		while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, &pinfo) == 0)
+			snd_seq_oss_midi_check_new_port(&pinfo);
+	}
+	return 0;
+}
+
+
+/*
+ */
+static seq_oss_midi_t *
+get_mdev(int dev)
+{
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	mdev = midi_devs[dev];
+	if (mdev)
+		snd_use_lock_use(&mdev->use_lock);
+	spin_unlock_irqrestore(&register_lock, flags);
+	return mdev;
+}
+
+/*
+ * look for the identical slot
+ */
+static seq_oss_midi_t *
+find_slot(int client, int port)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_midi_devs; i++) {
+		mdev = midi_devs[i];
+		if (mdev && mdev->client == client && mdev->port == port) {
+			/* found! */
+			snd_use_lock_use(&mdev->use_lock);
+			spin_unlock_irqrestore(&register_lock, flags);
+			return mdev;
+		}
+	}
+	spin_unlock_irqrestore(&register_lock, flags);
+	return NULL;
+}
+
+
+#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
+#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
+/*
+ * register a new port if it doesn't exist yet
+ */
+int
+snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
+	/* the port must include generic midi */
+	if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
+		return 0;
+	/* either read or write subscribable */
+	if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
+	    (pinfo->capability & PERM_READ) != PERM_READ)
+		return 0;
+
+	/*
+	 * look for the identical slot
+	 */
+	if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
+		/* already exists */
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	/*
+	 * allocate midi info record
+	 */
+	if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc midi info\n");
+		return -ENOMEM;
+	}
+
+	/* copy the port information */
+	mdev->client = pinfo->addr.client;
+	mdev->port = pinfo->addr.port;
+	mdev->flags = pinfo->capability;
+	mdev->opened = 0;
+	snd_use_lock_init(&mdev->use_lock);
+
+	/* copy and truncate the name of synth device */
+	strncpy(mdev->name, pinfo->name, sizeof(mdev->name));
+	mdev->name[sizeof(mdev->name) - 1] = 0;
+
+	/* create MIDI coder */
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
+		snd_printk(KERN_ERR "can't malloc midi coder\n");
+		kfree(mdev);
+		return -ENOMEM;
+	}
+
+	/*
+	 * look for en empty slot
+	 */
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_midi_devs; i++) {
+		if (midi_devs[i] == NULL)
+			break;
+	}
+	if (i >= max_midi_devs) {
+		if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
+			snd_midi_event_free(mdev->coder);
+			kfree(mdev);
+			spin_unlock_irqrestore(&register_lock, flags);
+			return -ENOMEM;
+		}
+		max_midi_devs++;
+	}
+	mdev->seq_device = i;
+	midi_devs[mdev->seq_device] = mdev;
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	/*MOD_INC_USE_COUNT;*/
+	return 0;
+}
+
+/*
+ * release the midi device if it was registered
+ */
+int
+snd_seq_oss_midi_check_exit_port(int client, int port)
+{
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+	int index;
+
+	if ((mdev = find_slot(client, port)) != NULL) {
+		spin_lock_irqsave(&register_lock, flags);
+		midi_devs[mdev->seq_device] = NULL;
+		spin_unlock_irqrestore(&register_lock, flags);
+		snd_use_lock_free(&mdev->use_lock);
+		snd_use_lock_sync(&mdev->use_lock);
+		if (mdev->coder)
+			snd_midi_event_free(mdev->coder);
+		kfree(mdev);
+	}
+	spin_lock_irqsave(&register_lock, flags);
+	for (index = max_midi_devs - 1; index >= 0; index--) {
+		if (midi_devs[index])
+			break;
+	}
+	max_midi_devs = index + 1;
+	spin_unlock_irqrestore(&register_lock, flags);
+	return 0;
+}
+
+
+/*
+ * release the midi device if it was registered
+ */
+void
+snd_seq_oss_midi_clear_all(void)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_midi_devs; i++) {
+		if ((mdev = midi_devs[i]) != NULL) {
+			if (mdev->coder)
+				snd_midi_event_free(mdev->coder);
+			kfree(mdev);
+			midi_devs[i] = NULL;
+		}
+	}
+	max_midi_devs = 0;
+	spin_unlock_irqrestore(&register_lock, flags);
+}
+
+
+/*
+ * set up midi tables
+ */
+void
+snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp)
+{
+	dp->max_mididev = max_midi_devs;
+}
+
+/*
+ * clean up midi tables
+ */
+void
+snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp)
+{
+	int i;
+	for (i = 0; i < dp->max_mididev; i++)
+		snd_seq_oss_midi_close(dp, i);
+	dp->max_mididev = 0;
+}
+
+
+/*
+ * open all midi devices.  ignore errors.
+ */
+void
+snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode)
+{
+	int i;
+	for (i = 0; i < dp->max_mididev; i++)
+		snd_seq_oss_midi_open(dp, i, file_mode);
+}
+
+
+/*
+ * get the midi device information
+ */
+static seq_oss_midi_t *
+get_mididev(seq_oss_devinfo_t *dp, int dev)
+{
+	if (dev < 0 || dev >= dp->max_mididev)
+		return NULL;
+	return get_mdev(dev);
+}
+
+
+/*
+ * open the midi device if not opened yet
+ */
+int
+snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
+{
+	int perm;
+	seq_oss_midi_t *mdev;
+	snd_seq_port_subscribe_t subs;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENODEV;
+
+	/* already used? */
+	if (mdev->opened && mdev->devinfo != dp) {
+		snd_use_lock_free(&mdev->use_lock);
+		return -EBUSY;
+	}
+
+	perm = 0;
+	if (is_write_mode(fmode))
+		perm |= PERM_WRITE;
+	if (is_read_mode(fmode))
+		perm |= PERM_READ;
+	perm &= mdev->flags;
+	if (perm == 0) {
+		snd_use_lock_free(&mdev->use_lock);
+		return -ENXIO;
+	}
+
+	/* already opened? */
+	if ((mdev->opened & perm) == perm) {
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	perm &= ~mdev->opened;
+
+	memset(&subs, 0, sizeof(subs));
+
+	if (perm & PERM_WRITE) {
+		subs.sender = dp->addr;
+		subs.dest.client = mdev->client;
+		subs.dest.port = mdev->port;
+		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
+			mdev->opened |= PERM_WRITE;
+	}
+	if (perm & PERM_READ) {
+		subs.sender.client = mdev->client;
+		subs.sender.port = mdev->port;
+		subs.dest = dp->addr;
+		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
+			mdev->opened |= PERM_READ;
+	}
+
+	if (! mdev->opened) {
+		snd_use_lock_free(&mdev->use_lock);
+		return -ENXIO;
+	}
+
+	mdev->devinfo = dp;
+	snd_use_lock_free(&mdev->use_lock);
+	return 0;
+}
+
+/*
+ * close the midi device if already opened
+ */
+int
+snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_midi_t *mdev;
+	snd_seq_port_subscribe_t subs;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENODEV;
+	if (! mdev->opened || mdev->devinfo != dp) {
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
+	memset(&subs, 0, sizeof(subs));
+	if (mdev->opened & PERM_WRITE) {
+		subs.sender = dp->addr;
+		subs.dest.client = mdev->client;
+		subs.dest.port = mdev->port;
+		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
+	}
+	if (mdev->opened & PERM_READ) {
+		subs.sender.client = mdev->client;
+		subs.sender.port = mdev->port;
+		subs.dest = dp->addr;
+		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
+	}
+
+	mdev->opened = 0;
+	mdev->devinfo = NULL;
+
+	snd_use_lock_free(&mdev->use_lock);
+	return 0;
+}
+
+/*
+ * change seq capability flags to file mode flags
+ */
+int
+snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_midi_t *mdev;
+	int mode;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return 0;
+
+	mode = 0;
+	if (mdev->opened & PERM_WRITE)
+		mode |= SNDRV_SEQ_OSS_FILE_WRITE;
+	if (mdev->opened & PERM_READ)
+		mode |= SNDRV_SEQ_OSS_FILE_READ;
+
+	snd_use_lock_free(&mdev->use_lock);
+	return mode;
+}
+
+/*
+ * reset the midi device and close it:
+ * so far, only close the device.
+ */
+void
+snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return;
+	if (! mdev->opened) {
+		snd_use_lock_free(&mdev->use_lock);
+		return;
+	}
+
+	if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC &&
+	    (mdev->opened & PERM_WRITE)) {
+		snd_seq_event_t ev;
+		int c;
+
+		debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
+		memset(&ev, 0, sizeof(ev));
+		ev.dest.client = mdev->client;
+		ev.dest.port = mdev->port;
+		ev.queue = dp->queue;
+		ev.source.port = dp->port;
+		for (c = 0; c < 16; c++) {
+			ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+			ev.data.control.channel = c;
+			ev.data.control.param = 123;
+			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
+			ev.data.control.param = 121;
+			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
+			ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
+			ev.data.control.value = 0;
+			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
+		}
+	}
+	snd_seq_oss_midi_close(dp, dev);
+	snd_use_lock_free(&mdev->use_lock);
+}
+
+
+/*
+ * get client/port of the specified MIDI device
+ */
+void
+snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return;
+	addr->client = mdev->client;
+	addr->port = mdev->port;
+	snd_use_lock_free(&mdev->use_lock);
+}
+
+
+/*
+ * input callback - this can be atomic
+ */
+int
+snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data)
+{
+	seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
+	seq_oss_midi_t *mdev;
+	int rc;
+
+	if (dp->readq == NULL)
+		return 0;
+	if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
+		return 0;
+	if (! (mdev->opened & PERM_READ)) {
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+		rc = send_synth_event(dp, ev, mdev->seq_device);
+	else
+		rc = send_midi_event(dp, ev, mdev);
+
+	snd_use_lock_free(&mdev->use_lock);
+	return rc;
+}
+
+/*
+ * convert ALSA sequencer event to OSS synth event
+ */
+static int
+send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
+{
+	evrec_t ossev;
+
+	memset(&ossev, 0, sizeof(ossev));
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+		ossev.v.cmd = MIDI_NOTEON; break;
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+		ossev.v.cmd = MIDI_NOTEOFF; break;
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		ossev.v.cmd = MIDI_KEY_PRESSURE; break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+		ossev.l.cmd = MIDI_CTL_CHANGE; break;
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+		ossev.l.cmd = MIDI_PGM_CHANGE; break;
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		ossev.l.cmd = MIDI_CHN_PRESSURE; break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		ossev.l.cmd = MIDI_PITCH_BEND; break;
+	default:
+		return 0; /* not supported */
+	}
+
+	ossev.v.dev = dev;
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		ossev.v.code = EV_CHN_VOICE;
+		ossev.v.note = ev->data.note.note;
+		ossev.v.parm = ev->data.note.velocity;
+		break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		ossev.l.code = EV_CHN_COMMON;
+		ossev.l.p1 = ev->data.control.param;
+		ossev.l.val = ev->data.control.value;
+		break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		ossev.l.code = EV_CHN_COMMON;
+		ossev.l.val = ev->data.control.value + 8192;
+		break;
+	}
+	
+	snd_seq_oss_readq_put_event(dp->readq, &ossev);
+
+	return 0;
+}
+
+/*
+ * decode event and send MIDI bytes to read queue
+ */
+static int
+send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev)
+{
+	char msg[32]; /* enough except for sysex? */
+	int len;
+	
+	snd_seq_oss_readq_put_timestamp(dp->readq, snd_seq_oss_timer_cur_tick(dp->timer), dp->seq_mode);
+	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
+					       ev->data.ext.ptr, ev->data.ext.len);
+	} else {
+		len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
+		if (len > 0)
+			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
+	}
+
+	return 0;
+}
+
+
+/*
+ * dump midi data
+ * return 0 : enqueued
+ *        non-zero : invalid - ignored
+ */
+int
+snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENODEV;
+	if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
+		snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+	snd_use_lock_free(&mdev->use_lock);
+	return -EINVAL;
+}
+
+/*
+ * create OSS compatible midi_info record
+ */
+int
+snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENXIO;
+	inf->device = dev;
+	inf->dev_type = 0; /* FIXME: ?? */
+	inf->capabilities = 0; /* FIXME: ?? */
+	strncpy(inf->name, mdev->name, sizeof(inf->name));
+	snd_use_lock_free(&mdev->use_lock);
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static char *
+capmode_str(int val)
+{
+	val &= PERM_READ|PERM_WRITE;
+	if (val == (PERM_READ|PERM_WRITE))
+		return "read/write";
+	else if (val == PERM_READ)
+		return "read";
+	else if (val == PERM_WRITE)
+		return "write";
+	else
+		return "none";
+}
+
+void
+snd_seq_oss_midi_info_read(snd_info_buffer_t *buf)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+
+	snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
+	for (i = 0; i < max_midi_devs; i++) {
+		snd_iprintf(buf, "\nmidi %d: ", i);
+		mdev = get_mdev(i);
+		if (mdev == NULL) {
+			snd_iprintf(buf, "*empty*\n");
+			continue;
+		}
+		snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
+			    mdev->client, mdev->port);
+		snd_iprintf(buf, "  capability %s / opened %s\n",
+			    capmode_str(mdev->flags),
+			    capmode_str(mdev->opened));
+		snd_use_lock_free(&mdev->use_lock);
+	}
+}
+
diff -Nru linux/sound/core/seq/oss/seq_oss_midi.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.h
--- linux/sound/core/seq/oss/seq_oss_midi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,49 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * midi device information
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_MIDI_H
+#define __SEQ_OSS_MIDI_H
+
+#include "seq_oss_device.h"
+#include <sound/seq_oss_legacy.h>
+
+typedef struct seq_oss_midi_t seq_oss_midi_t;
+
+int snd_seq_oss_midi_lookup_ports(int client);
+int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo);
+int snd_seq_oss_midi_check_exit_port(int client, int port);
+void snd_seq_oss_midi_clear_all(void);
+
+void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp);
+void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp);
+
+int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode);
+void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode);
+int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev);
+void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev);
+int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private);
+int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf);
+void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr);
+
+#endif
diff -Nru linux/sound/core/seq/oss/seq_oss_misc.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_misc.c
--- linux/sound/core/seq/oss/seq_oss_misc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_misc.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,110 @@
+/*----------------------------------------------------------------
+ * miscellaneous functions
+ *----------------------------------------------------------------*/
+
+unsigned short snd_seq_oss_semitone_tuning[24] = 
+{
+/*   0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, 
+/*   8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, 
+/*  16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+unsigned short snd_seq_oss_cent_tuning[100] =
+{
+/*   0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, 
+/*   8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, 
+/*  16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, 
+/*  24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, 
+/*  32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, 
+/*  40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, 
+/*  48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, 
+/*  56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, 
+/*  64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, 
+/*  72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, 
+/*  80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, 
+/*  88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, 
+/*  96 */ 10570, 10576, 10582, 10589
+};
+
+/* convert from MIDI note to frequency */
+int
+snd_seq_oss_note_to_freq(int note_num)
+{
+
+	/*
+	 * This routine converts a midi note to a frequency (multiplied by 1000)
+	 */
+
+	int note, octave, note_freq;
+	static int notes[] = {
+		261632, 277189, 293671, 311132, 329632, 349232,
+		369998, 391998, 415306, 440000, 466162, 493880
+	};
+
+#define BASE_OCTAVE	5
+
+	octave = note_num / 12;
+	note = note_num % 12;
+
+	note_freq = notes[note];
+
+	if (octave < BASE_OCTAVE)
+		note_freq >>= (BASE_OCTAVE - octave);
+	else if (octave > BASE_OCTAVE)
+		note_freq <<= (octave - BASE_OCTAVE);
+
+	/*
+	 * note_freq >>= 1;
+	 */
+
+	return note_freq;
+}
+
+unsigned long
+snd_seq_oss_compute_finetune(unsigned long base_freq, int bend, int range, int vibrato_cents)
+{
+	unsigned long amount;
+	int negative, semitones, cents, multiplier = 1;
+
+	if (!bend || !range || !base_freq)
+		return base_freq;
+
+	if (range >= 8192)
+		range = 8192;
+
+	bend = bend * range / 8192;	/* Convert to cents */
+	bend += vibrato_cents;
+
+	if (!bend)
+		return base_freq;
+
+	negative = bend < 0 ? 1 : 0;
+
+	if (bend < 0)
+		bend *= -1;
+	if (bend > range)
+		bend = range;
+
+	/*
+	   if (bend > 2399)
+	   bend = 2399;
+	 */
+	while (bend > 2399) {
+		multiplier *= 4;
+		bend -= 2400;
+	}
+
+	semitones = bend / 100;
+	if (semitones > 99)
+		semitones = 99;
+	cents = bend % 100;
+
+	amount = (int) (snd_seq_oss_semitone_tuning[semitones] * multiplier *
+			snd_seq_oss_cent_tuning[cents]) / 10000;
+
+	if (negative)
+		return (base_freq * 10000) / amount;	/* Bend down */
+	else
+		return (base_freq * amount) / 10000;	/* Bend up */
+}
+
diff -Nru linux/sound/core/seq/oss/seq_oss_readq.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.c
--- linux/sound/core/seq/oss/seq_oss_readq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,245 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * seq_oss_readq.c - MIDI input queue
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_readq.h"
+#include "seq_oss_event.h"
+#include <sound/seq_oss_legacy.h>
+#include "../seq_lock.h"
+
+/*
+ * constants
+ */
+//#define SNDRV_SEQ_OSS_MAX_TIMEOUT	(unsigned long)(-1)
+#define SNDRV_SEQ_OSS_MAX_TIMEOUT	(HZ * 3600)
+
+
+/*
+ * prototypes
+ */
+
+
+/*
+ * create a read queue
+ */
+seq_oss_readq_t *
+snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen)
+{
+	seq_oss_readq_t *q;
+
+	if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc read queue\n");
+		return NULL;
+	}
+
+	if ((q->q = snd_kcalloc(sizeof(evrec_t) * maxlen, GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc read queue buffer\n");
+		kfree(q);
+		return NULL;
+	}
+
+	q->maxlen = maxlen;
+	q->qlen = 0;
+	q->head = q->tail = 0;
+	init_waitqueue_head(&q->midi_sleep);
+	spin_lock_init(&q->lock);
+	q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT;
+	q->input_time = (unsigned long)-1;
+
+	return q;
+}
+
+/*
+ * delete the read queue
+ */
+void
+snd_seq_oss_readq_delete(seq_oss_readq_t *q)
+{
+	if (q) {
+		snd_seq_oss_readq_clear(q);	/* to be sure */
+		if (q->q)
+			kfree(q->q);
+		kfree(q);
+	}
+}
+
+/*
+ * reset the read queue
+ */
+void
+snd_seq_oss_readq_clear(seq_oss_readq_t *q)
+{
+	if (q->qlen) {
+		q->qlen = 0;
+		q->head = q->tail = 0;
+	}
+	/* if someone sleeping, wake'em up */
+	if (waitqueue_active(&q->midi_sleep))
+		wake_up(&q->midi_sleep);
+	q->input_time = (unsigned long)-1;
+}
+
+/*
+ * put a midi byte
+ */
+int
+snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len)
+{
+	evrec_t rec;
+	int result;
+
+	rec.c[0] = SEQ_MIDIPUTC;
+	rec.c[2] = dev;
+	rec.c[3] = 0;
+
+	while (len-- > 0) {
+		rec.c[1] = *data++;
+		result = snd_seq_oss_readq_put_event(q, &rec);
+		if (result < 0)
+			return result;
+	}
+	return 0;
+}
+
+/*
+ * copy an event to input queue:
+ * return zero if enqueued
+ */
+int
+snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->lock, flags);
+	if (q->qlen >= q->maxlen - 1) {
+		spin_unlock_irqrestore(&q->lock, flags);
+		return -ENOMEM;
+	}
+
+	memcpy(&q->q[q->tail], ev, ev_length(ev));
+	q->tail = (q->tail + 1) % q->maxlen;
+	q->qlen++;
+
+	/* wake up sleeper */
+	if (waitqueue_active(&q->midi_sleep))
+		wake_up(&q->midi_sleep);
+
+	spin_unlock_irqrestore(&q->lock, flags);
+
+	return 0;
+}
+
+
+/*
+ * pop queue
+ */
+evrec_t *
+snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags)
+{
+	evrec_t *p;
+
+	spin_lock_irqsave(&q->lock, *rflags);
+	if (q->qlen == 0) {
+		if (blocking) {
+			snd_seq_sleep_timeout_in_lock(&q->midi_sleep,
+						      &q->lock,
+						      q->pre_event_timeout);
+		}
+		if (q->qlen == 0) {
+			spin_unlock_irqrestore(&q->lock, *rflags);
+			return NULL;
+		}
+	}
+	p = q->q + q->head;
+
+	return p;
+}
+
+/*
+ * unlock queue
+ */
+void
+snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags)
+{
+	spin_unlock_irqrestore(&q->lock, flags);
+}
+
+/*
+ * drain one record and unlock queue
+ */
+void
+snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags)
+{
+	if (q->qlen > 0) {
+		q->head = (q->head + 1) % q->maxlen;
+		q->qlen--;
+	}
+	spin_unlock_irqrestore(&q->lock, flags);
+}
+
+/*
+ * polling/select:
+ * return non-zero if readq is not empty.
+ */
+unsigned int
+snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait)
+{
+	poll_wait(file, &q->midi_sleep, wait);
+	return q->qlen;
+}
+
+/*
+ * put a timestamp
+ */
+int
+snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode)
+{
+	if (curt != q->input_time) {
+		evrec_t rec;
+		switch (seq_mode) {
+		case SNDRV_SEQ_OSS_MODE_SYNTH:
+			rec.echo = (curt << 8) | SEQ_WAIT;
+			snd_seq_oss_readq_put_event(q, &rec);
+			break;
+		case SNDRV_SEQ_OSS_MODE_MUSIC:
+			rec.t.code = EV_TIMING;
+			rec.t.cmd = TMR_WAIT_ABS;
+			rec.t.time = curt;
+			snd_seq_oss_readq_put_event(q, &rec);
+			break;
+		}
+		q->input_time = curt;
+	}
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+void
+snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf)
+{
+	snd_iprintf(buf, "  read queue [%s] length = %d : tick = %ld\n",
+		    (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
+		    q->qlen, q->input_time);
+}
diff -Nru linux/sound/core/seq/oss/seq_oss_readq.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.h
--- linux/sound/core/seq/oss/seq_oss_readq.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,53 @@
+/*
+ * OSS compatible sequencer driver
+ * read fifo queue
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_READQ_H
+#define __SEQ_OSS_READQ_H
+
+#include "seq_oss_device.h"
+
+
+/*
+ * definition of read queue
+ */
+struct seq_oss_readq_t {
+	evrec_t *q;
+	int qlen;
+	int maxlen;
+	int head, tail;
+	unsigned long pre_event_timeout;
+	unsigned long input_time;
+	wait_queue_head_t midi_sleep;
+	spinlock_t lock;
+};
+
+seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen);
+void snd_seq_oss_readq_delete(seq_oss_readq_t *q);
+void snd_seq_oss_readq_clear(seq_oss_readq_t *readq);
+unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait);
+int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len);
+int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev);
+int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode);
+evrec_t *snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags);
+void snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags);
+void snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags);
+
+#endif
diff -Nru linux/sound/core/seq/oss/seq_oss_rw.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_rw.c
--- linux/sound/core/seq/oss/seq_oss_rw.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_rw.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,197 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * read/write/select interface to device file
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_device.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_writeq.h"
+#include "seq_oss_synth.h"
+#include <sound/seq_oss_legacy.h>
+#include "seq_oss_event.h"
+#include "seq_oss_timer.h"
+#include "../seq_clientmgr.h"
+
+
+/*
+ * protoypes
+ */
+static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt);
+
+
+/*
+ * read interface
+ */
+
+int
+snd_seq_oss_read(seq_oss_devinfo_t *dp, char *buf, int count)
+{
+	seq_oss_readq_t *readq = dp->readq;
+	int cnt, pos;
+	evrec_t *q;
+	unsigned long flags;
+
+	if (readq == NULL || ! is_read_mode(dp->file_mode))
+		return -EIO;
+
+	/* copy queued events to read buffer */
+	cnt = count;
+	pos = 0;
+	q = snd_seq_oss_readq_pick(readq, !is_nonblock_mode(dp->file_mode), &flags);
+	if (q == NULL)
+		return 0;
+	do {
+		int ev_len;
+		/* tansfer the data */
+		ev_len = ev_length(q);
+		if (copy_to_user(buf + pos, q, ev_len)) {
+			snd_seq_oss_readq_unlock(readq, flags);
+			break;
+		}
+		snd_seq_oss_readq_free(readq, flags);
+		pos += ev_len;
+		cnt -= ev_len;
+		if (cnt < ev_len)
+			break;
+	} while ((q = snd_seq_oss_readq_pick(readq, 0, &flags)) != NULL);
+
+	return count - cnt;
+}
+
+
+/*
+ * write interface
+ */
+
+int
+snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt)
+{
+	int rc, c, p, ev_size;
+	evrec_t rec;
+
+	if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
+		return -EIO;
+
+	c = count;
+	p = 0;
+	while (c >= SHORT_EVENT_SIZE) {
+		if (copy_from_user(rec.c, buf + p, SHORT_EVENT_SIZE))
+			break;
+		p += SHORT_EVENT_SIZE;
+
+		if (rec.s.code == SEQ_FULLSIZE) {
+			/* load patch */
+			int fmt = (*(unsigned short *)rec.c) & 0xffff;
+			return snd_seq_oss_synth_load_patch(dp, rec.s.dev, fmt, buf, p, c);
+
+		}
+		if (ev_is_long(&rec)) {
+			/* extended code */
+			if (rec.s.code == SEQ_EXTENDED &&
+			    dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+				return -EINVAL;
+			ev_size = LONG_EVENT_SIZE;
+			if (c < ev_size)
+				break;
+			/* copy the reset 4 bytes */
+			if (copy_from_user(rec.c + SHORT_EVENT_SIZE, buf + p,
+					   LONG_EVENT_SIZE - SHORT_EVENT_SIZE))
+				break;
+			p += LONG_EVENT_SIZE - SHORT_EVENT_SIZE;
+
+		} else {
+			/* old-type code */
+			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+				return -EINVAL;
+			ev_size = SHORT_EVENT_SIZE;
+		}
+
+		/* insert queue */
+		if ((rc = insert_queue(dp, &rec, opt)) < 0)
+			break;
+
+		c -= ev_size;
+	}
+
+	if (count == c && is_nonblock_mode(dp->file_mode))
+		return -EAGAIN;
+	return count - c;
+}
+
+
+/*
+ * insert event record to write queue
+ * return: 0 = OK, non-zero = NG
+ */
+static int
+insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt)
+{
+	int rc = 0;
+	snd_seq_event_t event;
+
+	/* if this is a timing event, process the current time */
+	if (snd_seq_oss_process_timer_event(dp->timer, rec))
+		return 0; /* no need to insert queue */
+
+	/* parse this event */
+	memset(&event, 0, sizeof(event));
+	/* set dummy -- to be sure */
+	event.type = SNDRV_SEQ_EVENT_NOTEOFF;
+	snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
+
+	if (snd_seq_oss_process_event(dp, rec, &event))
+		return 0; /* invalid event - no need to insert queue */
+
+	event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
+	if (dp->timer->realtime || !dp->timer->running) {
+		snd_seq_oss_dispatch(dp, &event, 0, 0);
+	} else {
+		if (is_nonblock_mode(dp->file_mode))
+			rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
+		else
+			rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
+	}
+	return rc;
+}
+		
+
+/*
+ * select / poll
+ */
+  
+unsigned int
+snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+
+	/* input */
+	if (dp->readq && is_read_mode(dp->file_mode)) {
+		if (snd_seq_oss_readq_poll(dp->readq, file, wait))
+			mask |= POLLIN | POLLRDNORM;
+	}
+
+	/* output */
+	if (dp->writeq && is_write_mode(dp->file_mode)) {
+		if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	return mask;
+}
diff -Nru linux/sound/core/seq/oss/seq_oss_synth.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.c
--- linux/sound/core/seq/oss/seq_oss_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,642 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * synth device handlers
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "../seq_lock.h"
+#include <linux/init.h>
+
+/*
+ * constants
+ */
+#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME	30
+#define MAX_SYSEX_BUFLEN		128
+
+
+/*
+ * definition of synth info records
+ */
+
+/* sysex buffer */
+struct seq_oss_synth_sysex_t {
+	int len;
+	int skip;
+	unsigned char buf[MAX_SYSEX_BUFLEN];
+};
+
+/* synth info */
+struct seq_oss_synth_t {
+	int seq_device;
+
+	/* for synth_info */
+	int synth_type;
+	int synth_subtype;
+	int nr_voices;
+
+	char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];
+	snd_seq_oss_callback_t oper;
+
+	int opened;
+
+	void *private_data;
+	snd_use_lock_t use_lock;
+};
+
+
+/*
+ * device table
+ */
+static int max_synth_devs = 0;
+static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
+static seq_oss_synth_t midi_synth_dev = {
+	-1, /* seq_device */
+	SYNTH_TYPE_MIDI, /* synth_type */
+	0, /* synth_subtype */
+	16, /* nr_voices */
+	"MIDI", /* name */
+};
+
+static spinlock_t register_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * prototypes
+ */
+static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev);
+static void reset_channels(seq_oss_synthinfo_t *info);
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+/*
+ * global initialization
+ */
+void __init
+snd_seq_oss_synth_init(void)
+{
+	snd_use_lock_init(&midi_synth_dev.use_lock);
+}
+
+/*
+ * registration of the synth device
+ */
+int
+snd_seq_oss_synth_register(snd_seq_device_t *dev)
+{
+	int i;
+	seq_oss_synth_t *rec;
+	snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	unsigned long flags;
+
+	if ((rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc synth info\n");
+		return -ENOMEM;
+	}
+	rec->seq_device = -1;
+	rec->synth_type = reg->type;
+	rec->synth_subtype = reg->subtype;
+	rec->nr_voices = reg->nvoices;
+	rec->oper = reg->oper;
+	rec->private_data = reg->private_data;
+	rec->opened = 0;
+	snd_use_lock_init(&rec->use_lock);
+
+	/* copy and truncate the name of synth device */
+	strncpy(rec->name, dev->name, sizeof(rec->name));
+	rec->name[sizeof(rec->name)-1] = 0;
+
+	/* registration */
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_synth_devs; i++) {
+		if (synth_devs[i] == NULL)
+			break;
+	}
+	if (i >= max_synth_devs) {
+		if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
+			spin_unlock_irqrestore(&register_lock, flags);
+			snd_printk(KERN_ERR "no more synth slot\n");
+			kfree(rec);
+			return -ENOMEM;
+		}
+		max_synth_devs++;
+	}
+	rec->seq_device = i;
+	synth_devs[i] = rec;
+	debug_printk(("synth %s registered %d\n", rec->name, i));
+	spin_unlock_irqrestore(&register_lock, flags);
+	dev->driver_data = rec;
+	return 0;
+}
+
+
+int
+snd_seq_oss_synth_unregister(snd_seq_device_t *dev)
+{
+	int index;
+	seq_oss_synth_t *rec = dev->driver_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	for (index = 0; index < max_synth_devs; index++) {
+		if (synth_devs[index] == rec)
+			break;
+	}
+	if (index >= max_synth_devs) {
+		spin_unlock_irqrestore(&register_lock, flags);
+		snd_printk(KERN_ERR "can't unregister synth\n");
+		return -EINVAL;
+	}
+	synth_devs[index] = NULL;
+	if (index == max_synth_devs - 1) {
+		for (index--; index >= 0; index--) {
+			if (synth_devs[index])
+				break;
+		}
+		max_synth_devs = index + 1;
+	}
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	snd_use_lock_sync(&rec->use_lock);
+	kfree(rec);
+
+	return 0;
+}
+
+
+/*
+ */
+static seq_oss_synth_t *
+get_sdev(int dev)
+{
+	seq_oss_synth_t *rec;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	rec = synth_devs[dev];
+	if (rec)
+		snd_use_lock_use(&rec->use_lock);
+	spin_unlock_irqrestore(&register_lock, flags);
+	return rec;
+}
+
+
+/*
+ * set up synth tables
+ */
+
+void
+snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp)
+{
+	int i;
+	seq_oss_synth_t *rec;
+	seq_oss_synthinfo_t *info;
+
+	dp->max_synthdev = max_synth_devs;
+	dp->synth_opened = 0;
+	memset(dp->synths, 0, sizeof(dp->synths));
+	for (i = 0; i < dp->max_synthdev; i++) {
+		rec = get_sdev(i);
+		if (rec == NULL)
+			continue;
+		if (rec->oper.open == NULL || rec->oper.close == NULL) {
+			snd_use_lock_free(&rec->use_lock);
+			continue;
+		}
+		info = &dp->synths[i];
+		info->arg.app_index = dp->port;
+		info->arg.file_mode = dp->file_mode;
+		info->arg.seq_mode = dp->seq_mode;
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
+			info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
+		else
+			info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
+		info->opened = 0;
+		if (!try_inc_mod_count(rec->oper.owner)) {
+			snd_use_lock_free(&rec->use_lock);
+			continue;
+		}
+		if (rec->oper.open(&info->arg, rec->private_data) < 0) {
+			dec_mod_count(rec->oper.owner);
+			snd_use_lock_free(&rec->use_lock);
+			continue;
+		}
+		info->nr_voices = rec->nr_voices;
+		if (info->nr_voices > 0) {
+			info->ch = snd_kcalloc(sizeof(seq_oss_chinfo_t) * info->nr_voices, GFP_KERNEL);
+			reset_channels(info);
+		}
+		debug_printk(("synth %d assigned\n", i));
+		info->opened++;
+		rec->opened++;
+		dp->synth_opened++;
+		snd_use_lock_free(&rec->use_lock);
+	}
+}
+
+
+/*
+ * set up synth tables for MIDI emulation - /dev/music mode only
+ */
+
+void
+snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp)
+{
+	int i;
+
+	if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
+		return;
+
+	for (i = 0; i < dp->max_mididev; i++) {
+		seq_oss_synthinfo_t *info;
+		info = &dp->synths[dp->max_synthdev];
+		if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)
+			continue;
+		info->arg.app_index = dp->port;
+		info->arg.file_mode = dp->file_mode;
+		info->arg.seq_mode = dp->seq_mode;
+		info->arg.private_data = info;
+		info->is_midi = 1;
+		info->midi_mapped = i;
+		info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
+		snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);
+		info->opened = 1;
+		midi_synth_dev.opened++;
+		dp->max_synthdev++;
+		if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
+			break;
+	}
+}
+
+
+/*
+ * clean up synth tables
+ */
+
+void
+snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp)
+{
+	int i;
+	seq_oss_synth_t *rec;
+	seq_oss_synthinfo_t *info;
+
+	for (i = 0; i < dp->max_synthdev; i++) {
+		info = &dp->synths[i];
+		if (! info->opened)
+			continue;
+		if (info->is_midi) {
+			snd_seq_oss_midi_close(dp, info->midi_mapped);
+			midi_synth_dev.opened--;
+		} else {
+			rec = get_sdev(i);
+			if (rec == NULL)
+				continue;
+			if (rec->opened) {
+				debug_printk(("synth %d closed\n", i));
+				rec->oper.close(&info->arg);
+				dec_mod_count(rec->oper.owner);
+				rec->opened--;
+			}
+			snd_use_lock_free(&rec->use_lock);
+		}
+		if (info->sysex)
+			kfree(info->sysex);
+		if (info->ch)
+			kfree(info->ch);
+	}
+	dp->synth_opened = 0;
+	dp->max_synthdev = 0;
+}
+
+/*
+ * check if the specified device is MIDI mapped device
+ */
+static int
+is_midi_dev(seq_oss_devinfo_t *dp, int dev)
+{
+	if (dev < 0 || dev >= dp->max_synthdev)
+		return 0;
+	if (dp->synths[dev].is_midi)
+		return 1;
+	return 0;
+}
+
+/*
+ * return synth device information pointer
+ */
+static seq_oss_synth_t *
+get_synthdev(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_synth_t *rec;
+	if (dev < 0 || dev >= dp->max_synthdev)
+		return NULL;
+	if (! dp->synths[dev].opened)
+		return NULL;
+	if (dp->synths[dev].is_midi)
+		return &midi_synth_dev;
+	if ((rec = get_sdev(dev)) == NULL)
+		return NULL;
+	if (! rec->opened) {
+		snd_use_lock_free(&rec->use_lock);
+		return NULL;
+	}
+	return rec;
+}
+
+
+/*
+ * reset note and velocity on each channel.
+ */
+static void
+reset_channels(seq_oss_synthinfo_t *info)
+{
+	int i;
+	if (info->ch == NULL || ! info->nr_voices)
+		return;
+	for (i = 0; i < info->nr_voices; i++) {
+		info->ch[i].note = -1;
+		info->ch[i].vel = 0;
+	}
+}
+
+
+/*
+ * reset synth device:
+ * call reset callback.  if no callback is defined, send a heartbeat
+ * event to the corresponding port.
+ */
+void
+snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_synth_t *rec;
+	seq_oss_synthinfo_t *info;
+
+	snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
+	info = &dp->synths[dev];
+	if (! info->opened)
+		return;
+	if (info->sysex)
+		info->sysex->len = 0; /* reset sysex */
+	reset_channels(info);
+	if (info->is_midi) {
+		snd_seq_oss_midi_reset(dp, info->midi_mapped);
+		if (snd_seq_oss_midi_open(dp, info->midi_mapped,
+					  dp->file_mode) < 0) {
+			midi_synth_dev.opened--;
+			info->opened = 0;
+			if (info->sysex)
+				kfree(info->sysex);
+			if (info->ch)
+				kfree(info->ch);
+		}
+		return;
+	}
+
+	rec = get_sdev(dev);
+	if (rec == NULL)
+		return;
+	if (rec->oper.reset) {
+		rec->oper.reset(&info->arg);
+	} else {
+		snd_seq_event_t ev;
+		memset(&ev, 0, sizeof(ev));
+		snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,
+				      info->arg.addr.port);
+		ev.type = SNDRV_SEQ_EVENT_RESET;
+		snd_seq_oss_dispatch(dp, &ev, 0, 0);
+	}
+	snd_use_lock_free(&rec->use_lock);
+}
+
+
+/*
+ * load a patch record:
+ * call load_patch callback function
+ */
+int
+snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt,
+			    const char *buf, int p, int c)
+{
+	seq_oss_synth_t *rec;
+	int rc;
+
+	if (dev < 0 || dev >= dp->max_synthdev)
+		return -ENXIO;
+
+	if (is_midi_dev(dp, dev))
+		return 0;
+	if ((rec = get_synthdev(dp, dev)) == NULL)
+		return -ENXIO;
+
+	if (rec->oper.load_patch == NULL)
+		rc = -ENXIO;
+	else
+		rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
+	snd_use_lock_free(&rec->use_lock);
+	return rc;
+}
+
+/*
+ * check if the device is valid synth device
+ */
+int
+snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_synth_t *rec;
+	rec = get_synthdev(dp, dev);
+	if (rec) {
+		snd_use_lock_free(&rec->use_lock);
+		return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * receive OSS 6 byte sysex packet:
+ * the full sysex message will be sent if it reaches to the end of data
+ * (0xff).
+ */
+int
+snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev)
+{
+	int i, send;
+	unsigned char *dest;
+	seq_oss_synth_sysex_t *sysex;
+
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -ENXIO;
+
+	sysex = dp->synths[dev].sysex;
+	if (sysex == NULL) {
+		sysex = snd_kcalloc(sizeof(*sysex), GFP_KERNEL);
+		if (sysex == NULL)
+			return -ENOMEM;
+		dp->synths[dev].sysex = sysex;
+	}
+
+	send = 0;
+	dest = sysex->buf + sysex->len;
+	/* copy 6 byte packet to the buffer */
+	for (i = 0; i < 6; i++) {
+		if (buf[i] == 0xff) {
+			send = 1;
+			break;
+		}
+		dest[i] = buf[i];
+		sysex->len++;
+		if (sysex->len >= MAX_SYSEX_BUFLEN) {
+			sysex->len = 0;
+			sysex->skip = 1;
+			break;
+		}
+	}
+
+	if (sysex->len && send) {
+		if (sysex->skip) {
+			sysex->skip = 0;
+			sysex->len = 0;
+			return -EINVAL; /* skip */
+		}
+		/* copy the data to event record and send it */
+		ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+		if (snd_seq_oss_synth_addr(dp, dev, ev))
+			return -EINVAL;
+		ev->data.ext.len = sysex->len;
+		ev->data.ext.ptr = sysex->buf;
+		sysex->len = 0;
+		return 0;
+	}
+
+	return -EINVAL; /* skip */
+}
+
+/*
+ * fill the event source/destination addresses
+ */
+int
+snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -EINVAL;
+	snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
+			      dp->synths[dev].arg.addr.port);
+	return 0;
+}
+
+
+/*
+ * OSS compatible ioctl
+ */
+int
+snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr)
+{
+	seq_oss_synth_t *rec;
+	int rc;
+
+	if (is_midi_dev(dp, dev))
+		return -ENXIO;
+	if ((rec = get_synthdev(dp, dev)) == NULL)
+		return -ENXIO;
+	if (rec->oper.ioctl == NULL)
+		rc = -ENXIO;
+	else
+		rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
+	snd_use_lock_free(&rec->use_lock);
+	return rc;
+}
+
+
+/*
+ * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME
+ */
+int
+snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
+		return -ENXIO;
+	ev->type = SNDRV_SEQ_EVENT_OSS;
+	memcpy(ev->data.raw8.d, data, 8);
+	return snd_seq_oss_synth_addr(dp, dev, ev);
+}
+
+
+/*
+ * create OSS compatible synth_info record
+ */
+int
+snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf)
+{
+	seq_oss_synth_t *rec;
+
+	if (dp->synths[dev].is_midi) {
+		struct midi_info minf;
+		snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
+		inf->synth_type = SYNTH_TYPE_MIDI;
+		inf->synth_subtype = 0;
+		inf->nr_voices = 16;
+		inf->device = dev;
+		strncpy(inf->name, minf.name, sizeof(inf->name));
+	} else {
+		if ((rec = get_synthdev(dp, dev)) == NULL)
+			return -ENXIO;
+		inf->synth_type = rec->synth_type;
+		inf->synth_subtype = rec->synth_subtype;
+		inf->nr_voices = rec->nr_voices;
+		inf->device = dev;
+		strncpy(inf->name, rec->name, sizeof(inf->name));
+		snd_use_lock_free(&rec->use_lock);
+	}
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+void
+snd_seq_oss_synth_info_read(snd_info_buffer_t *buf)
+{
+	int i;
+	seq_oss_synth_t *rec;
+
+	snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
+	for (i = 0; i < max_synth_devs; i++) {
+		snd_iprintf(buf, "\nsynth %d: ", i);
+		rec = get_sdev(i);
+		if (rec == NULL) {
+			snd_iprintf(buf, "*empty*\n");
+			continue;
+		}
+		snd_iprintf(buf, "[%s]\n", rec->name);
+		snd_iprintf(buf, "  type 0x%x : subtype 0x%x : voices %d\n",
+			    rec->synth_type, rec->synth_subtype,
+			    rec->nr_voices);
+		snd_iprintf(buf, "  capabilities : ioctl %s / load_patch %s\n",
+			    enabled_str((long)rec->oper.ioctl),
+			    enabled_str((long)rec->oper.load_patch));
+		snd_use_lock_free(&rec->use_lock);
+	}
+}
+
diff -Nru linux/sound/core/seq/oss/seq_oss_synth.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.h
--- linux/sound/core/seq/oss/seq_oss_synth.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,49 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * synth device information
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_SYNTH_H
+#define __SEQ_OSS_SYNTH_H
+
+#include "seq_oss_device.h"
+#include <sound/seq_oss_legacy.h>
+#include <sound/seq_device.h>
+
+typedef struct seq_oss_synth_t seq_oss_synth_t;
+
+void snd_seq_oss_synth_init(void);
+int snd_seq_oss_synth_register(snd_seq_device_t *dev);
+int snd_seq_oss_synth_unregister(snd_seq_device_t *dev);
+void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp);
+void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp);
+void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp);
+
+void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char *buf, int p, int c);
+int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev);
+int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev);
+int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr);
+int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev);
+
+int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf);
+
+#endif
diff -Nru linux/sound/core/seq/oss/seq_oss_timer.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.c
--- linux/sound/core/seq/oss/seq_oss_timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,284 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Timer control routines
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <sound/seq_oss_legacy.h>
+
+/*
+ */
+#define MIN_OSS_TEMPO		8
+#define MAX_OSS_TEMPO		360
+#define MIN_OSS_TIMEBASE	1
+#define MAX_OSS_TIMEBASE	1000
+
+/*
+ */
+static void calc_alsa_tempo(seq_oss_timer_t *timer);
+static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value);
+
+
+/*
+ * create and register a new timer.
+ * if queue is not started yet, start it.
+ */
+seq_oss_timer_t *
+snd_seq_oss_timer_new(seq_oss_devinfo_t *dp)
+{
+	seq_oss_timer_t *rec;
+
+	rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL);
+	if (rec == NULL)
+		return NULL;
+
+	rec->dp = dp;
+	rec->cur_tick = 0;
+	rec->realtime = 0;
+	rec->running = 0;
+	rec->oss_tempo = 60;
+	rec->oss_timebase = 100;
+	calc_alsa_tempo(rec);
+
+	return rec;
+}
+
+
+/*
+ * delete timer.
+ * if no more timer exists, stop the queue.
+ */
+void
+snd_seq_oss_timer_delete(seq_oss_timer_t *rec)
+{
+	if (rec) {
+		snd_seq_oss_timer_stop(rec);
+		kfree(rec);
+	}
+}
+
+
+/*
+ * process one timing event
+ * return 1 : event proceseed -- skip this event
+ *        0 : not a timer event -- enqueue this event
+ */
+int
+snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev)
+{
+	abstime_t parm = ev->t.time;
+
+	if (ev->t.code == EV_TIMING) {
+		switch (ev->t.cmd) {
+		case TMR_WAIT_REL:
+			parm += rec->cur_tick;
+			rec->realtime = 0;
+			/* continue to next */
+		case TMR_WAIT_ABS:
+			if (parm == 0) {
+				rec->realtime = 1;
+			} else if (parm >= rec->cur_tick) {
+				rec->realtime = 0;
+				rec->cur_tick = parm;
+			}
+			return 1;	/* skip this event */
+			
+		case TMR_START:
+			snd_seq_oss_timer_start(rec);
+			return 1;
+
+		}
+	} else if (ev->s.code == SEQ_WAIT) {
+		/* time = from 1 to 3 bytes */
+		parm = (ev->echo >> 8) & 0xffffff;
+		if (parm > rec->cur_tick) {
+			/* set next event time */
+			rec->cur_tick = parm;
+			rec->realtime = 0;
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * convert tempo units
+ */
+static void
+calc_alsa_tempo(seq_oss_timer_t *timer)
+{
+	timer->tempo = (60 * 1000000) / timer->oss_tempo;
+	timer->ppq = timer->oss_timebase;
+}
+
+
+/*
+ * dispatch a timer event
+ */
+static int
+send_timer_event(seq_oss_devinfo_t *dp, int type, int value)
+{
+	snd_seq_event_t ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = type;
+	ev.source.client = dp->cseq;
+	ev.source.port = 0;
+	ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	ev.queue = dp->queue;
+	ev.data.queue.queue = dp->queue;
+	ev.data.queue.param.value = value;
+	return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 0, 0);
+}
+
+/*
+ * set queue tempo and start queue
+ */
+int
+snd_seq_oss_timer_start(seq_oss_timer_t *timer)
+{
+	seq_oss_devinfo_t *dp = timer->dp;
+	snd_seq_queue_tempo_t tmprec;
+
+	if (timer->running)
+		snd_seq_oss_timer_stop(timer);
+
+	memset(&tmprec, 0, sizeof(tmprec));
+	tmprec.queue = dp->queue;
+	tmprec.ppq = timer->ppq;
+	tmprec.tempo = timer->tempo;
+	snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, &tmprec);
+
+	send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
+	timer->running = 1;
+	timer->cur_tick = 0;
+	return 0;
+}
+
+
+/*
+ * stop queue
+ */
+int
+snd_seq_oss_timer_stop(seq_oss_timer_t *timer)
+{
+	if (! timer->running)
+		return 0;
+	send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
+	timer->running = 0;
+	return 0;
+}
+
+
+/*
+ * continue queue
+ */
+int
+snd_seq_oss_timer_continue(seq_oss_timer_t *timer)
+{
+	if (timer->running)
+		return 0;
+	send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
+	timer->running = 1;
+	return 0;
+}
+
+
+/*
+ * change queue tempo
+ */
+int
+snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value)
+{
+	if (value < MIN_OSS_TEMPO)
+		value = MIN_OSS_TEMPO;
+	else if (value > MAX_OSS_TEMPO)
+		value = MAX_OSS_TEMPO;
+	timer->oss_tempo = value;
+	calc_alsa_tempo(timer);
+	if (timer->running)
+		send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
+	return 0;
+}
+
+
+/*
+ * ioctls
+ */
+int
+snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg)
+{
+	int value;
+
+	if (cmd == SNDCTL_SEQ_CTRLRATE) {
+		debug_printk(("ctrl rate\n"));
+		/* if *arg == 0, just return the current rate */
+		if (get_user(value, (int *)arg))
+			return -EFAULT;
+		if (value)
+			return -EINVAL;
+		value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
+		return put_user(value, (int *)arg) ? -EFAULT : 0;
+	}
+
+	if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
+		return 0;
+
+	switch (cmd) {
+	case SNDCTL_TMR_START:
+		debug_printk(("timer start\n"));
+		return snd_seq_oss_timer_start(timer);
+	case SNDCTL_TMR_STOP:
+		debug_printk(("timer stop\n"));
+		return snd_seq_oss_timer_stop(timer);
+	case SNDCTL_TMR_CONTINUE:
+		debug_printk(("timer continue\n"));
+		return snd_seq_oss_timer_continue(timer);
+	case SNDCTL_TMR_TEMPO:
+		debug_printk(("timer tempo\n"));
+		if (get_user(value, (int *)arg))
+			return -EFAULT;
+		return snd_seq_oss_timer_tempo(timer, value);
+	case SNDCTL_TMR_TIMEBASE:
+		debug_printk(("timer timebase\n"));
+		if (get_user(value, (int *)arg))
+			return -EFAULT;
+		if (value < MIN_OSS_TIMEBASE)
+			value = MIN_OSS_TIMEBASE;
+		else if (value > MAX_OSS_TIMEBASE)
+			value = MAX_OSS_TIMEBASE;
+		timer->oss_timebase = value;
+		calc_alsa_tempo(timer);
+		return 0;
+
+	case SNDCTL_TMR_METRONOME:
+	case SNDCTL_TMR_SELECT:
+	case SNDCTL_TMR_SOURCE:
+		debug_printk(("timer XXX\n"));
+		/* not supported */
+		return 0;
+	}
+	return 0;
+}
diff -Nru linux/sound/core/seq/oss/seq_oss_timer.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.h
--- linux/sound/core/seq/oss/seq_oss_timer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,70 @@
+/*
+ * OSS compatible sequencer driver
+ * timer handling routines
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_TIMER_H
+#define __SEQ_OSS_TIMER_H
+
+#include "seq_oss_device.h"
+
+/*
+ * timer information definition
+ */
+struct seq_oss_timer_t {
+	seq_oss_devinfo_t *dp;
+	reltime_t cur_tick;
+	int realtime;
+	int running;
+	int tempo, ppq;	/* ALSA queue */
+	int oss_tempo, oss_timebase;
+};	
+
+
+seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp);
+void snd_seq_oss_timer_delete(seq_oss_timer_t *dp);
+
+int snd_seq_oss_timer_start(seq_oss_timer_t *timer);
+int snd_seq_oss_timer_stop(seq_oss_timer_t *timer);
+int snd_seq_oss_timer_continue(seq_oss_timer_t *timer);
+int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value);
+#define snd_seq_oss_timer_reset  snd_seq_oss_timer_start
+
+int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg);
+
+/*
+ * get current processed time
+ */
+static inline abstime_t
+snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer)
+{
+	return timer->cur_tick;
+}
+
+
+/*
+ * is realtime event?
+ */
+static inline int
+snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer)
+{
+	return timer->realtime;
+}
+
+#endif
diff -Nru linux/sound/core/seq/oss/seq_oss_writeq.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.c
--- linux/sound/core/seq/oss/seq_oss_writeq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,183 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * seq_oss_writeq.c - write queue and sync
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#define __NO_VERSION__
+#include "seq_oss_writeq.h"
+#include "seq_oss_event.h"
+#include "seq_oss_timer.h"
+#include <sound/seq_oss_legacy.h>
+#include "../seq_lock.h"
+#include "../seq_clientmgr.h"
+
+
+/*
+ * create a write queue record
+ */
+seq_oss_writeq_t *
+snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen)
+{
+	seq_oss_writeq_t *q;
+	snd_seq_client_pool_t pool;
+
+	if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL)
+		return NULL;
+	q->dp = dp;
+	q->maxlen = maxlen;
+	spin_lock_init(&q->sync_lock);
+	q->sync_event_put = 0;
+	q->sync_time = 0;
+	init_waitqueue_head(&q->sync_sleep);
+
+	memset(&pool, 0, sizeof(pool));
+	pool.client = dp->cseq;
+	pool.output_pool = maxlen;
+	pool.output_room = maxlen / 2;
+
+	snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
+
+	return q;
+}
+
+/*
+ * delete the write queue
+ */
+void
+snd_seq_oss_writeq_delete(seq_oss_writeq_t *q)
+{
+	snd_seq_oss_writeq_clear(q);	/* to be sure */
+	kfree(q);
+}
+
+
+/*
+ * reset the write queue
+ */
+void
+snd_seq_oss_writeq_clear(seq_oss_writeq_t *q)
+{
+	snd_seq_remove_events_t reset;
+
+	memset(&reset, 0, sizeof(reset));
+	reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
+
+	/* wake up sleepers if any */
+	snd_seq_oss_writeq_wakeup(q, 0);
+}
+
+/*
+ * wait until the write buffer has enough room
+ */
+int
+snd_seq_oss_writeq_sync(seq_oss_writeq_t *q)
+{
+	seq_oss_devinfo_t *dp = q->dp;
+	abstime_t time;
+	unsigned long flags;
+
+	time = snd_seq_oss_timer_cur_tick(dp->timer);
+	if (q->sync_time >= time)
+		return 0; /* already finished */
+
+	if (! q->sync_event_put) {
+		snd_seq_event_t ev;
+		evrec_t *rec;
+
+		/* put echoback event */
+		memset(&ev, 0, sizeof(ev));
+		ev.flags = 0;
+		ev.type = SNDRV_SEQ_EVENT_ECHO;
+		ev.time.tick = time;
+		/* echo back to itself */
+		snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
+		rec = (evrec_t*)&ev.data;
+		rec->t.code = SEQ_SYNCTIMER;
+		rec->t.time = time;
+		q->sync_event_put = 1;
+		snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
+	}
+
+	spin_lock_irqsave(&q->sync_lock, flags);
+	if (! q->sync_event_put) { /* echoback event has been received */
+		spin_unlock_irqrestore(&q->sync_lock, flags);
+		return 0;
+	}
+		
+	/* wait for echo event */
+	snd_seq_sleep_timeout_in_lock(&q->sync_sleep, &q->sync_lock, HZ);
+	if (signal_pending(current)) {
+		/* interrupted - return 0 to finish sync */
+		q->sync_event_put = 0;
+		spin_unlock_irqrestore(&q->sync_lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&q->sync_lock, flags);
+	if (q->sync_time >= time)
+		return 0;
+	else
+		return 1;
+}
+
+/*
+ * wake up sync - echo event was catched
+ */
+void
+snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->sync_lock, flags);
+	q->sync_time = time;
+	q->sync_event_put = 0;
+	if (waitqueue_active(&q->sync_sleep)) {
+		wake_up(&q->sync_sleep);
+	}
+	spin_unlock_irqrestore(&q->sync_lock, flags);
+}
+
+
+/*
+ * return the unused pool size
+ */
+int
+snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q)
+{
+	snd_seq_client_pool_t pool;
+	pool.client = q->dp->cseq;
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
+	return pool.output_free;
+}
+
+
+/*
+ * set output threshold size from ioctl
+ */
+void
+snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val)
+{
+	snd_seq_client_pool_t pool;
+	pool.client = q->dp->cseq;
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
+	pool.output_room = val;
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
+}
+
diff -Nru linux/sound/core/seq/oss/seq_oss_writeq.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.h
--- linux/sound/core/seq/oss/seq_oss_writeq.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,50 @@
+/*
+ * OSS compatible sequencer driver
+ * write priority queue
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ */
+
+#ifndef __SEQ_OSS_WRITEQ_H
+#define __SEQ_OSS_WRITEQ_H
+
+#include "seq_oss_device.h"
+
+
+struct seq_oss_writeq_t {
+	seq_oss_devinfo_t *dp;
+	int maxlen;
+	abstime_t sync_time;
+	int sync_event_put;
+	wait_queue_head_t sync_sleep;
+	spinlock_t sync_lock;
+};
+
+
+/*
+ * seq_oss_writeq.c
+ */
+seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen);
+void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q);
+void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q);
+int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q);
+void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time);
+int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q);
+void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size);
+
+
+#endif
diff -Nru linux/sound/core/seq/seq.c linux-2.4.19-pre5-mjc/sound/core/seq/seq.c
--- linux/sound/core/seq/seq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,141 @@
+/*
+ *  ALSA sequencer main module
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_clientmgr.h"
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_lock.h"
+#include "seq_timer.h"
+#include "seq_system.h"
+#include "seq_info.h"
+#include <sound/seq_device.h>
+
+int snd_seq_client_load[64] = {[0 ... 63] = -1};
+int snd_seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
+int snd_seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
+int snd_seq_default_timer_card = -1;
+int snd_seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM;
+int snd_seq_default_timer_subdevice = 0;
+int snd_seq_default_timer_resolution = 0;	/* Hz */
+
+MODULE_AUTHOR("Frank van de Pol <fvdpol@home.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+MODULE_PARM(snd_seq_client_load, "i");
+MODULE_PARM_DESC(snd_seq_client_load, "The numbers of global (system) clients to load through kmod.");
+MODULE_PARM(snd_seq_default_timer_class, "i");
+MODULE_PARM_DESC(snd_seq_default_timer_class, "The default timer class.");
+MODULE_PARM(snd_seq_default_timer_sclass, "i");
+MODULE_PARM_DESC(snd_seq_default_timer_sclass, "The default timer slave class.");
+MODULE_PARM(snd_seq_default_timer_card, "i");
+MODULE_PARM_DESC(snd_seq_default_timer_card, "The default timer card number.");
+MODULE_PARM(snd_seq_default_timer_device, "i");
+MODULE_PARM_DESC(snd_seq_default_timer_device, "The default timer device number.");
+MODULE_PARM(snd_seq_default_timer_subdevice, "i");
+MODULE_PARM_DESC(snd_seq_default_timer_subdevice, "The default timer subdevice number.");
+MODULE_PARM(snd_seq_default_timer_resolution, "i");
+MODULE_PARM_DESC(snd_seq_default_timer_resolution, "The default timer resolution in Hz.");
+
+/*
+ *  INIT PART
+ */
+
+
+static int __init alsa_seq_init(void)
+{
+	int err;
+
+	if ((err = client_init_data()) < 0)
+		return err;
+
+	/* init memory, room for selected events */
+	if ((err = snd_sequencer_memory_init()) < 0)
+		return err;
+
+	/* init event queues */
+	if ((err = snd_seq_queues_init()) < 0)
+		return err;
+
+	/* register sequencer device */
+	if ((err = snd_sequencer_device_init()) < 0)
+		return err;
+
+	/* register proc interface */
+	if ((err = snd_seq_info_init()) < 0)
+		return err;
+
+	/* register our internal client */
+	if ((err = snd_seq_system_client_init()) < 0)
+		return err;
+
+	return 0;
+}
+
+static void __exit alsa_seq_exit(void)
+{
+	/* unregister our internal client */
+	snd_seq_system_client_done();
+
+	/* unregister proc interface */
+	snd_seq_info_done();
+	
+	/* delete timing queues */
+	snd_seq_queues_delete();
+
+	/* unregister sequencer device */
+	snd_sequencer_device_done();
+
+	/* release event memory */
+	snd_sequencer_memory_done();
+}
+
+module_init(alsa_seq_init)
+module_exit(alsa_seq_exit)
+
+  /* seq_clientmgr.c */
+EXPORT_SYMBOL(snd_seq_create_kernel_client);
+EXPORT_SYMBOL(snd_seq_delete_kernel_client);
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
+EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
+EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
+EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
+  /* seq_memory.c */
+EXPORT_SYMBOL(snd_seq_expand_var_event);
+EXPORT_SYMBOL(snd_seq_dump_var_event);
+  /* seq_ports.c */
+EXPORT_SYMBOL(snd_seq_event_port_attach);
+EXPORT_SYMBOL(snd_seq_event_port_detach);
+  /* seq_lock.c */
+#if defined(__SMP__) || defined(CONFIG_SND_DEBUG)
+EXPORT_SYMBOL(snd_seq_sleep_in_lock);
+EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);
+EXPORT_SYMBOL(snd_use_lock_sync_helper);
+#endif
diff -Nru linux/sound/core/seq/seq_clientmgr.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.c
--- linux/sound/core/seq/seq_clientmgr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2470 @@
+/*
+ *  ALSA sequencer Client Manager
+ *  Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@home.nl>
+ *                             Jaroslav Kysela <perex@suse.cz>
+ *                             Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <linux/kmod.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_clientmgr.h"
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+#include "seq_system.h"
+#include <sound/seq_device.h>
+
+/* Client Manager
+
+ * this module handles the connections of userland and kernel clients
+ * 
+ */
+
+#define SNDRV_SEQ_LFLG_INPUT	0x0001
+#define SNDRV_SEQ_LFLG_OUTPUT	0x0002
+#define SNDRV_SEQ_LFLG_OPEN	(SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT)
+
+static spinlock_t clients_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(register_mutex);
+
+/*
+ * client table
+ */
+static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];
+static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS];
+static usage_t client_usage = {0, 0};
+
+/*
+ * prototypes
+ */
+static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop);
+static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop);
+
+/*
+ */
+ 
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+
+/*
+ */
+static inline unsigned short snd_seq_file_flags(struct file *file)
+{
+        switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
+        case FMODE_WRITE:
+                return SNDRV_SEQ_LFLG_OUTPUT;
+        case FMODE_READ:
+                return SNDRV_SEQ_LFLG_INPUT;
+        default:
+                return SNDRV_SEQ_LFLG_OPEN;
+        }
+}
+
+static inline int snd_seq_write_pool_allocated(client_t *client)
+{
+	return snd_seq_total_cells(client->pool) > 0;
+}
+
+/* return pointer to client structure for specified id */
+static client_t *clientptr(int clientid)
+{
+	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
+		snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);
+		return NULL;
+	}
+	return clienttab[clientid];
+}
+
+extern int snd_seq_client_load[];
+
+client_t *snd_seq_client_use_ptr(int clientid)
+{
+	unsigned long flags;
+	client_t *client;
+
+	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
+		snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);
+		return NULL;
+	}
+	spin_lock_irqsave(&clients_lock, flags);
+	client = clientptr(clientid);
+	if (client)
+		goto __lock;
+	if (clienttablock[clientid]) {
+		spin_unlock_irqrestore(&clients_lock, flags);
+		return NULL;
+	}
+	spin_unlock_irqrestore(&clients_lock, flags);
+#ifdef CONFIG_KMOD
+	if (!in_interrupt()) {
+		if (clientid < 64) {
+			int idx;
+			char name[32];
+			
+			for (idx = 0; idx < 64; idx++) {
+				if (snd_seq_client_load[idx] < 0)
+					break;
+				if (snd_seq_client_load[idx] == clientid) {
+					sprintf(name, "snd-seq-client-%i", clientid);
+					request_module(name);
+					break;
+				}
+			}
+		} else if (clientid >= 64 && clientid < 128) {
+			int card = (clientid - 64) / 8;
+			if (card < snd_ecards_limit) {
+#ifndef MODULE
+				if (current->fs->root) {
+#endif
+					snd_request_card(card);
+					snd_seq_device_load_drivers();
+#ifndef MODULE
+				}
+#endif
+			}
+		}
+		spin_lock_irqsave(&clients_lock, flags);
+		client = clientptr(clientid);
+		if (client)
+			goto __lock;
+		spin_unlock_irqrestore(&clients_lock, flags);
+	}
+#endif
+	return NULL;
+
+      __lock:
+	snd_use_lock_use(&client->use_lock);
+	spin_unlock_irqrestore(&clients_lock, flags);
+	return client;
+}
+
+static void usage_alloc(usage_t * res, int num)
+{
+	res->cur += num;
+	if (res->cur > res->peak)
+		res->peak = res->cur;
+}
+
+static void usage_free(usage_t * res, int num)
+{
+	res->cur -= num;
+}
+
+/* initialise data structures */
+int __init client_init_data(void)
+{
+	/* zap out the client table */
+	memset(&clienttablock, 0, sizeof(clienttablock));
+	memset(&clienttab, 0, sizeof(clienttab));
+	return 0;
+}
+
+
+static client_t *seq_create_client1(int client_index, int poolsize)
+{
+	unsigned long flags;
+	int c;
+	client_t *client;
+
+	/* init client data */
+	client = snd_kcalloc(sizeof(client_t), GFP_KERNEL);
+	if (client == NULL)
+		return NULL;
+	client->pool = snd_seq_pool_new(poolsize);
+	if (client->pool == NULL) {
+		kfree(client);
+		return NULL;
+	}
+	client->type = NO_CLIENT;
+	snd_use_lock_init(&client->use_lock);
+	rwlock_init(&client->ports_lock);
+	init_MUTEX(&client->ports_mutex);
+	INIT_LIST_HEAD(&client->ports_list_head);
+
+	/* find free slot in the client table */
+	spin_lock_irqsave(&clients_lock, flags);
+	if (client_index < 0) {
+		for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
+			if (clienttab[c] || clienttablock[c])
+				continue;
+			clienttab[client->number = c] = client;
+			spin_unlock_irqrestore(&clients_lock, flags);
+			return client;
+		}
+	} else {
+		if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
+			clienttab[client->number = client_index] = client;
+			spin_unlock_irqrestore(&clients_lock, flags);
+			return client;
+		}
+	}
+	spin_unlock_irqrestore(&clients_lock, flags);
+	snd_seq_pool_delete(&client->pool);
+	kfree(client);
+	return NULL;	/* no free slot found or busy, return failure code */
+}
+
+
+static int seq_free_client1(client_t *client)
+{
+	unsigned long flags;
+
+	snd_assert(client != NULL, return -EINVAL);
+	snd_seq_delete_all_ports(client);
+	snd_seq_queue_client_leave(client->number);
+	spin_lock_irqsave(&clients_lock, flags);
+	clienttablock[client->number] = 1;
+	clienttab[client->number] = NULL;
+	spin_unlock_irqrestore(&clients_lock, flags);
+	snd_use_lock_sync(&client->use_lock);
+	snd_seq_queue_client_termination(client->number);
+	if (client->pool)
+		snd_seq_pool_delete(&client->pool);
+	spin_lock_irqsave(&clients_lock, flags);
+	clienttablock[client->number] = 0;
+	spin_unlock_irqrestore(&clients_lock, flags);
+	return 0;
+}
+
+
+static void seq_free_client(client_t * client)
+{
+	down(&register_mutex);
+	switch (client->type) {
+	case NO_CLIENT:
+		snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number);
+		break;
+	case USER_CLIENT:
+	case KERNEL_CLIENT:
+		seq_free_client1(client);
+		usage_free(&client_usage, 1);
+		break;
+
+	default:
+		snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type);
+	}
+	up(&register_mutex);
+
+	snd_seq_system_client_ev_client_exit(client->number);
+}
+
+
+
+/* -------------------------------------------------------- */
+
+/* create a user client */
+static int snd_seq_open(struct inode *inode, struct file *file)
+{
+	int c, mode;			/* client id */
+	client_t *client;
+	user_client_t *user;
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+	client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
+	if (client == NULL) {
+		up(&register_mutex);
+		return -ENOMEM;	/* failure code */
+	}
+
+	mode = snd_seq_file_flags(file);
+	if (mode & SNDRV_SEQ_LFLG_INPUT)
+		client->accept_input = 1;
+	if (mode & SNDRV_SEQ_LFLG_OUTPUT)
+		client->accept_output = 1;
+
+	user = &client->data.user;
+	user->fifo = NULL;
+	user->fifo_pool_size = 0;
+
+	if (mode & SNDRV_SEQ_LFLG_INPUT) {
+		user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
+		user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
+		if (user->fifo == NULL) {
+			seq_free_client1(client);
+			kfree(client);
+			up(&register_mutex);
+			return -ENOMEM;
+		}
+	}
+
+	usage_alloc(&client_usage, 1);
+	client->type = USER_CLIENT;
+	up(&register_mutex);
+
+	c = client->number;
+	(user_client_t *) file->private_data = client;
+
+	/* fill client data */
+	user->file = file;
+	sprintf(client->name, "Client-%d", c);
+
+	/* make others aware this new client */
+	snd_seq_system_client_ev_client_start(c);
+
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+
+	return 0;
+}
+
+/* delete a user client */
+static int snd_seq_release(struct inode *inode, struct file *file)
+{
+	client_t *client = (client_t *) file->private_data;
+
+	if (client) {
+		seq_free_client(client);
+		if (client->data.user.fifo)
+			snd_seq_fifo_delete(&client->data.user.fifo);
+		kfree(client);
+	}
+
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return 0;
+}
+
+
+/* handle client read() */
+/* possible error values:
+ *	-ENXIO	invalid client or file open mode
+ *	-ENOSPC	FIFO overflow (the flag is cleared after this error report)
+ *	-EINVAL	no enough user-space buffer to write the whole event
+ *	-EFAULT	seg. fault during copy to user space
+ */
+static ssize_t snd_seq_read(struct file *file, char *buf, size_t count, loff_t *offset)
+{
+	client_t *client = (client_t *) file->private_data;
+	fifo_t *fifo;
+	int err;
+	long result = 0;
+	snd_seq_event_cell_t *cell;
+
+	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
+		return -ENXIO;
+
+	if (verify_area(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+
+	if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
+		return -ENXIO;
+
+	if (atomic_read(&fifo->overflow) > 0) {
+		/* buffer overflow is detected */
+		snd_seq_fifo_clear(fifo);
+		/* return error code */
+		return -ENOSPC;
+	}
+
+	cell = NULL;
+	err = 0;
+	snd_seq_fifo_lock(fifo);
+
+	/* while data available in queue */
+	while (count >= sizeof(snd_seq_event_t)) {
+		int nonblock;
+
+		nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
+		if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
+			break;
+		}
+		if (snd_seq_ev_is_variable(&cell->event)) {
+			snd_seq_event_t tmpev;
+			tmpev = cell->event;
+			tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
+			if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) {
+				err = -EFAULT;
+				break;
+			}
+			count -= sizeof(snd_seq_event_t);
+			buf += sizeof(snd_seq_event_t);
+			err = snd_seq_expand_var_event(&cell->event, count, buf, 0, sizeof(snd_seq_event_t));
+			if (err < 0)
+				break;
+			result += err;
+			count -= err;
+			buf += err;
+		} else {
+			copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t));
+			count -= sizeof(snd_seq_event_t);
+			buf += sizeof(snd_seq_event_t);
+		}
+		snd_seq_cell_free(cell);
+		cell = NULL; /* to be sure */
+		result += sizeof(snd_seq_event_t);
+	}
+
+	if (err < 0) {
+		if (cell)
+			snd_seq_fifo_cell_putback(fifo, cell);
+		if (err == -EAGAIN && result > 0)
+			err = 0;
+	}
+	snd_seq_fifo_unlock(fifo);
+
+	return (err < 0) ? err : result;
+}
+
+
+/*
+ * check access permission to the port
+ */
+static int check_port_perm(client_port_t *port, unsigned int flags)
+{
+	if ((port->capability & flags) != flags)
+		return 0;
+	return flags;
+}
+
+/*
+ * check if the destination client is available, and return the pointer
+ * if filter is non-zero, client filter bitmap is tested.
+ */
+static client_t *get_event_dest_client(snd_seq_event_t *event, int filter)
+{
+	client_t *dest;
+
+	dest = snd_seq_client_use_ptr(event->dest.client);
+	if (dest == NULL)
+		return NULL;
+	if (! dest->accept_input)
+		goto __not_avail;
+	if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
+	    ! test_bit(event->type, &dest->event_filter))
+		goto __not_avail;
+	if (filter && !(dest->filter & filter))
+		goto __not_avail;
+
+	return dest; /* ok - accessible */
+__not_avail:
+	snd_seq_client_unlock(dest);
+	return NULL;
+}
+
+
+/*
+ * Return the error event.
+ *
+ * If the receiver client is a user client, the original event is
+ * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event.  If
+ * the original event is also variable length, the external data is
+ * copied after the event record. 
+ * If the receiver client is a kernel client, the original event is
+ * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra
+ * kmalloc.
+ */
+static int bounce_error_event(client_t *client, snd_seq_event_t *event,
+			      int err, int atomic, int hop)
+{
+	snd_seq_event_t bounce_ev;
+	int result;
+
+	if (client == NULL ||
+	    ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) ||
+	    ! client->accept_input)
+		return 0; /* ignored */
+
+	/* set up quoted error */
+	memset(&bounce_ev, 0, sizeof(bounce_ev));
+	bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
+	bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
+	bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	bounce_ev.dest.client = client->number;
+	bounce_ev.dest.port = event->source.port;
+	bounce_ev.data.quote.origin = event->dest;
+	bounce_ev.data.quote.event = event;
+	bounce_ev.data.quote.value = -err; /* use positive value */
+	result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
+	if (result < 0) {
+		client->event_lost++;
+		return result;
+	}
+
+	return result;
+}
+
+
+/*
+ * deliver an event to the specified destination.
+ * if filter is non-zero, client filter bitmap is tested.
+ *
+ *  RETURN VALUE: 0 : if succeeded
+ *		 <0 : error
+ */
+static int snd_seq_deliver_single_event(client_t *client,
+					snd_seq_event_t *event,
+					int filter, int atomic, int hop)
+{
+	client_t *dest = NULL;
+	client_port_t *dest_port = NULL;
+	int result = -ENOENT;
+	int direct, quoted = 0;
+
+	direct = snd_seq_ev_is_direct(event);
+
+	dest = get_event_dest_client(event, filter);
+	if (dest == NULL)
+		goto __skip;
+	dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
+	if (dest_port == NULL)
+		goto __skip;
+
+	/* check permission */
+	if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
+		result = -EPERM;
+		goto __skip;
+	}
+		
+	/* expand the quoted event */
+	if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) {
+		quoted = 1;
+		event = event->data.quote.event;
+		if (event == NULL) {
+			snd_printd("seq: quoted event is NULL\n");
+			result = 0; /* do not send bounce error */
+			goto __skip;
+		}
+	}
+
+	switch (dest->type) {
+	case USER_CLIENT:
+		if (dest->data.user.fifo)
+			result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
+		break;
+
+	case KERNEL_CLIENT:
+		if (dest_port->event_input == NULL)
+			break;
+		result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop);
+		break;
+	default:
+		break;
+	}
+
+  __skip:
+	if (dest_port)
+		snd_seq_port_unlock(dest_port);
+	if (dest)
+		snd_seq_client_unlock(dest);
+
+	if (result < 0 && !direct) {
+		if (quoted) {
+			/* return directly to the original source */
+			dest = snd_seq_client_use_ptr(event->source.client);
+			result = bounce_error_event(dest, event, result, atomic, hop);
+			snd_seq_client_unlock(dest);
+		} else {
+			result = bounce_error_event(client, event, result, atomic, hop);
+		}
+	}
+	return result;
+}
+
+
+static void snd_seq_subs_update_event_header(subscribers_t *subs, snd_seq_event_t *event)
+{
+	if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) {
+		/* convert time according to flag with subscription */
+		queue_t *q;
+		q = queueptr(subs->info.queue);
+		if (q) {
+			event->queue = subs->info.queue;
+			event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
+			if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) {
+				event->time.time = snd_seq_timer_get_cur_time(q->timer);
+				event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
+			} else {
+				event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
+				event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
+			}
+			queuefree(q);
+		}
+	}
+}
+
+/*
+ * send the event to all subscribers:
+ */
+static int deliver_to_subscribers(client_t *client,
+				  snd_seq_event_t *event,
+				  int atomic, int hop)
+{
+	subscribers_t *subs;
+	int err = 0, num_ev = 0;
+	snd_seq_event_t event_saved;
+	client_port_t *src_port;
+	struct list_head *p;
+	port_subs_info_t *grp;
+
+	src_port = snd_seq_port_use_ptr(client, event->source.port);
+	if (src_port == NULL)
+		return -EINVAL; /* invalid source port */
+	/* save original event record */
+	event_saved = *event;
+	grp = &src_port->c_src;
+	
+	/* lock list */
+	if (atomic)
+		read_lock(&grp->list_lock);
+	else
+		down_read(&grp->list_mutex);
+	list_for_each(p, &grp->list_head) {
+		subs = list_entry(p, subscribers_t, src_list);
+		event->dest = subs->info.dest;
+		snd_seq_subs_update_event_header(subs, event);
+		err = snd_seq_deliver_single_event(client, event,
+						   0, atomic, hop);
+		if (err < 0)
+			break;
+		num_ev++;
+		/* restore original event record */
+		*event = event_saved;
+	}
+	if (atomic)
+		read_unlock(&grp->list_lock);
+	else
+		up_read(&grp->list_mutex);
+	*event = event_saved; /* restore */
+	snd_seq_port_unlock(src_port);
+	return (err < 0) ? err : num_ev;
+}
+
+
+#ifdef SUPPORT_BROADCAST 
+/*
+ * broadcast to all ports:
+ */
+static int port_broadcast_event(client_t *client,
+				snd_seq_event_t *event,
+				int atomic, int hop)
+{
+	int num_ev = 0, err = 0;
+	client_t *dest_client;
+	struct list_head *p;
+
+	dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
+	if (dest_client == NULL)
+		return 0; /* no matching destination */
+
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		client_port_t *port = list_entry(p, client_port_t, list);
+		event->dest.port = port->addr.port;
+		/* pass NULL as source client to avoid error bounce */
+		err = snd_seq_deliver_single_event(NULL, event,
+						   SNDRV_SEQ_FILTER_BROADCAST,
+						   atomic, hop);
+		if (err < 0)
+			break;
+		num_ev++;
+	}
+	read_unlock(&client->ports_lock);
+	snd_seq_client_unlock(dest_client);
+	event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
+	return (err < 0) ? err : num_ev;
+}
+
+/*
+ * send the event to all clients:
+ * if destination port is also ADDRESS_BROADCAST, deliver to all ports.
+ */
+static int broadcast_event(client_t *client,
+			   snd_seq_event_t *event, int atomic, int hop)
+{
+	int err = 0, num_ev = 0;
+	int dest;
+	snd_seq_addr_t addr;
+
+	addr = event->dest; /* save */
+
+	for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) {
+		/* don't send to itself */
+		if (dest == client->number)
+			continue;
+		event->dest.client = dest;
+		event->dest.port = addr.port;
+		if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST)
+			err = port_broadcast_event(client, event, atomic, hop);
+		else
+			/* pass NULL as source client to avoid error bounce */
+			err = snd_seq_deliver_single_event(NULL, event,
+							   SNDRV_SEQ_FILTER_BROADCAST,
+							   atomic, hop);
+		if (err < 0)
+			break;
+		num_ev += err;
+	}
+	event->dest = addr; /* restore */
+	return (err < 0) ? err : num_ev;
+}
+
+
+/* multicast - not supported yet */
+static int multicast_event(client_t *client, snd_seq_event_t *event,
+			   int atomic, int hop)
+{
+	snd_printd("seq: multicast not supported yet.\n");
+	return 0; /* ignored */
+}
+#endif /* SUPPORT_BROADCAST */
+
+
+/* deliver an event to the destination port(s).
+ * if the event is to subscribers or broadcast, the event is dispatched
+ * to multiple targets.
+ *
+ * RETURN VALUE: n > 0  : the number of delivered events.
+ *               n == 0 : the event was not passed to any client.
+ *               n < 0  : error - event was not processed.
+ */
+int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event,
+			  int atomic, int hop)
+{
+	int result;
+
+	hop++;
+	if (hop >= SNDRV_SEQ_MAX_HOPS) {
+		snd_printd("too long delivery path (%d:%d->%d:%d)\n",
+			   event->source.client, event->source.port,
+			   event->dest.client, event->dest.port);
+		return -EMLINK;
+	}
+
+	if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
+	    event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
+		result = deliver_to_subscribers(client, event, atomic, hop);
+#ifdef SUPPORT_BROADCAST
+	else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST ||
+		 event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST)
+		result = broadcast_event(client, event, atomic, hop);
+	else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS)
+		result = multicast_event(client, event, atomic, hop);
+	else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST)
+		result = port_broadcast_event(client, event, atomic, hop);
+#endif
+	else
+		result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
+
+	return result;
+}
+
+/*
+ * dispatch an event cell:
+ * This function is called only from queue check routines in timer
+ * interrupts or after enqueued.
+ * The event cell shall be released or re-queued in this function.
+ *
+ * RETURN VALUE: n > 0  : the number of delivered events.
+ *		 n == 0 : the event was not passed to any client.
+ *		 n < 0  : error - event was not processed.
+ */
+int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop)
+{
+	client_t *client;
+	int result;
+
+	snd_assert(cell != NULL, return -EINVAL);
+
+	client = snd_seq_client_use_ptr(cell->event.source.client);
+	if (client == NULL) {
+		snd_seq_cell_free(cell); /* release this cell */
+		return -EINVAL;
+	}
+
+	if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
+		/* NOTE event:
+		 * the event cell is re-used as a NOTE-OFF event and
+		 * enqueued again.
+		 */
+		snd_seq_event_t tmpev, *ev;
+
+		/* reserve this event to enqueue note-off later */
+		tmpev = cell->event;
+		tmpev.type = SNDRV_SEQ_EVENT_NOTEON;
+		result = snd_seq_deliver_event(client, &tmpev, atomic, hop);
+
+		/*
+		 * This was originally a note event.  We now re-use the
+		 * cell for the note-off event.
+		 */
+
+		ev = &cell->event;
+		ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
+		ev->flags |= SNDRV_SEQ_PRIORITY_HIGH;
+
+		/* add the duration time */
+		switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+		case SNDRV_SEQ_TIME_STAMP_TICK:
+			ev->time.tick += ev->data.note.duration;
+			break;
+		case SNDRV_SEQ_TIME_STAMP_REAL:
+			/* unit for duration is ms */
+			ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000);
+			ev->time.time.tv_sec += ev->data.note.duration / 1000 +
+						ev->time.time.tv_nsec / 1000000000;
+			ev->time.time.tv_nsec %= 1000000000;
+			break;
+		}
+		ev->data.note.velocity = ev->data.note.off_velocity;
+
+		/* Now queue this cell as the note off event */
+		if (snd_seq_enqueue_event(cell, atomic, hop) < 0)
+			snd_seq_cell_free(cell); /* release this cell */
+
+	} else {
+		/* Normal events:
+		 * event cell is freed after processing the event
+		 */
+
+		result = snd_seq_deliver_event(client, &cell->event, atomic, hop);
+		snd_seq_cell_free(cell);
+	}
+
+	snd_seq_client_unlock(client);
+	return result;
+}
+
+
+/* Allocate a cell from client pool and enqueue it to queue:
+ * if pool is empty and blocking is TRUE, sleep until a new cell is
+ * available.
+ */
+static int snd_seq_client_enqueue_event(client_t *client,
+					snd_seq_event_t *event,
+					struct file *file, int blocking,
+					int atomic, int hop)
+{
+	snd_seq_event_cell_t *cell;
+	int err;
+
+	/* special queue values - force direct passing */
+	if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+		event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		event->queue = SNDRV_SEQ_QUEUE_DIRECT;
+	} else
+#ifdef SUPPORT_BROADCAST
+		if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) {
+			event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST;
+			event->queue = SNDRV_SEQ_QUEUE_DIRECT;
+		}
+#endif
+	if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+		/* check presence of source port */
+		client_port_t *src_port = snd_seq_port_use_ptr(client, event->source.port);
+		if (src_port == NULL)
+			return -EINVAL;
+		snd_seq_port_unlock(src_port);
+	}
+
+	/* direct event processing without enqueued */
+	if (snd_seq_ev_is_direct(event)) {
+		if (event->type == SNDRV_SEQ_EVENT_NOTE)
+			return -EINVAL; /* this event must be enqueued! */
+		return snd_seq_deliver_event(client, event, atomic, hop);
+	}
+
+	/* Not direct, normal queuing */
+	if (snd_seq_queue_is_used(event->queue, client->number) <= 0)
+		return -EINVAL;  /* invalid queue */
+	if (! snd_seq_write_pool_allocated(client))
+		return -ENXIO; /* queue is not allocated */
+
+	/* allocate an event cell */
+	err = snd_seq_event_dup(client->pool, event, &cell, !blocking && !atomic, file);
+	if (err < 0)
+		return err;
+
+	/* we got a cell. enqueue it. */
+	if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
+		snd_seq_cell_free(cell);
+		return err;
+	}
+
+	return 0;
+}
+
+
+/*
+ * check validity of event type and data length.
+ * return non-zero if invalid.
+ */
+static int check_event_type_and_length(snd_seq_event_t *ev)
+{
+	switch (snd_seq_ev_length_type(ev)) {
+	case SNDRV_SEQ_EVENT_LENGTH_FIXED:
+		if (snd_seq_ev_is_variable_type(ev) ||
+		    snd_seq_ev_is_varipc_type(ev))
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARIABLE:
+		if (! snd_seq_ev_is_variable_type(ev) ||
+		    (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN)
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
+		if (! snd_seq_ev_is_instr_type(ev) ||
+		    ! snd_seq_ev_is_direct(ev))
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARIPC:
+		if (! snd_seq_ev_is_varipc_type(ev))
+			return -EINVAL;
+		break;
+	}
+	return 0;
+}
+
+
+/* handle write() */
+/* possible error values:
+ *	-ENXIO	invalid client or file open mode
+ *	-ENOMEM	malloc failed
+ *	-EFAULT	seg. fault during copy from user space
+ *	-EINVAL	invalid event
+ *	-EAGAIN	no space in output pool
+ *	-EINTR	interrupts while sleep
+ *	-EMLINK	too many hops
+ *	others	depends on return value from driver callback
+ */
+static ssize_t snd_seq_write(struct file *file, const char *buf, size_t count, loff_t *offset)
+{
+	client_t *client = (client_t *) file->private_data;
+	int written = 0, len;
+	int err = -EINVAL;
+	snd_seq_event_t event;
+
+	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
+		return -ENXIO;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+		
+	if (!client->accept_output || client->pool == NULL)
+		return -ENXIO;
+
+	/* allocate the pool now if the pool is not allocated yet */ 
+	if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
+		if (snd_seq_pool_init(client->pool) < 0)
+			return -ENOMEM;
+	}
+
+	/* only process whole events */
+	while (count >= sizeof(snd_seq_event_t)) {
+		/* Read in the event header from the user */
+		len = sizeof(event);
+		if (copy_from_user(&event, buf, len)) {
+			err = -EFAULT;
+			break;
+		}
+		event.source.client = client->number;	/* fill in client number */
+		/* Check for extension data length */
+		if (check_event_type_and_length(&event)) {
+			err = -EINVAL;
+			break;
+		}
+
+		/* check for special events */
+		if (event.type == SNDRV_SEQ_EVENT_NONE)
+			goto __skip_event;
+		else if (snd_seq_ev_is_reserved(&event)) {
+			err = -EINVAL;
+			break;
+		}
+
+		if (snd_seq_ev_is_variable(&event)) {
+			int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+			if (extlen + len > count) {
+				/* back out, will get an error this time or next */
+				err = -EINVAL;
+				break;
+			}
+			/* set user space pointer */
+			event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
+			event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t);
+			len += extlen; /* increment data length */
+		}
+
+		/* ok, enqueue it */
+		err = snd_seq_client_enqueue_event(client, &event, file,
+						   !(file->f_flags & O_NONBLOCK),
+						   0, 0);
+		if (err < 0)
+			break;
+
+	__skip_event:
+		/* Update pointers and counts */
+		count -= len;
+		buf += len;
+		written += len;
+	}
+
+	return written ? written : err;
+}
+
+
+/*
+ * handle polling
+ */
+static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
+{
+	client_t *client = (client_t *) file->private_data;
+	unsigned int mask = 0;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+
+	if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
+	    client->data.user.fifo) {
+
+		/* check if data is available in the outqueue */
+		if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
+			mask |= POLLIN | POLLRDNORM;
+	}
+
+	if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
+
+		/* check if data is available in the pool */
+		if (!snd_seq_write_pool_allocated(client) ||
+		    snd_seq_pool_poll_wait(client->pool, file, wait))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+
+	return mask;
+}
+
+
+/*-----------------------------------------------------*/
+
+
+/* SYSTEM_INFO ioctl() */
+static int snd_seq_ioctl_system_info(client_t *client, unsigned long arg)
+{
+	snd_seq_system_info_t info;
+
+	memset(&info, 0, sizeof(info));
+	/* fill the info fields */
+	info.queues = SNDRV_SEQ_MAX_QUEUES;
+	info.clients = SNDRV_SEQ_MAX_CLIENTS;
+	info.ports = 256;	/* fixed limit */
+	info.channels = 256;	/* fixed limit */
+	info.cur_clients = client_usage.cur;
+	info.cur_queues = snd_seq_queue_get_cur_queues();
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* CLIENT_INFO ioctl() */
+static void get_client_info(client_t *cptr, snd_seq_client_info_t *info)
+{
+	info->client = cptr->number;
+
+	/* fill the info fields */
+	info->type = cptr->type;
+	strcpy(info->name, cptr->name);
+	info->filter = cptr->filter;
+	info->event_lost = cptr->event_lost;
+	*info->event_filter = *cptr->event_filter;
+	info->num_ports = cptr->num_ports;
+	memset(info->reserved, 0, sizeof(info->reserved));
+}
+
+static int snd_seq_ioctl_get_client_info(client_t * client, unsigned long arg)
+{
+	client_t *cptr;
+	snd_seq_client_info_t client_info;
+
+	if (copy_from_user(&client_info, (void*)arg, sizeof(client_info)))
+		return -EFAULT;
+
+	/* requested client number */
+	cptr = snd_seq_client_use_ptr(client_info.client);
+	if (cptr == NULL)
+		return -ENOENT;		/* don't change !!! */
+
+	get_client_info(cptr, &client_info);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user((void*)arg, &client_info, sizeof(client_info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* CLIENT_INFO ioctl() */
+static int snd_seq_ioctl_set_client_info(client_t * client, unsigned long arg)
+{
+	snd_seq_client_info_t client_info;
+
+	if (copy_from_user(&client_info, (void*)arg, sizeof(client_info)))
+		return -EFAULT;
+
+	/* it is not allowed to set the info fields for an another client */
+	if (client->number != client_info.client)
+		return -EPERM;
+	/* also client type must be set now */
+	if (client->type != client_info.type)
+		return -EINVAL;
+
+	/* fill the info fields */
+	if (client_info.name[0]) {
+		strncpy(client->name, client_info.name, sizeof(client->name)-1);
+		client->name[sizeof(client->name)-1] = '\0';
+	}
+	client->filter = client_info.filter;
+	client->event_lost = client_info.event_lost;
+	*client->event_filter = *client_info.event_filter;
+
+	return 0;
+}
+
+
+/* 
+ * CREATE PORT ioctl() 
+ */
+static int snd_seq_ioctl_create_port(client_t * client, unsigned long arg)
+{
+	client_port_t *port;
+	snd_seq_port_info_t info;
+	snd_seq_port_callback_t *callback;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	/* it is not allowed to create the port for an another client */
+	if (info.addr.client != client->number)
+		return -EPERM;
+
+	port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
+	if (port == NULL)
+		return -ENOMEM;
+
+	if (client->type == USER_CLIENT && info.kernel) {
+		snd_seq_delete_port(client, port->addr.port);
+		return -EINVAL;
+	}
+	if (client->type == KERNEL_CLIENT) {
+		if ((callback = info.kernel) != NULL) {
+			if (callback->owner)
+				port->owner = callback->owner;
+			port->private_data = callback->private_data;
+			port->private_free = callback->private_free;
+			port->callback_all = callback->callback_all;
+			port->event_input = callback->event_input;
+			port->c_src.open = callback->subscribe;
+			port->c_src.close = callback->unsubscribe;
+			port->c_dest.open = callback->use;
+			port->c_dest.close = callback->unuse;
+		}
+	}
+
+	info.addr = port->addr;
+
+	snd_seq_set_port_info(port, &info);
+	snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* 
+ * DELETE PORT ioctl() 
+ */
+static int snd_seq_ioctl_delete_port(client_t * client, unsigned long arg)
+{
+	snd_seq_port_info_t info;
+	int err;
+
+	/* set passed parameters */
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+	
+	/* it is not allowed to remove the port for an another client */
+	if (info.addr.client != client->number)
+		return -EPERM;
+
+	err = snd_seq_delete_port(client, info.addr.port);
+	if (err >= 0)
+		snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
+	return err;
+}
+
+
+/* 
+ * GET_PORT_INFO ioctl() (on any client) 
+ */
+static int snd_seq_ioctl_get_port_info(client_t *client, unsigned long arg)
+{
+	client_t *cptr;
+	client_port_t *port;
+	snd_seq_port_info_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+	cptr = snd_seq_client_use_ptr(info.addr.client);
+	if (cptr == NULL)
+		return -ENXIO;
+
+	port = snd_seq_port_use_ptr(cptr, info.addr.port);
+	if (port == NULL) {
+		snd_seq_client_unlock(cptr);
+		return -ENOENT;			/* don't change */
+	}
+
+	/* get port info */
+	snd_seq_get_port_info(port, &info);
+	snd_seq_port_unlock(port);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* 
+ * SET_PORT_INFO ioctl() (only ports on this/own client) 
+ */
+static int snd_seq_ioctl_set_port_info(client_t * client, unsigned long arg)
+{
+	client_port_t *port;
+	snd_seq_port_info_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.addr.client != client->number) /* only set our own ports ! */
+		return -EPERM;
+	port = snd_seq_port_use_ptr(client, info.addr.port);
+	if (port) {
+		snd_seq_set_port_info(port, &info);
+		snd_seq_port_unlock(port);
+	}
+	return 0;
+}
+
+
+/*
+ * port subscription (connection)
+ */
+#define PERM_RD		(SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
+#define PERM_WR		(SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
+
+static int check_subscription_permission(client_t *client, client_port_t *sport,
+					 client_port_t *dport,
+					 snd_seq_port_subscribe_t *subs)
+{
+	if (client->number != subs->sender.client &&
+	    client->number != subs->dest.client) {
+		/* connection by third client - check export permission */
+		if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
+			return -EPERM;
+		if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
+			return -EPERM;
+	}
+
+	/* check read permission */
+	/* if sender or receiver is the subscribing client itself,
+	 * no permission check is necessary
+	 */
+	if (client->number != subs->sender.client) {
+		if (! check_port_perm(sport, PERM_RD))
+			return -EPERM;
+	}
+	/* check write permission */
+	if (client->number != subs->dest.client) {
+		if (! check_port_perm(dport, PERM_WR))
+			return -EPERM;
+	}
+	return 0;
+}
+
+/*
+ * send an subscription notify event to user client:
+ * client must be user client.
+ */
+int snd_seq_client_notify_subscription(int client, int port,
+				       snd_seq_port_subscribe_t *info, int evtype)
+{
+	snd_seq_event_t event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = evtype;
+	event.data.connect.dest = info->dest;
+	event.data.connect.sender = info->sender;
+
+	return snd_seq_system_notify(client, port, &event);  /* non-atomic */
+}
+
+
+/* 
+ * add to port's subscription list IOCTL interface 
+ */
+static int snd_seq_ioctl_subscribe_port(client_t * client, unsigned long arg)
+{
+	int result = -EINVAL;
+	client_t *receiver = NULL, *sender = NULL;
+	client_port_t *sport = NULL, *dport = NULL;
+	snd_seq_port_subscribe_t subs;
+
+	if (copy_from_user(&subs, (void*)arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+		goto __end;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+		goto __end;
+
+	result = check_subscription_permission(client, sport, dport, &subs);
+	if (result < 0)
+		goto __end;
+
+	/* connect them */
+	result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
+	if (! result) /* broadcast announce */
+		snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
+						   &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (dport)
+		snd_seq_port_unlock(dport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (receiver)
+		snd_seq_client_unlock(receiver);
+	return result;
+}
+
+
+/* 
+ * remove from port's subscription list 
+ */
+static int snd_seq_ioctl_unsubscribe_port(client_t * client, unsigned long arg)
+{
+	int result = -ENXIO;
+	client_t *receiver = NULL, *sender = NULL;
+	client_port_t *sport = NULL, *dport = NULL;
+	snd_seq_port_subscribe_t subs;
+
+	if (copy_from_user(&subs, (void*)arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+		goto __end;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+		goto __end;
+
+	result = check_subscription_permission(client, sport, dport, &subs);
+	if (result < 0)
+		goto __end;
+
+	result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
+	if (! result) /* broadcast announce */
+		snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
+						   &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (dport)
+		snd_seq_port_unlock(dport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (receiver)
+		snd_seq_client_unlock(receiver);
+	return result;
+}
+
+
+/* CREATE_QUEUE ioctl() */
+static int snd_seq_ioctl_create_queue(client_t *client, unsigned long arg)
+{
+	snd_seq_queue_info_t info;
+	int result;
+	queue_t *q;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
+	if (result < 0)
+		return result;
+
+	q = queueptr(result);
+	if (q == NULL)
+		return -EINVAL;
+
+	info.queue = q->queue;
+	info.locked = q->locked;
+	info.owner = q->owner;
+
+	/* set queue name */
+	if (! info.name[0])
+		sprintf(info.name, "Queue-%d", q->queue);
+	strncpy(q->name, info.name, sizeof(q->name)-1);
+	q->name[sizeof(q->name)-1] = 0;
+	queuefree(q);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* DELETE_QUEUE ioctl() */
+static int snd_seq_ioctl_delete_queue(client_t *client, unsigned long arg)
+{
+	snd_seq_queue_info_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	return snd_seq_queue_delete(client->number, info.queue);
+}
+
+/* GET_QUEUE_INFO ioctl() */
+static int snd_seq_ioctl_get_queue_info(client_t *client, unsigned long arg)
+{
+	snd_seq_queue_info_t info;
+	queue_t *q;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	q = queueptr(info.queue);
+	if (q == NULL)
+		return -EINVAL;
+
+	memset(&info, 0, sizeof(info));
+	info.queue = q->queue;
+	info.owner = q->owner;
+	info.locked = q->locked;
+	strncpy(info.name, q->name, sizeof(info.name) - 1);
+	info.name[sizeof(info.name)-1] = 0;
+	queuefree(q);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* SET_QUEUE_INFO ioctl() */
+static int snd_seq_ioctl_set_queue_info(client_t *client, unsigned long arg)
+{
+	snd_seq_queue_info_t info;
+	queue_t *q;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.owner != client->number)
+		return -EINVAL;
+
+	/* change owner/locked permission */
+	if (snd_seq_queue_check_access(info.queue, client->number)) {
+		if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
+			return -EPERM;
+		if (info.locked)
+			snd_seq_queue_use(info.queue, client->number, 1);
+	} else {
+		return -EPERM;
+	}	
+
+	q = queueptr(info.queue);
+	if (! q)
+		return -EINVAL;
+	if (q->owner != client->number) {
+		queuefree(q);
+		return -EPERM;
+	}
+	strncpy(q->name, info.name, sizeof(q->name) - 1);
+	q->name[sizeof(q->name)-1] = 0;
+	queuefree(q);
+
+	return 0;
+}
+
+/* GET_NAMED_QUEUE ioctl() */
+static int snd_seq_ioctl_get_named_queue(client_t *client, unsigned long arg)
+{
+	snd_seq_queue_info_t info;
+	queue_t *q;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	q = snd_seq_queue_find_name(info.name);
+	if (q == NULL)
+		return -EINVAL;
+	info.queue = q->queue;
+	info.owner = q->owner;
+	info.locked = q->locked;
+	queuefree(q);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* GET_QUEUE_STATUS ioctl() */
+static int snd_seq_ioctl_get_queue_status(client_t * client, unsigned long arg)
+{
+	snd_seq_queue_status_t status;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	if (copy_from_user(&status, (void*)arg, sizeof(status)))
+		return -EFAULT;
+
+	queue = queueptr(status.queue);
+	if (queue == NULL)
+		return -EINVAL;
+	memset(&status, 0, sizeof(status));
+	status.queue = queue->queue;
+	
+	tmr = queue->timer;
+	status.events = queue->tickq->cells + queue->timeq->cells;
+
+	status.time = snd_seq_timer_get_cur_time(tmr);
+	status.tick = snd_seq_timer_get_cur_tick(tmr);
+
+	status.running = tmr->running;
+
+	status.flags = queue->flags;
+	queuefree(queue);
+
+	if (copy_to_user((void*)arg, &status, sizeof(status)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* GET_QUEUE_TEMPO ioctl() */
+static int snd_seq_ioctl_get_queue_tempo(client_t * client, unsigned long arg)
+{
+	snd_seq_queue_tempo_t tempo;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	if (copy_from_user(&tempo, (void*)arg, sizeof(tempo)))
+		return -EFAULT;
+
+	queue = queueptr(tempo.queue);
+	if (queue == NULL)
+		return -EINVAL;
+	memset(&tempo, 0, sizeof(tempo));
+	tempo.queue = queue->queue;
+	
+	tmr = queue->timer;
+
+	tempo.tempo = tmr->tempo;
+	tempo.ppq = tmr->ppq;
+	tempo.skew_value = tmr->skew;
+	tempo.skew_base = tmr->skew_base;
+	queuefree(queue);
+
+	if (copy_to_user((void*)arg, &tempo, sizeof(tempo)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_TEMPO ioctl() */
+static int snd_seq_ioctl_set_queue_tempo(client_t * client, unsigned long arg)
+{
+	int result;
+	snd_seq_queue_tempo_t tempo;
+
+	if (copy_from_user(&tempo, (void*)arg, sizeof(tempo)))
+		return -EFAULT;
+
+	if (snd_seq_queue_check_access(tempo.queue, client->number)) {
+		result = snd_seq_queue_timer_set_tempo(tempo.queue, client->number, &tempo);
+		if (result < 0)
+			return result;
+	} else {
+		return -EPERM;
+	}	
+
+	return 0;
+}
+
+
+/* GET_QUEUE_TIMER ioctl() */
+static int snd_seq_ioctl_get_queue_timer(client_t * client, unsigned long arg)
+{
+	snd_seq_queue_timer_t timer;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	if (copy_from_user(&timer, (void*)arg, sizeof(timer)))
+		return -EFAULT;
+
+	queue = queueptr(timer.queue);
+	if (queue == NULL)
+		return -EINVAL;
+
+	if (down_interruptible(&queue->timer_mutex)) {
+		queuefree(queue);
+		return -ERESTARTSYS;
+	}
+	tmr = queue->timer;
+	memset(&timer, 0, sizeof(timer));
+	timer.queue = queue->queue;
+
+	timer.type = tmr->type;
+	if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
+		timer.u.alsa.id = tmr->alsa_id;
+		timer.u.alsa.resolution = tmr->preferred_resolution;
+	}
+	up(&queue->timer_mutex);
+	queuefree(queue);
+	
+	if (copy_to_user((void*)arg, &timer, sizeof(timer)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_TIMER ioctl() */
+static int snd_seq_ioctl_set_queue_timer(client_t * client, unsigned long arg)
+{
+	int result = 0;
+	snd_seq_queue_timer_t timer;
+
+	if (copy_from_user(&timer, (void*)arg, sizeof(timer)))
+		return -EFAULT;
+
+	if (timer.type != SNDRV_SEQ_TIMER_ALSA)
+		return -EINVAL;
+
+	if (snd_seq_queue_check_access(timer.queue, client->number)) {
+		queue_t *q;
+		seq_timer_t *tmr;
+
+		q = queueptr(timer.queue);
+		if (q == NULL)
+			return -ENXIO;
+		if (down_interruptible(&q->timer_mutex)) {
+			queuefree(q);
+			return -ERESTARTSYS;
+		}
+		tmr = q->timer;
+		snd_seq_queue_timer_close(timer.queue);
+		tmr->type = timer.type;
+		if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
+			tmr->alsa_id = timer.u.alsa.id;
+			tmr->preferred_resolution = timer.u.alsa.resolution;
+		}
+		result = snd_seq_queue_timer_open(timer.queue);
+		up(&q->timer_mutex);
+		queuefree(q);
+	} else {
+		return -EPERM;
+	}	
+
+	return result;
+}
+
+
+/* GET_QUEUE_CLIENT ioctl() */
+static int snd_seq_ioctl_get_queue_client(client_t * client, unsigned long arg)
+{
+	snd_seq_queue_client_t info;
+	int used;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	used = snd_seq_queue_is_used(info.queue, client->number);
+	if (used < 0)
+		return -EINVAL;
+	info.used = used;
+	info.client = client->number;
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_CLIENT ioctl() */
+static int snd_seq_ioctl_set_queue_client(client_t * client, unsigned long arg)
+{
+	int err;
+	snd_seq_queue_client_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.used >= 0) {
+		err = snd_seq_queue_use(info.queue, client->number, info.used);
+		if (err < 0)
+			return err;
+	}
+
+	return snd_seq_ioctl_get_queue_client(client, arg);
+}
+
+
+/* GET_CLIENT_POOL ioctl() */
+static int snd_seq_ioctl_get_client_pool(client_t * client, unsigned long arg)
+{
+	snd_seq_client_pool_t info;
+	client_t *cptr;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	cptr = snd_seq_client_use_ptr(info.client);
+	if (cptr == NULL)
+		return -ENOENT;
+	memset(&info, 0, sizeof(info));
+	info.output_pool = cptr->pool->size;
+	info.output_room = cptr->pool->room;
+	info.output_free = info.output_pool;
+	if (cptr->pool)
+		info.output_free = snd_seq_unused_cells(cptr->pool);
+	if (cptr->type == USER_CLIENT) {
+		info.input_pool = cptr->data.user.fifo_pool_size;
+		info.input_free = info.input_pool;
+		if (cptr->data.user.fifo)
+			info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
+	} else {
+		info.input_pool = 0;
+		info.input_free = 0;
+	}
+	snd_seq_client_unlock(cptr);
+	
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* SET_CLIENT_POOL ioctl() */
+static int snd_seq_ioctl_set_client_pool(client_t * client, unsigned long arg)
+{
+	snd_seq_client_pool_t info;
+	int rc;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	if (client->number != info.client)
+		return -EINVAL; /* can't change other clients */
+
+	if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
+	    (! snd_seq_write_pool_allocated(client) ||
+	     info.output_pool != client->pool->size)) {
+		if (snd_seq_write_pool_allocated(client)) {
+			/* remove all existing cells */
+			snd_seq_queue_client_leave_cells(client->number);
+			snd_seq_pool_done(client->pool);
+		}
+		client->pool->size = info.output_pool;
+		rc = snd_seq_pool_init(client->pool);
+		if (rc < 0)
+			return rc;
+	}
+	if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
+	    info.input_pool >= 1 &&
+	    info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
+	    info.input_pool != client->data.user.fifo_pool_size) {
+		/* change pool size */
+		rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
+		if (rc < 0)
+			return rc;
+		client->data.user.fifo_pool_size = info.input_pool;
+	}
+	if (info.output_room >= 1 &&
+	    info.output_room <= client->pool->size) {
+		client->pool->room  = info.output_room;
+	}
+
+	return snd_seq_ioctl_get_client_pool(client, arg);
+}
+
+
+/* REMOVE_EVENTS ioctl() */
+static int snd_seq_ioctl_remove_events(client_t * client, unsigned long arg)
+{
+	snd_seq_remove_events_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	/*
+	 * Input mostly not implemented XXX.
+	 */
+	if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
+		/*
+		 * No restrictions so for a user client we can clear
+		 * the whole fifo
+		 */
+		if (client->type == USER_CLIENT)
+			snd_seq_fifo_clear(client->data.user.fifo);
+	}
+
+	if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
+		snd_seq_queue_remove_cells(client->number, &info);
+
+	return 0;
+}
+
+
+/*
+ * get subscription info
+ */
+static int snd_seq_ioctl_get_subscription(client_t *client, unsigned long arg)
+{
+	int result;
+	client_t *sender = NULL;
+	client_port_t *sport = NULL;
+	snd_seq_port_subscribe_t subs;
+	subscribers_t *p;
+
+	if (copy_from_user(&subs, (void*)arg, sizeof(subs)))
+		return -EFAULT;
+
+	result = -EINVAL;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
+	if (p) {
+		result = 0;
+		subs = p->info;
+	} else
+		result = -ENOENT;
+
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (result >= 0) {
+		if (copy_to_user((void*)arg, &subs, sizeof(subs)))
+			return -EFAULT;
+	}
+	return result;
+}
+
+
+/*
+ * get subscription info - check only its presence
+ */
+static int snd_seq_ioctl_query_subs(client_t *client, unsigned long arg)
+{
+	int result = -ENXIO;
+	client_t *cptr = NULL;
+	client_port_t *port = NULL;
+	snd_seq_query_subs_t subs;
+	port_subs_info_t *group;
+	struct list_head *p;
+	int i;
+
+	if (copy_from_user(&subs, (void*)arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
+		goto __end;
+	if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
+		goto __end;
+
+	switch (subs.type) {
+	case SNDRV_SEQ_QUERY_SUBS_READ:
+		group = &port->c_src;
+		break;
+	case SNDRV_SEQ_QUERY_SUBS_WRITE:
+		group = &port->c_dest;
+		break;
+	default:
+		goto __end;
+	}
+
+	down_read(&group->list_mutex);
+	/* search for the subscriber */
+	subs.num_subs = group->count;
+	i = 0;
+	result = -ENOENT;
+	list_for_each(p, &group->list_head) {
+		if (i++ == subs.index) {
+			/* found! */
+			subscribers_t *s;
+			if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
+				s = list_entry(p, subscribers_t, src_list);
+				subs.addr = s->info.dest;
+			} else {
+				s = list_entry(p, subscribers_t, dest_list);
+				subs.addr = s->info.sender;
+			}
+			subs.flags = s->info.flags;
+			subs.queue = s->info.queue;
+			result = 0;
+			break;
+		}
+	}
+	up_read(&group->list_mutex);
+
+      __end:
+   	if (port)
+		snd_seq_port_unlock(port);
+	if (cptr)
+		snd_seq_client_unlock(cptr);
+	if (result >= 0) {
+		if (copy_to_user((void*)arg, &subs, sizeof(subs)))
+			return -EFAULT;
+	}
+	return result;
+}
+
+
+/*
+ * query next client
+ */
+static int snd_seq_ioctl_query_next_client(client_t *client, unsigned long arg)
+{
+	client_t *cptr = NULL;
+	snd_seq_client_info_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+
+	/* search for next client */
+	info.client++;
+	if (info.client < 0)
+		info.client = 0;
+	for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
+		cptr = snd_seq_client_use_ptr(info.client);
+		if (cptr)
+			break; /* found */
+	}
+	if (cptr == NULL)
+		return -ENOENT;
+
+	get_client_info(cptr, &info);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* 
+ * query next port
+ */
+static int snd_seq_ioctl_query_next_port(client_t *client, unsigned long arg)
+{
+	client_t *cptr;
+	client_port_t *port = NULL;
+	snd_seq_port_info_t info;
+
+	if (copy_from_user(&info, (void*)arg, sizeof(info)))
+		return -EFAULT;
+	cptr = snd_seq_client_use_ptr(info.addr.client);
+	if (cptr == NULL)
+		return -ENXIO;
+
+	/* search for next port */
+	info.addr.port++;
+	port = snd_seq_port_query_nearest(cptr, &info);
+	if (port == NULL) {
+		snd_seq_client_unlock(cptr);
+		return -ENOENT;
+	}
+
+	/* get port info */
+	info.addr = port->addr;
+	snd_seq_get_port_info(port, &info);
+	snd_seq_port_unlock(port);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user((void*)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* -------------------------------------------------------- */
+
+static struct seq_ioctl_table {
+	unsigned int cmd;
+	int (*func)(client_t *client, unsigned long arg);
+} ioctl_tables[] = {
+	{ SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
+	{ SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
+	{ SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info },
+	{ SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port },
+	{ SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port },
+	{ SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info },
+	{ SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info },
+	{ SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port },
+	{ SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port },
+	{ SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue },
+	{ SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info },
+	{ SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client },
+	{ SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool },
+	{ SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool },
+	{ SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription },
+	{ SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client },
+	{ SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port },
+	{ SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events },
+	{ SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs },
+	{ 0, NULL },
+};
+
+static int snd_seq_do_ioctl(client_t *client, unsigned int cmd, unsigned long arg)
+{
+	struct seq_ioctl_table *p;
+
+	switch (cmd) {
+	case SNDRV_SEQ_IOCTL_PVERSION:
+		/* return sequencer version number */
+		return put_user(SNDRV_SEQ_VERSION, (int *)arg) ? -EFAULT : 0;
+	case SNDRV_SEQ_IOCTL_CLIENT_ID:
+		/* return the id of this client */
+		return put_user(client->number, (int *)arg) ? -EFAULT : 0;
+	}
+
+	if (! arg)
+		return -EFAULT;
+	for (p = ioctl_tables; p->cmd; p++) {
+		if (p->cmd == cmd)
+			return p->func(client, arg);
+	}
+	snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n",
+		   cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
+	return -ENOTTY;
+}
+
+
+static int snd_seq_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	client_t *client = (client_t *) file->private_data;
+
+	snd_assert(client != NULL, return -ENXIO);
+		
+	return snd_seq_do_ioctl(client, cmd, arg);
+}
+
+
+/* -------------------------------------------------------- */
+
+
+/* exported to kernel modules */
+int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t * callback)
+{
+	client_t *client;
+
+	snd_assert(! in_interrupt(), return -EBUSY);
+
+	if (callback == NULL)
+		return -EINVAL;
+	if (card && client_index > 7)
+		return -EINVAL;
+	if (card == NULL && client_index > 63)
+		return -EINVAL;
+	if (card)
+		client_index += 64 + (card->number << 3);
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+	/* empty write queue as default */
+	client = seq_create_client1(client_index, 0);
+	if (client == NULL) {
+		up(&register_mutex);
+		return -EBUSY;	/* failure code */
+	}
+	usage_alloc(&client_usage, 1);
+
+	client->accept_input = callback->allow_output;
+	client->accept_output = callback->allow_input;
+		
+	/* fill client data */
+	client->data.kernel.card = card;
+	client->data.kernel.private_data = callback->private_data;
+	sprintf(client->name, "Client-%d", client->number);
+
+	client->type = KERNEL_CLIENT;
+	up(&register_mutex);
+
+	/* make others aware this new client */
+	snd_seq_system_client_ev_client_start(client->number);
+	
+	/* return client number to caller */
+	return client->number;
+}
+
+/* exported to kernel modules */
+int snd_seq_delete_kernel_client(int client)
+{
+	client_t *ptr;
+
+	snd_assert(! in_interrupt(), return -EBUSY);
+
+	ptr = clientptr(client);
+	if (ptr == NULL)
+		return -EINVAL;
+
+	seq_free_client(ptr);
+	kfree(ptr);
+	return 0;
+}
+
+
+/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
+ * and snd_seq_kernel_client_enqueue_blocking
+ */
+static int kernel_client_enqueue(int client, snd_seq_event_t *ev,
+				 struct file *file, int blocking,
+				 int atomic, int hop)
+{
+	client_t *cptr;
+	int result;
+
+	snd_assert(ev != NULL, return -EINVAL);
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return 0; /* ignore this */
+	if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR ||
+	    ev->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE)
+		return -EINVAL; /* quoted events can't be enqueued */
+
+	/* fill in client number */
+	ev->source.client = client;
+
+	if (check_event_type_and_length(ev))
+		return -EINVAL;
+
+	cptr = snd_seq_client_use_ptr(client);
+	if (cptr == NULL)
+		return -EINVAL;
+	
+	if (! cptr->accept_output)
+		result = -EPERM;
+	else /* send it */
+		result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+
+	snd_seq_client_unlock(cptr);
+	return result;
+}
+
+/*
+ * exported, called by kernel clients to enqueue events (w/o blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
+ */
+int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t * ev,
+				  int atomic, int hop)
+{
+	return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
+}
+
+/*
+ * exported, called by kernel clients to enqueue events (with blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
+ */
+int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev,
+					   struct file *file,
+					   int atomic, int hop)
+{
+	return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
+}
+
+
+/* 
+ * exported, called by kernel clients to dispatch events directly to other
+ * clients, bypassing the queues.  Event time-stamp will be updated.
+ *
+ * RETURN VALUE: negative = delivery failed,
+ *		 zero, or positive: the number of delivered events
+ */
+int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t * ev,
+				   int atomic, int hop)
+{
+	client_t *cptr;
+	int result;
+
+	snd_assert(ev != NULL, return -EINVAL);
+
+	/* fill in client number */
+	ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
+	ev->source.client = client;
+
+	if (check_event_type_and_length(ev))
+		return -EINVAL;
+
+	cptr = snd_seq_client_use_ptr(client);
+	if (cptr == NULL)
+		return -EINVAL;
+
+	if (!cptr->accept_output)
+		result = -EPERM;
+	else
+		result = snd_seq_deliver_event(cptr, ev, atomic, hop);
+
+	snd_seq_client_unlock(cptr);
+	return result;
+}
+
+
+/*
+ * exported, called by kernel clients to perform same functions as with
+ * userland ioctl() 
+ */
+int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
+{
+	client_t *client;
+	mm_segment_t fs;
+	int result;
+
+	client = clientptr(clientid);
+	if (client == NULL)
+		return -ENXIO;
+	fs = snd_enter_user();
+	result = snd_seq_do_ioctl(client, cmd, (unsigned long)arg);
+	snd_leave_user(fs);
+	return result;
+}
+
+
+/* exported (for OSS emulator) */
+int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
+{
+	client_t *client;
+
+	client = clientptr(clientid);
+	if (client == NULL)
+		return -ENXIO;
+
+	if (! snd_seq_write_pool_allocated(client))
+		return 1;
+	if (snd_seq_pool_poll_wait(client->pool, file, wait))
+		return 1;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ *  /proc interface
+ */
+static void snd_seq_info_dump_subscribers(snd_info_buffer_t *buffer, port_subs_info_t *group, int is_src, char *msg)
+{
+	struct list_head *p;
+	subscribers_t *s;
+	int count = 0;
+
+	down_read(&group->list_mutex);
+	if (list_empty(&group->list_head)) {
+		up_read(&group->list_mutex);
+		return;
+	}
+	snd_iprintf(buffer, msg);
+	list_for_each(p, &group->list_head) {
+		if (is_src)
+			s = list_entry(p, subscribers_t, src_list);
+		else
+			s = list_entry(p, subscribers_t, dest_list);
+		if (count++)
+			snd_iprintf(buffer, ", ");
+		snd_iprintf(buffer, "%d:%d",
+			    is_src ? s->info.dest.client : s->info.sender.client,
+			    is_src ? s->info.dest.port : s->info.sender.port);
+		if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
+			snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue);
+		if (group->exclusive)
+			snd_iprintf(buffer, "[ex]");
+	}
+	up_read(&group->list_mutex);
+	snd_iprintf(buffer, "\n");
+}
+
+#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-')
+#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-')
+#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e')
+
+#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
+
+static void snd_seq_info_dump_ports(snd_info_buffer_t *buffer, client_t *client)
+{
+	struct list_head *l;
+
+	down(&client->ports_mutex);
+	list_for_each(l, &client->ports_list_head) {
+		client_port_t *p = list_entry(l, client_port_t, list);
+		snd_iprintf(buffer, "  Port %3d : \"%s\" (%c%c%c%c)\n",
+			    p->addr.port, p->name,
+			    FLAG_PERM_RD(p->capability),
+			    FLAG_PERM_WR(p->capability),
+			    FLAG_PERM_EX(p->capability),
+			    FLAG_PERM_DUPLEX(p->capability));
+		snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, "    Connecting To: ");
+		snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, "    Connected From: ");
+	}
+	up(&client->ports_mutex);
+}
+
+
+/* exported to seq_info.c */
+void snd_seq_info_clients_read(snd_info_entry_t *entry, 
+			       snd_info_buffer_t * buffer)
+{
+	extern void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t * pool, char *space);
+	int c;
+	client_t *client;
+
+	snd_iprintf(buffer, "Client info\n");
+	snd_iprintf(buffer, "  cur  clients : %d\n", client_usage.cur);
+	snd_iprintf(buffer, "  peak clients : %d\n", client_usage.peak);
+	snd_iprintf(buffer, "  max  clients : %d\n", SNDRV_SEQ_MAX_CLIENTS);
+	snd_iprintf(buffer, "\n");
+
+	/* list the client table */
+	for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
+		client = snd_seq_client_use_ptr(c);
+		if (client == NULL)
+			continue;
+		if (client->type == NO_CLIENT)
+			continue;
+
+		snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+			    c, client->name,
+			    client->type == USER_CLIENT ? "User" : "Kernel");
+		snd_seq_info_dump_ports(buffer, client);
+		if (snd_seq_write_pool_allocated(client)) {
+			snd_iprintf(buffer, "  Output pool :\n");
+			snd_seq_info_pool(buffer, client->pool, "    ");
+		}
+		if (client->type == USER_CLIENT && client->data.user.fifo &&
+		    client->data.user.fifo->pool) {
+			snd_iprintf(buffer, "  Input pool :\n");
+			snd_seq_info_pool(buffer, client->data.user.fifo->pool, "    ");
+		}
+		snd_seq_client_unlock(client);
+	}
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+
+/*
+ *  REGISTRATION PART
+ */
+
+static struct file_operations snd_seq_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		snd_seq_read,
+	write:		snd_seq_write,
+	open:		snd_seq_open,
+	release:	snd_seq_release,
+	poll:		snd_seq_poll,
+	ioctl:		snd_seq_ioctl,
+};
+
+static snd_minor_t snd_seq_reg =
+{
+	comment:	"sequencer",
+	f_ops:		&snd_seq_f_ops,
+};
+
+
+/* 
+ * register sequencer device 
+ */
+int __init snd_sequencer_device_init(void)
+{
+	int err;
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) {
+		up(&register_mutex);
+		return err;
+	}
+	
+	up(&register_mutex);
+
+	return 0;
+}
+
+
+
+/* 
+ * unregister sequencer device 
+ */
+void __exit snd_sequencer_device_done(void)
+{
+	snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0);
+}
diff -Nru linux/sound/core/seq/seq_clientmgr.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.h
--- linux/sound/core/seq/seq_clientmgr.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,104 @@
+/*
+ *   ALSA sequencer Client Manager
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_CLIENTMGR_H
+#define __SND_SEQ_CLIENTMGR_H
+
+#include <sound/seq_kernel.h>
+#include "seq_fifo.h"
+#include "seq_ports.h"
+#include "seq_lock.h"
+
+
+/* client manager */
+
+struct _snd_seq_user_client {
+	struct file *file;	/* file struct of client */
+	/* ... */
+	
+	/* fifo */
+	fifo_t *fifo;	/* queue for incoming events */
+	int fifo_pool_size;
+};
+
+struct _snd_seq_kernel_client {
+	snd_card_t *card;
+	/* pointer to client functions */
+	void *private_data;			/* private data for client */
+	/* ... */
+};
+
+
+struct _snd_seq_client {
+	snd_seq_client_type_t type;
+	unsigned int accept_input: 1,
+		accept_output: 1;
+	char name[64];		/* client name */
+	int number;		/* client number */
+	unsigned int filter;	/* filter flags */
+	unsigned char client_filter[32];
+	unsigned char event_filter[32];
+	snd_use_lock_t use_lock;
+	int event_lost;
+	/* ports */
+	int num_ports;		/* number of ports */
+	struct list_head ports_list_head;
+	rwlock_t ports_lock;
+	struct semaphore ports_mutex;
+
+	/* output pool */
+	pool_t *pool;		/* memory pool for this client */
+
+	union {
+		user_client_t user;
+		kernel_client_t kernel;
+	} data;
+};
+
+/* usage statistics */
+typedef struct {
+	int cur;
+	int peak;
+} usage_t;
+
+
+extern int client_init_data(void);
+extern int snd_sequencer_device_init(void);
+extern void snd_sequencer_device_done(void);
+
+/* get locked pointer to client */
+extern client_t *snd_seq_client_use_ptr(int clientid);
+
+/* unlock pointer to client */
+#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
+
+/* dispatch event to client(s) */
+extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop);
+
+/* exported to other modules */
+extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data);
+extern int snd_seq_unregister_kernel_client(int client);
+extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop);
+int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop);
+int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
+int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype);
+int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, int atomic, int hop);
+
+#endif
diff -Nru linux/sound/core/seq/seq_device.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_device.c
--- linux/sound/core/seq/seq_device.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_device.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,528 @@
+/*
+ *  ALSA sequencer device management
+ *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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 device handler seperates the card driver module from sequencer
+ * stuff (sequencer core, synth drivers, etc), so that user can avoid
+ * to spend unnecessary resources e.g. if he needs only listening to
+ * MP3s.
+ *
+ * The card (or lowlevel) driver creates a sequencer device entry
+ * via snd_seq_device_new().  This is an entry pointer to communicate
+ * with the sequencer device "driver", which is involved with the
+ * actual part to communicate with the sequencer core.
+ * Each sequencer device entry has an id string and the corresponding
+ * driver with the same id is loaded when required.  For example,
+ * lowlevel codes to access emu8000 chip on sbawe card are included in
+ * emu8000-synth module.  To activate this module, the hardware
+ * resources like i/o port are passed via snd_seq_device argument.
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/seq_device.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer device management");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+/*
+ * driver list
+ */
+typedef struct ops_list ops_list_t;
+
+/* driver state */
+#define DRIVER_EMPTY		0
+#define DRIVER_LOADED		(1<<0)
+#define DRIVER_REQUESTED	(1<<1)
+#define DRIVER_LOCKED		(1<<2)
+
+struct ops_list {
+	char id[ID_LEN];	/* driver id */
+	int driver;		/* driver state */
+	int used;		/* reference counter */
+	int argsize;		/* argument size */
+
+	/* operators */
+	snd_seq_dev_ops_t ops;
+
+	/* registred devices */
+	struct list_head dev_list;	/* list of devices */
+	int num_devices;	/* number of associated devices */
+	int num_init_devices;	/* number of initialized devices */
+	struct semaphore reg_mutex;
+
+	struct list_head list;	/* next driver */
+};
+
+
+static LIST_HEAD(opslist);
+static int num_ops = 0;
+static DECLARE_MUTEX(ops_mutex);
+static snd_info_entry_t *info_entry = NULL;
+
+/*
+ * prototypes
+ */
+static int snd_seq_device_free(snd_seq_device_t *dev);
+static int snd_seq_device_dev_free(snd_device_t *device);
+static int snd_seq_device_dev_register(snd_device_t *device);
+static int snd_seq_device_dev_unregister(snd_device_t *device);
+
+static int init_device(snd_seq_device_t *dev, ops_list_t *ops);
+static int free_device(snd_seq_device_t *dev, ops_list_t *ops);
+static ops_list_t *find_driver(char *id, int create_if_empty);
+static ops_list_t *create_driver(char *id);
+static void unlock_driver(ops_list_t *ops);
+static void remove_drivers(void);
+
+/*
+ * show all drivers and their status
+ */
+
+static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
+				ops->id,
+				ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
+				ops->driver & DRIVER_REQUESTED ? ",requested" : "",
+				ops->driver & DRIVER_LOCKED ? ",locked" : "",
+				ops->num_devices);
+	}
+	up(&ops_mutex);	
+}
+ 
+/*
+ * load all registered drivers (called from seq_clientmgr.c)
+ */
+
+void snd_seq_device_load_drivers(void)
+{
+#ifdef CONFIG_KMOD
+	struct list_head *head;
+	char modname[64];
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		if (! (ops->driver & DRIVER_LOADED) &&
+		    ! (ops->driver & DRIVER_REQUESTED)) {
+			ops->used++;
+			up(&ops_mutex);
+			ops->driver |= DRIVER_REQUESTED;
+			sprintf(modname, "snd-%s", ops->id);
+			request_module(modname);
+			down(&ops_mutex);
+			ops->used--;
+		}
+	}
+	up(&ops_mutex);
+#endif
+}
+
+/*
+ * register a sequencer device
+ * card = card info (NULL allowed)
+ * device = device number (if any)
+ * id = id of driver
+ * result = return pointer (NULL allowed if unnecessary)
+ */
+int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize,
+		       snd_seq_device_t **result)
+{
+	snd_seq_device_t *dev;
+	ops_list_t *ops;
+	int err;
+	static snd_device_ops_t dops = {
+		dev_free:	snd_seq_device_dev_free,
+		dev_register:	snd_seq_device_dev_register,
+		dev_unregister:	snd_seq_device_dev_unregister
+	};
+
+	if (result)
+		*result = NULL;
+
+	snd_assert(id != NULL, return -EINVAL);
+
+	ops = find_driver(id, 1);
+	if (ops == NULL)
+		return -ENOMEM;
+
+	dev = snd_magic_kcalloc(snd_seq_device_t, sizeof(*dev) + argsize, GFP_KERNEL);
+	if (dev == NULL) {
+		unlock_driver(ops);
+		return -ENOMEM;
+	}
+
+	/* set up device info */
+	dev->card = card;
+	dev->device = device;
+	strncpy(dev->id, id, sizeof(dev->id) - 1);
+	dev->id[sizeof(dev->id) - 1] = 0;
+	dev->argsize = argsize;
+	dev->status = SNDRV_SEQ_DEVICE_FREE;
+
+	/* add this device to the list */
+	down(&ops->reg_mutex);
+	list_add_tail(&dev->list, &ops->dev_list);
+	ops->num_devices++;
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+	
+	if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
+		snd_seq_device_free(dev);
+		return err;
+	}
+	
+	if (result)
+		*result = dev;
+
+	return 0;
+}
+
+/*
+ * free the existing device
+ */
+static int snd_seq_device_free(snd_seq_device_t *dev)
+{
+	ops_list_t *ops;
+
+	snd_assert(dev != NULL, return -EINVAL);
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENXIO;
+
+	/* remove the device from the list */
+	down(&ops->reg_mutex);
+	list_del(&dev->list);
+	ops->num_devices--;
+	up(&ops->reg_mutex);
+
+	free_device(dev, ops);
+	if (dev->private_free)
+		dev->private_free(dev);
+	snd_magic_kfree(dev);
+
+	unlock_driver(ops);
+
+	return 0;
+}
+
+static int snd_seq_device_dev_free(snd_device_t *device)
+{
+	snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO);
+	return snd_seq_device_free(dev);
+}
+
+/*
+ * register the device
+ */
+static int snd_seq_device_dev_register(snd_device_t *device)
+{
+	snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO);
+	ops_list_t *ops;
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENOENT;
+
+	/* initialize this device if the corresponding driver was
+	 * already loaded
+	 */
+	if (ops->driver & DRIVER_LOADED)
+		init_device(dev, ops);
+
+	unlock_driver(ops);
+	return 0;
+}
+
+/*
+ * unregister the existing device
+ */
+static int snd_seq_device_dev_unregister(snd_device_t *device)
+{
+	snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO);
+	return snd_seq_device_free(dev);
+}
+
+/*
+ * register device driver
+ * id = driver id
+ * entry = driver operators - duplicated to each instance
+ */
+int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize)
+{
+	struct list_head *head;
+	ops_list_t *ops;
+
+	if (id == NULL || entry == NULL ||
+	    entry->init_device == NULL || entry->free_device == NULL)
+		return -EINVAL;
+
+	ops = find_driver(id, 1);
+	if (ops == NULL)
+		return -ENOMEM;
+	if (ops->driver & DRIVER_LOADED) {
+		snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
+		unlock_driver(ops);
+		return -EBUSY;
+	}
+
+	down(&ops->reg_mutex);
+	/* copy driver operators */
+	ops->ops = *entry;
+	ops->driver |= DRIVER_LOADED;
+	ops->argsize = argsize;
+
+	/* initialize existing devices if necessary */
+	list_for_each(head, &ops->dev_list) {
+		snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
+		init_device(dev, ops);
+	}
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+
+	return 0;
+}
+
+
+/*
+ * create driver record
+ */
+static ops_list_t * create_driver(char *id)
+{
+	ops_list_t *ops;
+
+	ops = kmalloc(sizeof(*ops), GFP_KERNEL);
+	if (ops == NULL)
+		return ops;
+	memset(ops, 0, sizeof(*ops));
+
+	/* set up driver entry */
+	strncpy(ops->id, id, sizeof(ops->id) - 1);
+	ops->id[sizeof(ops->id) - 1] = 0;
+	init_MUTEX(&ops->reg_mutex);
+	ops->driver = DRIVER_EMPTY;
+	INIT_LIST_HEAD(&ops->dev_list);
+	/* lock this instance */
+	ops->used = 1;
+
+	/* register driver entry */
+	down(&ops_mutex);
+	list_add_tail(&ops->list, &opslist);
+	num_ops++;
+	up(&ops_mutex);
+
+	return ops;
+}
+
+
+/*
+ * unregister the specified driver
+ */
+int snd_seq_device_unregister_driver(char *id)
+{
+	struct list_head *head;
+	ops_list_t *ops;
+
+	ops = find_driver(id, 0);
+	if (ops == NULL)
+		return -ENXIO;
+	if (! (ops->driver & DRIVER_LOADED) ||
+	    (ops->driver & DRIVER_LOCKED)) {
+		snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver);
+		unlock_driver(ops);
+		return -EBUSY;
+	}
+
+	/* close and release all devices associated with this driver */
+	down(&ops->reg_mutex);
+	ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
+	list_for_each(head, &ops->dev_list) {
+		snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
+		free_device(dev, ops);
+	}
+
+	ops->driver = 0;
+	if (ops->num_init_devices > 0)
+		snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices);
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+
+	/* remove empty driver entries */
+	remove_drivers();
+
+	return 0;
+}
+
+
+/*
+ * remove empty driver entries
+ */
+static void remove_drivers(void)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	head = opslist.next;
+	while (head != &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		if (! (ops->driver & DRIVER_LOADED) &&
+		    ops->used == 0 && ops->num_devices == 0) {
+			head = head->next;
+			list_del(&ops->list);
+			kfree(ops);
+			num_ops--;
+		} else
+			head = head->next;
+	}
+	up(&ops_mutex);
+}
+
+/*
+ * initialize the device - call init_device operator
+ */
+static int init_device(snd_seq_device_t *dev, ops_list_t *ops)
+{
+	if (! (ops->driver & DRIVER_LOADED))
+		return 0; /* driver is not loaded yet */
+	if (dev->status != SNDRV_SEQ_DEVICE_FREE)
+		return 0; /* already initialized */
+	if (ops->argsize != dev->argsize) {
+		snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
+		return -EINVAL;
+	}
+	if (ops->ops.init_device(dev) >= 0) {
+		dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
+		ops->num_init_devices++;
+	} else {
+		snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id);
+	}
+
+	return 0;
+}
+
+/*
+ * release the device - call free_device operator
+ */
+static int free_device(snd_seq_device_t *dev, ops_list_t *ops)
+{
+	int result;
+
+	if (! (ops->driver & DRIVER_LOADED))
+		return 0; /* driver is not loaded yet */
+	if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
+		return 0; /* not registered */
+	if (ops->argsize != dev->argsize) {
+		snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
+		return -EINVAL;
+	}
+	if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
+		dev->status = SNDRV_SEQ_DEVICE_FREE;
+		dev->driver_data = NULL;
+		ops->num_init_devices--;
+	} else {
+		snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id);
+	}
+
+	return 0;
+}
+
+/*
+ * find the matching driver with given id
+ */
+static ops_list_t * find_driver(char *id, int create_if_empty)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		if (strcmp(ops->id, id) == 0) {
+			ops->used++;
+			up(&ops_mutex);
+			return ops;
+		}
+	}
+	up(&ops_mutex);
+	if (create_if_empty)
+		return create_driver(id);
+	return NULL;
+}
+
+static void unlock_driver(ops_list_t *ops)
+{
+	down(&ops_mutex);
+	ops->used--;
+	up(&ops_mutex);
+}
+
+
+/*
+ * module part
+ */
+
+static int __init alsa_seq_device_init(void)
+{
+	info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root);
+	if (info_entry == NULL)
+		return -ENOMEM;
+	info_entry->content = SNDRV_INFO_CONTENT_TEXT;
+	info_entry->c.text.read_size = 2048;
+	info_entry->c.text.read = snd_seq_device_info;
+	if (snd_info_register(info_entry) < 0) {
+		snd_info_free_entry(info_entry);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void __exit alsa_seq_device_exit(void)
+{
+	remove_drivers();
+	snd_info_unregister(info_entry);
+	if (num_ops)
+		snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
+}
+
+module_init(alsa_seq_device_init)
+module_exit(alsa_seq_device_exit)
+
+EXPORT_SYMBOL(snd_seq_device_load_drivers);
+EXPORT_SYMBOL(snd_seq_device_new);
+EXPORT_SYMBOL(snd_seq_device_register_driver);
+EXPORT_SYMBOL(snd_seq_device_unregister_driver);
diff -Nru linux/sound/core/seq/seq_dtl.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_dtl.c
--- linux/sound/core/seq/seq_dtl.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_dtl.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,193 @@
+/*
+ * DTL(e) event converter
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include "seq_queue.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+typedef struct dtl_out {
+	int out_mtp_network;
+	unsigned int time_format;
+	unsigned int full_frame_count;
+	unsigned char sysex[11];
+} dtl_out_t;
+
+typedef struct dtl_in {
+	unsigned int time_format;
+	unsigned int cur_pos;
+} dtl_in_t;
+
+
+static int dtl_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	dtl_out_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->out_mtp_network = sync_info->opt_info[0];
+	arg->full_frame_count = sync_info->opt_info[1];
+	arg->time_format = sync_info->time_format;
+	sync_info->param.time.subframes = 1; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	*retp = arg;
+	return 0;
+}
+
+static int dtl_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	dtl_in_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->time_format = sync_info->time_format;
+	arg->cur_pos = 0;
+	sync_info->param.time.subframes = 1; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	*retp = arg;
+	return 0;
+}
+
+
+/* decode sync position */
+static int sync_pos_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	sndrv_seq_time_frame_t cur_out;
+	unsigned char *buf = arg->sysex;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+
+	cur_out = snd_seq_position_to_time_frame(arg->time_format, 1, src->data.queue.param.position);
+	buf[0] = 0xf0; /* SYSEX */
+	buf[1] = 0x00; /* MOTU */
+	buf[2] = 0x33; /* MOTU */
+	buf[3] = 0x7f;
+	buf[4] = 0x0c; /* DTL full frame */
+	buf[5] = arg->out_mtp_network; /* 0x00 or 0x08 */
+	buf[6] = cur_out.hour | (arg->time_format << 5);
+	buf[7] = cur_out.min;
+	buf[8] = cur_out.sec;
+	buf[9] = cur_out.frame;
+	buf[10] = 0xf7;
+
+	ev->type = SNDRV_SEQ_EVENT_SYSEX;
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+	ev->data.ext.len = 11;
+	ev->data.ext.ptr = buf;
+
+	return 1;
+}
+
+/* decode sync signal */
+static int sync_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned int pos;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+	pos = src->data.queue.param.position;
+	if (arg->full_frame_count &&
+	    (pos % arg->full_frame_count) == 0)
+		/* send full frame */
+		return sync_pos_out(arg, src, ev);
+	ev->type = SNDRV_SEQ_EVENT_CLOCK;
+	return 1;
+}
+
+static int dtl_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	dtl_out_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_SYNC:
+		return sync_out(arg, src, ev);
+	case SNDRV_SEQ_EVENT_SYNC_POS:
+		return sync_pos_out(arg, src, ev);
+	}
+	return 0;
+}
+
+/* decode sync position */
+static int sync_pos_in(dtl_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned time_format;
+	static unsigned char id[] = {
+		0xf0, 0x00, 0x33, 0x7f, 0x0c,
+	};
+	sndrv_seq_time_frame_t cur_in;
+	char buf[11];
+
+	if (snd_seq_expand_var_event(src, 11, buf, 1, 0) != 11)
+		return 0;
+	if (memcmp(buf, id, sizeof(id)) != 0)
+		return 0;
+	time_format = (buf[6] >> 5) & 3;
+	if (time_format != arg->time_format)
+		return -EINVAL;
+	cur_in.hour = buf[6] & 0x1f;
+	cur_in.min = buf[7];
+	cur_in.sec = buf[8];
+	cur_in.frame = buf[9];
+	arg->cur_pos = snd_seq_time_frame_to_position(time_format, 1, &cur_in);
+
+	ev->type = SNDRV_SEQ_EVENT_SYNC_POS;
+	ev->data.queue.sync_time_format = time_format;
+	ev->data.queue.param.position = arg->cur_pos;
+
+	return 1;
+}
+
+static int dtl_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	dtl_in_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_CLOCK:
+		arg->cur_pos++;
+		ev->type = SNDRV_SEQ_EVENT_SYNC;
+		ev->data.queue.param.position = arg->cur_pos;
+		return 1;
+	case SNDRV_SEQ_EVENT_SYSEX:
+		return sync_pos_in(arg, src, ev);
+	}
+	return 0;
+}
+
+/* exported */
+seq_sync_parser_t snd_seq_dtl_parser = {
+	format: SNDRV_SEQ_SYNC_FMT_DTL,
+	in: {
+		open: dtl_open_in,
+		sync: dtl_sync_in,
+	},
+	out: {
+		open: dtl_open_out,
+		sync: dtl_sync_out,
+	},
+};
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
diff -Nru linux/sound/core/seq/seq_dummy.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_dummy.c
--- linux/sound/core/seq/seq_dummy.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_dummy.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,274 @@
+/*
+ * ALSA sequencer MIDI-through client
+ * Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "seq_clientmgr.h"
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+/*
+
+  Sequencer MIDI-through client
+
+  This gives a simple midi-through client.  All the normal input events
+  are redirected to output port immediately.
+  The routing can be done via aconnect program in alsa-utils.
+
+  Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
+  If you want to auto-load this module, you may add the following alias
+  in your /etc/conf.modules file.
+
+	alias snd-seq-client-62  snd-seq-dummy
+
+  The module is loaded on demand for client 62, or /proc/asound/seq/
+  is accessed.  If you don't need this module to be loaded, alias
+  snd-seq-client-62 as "off".  This will help modprobe.
+
+  The number of ports to be created can be specified via the module
+  paramter "ports".  For example, to create four ports, add the
+  following option in /etc/modules.conf:
+
+	option snd-seq-dummy ports=4
+
+  The modle option "duplex=1" enables duplex operation to the port.
+  In duplex mode, a pair of ports are created instead of single port,
+  and events are tunneled between pair-ports.  For example, input to
+  port A is sent to output port of another port B and vice versa.
+  In duplex mode, each port has DUPLEX capability.
+
+ */
+
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+MODULE_PARM(ports, "i");
+MODULE_PARM_DESC(ports, "number of ports to be created");
+MODULE_PARM(duplex, "i");
+MODULE_PARM_DESC(duplex, "create DUPLEX ports");
+int ports = 1;
+int duplex = 0;
+
+typedef struct snd_seq_dummy_port {
+	int client;
+	int port;
+	int duplex;
+	int connect;
+} snd_seq_dummy_port_t;
+
+static int my_client = -1;
+
+/*
+ * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
+ * to subscribers.
+ * Note: this callback is called only after all subscribers are removed.
+ */
+static int
+dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_seq_dummy_port_t *p;
+	int i;
+	snd_seq_event_t ev;
+
+	p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL);
+	memset(&ev, 0, sizeof(ev));
+	if (p->duplex)
+		ev.source.port = p->connect;
+	else
+		ev.source.port = p->port;
+	ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+	for (i = 0; i < 16; i++) {
+		ev.data.control.channel = i;
+		ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
+		snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
+		ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
+		snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
+	}
+	return 0;
+}
+
+/*
+ * event input callback - just redirect events to subscribers
+ */
+static int
+dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
+{
+	snd_seq_dummy_port_t *p;
+	snd_seq_event_t tmpev;
+
+	p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL);
+	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
+	    ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+		return 0; /* ignore system messages */
+	/* save the original sender */
+	tmpev.type = SNDRV_SEQ_EVENT_KERNEL_QUOTE;
+	tmpev.flags = (ev->flags & ~SNDRV_SEQ_EVENT_LENGTH_MASK)
+		| SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	tmpev.tag = ev->tag;
+	tmpev.time = ev->time;
+	tmpev.data.quote.origin = ev->source;
+	tmpev.data.quote.event = ev;
+	if (p->duplex)
+		tmpev.source.port = p->connect;
+	else
+		tmpev.source.port = p->port;
+	tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
+}
+
+/*
+ * free_private callback
+ */
+static void
+dummy_free(void *private_data)
+{
+	snd_seq_dummy_port_t *p;
+
+	p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return);
+	snd_magic_kfree(p);
+}
+
+/*
+ * create a port
+ */
+static snd_seq_dummy_port_t __init *
+create_port(int idx, int type)
+{
+	snd_seq_port_info_t pinfo;
+	snd_seq_port_callback_t pcb;
+	snd_seq_dummy_port_t *rec;
+
+	if ((rec = snd_magic_kcalloc(snd_seq_dummy_port_t, 0, GFP_KERNEL)) == NULL)
+		return NULL;
+
+	rec->client = my_client;
+	rec->duplex = duplex;
+	rec->connect = 0;
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.addr.client = my_client;
+	if (duplex)
+		sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
+			(type ? 'B' : 'A'));
+	else
+		sprintf(pinfo.name, "Midi Through Port-%d", idx);
+	pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	if (duplex)
+		pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	memset(&pcb, 0, sizeof(pcb));
+	pcb.owner = THIS_MODULE;
+	pcb.unuse = dummy_unuse;
+	pcb.event_input = dummy_input;
+	pcb.private_free = dummy_free;
+	pcb.private_data = rec;
+	pinfo.kernel = &pcb;
+	if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
+		snd_magic_kfree(rec);
+		return NULL;
+	}
+	rec->port = pinfo.addr.port;
+	return rec;
+}
+
+/*
+ * register client and create ports
+ */
+static int __init
+register_client(void)
+{
+	snd_seq_client_callback_t cb;
+	snd_seq_client_info_t cinfo;
+	snd_seq_dummy_port_t *rec1, *rec2;
+	int i;
+
+	if (ports < 1) {
+		snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
+		return -EINVAL;
+	}
+
+	/* create client */
+	memset(&cb, 0, sizeof(cb));
+	cb.allow_input = 1;
+	cb.allow_output = 1;
+	my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb);
+	if (my_client < 0)
+		return my_client;
+
+	/* set client name */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = my_client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, "Midi Through");
+	snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	/* create ports */
+	for (i = 0; i < ports; i++) {
+		rec1 = create_port(i, 0);
+		if (rec1 == NULL) {
+			snd_seq_delete_kernel_client(my_client);
+			return -ENOMEM;
+		}
+		if (duplex) {
+			rec2 = create_port(i, 1);
+			if (rec2 == NULL) {
+				snd_seq_delete_kernel_client(my_client);
+				return -ENOMEM;
+			}
+			rec1->connect = rec2->port;
+			rec2->connect = rec1->port;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * delete client if exists
+ */
+static void __exit
+delete_client(void)
+{
+	if (my_client >= 0)
+		snd_seq_delete_kernel_client(my_client);
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_seq_dummy_init(void)
+{
+	return register_client();
+}
+
+static void __exit alsa_seq_dummy_exit(void)
+{
+	delete_client();
+}
+
+module_init(alsa_seq_dummy_init)
+module_exit(alsa_seq_dummy_exit)
diff -Nru linux/sound/core/seq/seq_fifo.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.c
--- linux/sound/core/seq/seq_fifo.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,259 @@
+/*
+ *   ALSA sequencer FIFO
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_fifo.h"
+#include "seq_lock.h"
+
+
+/* FIFO */
+
+/* create new fifo */
+fifo_t *snd_seq_fifo_new(int poolsize)
+{
+	fifo_t *f;
+
+	f = snd_kcalloc(sizeof(fifo_t), GFP_KERNEL);
+	if (f == NULL) {
+		snd_printd("malloc failed for snd_seq_fifo_new() \n");
+		return NULL;
+	}
+
+	f->pool = snd_seq_pool_new(poolsize);
+	if (f->pool == NULL) {
+		kfree(f);
+		return NULL;
+	}
+	if (snd_seq_pool_init(f->pool) < 0) {
+		snd_seq_pool_delete(&f->pool);
+		kfree(f);
+		return NULL;
+	}
+
+	spin_lock_init(&f->lock);
+	snd_use_lock_init(&f->use_lock);
+	init_waitqueue_head(&f->input_sleep);
+	atomic_set(&f->overflow, 0);
+
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	
+	return f;
+}
+
+void snd_seq_fifo_delete(fifo_t **fifo)
+{
+	fifo_t *f;
+
+	snd_assert(fifo != NULL, return);
+	f = *fifo;
+	snd_assert(f != NULL, return);
+	*fifo = NULL;
+
+	snd_seq_fifo_clear(f);
+
+	/* wake up clients if any */
+	if (waitqueue_active(&f->input_sleep))
+		wake_up(&f->input_sleep);
+
+	/* release resources...*/
+	/*....................*/
+
+	if (f->pool) {
+		snd_seq_pool_done(f->pool);
+		snd_seq_pool_delete(&f->pool);
+	}
+	
+	kfree(f);
+}
+
+static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f);
+
+/* clear queue */
+void snd_seq_fifo_clear(fifo_t *f)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+
+	/* clear overflow flag */
+	atomic_set(&f->overflow, 0);
+
+	snd_use_lock_sync(&f->use_lock);
+	spin_lock_irqsave(&f->lock, flags);
+	/* drain the fifo */
+	while ((cell = fifo_cell_out(f)) != NULL) {
+		snd_seq_cell_free(cell);
+	}
+	spin_unlock_irqrestore(&f->lock, flags);
+}
+
+
+/* enqueue event to fifo */
+int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+	int err;
+
+	snd_assert(f != NULL, return -EINVAL);
+
+	snd_use_lock_use(&f->use_lock);
+	err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+	if (err < 0) {
+		if (err == -ENOMEM)
+			atomic_inc(&f->overflow);
+		snd_use_lock_free(&f->use_lock);
+		return err;
+	}
+		
+	/* append new cells to fifo */
+	spin_lock_irqsave(&f->lock, flags);
+	if (f->tail != NULL)
+		f->tail->next = cell;
+	f->tail = cell;
+	if (f->head == NULL)
+		f->head = cell;
+	f->cells++;
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	/* wakeup client */
+	if (waitqueue_active(&f->input_sleep))
+		wake_up(&f->input_sleep);
+
+	snd_use_lock_free(&f->use_lock);
+
+	return 0; /* success */
+
+}
+
+/* dequeue cell from fifo */
+static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f)
+{
+	snd_seq_event_cell_t *cell;
+
+	if ((cell = f->head) != NULL) {
+		f->head = cell->next;
+
+		/* reset tail if this was the last element */
+		if (f->tail == cell)
+			f->tail = NULL;
+
+		cell->next = NULL;
+		f->cells--;
+	}
+
+	return cell;
+}
+
+/* dequeue cell from fifo and copy on user space */
+int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+
+	snd_assert(f != NULL, return -EINVAL);
+
+	*cellp = NULL;
+	spin_lock_irqsave(&f->lock, flags);
+	while ((cell = fifo_cell_out(f)) == NULL) {
+		if (nonblock) {
+			/* non-blocking - return immediately */
+			spin_unlock_irqrestore(&f->lock, flags);
+			return -EAGAIN;
+		}
+		snd_seq_sleep_in_lock(&f->input_sleep, &f->lock);
+
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&f->lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
+	*cellp = cell;
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	return 0;
+}
+
+
+void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell)
+{
+	unsigned long flags;
+
+	if (cell) {
+		spin_lock_irqsave(&f->lock, flags);
+		cell->next = f->head;
+		f->head = cell;
+		f->cells++;
+		spin_unlock_irqrestore(&f->lock, flags);
+	}
+}
+
+
+/* polling; return non-zero if queue is available */
+int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait)
+{
+	poll_wait(file, &f->input_sleep, wait);
+	return (f->cells > 0);
+}
+
+/* change the size of pool; all old events are removed */
+int snd_seq_fifo_resize(fifo_t *f, int poolsize)
+{
+	unsigned long flags;
+	pool_t *newpool, *oldpool;
+	snd_seq_event_cell_t *cell, *next, *oldhead;
+
+	snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
+
+	/* allocate new pool */
+	newpool = snd_seq_pool_new(poolsize);
+	if (newpool == NULL)
+		return -ENOMEM;
+	if (snd_seq_pool_init(newpool) < 0) {
+		snd_seq_pool_delete(&newpool);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&f->lock, flags);
+	/* remember old pool */
+	oldpool = f->pool;
+	oldhead = f->head;
+	/* exchange pools */
+	f->pool = newpool;
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	/* NOTE: overflow flag is not cleared */
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	/* release cells in old pool */
+	for (cell = oldhead; cell; cell = next) {
+		next = cell->next;
+		snd_seq_cell_free(cell);
+	}
+	snd_seq_pool_delete(&oldpool);
+
+	return 0;
+}
diff -Nru linux/sound/core/seq/seq_fifo.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.h
--- linux/sound/core/seq/seq_fifo.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,72 @@
+/*
+ *   ALSA sequencer FIFO
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_FIFO_H
+#define __SND_SEQ_FIFO_H
+
+#include "seq_memory.h"
+#include "seq_lock.h"
+
+
+/* === FIFO === */
+
+typedef struct {
+	pool_t *pool;			/* FIFO pool */
+	snd_seq_event_cell_t* head;    	/* pointer to head of fifo */
+	snd_seq_event_cell_t* tail;    	/* pointer to tail of fifo */
+	int cells;
+	spinlock_t lock;
+	snd_use_lock_t use_lock;
+	wait_queue_head_t input_sleep;
+	atomic_t overflow;
+
+} fifo_t;
+
+/* create new fifo (constructor) */
+extern fifo_t *snd_seq_fifo_new(int poolsize);
+
+/* delete fifo (destructor) */
+extern void snd_seq_fifo_delete(fifo_t **f);
+
+
+/* enqueue event to fifo */
+extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event);
+
+/* lock fifo from release */
+#define snd_seq_fifo_lock(fifo)		snd_use_lock_use(&(fifo)->use_lock)
+#define snd_seq_fifo_unlock(fifo)	snd_use_lock_free(&(fifo)->use_lock)
+
+/* get a cell from fifo - fifo should be locked */
+int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock);
+
+/* free dequeued cell - fifo should be locked */
+extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell);
+
+/* clean up queue */
+extern void snd_seq_fifo_clear(fifo_t *f);
+
+/* polling */
+extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait);
+
+/* resize pool in fifo */
+int snd_seq_fifo_resize(fifo_t *f, int poolsize);
+
+
+#endif
diff -Nru linux/sound/core/seq/seq_info.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.c
--- linux/sound/core/seq/seq_info.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,76 @@
+/*
+ *   ALSA sequencer /proc interface
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+
+#include "seq_info.h"
+#include "seq_clientmgr.h"
+#include "seq_timer.h"
+
+
+static snd_info_entry_t *queues_entry;
+static snd_info_entry_t *clients_entry;
+static snd_info_entry_t *timer_entry;
+
+
+static snd_info_entry_t * __init
+create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *))
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root);
+	if (entry == NULL)
+		return NULL;
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->c.text.read_size = size;
+	entry->c.text.read = read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return NULL;
+	}
+	return entry;
+}
+
+
+/* create all our /proc entries */
+int __init snd_seq_info_init(void)
+{
+	queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES),
+					 snd_seq_info_queues_read);
+	clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS),
+					  snd_seq_info_clients_read);
+	timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read);
+	return 0;
+}
+
+int __exit snd_seq_info_done(void)
+{
+	if (queues_entry)
+		snd_info_unregister(queues_entry);
+	if (clients_entry)
+		snd_info_unregister(clients_entry);
+	if (timer_entry)
+		snd_info_unregister(timer_entry);
+	return 0;
+}
diff -Nru linux/sound/core/seq/seq_info.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.h
--- linux/sound/core/seq/seq_info.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,36 @@
+/*
+ *   ALSA sequencer /proc info
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_INFO_H
+#define __SND_SEQ_INFO_H
+
+#include <sound/info.h>
+#include <sound/seq_kernel.h>
+
+void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+
+
+int snd_seq_info_init( void );
+int snd_seq_info_done( void );
+
+
+#endif
diff -Nru linux/sound/core/seq/seq_instr.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_instr.c
--- linux/sound/core/seq/seq_instr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_instr.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,671 @@
+/*
+ *   Generic Instrument routines for ALSA sequencer
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "seq_clientmgr.h"
+#include <sound/seq_instr.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+
+static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list)
+{
+	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+		spin_lock_irqsave(&list->ops_lock, list->ops_flags);
+	} else {
+		down(&list->ops_mutex);
+	}
+}
+
+static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list)
+{
+	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+		spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
+	} else {
+		up(&list->ops_mutex);
+	}
+}
+
+snd_seq_kcluster_t *snd_seq_cluster_new(int atomic)
+{
+	snd_seq_kcluster_t *cluster;
+	
+	cluster = (snd_seq_kcluster_t *) snd_kcalloc(sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL);
+	return cluster;
+}
+
+void snd_seq_cluster_free(snd_seq_kcluster_t *cluster, int atomic)
+{
+	if (cluster == NULL)
+		return;
+	kfree(cluster);
+}
+
+snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic)
+{
+	snd_seq_kinstr_t *instr;
+	
+	instr = (snd_seq_kinstr_t *) snd_kcalloc(sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
+	if (instr == NULL)
+		return NULL;
+	instr->add_len = add_len;
+	return instr;
+}
+
+int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic)
+{
+	int result = 0;
+
+	if (instr == NULL)
+		return -EINVAL;
+	if (instr->ops && instr->ops->remove)
+		result = instr->ops->remove(instr->ops->private_data, instr, 1);
+	if (!result)
+		kfree(instr);
+	return result;
+}
+
+snd_seq_kinstr_list_t *snd_seq_instr_list_new(void)
+{
+	snd_seq_kinstr_list_t *list;
+
+	list = (snd_seq_kinstr_list_t *) snd_kcalloc(sizeof(snd_seq_kinstr_list_t), GFP_KERNEL);
+	if (list == NULL)
+		return NULL;
+	spin_lock_init(&list->lock);
+	spin_lock_init(&list->ops_lock);
+	init_MUTEX(&list->ops_mutex);
+	list->owner = -1;
+	return list;
+}
+
+void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr)
+{
+	snd_seq_kinstr_list_t *list;
+	snd_seq_kinstr_t *instr;
+	snd_seq_kcluster_t *cluster;
+	int idx;
+	unsigned long flags;
+
+	if (list_ptr == NULL)
+		return;
+	list = *list_ptr;
+	*list_ptr = NULL;
+	if (list == NULL)
+		return;
+	
+	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {		
+		while ((instr = list->hash[idx]) != NULL) {
+			list->hash[idx] = instr->next;
+			list->count--;
+			spin_lock_irqsave(&list->lock, flags);
+			while (instr->use) {
+				spin_unlock_irqrestore(&list->lock, flags);
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(1);
+				spin_lock_irqsave(&list->lock, flags);
+			}				
+			spin_unlock_irqrestore(&list->lock, flags);
+			if (snd_seq_instr_free(instr, 0)<0)
+				snd_printk(KERN_WARNING "instrument free problem\n");
+		}
+		while ((cluster = list->chash[idx]) != NULL) {
+			list->chash[idx] = cluster->next;
+			list->ccount--;
+			snd_seq_cluster_free(cluster, 0);
+		}
+	}
+	kfree(list);
+}
+
+static int instr_free_compare(snd_seq_kinstr_t *instr,
+			      snd_seq_instr_header_t *ifree,
+			      int client)
+{
+	switch (ifree->cmd) {
+	case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
+		/* all, except private for other clients */
+		if ((instr->instr.std & 0xff000000) == 0)
+			return 0;
+		if (((instr->instr.std >> 24) & 0xff) == client)
+			return 0;
+		return 1;
+	case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
+		/* all my private instruments */
+		if ((instr->instr.std & 0xff000000) == 0)
+			return 1;
+		if (((instr->instr.std >> 24) & 0xff) == client)
+			return 0;
+		return 1;
+	case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
+		/* all my private instruments */
+		if ((instr->instr.std & 0xff000000) == 0) {
+			if (instr->instr.cluster == ifree->id.cluster)
+				return 0;
+			return 1;
+		}
+		if (((instr->instr.std >> 24) & 0xff) == client) {
+			if (instr->instr.cluster == ifree->id.cluster)
+				return 0;
+		}
+		return 1;
+	}
+	return 1;
+}
+
+int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list,
+			         snd_seq_instr_header_t *ifree,
+			         int client,
+			         int atomic)
+{
+	snd_seq_kinstr_t *instr, *prev, *next, *flist;
+	int idx;
+	unsigned long flags;
+
+	snd_instr_lock_ops(list);
+	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
+		spin_lock_irqsave(&list->lock, flags);
+		instr = list->hash[idx];
+		prev = flist = NULL;
+		while (instr) {
+			while (instr && instr_free_compare(instr, ifree, client)) {
+				prev = instr;
+				instr = instr->next;
+			}
+			if (instr == NULL)
+				continue;
+			if (instr->ops && instr->ops->notify)
+				instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+			next = instr->next;
+			if (prev == NULL) {
+				list->hash[idx] = next;
+			} else {
+				prev->next = next;
+			}
+			list->count--;
+			instr->next = flist;
+			flist = instr;
+			instr = next;
+		}
+		spin_unlock_irqrestore(&list->lock, flags);
+		while (flist) {
+			instr = flist;
+			flist = instr->next;
+			while (instr->use) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(1);
+			}				
+			if (snd_seq_instr_free(instr, atomic)<0)
+				snd_printk(KERN_WARNING "instrument free problem\n");
+			instr = next;
+		}
+	}
+	snd_instr_unlock_ops(list);
+	return 0;	
+}
+
+static int compute_hash_instr_key(snd_seq_instr_t *instr)
+{
+	int result;
+	
+	result = instr->bank | (instr->prg << 16);
+	result += result >> 24;
+	result += result >> 16;
+	result += result >> 8;
+	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+}
+
+#if 0
+static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
+{
+	int result;
+	
+	result = cluster;
+	result += result >> 24;
+	result += result >> 16;
+	result += result >> 8;
+	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+}
+#endif
+
+static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact)
+{
+	if (exact) {
+		if (i1->cluster != i2->cluster ||
+		    i1->bank != i2->bank ||
+		    i1->prg != i2->prg)
+			return 1;
+		if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
+			return 1;
+		if (!(i1->std & i2->std))
+			return 1;
+		return 0;
+	} else {
+		unsigned int client_check;
+		
+		if (i2->cluster && i1->cluster != i2->cluster)
+			return 1;
+		client_check = i2->std & 0xff000000;
+		if (client_check) {
+			if ((i1->std & 0xff000000) != client_check)
+				return 1;
+		} else {
+			if ((i1->std & i2->std) != i2->std)
+				return 1;
+		}
+		return i1->bank != i2->bank || i1->prg != i2->prg;
+	}
+}
+
+snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list,
+				     snd_seq_instr_t *instr,
+				     int exact,
+				     int follow_alias)
+{
+	unsigned long flags;
+	int depth = 0;
+	snd_seq_kinstr_t *result;
+
+	if (list == NULL || instr == NULL)
+		return NULL;
+	spin_lock_irqsave(&list->lock, flags);
+      __again:
+	result = list->hash[compute_hash_instr_key(instr)];
+	while (result) {
+		if (!compare_instr(&result->instr, instr, exact)) {
+			if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
+				instr = (snd_seq_instr_t *)KINSTR_DATA(result);
+				if (++depth > 10)
+					goto __not_found;
+				goto __again;
+			}
+			result->use++;
+			spin_unlock_irqrestore(&list->lock, flags);
+			return result;
+		}
+		result = result->next;
+	}
+      __not_found:
+	spin_unlock_irqrestore(&list->lock, flags);
+	return NULL;
+}
+
+void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list,
+			    snd_seq_kinstr_t *instr)
+{
+	unsigned long flags;
+
+	if (list == NULL || instr == NULL)
+		return;
+	spin_lock_irqsave(&list->lock, flags);
+	if (instr->use <= 0) {
+		snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
+	} else {
+		instr->use--;
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type)
+{
+	while (ops) {
+		if (!strcmp(ops->instr_type, instr_type))
+			return ops;
+		ops = ops->next;
+	}
+	return NULL;
+}
+
+static int instr_result(snd_seq_event_t *ev,
+			int type, int result,
+			int atomic)
+{
+	snd_seq_event_t sev;
+	
+	memset(&sev, 0, sizeof(sev));
+	sev.type = SNDRV_SEQ_EVENT_RESULT;
+	sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
+	            SNDRV_SEQ_PRIORITY_NORMAL;
+	sev.source = ev->dest;
+	sev.dest = ev->source;
+	sev.data.result.event = type;
+	sev.data.result.result = result;
+#if 0
+	printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
+				type, result,
+				sev.queue,
+				sev.source.client, sev.source.port,
+				sev.dest.client, sev.dest.port);
+#endif
+	return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
+}
+
+static int instr_begin(snd_seq_kinstr_ops_t *ops,
+		       snd_seq_kinstr_list_t *list,
+		       snd_seq_event_t *ev,
+		       int atomic, int hop)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	if (list->owner >= 0 && list->owner != ev->source.client) {
+		spin_unlock_irqrestore(&list->lock, flags);
+		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
+	}
+	list->owner = ev->source.client;
+	spin_unlock_irqrestore(&list->lock, flags);
+	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
+}
+
+static int instr_end(snd_seq_kinstr_ops_t *ops,
+		     snd_seq_kinstr_list_t *list,
+		     snd_seq_event_t *ev,
+		     int atomic, int hop)
+{
+	unsigned long flags;
+
+	/* TODO: timeout handling */
+	spin_lock_irqsave(&list->lock, flags);
+	if (list->owner == ev->source.client) {
+		list->owner = -1;
+		spin_unlock_irqrestore(&list->lock, flags);
+		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
+}
+
+static int instr_info(snd_seq_kinstr_ops_t *ops,
+		      snd_seq_kinstr_list_t *list,
+		      snd_seq_event_t *ev,
+		      int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_format_info(snd_seq_kinstr_ops_t *ops,
+			     snd_seq_kinstr_list_t *list,
+			     snd_seq_event_t *ev,
+			     int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_reset(snd_seq_kinstr_ops_t *ops,
+		       snd_seq_kinstr_list_t *list,
+		       snd_seq_event_t *ev,
+		       int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_status(snd_seq_kinstr_ops_t *ops,
+			snd_seq_kinstr_list_t *list,
+			snd_seq_event_t *ev,
+			int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_put(snd_seq_kinstr_ops_t *ops,
+		     snd_seq_kinstr_list_t *list,
+		     snd_seq_event_t *ev,
+		     int atomic, int hop)
+{
+	unsigned long flags;
+	snd_seq_instr_header_t put;
+	snd_seq_kinstr_t *instr;
+	int result = -EINVAL, len, key;
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+		goto __return;
+
+	if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
+		goto __return;
+	if (copy_from_user(&put, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
+		result = -EFAULT;
+		goto __return;
+	}
+	snd_instr_lock_ops(list);
+	if (put.id.instr.std & 0xff000000) {	/* private instrument */
+		put.id.instr.std &= 0x00ffffff;
+		put.id.instr.std |= (unsigned int)ev->source.client << 24;
+	}
+	if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
+		snd_seq_instr_free_use(list, instr);
+		snd_instr_unlock_ops(list);
+		result = -EBUSY;
+		goto __return;
+	}
+	ops = instr_ops(ops, put.data.data.format);
+	if (ops == NULL) {
+		snd_instr_unlock_ops(list);
+		goto __return;
+	}
+	len = ops->add_len;
+	if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
+		len = sizeof(snd_seq_instr_t);
+	instr = snd_seq_instr_new(len, atomic);
+	if (instr == NULL) {
+		snd_instr_unlock_ops(list);
+		result = -ENOMEM;
+		goto __return;
+	}
+	instr->ops = ops;
+	instr->instr = put.id.instr;
+	strncpy(instr->name, put.data.name, sizeof(instr->name)-1);
+	instr->name[sizeof(instr->name)-1] = '\0';
+	instr->type = put.data.type;
+	if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
+		result = ops->put(ops->private_data,
+				  instr,
+				  ev->data.ext.ptr + sizeof(snd_seq_instr_header_t),
+				  ev->data.ext.len - sizeof(snd_seq_instr_header_t),
+				  atomic,
+				  put.cmd);
+		if (result < 0) {
+			snd_seq_instr_free(instr, atomic);
+			snd_instr_unlock_ops(list);
+			goto __return;
+		}
+	}
+	key = compute_hash_instr_key(&instr->instr);
+	spin_lock_irqsave(&list->lock, flags);
+	instr->next = list->hash[key];
+	list->hash[key] = instr;
+	list->count++;
+	spin_unlock_irqrestore(&list->lock, flags);
+	snd_instr_unlock_ops(list);
+	result = 0;
+      __return:
+	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
+	return result;
+}
+
+static int instr_get(snd_seq_kinstr_ops_t *ops,
+		     snd_seq_kinstr_list_t *list,
+		     snd_seq_event_t *ev,
+		     int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_free(snd_seq_kinstr_ops_t *ops,
+		      snd_seq_kinstr_list_t *list,
+		      snd_seq_event_t *ev,
+		      int atomic, int hop)
+{
+	snd_seq_instr_header_t ifree;
+	snd_seq_kinstr_t *instr, *prev;
+	int result = -EINVAL;
+	unsigned long flags;
+	unsigned int hash;
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+		goto __return;
+
+	if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
+		goto __return;
+	if (copy_from_user(&ifree, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
+		result = -EFAULT;
+		goto __return;
+	}
+	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
+	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
+	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
+	    	result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
+	    	goto __return;
+	}
+	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
+		if (ifree.id.instr.std & 0xff000000) {
+			ifree.id.instr.std &= 0x00ffffff;
+			ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
+		}
+		hash = compute_hash_instr_key(&ifree.id.instr);
+		snd_instr_lock_ops(list);
+		spin_lock_irqsave(&list->lock, flags);
+		instr = list->hash[hash];
+		prev = NULL;
+		while (instr) {
+			if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
+				goto __free_single;
+			prev = instr;
+			instr = instr->next;
+		}
+		result = -ENOENT;
+		spin_unlock_irqrestore(&list->lock, flags);
+		snd_instr_unlock_ops(list);
+		goto __return;
+		
+	      __free_single:
+		if (prev) {
+			prev->next = instr->next;
+		} else {
+			list->hash[hash] = instr->next;
+		}
+		if (instr->ops && instr->ops->notify)
+			instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+		while (instr->use) {
+			spin_unlock_irqrestore(&list->lock, flags);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(1);
+			spin_lock_irqsave(&list->lock, flags);
+		}				
+		spin_unlock_irqrestore(&list->lock, flags);
+		result = snd_seq_instr_free(instr, atomic);
+		snd_instr_unlock_ops(list);
+		goto __return;
+	}
+
+      __return:
+	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
+	return result;
+}
+
+static int instr_list(snd_seq_kinstr_ops_t *ops,
+		      snd_seq_kinstr_list_t *list,
+		      snd_seq_event_t *ev,
+		      int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_cluster(snd_seq_kinstr_ops_t *ops,
+			 snd_seq_kinstr_list_t *list,
+			 snd_seq_event_t *ev,
+			 int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops,
+			snd_seq_kinstr_list_t *list,
+			snd_seq_event_t *ev,
+			int client,
+			int atomic,
+			int hop)
+{
+	int direct = 0;
+
+	snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
+	if (snd_seq_ev_is_direct(ev)) {
+		direct = 1;
+		switch (ev->type) {
+		case SNDRV_SEQ_EVENT_INSTR_BEGIN:
+			return instr_begin(ops, list, ev, atomic, hop);
+		case SNDRV_SEQ_EVENT_INSTR_END:
+			return instr_end(ops, list, ev, atomic, hop);
+		}
+	}
+	if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
+		return -EINVAL;
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_INSTR_INFO:
+		return instr_info(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_FINFO:
+		return instr_format_info(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_RESET:
+		return instr_reset(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_STATUS:
+		return instr_status(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_PUT:
+		return instr_put(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_GET:
+		return instr_get(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_FREE:
+		return instr_free(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_LIST:
+		return instr_list(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
+		return instr_cluster(ops, list, ev, atomic, hop);
+	}
+	return -EINVAL;
+}
+			
+/*
+ *  Init part
+ */
+
+static int __init alsa_seq_instr_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_instr_exit(void)
+{
+}
+
+module_init(alsa_seq_instr_init)
+module_exit(alsa_seq_instr_exit)
+
+EXPORT_SYMBOL(snd_seq_instr_list_new);
+EXPORT_SYMBOL(snd_seq_instr_list_free);
+EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
+EXPORT_SYMBOL(snd_seq_instr_find);
+EXPORT_SYMBOL(snd_seq_instr_free_use);
+EXPORT_SYMBOL(snd_seq_instr_event);
diff -Nru linux/sound/core/seq/seq_lock.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.c
--- linux/sound/core/seq/seq_lock.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,85 @@
+/*
+ *  Do sleep inside a spin-lock
+ *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include "seq_lock.h"
+
+#if defined(__SMP__) || defined(CONFIG_SND_DEBUG)
+
+/* (interruptible) sleep_on during the specified spinlock */
+void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock)
+{
+	wait_queue_t wait;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	init_waitqueue_entry(&wait, current);
+
+	add_wait_queue(p, &wait);
+
+	spin_unlock(lock);
+	schedule();
+	spin_lock_irq(lock);
+
+	remove_wait_queue(p, &wait);
+}
+
+/* (interruptible) sleep_on with timeout during the specified spinlock */
+long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout)
+{
+	wait_queue_t wait;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	init_waitqueue_entry(&wait, current);
+
+	add_wait_queue(p, &wait);
+
+	spin_unlock(lock);
+	timeout = schedule_timeout(timeout);
+	spin_lock_irq(lock);
+
+	remove_wait_queue(p, &wait);
+
+	return timeout;
+}
+
+/* wait until all locks are released */
+void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
+{
+	int max_count = 5 * HZ;
+
+	if (atomic_read(lockp) < 0) {
+		printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
+		return;
+	}
+	while (atomic_read(lockp) > 0) {
+		if (max_count == 0) {
+			snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		max_count--;
+	}
+}
+
+#endif
diff -Nru linux/sound/core/seq/seq_lock.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.h
--- linux/sound/core/seq/seq_lock.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,42 @@
+#ifndef __SND_SEQ_LOCK_H
+#define __SND_SEQ_LOCK_H
+
+#include <linux/sched.h>
+
+#if defined(__SMP__) || defined(CONFIG_SND_DEBUG)
+
+typedef atomic_t snd_use_lock_t;
+
+/* initialize lock */
+#define snd_use_lock_init(lockp) atomic_set(lockp, 0)
+
+/* increment lock */
+#define snd_use_lock_use(lockp) atomic_inc(lockp)
+
+/* release lock */
+#define snd_use_lock_free(lockp) atomic_dec(lockp)
+
+/* wait until all locks are released */
+void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
+#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
+
+/* (interruptible) sleep_on during the specified spinlock */
+void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock);
+
+/* (interruptible) sleep_on with timeout during the specified spinlock */
+long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout);
+
+#else /* SMP || CONFIG_SND_DEBUG */
+
+typedef spinlock_t snd_use_lock_t;	/* dummy */
+#define snd_use_lock_init(lockp) /**/
+#define snd_use_lock_use(lockp) /**/
+#define snd_use_lock_free(lockp) /**/
+#define snd_use_lock_sync(lockp) /**/
+
+#define snd_seq_sleep_in_lock(p,lock)	interruptible_sleep_on(p)
+#define snd_seq_sleep_timeout_in_lock(p,lock,timeout)	interruptible_sleep_on_timeout(p,timeout)
+
+#endif /* SMP || CONFIG_SND_DEBUG */
+
+#endif /* __SND_SEQ_LOCK_H */
diff -Nru linux/sound/core/seq/seq_memory.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.c
--- linux/sound/core/seq/seq_memory.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,514 @@
+/*
+ *  ALSA sequencer Memory Manager
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *                2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_info.h"
+#include "seq_lock.h"
+
+/* semaphore in struct file record */
+#define semaphore_of(fp)	((fp)->f_dentry->d_inode->i_sem)
+
+
+inline static int snd_seq_pool_available(pool_t *pool)
+{
+	return pool->total_elements - atomic_read(&pool->counter);
+}
+
+inline static int snd_seq_output_ok(pool_t *pool)
+{
+	return snd_seq_pool_available(pool) >= pool->room;
+}
+
+/*
+ * Variable length event:
+ * The event like sysex uses variable length type.
+ * The external data may be stored in three different formats.
+ * 1) kernel space
+ *    This is the normal case.
+ *      ext.data.len = length
+ *      ext.data.ptr = buffer pointer
+ * 2) user space
+ *    When an event is generated via read(), the external data is
+ *    kept in user space until expanded.
+ *      ext.data.len = length | SNDRV_SEQ_EXT_USRPTR
+ *      ext.data.ptr = userspace pointer
+ * 3) chained cells
+ *    When the variable length event is enqueued (in prioq or fifo),
+ *    the external data is decomposed to several cells.
+ *      ext.data.len = length | SNDRV_SEQ_EXT_CHAINED
+ *      ext.data.ptr = the additiona cell head
+ *         -> cell.next -> cell.next -> ..
+ */
+
+/*
+ * exported:
+ * call dump function to expand external data.
+ */
+
+static int get_var_len(const snd_seq_event_t *event)
+{
+	if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+		return -EINVAL;
+
+	return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+}
+
+int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data)
+{
+	int len, err;
+	snd_seq_event_cell_t *cell;
+
+	if ((len = get_var_len(event)) <= 0)
+		return len;
+
+	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+		char buf[32];
+		char *curptr = event->data.ext.ptr;
+		while (len > 0) {
+			int size = sizeof(buf);
+			if (len < size)
+				size = len;
+			if (copy_from_user(buf, curptr, size) < 0)
+				return -EFAULT;
+			err = func(private_data, buf, size);
+			if (err < 0)
+				return err;
+			curptr += size;
+			len -= size;
+		}
+		return 0;
+	} if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
+		return func(private_data, event->data.ext.ptr, len);
+	}
+
+	cell = (snd_seq_event_cell_t*)event->data.ext.ptr;
+	for (; len > 0 && cell; cell = cell->next) {
+		int size = sizeof(snd_seq_event_t);
+		if (len < size)
+			size = len;
+		err = func(private_data, &cell->event, size);
+		if (err < 0)
+			return err;
+		len -= size;
+	}
+	return 0;
+}
+
+
+/*
+ * exported:
+ * expand the variable length event to linear buffer space.
+ */
+
+static int copy_in_kernel(char **bufptr, const void *src, int size)
+{
+	memcpy(*bufptr, src, size);
+	*bufptr += size;
+	return 0;
+}
+
+static int copy_in_user(char **bufptr, const void *src, int size)
+{
+	if (copy_to_user(*bufptr, src, size))
+		return -EFAULT;
+	*bufptr += size;
+	return 0;
+}
+
+int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned)
+{
+	int len, newlen;
+	int err;
+
+	if ((len = get_var_len(event)) < 0)
+		return len;
+	newlen = len;
+	if (size_aligned > 0)
+		newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned;
+	if (count < newlen)
+		return -EAGAIN;
+
+	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+		if (! in_kernel)
+			return -EINVAL;
+		if (copy_from_user(buf, event->data.ext.ptr, len) < 0)
+			return -EFAULT;
+		return newlen;
+	}
+	err = snd_seq_dump_var_event(event,
+				     in_kernel ? (snd_seq_dump_func_t)copy_in_kernel :
+				     (snd_seq_dump_func_t)copy_in_user,
+				     &buf);
+	return err < 0 ? err : newlen;
+}
+
+
+/*
+ * release this cell, free extended data if available
+ */
+
+static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell)
+{
+	cell->next = pool->free;
+	pool->free = cell;
+	atomic_dec(&pool->counter);
+}
+
+void snd_seq_cell_free(snd_seq_event_cell_t * cell)
+{
+	unsigned long flags;
+	pool_t *pool;
+
+	snd_assert(cell != NULL, return);
+	pool = cell->pool;
+	snd_assert(pool != NULL, return);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	free_cell(pool, cell);
+	if (snd_seq_ev_is_variable(&cell->event)) {
+		if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
+			snd_seq_event_cell_t *curp, *nextptr;
+			curp = cell->event.data.ext.ptr;
+			for (; curp; curp = nextptr) {
+				nextptr = curp->next;
+				curp->next = pool->free;
+				free_cell(pool, curp);
+			}
+		}
+	}
+	if (waitqueue_active(&pool->output_sleep)) {
+		/* has enough space now? */
+		if (snd_seq_output_ok(pool))
+			wake_up(&pool->output_sleep);
+	}
+	spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+
+/*
+ * allocate an event cell.
+ */
+int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+	int err = -EAGAIN;
+
+	if (pool == NULL)
+		return -EINVAL;
+
+	*cellp = NULL;
+
+	spin_lock_irqsave(&pool->lock, flags);
+	if (pool->ptr == NULL) {	/* not initialized */
+		snd_printd("seq: pool is not initialized\n");
+		err = -EINVAL;
+		goto __error;
+	}
+	while (pool->free == NULL && ! nonblock && ! pool->closing) {
+		/* change semaphore to allow other clients
+		   to access device file */
+		if (file)
+			up(&semaphore_of(file));
+
+		snd_seq_sleep_in_lock(&pool->output_sleep, &pool->lock);
+
+		/* restore semaphore again */
+		if (file)
+			down(&semaphore_of(file));
+
+		/* interrupted? */
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			goto __error;
+		}
+	}
+	if (pool->closing) { /* closing.. */
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	cell = pool->free;
+	if (cell) {
+		int used;
+		pool->free = cell->next;
+		atomic_inc(&pool->counter);
+		used = atomic_read(&pool->counter);
+		if (pool->max_used < used)
+			pool->max_used = used;
+		pool->event_alloc_success++;
+		/* clear cell pointers */
+		cell->next = NULL;
+		err = 0;
+	} else
+		pool->event_alloc_failures++;
+	*cellp = cell;
+
+__error:
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return err;
+}
+
+
+/*
+ * duplicate the event to a cell.
+ * if the event has external data, the data is decomposed to additional
+ * cells.
+ */
+int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
+{
+	int ncells, err;
+	unsigned int extlen;
+	snd_seq_event_cell_t *cell;
+
+	*cellp = NULL;
+
+	ncells = 0;
+	extlen = 0;
+	if (snd_seq_ev_is_variable(event)) {
+		extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+		ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t);
+	}
+	if (ncells >= pool->total_elements)
+		return -ENOMEM;
+
+	err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+	if (err < 0)
+		return err;
+
+	/* copy the event */
+	cell->event = *event;
+
+	/* decompose */
+	if (snd_seq_ev_is_variable(event)) {
+		int len = extlen;
+		int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED;
+		int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR;
+		snd_seq_event_cell_t *src, *tmp, *tail;
+		char *buf;
+
+		cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED;
+		cell->event.data.ext.ptr = NULL;
+
+		src = (snd_seq_event_cell_t*)event->data.ext.ptr;
+		buf = (char *)event->data.ext.ptr;
+		tail = NULL;
+
+		while (ncells-- > 0) {
+			int size = sizeof(snd_seq_event_t);
+			if (len < size)
+				size = len;
+			err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+			if (err < 0)
+				goto __error;
+			if (cell->event.data.ext.ptr == NULL)
+				cell->event.data.ext.ptr = tmp;
+			if (tail)
+				tail->next = tmp;
+			tail = tmp;
+			/* copy chunk */
+			if (is_chained && src) {
+				tmp->event = src->event;
+				src = src->next;
+			} else if (is_usrptr) {
+				if (copy_from_user(&tmp->event, buf, size)) {
+					err = -EFAULT;
+					goto __error;
+				}
+			} else {
+				memcpy(&tmp->event, buf, size);
+			}
+			buf += size;
+			len -= size;
+		}
+	}
+
+	*cellp = cell;
+	return 0;
+
+__error:
+	snd_seq_cell_free(cell);
+	return err;
+}
+  
+
+/* poll wait */
+int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait)
+{
+	poll_wait(file, &pool->output_sleep, wait);
+	return snd_seq_output_ok(pool);
+}
+
+
+/* allocate room specified number of events */
+int snd_seq_pool_init(pool_t *pool)
+{
+	int cell;
+	snd_seq_event_cell_t *cellptr;
+	unsigned long flags;
+
+	snd_assert(pool != NULL, return -EINVAL);
+	if (pool->ptr)			/* should be atomic? */
+		return 0;
+
+	pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size);
+	if (pool->ptr == NULL) {
+		snd_printd("seq: malloc for sequencer events failed\n");
+		return -ENOMEM;
+	}
+
+	/* add new cells to the free cell list */
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->free = NULL;
+
+	for (cell = 0; cell < pool->size; cell++) {
+		cellptr = pool->ptr + cell;
+		cellptr->pool = pool;
+		cellptr->next = pool->free;
+		pool->free = cellptr;
+	}
+	pool->room = (pool->size + 1) / 2;
+
+	/* init statistics */
+	pool->max_used = 0;
+	pool->total_elements = pool->size;
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return 0;
+}
+
+/* remove events */
+int snd_seq_pool_done(pool_t *pool)
+{
+	unsigned long flags;
+	snd_seq_event_cell_t *ptr;
+	int max_count = 5 * HZ;
+
+	snd_assert(pool != NULL, return -EINVAL);
+
+	/* wait for closing all threads */
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->closing = 1;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	if (waitqueue_active(&pool->output_sleep))
+		wake_up(&pool->output_sleep);
+
+	while (atomic_read(&pool->counter) > 0) {
+		if (max_count == 0) {
+			snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		max_count--;
+	}
+	
+	/* release all resources */
+	spin_lock_irqsave(&pool->lock, flags);
+	ptr = pool->ptr;
+	pool->ptr = NULL;
+	pool->free = NULL;
+	pool->total_elements = 0;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	if (ptr)
+		vfree(ptr);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->closing = 0;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	return 0;
+}
+
+
+/* init new memory pool */
+pool_t *snd_seq_pool_new(int poolsize)
+{
+	pool_t *pool;
+
+	/* create pool block */
+	pool = snd_kcalloc(sizeof(pool_t), GFP_KERNEL);
+	if (pool == NULL) {
+		snd_printd("seq: malloc failed for pool\n");
+		return NULL;
+	}
+	spin_lock_init(&pool->lock);
+	pool->ptr = NULL;
+	pool->free = NULL;
+	pool->total_elements = 0;
+	atomic_set(&pool->counter, 0);
+	pool->closing = 0;
+	init_waitqueue_head(&pool->output_sleep);
+	
+	pool->size = poolsize;
+
+	/* init statistics */
+	pool->max_used = 0;
+	return pool;
+}
+
+/* remove memory pool */
+int snd_seq_pool_delete(pool_t **ppool)
+{
+	pool_t *pool = *ppool;
+
+	*ppool = NULL;
+	if (pool == NULL)
+		return 0;
+	snd_seq_pool_done(pool);
+	kfree(pool);
+	return 0;
+}
+
+/* initialize sequencer memory */
+int __init snd_sequencer_memory_init(void)
+{
+	return 0;
+}
+
+/* release sequencer memory */
+void __exit snd_sequencer_memory_done(void)
+{
+}
+
+
+/* exported to seq_clientmgr.c */
+void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space)
+{
+	if (pool == NULL)
+		return;
+	snd_iprintf(buffer, "%sPool size          : %d\n", space, pool->total_elements);
+	snd_iprintf(buffer, "%sCells in use       : %d\n", space, atomic_read(&pool->counter));
+	snd_iprintf(buffer, "%sPeak cells in use  : %d\n", space, pool->max_used);
+	snd_iprintf(buffer, "%sAlloc success      : %d\n", space, pool->event_alloc_success);
+	snd_iprintf(buffer, "%sAlloc failures     : %d\n", space, pool->event_alloc_failures);
+}
diff -Nru linux/sound/core/seq/seq_memory.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.h
--- linux/sound/core/seq/seq_memory.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,105 @@
+/*
+ *  ALSA sequencer Memory Manager
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_MEMORYMGR_H
+#define __SND_SEQ_MEMORYMGR_H
+
+#include <sound/seq_kernel.h>
+#include <linux/poll.h>
+
+typedef struct pool pool_t;
+
+/* container for sequencer event (internal use) */
+typedef struct snd_seq_event_cell_t {
+	snd_seq_event_t event;
+	pool_t *pool;				/* used pool */
+	struct snd_seq_event_cell_t *next;	/* next cell */
+} snd_seq_event_cell_t;
+
+/* design note: the pool is a contigious block of memory, if we dynamicly
+   want to add additional cells to the pool be better store this in another
+   pool as we need to know the base address of the pool when releasing
+   memory. */
+
+struct pool {
+	snd_seq_event_cell_t *ptr;	/* pointer to first event chunk */
+	snd_seq_event_cell_t *free;	/* pointer to the head of the free list */
+
+	int total_elements;	/* pool size actually allocated */
+	atomic_t counter;	/* cells free */
+
+	int size;		/* pool size to be allocated */
+	int room;		/* watermark for sleep/wakeup */
+
+	int closing;
+
+	/* statistics */
+	int max_used;
+	int event_alloc_nopool;
+	int event_alloc_failures;
+	int event_alloc_success;
+
+	/* Write locking */
+	wait_queue_head_t output_sleep;
+
+	/* Pool lock */
+	spinlock_t lock;
+};
+
+extern void snd_seq_cell_free(snd_seq_event_cell_t* cell);
+int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file);
+
+int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file);
+
+/* return number of unused (free) cells */
+static inline int snd_seq_unused_cells(pool_t *pool)
+{
+	return pool ? pool->total_elements - atomic_read(&pool->counter) : 0;
+}
+
+/* return total number of allocated cells */
+static inline int snd_seq_total_cells(pool_t *pool)
+{
+	return pool ? pool->total_elements : 0;
+}
+
+/* init pool - allocate events */
+int snd_seq_pool_init(pool_t *pool);
+
+/* done pool - free events */
+int snd_seq_pool_done(pool_t *pool);
+
+/* create pool */
+pool_t *snd_seq_pool_new(int poolsize);
+
+/* remove pool */
+int snd_seq_pool_delete(pool_t **pool);
+
+/* init memory */
+int snd_sequencer_memory_init(void);
+            
+/* release event memory */
+void snd_sequencer_memory_done(void);
+
+/* polling */
+int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait);
+
+
+#endif
diff -Nru linux/sound/core/seq/seq_midi.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi.c
--- linux/sound/core/seq/seq_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,461 @@
+/*
+ *   Generic MIDI synth driver for ALSA sequencer
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *                         Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+ 
+/* 
+Possible options for midisynth module:
+	- automatic opening of midi ports on first received event or subscription
+	  (close will be performed when client leaves)
+*/
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_device.h>
+#include <sound/seq_midi_event.h>
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Frank van de Pol <fvdpol@home.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+int output_buffer_size = PAGE_SIZE;
+MODULE_PARM(output_buffer_size, "i");
+MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
+int input_buffer_size = PAGE_SIZE;
+MODULE_PARM(input_buffer_size, "i");
+MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
+
+/* data for this midi synth driver */
+typedef struct {
+	snd_card_t *card;
+	int device;
+	int subdevice;
+	snd_rawmidi_file_t input_rfile;
+	snd_rawmidi_file_t output_rfile;
+	int seq_client;
+	int seq_port;
+	snd_midi_event_t *parser;
+} seq_midisynth_t;
+
+typedef struct {
+	int seq_client;
+	int num_ports;
+	int ports_per_device[SNDRV_RAWMIDI_DEVICES];
+ 	seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES];
+} seq_midisynth_client_t;
+
+static seq_midisynth_client_t *synths[SNDRV_CARDS];
+static DECLARE_MUTEX(register_mutex);
+
+/* handle rawmidi input event (MIDI v1.0 stream) */
+static void snd_midi_input_event(snd_rawmidi_substream_t * substream)
+{
+	snd_rawmidi_runtime_t *runtime;
+	seq_midisynth_t *msynth;
+	snd_seq_event_t ev;
+	char buf[16], *pbuf;
+	long res, count;
+
+	if (substream == NULL)
+		return;
+	runtime = substream->runtime;
+	msynth = (seq_midisynth_t *) runtime->private_data;
+	if (msynth == NULL)
+		return;
+	memset(&ev, 0, sizeof(ev));
+	while (runtime->avail > 0) {
+		res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
+		if (res <= 0)
+			continue;
+		if (msynth->parser == NULL)
+			continue;
+		pbuf = buf;
+		while (res > 0) {
+			count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
+			if (count < 0)
+				break;
+			pbuf += count;
+			res -= count;
+			if (ev.type != SNDRV_SEQ_EVENT_NONE) {
+				ev.source.port = msynth->seq_port;
+				ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+				snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
+				/* clear event and reset header */
+				memset(&ev, 0, sizeof(ev));
+			}
+		}
+	}
+}
+
+static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count)
+{
+	snd_rawmidi_runtime_t *runtime;
+	int tmp;
+
+	snd_assert(substream != NULL || buf != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	if ((tmp = runtime->avail) < count) {
+		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
+		return -ENOMEM;
+	}
+	if (snd_rawmidi_kernel_write(substream, buf, count) < count)
+		return -EINVAL;
+	return 0;
+}
+
+static int event_process_midi(snd_seq_event_t * ev, int direct,
+			      void *private_data, int atomic, int hop)
+{
+	seq_midisynth_t *msynth = (seq_midisynth_t *) private_data;
+	unsigned char msg[10];	/* buffer for constructing midi messages */
+	snd_rawmidi_substream_t *substream;
+	int res;
+
+	snd_assert(msynth != NULL, return -EINVAL);
+	substream = msynth->output_rfile.output;
+	if (substream == NULL)
+		return -ENODEV;
+	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {	/* special case, to save space */
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
+			/* invalid event */
+			snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
+			return 0;
+		}
+		res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
+		snd_midi_event_reset_decode(msynth->parser);
+		if (res < 0)
+			return res;
+	} else {
+		if (msynth->parser == NULL)
+			return -EIO;
+		res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
+		if (res < 0)
+			return res;
+		if ((res = dump_midi(substream, msg, res)) < 0) {
+			snd_midi_event_reset_decode(msynth->parser);
+			return res;
+		}
+	}
+	return 0;
+}
+
+
+static int snd_seq_midisynth_new(seq_midisynth_t *msynth,
+				 snd_card_t *card,
+				 int device,
+				 int subdevice)
+{
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
+		return -ENOMEM;
+	msynth->card = card;
+	msynth->device = device;
+	msynth->subdevice = subdevice;
+	return 0;
+}
+
+/* open associated midi device for input */
+static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	int err;
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+	snd_rawmidi_runtime_t *runtime;
+	snd_rawmidi_params_t params;
+
+	/* open midi port */
+	if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) {
+		snd_printd("midi input open failed!!!\n");
+		return err;
+	}
+	runtime = msynth->input_rfile.input->runtime;
+	memset(&params, 0, sizeof(params));
+	params.avail_min = 1;
+	params.buffer_size = input_buffer_size;
+	if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
+		snd_rawmidi_kernel_release(&msynth->input_rfile);
+		return err;
+	}
+	runtime->event = snd_midi_input_event;
+	runtime->private_data = msynth;
+	snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
+	return 0;
+}
+
+/* close associated midi device for input */
+static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	int err;
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+
+	snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
+	err = snd_rawmidi_kernel_release(&msynth->input_rfile);
+	return err;
+}
+
+/* open associated midi device for output */
+static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	int err;
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+	snd_rawmidi_params_t params;
+
+	/* open midi port */
+	if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) {
+		snd_printd("midi output open failed!!!\n");
+		return err;
+	}
+	memset(&params, 0, sizeof(params));
+	params.avail_min = 1;
+	params.buffer_size = output_buffer_size;
+	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
+		snd_rawmidi_kernel_release(&msynth->output_rfile);
+		return err;
+	}
+	return 0;
+}
+
+/* close associated midi device for output */
+static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+	unsigned char buf = 0xff; /* MIDI reset */
+
+	snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
+	/* sending single MIDI reset message to shut the device up */
+	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
+	snd_rawmidi_drain_output(msynth->output_rfile.output);
+	return snd_rawmidi_kernel_release(&msynth->output_rfile);
+}
+
+/* delete given midi synth port */
+static void snd_seq_midisynth_delete(seq_midisynth_t *msynth)
+{
+	snd_seq_port_info_t port;
+	
+	if (msynth == NULL)
+		return;
+
+	if (msynth->seq_client > 0) {
+		/* delete port */
+		memset(&port, 0, sizeof(port));
+		port.addr.client = msynth->seq_client;
+		port.addr.port = msynth->seq_port;
+		snd_seq_kernel_client_ctl(port.addr.client, SNDRV_SEQ_IOCTL_DELETE_PORT, &port);
+	}
+
+	if (msynth->parser)
+		snd_midi_event_free(msynth->parser);
+}
+
+/* register new midi synth port */
+int
+snd_seq_midisynth_register_port(snd_seq_device_t *dev)
+{
+	seq_midisynth_client_t *client;
+	seq_midisynth_t *msynth, *ms;
+	snd_seq_port_info_t port;
+	snd_rawmidi_info_t info;
+	int newclient = 0, p, ports;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_port_callback_t pcallbacks;
+	snd_seq_client_info_t inf;
+	snd_card_t *card = dev->card;
+	int device = dev->device;
+	unsigned int input_count = 0, output_count = 0;
+
+	snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
+	info.device = device;
+	info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+	info.subdevice = 0;
+	if (snd_rawmidi_info_select(card, &info) >= 0)
+		output_count = info.subdevices_count;
+	info.stream = SNDRV_RAWMIDI_STREAM_INPUT;
+	if (snd_rawmidi_info_select(card, &info) >= 0) {
+		input_count = info.subdevices_count;
+	}
+	ports = output_count;
+	if (ports < input_count)
+		ports = input_count;
+	if (ports == 0)
+		return -ENODEV;
+	if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
+		ports = 256 / SNDRV_RAWMIDI_DEVICES;
+
+	down(&register_mutex);
+	client = synths[card->number];
+	if (client == NULL) {
+		newclient = 1;
+		client = snd_kcalloc(sizeof(seq_midisynth_client_t), GFP_KERNEL);
+		if (client == NULL) {
+			up(&register_mutex);
+			return -ENOMEM;
+		}
+		memset(&callbacks, 0, sizeof(callbacks));
+		callbacks.private_data = client;
+		callbacks.allow_input = callbacks.allow_output = 1;
+		client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks);
+		if (client->seq_client < 0) {
+			kfree(client);
+			up(&register_mutex);
+			return -ENOMEM;
+		}
+		/* set our client name */
+		memset(&inf,0,sizeof(snd_seq_client_info_t));
+		inf.client = client->seq_client;
+		inf.type = KERNEL_CLIENT;
+		sprintf(inf.name, "External MIDI %i", card->number);
+		snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf);
+	}
+
+	msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL);
+	if (msynth == NULL)
+		goto __nomem;
+
+	for (p = 0; p < ports; p++) {
+		ms = &msynth[p];
+
+		if (snd_seq_midisynth_new(ms, card, device, p) < 0)
+			goto __nomem;
+
+		/* declare port */
+		memset(&port, 0, sizeof(port));
+		port.addr.client = client->seq_client;
+		port.addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
+		port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+		memset(&info, 0, sizeof(info));
+		info.device = device;
+		if (p < output_count)
+			info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+		else
+			info.stream = SNDRV_RAWMIDI_STREAM_INPUT;
+		info.subdevice = p;
+		if (snd_rawmidi_info_select(card, &info) >= 0)
+			strcpy(port.name, info.subname);
+		if (! port.name[0]) {
+			if (ports > 1)
+				sprintf(port.name, "MIDI %d-%d-%d", card->number, device, p);
+			else
+				sprintf(port.name, "MIDI %d-%d", card->number, device);
+		}
+		if ((info.flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
+			port.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+		if ((info.flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
+			port.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+		if ((port.capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
+		    info.flags & SNDRV_RAWMIDI_INFO_DUPLEX)
+			port.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+		port.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+		port.midi_channels = 16;
+		memset(&pcallbacks, 0, sizeof(pcallbacks));
+		pcallbacks.owner = THIS_MODULE;
+		pcallbacks.private_data = ms;
+		pcallbacks.subscribe = midisynth_subscribe;
+		pcallbacks.unsubscribe = midisynth_unsubscribe;
+		pcallbacks.use = midisynth_use;
+		pcallbacks.unuse = midisynth_unuse;
+		pcallbacks.event_input = event_process_midi;
+		port.kernel = &pcallbacks;
+		if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &port)<0)
+			goto __nomem;
+		ms->seq_client = client->seq_client;
+		ms->seq_port = port.addr.port;
+	}
+	client->ports_per_device[device] = ports;
+	client->ports[device] = msynth;
+	client->num_ports++;
+	if (newclient)
+		synths[card->number] = client;
+	up(&register_mutex);
+	return 0;	/* success */
+
+      __nomem:
+	if (msynth != NULL) {
+	      	for (p = 0; p < ports; p++)
+	      		snd_seq_midisynth_delete(&msynth[p]);
+		kfree(msynth);
+	}
+	if (newclient) {
+		snd_seq_delete_kernel_client(client->seq_client);
+		kfree(client);
+	}
+	up(&register_mutex);
+	return -ENOMEM;
+}
+
+/* release midi synth port */
+int
+snd_seq_midisynth_unregister_port(snd_seq_device_t *dev)
+{
+	seq_midisynth_client_t *client;
+	seq_midisynth_t *msynth;
+	snd_card_t *card = dev->card;
+	int device = dev->device, p, ports;
+	
+	down(&register_mutex);
+	client = synths[card->number];
+	if (client == NULL || client->ports[device] == NULL) {
+		up(&register_mutex);
+		return -ENODEV;
+	}
+	snd_seq_event_port_detach(client->seq_client, client->ports[device]->seq_port);
+	ports = client->ports_per_device[device];
+	client->ports_per_device[device] = 0;
+	msynth = client->ports[device];
+	client->ports[device] = NULL;
+	snd_runtime_check(msynth != NULL || ports <= 0, goto __skip);
+	for (p = 0; p < ports; p++)
+		snd_seq_midisynth_delete(&msynth[p]);
+	kfree(msynth);
+      __skip:
+	client->num_ports--;
+	if (client->num_ports <= 0) {
+		snd_seq_delete_kernel_client(client->seq_client);
+		synths[card->number] = NULL;
+		kfree(client);
+	}
+	up(&register_mutex);
+	return 0;
+}
+
+
+static int __init alsa_seq_midi_init(void)
+{
+	static snd_seq_dev_ops_t ops = {
+		snd_seq_midisynth_register_port,
+		snd_seq_midisynth_unregister_port,
+	};
+	memset(&synths, 0, sizeof(synths));
+	snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
+	return 0;
+}
+
+static void __exit alsa_seq_midi_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
+}
+
+module_init(alsa_seq_midi_init)
+module_exit(alsa_seq_midi_exit)
diff -Nru linux/sound/core/seq/seq_midi_clock.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_clock.c
--- linux/sound/core/seq/seq_midi_clock.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_clock.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,91 @@
+/*
+ *  MIDI clock event converter
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include "seq_queue.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+typedef struct midi_clock {
+	unsigned int cur_pos;
+} midi_clock_t;
+
+static int midi_open(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	midi_clock_t *arg;
+
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	sync_info->param.tick.ppq = 24;
+	sync_info->param.tick.ticks = 1;
+	arg->cur_pos = 0;
+	*retp = arg;
+	return 0;
+}
+
+static int midi_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_SYNC:
+		ev->type = SNDRV_SEQ_EVENT_CLOCK;
+		return 1;
+	case SNDRV_SEQ_EVENT_SYNC_POS:
+		ev->type = SNDRV_SEQ_EVENT_SONGPOS;
+		ev->data.control.value = src->data.queue.param.position / 6;
+		return 1;
+	}
+	return 0;
+}
+
+static int midi_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	midi_clock_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_CLOCK:
+		ev->type = SNDRV_SEQ_EVENT_SYNC;
+		ev->data.queue.param.position = arg->cur_pos;
+		arg->cur_pos++;
+		return 1;
+	case SNDRV_SEQ_EVENT_SONGPOS:
+		ev->type = SNDRV_SEQ_EVENT_SYNC_POS;
+		arg->cur_pos = src->data.control.value * 6;
+		ev->data.queue.param.position = arg->cur_pos;
+		return 1;
+	}
+	return 0;
+}
+
+/* exported */
+seq_sync_parser_t snd_seq_midi_clock_parser = {
+	format: SNDRV_SEQ_SYNC_FMT_MIDI_CLOCK,
+	in: {
+		open: midi_open,
+		sync: midi_sync_in,
+	},
+	out: {
+		open: midi_open,
+		sync: midi_sync_out,
+	},
+};
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
diff -Nru linux/sound/core/seq/seq_midi_emul.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_emul.c
--- linux/sound/core/seq/seq_midi_emul.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_emul.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,739 @@
+/*
+ *  GM/GS/XG midi module.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *  Based on awe_wave.c by Takashi Iwai
+ *
+ *   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 module is used to keep track of the current midi state.
+ * It can be used for drivers that are required to emulate midi when
+ * the hardware doesn't.
+ *
+ * It was written for a AWE64 driver, but there should be no AWE specific
+ * code in here.  If there is it should be reported as a bug.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_emul.h>
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+
+/* Prototypes for static functions */
+static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel);
+static void do_control(snd_midi_op_t *ops, void *private,
+		       snd_midi_channel_set_t *chset, snd_midi_channel_t *chan,
+		       int control, int value);
+static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset);
+static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
+static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
+void snd_midi_reset_controllers(snd_midi_channel_t *chan);
+static void reset_all_channels(snd_midi_channel_set_t *chset);
+
+
+/*
+ * Process an event in a driver independant way.  This means dealing
+ * with RPN, NRPN, SysEx etc that are defined for common midi applications
+ * such as GM, GS and XG.
+ * There modes that this module will run in are:
+ *   Generic MIDI - no interpretation at all, it will just save current values
+ *                  of controlers etc.
+ *   GM - You can use all gm_ prefixed elements of chan.  Controls, RPN, NRPN,
+ *        SysEx will be interpreded as defined in General Midi.
+ *   GS - You can use all gs_ prefixed elements of chan. Codes for GS will be
+ *        interpreted.
+ *   XG - You can use all xg_ prefixed elements of chan.  Codes for XG will
+ *        be interpreted.
+ */
+void
+snd_midi_process_event(snd_midi_op_t *ops,
+		       snd_seq_event_t *ev, snd_midi_channel_set_t *chanset)
+{
+	snd_midi_channel_t *chan;
+	void *drv;
+	int dest_channel = 0;
+
+	if (ev == NULL || chanset == NULL) {
+		snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
+		return;
+	}
+	if (chanset->channels == NULL)
+		return;
+
+	if (snd_seq_ev_is_channel_type(ev)) {
+		dest_channel = ev->data.note.channel;
+		if (dest_channel >= chanset->max_channels) {
+			snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels);
+			return;
+		}
+	}
+
+	chan = chanset->channels + dest_channel;
+	drv  = chanset->private_data;
+
+	/* EVENT_NOTE should be processed before queued */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTE)
+		return;
+
+	/* Make sure that we don't have a note on that should really be
+	 * a note off */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
+		ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
+
+	/* Make sure the note is within array range */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTEON ||
+	    ev->type == SNDRV_SEQ_EVENT_NOTEOFF ||
+	    ev->type == SNDRV_SEQ_EVENT_KEYPRESS) {
+		if (ev->data.note.note >= 128)
+			return;
+	}
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+		if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) {
+			if (ops->note_off)
+				ops->note_off(drv, ev->data.note.note, 0, chan);
+		}
+		chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON;
+		if (ops->note_on)
+			ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan);
+		break;
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+		if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON))
+			break;
+		if (ops->note_off)
+			note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity);
+		break;
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		if (ops->key_press)
+			ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+		do_control(ops, drv, chanset, chan,
+			   ev->data.control.param, ev->data.control.value);
+		break;
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+		chan->midi_program = ev->data.control.value;
+		break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		chan->midi_pitchbend = ev->data.control.value;
+		if (ops->control)
+			ops->control(drv, MIDI_CTL_PITCHBEND, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		chan->midi_pressure = ev->data.control.value;
+		if (ops->control)
+			ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROL14:
+		/* Best guess is that this is any of the 14 bit controller values */
+		if (ev->data.control.param < 32) {
+			/* set low part first */
+			chan->control[ev->data.control.param + 32] =
+				ev->data.control.value & 0x7f;
+			do_control(ops, drv, chanset, chan,
+				   ev->data.control.param,
+				   ((ev->data.control.value>>7) & 0x7f));
+		} else
+			do_control(ops, drv, chanset, chan,
+				   ev->data.control.param,
+				   ev->data.control.value);
+		break;
+	case SNDRV_SEQ_EVENT_NONREGPARAM:
+		/* Break it back into its controler values */
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
+		chan->control[MIDI_CTL_MSB_DATA_ENTRY]
+			= (ev->data.control.value >> 7) & 0x7f;
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY]
+			= ev->data.control.value & 0x7f;
+		chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB]
+			= (ev->data.control.param >> 7) & 0x7f;
+		chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB]
+			= ev->data.control.param & 0x7f;
+		nrpn(ops, drv, chan, chanset);
+		break;
+	case SNDRV_SEQ_EVENT_REGPARAM:
+		/* Break it back into its controler values */
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
+		chan->control[MIDI_CTL_MSB_DATA_ENTRY]
+			= (ev->data.control.value >> 7) & 0x7f;
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY]
+			= ev->data.control.value & 0x7f;
+		chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB]
+			= (ev->data.control.param >> 7) & 0x7f;
+		chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]
+			= ev->data.control.param & 0x7f;
+		rpn(ops, drv, chan, chanset);
+		break;
+	case SNDRV_SEQ_EVENT_SYSEX:
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
+			unsigned char sysexbuf[64];
+			int len;
+			len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0);
+			if (len > 0)
+				sysex(ops, drv, sysexbuf, len, chanset);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_SONGPOS:
+	case SNDRV_SEQ_EVENT_SONGSEL:
+	case SNDRV_SEQ_EVENT_CLOCK:
+	case SNDRV_SEQ_EVENT_START:
+	case SNDRV_SEQ_EVENT_CONTINUE:
+	case SNDRV_SEQ_EVENT_STOP:
+	case SNDRV_SEQ_EVENT_QFRAME:
+	case SNDRV_SEQ_EVENT_TEMPO:
+	case SNDRV_SEQ_EVENT_TIMESIGN:
+	case SNDRV_SEQ_EVENT_KEYSIGN:
+		goto not_yet;
+	case SNDRV_SEQ_EVENT_SENSING:
+		break;
+	case SNDRV_SEQ_EVENT_CLIENT_START:
+	case SNDRV_SEQ_EVENT_CLIENT_EXIT:
+	case SNDRV_SEQ_EVENT_CLIENT_CHANGE:
+	case SNDRV_SEQ_EVENT_PORT_START:
+	case SNDRV_SEQ_EVENT_PORT_EXIT:
+	case SNDRV_SEQ_EVENT_PORT_CHANGE:
+	case SNDRV_SEQ_EVENT_SAMPLE:
+	case SNDRV_SEQ_EVENT_SAMPLE_START:
+	case SNDRV_SEQ_EVENT_SAMPLE_STOP:
+	case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
+	case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
+	case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
+	case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
+	case SNDRV_SEQ_EVENT_ECHO:
+	not_yet:
+	default:
+		/*snd_printd("Unimplemented event %d\n", ev->type);*/
+		break;
+	}
+}
+
+
+/*
+ * release note
+ */
+static void
+note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel)
+{
+	if (chan->gm_hold) {
+		/* Hold this note until pedal is turned off */
+		chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
+	} else if (chan->note[note] & SNDRV_MIDI_NOTE_SUSTENUTO) {
+		/* Mark this note as release; it will be turned off when sustenuto
+		 * is turned off */
+		chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
+	} else {
+		chan->note[note] = 0;
+		if (ops->note_off)
+			ops->note_off(drv, note, vel, chan);
+	}
+}
+
+/*
+ * Do all driver independant operations for this controler and pass
+ * events that need to take place immediately to the driver.
+ */
+static void
+do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset,
+	   snd_midi_channel_t *chan, int control, int value)
+{
+	int  i;
+
+	/* Switches */
+	if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
+		/* These are all switches; either off or on so set to 0 or 127 */
+		value = (value >= 64)? 127: 0;
+	}
+	chan->control[control] = value;
+
+	switch (control) {
+	case MIDI_CTL_SUSTAIN:
+		if (value == 0) {
+			/* Sustain has been released, turn off held notes */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
+					chan->note[i] = SNDRV_MIDI_NOTE_OFF;
+					if (ops->note_off)
+						ops->note_off(drv, i, 0, chan);
+				}
+			}
+		}
+		break;
+	case MIDI_CTL_PORTAMENTO:
+		break;
+	case MIDI_CTL_SUSTENUTO:
+		if (value) {
+			/* Mark each note that is currently held down */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_ON)
+					chan->note[i] |= SNDRV_MIDI_NOTE_SUSTENUTO;
+			}
+		} else {
+			/* release all notes that were held */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_SUSTENUTO) {
+					chan->note[i] &= ~SNDRV_MIDI_NOTE_SUSTENUTO;
+					if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
+						chan->note[i] = SNDRV_MIDI_NOTE_OFF;
+						if (ops->note_off)
+							ops->note_off(drv, i, 0, chan);
+					}
+				}
+			}
+		}
+		break;
+	case MIDI_CTL_MSB_DATA_ENTRY:
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
+		/* go through here */
+	case MIDI_CTL_LSB_DATA_ENTRY:
+		if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
+			rpn(ops, drv, chan, chset);
+		else
+			nrpn(ops, drv, chan, chset);
+		break;
+	case MIDI_CTL_REGIST_PARM_NUM_LSB:
+	case MIDI_CTL_REGIST_PARM_NUM_MSB:
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
+		break;
+	case MIDI_CTL_NONREG_PARM_NUM_LSB:
+	case MIDI_CTL_NONREG_PARM_NUM_MSB:
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
+		break;
+
+	case MIDI_CTL_ALL_SOUNDS_OFF:
+		all_sounds_off(ops, drv, chan);
+		break;
+
+	case MIDI_CTL_ALL_NOTES_OFF:
+		all_notes_off(ops, drv, chan);
+		break;
+
+	case MIDI_CTL_MSB_BANK:
+		if (chset->midi_mode == SNDRV_MIDI_MODE_XG) {
+			if (value == 127)
+				chan->drum_channel = 1;
+			else
+				chan->drum_channel = 0;
+		}
+		break;
+	case MIDI_CTL_LSB_BANK:
+		break;
+
+	case MIDI_CTL_RESET_CONTROLLERS:
+		snd_midi_reset_controllers(chan);
+		break;
+
+	case MIDI_CTL_SOFT_PEDAL:
+	case MIDI_CTL_LEGATO_FOOTSWITCH:
+	case MIDI_CTL_HOLD2:
+	case MIDI_CTL_SC1_SOUND_VARIATION:
+	case MIDI_CTL_SC2_TIMBRE:
+	case MIDI_CTL_SC3_RELEASE_TIME:
+	case MIDI_CTL_SC4_ATTACK_TIME:
+	case MIDI_CTL_SC5_BRIGHTNESS:
+	case MIDI_CTL_E1_REVERB_DEPTH:
+	case MIDI_CTL_E2_TREMOLO_DEPTH:
+	case MIDI_CTL_E3_CHORUS_DEPTH:
+	case MIDI_CTL_E4_DETUNE_DEPTH:
+	case MIDI_CTL_E5_PHASER_DEPTH:
+		goto notyet;
+	notyet:
+	default:
+		if (ops->control)
+			ops->control(drv, control, chan);
+		break;
+	}
+}
+
+
+/*
+ * intialize the MIDI status
+ */
+void
+snd_midi_channel_set_clear(snd_midi_channel_set_t *chset)
+{
+	int i;
+
+	chset->midi_mode = SNDRV_MIDI_MODE_GM;
+	chset->gs_master_volume = 127;
+
+	for (i = 0; i < chset->max_channels; i++) {
+		snd_midi_channel_t *chan = chset->channels + i;
+		memset(chan->note, 0, sizeof(chan->note));
+
+		chan->midi_aftertouch = 0;
+		chan->midi_pressure = 0;
+		chan->midi_program = 0;
+		chan->midi_pitchbend = 0;
+		snd_midi_reset_controllers(chan);
+		chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+		chan->gm_rpn_fine_tuning = 0;
+		chan->gm_rpn_coarse_tuning = 0;
+
+		if (i == 9)
+			chan->drum_channel = 1;
+		else
+			chan->drum_channel = 0;
+	}
+}
+
+/*
+ * Process a rpn message.
+ */
+static void
+rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
+    snd_midi_channel_set_t *chset)
+{
+	int type;
+	int val;
+
+	if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) {
+		type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) |
+			chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB];
+		val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
+			chan->control[MIDI_CTL_LSB_DATA_ENTRY];
+
+		switch (type) {
+		case 0x0000: /* Pitch bend sensitivity */
+			/* MSB only / 1 semitone per 128 */
+			chan->gm_rpn_pitch_bend_range = val;
+			break;
+					
+		case 0x0001: /* fine tuning: */
+			/* MSB/LSB, 8192=center, 100/8192 cent step */
+			chan->gm_rpn_fine_tuning = val - 8192;
+			break;
+
+		case 0x0002: /* coarse tuning */
+			/* MSB only / 8192=center, 1 semitone per 128 */
+			chan->gm_rpn_coarse_tuning = val - 8192;
+			break;
+
+		case 0x7F7F: /* "lock-in" RPN */
+			/* ignored */
+			break;
+		}
+	}
+	/* should call nrpn or rpn callback here.. */
+}
+
+/*
+ * Process an nrpn message.
+ */
+static void
+nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
+     snd_midi_channel_set_t *chset)
+{
+	/* parse XG NRPNs here if possible */
+	if (ops->nrpn)
+		ops->nrpn(drv, chan, chset);
+}
+
+
+/*
+ * convert channel parameter in GS sysex
+ */
+static int
+get_channel(unsigned char cmd)
+{
+	int p = cmd & 0x0f;
+	if (p == 0)
+		p = 9;
+	else if (p < 10)
+		p--;
+	return p;
+}
+
+
+/*
+ * Process a sysex message.
+ */
+static void
+sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset)
+{
+	/* GM on */
+	static unsigned char gm_on_macro[] = {
+		0x7e,0x7f,0x09,0x01,
+	};
+	/* XG on */
+	static unsigned char xg_on_macro[] = {
+		0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
+	};
+	/* GS prefix
+	 * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
+	 * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
+	 * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
+	 * master vol:  XX=0x00, YY=0x04, ZZ=0-127
+	 */
+	static unsigned char gs_pfx_macro[] = {
+		0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
+	};
+
+	int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED;
+
+	if (len <= 0 || buf[0] != 0xf0)
+		return;
+	/* skip first byte */
+	buf++;
+	len--;
+
+	/* GM on */
+	if (len >= sizeof(gm_on_macro) &&
+	    memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
+		if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
+		    chset->midi_mode != SNDRV_MIDI_MODE_XG) {
+			chset->midi_mode = SNDRV_MIDI_MODE_GM;
+			reset_all_channels(chset);
+			parsed = SNDRV_MIDI_SYSEX_GM_ON;
+		}
+	}
+
+	/* GS macros */
+	else if (len >= 8 &&
+		 memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
+		if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
+		    chset->midi_mode != SNDRV_MIDI_MODE_XG)
+			chset->midi_mode = SNDRV_MIDI_MODE_GS;
+
+		if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
+			/* GS reset */
+			parsed = SNDRV_MIDI_SYSEX_GS_RESET;
+			reset_all_channels(chset);
+		}
+
+		else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
+			/* drum pattern */
+			int p = get_channel(buf[5]);
+			if (p < chset->max_channels) {
+				parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
+				if (buf[7])
+					chset->channels[p].drum_channel = 1;
+				else
+					chset->channels[p].drum_channel = 0;
+			}
+
+		} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
+			/* program */
+			int p = get_channel(buf[5]);
+			if (p < chset->max_channels &&
+			    ! chset->channels[p].drum_channel) {
+				parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
+				chset->channels[p].midi_program = buf[7];
+			}
+
+		} else if (buf[5] == 0x01 && buf[6] == 0x30) {
+			/* reverb mode */
+			parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE;
+			chset->gs_reverb_mode = buf[7];
+
+		} else if (buf[5] == 0x01 && buf[6] == 0x38) {
+			/* chorus mode */
+			parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE;
+			chset->gs_chorus_mode = buf[7];
+
+		} else if (buf[5] == 0x00 && buf[6] == 0x04) {
+			/* master volume */
+			parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE;
+			chset->gs_master_volume = buf[7];
+
+		}
+	}
+
+	/* XG on */
+	else if (len >= sizeof(xg_on_macro) &&
+		 memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
+		int i;
+		chset->midi_mode = SNDRV_MIDI_MODE_XG;
+		parsed = SNDRV_MIDI_SYSEX_XG_ON;
+		/* reset CC#0 for drums */
+		for (i = 0; i < chset->max_channels; i++) {
+			if (chset->channels[i].drum_channel)
+				chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127;
+			else
+				chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0;
+		}
+	}
+
+	if (ops->sysex)
+		ops->sysex(private, buf - 1, len + 1, parsed, chset);
+}
+
+/*
+ * all sound off
+ */
+static void
+all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
+{
+	int n;
+
+	if (! ops->note_terminate)
+		return;
+	for (n = 0; n < 128; n++) {
+		if (chan->note[n]) {
+			ops->note_terminate(drv, n, chan);
+			chan->note[n] = 0;
+		}
+	}
+}
+
+/*
+ * all notes off
+ */
+static void
+all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
+{
+	int n;
+
+	if (! ops->note_off)
+		return;
+	for (n = 0; n < 128; n++) {
+		if (chan->note[n] == SNDRV_MIDI_NOTE_ON)
+			note_off(ops, drv, chan, n, 0);
+	}
+}
+
+/*
+ * Initialise a single midi channel control block.
+ */
+void snd_midi_channel_init(snd_midi_channel_t *p, int n)
+{
+	if (p == NULL)
+		return;
+
+	memset(p, 0, sizeof(snd_midi_channel_t));
+	p->private = NULL;
+	p->number = n;
+
+	snd_midi_reset_controllers(p);
+	p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+	p->gm_rpn_fine_tuning = 0;
+	p->gm_rpn_coarse_tuning = 0;
+
+	if (n == 9)
+		p->drum_channel = 1;	/* Default ch 10 as drums */
+}
+
+/*
+ * Allocate and initialise a set of midi channel control blocks.
+ */
+snd_midi_channel_t *snd_midi_channel_init_set(int n)
+{
+	snd_midi_channel_t *chan;
+	int  i;
+
+	chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL);
+	if (chan) {
+		for (i = 0; i < n; i++)
+			snd_midi_channel_init(chan+i, i);
+	}
+
+	return chan;
+}
+
+/*
+ * reset all midi channels
+ */
+static void
+reset_all_channels(snd_midi_channel_set_t *chset)
+{
+	int ch;
+	for (ch = 0; ch < chset->max_channels; ch++) {
+		snd_midi_channel_t *chan = chset->channels + ch;
+		snd_midi_reset_controllers(chan);
+		chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+		chan->gm_rpn_fine_tuning = 0;
+		chan->gm_rpn_coarse_tuning = 0;
+
+		if (ch == 9)
+			chan->drum_channel = 1;
+		else
+			chan->drum_channel = 0;
+	}
+}
+
+
+/*
+ * Allocate and initialise a midi channel set.
+ */
+snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n)
+{
+	snd_midi_channel_set_t *chset;
+
+	chset = kmalloc(sizeof(*chset), GFP_KERNEL);
+	if (chset) {
+		chset->channels = snd_midi_channel_init_set(n);
+		chset->private_data = NULL;
+		chset->max_channels = n;
+	}
+	return chset;
+}
+
+/*
+ * Reset the midi controllers on a particular channel to default values.
+ */
+void snd_midi_reset_controllers(snd_midi_channel_t *chan)
+{
+	memset(chan->control, 0, sizeof(chan->control));
+	chan->gm_volume = 127;
+	chan->gm_expression = 127;
+	chan->gm_pan = 64;
+}
+
+
+/*
+ * Free a midi channel set.
+ */
+void snd_midi_channel_free_set(snd_midi_channel_set_t *chset)
+{
+	if (chset == NULL)
+		return;
+	if (chset->channels != NULL)
+		kfree(chset->channels);
+	kfree(chset);
+}
+
+static int __init alsa_seq_midi_emul_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_midi_emul_exit(void)
+{
+}
+
+module_init(alsa_seq_midi_emul_init)
+module_exit(alsa_seq_midi_emul_exit)
+
+EXPORT_SYMBOL(snd_midi_process_event);
+EXPORT_SYMBOL(snd_midi_channel_set_clear);
+EXPORT_SYMBOL(snd_midi_channel_init);
+EXPORT_SYMBOL(snd_midi_channel_init_set);
+EXPORT_SYMBOL(snd_midi_channel_alloc_set);
+EXPORT_SYMBOL(snd_midi_channel_free_set);
diff -Nru linux/sound/core/seq/seq_midi_event.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_event.c
--- linux/sound/core/seq/seq_midi_event.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_event.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,469 @@
+/*
+ *  MIDI byte <-> sequencer event coder
+ *
+ *  Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_event.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
+MODULE_LICENSE("GPL");
+
+/* queue type */
+/* from 0 to 7 are normal commands (note off, on, etc.) */
+#define ST_NOTEOFF	0
+#define ST_NOTEON	1
+#define ST_SPECIAL	8
+#define ST_SYSEX	ST_SPECIAL
+/* from 8 to 15 are events for 0xf0-0xf7 */
+
+
+/* status event types */
+typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev);
+typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf);
+
+/*
+ * prototypes
+ */
+static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void note_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf);
+
+/*
+ * event list
+ */
+static struct status_event_list_t {
+	int event;
+	int qlen;
+	event_encode_t encode;
+	event_decode_t decode;
+} status_event[] = {
+	/* 0x80 - 0xf0 */
+	{SNDRV_SEQ_EVENT_NOTEOFF,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_NOTEON,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_KEYPRESS,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_CONTROLLER,	2, two_param_ctrl_event, two_param_decode},
+	{SNDRV_SEQ_EVENT_PGMCHANGE,	1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_CHANPRESS,	1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_PITCHBEND,	2, pitchbend_ctrl_event, pitchbend_decode},
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf0 */
+	/* 0xf0 - 0xff */
+	{SNDRV_SEQ_EVENT_SYSEX,		1, NULL, NULL}, /* sysex: 0xf0 */
+	{SNDRV_SEQ_EVENT_QFRAME,	1, one_param_event, one_param_decode}, /* 0xf1 */
+	{SNDRV_SEQ_EVENT_SONGPOS,	2, songpos_event, songpos_decode}, /* 0xf2 */
+	{SNDRV_SEQ_EVENT_SONGSEL,	1, one_param_event, one_param_decode}, /* 0xf3 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf4 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf5 */
+	{SNDRV_SEQ_EVENT_TUNE_REQUEST,	0, NULL, NULL},	/* 0xf6 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf7 */
+	{SNDRV_SEQ_EVENT_CLOCK,		0, NULL, NULL}, /* 0xf8 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf9 */
+	{SNDRV_SEQ_EVENT_START,		0, NULL, NULL}, /* 0xfa */
+	{SNDRV_SEQ_EVENT_CONTINUE,	0, NULL, NULL}, /* 0xfb */
+	{SNDRV_SEQ_EVENT_STOP, 		0, NULL, NULL}, /* 0xfc */
+	{SNDRV_SEQ_EVENT_NONE, 		0, NULL, NULL}, /* 0xfd */
+	{SNDRV_SEQ_EVENT_SENSING, 	0, NULL, NULL}, /* 0xfe */
+	{SNDRV_SEQ_EVENT_RESET, 	0, NULL, NULL}, /* 0xff */
+};
+
+static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
+
+static struct extra_event_list_t {
+	int event;
+	int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
+} extra_event[] = {
+	{SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14},
+	/*{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_nrpn},*/
+	/*{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_rpn},*/
+};
+
+#define numberof(ary)	(sizeof(ary)/sizeof(ary[0]))
+
+/*
+ *  new/delete record
+ */
+
+int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev)
+{
+	snd_midi_event_t *dev;
+
+	*rdev = NULL;
+	dev = (snd_midi_event_t *)snd_kcalloc(sizeof(snd_midi_event_t), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	if (bufsize > 0) {
+		dev->buf = kmalloc(bufsize, GFP_KERNEL);
+		if (dev->buf == NULL) {
+			kfree(dev);
+			return -ENOMEM;
+		}
+	}
+	dev->bufsize = bufsize;
+	dev->lastcmd = 0xff;
+	spin_lock_init(&dev->lock);
+	*rdev = dev;
+	return 0;
+}
+
+void snd_midi_event_free(snd_midi_event_t *dev)
+{
+	if (dev != NULL) {
+		if (dev->buf)
+			kfree(dev->buf);
+		kfree(dev);
+	}
+}
+
+/*
+ * initialize record
+ */
+inline static void reset_encode(snd_midi_event_t *dev)
+{
+	dev->read = 0;
+	dev->qlen = 0;
+	dev->type = 0;
+}
+
+void snd_midi_event_reset_encode(snd_midi_event_t *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	reset_encode(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+void snd_midi_event_reset_decode(snd_midi_event_t *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->lastcmd = 0xff;
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+void snd_midi_event_init(snd_midi_event_t *dev)
+{
+	snd_midi_event_reset_encode(dev);
+	snd_midi_event_reset_decode(dev);
+}
+
+/*
+ * resize buffer
+ */
+int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
+{
+	unsigned char *new_buf, *old_buf;
+	unsigned long flags;
+
+	if (bufsize == dev->bufsize)
+		return 0;
+	new_buf = kmalloc(bufsize, GFP_KERNEL);
+	if (new_buf == NULL)
+		return -ENOMEM;
+	spin_lock_irqsave(&dev->lock, flags);
+	old_buf = dev->buf;
+	dev->buf = new_buf;
+	dev->bufsize = bufsize;
+	reset_encode(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	if (old_buf)
+		kfree(old_buf);
+	return 0;
+}
+
+/*
+ *  read bytes and encode to sequencer event if finished
+ *  return the size of encoded bytes
+ */
+long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
+{
+	long result = 0;
+	int rc;
+
+	ev->type = SNDRV_SEQ_EVENT_NONE;
+
+	while (count-- > 0) {
+		rc = snd_midi_event_encode_byte(dev, *buf++, ev);
+		result++;
+		if (rc < 0)
+			return rc;
+		else if (rc > 0)
+			return result;
+	}
+
+	return result;
+}
+
+/*
+ *  read one byte and encode to sequencer event:
+ *  return 1 if MIDI bytes are encoded to an event
+ *         0 data is not finished
+ *         negative for error
+ */
+int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	c &= 0xff;
+
+	if (c >= MIDI_CMD_COMMON_CLOCK) {
+		/* real-time event */
+		ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		return 1;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->qlen > 0) {
+		/* rest of command */
+		dev->buf[dev->read++] = c;
+		if (dev->type != ST_SYSEX)
+			dev->qlen--;
+	} else {
+		/* new command */
+		dev->read = 1;
+		if (c & 0x80) {
+			dev->buf[0] = c;
+			if ((c & 0xf0) == 0xf0) /* special events */
+				dev->type = (c & 0x0f) + ST_SPECIAL;
+			else
+				dev->type = (c >> 4) & 0x07;
+			dev->qlen = status_event[dev->type].qlen;
+		} else {
+			/* process this byte as argument */
+			dev->buf[dev->read++] = c;
+			dev->qlen = status_event[dev->type].qlen - 1;
+		}
+	}
+	if (dev->qlen == 0) {
+		ev->type = status_event[dev->type].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		if (status_event[dev->type].encode) /* set data values */
+			status_event[dev->type].encode(dev, ev);
+		rc = 1;
+	} else 	if (dev->type == ST_SYSEX) {
+		if (c == MIDI_CMD_COMMON_SYSEX_END ||
+		    dev->read >= dev->bufsize) {
+			ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+			ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+			ev->type = SNDRV_SEQ_EVENT_SYSEX;
+			ev->data.ext.len = dev->read;
+			ev->data.ext.ptr = dev->buf;
+			if (c != MIDI_CMD_COMMON_SYSEX_END)
+				dev->read = 0; /* continue to parse */
+			else
+				reset_encode(dev); /* all parsed */
+			rc = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return rc;
+}
+
+/* encode note event */
+static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.note.channel = dev->buf[0] & 0x0f;
+	ev->data.note.note = dev->buf[1];
+	ev->data.note.velocity = dev->buf[2];
+}
+
+/* encode one parameter controls */
+static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode pitch wheel change */
+static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192;
+}
+
+/* encode midi control change */
+static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.param = dev->buf[1];
+	ev->data.control.value = dev->buf[2];
+}
+
+/* encode one parameter value*/
+static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode song position */
+static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1];
+}
+
+/*
+ * decode from a sequencer event to midi bytes
+ * return the size of decoded midi events
+ */
+long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
+{
+	int cmd, type;
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return -ENOENT;
+
+	for (type = 0; type < numberof(status_event); type++) {
+		if (ev->type == status_event[type].event)
+			goto __found;
+	}
+	for (type = 0; type < numberof(extra_event); type++) {
+		if (ev->type == extra_event[type].event)
+			return extra_event[type].decode(dev, buf, count, ev);
+	}
+	return -ENOENT;
+
+      __found:
+	if (type >= ST_SPECIAL)
+		cmd = 0xf0 + (type - ST_SPECIAL);
+	else
+		/* data.note.channel and data.control.channel is identical */
+		cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f);
+
+
+	if (cmd == MIDI_CMD_COMMON_SYSEX) {
+		snd_midi_event_reset_decode(dev);
+		return snd_seq_expand_var_event(ev, count, buf, 1, 0);
+	} else {
+		int qlen;
+		unsigned char xbuf[4];
+		unsigned long flags;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd) {
+			dev->lastcmd = cmd;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			xbuf[0] = cmd;
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 1);
+			qlen = status_event[type].qlen + 1;
+		} else {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 0);
+			qlen = status_event[type].qlen;
+		}
+		if (count < qlen)
+			return -ENOMEM;
+		memcpy(buf, xbuf, qlen);
+		return qlen;
+	}
+}
+
+
+/* decode note event */
+static void note_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.note.note & 0x7f;
+	buf[1] = ev->data.note.velocity & 0x7f;
+}
+
+/* decode one parameter controls */
+static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+}
+
+/* decode pitch wheel change */
+static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	int value = ev->data.control.value + 8192;
+	buf[0] = value & 0x7f;
+	buf[1] = (value >> 7) & 0x7f;
+}
+
+/* decode midi control change */
+static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.param & 0x7f;
+	buf[1] = ev->data.control.value & 0x7f;
+}
+
+/* decode song position */
+static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+	buf[1] = (ev->data.control.value >> 7) & 0x7f;
+}
+
+/* decode 14bit control */
+static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
+{
+	if (ev->data.control.param < 32) {
+		if (count < 5)
+			return -ENOMEM;
+		buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+		buf[1] = ev->data.control.param;
+		buf[2] = (ev->data.control.value >> 7) & 0x7f;
+		buf[3] = ev->data.control.param + 32;
+		buf[4] = ev->data.control.value & 0x7f;
+		dev->lastcmd = buf[0];
+		return 5;
+	} else {
+		if (count < 3)
+			return -ENOMEM;
+		buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+		buf[1] = ev->data.control.param & 0x7f;
+		buf[4] = ev->data.control.value & 0x7f;
+		dev->lastcmd = buf[0];
+		return 3;
+	}
+}
+
+/*
+ *  exports
+ */
+ 
+EXPORT_SYMBOL(snd_midi_event_new);
+EXPORT_SYMBOL(snd_midi_event_free);
+EXPORT_SYMBOL(snd_midi_event_resize_buffer);
+EXPORT_SYMBOL(snd_midi_event_init);
+EXPORT_SYMBOL(snd_midi_event_reset_encode);
+EXPORT_SYMBOL(snd_midi_event_reset_decode);
+EXPORT_SYMBOL(snd_midi_event_encode);
+EXPORT_SYMBOL(snd_midi_event_encode_byte);
+EXPORT_SYMBOL(snd_midi_event_decode);
diff -Nru linux/sound/core/seq/seq_mtc.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_mtc.c
--- linux/sound/core/seq/seq_mtc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_mtc.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,252 @@
+/*
+ * MTC event converter
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include "seq_queue.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+typedef struct mtc_out {
+	int out_channel;
+	unsigned int time_format;
+	sndrv_seq_time_frame_t cur_time;
+	unsigned int decode_offset;
+	unsigned char sysex[10];
+} mtc_out_t;
+
+typedef struct mtc_in {
+	unsigned int time_format;
+	sndrv_seq_time_frame_t cur_time;
+	unsigned int cur_pos;
+	int prev_in_offset;
+} mtc_in_t;
+
+
+static int mtc_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	mtc_out_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->out_channel = sync_info->opt_info[0];
+	if (arg->out_channel == 0)
+		arg->out_channel = 127;
+	arg->time_format = sync_info->time_format;
+	sync_info->param.time.subframes = 4; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	memset(&arg->cur_time, 0, sizeof(arg->cur_time));
+	*retp = arg;
+	return 0;
+}
+
+static int mtc_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	mtc_in_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->time_format = sync_info->time_format;
+	memset(&arg->cur_time, 0, sizeof(arg->cur_time));
+	arg->cur_pos = 0;
+	arg->prev_in_offset = -1;
+	sync_info->param.time.subframes = 4; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	*retp = arg;
+	return 0;
+}
+
+
+/* decode sync signal */
+static int sync_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	int val, offset;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+	offset = (src->data.queue.param.position + arg->decode_offset) % 8;
+	if (offset == 0) {
+		/* convert and remember the current time
+		   for the following 7 MTC quarter frames */
+		arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, src->data.queue.param.position);
+	}
+	switch (offset) {
+	case 0: val = arg->cur_time.frame & 0x0f; break;
+	case 1: val = (arg->cur_time.frame >> 4) & 0x0f; break;
+	case 2: val = arg->cur_time.sec & 0x0f; break;
+	case 3: val = (arg->cur_time.sec >> 4) & 0x0f; break;
+	case 4: val = arg->cur_time.min & 0x0f; break;
+	case 5: val = (arg->cur_time.min >> 4) & 0x0f; break;
+	case 6: val = arg->cur_time.hour & 0x0f; break;
+	case 7:
+	default:
+		val = ((arg->cur_time.hour >> 4) & 0x01) | (arg->time_format << 1);
+		break;
+	}
+	val |= (offset << 4);
+	ev->type = SNDRV_SEQ_EVENT_QFRAME;
+	ev->data.control.value = val;
+	return 1;
+}
+
+/* decode sync position */
+static int sync_pos_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned int pos;
+	unsigned char *buf = arg->sysex;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+
+	pos = src->data.queue.param.position; /* quarter frames */
+	arg->decode_offset = pos & 4;
+	pos /= 4;
+	arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, pos);
+
+	buf[0] = 0xf0; /* SYSEX */
+	buf[1] = 0x7f;
+	buf[2] = arg->out_channel;
+	buf[3] = 0x01;
+	buf[4] = 0x01;
+	buf[5] = arg->cur_time.hour | (arg->time_format << 5);
+	buf[6] = arg->cur_time.min;
+	buf[7] = arg->cur_time.sec;
+	buf[8] = arg->cur_time.frame;
+	buf[9] = 0xf7;
+
+	ev->type = SNDRV_SEQ_EVENT_SYSEX;
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+	ev->data.ext.len = 10;
+	ev->data.ext.ptr = buf;
+
+	return 1;
+}
+
+static int mtc_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	mtc_out_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_SYNC:
+		return sync_out(arg, src, ev);
+	case SNDRV_SEQ_EVENT_SYNC_POS:
+		return sync_pos_out(arg, src, ev);
+	}
+	return 0;
+}
+
+/* decode sync signal */
+static int sync_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	int val, offset;
+	unsigned int time_format;
+
+	offset = (src->data.control.value & 0x70) >> 4;
+	val = src->data.control.value & 0x0f;
+	if (offset > 0 && offset != arg->prev_in_offset + 1) {
+		/* bad quarter frame message - something missing.. */
+		arg->prev_in_offset = -1; /* wait for next 0 */
+		return -EINVAL;
+	}
+	switch (offset) {
+	case 0: arg->cur_time.frame =  val; break;
+	case 1: arg->cur_time.frame |= (val << 4); break;
+	case 2: arg->cur_time.sec = val; break;
+	case 3: arg->cur_time.sec |= (val << 4); break;
+	case 4: arg->cur_time.min = val; break;
+	case 5: arg->cur_time.min |= (val << 4); break;
+	case 6: arg->cur_time.hour = val; break;
+	case 7:
+	default:
+		arg->cur_time.hour |= (val & 1) << 4;
+		time_format = (val >> 1) & 3;
+		if (time_format != arg->time_format)
+			return -EINVAL;
+		arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time);
+		arg->cur_pos += 7; /* correct the receive time */
+		break;
+	}
+
+	ev->type = SNDRV_SEQ_EVENT_SYNC;
+	ev->data.queue.sync_time_format = arg->time_format;
+	ev->data.queue.param.position = arg->cur_pos;
+	arg->cur_pos++;
+
+	return 1; /* composed */
+}
+
+/* decode sync position */
+static int sync_pos_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned time_format;
+	char buf[10];
+
+	if (snd_seq_expand_var_event(src, 10, buf, 1, 0) != 10)
+		return 0;
+	if (buf[1] != 0x7f || buf[3] != 0x01 || buf[4] != 0x01)
+		return 0;
+	time_format = (buf[5] >> 5) & 3;
+	if (time_format != arg->time_format)
+		return -EINVAL;
+	arg->cur_time.hour = buf[5] & 0x1f;
+	arg->cur_time.min = buf[6];
+	arg->cur_time.sec = buf[7];
+	arg->cur_time.frame = buf[8];
+	arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time);
+
+	ev->type = SNDRV_SEQ_EVENT_SYNC_POS;
+	ev->data.queue.sync_time_format = time_format;
+	ev->data.queue.param.position = arg->cur_pos;
+
+	return 1; /* composed */
+}
+
+static int mtc_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	mtc_in_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_QFRAME:
+		return sync_in(arg, src, ev);
+	case SNDRV_SEQ_EVENT_SYSEX:
+		return sync_pos_in(arg, src, ev);
+	}
+	return 0;
+}
+
+/* exported */
+seq_sync_parser_t snd_seq_mtc_parser = {
+	format: SNDRV_SEQ_SYNC_FMT_MTC,
+	in: {
+		open: mtc_open_in,
+		sync: mtc_sync_in,
+	},
+	out: {
+		open: mtc_open_out,
+		sync: mtc_sync_out,
+	},
+};
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
diff -Nru linux/sound/core/seq/seq_ports.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.c
--- linux/sound/core/seq/seq_ports.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,669 @@
+/*
+ *   ALSA sequencer Ports
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *                         Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_system.h"
+#include "seq_ports.h"
+#include "seq_clientmgr.h"
+
+/*
+
+   registration of client ports
+
+ */
+
+
+/* 
+
+NOTE: the current implementation of the port structure as a linked list is
+not optimal for clients that have many ports. For sending messages to all
+subscribers of a port we first need to find the address of the port
+structure, which means we have to traverse the list. A direct access table
+(array) would be better, but big preallocated arrays waste memory.
+
+Possible actions:
+
+1) leave it this way, a client does normaly does not have more than a few
+ports
+
+2) replace the linked list of ports by a array of pointers which is
+dynamicly kmalloced. When a port is added or deleted we can simply allocate
+a new array, copy the corresponding pointers, and delete the old one. We
+then only need a pointer to this array, and an integer that tells us how
+much elements are in array.
+
+*/
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+/* return pointer to port structure - port is locked if found */
+client_port_t *snd_seq_port_use_ptr(client_t *client, int num)
+{
+	struct list_head *p;
+	client_port_t *port;
+
+	if (client == NULL)
+		return NULL;
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		port = list_entry(p, client_port_t, list);
+		if (port->addr.port == num) {
+			if (port->closing)
+				break; /* deleting now */
+			snd_use_lock_use(&port->use_lock);
+			read_unlock(&client->ports_lock);
+			return port;
+		}
+	}
+	read_unlock(&client->ports_lock);
+	return NULL;		/* not found */
+}
+
+
+/* search for the next port - port is locked if found */
+client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo)
+{
+	int num;
+	struct list_head *p;
+	client_port_t *port, *found;
+
+	num = pinfo->addr.port;
+	found = NULL;
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		port = list_entry(p, client_port_t, list);
+		if (port->addr.port < num)
+			continue;
+		if (port->addr.port == num) {
+			found = port;
+			break;
+		}
+		if (found == NULL || port->addr.port < found->addr.port)
+			found = port;
+	}
+	if (found) {
+		if (found->closing)
+			found = NULL;
+		else
+			snd_use_lock_use(&found->use_lock);
+	}
+	read_unlock(&client->ports_lock);
+	return found;
+}
+
+
+/* initialize port_subs_info_t */
+static void port_subs_info_init(port_subs_info_t *grp)
+{
+	INIT_LIST_HEAD(&grp->list_head);
+	grp->count = 0;
+	grp->exclusive = 0;
+	rwlock_init(&grp->list_lock);
+	init_rwsem(&grp->list_mutex);
+	grp->open = NULL;
+	grp->close = NULL;
+}
+
+
+/* create a port, port number is returned (-1 on failure) */
+client_port_t *snd_seq_create_port(client_t *client, int port)
+{
+	unsigned long flags;
+	client_port_t *new_port;
+	struct list_head *l;
+	int num = -1;
+	
+	/* sanity check */
+	snd_assert(client, return NULL);
+
+	if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
+		snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
+		return NULL;
+	}
+
+	/* create a new port */
+	new_port = snd_kcalloc(sizeof(client_port_t), GFP_KERNEL);
+	if (! new_port) {
+		snd_printd("malloc failed for registering client port\n");
+		return NULL;	/* failure, out of memory */
+	}
+	/* init port data */
+	new_port->addr.client = client->number;
+	new_port->addr.port = -1;
+	new_port->owner = THIS_MODULE;
+	sprintf(new_port->name, "port-%d", num);
+	snd_use_lock_init(&new_port->use_lock);
+	port_subs_info_init(&new_port->c_src);
+	port_subs_info_init(&new_port->c_dest);
+
+	num = port >= 0 ? port : 0;
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	list_for_each(l, &client->ports_list_head) {
+		client_port_t *p = list_entry(l, client_port_t, list);
+		if (p->addr.port > num)
+			break;
+		if (port < 0) /* auto-probe mode */
+			num = p->addr.port + 1;
+	}
+	/* insert the new port */
+	list_add_tail(&new_port->list, l);
+	client->num_ports++;
+	new_port->addr.port = num;	/* store the port number in the port */
+	write_unlock_irqrestore(&client->ports_lock, flags);
+	up(&client->ports_mutex);
+	sprintf(new_port->name, "port-%d", num);
+
+	return new_port;
+}
+
+/* */
+enum group_type_t {
+	SRC_LIST, DEST_LIST
+};
+
+static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
+static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
+
+
+static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp)
+{
+	client_port_t *p;
+	*cp = snd_seq_client_use_ptr(addr->client);
+	if (*cp) {
+		p = snd_seq_port_use_ptr(*cp, addr->port);
+		if (! p) {
+			snd_seq_client_unlock(*cp);
+			*cp = NULL;
+		}
+		return p;
+	}
+	return NULL;
+}
+
+/*
+ * remove all subscribers on the list
+ * this is called from port_delete, for each src and dest list.
+ */
+static void clear_subscriber_list(client_t *client, client_port_t *port,
+				  port_subs_info_t *grp, int grptype)
+{
+	struct list_head *p, *n;
+
+	down_write(&grp->list_mutex);
+	list_for_each_safe(p, n, &grp->list_head) {
+		subscribers_t *subs;
+		client_t *c;
+		client_port_t *aport;
+
+		if (grptype == SRC_LIST) {
+			subs = list_entry(p, subscribers_t, src_list);
+			aport = get_client_port(&subs->info.dest, &c);
+		} else {
+			subs = list_entry(p, subscribers_t, dest_list);
+			aport = get_client_port(&subs->info.sender, &c);
+		}
+		list_del(p);
+		unsubscribe_port(client, port, grp, &subs->info, 0);
+		if (!aport) {
+			/* looks like the connected port is being deleted.
+			 * we decrease the counter, and when both ports are deleted
+			 * remove the subscriber info
+			 */
+			if (atomic_dec_and_test(&subs->ref_count))
+				kfree(subs);
+		} else {
+			/* ok we got the connected port */
+			port_subs_info_t *agrp;
+			agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
+			down_write(&agrp->list_mutex);
+			if (grptype == SRC_LIST)
+				list_del(&subs->dest_list);
+			else
+				list_del(&subs->src_list);
+			unsubscribe_port(c, aport, agrp, &subs->info, 1);
+			kfree(subs);
+			up_write(&agrp->list_mutex);
+			snd_seq_port_unlock(aport);
+			snd_seq_client_unlock(c);
+		}
+	}
+	up_write(&grp->list_mutex);
+}
+
+/* delete port data */
+static int port_delete(client_t *client, client_port_t *port)
+{
+	/* set closing flag and wait for all port access are gone */
+	port->closing = 1;
+	snd_use_lock_sync(&port->use_lock); 
+
+	/* clear subscribers info */
+	clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
+	clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+
+	if (port->private_free)
+		port->private_free(port->private_data);
+
+	snd_assert(port->c_src.count == 0,);
+	snd_assert(port->c_dest.count == 0,);
+
+	kfree(port);
+	return 0;
+}
+
+
+/* delete a port with the given port id */
+int snd_seq_delete_port(client_t *client, int port)
+{
+	unsigned long flags;
+	struct list_head *l;
+	client_port_t *found = NULL;
+
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	list_for_each(l, &client->ports_list_head) {
+		client_port_t *p = list_entry(l, client_port_t, list);
+		if (p->addr.port == port) {
+			/* ok found.  delete from the list at first */
+			list_del(l);
+			client->num_ports--;
+			found = p;
+			break;
+		}
+	}
+	write_unlock_irqrestore(&client->ports_lock, flags);
+	up(&client->ports_mutex);
+	if (found)
+		return port_delete(client, found);
+	else
+		return -ENOENT;
+}
+
+/* delete the all ports belonging to the given client */
+int snd_seq_delete_all_ports(client_t *client)
+{
+	unsigned long flags;
+	struct list_head deleted_list, *p, *n;
+	
+	/* move the port list to deleted_list, and
+	 * clear the port list in the client data.
+	 */
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	if (! list_empty(&client->ports_list_head)) {
+		__list_add(&deleted_list,
+			   client->ports_list_head.prev,
+			   client->ports_list_head.next);
+		INIT_LIST_HEAD(&client->ports_list_head);
+	} else {
+		INIT_LIST_HEAD(&deleted_list);
+	}
+	client->num_ports = 0;
+	write_unlock_irqrestore(&client->ports_lock, flags);
+
+	/* remove each port in deleted_list */
+	list_for_each_safe(p, n, &deleted_list) {
+		client_port_t *port = list_entry(p, client_port_t, list);
+		list_del(p);
+		snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
+		port_delete(client, port);
+	}
+	up(&client->ports_mutex);
+	return 0;
+}
+
+/* set port info fields */
+int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info)
+{
+	snd_assert(port && info, return -EINVAL);
+
+	/* set port name */
+	if (info->name[0]) {
+		strncpy(port->name, info->name, sizeof(port->name)-1);
+		port->name[sizeof(port->name)-1] = '\0';
+	}
+	
+	/* set capabilities */
+	port->capability = info->capability;
+	
+	/* get port type */
+	port->type = info->type;
+
+	/* information about supported channels/voices */
+	port->midi_channels = info->midi_channels;
+	port->synth_voices = info->synth_voices;
+
+	return 0;
+}
+
+/* get port info fields */
+int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info)
+{
+	snd_assert(port && info, return -EINVAL);
+
+	/* get port name */
+	strncpy(info->name, port->name, sizeof(info->name));
+	
+	/* get capabilities */
+	info->capability = port->capability;
+
+	/* get port type */
+	info->type = port->type;
+
+	/* information about supported channels/voices */
+	info->midi_channels = port->midi_channels;
+	info->synth_voices = port->synth_voices;
+
+	/* get subscriber counts */
+	info->read_use = port->c_src.count;
+	info->write_use = port->c_dest.count;
+	
+	return 0;
+}
+
+
+
+/*
+ * call callback functions (if any):
+ * the callbacks are invoked only when the first (for connection) or
+ * the last subscription (for disconnection) is done.  Second or later
+ * subscription results in increment of counter, but no callback is
+ * invoked.
+ * This feature is useful if these callbacks are associated with
+ * initialization or termination of devices (see seq_midi.c).
+ *
+ * If callback_all option is set, the callback function is invoked
+ * at each connnection/disconnection. 
+ */
+
+static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp,
+			  snd_seq_port_subscribe_t *info, int send_ack)
+{
+	int err = 0;
+
+	if (!try_inc_mod_count(port->owner))
+		return -EFAULT;
+	grp->count++;
+	if (grp->open && (port->callback_all || grp->count == 1)) {
+		err = grp->open(port->private_data, info);
+		if (err < 0) {
+			dec_mod_count(port->owner);
+			grp->count--;
+		}
+	}
+	if (err >= 0 && send_ack && client->type == USER_CLIENT)
+		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
+						   info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+
+	return err;
+}
+
+static int unsubscribe_port(client_t *client, client_port_t *port,
+			    port_subs_info_t *grp,
+			    snd_seq_port_subscribe_t *info, int send_ack)
+{
+	int err = 0;
+
+	snd_assert(port->owner, return -EFAULT);
+	if (! grp->count)
+		return -EINVAL;
+	grp->count--;
+	if (grp->close && (port->callback_all || grp->count == 0))
+		err = grp->close(port->private_data, info);
+	if (send_ack && client->type == USER_CLIENT)
+		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
+						   info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+	dec_mod_count(port->owner);
+	return err;
+}
+
+
+
+/* check if both addresses are identical */
+static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s)
+{
+	return (r->client == s->client) && (r->port == s->port);
+}
+
+/* check the two subscribe info match */
+/* if flags is zero, checks only sender and destination addresses */
+static int match_subs_info(snd_seq_port_subscribe_t *r,
+			   snd_seq_port_subscribe_t *s)
+{
+	if (addr_match(&r->sender, &s->sender) &&
+	    addr_match(&r->dest, &s->dest)) {
+		if (r->flags && r->flags == s->flags)
+			return r->queue == s->queue;
+		else if (! r->flags)
+			return 1;
+	}
+	return 0;
+}
+
+
+/* connect two ports */
+int snd_seq_port_connect(client_t *connector,
+			 client_t *src_client, client_port_t *src_port,
+			 client_t *dest_client, client_port_t *dest_port,
+			 snd_seq_port_subscribe_t *info)
+{
+	port_subs_info_t *src = &src_port->c_src;
+	port_subs_info_t *dest = &dest_port->c_dest;
+	subscribers_t *subs;
+	struct list_head *p;
+	int err, src_called = 0;
+	unsigned long flags;
+	int exclusive;
+
+	subs = snd_kcalloc(sizeof(*subs), GFP_KERNEL);
+	if (! subs)
+		return -ENOMEM;
+
+	subs->info = *info;
+	atomic_set(&subs->ref_count, 2);
+
+	down_write(&src->list_mutex);
+	down_write(&dest->list_mutex);
+
+	exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
+	err = -EBUSY;
+	if (exclusive) {
+		if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
+			goto __error;
+	} else {
+		if (src->exclusive || dest->exclusive)
+			goto __error;
+		/* check whether already exists */
+		list_for_each(p, &src->list_head) {
+			subscribers_t *s = list_entry(p, subscribers_t, src_list);
+			if (match_subs_info(info, &s->info))
+				goto __error;
+		}
+		list_for_each(p, &dest->list_head) {
+			subscribers_t *s = list_entry(p, subscribers_t, dest_list);
+			if (match_subs_info(info, &s->info))
+				goto __error;
+		}
+	}
+
+	if ((err = subscribe_port(src_client, src_port, src, info,
+				  connector->number != src_client->number)) < 0)
+		goto __error;
+	src_called = 1;
+
+	if ((err = subscribe_port(dest_client, dest_port, dest, info,
+				  connector->number != dest_client->number)) < 0)
+		goto __error;
+
+	/* add to list */
+	write_lock_irqsave(&src->list_lock, flags);
+	// write_lock(&dest->list_lock); // no other lock yet
+	list_add_tail(&subs->src_list, &src->list_head);
+	list_add_tail(&subs->dest_list, &dest->list_head);
+	// write_unlock(&dest->list_lock); // no other lock yet
+	write_unlock_irqrestore(&src->list_lock, flags);
+
+	src->exclusive = dest->exclusive = exclusive;
+
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return 0;
+
+ __error:
+	if (src_called)
+		unsubscribe_port(src_client, src_port, src, info,
+				 connector->number != src_client->number);
+	kfree(subs);
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return err;
+}
+
+
+/* remove the connection */
+int snd_seq_port_disconnect(client_t *connector,
+			    client_t *src_client, client_port_t *src_port,
+			    client_t *dest_client, client_port_t *dest_port,
+			    snd_seq_port_subscribe_t *info)
+{
+	port_subs_info_t *src = &src_port->c_src;
+	port_subs_info_t *dest = &dest_port->c_dest;
+	subscribers_t *subs;
+	struct list_head *p;
+	int err = -ENOENT;
+	unsigned long flags;
+
+	down_write(&src->list_mutex);
+	down_write(&dest->list_mutex);
+
+	/* look for the connection */
+	list_for_each(p, &src->list_head) {
+		subs = list_entry(p, subscribers_t, src_list);
+		if (match_subs_info(info, &subs->info)) {
+			write_lock_irqsave(&src->list_lock, flags);
+			// write_lock(&dest->list_lock);  // no lock yet
+			list_del(&subs->src_list);
+			list_del(&subs->dest_list);
+			// write_unlock(&dest->list_lock);
+			write_unlock_irqrestore(&src->list_lock, flags);
+			src->exclusive = dest->exclusive = 0;
+			unsubscribe_port(src_client, src_port, src, info,
+					 connector->number != src_client->number);
+			unsubscribe_port(dest_client, dest_port, dest, info,
+					 connector->number != dest_client->number);
+			kfree(subs);
+			err = 0;
+			break;
+		}
+	}
+
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return err;
+}
+
+
+/* get matched subscriber */
+subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp,
+					     snd_seq_addr_t *dest_addr)
+{
+	struct list_head *p;
+	subscribers_t *s, *found = NULL;
+
+	down_read(&src_grp->list_mutex);
+	list_for_each(p, &src_grp->list_head) {
+		s = list_entry(p, subscribers_t, src_list);
+		if (addr_match(dest_addr, &s->info.dest)) {
+			found = s;
+			break;
+		}
+	}
+	up_read(&src_grp->list_mutex);
+	return found;
+}
+
+/*
+ * Attach a device driver that wants to receive events from the
+ * sequencer.  Returns the new port number on success.
+ * A driver that wants to receive the events converted to midi, will
+ * use snd_seq_midisynth_register_port().
+ */
+/* exported */
+int snd_seq_event_port_attach(int client,
+			      snd_seq_port_callback_t *pcbp,
+			      int cap,
+			      int type,
+			      char *portname)
+{
+	snd_seq_port_info_t portinfo;
+	int  ret;
+
+	/* Set up the port */
+	memset(&portinfo, 0, sizeof(portinfo));
+	portinfo.addr.client = client;
+	if (portname)
+		strncpy(portinfo.name, portname, sizeof(portinfo.name));
+	else
+		sprintf(portinfo.name, "Unamed port");
+
+	portinfo.capability = cap;
+	portinfo.type = type;
+	portinfo.kernel = pcbp;
+
+	/* Create it */
+	ret = snd_seq_kernel_client_ctl(client,
+					SNDRV_SEQ_IOCTL_CREATE_PORT,
+					&portinfo);
+
+	if (ret >= 0)
+		ret = portinfo.addr.port;
+
+	return ret;
+}
+
+
+/*
+ * Detach the driver from a port.
+ */
+/* exported */
+int snd_seq_event_port_detach(int client, int port)
+{
+	snd_seq_port_info_t portinfo;
+	int  err;
+
+	memset(&portinfo, 0, sizeof(portinfo));
+	portinfo.addr.client = client;
+	portinfo.addr.port   = port;
+	err = snd_seq_kernel_client_ctl(client,
+					SNDRV_SEQ_IOCTL_DELETE_PORT,
+					&portinfo);
+
+	return err;
+}
diff -Nru linux/sound/core/seq/seq_ports.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.h
--- linux/sound/core/seq/seq_ports.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,124 @@
+/*
+ *   ALSA sequencer Ports 
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_PORTS_H
+#define __SND_SEQ_PORTS_H
+
+#include <sound/seq_kernel.h>
+#include "seq_lock.h"
+
+/* list of 'exported' ports */
+
+/* Client ports that are not exported are still accessible, but are
+ anonymous ports. 
+ 
+ If a port supports SUBSCRIPTION, that port can send events to all
+ subscribersto a special address, with address
+ (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all
+ recipients that are registered in the subscription list. A typical
+ application for these SUBSCRIPTION events is handling of incoming MIDI
+ data. The port doesn't 'know' what other clients are interested in this
+ message. If for instance a MIDI recording application would like to receive
+ the events from that port, it will first have to subscribe with that port.
+ 
+*/
+
+typedef struct subscribers_t {
+	snd_seq_port_subscribe_t info;	/* additional info */
+	struct list_head src_list;	/* link of sources */
+	struct list_head dest_list;	/* link of destinations */
+	atomic_t ref_count;
+} subscribers_t;
+
+typedef struct port_subs_info_t {
+	struct list_head list_head;	/* list of subscribed ports */
+	unsigned int count;		/* count of subscribers */
+	unsigned int exclusive: 1;	/* exclusive mode */
+	struct rw_semaphore list_mutex;
+	rwlock_t list_lock;
+	snd_seq_kernel_port_open_t *open;
+	snd_seq_kernel_port_close_t *close;
+} port_subs_info_t;
+
+typedef struct client_port_t {
+
+	snd_seq_addr_t addr;		/* client/port number */
+	struct module *owner;		/* owner of this port */
+	char name[64];			/* port name */	
+	struct list_head list;		/* port list */
+	snd_use_lock_t use_lock;
+
+	/* subscribers */
+	port_subs_info_t c_src;		/* read (sender) list */
+	port_subs_info_t c_dest;	/* write (dest) list */
+
+	snd_seq_kernel_port_input_t *event_input;
+	snd_seq_kernel_port_private_free_t *private_free;
+	void *private_data;
+	unsigned int callback_all : 1;
+	unsigned int closing : 1;
+	
+	/* capability, inport, output, sync */
+	unsigned int capability;	/* port capability bits */
+	unsigned int type;		/* port type bits */
+
+	/* supported channels */
+	int midi_channels;
+	int synth_voices;
+		
+} client_port_t;
+
+/* return pointer to port structure and lock port */
+client_port_t *snd_seq_port_use_ptr(client_t *client, int num);
+
+/* search for next port - port is locked if found */
+client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo);
+
+/* unlock the port */
+#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
+
+/* create a port, port number is returned (-1 on failure) */
+client_port_t *snd_seq_create_port(client_t *client, int port_index);
+
+/* delete a port */
+int snd_seq_delete_port(client_t *client, int port);
+
+/* delete all ports */
+int snd_seq_delete_all_ports(client_t *client);
+
+/* set port info fields */
+int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info);
+
+/* get port info fields */
+int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info);
+
+/* add subscriber to subscription list */
+int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
+
+/* remove subscriber from subscription list */ 
+int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
+
+/* subscribe port */
+int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info);
+
+/* get matched subscriber */
+subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr);
+
+#endif
diff -Nru linux/sound/core/seq/seq_prioq.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.c
--- linux/sound/core/seq/seq_prioq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,449 @@
+/*
+ *   ALSA sequencer Priority Queue
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "seq_timer.h"
+#include "seq_prioq.h"
+#include "seq_timer.h"
+
+
+/* Implementation is a simple linked list for now...
+
+   This priority queue orders the events on timestamp. For events with an
+   equeal timestamp the queue behaves as a FIFO. 
+
+   *
+   *           +-------+
+   *  Head --> | first |
+   *           +-------+
+   *                 |next
+   *           +-----v-+
+   *           |       |
+   *           +-------+
+   *                 |
+   *           +-----v-+
+   *           |       |
+   *           +-------+
+   *                 |
+   *           +-----v-+
+   *  Tail --> | last  |
+   *           +-------+
+   *
+
+ */
+
+
+
+/* create new prioq (constructor) */
+prioq_t *snd_seq_prioq_new(void)
+{
+	prioq_t *f;
+
+	f = snd_kcalloc(sizeof(prioq_t), GFP_KERNEL);
+	if (f == NULL) {
+		snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
+		return NULL;
+	}
+	
+	spin_lock_init(&f->lock);
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	
+	return f;
+}
+
+/* delete prioq (destructor) */
+void snd_seq_prioq_delete(prioq_t **fifo)
+{
+	prioq_t *f = *fifo;
+	*fifo = NULL;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
+		return;
+	}
+
+	/* release resources...*/
+	/*....................*/
+	
+	if (f->cells > 0) {
+		/* drain prioQ */
+		while (f->cells > 0)
+			snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+	}
+	
+	kfree(f);
+}
+
+
+
+
+/* compare timestamp between events */
+/* return 1 if a >= b; 0 */
+static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b)
+{
+	if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
+		/* compare ticks */
+		return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
+	} else {
+		/* compare real time */
+		return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
+	}
+}
+
+/* compare timestamp between events */
+/* return negative if a < b;
+ *        zero     if a = b;
+ *        positive if a > b;
+ */
+static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b)
+{
+	if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
+		/* compare ticks */
+		if (a->time.tick > b->time.tick)
+			return 1;
+		else if (a->time.tick == b->time.tick)
+			return 0;
+		else
+			return -1;
+	} else {
+		/* compare real time */
+		if (a->time.time.tv_sec > b->time.time.tv_sec)
+			return 1;
+		else if (a->time.time.tv_sec == b->time.time.tv_sec) {
+			if (a->time.time.tv_nsec > b->time.time.tv_nsec)
+				return 1;
+			else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
+				return 0;
+			else
+				return -1;
+		} else
+			return -1;
+	}
+}
+
+/* enqueue cell to prioq */
+void snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell)
+{
+	snd_seq_event_cell_t *cur, *prev;
+	unsigned long flags;
+	int prior;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return;
+	}
+	if (cell == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL cell\n");
+		return;
+	}
+	
+	/* check flags */
+	prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
+
+	spin_lock_irqsave(&f->lock, flags);
+
+	/* check if this element needs to inserted at the end (ie. ordered 
+	   data is inserted) This will be very likeley if a sequencer 
+	   application or midi file player is feeding us (sequential) data */
+	if (f->tail && !prior) {
+		if (compare_timestamp(&cell->event, &f->tail->event)) {
+			/* add new cell to tail of the fifo */
+			f->tail->next = cell;
+			f->tail = cell;
+			cell->next = NULL;
+			f->cells++;
+			spin_unlock_irqrestore(&f->lock, flags);
+			return;
+		}
+	}
+	/* traverse list of elements to find the place where the new cell is
+	   to be inserted... Note that this is a order n process ! */
+
+	prev = NULL;		/* previous cell */
+	cur = f->head;		/* cursor */
+
+	while (cur != NULL) {
+		/* compare timestamps */
+		int rel = compare_timestamp_rel(&cell->event, &cur->event);
+		if (rel < 0)
+			/* new cell has earlier schedule time, */
+			break;
+		else if (rel == 0 && prior)
+			/* equal schedule time and prior to others */
+			break;
+		/* new cell has equal or larger schedule time, */
+		/* move cursor to next cell */
+		prev = cur;
+		cur = cur->next;
+	}
+
+	/* insert it before cursor */
+	if (prev != NULL)
+		prev->next = cell;
+	cell->next = cur;
+
+	if (f->head == cur) /* this is the first cell, set head to it */
+		f->head = cell;
+	if (cur == NULL) /* reached end of the list */
+		f->tail = cell;
+	f->cells++;
+	spin_unlock_irqrestore(&f->lock, flags);
+}
+
+/* dequeue cell from prioq */
+snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return NULL;
+	}
+	spin_lock_irqsave(&f->lock, flags);
+
+	cell = f->head;
+	if (cell) {
+		f->head = cell->next;
+
+		/* reset tail if this was the last element */
+		if (f->tail == cell)
+			f->tail = NULL;
+
+		cell->next = NULL;
+		f->cells--;
+	}
+
+	spin_unlock_irqrestore(&f->lock, flags);
+	return cell;
+}
+
+/* return number of events available in prioq */
+int snd_seq_prioq_avail(prioq_t * f)
+{
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return 0;
+	}
+	return f->cells;
+}
+
+
+/* peek at cell at the head of the prioq */
+snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f)
+{
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return NULL;
+	}
+	return f->head;
+}
+
+
+static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp)
+{
+	if (cell->event.source.client == client ||
+	    cell->event.dest.client == client)
+		return 1;
+	if (!timestamp)
+		return 0;
+	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+	case SNDRV_SEQ_TIME_STAMP_TICK:
+		if (cell->event.time.tick)
+			return 1;
+		break;
+	case SNDRV_SEQ_TIME_STAMP_REAL:
+		if (cell->event.time.time.tv_sec ||
+		    cell->event.time.time.tv_nsec)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+/* remove cells for left client */
+void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp)
+{
+	register snd_seq_event_cell_t *cell, *next;
+	unsigned long flags;
+	snd_seq_event_cell_t *prev = NULL;
+	snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
+
+	/* collect all removed cells */
+	spin_lock_irqsave(&f->lock, flags);
+	cell = f->head;
+	while (cell) {
+		next = cell->next;
+		if (prioq_match(cell, client, timestamp)) {
+			/* remove cell from prioq */
+			if (cell == f->head) {
+				f->head = cell->next;
+			} else {
+				prev->next = cell->next;
+			}
+			if (cell == f->tail)
+				f->tail = cell->next;
+			f->cells--;
+			/* add cell to free list */
+			cell->next = NULL;
+			if (freefirst == NULL) {
+				freefirst = cell;
+			} else {
+				freeprev->next = cell;
+			}
+			freeprev = cell;
+		} else {
+#if 0
+			printk("type = %i, source = %i, dest = %i, client = %i\n",
+				cell->event.type,
+				cell->event.source.client,
+				cell->event.dest.client,
+				client);
+#endif
+			prev = cell;
+		}
+		cell = next;		
+	}
+	spin_unlock_irqrestore(&f->lock, flags);	
+
+	/* remove selected cells */
+	while (freefirst) {
+		freenext = freefirst->next;
+		snd_seq_cell_free(freefirst);
+		freefirst = freenext;
+	}
+}
+
+static int prioq_remove_match(snd_seq_remove_events_t *info,
+	snd_seq_event_t *ev)
+{
+	int res;
+
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
+		if (ev->dest.client != info->dest.client ||
+				ev->dest.port != info->dest.port)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
+		if (! snd_seq_ev_is_channel_type(ev))
+			return 0;
+		/* data.note.channel and data.control.channel are identical */
+		if (ev->data.note.channel != info->channel)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
+		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
+			res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
+		else
+			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
+		if (!res)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
+		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
+			res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
+		else
+			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
+		if (res)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
+		if (ev->type != info->type)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
+		/* Do not remove off events */
+		switch (ev->type) {
+		case SNDRV_SEQ_EVENT_NOTEOFF:
+		/* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
+			return 0;
+		default:
+			break;
+		}
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
+		if (info->tag != ev->tag)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* remove cells matching remove criteria */
+void snd_seq_prioq_remove_events(prioq_t * f, int client,
+	snd_seq_remove_events_t *info)
+{
+	register snd_seq_event_cell_t *cell, *next;
+	unsigned long flags;
+	snd_seq_event_cell_t *prev = NULL;
+	snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
+
+	/* collect all removed cells */
+	spin_lock_irqsave(&f->lock, flags);
+	cell = f->head;
+
+	while (cell) {
+		next = cell->next;
+		if (cell->event.source.client == client &&
+			prioq_remove_match(info, &cell->event)) {
+
+			/* remove cell from prioq */
+			if (cell == f->head) {
+				f->head = cell->next;
+			} else {
+				prev->next = cell->next;
+			}
+
+			if (cell == f->tail)
+				f->tail = cell->next;
+			f->cells--;
+
+			/* add cell to free list */
+			cell->next = NULL;
+			if (freefirst == NULL) {
+				freefirst = cell;
+			} else {
+				freeprev->next = cell;
+			}
+
+			freeprev = cell;
+		} else {
+			prev = cell;
+		}
+		cell = next;		
+	}
+	spin_unlock_irqrestore(&f->lock, flags);	
+
+	/* remove selected cells */
+	while (freefirst) {
+		freenext = freefirst->next;
+		snd_seq_cell_free(freefirst);
+		freefirst = freenext;
+	}
+}
+
+
diff -Nru linux/sound/core/seq/seq_prioq.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.h
--- linux/sound/core/seq/seq_prioq.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,62 @@
+/*
+ *   ALSA sequencer Priority Queue
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_PRIOQ_H
+#define __SND_SEQ_PRIOQ_H
+
+#include "seq_memory.h"
+
+
+/* === PRIOQ === */
+
+typedef struct {
+	snd_seq_event_cell_t* head;      /* pointer to head of prioq */
+	snd_seq_event_cell_t* tail;      /* pointer to tail of prioq */
+	int cells;
+	spinlock_t lock;
+} prioq_t;
+
+
+/* create new prioq (constructor) */
+extern prioq_t *snd_seq_prioq_new(void);
+
+/* delete prioq (destructor) */
+extern void snd_seq_prioq_delete(prioq_t **fifo);
+
+/* enqueue cell to prioq */
+extern void snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell);
+
+/* dequeue cell from prioq */ 
+extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f);
+
+/* return number of events available in prioq */
+extern int snd_seq_prioq_avail(prioq_t *f);
+
+/* peek at cell at the head of the prioq */
+extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f);
+
+/* client left queue */
+extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp);        
+
+/* Remove events */
+void snd_seq_prioq_remove_events(prioq_t * f, int client,
+	snd_seq_remove_events_t *info);
+
+#endif
diff -Nru linux/sound/core/seq/seq_queue.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.c
--- linux/sound/core/seq/seq_queue.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,872 @@
+/*
+ *   ALSA sequencer Timing queue handling
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *   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
+ *
+ * MAJOR CHANGES
+ *   Nov. 13, 1999	Takashi Iwai <iwai@ww.uni-erlangen.de>
+ *     - Queues are allocated dynamically via ioctl.
+ *     - When owner client is deleted, all owned queues are deleted, too.
+ *     - Owner of unlocked queue is kept unmodified even if it is
+ *	 manipulated by other clients.
+ *     - Owner field in SET_QUEUE_OWNER ioctl must be identical with the
+ *       caller client.  i.e. Changing owner to a third client is not
+ *       allowed.
+ *
+ *  Aug. 30, 2000	Takashi Iwai
+ *     - Queues are managed in static array again, but with better way.
+ *       The API itself is identical.
+ *     - The queue is locked when queue_t pinter is returned via
+ *       queueptr().  This pointer *MUST* be released afterward by
+ *       queuefree(ptr).
+ *     - Addition of experimental sync support.
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_clientmgr.h"
+#include "seq_fifo.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+/* FIXME: this should be in a header file */
+void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer);
+#endif
+
+static void snd_seq_check_queue_in_tasklet(unsigned long private_data);
+
+/* list of allocated queues */
+static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES];
+static spinlock_t queue_list_lock = SPIN_LOCK_UNLOCKED;
+/* number of queues allocated */
+static int num_queues = 0;
+
+int snd_seq_queue_get_cur_queues(void)
+{
+	return num_queues;
+}
+
+/*----------------------------------------------------------------*/
+
+/* assign queue id and insert to list */
+static int queue_list_add(queue_t *q)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue_list_lock, flags);
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if (! queue_list[i]) {
+			queue_list[i] = q;
+			q->queue = i;
+			num_queues++;
+			spin_unlock_irqrestore(&queue_list_lock, flags);
+			return i;
+		}
+	}
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return -1;
+}
+
+static queue_t *queue_list_remove(int id, int client)
+{
+	queue_t *q;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue_list_lock, flags);
+	q = queue_list[id];
+	if (q) {
+		spin_lock(&q->owner_lock);
+		if (q->owner == client) {
+			/* found */
+			q->klocked = 1;
+			spin_unlock(&q->owner_lock);
+			queue_list[id] = NULL;
+			num_queues--;
+			spin_unlock_irqrestore(&queue_list_lock, flags);
+			return q;
+		}
+		spin_unlock(&q->owner_lock);
+	}
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return NULL;
+}
+
+/*----------------------------------------------------------------*/
+
+/* create new queue (constructor) */
+static queue_t *queue_new(int owner, int locked)
+{
+	queue_t *q;
+
+	q = snd_kcalloc(sizeof(queue_t), GFP_KERNEL);
+	if (q == NULL) {
+		snd_printd("malloc failed for snd_seq_queue_new()\n");
+		return NULL;
+	}
+
+	spin_lock_init(&q->owner_lock);
+	spin_lock_init(&q->check_lock);
+	init_MUTEX(&q->timer_mutex);
+	snd_use_lock_init(&q->use_lock);
+	tasklet_init(&q->taskq, snd_seq_check_queue_in_tasklet, (unsigned long)q);
+	q->queue = -1;
+
+	q->tickq = snd_seq_prioq_new();
+	q->timeq = snd_seq_prioq_new();
+	q->timer = snd_seq_timer_new();
+	if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) {
+		snd_seq_prioq_delete(&q->tickq);
+		snd_seq_prioq_delete(&q->timeq);
+		snd_seq_timer_delete(&q->timer);
+		kfree(q);
+		return NULL;
+	}
+
+	q->owner = owner;
+	q->locked = locked;
+	q->klocked = 0;
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	q->master_lock = RW_LOCK_UNLOCKED;
+	q->slave_lock = RW_LOCK_UNLOCKED;
+	INIT_LIST_HEAD(&q->master_head);
+	q->slave.format = 0;
+#endif
+
+	return q;
+}
+
+/* delete queue (destructor) */
+static void queue_delete(queue_t *q)
+{
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC)
+		snd_seq_sync_delete_port(q);
+#endif
+	/* stop and release the timer */
+	snd_seq_timer_stop(q->timer);
+	snd_seq_timer_close(q);
+	/* wait until access free */
+	snd_use_lock_sync(&q->use_lock);
+	/* release resources... */
+	snd_seq_prioq_delete(&q->tickq);
+	snd_seq_prioq_delete(&q->timeq);
+	snd_seq_timer_delete(&q->timer);
+
+	kfree(q);
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* setup queues */
+int __init snd_seq_queues_init(void)
+{
+	/*
+	memset(queue_list, 0, sizeof(queue_list));
+	num_queues = 0;
+	*/
+	return 0;
+}
+
+/* delete all existing queues */
+void __exit snd_seq_queues_delete(void)
+{
+	int i;
+
+	/* clear list */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if (queue_list[i])
+			queue_delete(queue_list[i]);
+	}
+}
+
+/* allocate a new queue -
+ * return queue index value or negative value for error
+ */
+int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+{
+	queue_t *q;
+
+	q = queue_new(client, locked);
+	if (q == NULL)
+		return -ENOMEM;
+	q->info_flags = info_flags;
+	if (queue_list_add(q) < 0) {
+		queue_delete(q);
+		return -ENOMEM;
+	}
+	snd_seq_queue_use(q->queue, client, 1); /* use this queue */
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) {
+		if (snd_seq_sync_create_port(q) < 0)
+			q->info_flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC;
+	}
+#endif
+	return q->queue;
+}
+
+/* delete a queue - queue must be owned by the client */
+int snd_seq_queue_delete(int client, int queueid)
+{
+	queue_t *q;
+
+	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
+		return -EINVAL;
+	q = queue_list_remove(queueid, client);
+	if (q == NULL)
+		return -EINVAL;
+	queue_delete(q);
+
+	return 0;
+}
+
+
+/* return pointer to queue structure for specified id */
+queue_t *queueptr(int queueid)
+{
+	queue_t *q;
+	unsigned long flags;
+
+	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
+		return NULL;
+	spin_lock_irqsave(&queue_list_lock, flags);
+	q = queue_list[queueid];
+	if (q)
+		snd_use_lock_use(&q->use_lock);
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return q;
+}
+
+/* return the (first) queue matching with the specified name */
+queue_t *snd_seq_queue_find_name(char *name)
+{
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) != NULL) {
+			if (strncpy(q->name, name, sizeof(q->name)) == 0)
+				return q;
+			queuefree(q);
+		}
+	}
+	return NULL;
+}
+
+
+/* -------------------------------------------------------- */
+
+void snd_seq_check_queue(queue_t *q, int atomic, int hop)
+{
+	unsigned long flags;
+	int dequeue;
+	int rc;
+	snd_seq_event_cell_t *cell;
+
+	if (q == NULL)
+		return;
+
+	/* make this function non-reentrant */
+	spin_lock_irqsave(&q->check_lock, flags);
+	if (q->check_blocked) {
+		q->check_again = 1;
+		spin_unlock_irqrestore(&q->check_lock, flags);
+		return;		/* other thread is already checking queues */
+	}
+	q->check_blocked = 1;
+	spin_unlock_irqrestore(&q->check_lock, flags);
+
+	if (atomic)
+		dequeue = SNDRV_SEQ_MAX_DEQUEUE;
+	else
+		dequeue = 0x7fffffff; /* XXX */
+
+      __again:
+	/* Process tick queue... */
+
+	/* limit the number of elements dequeued per pass to save the machine from lockups */
+	while (dequeue > 0) {
+
+		cell = snd_seq_prioq_cell_peek(q->tickq);
+		if (cell == NULL)
+			break;
+		if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) {
+			cell = snd_seq_prioq_cell_out(q->tickq);
+			if (cell != NULL) {
+				rc = snd_seq_dispatch_event(cell, atomic, hop);
+				if (rc > 0)
+					dequeue -= rc;
+			}
+		} else {
+			/* event remains in the queue */
+			break;
+		}
+	}
+
+
+	/* Process time queue... */
+
+	/* limit the number of elements dequeued per pass to save the machine from lockups */
+	while (dequeue > 0) {
+		cell = snd_seq_prioq_cell_peek(q->timeq);
+		if (cell == NULL)
+			break;
+		if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) {
+			cell = snd_seq_prioq_cell_out(q->timeq);
+			if (cell != NULL) {
+				rc = snd_seq_dispatch_event(cell, atomic, hop);
+				if (rc > 0)
+					dequeue -= rc;
+			}
+
+		} else {
+			/* event remains in the queue */
+			break;
+		}
+	}
+
+	/* free lock */
+	spin_lock_irqsave(&q->check_lock, flags);
+	if (q->check_again && dequeue > 0) {
+		q->check_again = 0;
+		spin_unlock_irqrestore(&q->check_lock, flags);
+		goto __again;
+	}
+	q->check_blocked = 0;
+	if (dequeue <= 0 && atomic)
+		tasklet_hi_schedule(&q->taskq);
+	spin_unlock_irqrestore(&q->check_lock, flags);
+}
+
+/* tasklet */
+static void snd_seq_check_queue_in_tasklet(unsigned long private_data)
+{
+	queue_t *q = (queue_t *)private_data;
+	snd_seq_check_queue(q, 0, 0);
+}
+
+/* enqueue a event to singe queue */
+int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop)
+{
+	int dest;
+	queue_t *q;
+
+	snd_assert(cell != NULL, return -EINVAL);
+	dest = cell->event.queue;	/* destination queue */
+	q = queueptr(dest);
+	if (q == NULL)
+		return -EINVAL;
+	/* handle relative time stamps, convert them into absolute */
+	if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) {
+		switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+		case SNDRV_SEQ_TIME_STAMP_TICK:
+			cell->event.time.tick += q->timer->tick.cur_tick;
+			break;
+
+		case SNDRV_SEQ_TIME_STAMP_REAL:
+			snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time);
+			break;
+		}
+		cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK;
+		cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS;
+	}
+	/* enqueue event in the real-time or midi queue */
+	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+	case SNDRV_SEQ_TIME_STAMP_TICK:
+		snd_seq_prioq_cell_in(q->tickq, cell);
+		break;
+
+	case SNDRV_SEQ_TIME_STAMP_REAL:
+		snd_seq_prioq_cell_in(q->timeq, cell);
+		break;
+	}
+
+	/* trigger dispatching */
+	snd_seq_check_queue(q, atomic, hop);
+
+	queuefree(q); /* unlock */
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+static inline int check_access(queue_t *q, int client)
+{
+	return (q->owner == client) || (!q->locked && !q->klocked);
+}
+
+/* check if the client has permission to modify queue parameters.
+ * if it does, lock the queue
+ */
+static int queue_access_lock(queue_t *q, int client)
+{
+	unsigned long flags;
+	int access_ok;
+	
+	spin_lock_irqsave(&q->owner_lock, flags);
+	access_ok = check_access(q, client);
+	if (access_ok)
+		q->klocked = 1;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+	return access_ok;
+}
+
+/* unlock the queue */
+static inline void queue_access_unlock(queue_t *q)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 0;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+}
+
+/* exported - only checking permission */
+int snd_seq_queue_check_access(int queueid, int client)
+{
+	queue_t *q = queueptr(queueid);
+	int access_ok;
+	unsigned long flags;
+
+	if (! q)
+		return 0;
+	spin_lock_irqsave(&q->owner_lock, flags);
+	access_ok = check_access(q, client);
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+	queuefree(q);
+	return access_ok;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * change queue's owner and permission
+ */
+int snd_seq_queue_set_owner(int queueid, int client, int locked)
+{
+	queue_t *q = queueptr(queueid);
+
+	if (q == NULL)
+		return -EINVAL;
+
+	if (! queue_access_lock(q, client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	q->locked = locked ? 1 : 0;
+	q->owner = client;
+	queue_access_unlock(q);
+	queuefree(q);
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* open timer -
+ * q->use mutex should be down before calling this function to avoid
+ * confliction with snd_seq_queue_use()
+ */
+int snd_seq_queue_timer_open(int queueid)
+{
+	int result = 0;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	tmr = queue->timer;
+	if ((result = snd_seq_timer_open(queue)) < 0) {
+		snd_seq_timer_defaults(tmr);
+		result = snd_seq_timer_open(queue);
+	}
+	queuefree(queue);
+	return result;
+}
+
+/* close timer -
+ * q->use mutex should be down before calling this function
+ */
+int snd_seq_queue_timer_close(int queueid)
+{
+	queue_t *queue;
+	seq_timer_t *tmr;
+	int result = 0;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	tmr = queue->timer;
+	snd_seq_timer_close(queue);
+	queuefree(queue);
+	return result;
+}
+
+/* change queue tempo and ppq */
+int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info)
+{
+	queue_t *q = queueptr(queueid);
+	int result;
+
+	if (q == NULL)
+		return -EINVAL;
+	if (! queue_access_lock(q, client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	result = snd_seq_timer_set_tempo(q->timer, info->tempo);
+	if (result >= 0)
+		result = snd_seq_timer_set_ppq(q->timer, info->ppq);
+	if (result >= 0 && info->skew_base > 0)
+		result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base);
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	snd_seq_sync_update_tempo(q);
+#endif
+	queue_access_unlock(q);
+	queuefree(q);
+	return result;
+}
+
+
+/* use or unuse this queue -
+ * if it is the first client, starts the timer.
+ * if it is not longer used by any clients, stop the timer.
+ */
+int snd_seq_queue_use(int queueid, int client, int use)
+{
+	queue_t *queue;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	down(&queue->timer_mutex);
+	if (use) {
+		if (!test_and_set_bit(client, &queue->clients_bitmap))
+			queue->clients++;
+	} else {
+		if (test_and_clear_bit(client, &queue->clients_bitmap))
+			queue->clients--;
+	}
+	if (queue->clients) {
+		if (use && queue->clients == 1)
+			snd_seq_timer_defaults(queue->timer);
+		snd_seq_timer_open(queue);
+	} else {
+		snd_seq_timer_close(queue);
+	}
+	up(&queue->timer_mutex);
+	queuefree(queue);
+	return 0;
+}
+
+/*
+ * check if queue is used by the client
+ * return negative value if the queue is invalid.
+ * return 0 if not used, 1 if used.
+ */
+int snd_seq_queue_is_used(int queueid, int client)
+{
+	queue_t *q;
+	int result;
+
+	q = queueptr(queueid);
+	if (q == NULL)
+		return -EINVAL; /* invalid queue */
+	result = test_bit(client, &q->clients_bitmap) ? 1 : 0;
+	queuefree(q);
+	return result;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* notification that client has left the system -
+ * stop the timer on all queues owned by this client
+ */
+void snd_seq_queue_client_termination(int client)
+{
+	unsigned long flags;
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		spin_lock_irqsave(&q->owner_lock, flags);
+		if (q->owner == client)
+			q->klocked = 1;
+		spin_unlock_irqrestore(&q->owner_lock, flags);
+		if (q->owner == client) {
+			if (q->timer->running)
+				snd_seq_timer_stop(q->timer);
+			snd_seq_timer_reset(q->timer);
+		}
+		queuefree(q);
+	}
+}
+
+/* final stage notification -
+ * remove cells for no longer exist client (for non-owned queue)
+ * or delete this queue (for owned queue)
+ */
+void snd_seq_queue_client_leave(int client)
+{
+	int i;
+	queue_t *q;
+
+	/* delete own queues from queue list */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queue_list_remove(i, client)) != NULL)
+			queue_delete(q);
+	}
+
+	/* remove cells from existing queues -
+	 * they are not owned by this client
+	 */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		if (test_bit(client, q->clients_bitmap)) {
+			snd_seq_prioq_leave(q->tickq, client, 0);
+			snd_seq_prioq_leave(q->timeq, client, 0);
+			snd_seq_queue_use(q->queue, client, 0);
+		}
+		queuefree(q);
+	}
+}
+
+
+
+/*----------------------------------------------------------------*/
+
+/* remove cells from all queues */
+void snd_seq_queue_client_leave_cells(int client)
+{
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		snd_seq_prioq_leave(q->tickq, client, 0);
+		snd_seq_prioq_leave(q->timeq, client, 0);
+		queuefree(q);
+	}
+}
+
+/* remove cells based on flush criteria */
+void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info)
+{
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		if (test_bit(client, q->clients_bitmap) &&
+		    (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
+		     q->queue == info->queue)) {
+			snd_seq_prioq_remove_events(q->tickq, client, info);
+			snd_seq_prioq_remove_events(q->timeq, client, info);
+		}
+		queuefree(q);
+	}
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * send events to all subscribed ports
+ */
+static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop)
+{
+	snd_seq_event_t sev;
+
+	sev = *ev;
+	
+	sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS;
+	sev.time.tick = q->timer->tick.cur_tick;
+	sev.queue = q->queue;
+	sev.data.queue.queue = q->queue;
+
+	if (from_timer_port) {
+		/* broadcast events from Timer port */
+		sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
+		sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+		sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
+	}
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) {
+		/* broadcast events also to slave clients */
+		sev.source = q->sync_port;
+		sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
+	}
+#endif
+}
+
+/*
+ * process a received queue-control event.
+ * this function is exported for seq_sync.c.
+ */
+void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop)
+{
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_START:
+		snd_seq_prioq_leave(q->tickq, ev->source.client, 1);
+		snd_seq_prioq_leave(q->timeq, ev->source.client, 1);
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+		snd_seq_sync_clear(q);
+#endif
+		snd_seq_timer_start(q->timer);
+		queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+		q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+		snd_seq_sync_check(q, 0, atomic, hop); /* trigger the first signal */
+#endif
+		break;
+
+	case SNDRV_SEQ_EVENT_CONTINUE:
+		snd_seq_timer_continue(q->timer);
+		queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+		q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+		snd_seq_sync_check(q, 0, atomic, hop);
+#endif
+		break;
+
+	case SNDRV_SEQ_EVENT_STOP:
+		snd_seq_timer_stop(q->timer);
+		queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_TEMPO:
+		snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value);
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+		snd_seq_sync_update_tempo(q);
+#endif
+		queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_SETPOS_TICK:
+		if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) {
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+			snd_seq_sync_update_tick(q, 0, atomic, hop);
+#endif
+			queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+		}
+		break;
+
+	case SNDRV_SEQ_EVENT_SETPOS_TIME:
+		if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) {
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+			snd_seq_sync_update_time(q, 0, atomic, hop);
+#endif
+			queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_QUEUE_SKEW:
+		if (snd_seq_timer_set_skew(q->timer,
+					   ev->data.queue.param.skew.value,
+					   ev->data.queue.param.skew.base) == 0) {
+			queue_broadcast_event(q, ev, from_timer_port, atomic, hop);
+		}
+		break;
+	}
+}
+
+
+/*
+ * Queue control via timer control port:
+ * this function is exported as a callback of timer port.
+ */
+int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop)
+{
+	queue_t *q;
+
+	snd_assert(ev != NULL, return -EINVAL);
+	q = queueptr(ev->data.queue.queue);
+
+	if (q == NULL)
+		return -EINVAL;
+
+	if (! queue_access_lock(q, ev->source.client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	snd_seq_queue_process_event(q, ev, 1, atomic, hop);
+
+	queue_access_unlock(q);
+	queuefree(q);
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* exported to seq_info.c */
+void snd_seq_info_queues_read(snd_info_entry_t *entry, 
+			      snd_info_buffer_t * buffer)
+{
+	int i, bpm;
+	queue_t *q;
+	seq_timer_t *tmr;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+
+		tmr = q->timer;
+		if (tmr->tempo)
+			bpm = 60000000 / tmr->tempo;
+		else
+			bpm = 0;
+
+		snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
+		snd_iprintf(buffer, "owned by client    : %d\n", q->owner);
+		snd_iprintf(buffer, "lock status        : %s\n", q->locked ? "Locked" : "Free");
+		snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
+		snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
+		snd_iprintf(buffer, "timer state        : %s\n", tmr->running ? "Running" : "Stopped");
+		snd_iprintf(buffer, "timer PPQ          : %d\n", tmr->ppq);
+		snd_iprintf(buffer, "current tempo      : %d\n", tmr->tempo);
+		snd_iprintf(buffer, "current BPM        : %d\n", bpm);
+		snd_iprintf(buffer, "current time       : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
+		snd_iprintf(buffer, "current tick       : %d\n", tmr->tick.cur_tick);
+		snd_iprintf(buffer, "\n");
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+		snd_seq_sync_info_read(q, buffer); 
+#endif
+		queuefree(q);
+	}
+}
diff -Nru linux/sound/core/seq/seq_queue.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.h
--- linux/sound/core/seq/seq_queue.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,154 @@
+/*
+ *   ALSA sequencer Queue handling
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_QUEUE_H
+#define __SND_SEQ_QUEUE_H
+
+#include "seq_memory.h"
+#include "seq_prioq.h"
+#include "seq_timer.h"
+#include "seq_lock.h"
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+#include "seq_sync.h"
+#endif
+#include <linux/interrupt.h>
+#include <linux/list.h>
+
+#define SEQ_QUEUE_NO_OWNER (-1)
+
+struct _snd_seq_queue {
+	int queue;		/* queue number */
+
+	char name[64];		/* name of this queue */
+
+	prioq_t	*tickq;		/* midi tick event queue */
+	prioq_t	*timeq;		/* real-time event queue */	
+	
+	seq_timer_t *timer;	/* time keeper for this queue */
+	int	owner;		/* client that 'owns' the timer */
+	unsigned int	locked:1,	/* timer is only accesibble by owner if set */
+		klocked:1,	/* kernel lock (after START) */	
+		check_again:1,
+		check_blocked:1;
+
+	unsigned int flags;		/* status flags */
+	unsigned int info_flags;	/* info for sync */
+
+	spinlock_t owner_lock;
+	spinlock_t check_lock;
+
+	/* clients which uses this queue (bitmap) */
+	unsigned int clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned int)];
+	unsigned int clients;	/* users of this queue */
+	struct semaphore timer_mutex;
+
+	snd_use_lock_t use_lock;
+
+	struct tasklet_struct taskq;
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	struct list_head master_head;	/* list of masters */
+	queue_sync_t slave;		/* slave (exclusive) */
+
+	rwlock_t master_lock;
+	rwlock_t slave_lock;
+
+	snd_seq_addr_t sync_port;	/* address of the attached sync port */
+#endif
+};
+
+
+/* get the number of current queues */
+int snd_seq_queue_get_cur_queues(void);
+
+/* init queues structure */
+int snd_seq_queues_init(void);
+
+/* delete queues */ 
+void snd_seq_queues_delete(void);
+
+
+/* create new queue (constructor) */
+int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+
+/* delete queue (destructor) */
+int snd_seq_queue_delete(int client, int queueid);
+
+/* notification that client has left the system */
+void snd_seq_queue_client_termination(int client);
+
+/* final stage */
+void snd_seq_queue_client_leave(int client);
+
+/* enqueue a event received from one the clients */
+int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop);
+
+/* Remove events */
+void snd_seq_queue_client_leave_cells(int client);
+void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info);
+
+/* return pointer to queue structure for specified id */
+queue_t *queueptr(int queueid);
+/* unlock */
+#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
+
+/* return the (first) queue matching with the specified name */
+queue_t *snd_seq_queue_find_name(char *name);
+
+/* check single queue and dispatch events */
+void snd_seq_check_queue(queue_t *q, int atomic, int hop);
+
+/* access to queue's parameters */
+int snd_seq_queue_check_access(int queueid, int client);
+int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info);
+int snd_seq_queue_set_owner(int queueid, int client, int locked);
+int snd_seq_queue_set_locked(int queueid, int client, int locked);
+int snd_seq_queue_timer_open(int queueid);
+int snd_seq_queue_timer_close(int queueid);
+int snd_seq_queue_use(int queueid, int client, int use);
+int snd_seq_queue_is_used(int queueid, int client);
+
+int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop);
+void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop);
+
+/*
+ * 64bit division - for sync stuff..
+ */
+#if defined(i386) || defined(i486)
+
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divl %4"		\
+	   : "=a" ((u32)(q)),	\
+	     "=d" ((u32)(r))	\
+	   : "0" ((u32)(n0)),	\
+	     "1" ((u32)(n1)),	\
+	     "rm" ((u32)(d)))
+
+#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
+#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
+#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
+
+#else
+#define u64_div(x,y,q)	((q) = (u32)((u64)(x) / (u64)(y)))
+#define u64_mod(x,y,r)	((r) = (u32)((u64)(x) % (u64)(y)))
+#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
+#endif
+
+
+#endif
diff -Nru linux/sound/core/seq/seq_sync.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.c
--- linux/sound/core/seq/seq_sync.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1006 @@
+/*
+ *   ALSA sequencer queue synchronization routine
+ *
+ *   Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_clientmgr.h"
+#include "seq_fifo.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+#define FOR_EACH_LIST(var,list) \
+for (var = (list)->next; var != list; var = var->next)
+
+/*
+ * callbacks
+ */
+static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop);
+static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs);
+static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs);
+static void queue_delete_all_masters(queue_t *q);
+static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs);
+static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs);
+static void queue_sync_close_parser(queue_sync_t *sync, int slave);
+
+/*
+ * pre-defined event parsers
+ */
+
+extern seq_sync_parser_t snd_seq_midi_clock_parser; /* seq_midi_clock.c */
+extern seq_sync_parser_t snd_seq_mtc_parser; /* seq_mtc.c */
+extern seq_sync_parser_t snd_seq_dtl_parser; /* seq_dtl.c */
+
+static seq_sync_parser_t *event_parsers[] = {
+	&snd_seq_midi_clock_parser,
+	&snd_seq_mtc_parser,
+	&snd_seq_dtl_parser,
+	NULL
+};
+
+/*
+ * create a sync port corresponding to the specified queue
+ */
+int snd_seq_sync_create_port(queue_t *queue)
+{
+	snd_seq_port_info_t port;
+	snd_seq_port_callback_t pcallbacks;
+
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	memset(&port, 0, sizeof(port));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.event_input = event_input_sync;
+	pcallbacks.subscribe = queue_master_add;
+	pcallbacks.unsubscribe = queue_master_remove;
+	pcallbacks.use = queue_slave_set;
+	pcallbacks.unuse = queue_slave_reset;
+	pcallbacks.private_data = queue;
+	pcallbacks.callback_all = 1; /* call callbacks at each subscription */
+	port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ|
+		SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE|
+		SNDRV_SEQ_PORT_CAP_DUPLEX|
+		SNDRV_SEQ_PORT_CAP_SYNC_READ|SNDRV_SEQ_PORT_CAP_SYNC_WRITE;
+	port.type = 0;
+	sprintf(port.name, "Sync Queue %d", queue->queue);
+	port.kernel = &pcallbacks;
+	port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port.port = snd_seq_queue_sync_port(queue->queue);
+	if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_CREATE_PORT, &port) < 0)
+		return -ENOMEM;
+	queue->sync_port.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	queue->sync_port.port = port.port;
+	return 0;
+}
+
+/*
+ * delete attached sync port to the queue
+ */
+int snd_seq_sync_delete_port(queue_t *queue)
+{
+	snd_seq_port_info_t port;
+
+	memset(&port, 0, sizeof(port));
+	port.client = queue->sync_port.client;
+	port.port = queue->sync_port.port;
+	if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_DELETE_PORT, &port) < 0)
+		return -ENOMEM;
+	queue_delete_all_masters(queue);
+	queue_sync_close_parser(&queue->slave, 1);
+	return 0;
+}
+
+
+/*
+ * send a sync signal to the sync slave client
+ */
+static void queue_send_sync_event(queue_t *q, queue_sync_t *master, int type, int atomic, int hop)
+{
+	snd_seq_event_t event;
+
+	memset(&event, 0, sizeof(event));
+	
+	event.flags = SNDRV_SEQ_TIME_MODE_ABS;
+	/* since we use direct delivery, we have to convert time stamp here.. */
+	switch (master->format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		event.flags |= SNDRV_SEQ_TIME_STAMP_TICK;
+		event.time.tick = q->timer->tick.cur_tick;
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		event.flags |= SNDRV_SEQ_TIME_STAMP_REAL;
+		event.time.time = q->timer->cur_time;
+		break;
+	}
+	event.type = type;
+	event.data.queue.queue = q->queue;
+	event.data.queue.sync_format = master->format;
+	event.data.queue.sync_time_format = master->time_format;
+	event.data.queue.param.position = master->counter;
+	event.source = q->sync_port;
+	event.dest = master->addr;
+	if (master->parser) {
+		snd_seq_event_t newev;
+		newev = event;
+		if (master->parser->out.sync(master->parser_arg, &event, &newev) > 0) {
+			snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &newev, atomic, hop);
+			return;
+		}
+	}
+	snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &event, atomic, hop);
+}
+
+/*
+ * initialize the sync position
+ */
+static void queue_sync_clear(queue_sync_t *sync)
+{
+	memset(&sync->cur_time, 0, sizeof(sync->cur_time));
+	sync->counter = 0;
+	sync->sync_tick.cur_tick = 0;
+	sync->sync_tick.fraction = 0;
+}
+
+/*
+ * initialize all sync positions
+ */
+void snd_seq_sync_clear(queue_t *q)
+{
+	struct list_head *head;
+
+	/* clear master positions */
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		queue_sync_clear(master);
+	}
+	read_unlock(&q->master_lock);
+	read_lock(&q->slave_lock);
+	queue_sync_clear(&q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+
+/*
+ * change tick resolution of sync master/slave
+ */
+static void queue_sync_set_tick_resolution(queue_t *q, queue_sync_t *sync)
+{
+	unsigned int tempo, ppq;
+	tempo = q->timer->tempo;
+	if (sync->param.tick.ppq == 0)
+		ppq = q->timer->ppq;
+	else
+		ppq = sync->param.tick.ppq;
+	snd_seq_timer_set_tick_resolution(&sync->sync_tick, tempo, ppq, sync->param.tick.ticks);
+}
+
+
+/*
+ * update sync-master resolutions
+ */
+void snd_seq_sync_update_tempo(queue_t *q)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format & SNDRV_SEQ_SYNC_TICK)
+			queue_sync_set_tick_resolution(q, master);
+	}
+	read_unlock(&q->master_lock);
+	read_lock(&q->slave_lock);
+	if (q->slave.format & SNDRV_SEQ_SYNC_TICK)
+		queue_sync_set_tick_resolution(q, &q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+
+/*
+ * change the tick position from the current tick of the queue
+ */
+static void queue_sync_change_tick(queue_t *q, queue_sync_t *sync)
+{
+	if (sync->param.tick.ppq == 0)
+		sync->counter = q->timer->tick.cur_tick;
+	else
+		sync->counter = (q->timer->tick.cur_tick * sync->param.tick.ppq) / q->timer->ppq;
+	sync->counter /= sync->param.tick.ticks;
+	sync->sync_tick.cur_tick = sync->counter;
+	sync->sync_tick.fraction = 0;
+}
+
+/*
+ * change the time position from the current time of the queue
+ */
+static void queue_sync_change_time(queue_t *q, queue_sync_t *sync)
+{
+	/* we need 64bit calculation here.. */	
+	u64 nsec;
+
+	nsec = q->timer->cur_time.tv_sec;
+	nsec *= 1000000000UL;
+	nsec += q->timer->cur_time.tv_nsec;
+	u64_div(nsec, sync->param.time.resolution, sync->counter);
+	sync->counter *= sync->param.time.subframes;
+	sync->cur_time = q->timer->cur_time;
+}
+
+
+/*
+ * update the tick position of all sync
+ */
+void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format & SNDRV_SEQ_SYNC_TICK) {
+			queue_sync_change_tick(q, master);
+			queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop); /* broadcast to client */
+		}
+	}
+	read_unlock(&q->master_lock);
+	if (master_only)
+		return;
+	read_lock(&q->slave_lock);
+	if (q->slave.format & SNDRV_SEQ_SYNC_TICK)
+		queue_sync_change_tick(q, &q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+/*
+ * update the time position of all sync
+ */
+void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format & SNDRV_SEQ_SYNC_TIME) {
+			queue_sync_change_time(q, master);
+			queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop);
+		}
+	}
+	read_unlock(&q->master_lock);
+	if (master_only)
+		return;
+	read_lock(&q->slave_lock);
+	if (q->slave.format & SNDRV_SEQ_SYNC_TIME)
+		queue_sync_change_time(q, &q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+
+/*
+ * check the current timer value and send sync messages if the sync
+ * time is elapsed
+ */
+static void queue_master_check(queue_t *q, unsigned long ticks, int atomic, int hop)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		switch (master->format & SNDRV_SEQ_SYNC_MODE) {
+		case SNDRV_SEQ_SYNC_TICK:
+			snd_seq_timer_update_tick(&master->sync_tick, ticks);
+			while (master->sync_tick.cur_tick >= master->counter) {
+				queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop);
+				master->counter++;
+			}
+			break;
+		case SNDRV_SEQ_SYNC_TIME:
+			while (snd_seq_compare_real_time(&q->timer->cur_time, &master->cur_time)) {
+				queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop);
+				snd_seq_inc_time_nsec(&master->cur_time, master->resolution);
+				master->counter++;
+			}
+			break;
+		}
+	}
+	read_unlock(&q->master_lock);
+}
+
+
+/*
+ * slave stuff
+ */
+
+/*
+ * update tick
+ */
+static void queue_slave_check(queue_t *q, unsigned long ticks)
+{
+	switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		snd_seq_timer_update_tick(&q->slave.sync_tick, ticks);
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		/* nothing */
+		break;
+	}
+}
+
+/*
+ * slave synchronization in real-time unit
+ */
+static int queue_slave_sync_time(queue_t *q, unsigned int position)
+{
+	struct timeval tm;
+	long diff_time, new_period;
+	queue_sync_t *sync = &q->slave;
+	sndrv_seq_queue_time_sync_t *p = &sync->param.time;
+	seq_timer_t *tmr = q->timer;
+	u64 external_counter, tmp;
+
+	do_gettimeofday(&tm);
+	if (tmr->sync_start) {
+		/* XXX: we should use 64bit for diff_time, too. */
+		diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 +
+			((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec);
+		diff_time = (p->x0 * tmr->sync_time_diff + p->x1 * (diff_time * 1000)) / (p->x0 + p->x1);
+#define MIN_DIFF_TIME	1000	/* 1ms minimum */
+		if (diff_time < MIN_DIFF_TIME)
+			diff_time = MIN_DIFF_TIME;
+		tmr->sync_time_diff = diff_time;
+		tmp = (u64)tmr->base_period * (u64)sync->resolution;
+		u64_div(tmp, diff_time, new_period);
+
+		/* phase adjustment */
+		external_counter = position;
+		external_counter *= sync->resolution;
+
+		/* calculate current time */
+		tmp = snd_seq_timer_get_cur_nsec(tmr, &tm);
+
+		if (external_counter > tmp) {
+			tmp = external_counter - tmp;
+			if (tmp < p->max_time_diff) {
+				/* locked */
+				int hz = p->phase_correct_time / tmr->base_period;
+				diff_time = (u32)tmp;
+				diff_time /= hz;
+				new_period += diff_time;
+				q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			}
+		} else {
+			tmp = tmp - external_counter;
+			if (tmp == 0)
+				q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			else if (tmp < p->max_time_diff) {
+				/* locked */
+				int hz = p->phase_correct_time / tmr->base_period;
+				diff_time = (u32)tmp;
+				diff_time /= hz;
+				if (new_period - diff_time > MIN_DIFF_TIME) {
+					new_period -= diff_time;
+					q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+				} else
+					q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			}
+		}
+		tmr->period = new_period;
+	} else {
+		tmr->sync_start = 1;
+		tmr->sync_time_diff = sync->resolution;
+	}
+	tmr->sync_last_tm = tm;
+	sync->counter = position;
+
+	return 0;
+}
+
+/*
+ * slave synchronization in tick unit
+ */
+static int queue_slave_sync_tick(queue_t *q, unsigned int position)
+{
+	struct timeval tm;
+	long diff_time, tick_diff;
+	unsigned int tick_time;
+	queue_sync_t *sync = &q->slave;
+	seq_timer_t *tmr = q->timer;
+	sndrv_seq_queue_tick_sync_t *p = &sync->param.tick;
+
+	do_gettimeofday(&tm);
+	if (tmr->sync_start) {
+		/* XXX: diff_time should be 64bit for enough long sync period.. */
+		diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 +
+			((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec);
+		diff_time *= 1000; /* in nsec */
+		tick_time = (p->x0 * sync->sync_tick.resolution +
+			     p->x1 * diff_time) / (p->x0 + p->x1);
+		/* phase adjustment */
+		tick_diff = (long)position - (long)sync->sync_tick.cur_tick;
+		if (tick_diff != 0) {
+			if (tick_diff >= -p->max_tick_diff &&
+			    tick_diff <= p->max_tick_diff) {
+				/* locked */
+				q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+				tick_time = (tick_time * p->max_tick_diff2) /
+					(p->max_tick_diff2 + tick_diff);
+			} else {
+				/* sync lost.. freewheeling */
+				q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			}
+		} else
+			q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+
+#define MIN_TICK_TIME	1000	/* 1ms */
+		if (tick_time < MIN_TICK_TIME)
+			tick_time = MIN_TICK_TIME;
+
+		sync->sync_tick.resolution = tick_time;
+		snd_seq_timer_update_tick(&sync->sync_tick, 0);
+		if (p->ppq)
+			tmr->tick.resolution = (tick_time * p->ppq) / tmr->ppq;
+		else
+			tmr->tick.resolution = tick_time;
+		snd_seq_timer_update_tick(&tmr->tick, 0);
+		tmr->tempo = (tmr->tick.resolution * tmr->ppq) / 1000;
+
+	} else 
+		tmr->sync_start = 1;
+	tmr->sync_last_tm = tm;
+
+	sync->counter = position;
+
+	return 0;
+}
+
+
+/*
+ */
+static void queue_slave_jump_to_time(queue_t *q, unsigned int position, int atomic, int hop)
+{
+	u64 nsec;
+	queue_sync_t *sync = &q->slave;
+
+	q->slave.counter = position;
+	nsec = sync->counter;
+	nsec *= sync->resolution;
+	u64_divmod(nsec, 1000000000, sync->cur_time.tv_sec, sync->cur_time.tv_nsec);
+	q->timer->cur_time = sync->cur_time;
+
+	/* update master */
+	snd_seq_sync_update_time(q, 1, atomic, hop);
+}
+
+static void queue_slave_jump_to_tick(queue_t *q, unsigned int position, int atomic, int hop)
+{
+	unsigned int tick;
+	queue_sync_t *sync = &q->slave;
+
+	sync->counter = position;
+	sync->sync_tick.cur_tick = sync->counter;
+	sync->sync_tick.fraction = 0;
+
+	/* update queue timer */
+	if (sync->param.tick.ppq == 0)
+		tick = sync->counter;
+	else
+		tick = sync->counter * q->timer->ppq / sync->param.tick.ppq;
+	q->timer->tick.cur_tick = tick * sync->param.tick.ticks;
+	q->timer->tick.fraction = 0;
+
+	/* update master */
+	snd_seq_sync_update_tick(q, 1, atomic, hop);
+}
+
+
+/*
+ * event input callback
+ */
+static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+	queue_t *q = private_data;
+	unsigned long flags;
+	snd_seq_event_t newev;
+
+	snd_assert(q != NULL, return -ENXIO);
+
+	/* lock the queue owner access.. */
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 1;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+
+	read_lock(&q->slave_lock);
+	if (q->slave.format) {
+		if (q->slave.parser) {
+			memset(&newev, 0, sizeof(newev));
+			if (q->slave.parser->in.sync(q->slave.parser_arg, ev, &newev) > 0)
+				ev = &newev;
+		}
+	}
+	if (ev->type == SNDRV_SEQ_EVENT_SYNC) {
+		/* slave signal received */
+		switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) {
+		case SNDRV_SEQ_SYNC_TICK:
+			queue_slave_sync_tick(q, ev->data.queue.param.position);
+			break;
+		case SNDRV_SEQ_SYNC_TIME:
+			queue_slave_sync_time(q, ev->data.queue.param.position);
+			break;
+		}
+	} else if (ev->type == SNDRV_SEQ_EVENT_SYNC_POS) {
+		/* jump to position */
+		switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) {
+		case SNDRV_SEQ_SYNC_TICK:
+			if (q->timer->running)
+				queue_slave_sync_tick(q, ev->data.queue.param.position);
+			else
+				queue_slave_jump_to_tick(q, ev->data.queue.param.position, atomic, hop);
+			break;
+		case SNDRV_SEQ_SYNC_TIME:
+			if (q->timer->running)
+				queue_slave_sync_time(q, ev->data.queue.param.position);
+			else
+				queue_slave_jump_to_time(q, ev->data.queue.param.position, atomic, hop);
+			break;
+		}
+	} else {
+		/* control queue */
+		snd_seq_queue_process_event(q, ev, 0, atomic, hop);
+	}
+	read_unlock(&q->slave_lock);
+
+	/* unlock */
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 0;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+
+	return 0;
+}
+
+
+/*
+ * initialize sync parameters
+ */
+static int queue_param_init(queue_t *q, queue_sync_t *sync,
+			    snd_seq_addr_t *addr, sndrv_seq_queue_sync_t *info,
+			    int slave)
+{
+	seq_sync_parser_t *parser, **list;
+
+	sync->format = info->format;
+	sync->time_format = info->time_format;
+	*sync->opt_info = *info->info;
+	sync->addr = *addr;
+	/* copy params */
+	if (info->format&SNDRV_SEQ_SYNC_TICK)
+	    sync->param.tick=info->param.tick;
+	else
+	    sync->param.time=info->param.time;
+
+	sync->parser = NULL;
+	sync->parser_arg = NULL;
+	for (list = event_parsers; (parser = *list) != NULL; list++) {
+		if (parser->format == sync->format) {
+			int err;
+			if (slave)
+				err = parser->in.open(sync, &sync->parser_arg);
+			else
+				err = parser->out.open(sync, &sync->parser_arg);
+			if (err < 0)
+				return err;
+			sync->parser = parser;
+			break;
+		}
+	}
+
+	switch (sync->format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		if (sync->param.tick.ppq > 200)
+			goto __error;
+		if (sync->param.tick.ticks == 0)
+			sync->param.tick.ticks = 1;
+		queue_sync_set_tick_resolution(q, sync);
+		/* sync slave parameters -- will be configurable */
+		sync->param.tick.x0 = 4;
+		sync->param.tick.x1 = 1;
+		sync->param.tick.max_tick_diff = 50;
+		sync->param.tick.max_tick_diff2 = sync->param.tick.max_tick_diff * 2;
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		sync->resolution = sync->param.time.resolution;
+		if (sync->param.time.subframes == 0)
+			goto __error;
+		sync->resolution /= sync->param.time.subframes;
+		if (sync->resolution < 1000000) /* minimum = 1ms */
+			goto __error;
+		/* sync slave parameters -- will be configurable */
+		sync->param.time.x0 = 2;
+		sync->param.time.x1 = 1;
+		sync->param.time.max_time_diff = 1000000000UL; /* 1s */
+		sync->param.time.phase_correct_time = 100000000UL; /* 0.1s */
+		break;
+	default:
+		snd_printd("seq_sync: invalid format %x\n", sync->format);
+		goto __error;
+	}
+	return 0;
+
+__error:
+	queue_sync_close_parser(sync, slave);
+	return -EINVAL;
+}
+
+
+/*
+ * close event parser if exists
+ */
+static void queue_sync_close_parser(queue_sync_t *sync, int slave)
+{
+	if (sync->parser == NULL)
+		return;
+	if (slave) {
+		if (sync->parser->in.close)
+			sync->parser->in.close(sync->parser_arg);
+		else if (sync->parser_arg)
+			kfree(sync->parser_arg);
+	} else {
+		if (sync->parser->out.close)
+			sync->parser->out.close(sync->parser_arg);
+		else if (sync->parser_arg)
+			kfree(sync->parser_arg);
+	}
+	sync->parser = NULL;
+	sync->parser_arg = NULL;
+}
+
+
+/*
+ * add to master list
+ */
+static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	queue_sync_t *master;
+	unsigned long flags;
+	int err;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	master = snd_kcalloc(sizeof(*master), GFP_KERNEL);
+	if (master == NULL)
+		return -ENOMEM;
+	err = queue_param_init(q, master, &subs->dest, &subs->opt.sync_info, 0);
+	if (err < 0) {
+		kfree(master);
+		return err;
+	}
+	write_lock_irqsave(&q->master_lock, flags);
+	list_add(&master->list, &q->master_head);
+	write_unlock_irqrestore(&q->master_lock, flags);
+
+	return 0;
+}
+
+/*
+ * remove master
+ */
+static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	sndrv_seq_queue_sync_t *info;
+	snd_seq_addr_t *addr;
+	struct list_head *head;
+	unsigned long flags;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	info = &subs->opt.sync_info;
+	addr = &subs->dest;
+
+	write_lock_irqsave(&q->master_lock, flags);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format == info->format &&
+		    master->addr.client == addr->client &&
+		    master->addr.port == addr->port) {
+			list_del(&master->list);
+			write_unlock_irqrestore(&q->master_lock, flags);
+			queue_sync_close_parser(master, 0);
+			kfree(master);
+			return 0;
+		}
+	}
+	write_unlock_irqrestore(&q->master_lock, flags);
+	snd_printd("seq_queue: can't find master from %d.%d format %0x\n", addr->client, addr->port, info->format);
+	return -ENXIO;
+}
+
+/* remove all master connections if any exist */
+static void queue_delete_all_masters(queue_t *q)
+{
+	struct list_head *head;
+	unsigned long flags;
+
+	write_lock_irqsave(&q->master_lock, flags);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		list_del(&master->list);
+		queue_sync_close_parser(master, 0);
+		kfree(master);
+	}
+	write_unlock_irqrestore(&q->master_lock, flags);
+}
+
+/*
+ * set slave mode
+ */
+static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	unsigned long flags;
+	int err;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	write_lock_irqsave(&q->slave_lock, flags);
+	if (q->slave.format) {
+		write_unlock_irqrestore(&q->slave_lock, flags);
+		return -EBUSY;
+	}
+	err = queue_param_init(q, &q->slave, &subs->sender,
+			       &subs->opt.sync_info, 1);
+	if (err < 0) {
+		q->slave.format = 0;
+		write_unlock_irqrestore(&q->slave_lock, flags);
+		return err;
+	}
+	write_unlock_irqrestore(&q->slave_lock, flags);
+	return 0;
+}
+
+/*
+ * remove slave mode
+ */
+static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	unsigned long flags;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	write_lock_irqsave(&q->slave_lock, flags);
+	if (q->slave.addr.client == subs->sender.client &&
+	    q->slave.addr.port == subs->sender.port) {
+		q->slave.format = 0;
+		queue_sync_close_parser(&q->slave, 1);
+		write_unlock_irqrestore(&q->slave_lock, flags);
+		return 0;
+	}
+	write_unlock_irqrestore(&q->slave_lock, flags);
+	snd_printd("seq_queue: can't match slave condition\n");
+	return -ENXIO;
+}
+
+
+/*
+ * sync check
+ * this function is called at each timer interrupt.
+ */
+
+void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop)
+{
+	queue_master_check(q, resolution, atomic, hop);
+	queue_slave_check(q, resolution);
+}
+
+
+/*
+ * support functions for SMPTE time frame
+ */
+static unsigned int linear_time_to_position(sndrv_seq_time_frame_t time,
+					    int nframes, int nsubs)
+{
+	unsigned int count;
+	count = time.hour * 60 + time.min;
+	count = count * 60 + time.sec;
+	count = count * nframes + time.frame;
+	count = count * nsubs + time.subframe;
+	return count;
+}
+
+static sndrv_seq_time_frame_t linear_position_to_time(unsigned int count,
+						      int nframes, int nsubs)
+{
+	sndrv_seq_time_frame_t time;
+	time.subframe = count % nsubs;
+	count /= nsubs;
+	time.hour = count / (3600 * nframes);
+	count %= 3600 * nframes;
+	time.min = count / (60 * nframes);
+	count %= 60 * nframes;
+	time.sec = count / nframes;
+	time.frame = count % nframes;
+	return time;
+}
+
+/* drop frame - only 30fps */
+#define NFRAMES		30
+#define FRAMES_PER_MIN		(NFRAMES * 60 - 2)
+#define FRAMES_PER_10MIN	(FRAMES_PER_MIN * 10 + 2)
+#define FRAMES_PER_HOUR		(FRAMES_PER_10MIN * 6)
+
+static unsigned int drop_time_to_position(sndrv_seq_time_frame_t time, int nsubs)
+{
+	unsigned int count, min;
+
+	min = time.min % 10;
+	count = time.frame;
+	if (min > 0) {
+		if (time.sec == 0 && time.frame < 2)
+			count = 2;
+	}
+	count += time.sec * NFRAMES;
+	count += min * FRAMES_PER_MIN;
+	count += (time.min / 10) * FRAMES_PER_10MIN;
+	count += time.hour * (FRAMES_PER_HOUR);
+	count *= nsubs;
+	count += time.subframe;
+
+	return count;
+}
+
+static sndrv_seq_time_frame_t drop_position_to_time(int count, int nsubs)
+{
+	unsigned int min10;
+	sndrv_seq_time_frame_t time;
+
+	time.subframe = count % nsubs;
+	count /= nsubs;
+	min10 = count / FRAMES_PER_10MIN;
+	time.hour = min10 / 6;
+	min10 %= 6;
+	count %= FRAMES_PER_10MIN;
+	if (count < 2) {
+		time.min = min10 * 10;
+		time.sec = 0;
+	} else {
+		count -= 2;
+		time.min = count / FRAMES_PER_MIN;
+		time.min += min10 * 10;
+		count %= FRAMES_PER_MIN;
+		count += 2;
+		time.sec = count / NFRAMES;
+		count %= NFRAMES;
+	}
+	time.frame = count;
+
+	return time;
+}
+
+/* convert from position counter to time frame */
+sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos)
+{
+	switch (format) {
+	case SNDRV_SEQ_SYNC_FPS_24:
+		return linear_position_to_time(pos, 24, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_25:
+		return linear_position_to_time(pos, 25, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_NDP:
+		return linear_position_to_time(pos, 30, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_DP:
+	default:
+		return drop_position_to_time(pos, nsubs);
+	}
+}
+
+/* convert from position counter to time frame */
+unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime)
+{
+	switch (format) {
+	case SNDRV_SEQ_SYNC_FPS_24:
+		return linear_time_to_position(*rtime, 24, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_25:
+		return linear_time_to_position(*rtime, 25, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_NDP:
+		return linear_time_to_position(*rtime, 30, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_DP:
+	default:
+		return drop_time_to_position(*rtime, nsubs);
+	}
+}
+
+/* resolution in nsec */
+unsigned long snd_seq_get_smpte_resolution(int time_format)
+{
+	switch (time_format) {
+	case SNDRV_SEQ_SYNC_FPS_24:
+		return 1000000000UL / 24;
+	case SNDRV_SEQ_SYNC_FPS_25:
+		return 1000000000UL / 25;
+	case SNDRV_SEQ_SYNC_FPS_30_DP:
+	case SNDRV_SEQ_SYNC_FPS_30_NDP:
+		return (unsigned long)(1000000000.0/29.97);
+	}
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+
+static void print_sync_info(snd_info_buffer_t *buffer, queue_sync_t *sync)
+{
+	snd_iprintf(buffer, " [%s] ==> %d:%d\n",
+		    (sync->format & SNDRV_SEQ_SYNC_TICK ? "tick" : "time"),
+		    sync->addr.client, sync->addr.port);
+	snd_iprintf(buffer, "    format 0x%0x / time_format %d\n",
+		    sync->format, sync->time_format);
+	switch (sync->format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		snd_iprintf(buffer, "   ppq: %d, ticks: %d\n",
+			    sync->param.tick.ppq, sync->param.tick.ticks);
+		snd_iprintf(buffer, "   resolution: %ld ns, position: %d\n",
+			    sync->sync_tick.resolution,
+			    sync->counter);
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		snd_iprintf(buffer, "   subframes %d, resolution: %ld ns, position: %d\n",
+			    sync->param.time.subframes,
+			    sync->resolution,
+			    sync->counter);
+		break;
+	}
+}
+
+void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer)
+{
+	struct list_head *head;
+	int count = 0;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		snd_iprintf(buffer, "master %d", count);
+		print_sync_info(buffer, master);
+		count++;
+	}
+	read_unlock(&q->master_lock);
+	if (q->slave.format) {
+		snd_iprintf(buffer, "slave");
+		print_sync_info(buffer, &q->slave);
+		count++;
+	}
+	if (count)
+		snd_iprintf(buffer, "\n");
+}
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
diff -Nru linux/sound/core/seq/seq_sync.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.h
--- linux/sound/core/seq/seq_sync.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,79 @@
+/*
+ *  Synchronization of ALSA sequencer queues
+ *
+ *   Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_SYNC_H
+#define __SND_SEQ_SYNC_H
+
+typedef struct snd_queue_sync queue_sync_t;
+typedef struct snd_seq_sync_parser seq_sync_parser_t;
+typedef void *seq_sync_arg_t;
+
+struct snd_queue_sync {
+	unsigned char format;
+	unsigned char time_format;
+	unsigned char opt_info[6];	/* optional info */
+	snd_seq_addr_t addr;		/* master/slave address */
+
+	unsigned int counter;		/* current position */
+	unsigned long resolution;	/* resolution for time */
+	snd_seq_real_time_t cur_time;	/* current time */
+	seq_timer_tick_t sync_tick;		/* tick info */
+
+	union {
+		struct sndrv_seq_queue_tick_sync tick;
+		struct sndrv_seq_queue_time_sync time;
+	} param;
+
+	seq_sync_parser_t *parser;
+	seq_sync_arg_t parser_arg;
+
+	struct list_head list;
+};
+
+
+struct seq_sync_parser_ops {
+	int (*open)(queue_sync_t *sync_info, seq_sync_arg_t *retp);
+	int (*sync)(seq_sync_arg_t arg, const snd_seq_event_t *src, snd_seq_event_t *ev);
+	int (*close)(seq_sync_arg_t arg);
+};
+
+struct snd_seq_sync_parser {
+	unsigned int format;	/* supported format */
+	struct seq_sync_parser_ops in;	/* sync-in (slave) */
+	struct seq_sync_parser_ops out;	/* sync-out (mastering) */
+};
+
+/*
+ * prototypes
+ */
+int snd_seq_sync_create_port(queue_t *queue);
+int snd_seq_sync_delete_port(queue_t *queue);
+void snd_seq_sync_clear(queue_t *q);
+void snd_seq_sync_update_tempo(queue_t *q);
+void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop);
+void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop);
+void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop);
+
+sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos);
+unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime);
+unsigned long snd_seq_get_smpte_resolution(int time_format);
+
+
+#endif
diff -Nru linux/sound/core/seq/seq_system.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.c
--- linux/sound/core/seq/seq_system.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,183 @@
+/*
+ *   ALSA sequencer System services Client
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "seq_system.h"
+#include "seq_timer.h"
+#include "seq_queue.h"
+
+/* internal client that provide system services, access to timer etc. */
+
+/*
+ * Port "Timer"
+ *      - send tempo /start/stop etc. events to this port to manipulate the 
+ *        queue's timer. The queue address is specified in
+ *	  data.queue.queue.
+ *      - this port supports subscription. The received timer events are 
+ *        broadcasted to all subscribed clients. The modified tempo
+ *	  value is stored on data.queue.value.
+ *	  The modifier client/port is not send.
+ *
+ * Port "Announce"
+ *      - does not receive message
+ *      - supports supscription. For each client or port attaching to or 
+ *        detaching from the system an announcement is send to the subscribed
+ *        clients.
+ *
+ * Idea: the subscription mechanism might also work handy for distributing 
+ * synchronisation and timing information. In this case we would ideally have
+ * a list of subscribers for each type of sync (time, tick), for each timing
+ * queue.
+ *
+ * NOTE: the queue to be started, stopped, etc. must be specified
+ *	 in data.queue.addr.queue field.  queue is used only for
+ *	 scheduling, and no longer referred as affected queue.
+ *	 They are used only for timer broadcast (see above).
+ *							-- iwai
+ */
+
+
+/* client id of our system client */
+static int sysclient = -1;
+
+/* port id numbers for this client */
+static int announce_port = -1;
+
+
+
+/* fill standard header data, source port & channel are filled in */
+static int setheader(snd_seq_event_t * ev, int client, int port)
+{
+	if (announce_port < 0)
+		return -ENODEV;
+
+	memset(ev, 0, sizeof(snd_seq_event_t));
+
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+
+	ev->source.client = sysclient;
+	ev->source.port = announce_port;
+	ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+
+	/* fill data */
+	/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
+	ev->data.addr.client = client;
+	ev->data.addr.port = port;
+
+	return 0;
+}
+
+
+/* entry points for broadcasting system events */
+void snd_seq_system_broadcast(int client, int port, int type)
+{
+	snd_seq_event_t ev;
+	
+	if (setheader(&ev, client, port) < 0)
+		return;
+	ev.type = type;
+	snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
+}
+
+/* entry points for broadcasting system events */
+int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev)
+{
+	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	ev->source.client = sysclient;
+	ev->source.port = announce_port;
+	ev->dest.client = client;
+	ev->dest.port = port;
+	return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
+}
+
+/* call-back handler for timer events */
+static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+	return snd_seq_control_queue(ev, atomic, hop);
+}
+
+/* register our internal client */
+int __init snd_seq_system_client_init(void)
+{
+
+	snd_seq_client_callback_t callbacks;
+	snd_seq_port_callback_t pcallbacks;
+	snd_seq_client_info_t inf;
+	snd_seq_port_info_t port;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	memset(&inf, 0, sizeof(inf));
+	memset(&port, 0, sizeof(port));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.event_input = event_input_timer;
+
+	/* register client */
+	callbacks.allow_input = callbacks.allow_output = 1;
+	sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks);
+
+	/* set our name */
+	inf.client = 0;
+	inf.type = KERNEL_CLIENT;
+	strcpy(inf.name, "System");
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf);
+
+	/* register timer */
+	strcpy(port.name, "Timer");
+	port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
+	port.capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
+	port.kernel = &pcallbacks;
+	port.type = 0;
+	port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port.addr.client = sysclient;
+	port.addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
+
+	/* register announcement port */
+	strcpy(port.name, "Announce");
+	port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
+	port.kernel = NULL;
+	port.type = 0;
+	port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port.addr.client = sysclient;
+	port.addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
+	announce_port = port.addr.port;
+
+	return 0;
+}
+
+
+/* unregister our internal client */
+void __exit snd_seq_system_client_done(void)
+{
+	int oldsysclient = sysclient;
+
+	if (oldsysclient >= 0) {
+		sysclient = -1;
+		announce_port = -1;
+		snd_seq_delete_kernel_client(oldsysclient);
+	}
+}
diff -Nru linux/sound/core/seq/seq_system.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.h
--- linux/sound/core/seq/seq_system.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,46 @@
+/*
+ *  ALSA sequencer System Client
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_SYSTEM_H
+#define __SND_SEQ_SYSTEM_H
+
+#include <sound/seq_kernel.h>
+
+
+/* entry points for broadcasting system events */
+void snd_seq_system_broadcast(int client, int port, int type);
+
+#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
+#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
+#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
+#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
+#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
+#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
+
+int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev);
+
+/* register our internal client */
+int snd_seq_system_client_init(void);
+
+/* unregister our internal client */
+void snd_seq_system_client_done(void);
+
+
+#endif
diff -Nru linux/sound/core/seq/seq_timer.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.c
--- linux/sound/core/seq/seq_timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,432 @@
+/*
+ *   ALSA sequencer Timer
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *                              Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_timer.h"
+#include "seq_queue.h"
+#include "seq_info.h"
+
+extern int snd_seq_default_timer_class;
+extern int snd_seq_default_timer_sclass;
+extern int snd_seq_default_timer_card;
+extern int snd_seq_default_timer_device;
+extern int snd_seq_default_timer_subdevice;
+extern int snd_seq_default_timer_resolution;
+
+#define SKEW_BASE	0x10000	/* 16bit shift */
+
+void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks)
+{
+	if (tempo < 1000000)
+		tick->resolution = (tempo * 1000) / ppq;
+	else {
+		/* might overflow.. */
+		unsigned int s;
+		s = tempo % ppq;
+		s = (s * 1000) / ppq;
+		tick->resolution = (tempo / ppq) * 1000;
+		tick->resolution += s;
+	}
+	if (tick->resolution <= 0)
+		tick->resolution = 1;
+	tick->resolution *= nticks;
+	snd_seq_timer_update_tick(tick, 0);
+}
+
+/* create new timer (constructor) */
+seq_timer_t *snd_seq_timer_new(void)
+{
+	seq_timer_t *tmr;
+	
+	tmr = snd_kcalloc(sizeof(seq_timer_t), GFP_KERNEL);
+	if (tmr == NULL) {
+		snd_printd("malloc failed for snd_seq_timer_new() \n");
+		return NULL;
+	}
+	spin_lock_init(&tmr->lock);
+
+	/* reset setup to defaults */
+	snd_seq_timer_defaults(tmr);
+	
+	/* reset time */
+	snd_seq_timer_reset(tmr);
+	
+	return tmr;
+}
+
+/* delete timer (destructor) */
+void snd_seq_timer_delete(seq_timer_t **tmr)
+{
+	seq_timer_t *t = *tmr;
+	*tmr = NULL;
+
+	if (t == NULL) {
+		snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
+		return;
+	}
+	t->running = 0;
+
+	/* reset time */
+	snd_seq_timer_stop(t);
+	snd_seq_timer_reset(t);
+
+	kfree(t);
+}
+
+void snd_seq_timer_defaults(seq_timer_t * tmr)
+{
+	/* setup defaults */
+	tmr->ppq = 96;		/* 96 PPQ */
+	tmr->tempo = 500000;	/* 120 BPM */
+	snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
+	tmr->running = 0;
+
+	tmr->type = SNDRV_SEQ_TIMER_ALSA;
+	tmr->alsa_id.dev_class = snd_seq_default_timer_class;
+	tmr->alsa_id.dev_sclass = snd_seq_default_timer_sclass;
+	tmr->alsa_id.card = snd_seq_default_timer_card;
+	tmr->alsa_id.device = snd_seq_default_timer_device;
+	tmr->alsa_id.subdevice = snd_seq_default_timer_subdevice;
+	tmr->preferred_resolution = snd_seq_default_timer_resolution;
+
+	tmr->skew = tmr->skew_base = SKEW_BASE;
+}
+
+void snd_seq_timer_reset(seq_timer_t * tmr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tmr->lock, flags);
+
+	/* reset time & songposition */
+	tmr->cur_time.tv_sec = 0;
+	tmr->cur_time.tv_nsec = 0;
+
+	tmr->tick.cur_tick = 0;
+	tmr->tick.fraction = 0;
+
+	spin_unlock_irqrestore(&tmr->lock, flags);
+}
+
+
+/* called by timer interrupt routine. the period time since previous invocation is passed */
+static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri,
+				    unsigned long resolution,
+				    unsigned long ticks, void *data)
+{
+	unsigned long flags;
+	queue_t *q = (queue_t *)data;
+	seq_timer_t *tmr;
+
+	if (q == NULL)
+		return;
+	tmr = q->timer;
+	if (tmr == NULL)
+		return;
+	if (!tmr->running)
+		return;
+
+	resolution *= ticks;
+	if (tmr->skew != tmr->skew_base) {
+		/* FIXME: assuming skew_base = 0x10000 */
+		resolution = (resolution >> 16) * tmr->skew +
+			(((resolution & 0xffff) * tmr->skew) >> 16);
+	}
+
+	spin_lock_irqsave(&tmr->lock, flags);
+
+	/* update timer */
+	snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
+
+	/* calculate current tick */
+	snd_seq_timer_update_tick(&tmr->tick, resolution);
+
+	/* register actual time of this timer update */
+	do_gettimeofday(&tmr->last_update);
+
+	spin_unlock_irqrestore(&tmr->lock, flags);
+
+	/* check queues and dispatch events */
+	snd_seq_check_queue(q, 1, 0);
+}
+
+/* set current tempo */
+int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+	if (tempo <= 0)
+		return -EINVAL;
+	spin_lock_irqsave(&tmr->lock, flags);
+	if (tempo != tmr->tempo) {
+		tmr->tempo = tempo;
+		snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
+	}
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current ppq */
+int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+	if (ppq <= 0)
+		return -EINVAL;
+	spin_lock_irqsave(&tmr->lock, flags);
+	if (tmr->running && (ppq != tmr->ppq)) {
+		/* refuse to change ppq on running timers */
+		/* because it will upset the song position (ticks) */
+		spin_unlock_irqrestore(&tmr->lock, flags);
+		snd_printd("seq: cannot change ppq of a running timer\n");
+		return -EBUSY;
+	}
+
+	tmr->ppq = ppq;
+	snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current tick position */
+int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->tick.cur_tick = position;
+	tmr->tick.fraction = 0;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current real-time position */
+int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	snd_seq_sanity_real_time(&position);
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->cur_time = position;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set timer skew */
+int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	/* FIXME */
+	if (base != SKEW_BASE) {
+		snd_printd("invalid skew base 0x%x\n", base);
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->skew = skew;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+int snd_seq_timer_open(queue_t *q)
+{
+	snd_timer_instance_t *t;
+	seq_timer_t *tmr;
+	char str[32];
+
+	tmr = q->timer;
+	snd_assert(tmr != NULL, return -EINVAL);
+	if (tmr->timeri)
+		return -EBUSY;
+	sprintf(str, "sequencer queue %i", q->queue);
+	if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {	/* standard ALSA timer */
+		if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
+			tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
+		t = snd_timer_open(str, &tmr->alsa_id, q->queue);
+		if (t == NULL && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
+			if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
+			    tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
+				snd_timer_id_t tid;
+				memset(&tid, 0, sizeof(tid));
+				tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+				tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
+				tid.card = -1;
+				tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
+				t = snd_timer_open(str, &tid, q->queue);
+			}
+			if (t == NULL) {
+				snd_printk(KERN_ERR "fatal error: cannot create timer\n");
+				return -ENODEV;
+			}
+		}
+	} else {
+		return -EINVAL;
+	}
+	t->callback = snd_seq_timer_interrupt;
+	t->callback_data = q;
+	t->flags |= SNDRV_TIMER_IFLG_AUTO;
+	tmr->timeri = t;
+	return 0;
+}
+
+int snd_seq_timer_close(queue_t *q)
+{
+	seq_timer_t *tmr;
+	
+	tmr = q->timer;
+	snd_assert(tmr != NULL, return -EINVAL);
+	if (tmr->timeri) {
+		snd_timer_stop(tmr->timeri);
+		snd_timer_close(tmr->timeri);
+		tmr->timeri = NULL;
+	}
+	return 0;
+}
+
+void snd_seq_timer_stop(seq_timer_t * tmr)
+{
+	if (! tmr->timeri)
+		return;
+	if (!tmr->running)
+		return;
+	tmr->running = 0;
+	snd_timer_stop(tmr->timeri);
+}
+
+static int initialize_timer(seq_timer_t *tmr)
+{
+	snd_timer_t *t;
+	t = tmr->timeri->timer;
+	snd_assert(t, return -EINVAL);
+
+	tmr->ticks = 1;
+	if (tmr->preferred_resolution &&
+	    ! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+		unsigned long r = t->hw.resolution;
+		if (! r && t->hw.c_resolution)
+			r = t->hw.c_resolution(t);
+		if (r) {
+			tmr->ticks = (unsigned int)(tmr->preferred_resolution / r);
+			if (! tmr->ticks)
+				tmr->ticks = 1;
+		}
+	}
+	tmr->initialized = 1;
+	return 0;
+}
+
+void snd_seq_timer_start(seq_timer_t * tmr)
+{
+	if (! tmr->timeri)
+		return;
+	if (tmr->running)
+		snd_seq_timer_stop(tmr);
+	snd_seq_timer_reset(tmr);
+	if (initialize_timer(tmr) < 0)
+		return;
+	snd_timer_start(tmr->timeri, tmr->ticks);
+	tmr->running = 1;
+	do_gettimeofday(&tmr->last_update);
+}
+
+void snd_seq_timer_continue(seq_timer_t * tmr)
+{
+	if (! tmr->timeri)
+		return;
+	if (tmr->running)
+		return;
+	if (! tmr->initialized)
+		if (initialize_timer(tmr) < 0)
+			return;
+	snd_timer_start(tmr->timeri, tmr->ticks);
+	tmr->running = 1;
+	do_gettimeofday(&tmr->last_update);
+}
+
+/* return current 'real' time. use timeofday() to get better granularity. */
+snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr)
+{
+	snd_seq_real_time_t cur_time;
+
+	cur_time = tmr->cur_time;
+	if (tmr->running) { 
+		struct timeval tm;
+		int usec;
+		do_gettimeofday(&tm);
+		usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
+		if (usec < 0) {
+			cur_time.tv_nsec += (1000000 + usec) * 1000;
+			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
+		} else {
+			cur_time.tv_nsec += usec * 1000;
+			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
+		}
+		snd_seq_sanity_real_time(&cur_time);
+	}
+                
+	return cur_time;	
+}
+
+/* TODO: use interpolation on tick queue (will only be usefull for very
+ high PPQ values) */
+snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr)
+{
+	return tmr->tick.cur_tick;
+}
+
+
+/* exported to seq_info.c */
+void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	int idx;
+	queue_t *q;
+	seq_timer_t *tmr;
+	snd_timer_instance_t *ti;
+	unsigned long resolution;
+	
+	for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
+		q = queueptr(idx);
+		if (q == NULL)
+			continue;
+		if ((tmr = q->timer) == NULL ||
+		    (ti = tmr->timeri) == NULL) {
+			queuefree(q);
+			continue;
+		}
+		snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
+		resolution = snd_timer_resolution(ti) * tmr->ticks;
+		snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
+		snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
+		queuefree(q);
+ 	}
+}
diff -Nru linux/sound/core/seq/seq_timer.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.h
--- linux/sound/core/seq/seq_timer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,151 @@
+/*
+ *  ALSA sequencer Timer
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@home.nl>
+ *
+ *
+ *   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
+ *
+ */
+#ifndef __SND_SEQ_TIMER_H
+#define __SND_SEQ_TIMER_H
+
+#include <sound/timer.h>
+#include <sound/seq_kernel.h>
+
+typedef struct {
+	snd_seq_tick_time_t	cur_tick;	/* current tick */
+	unsigned long		resolution;	/* time per tick in nsec */
+	unsigned long		fraction;	/* current time per tick in nsec */
+} seq_timer_tick_t;
+
+typedef struct {
+	/* ... tempo / offset / running state */
+
+	unsigned int		running:1,	/* running state of queue */	
+				initialized:1;	/* timer is initialized */
+
+	unsigned int		tempo;		/* current tempo, us/tick */
+	int			ppq;		/* time resolution, ticks/quarter */
+
+	snd_seq_real_time_t	cur_time;	/* current time */
+	seq_timer_tick_t	tick;		/* current tick */
+	int tick_updated;
+	
+	int			type;		/* timer type */
+	snd_timer_id_t		alsa_id;	/* ALSA's timer ID */
+	snd_timer_instance_t	*timeri;	/* timer instance */
+	unsigned int		ticks;
+	unsigned long		preferred_resolution; /* timer resolution */
+
+	unsigned int skew;
+	unsigned int skew_base;
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+	int sync_start;
+	struct timeval sync_last_tm;
+	unsigned int sync_time_diff;
+#endif
+
+	struct timeval 		last_update;	 /* time of last clock update, used for interpolation */
+
+	spinlock_t lock;
+} seq_timer_t;
+
+
+/* create new timer (constructor) */
+extern seq_timer_t *snd_seq_timer_new(void);
+
+/* delete timer (destructor) */
+extern void snd_seq_timer_delete(seq_timer_t **tmr);
+
+void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks);
+
+/* */
+static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution)
+{
+	if (tick->resolution > 0) {
+		tick->fraction += resolution;
+		tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution);
+		tick->fraction %= tick->resolution;
+	}
+}
+
+
+/* compare timestamp between events */
+/* return 1 if a >= b; otherwise return 0 */
+static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b)
+{
+	/* compare ticks */
+	return (*a >= *b);
+}
+
+static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b)
+{
+	/* compare real time */
+	if (a->tv_sec > b->tv_sec)
+		return 1;
+	if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec))
+		return 1;
+	return 0;
+}
+
+
+static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm)
+{
+	while (tm->tv_nsec >= 1000000000) {
+		/* roll-over */
+		tm->tv_nsec -= 1000000000;
+                tm->tv_sec++;
+        }
+}
+
+
+/* increment timestamp */
+static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc)
+{
+	tm->tv_sec  += inc->tv_sec;
+	tm->tv_nsec += inc->tv_nsec;
+	snd_seq_sanity_real_time(tm);
+}
+
+static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec)
+{
+	tm->tv_nsec  += nsec;
+	snd_seq_sanity_real_time(tm);
+}
+
+/* called by timer isr */
+int snd_seq_timer_open(queue_t *q);
+int snd_seq_timer_close(queue_t *q);
+int snd_seq_timer_midi_open(queue_t *q);
+int snd_seq_timer_midi_close(queue_t *q);
+void snd_seq_timer_defaults(seq_timer_t *tmr);
+void snd_seq_timer_reset(seq_timer_t *tmr);
+void snd_seq_timer_stop(seq_timer_t *tmr);
+void snd_seq_timer_start(seq_timer_t *tmr);
+void snd_seq_timer_continue(seq_timer_t *tmr);
+int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo);
+int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq);
+int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position);
+int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position);
+int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base);
+snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr);
+snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr);
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+u64 snd_seq_timer_get_cur_nsec(seq_timer_t *tmr, struct timeval *tm);
+#endif
+                        
+#endif
diff -Nru linux/sound/core/seq/seq_virmidi.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_virmidi.c
--- linux/sound/core/seq/seq_virmidi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_virmidi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,525 @@
+/*
+ *  Virtual Raw MIDI client on Sequencer
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@perex.cz>
+ *
+ *   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
+ *
+ */
+
+/*
+ * Virtual Raw MIDI client
+ *
+ * The virtual rawmidi client is a sequencer client which associate
+ * a rawmidi device file.  The created rawmidi device file can be
+ * accessed as a normal raw midi, but its MIDI source and destination
+ * are arbitrary.  For example, a user-client software synth connected
+ * to this port can be used as a normal midi device as well.
+ *
+ * The virtual rawmidi device accepts also multiple opens.  Each file
+ * has its own input buffer, so that no conflict would occur.  The drain
+ * of input/output buffer acts only to the local buffer.
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_event.h>
+#include <sound/seq_virmidi.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
+MODULE_LICENSE("GPL");
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+/*
+ * initialize an event record
+ */
+static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev)
+{
+	memset(ev, 0, sizeof(*ev));
+	ev->source.port = vmidi->port;
+	switch (vmidi->seq_mode) {
+	case SNDRV_VIRMIDI_SEQ_DISPATCH:
+		ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		break;
+	case SNDRV_VIRMIDI_SEQ_ATTACH:
+		/* FIXME: source and destination are same - not good.. */
+		ev->dest.client = vmidi->client;
+		ev->dest.port = vmidi->port;
+		break;
+	}
+}
+
+/*
+ * decode input event and put to read buffer of each opened file
+ */
+static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev)
+{
+	snd_virmidi_t *vmidi;
+	struct list_head *list;
+	unsigned char msg[4];
+	int len;
+
+	snd_assert(rdev != NULL, return -EINVAL);
+
+	if (!(rdev->flags & SNDRV_VIRMIDI_USE))
+		return 0; /* ignored */
+
+	read_lock(&rdev->filelist_lock);
+	list_for_each(list, &rdev->filelist) {
+		vmidi = list_entry(list, snd_virmidi_t, list);
+		if (!vmidi->trigger)
+			continue;
+		if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
+			if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+				continue;
+			snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+		} else {
+			len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
+			if (len > 0)
+				snd_rawmidi_receive(vmidi->substream, msg, len);
+		}
+	}
+	read_unlock(&rdev->filelist_lock);
+
+	return 0;
+}
+
+/*
+ * event_input callback from sequencer
+ */
+int snd_virmidi_receive(snd_seq_event_t *ev, int direct,
+			void *private_data, int atomic, int hop)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL);
+	return snd_virmidi_dev_receive_event(rdev, ev);
+}
+
+/*
+ * trigger rawmidi stream for input
+ */
+static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return);
+
+	if (up) {
+		vmidi->trigger = 1;
+	} else {
+		vmidi->trigger = 0;
+	}
+}
+
+/*
+ * trigger rawmidi stream for output
+ */
+static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return);
+	int count, res;
+	unsigned char buf[32], *pbuf;
+
+	if (up) {
+		vmidi->trigger = 1;
+		if (!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
+			snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
+			return;		/* ignored */
+		}
+		if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
+			if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0)
+				return;
+			vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
+		}
+		while (1) {
+			count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
+			if (count <= 0)
+				break;
+			pbuf = buf;
+			while (count > 0) {
+				res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
+				if (res < 0) {
+					snd_midi_event_reset_encode(vmidi->parser);
+					continue;
+				}
+				snd_rawmidi_transmit_ack(substream, res);
+				pbuf += res;
+				count -= res;
+				if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
+					if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0)
+						return;
+					vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
+				}
+			}
+		}
+	} else {
+		vmidi->trigger = 0;
+	}
+}
+
+/*
+ * open rawmidi handle for input
+ */
+static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL);
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	snd_virmidi_t *vmidi;
+	unsigned long flags;
+
+	vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL);
+	if (vmidi == NULL)
+		return -ENOMEM;
+	vmidi->substream = substream;
+	if (snd_midi_event_new(0, &vmidi->parser) < 0) {
+		snd_magic_kfree(vmidi);
+		return -ENOMEM;
+	}
+	vmidi->seq_mode = rdev->seq_mode;
+	vmidi->client = rdev->client;
+	vmidi->port = rdev->port;	
+	runtime->private_data = vmidi;
+	write_lock_irqsave(&rdev->filelist_lock, flags);
+	list_add_tail(&vmidi->list, &rdev->filelist);
+	write_unlock_irqrestore(&rdev->filelist_lock, flags);
+	vmidi->rdev = rdev;
+	return 0;
+}
+
+/*
+ * open rawmidi handle for output
+ */
+static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL);
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	snd_virmidi_t *vmidi;
+
+	vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL);
+	if (vmidi == NULL)
+		return -ENOMEM;
+	vmidi->substream = substream;
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
+		snd_magic_kfree(vmidi);
+		return -ENOMEM;
+	}
+	vmidi->seq_mode = rdev->seq_mode;
+	vmidi->client = rdev->client;
+	vmidi->port = rdev->port;
+	snd_virmidi_init_event(vmidi, &vmidi->event);
+	vmidi->rdev = rdev;
+	runtime->private_data = vmidi;
+	return 0;
+}
+
+/*
+ * close rawmidi handle for input
+ */
+static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL);
+	snd_midi_event_free(vmidi->parser);
+	list_del(&vmidi->list);
+	substream->runtime->private_data = NULL;
+	snd_magic_kfree(vmidi);
+	return 0;
+}
+
+/*
+ * close rawmidi handle for output
+ */
+static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL);
+	snd_midi_event_free(vmidi->parser);
+	substream->runtime->private_data = NULL;
+	snd_magic_kfree(vmidi);
+	return 0;
+}
+
+/*
+ * subscribe callback - allow output to rawmidi device
+ */
+static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL);
+	if (!try_inc_mod_count(rdev->card->module))
+		return -EFAULT;
+	rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
+	return 0;
+}
+
+/*
+ * unsubscribe callback - disallow output to rawmidi device
+ */
+static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL);
+	rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
+	dec_mod_count(rdev->card->module);
+	return 0;
+}
+
+
+/*
+ * use callback - allow input to rawmidi device
+ */
+static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL);
+	if (!try_inc_mod_count(rdev->card->module))
+		return -EFAULT;
+	rdev->flags |= SNDRV_VIRMIDI_USE;
+	return 0;
+}
+
+/*
+ * unuse callback - disallow input to rawmidi device
+ */
+static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL);
+	rdev->flags &= ~SNDRV_VIRMIDI_USE;
+	dec_mod_count(rdev->card->module);
+	return 0;
+}
+
+
+/*
+ *  Register functions
+ */
+
+static snd_rawmidi_ops_t snd_virmidi_input_ops = {
+	open: snd_virmidi_input_open,
+	close: snd_virmidi_input_close,
+	trigger: snd_virmidi_input_trigger,
+};
+
+static snd_rawmidi_ops_t snd_virmidi_output_ops = {
+	open: snd_virmidi_output_open,
+	close: snd_virmidi_output_close,
+	trigger: snd_virmidi_output_trigger,
+};
+
+/*
+ * create a sequencer client and a port
+ */
+static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev)
+{
+	int client;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_port_callback_t pcallbacks;
+	snd_seq_client_info_t info;
+	snd_seq_port_info_t pinfo;
+	int err;
+
+	if (rdev->client >= 0)
+		return 0;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = rdev;
+	callbacks.allow_input = 1;
+	callbacks.allow_output = 1;
+	client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks);
+	if (client < 0)
+		return client;
+	rdev->client = client;
+
+	/* set client name */
+	memset(&info, 0, sizeof(info));
+	info.client = client;
+	info.type = KERNEL_CLIENT;
+	sprintf(info.name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
+
+	/* create a port */
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.addr.client = client;
+	sprintf(pinfo.name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
+	/* set all capabilities */
+	pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	pinfo.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	pinfo.midi_channels = 16;
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.private_data = rdev;
+	pcallbacks.subscribe = snd_virmidi_subscribe;
+	pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
+	pcallbacks.use = snd_virmidi_use;
+	pcallbacks.unuse = snd_virmidi_unuse;
+	pcallbacks.event_input = snd_virmidi_receive;
+	pinfo.kernel = &pcallbacks;
+	err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo);
+	if (err < 0) {
+		snd_seq_delete_kernel_client(client);
+		rdev->client = -1;
+		return err;
+	}
+
+	rdev->port = pinfo.addr.port;
+	return 0;	/* success */
+}
+
+
+/*
+ * release the sequencer client
+ */
+static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev)
+{
+	if (rdev->client >= 0) {
+		snd_seq_delete_kernel_client(rdev->client);
+		rdev->client = -1;
+	}
+}
+
+/*
+ * register the device
+ */
+static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi)
+{
+	snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
+	int err;
+
+	switch (rdev->seq_mode) {
+	case SNDRV_VIRMIDI_SEQ_DISPATCH:
+		err = snd_virmidi_dev_attach_seq(rdev);
+		if (err < 0)
+			return err;
+		break;
+	case SNDRV_VIRMIDI_SEQ_ATTACH:
+		if (rdev->client == 0)
+			return -EINVAL;
+		/* should check presence of port more strictly.. */
+		break;
+	default:
+		snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/*
+ * unregister the device
+ */
+static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi)
+{
+	snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
+
+	if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
+		snd_virmidi_dev_detach_seq(rdev);
+	return 0;
+}
+
+/*
+ *
+ */
+static snd_rawmidi_global_ops_t snd_virmidi_global_ops = {
+	dev_register: snd_virmidi_dev_register,
+	dev_unregister: snd_virmidi_dev_unregister,
+};
+
+/*
+ * free device
+ */
+static void snd_virmidi_free(snd_rawmidi_t *rmidi)
+{
+	snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return);
+	snd_magic_kfree(rdev);
+}
+
+/*
+ * create a new device
+ */
+int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi)
+{
+	snd_rawmidi_t *rmidi;
+	snd_virmidi_dev_t *rdev;
+	int err;
+	
+	*rrmidi = NULL;
+	if ((err = snd_rawmidi_new(card, "VirMidi", device,
+				   16,	/* may be configurable */
+				   16,	/* may be configurable */
+				   &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, rmidi->id);
+	rdev = snd_magic_kcalloc(snd_virmidi_dev_t, 0, GFP_KERNEL);
+	if (rdev == NULL) {
+		snd_device_free(card, rmidi);
+		return -ENOMEM;
+	}
+	rdev->card = card;
+	rdev->rmidi = rmidi;
+	rdev->device = device;
+	rdev->client = -1;
+	rwlock_init(&rdev->filelist_lock);
+	INIT_LIST_HEAD(&rdev->filelist);
+	rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+	rmidi->private_data = rdev;
+	rmidi->private_free = snd_virmidi_free;
+	rmidi->ops = &snd_virmidi_global_ops;
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
+	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
+			    SNDRV_RAWMIDI_INFO_OUTPUT |
+			    SNDRV_RAWMIDI_INFO_DUPLEX;
+	*rrmidi = rmidi;
+	return 0;
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static int __init alsa_virmidi_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_virmidi_exit(void)
+{
+}
+
+module_init(alsa_virmidi_init)
+module_exit(alsa_virmidi_exit)
+
+EXPORT_SYMBOL(snd_virmidi_new);
diff -Nru linux/sound/core/sound.c linux-2.4.19-pre5-mjc/sound/core/sound.c
--- linux/sound/core/sound.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/sound.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,507 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+
+#define SNDRV_OS_MINORS 256
+
+int snd_major = CONFIG_SND_MAJOR;
+static int snd_cards_limit = SNDRV_CARDS;
+int snd_device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+int snd_device_gid = 0;
+int snd_device_uid = 0;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+MODULE_PARM(snd_major, "i");
+MODULE_PARM_DESC(snd_major, "Major # for sound driver.");
+MODULE_PARM_SYNTAX(snd_major, "default:116,skill:devel");
+MODULE_PARM(snd_cards_limit, "i");
+MODULE_PARM_DESC(snd_cards_limit, "Count of soundcards installed in the system.");
+MODULE_PARM_SYNTAX(snd_cards_limit, "default:8,skill:advanced");
+MODULE_PARM(snd_device_mode, "i");
+MODULE_PARM_DESC(snd_device_mode, "Device file permission mask for sound dynamic device filesystem.");
+MODULE_PARM_SYNTAX(snd_device_mode, "default:0666,base:8");
+MODULE_PARM(snd_device_gid, "i");
+MODULE_PARM_DESC(snd_device_gid, "Device file GID for sound dynamic device filesystem.");
+MODULE_PARM_SYNTAX(snd_device_gid, "default:0");
+MODULE_PARM(snd_device_uid, "i");
+MODULE_PARM_DESC(snd_device_uid, "Device file UID for sound dynamic device filesystem.");
+MODULE_PARM_SYNTAX(snd_device_uid, "default:0");
+
+int snd_ecards_limit;
+
+static struct list_head snd_minors_hash[SNDRV_CARDS];
+
+static DECLARE_MUTEX(sound_mutex);
+
+#ifdef CONFIG_DEVFS_FS
+static devfs_handle_t devfs_handle = NULL;
+#endif
+
+#ifdef CONFIG_KMOD
+
+void snd_request_card(int card)
+{
+	char str[32];
+
+	if (snd_cards[card] != NULL)
+		return;
+	if (card < 0 || card >= snd_ecards_limit)
+		return;
+	sprintf(str, "snd-card-%i", card);
+	request_module(str);
+}
+
+static void snd_request_other(int minor)
+{
+	char *str;
+
+	switch (minor) {
+	case SNDRV_MINOR_SEQUENCER:	str = "snd-seq";	break;
+	case SNDRV_MINOR_TIMER:		str = "snd-timer";	break;
+	default:			return;
+	}
+	request_module(str);
+}
+
+#endif				/* request_module support */
+
+static snd_minor_t *snd_minor_search(int minor)
+{
+	struct list_head *list;
+	snd_minor_t *mptr;
+
+	list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) {
+		mptr = list_entry(list, snd_minor_t, list);
+		if (mptr->number == minor)
+			return mptr;
+	}
+	return NULL;
+}
+
+static int snd_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	int card = SNDRV_MINOR_CARD(minor);
+	int dev = SNDRV_MINOR_DEVICE(minor);
+	snd_minor_t *mptr = NULL;
+	struct file_operations *old_fops;
+	int err = 0;
+
+	if (dev != SNDRV_MINOR_SEQUENCER) {
+		if (snd_cards[card] == NULL) {
+#ifdef CONFIG_KMOD
+			snd_request_card(card);
+			if (snd_cards[card] == NULL)
+#endif
+				return -ENODEV;
+		}
+	} else {
+#ifdef CONFIG_KMOD
+		if ((mptr = snd_minor_search(minor)) == NULL)
+			snd_request_other(minor);
+#endif
+	}
+	if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL)
+		return -ENODEV;
+	old_fops = file->f_op;
+	file->f_op = fops_get(mptr->f_ops);
+	if (file->f_op->open)
+		err = file->f_op->open(inode, file);
+	if (err) {
+		fops_put(file->f_op);
+		file->f_op = fops_get(old_fops);
+	}
+	fops_put(old_fops);
+	return err;
+}
+
+struct file_operations snd_fops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	open:		snd_open
+};
+
+static int snd_kernel_minor(int type, snd_card_t * card, int dev)
+{
+	int minor;
+
+	switch (type) {
+	case SNDRV_DEVICE_TYPE_SEQUENCER:
+	case SNDRV_DEVICE_TYPE_TIMER:
+		minor = type;
+		break;
+	case SNDRV_DEVICE_TYPE_CONTROL:
+		snd_assert(card != NULL, return -EINVAL);
+		minor = SNDRV_MINOR(card->number, type);
+		break;
+	case SNDRV_DEVICE_TYPE_HWDEP:
+	case SNDRV_DEVICE_TYPE_RAWMIDI:
+	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
+	case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
+		snd_assert(card != NULL, return -EINVAL);
+		minor = SNDRV_MINOR(card->number, type + dev);
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
+	return minor;
+}
+
+int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name)
+{
+	int minor = snd_kernel_minor(type, card, dev);
+	snd_minor_t *preg;
+
+	if (minor < 0)
+		return minor;
+	preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL);
+	if (preg == NULL)
+		return -ENOMEM;
+	*preg = *reg;
+	preg->number = minor;
+	preg->device = dev;
+	preg->dev = NULL;
+	down(&sound_mutex);
+	if (snd_minor_search(minor)) {
+		up(&sound_mutex);
+		kfree(preg);
+		return -EBUSY;
+	}
+	if (name)
+		preg->dev = snd_info_create_device(name, minor, 0);
+	list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]);
+	up(&sound_mutex);
+	return 0;
+}
+
+int snd_unregister_device(int type, snd_card_t * card, int dev)
+{
+	int minor = snd_kernel_minor(type, card, dev);
+	snd_minor_t *mptr;
+
+	if (minor < 0)
+		return minor;
+	down(&sound_mutex);
+	if ((mptr = snd_minor_search(minor)) == NULL) {
+		up(&sound_mutex);
+		return -EINVAL;
+	}
+	if (mptr->dev)
+		snd_info_free_device(mptr->dev);
+	list_del(&mptr->list);
+	up(&sound_mutex);
+	kfree(mptr);
+	return 0;
+}
+
+/*
+ *  INFO PART
+ */
+
+static snd_info_entry_t *snd_minor_info_entry = NULL;
+
+static void snd_minor_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	int card, device;
+	struct list_head *list;
+	snd_minor_t *mptr;
+
+	down(&sound_mutex);
+	for (card = 0; card < SNDRV_CARDS; card++) {
+		list_for_each(list, &snd_minors_hash[card]) {
+			mptr = list_entry(list, snd_minor_t, list);
+			if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) {
+				if ((device = mptr->device) >= 0)
+					snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment);
+				else
+					snd_iprintf(buffer, "%3i: [%i]   : %s\n", mptr->number, card, mptr->comment);
+			} else {
+				snd_iprintf(buffer, "%3i:       : %s\n", mptr->number, mptr->comment);
+			}
+		}
+	}
+	up(&sound_mutex);
+}
+
+int __init snd_minor_info_init(void)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = PAGE_SIZE;
+		entry->c.text.read = snd_minor_info_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_minor_info_entry = entry;
+	return 0;
+}
+
+int __exit snd_minor_info_done(void)
+{
+	if (snd_minor_info_entry)
+		snd_info_unregister(snd_minor_info_entry);
+	return 0;
+}
+
+/*
+ *  INIT PART
+ */
+
+static int __init alsa_sound_init(void)
+{
+#ifdef CONFIG_DEVFS_FS
+	short controlnum;
+	char controlname[24];
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+	int err;
+#endif
+	int card;
+
+	snd_ecards_limit = snd_cards_limit;
+	for (card = 0; card < SNDRV_CARDS; card++)
+		INIT_LIST_HEAD(&snd_minors_hash[card]);
+#ifdef CONFIG_SND_OSSEMUL
+	if ((err = snd_oss_init_module()) < 0)
+		return err;
+#endif
+#ifdef CONFIG_DEVFS_FS
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+	devfs_handle = devfs_mk_dir(NULL, "snd", 3, NULL);
+#else
+	devfs_handle = devfs_mk_dir(NULL, "snd", NULL);
+#endif
+	if (devfs_register_chrdev(snd_major, "alsa", &snd_fops)) {
+#else
+	if (register_chrdev(snd_major, "alsa", &snd_fops)) {
+#endif
+		snd_printk(KERN_ERR "unable to register native major device number %d\n", snd_major);
+#ifdef CONFIG_SND_OSSEMUL
+		snd_oss_cleanup_module();
+#endif
+		return -EIO;
+	}
+#ifdef CONFIG_SND_DEBUG_MEMORY
+	snd_memory_init();
+#endif
+	if (snd_info_init() < 0) {
+#ifdef CONFIG_SND_DEBUG_MEMORY
+		snd_memory_done();
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+		snd_oss_cleanup_module();
+#endif
+		return -ENOMEM;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	snd_info_minor_register();
+#endif
+#ifdef CONFIG_DEVFS_FS
+	for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) {
+		sprintf(controlname, "snd/controlC%d", controlnum);
+		devfs_register(NULL, controlname, DEVFS_FL_DEFAULT,
+				snd_major, controlnum<<5, snd_device_mode | S_IFCHR,
+				&snd_fops, NULL);
+	}
+#endif
+#ifndef MODULE
+	printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM)
+	pm_init();
+#endif
+	return 0;
+}
+
+static void __exit alsa_sound_exit(void)
+{
+#ifdef CONFIG_DEVFS_FS
+	devfs_handle_t master;
+	char controlname[24];
+	short controlnum;
+
+	for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) {
+		sprintf(controlname, "snd/controlC%d", controlnum);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+		master = devfs_find_handle(NULL, controlname, strlen(controlname), 0, 0, DEVFS_SPECIAL_CHR, 0);
+#else
+		master = devfs_find_handle(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0);
+#endif
+		devfs_unregister(master);
+	}
+#endif
+	
+#ifdef CONFIG_SND_OSSEMUL
+	snd_info_minor_unregister();
+	snd_oss_cleanup_module();
+#endif
+	snd_info_done();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM)
+	pm_done();
+#endif
+#ifdef CONFIG_SND_DEBUG_MEMORY
+	snd_memory_done();
+#endif
+#ifdef CONFIG_DEVFS_FS
+	if (devfs_unregister_chrdev(snd_major, "alsa") != 0)
+#else
+	if (unregister_chrdev(snd_major, "alsa") != 0)
+#endif
+		snd_printk(KERN_ERR "unable to unregister major device number %d\n", snd_major);
+#ifdef CONFIG_DEVFS_FS
+	devfs_unregister(devfs_handle);
+#endif
+}
+
+module_init(alsa_sound_init)
+module_exit(alsa_sound_exit)
+
+  /* sound.c */
+EXPORT_SYMBOL(snd_ecards_limit);
+#if defined(CONFIG_KMOD)
+EXPORT_SYMBOL(snd_request_card);
+#endif
+EXPORT_SYMBOL(snd_register_device);
+EXPORT_SYMBOL(snd_unregister_device);
+#if defined(CONFIG_SND_OSSEMUL)
+EXPORT_SYMBOL(snd_register_oss_device);
+EXPORT_SYMBOL(snd_unregister_oss_device);
+#endif
+  /* memory.c */
+#ifdef CONFIG_SND_DEBUG_MEMORY
+EXPORT_SYMBOL(snd_hidden_kmalloc);
+EXPORT_SYMBOL(snd_hidden_kfree);
+EXPORT_SYMBOL(snd_hidden_vmalloc);
+EXPORT_SYMBOL(snd_hidden_vfree);
+EXPORT_SYMBOL(_snd_magic_kmalloc);
+EXPORT_SYMBOL(_snd_magic_kcalloc);
+EXPORT_SYMBOL(snd_magic_kfree);
+#endif
+EXPORT_SYMBOL(snd_kcalloc);
+EXPORT_SYMBOL(snd_kmalloc_strdup);
+EXPORT_SYMBOL(snd_malloc_pages);
+EXPORT_SYMBOL(snd_malloc_pages_fallback);
+EXPORT_SYMBOL(snd_free_pages);
+#ifdef CONFIG_ISA
+EXPORT_SYMBOL(snd_malloc_isa_pages);
+EXPORT_SYMBOL(snd_malloc_isa_pages_fallback);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(snd_malloc_pci_pages);
+EXPORT_SYMBOL(snd_malloc_pci_pages_fallback);
+EXPORT_SYMBOL(snd_free_pci_pages);
+#endif
+EXPORT_SYMBOL(copy_to_user_fromio);
+EXPORT_SYMBOL(copy_from_user_toio);
+  /* init.c */
+EXPORT_SYMBOL(snd_cards_count);
+EXPORT_SYMBOL(snd_cards);
+#ifdef CONFIG_SND_OSSEMUL
+EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
+#endif
+EXPORT_SYMBOL(snd_card_new);
+EXPORT_SYMBOL(snd_card_free);
+EXPORT_SYMBOL(snd_card_register);
+EXPORT_SYMBOL(snd_component_add);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_power_wait);
+#endif
+  /* device.c */
+EXPORT_SYMBOL(snd_device_new);
+EXPORT_SYMBOL(snd_device_register);
+EXPORT_SYMBOL(snd_device_free);
+EXPORT_SYMBOL(snd_device_free_all);
+  /* isadma.c */
+#ifdef CONFIG_ISA
+EXPORT_SYMBOL(snd_dma_program);
+EXPORT_SYMBOL(snd_dma_disable);
+EXPORT_SYMBOL(snd_dma_residue);
+#endif
+  /* info.c */
+EXPORT_SYMBOL(snd_seq_root);
+EXPORT_SYMBOL(snd_create_proc_entry);
+EXPORT_SYMBOL(snd_remove_proc_entry);
+EXPORT_SYMBOL(snd_iprintf);
+EXPORT_SYMBOL(snd_info_get_line);
+EXPORT_SYMBOL(snd_info_get_str);
+EXPORT_SYMBOL(snd_info_create_module_entry);
+EXPORT_SYMBOL(snd_info_create_card_entry);
+EXPORT_SYMBOL(snd_info_free_entry);
+EXPORT_SYMBOL(snd_info_create_device);
+EXPORT_SYMBOL(snd_info_free_device);
+EXPORT_SYMBOL(snd_info_register);
+EXPORT_SYMBOL(snd_info_unregister);
+  /* info_oss.c */
+#ifdef CONFIG_SND_OSSEMUL
+EXPORT_SYMBOL(snd_oss_info_register);
+#endif
+  /* control.c */
+EXPORT_SYMBOL(snd_ctl_new);
+EXPORT_SYMBOL(snd_ctl_new1);
+EXPORT_SYMBOL(snd_ctl_free_one);
+EXPORT_SYMBOL(snd_ctl_add);
+EXPORT_SYMBOL(snd_ctl_remove);
+EXPORT_SYMBOL(snd_ctl_remove_id);
+EXPORT_SYMBOL(snd_ctl_rename_id);
+EXPORT_SYMBOL(snd_ctl_find_numid);
+EXPORT_SYMBOL(snd_ctl_find_id);
+EXPORT_SYMBOL(snd_ctl_notify);
+EXPORT_SYMBOL(snd_ctl_register_ioctl);
+EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
+  /* misc.c */
+EXPORT_SYMBOL(snd_task_name);
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+EXPORT_SYMBOL(snd_verbose_printk);
+#endif
+  /* wrappers */
+#ifdef CONFIG_SND_DEBUG_MEMORY
+EXPORT_SYMBOL(snd_wrapper_kmalloc);
+EXPORT_SYMBOL(snd_wrapper_kfree);
+#endif
+#ifdef HACK_PCI_ALLOC_CONSISTENT
+EXPORT_SYMBOL(snd_pci_hack_alloc_consistent);
+#endif 
diff -Nru linux/sound/core/sound_oss.c linux-2.4.19-pre5-mjc/sound/core/sound_oss.c
--- linux/sound/core/sound_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/sound_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,249 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+
+#ifdef CONFIG_SND_OSSEMUL
+
+#if !defined(CONFIG_SOUND_ALSA) && !defined(CONFIG_SOUND_ALSA_MODULE)
+#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND_ALSA) in the kernel."
+#endif
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <linux/sound.h>
+
+#define SNDRV_OS_MINORS		256
+
+static struct list_head snd_oss_minors_hash[SNDRV_CARDS];
+
+static DECLARE_MUTEX(sound_oss_mutex);
+
+static snd_minor_t *snd_oss_minor_search(int minor)
+{
+	struct list_head *list;
+	snd_minor_t *mptr;
+
+	list_for_each(list, &snd_oss_minors_hash[SNDRV_MINOR_OSS_CARD(minor)]) {
+		mptr = list_entry(list, snd_minor_t, list);
+		if (mptr->number == minor)
+			return mptr;
+	}
+	return NULL;
+}
+
+static int snd_oss_kernel_minor(int type, snd_card_t * card, int dev)
+{
+	int minor;
+
+	switch (type) {
+	case SNDRV_OSS_DEVICE_TYPE_MIXER:
+		snd_assert(card != NULL && dev <= 1, return -EINVAL);
+		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
+		break;
+	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
+		minor = SNDRV_MINOR_OSS_SEQUENCER;
+		break;
+	case SNDRV_OSS_DEVICE_TYPE_MUSIC:
+		minor = SNDRV_MINOR_OSS_MUSIC;
+		break;
+	case SNDRV_OSS_DEVICE_TYPE_PCM:
+		snd_assert(card != NULL && dev <= 1, return -EINVAL);
+		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
+		break;
+	case SNDRV_OSS_DEVICE_TYPE_MIDI:
+		snd_assert(card != NULL && dev <= 1, return -EINVAL);
+		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
+		break;
+	case SNDRV_OSS_DEVICE_TYPE_DMFM:
+		minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM);
+		break;
+	case SNDRV_OSS_DEVICE_TYPE_SNDSTAT:
+		minor = SNDRV_MINOR_OSS_SNDSTAT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
+	return minor;
+}
+
+int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name)
+{
+	int minor = snd_oss_kernel_minor(type, card, dev);
+	int minor_unit;
+	snd_minor_t *preg;
+	int cidx = SNDRV_MINOR_OSS_CARD(minor);
+	int track2 = -1;
+	int register1 = -1, register2 = -1;
+
+	if (minor < 0)
+		return minor;
+	preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL);
+	if (preg == NULL)
+		return -ENOMEM;
+	*preg = *reg;
+	preg->number = minor;
+	preg->device = dev;
+	preg->dev = NULL;
+	down(&sound_oss_mutex);
+	list_add_tail(&preg->list, &snd_oss_minors_hash[cidx]);
+	minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
+	switch (minor_unit) {
+	case SNDRV_MINOR_OSS_PCM:
+		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
+		break;
+	case SNDRV_MINOR_OSS_MIDI:
+		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
+		break;
+	case SNDRV_MINOR_OSS_MIDI1:
+		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
+		break;
+	}
+	register1 = register_sound_special(reg->f_ops, minor);
+	if (register1 != minor)
+		goto __end;
+	if (track2 >= 0) {
+		register2 = register_sound_special(reg->f_ops, track2);
+		if (register2 != track2)
+			goto __end;
+	}
+	up(&sound_oss_mutex);
+	return 0;
+
+      __end:
+      	if (register2 >= 0)
+      		unregister_sound_special(register2);
+      	if (register1 >= 0)
+      		unregister_sound_special(register1);
+      	list_del(&preg->list);
+	up(&sound_oss_mutex);
+	kfree(preg);
+      	return -EBUSY;
+}
+
+int snd_unregister_oss_device(int type, snd_card_t * card, int dev)
+{
+	int minor = snd_oss_kernel_minor(type, card, dev);
+	int cidx = SNDRV_MINOR_OSS_CARD(minor);
+	int track2 = -1;
+	snd_minor_t *mptr;
+
+	if (minor < 0)
+		return minor;
+	down(&sound_oss_mutex);
+	mptr = snd_oss_minor_search(minor);
+	if (mptr == NULL) {
+		up(&sound_oss_mutex);
+		return -ENOENT;
+	}
+	unregister_sound_special(minor);
+	switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
+	case SNDRV_MINOR_OSS_PCM:
+		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
+		break;
+	case SNDRV_MINOR_OSS_MIDI:
+		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
+		break;
+	case SNDRV_MINOR_OSS_MIDI1:
+		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
+		break;
+	}
+	if (track2 >= 0)
+		unregister_sound_special(track2);
+	list_del(&mptr->list);
+	up(&sound_oss_mutex);
+	kfree(mptr);
+	return 0;
+}
+
+/*
+ *  INFO PART
+ */
+
+static snd_info_entry_t *snd_minor_info_oss_entry = NULL;
+
+static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	int card, dev;
+	struct list_head *list;
+	snd_minor_t *mptr;
+
+	down(&sound_oss_mutex);
+	for (card = 0; card < SNDRV_CARDS; card++) {
+		list_for_each(list, &snd_oss_minors_hash[card]) {
+			mptr = list_entry(list, snd_minor_t, list);
+			dev = SNDRV_MINOR_OSS_DEVICE(mptr->number);
+		        if (dev != SNDRV_MINOR_OSS_SNDSTAT &&
+			    dev != SNDRV_MINOR_OSS_SEQUENCER &&
+			    dev != SNDRV_MINOR_OSS_MUSIC)
+				snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, dev, mptr->comment);
+			else
+				snd_iprintf(buffer, "%3i:       : %s\n", mptr->number, mptr->comment);
+		}
+	}
+	up(&sound_oss_mutex);
+}
+
+int __init snd_minor_info_oss_init(void)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = PAGE_SIZE;
+		entry->c.text.read = snd_minor_info_oss_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_minor_info_oss_entry = entry;
+	return 0;
+}
+
+int __exit snd_minor_info_oss_done(void)
+{
+	if (snd_minor_info_oss_entry)
+		snd_info_unregister(snd_minor_info_oss_entry);
+	return 0;
+}
+
+int __init snd_oss_init_module(void)
+{
+	int card;
+	
+	for (card = 0; card < SNDRV_CARDS; card++)
+		INIT_LIST_HEAD(&snd_oss_minors_hash[card]);
+	return 0;
+}
+
+void snd_oss_cleanup_module(void)
+{
+}
+
+#endif /* CONFIG_SND_OSSEMUL */
diff -Nru linux/sound/core/timer.c linux-2.4.19-pre5-mjc/sound/core/timer.c
--- linux/sound/core/timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/timer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1362 @@
+/*
+ *  Timers abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/minors.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+int snd_timer_limit = 1;
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA timer interface");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_PARM(snd_timer_limit, "i");
+MODULE_PARM_DESC(snd_timer_limit, "Maximum global timers in system. (1 by default)");
+
+typedef struct {
+	snd_timer_instance_t *timeri;
+	unsigned long ticks;
+	unsigned long overrun;
+	int qhead;
+	int qtail;
+	int qused;
+	int queue_size;
+	snd_timer_read_t *queue;
+	spinlock_t qlock;
+	wait_queue_head_t qchange_sleep;
+} snd_timer_user_t;
+
+/* list of timers */
+static LIST_HEAD(snd_timer_list);
+
+/* list of slave instances */
+static LIST_HEAD(snd_timer_slave_list);
+
+/* lock for slave active lists */
+static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED;
+
+static DECLARE_MUTEX(register_mutex);
+
+static int snd_timer_free(snd_timer_t *timer);
+static int snd_timer_dev_free(snd_device_t *device);
+static int snd_timer_dev_register(snd_device_t *device);
+static int snd_timer_dev_unregister(snd_device_t *device);
+
+static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left);
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+/*
+ * create a timer instance with the given owner string.
+ * when timer is not NULL, increments the module counter
+ */
+static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer)
+{
+	snd_timer_instance_t *timeri;
+	timeri = snd_kcalloc(sizeof(snd_timer_instance_t), GFP_KERNEL);
+	if (timeri == NULL)
+		return NULL;
+	timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL);
+	if (! timeri->owner) {
+		kfree(timeri);
+		return NULL;
+	}
+	INIT_LIST_HEAD(&timeri->open_list);
+	INIT_LIST_HEAD(&timeri->active_list);
+	INIT_LIST_HEAD(&timeri->slave_list_head);
+	INIT_LIST_HEAD(&timeri->slave_active_head);
+	timeri->in_use = (atomic_t)ATOMIC_INIT(0);
+
+	timeri->timer = timer;
+	if (timer && timer->card && !try_inc_mod_count(timer->card->module)) {
+		kfree(timeri->owner);
+		kfree(timeri);
+		return NULL;
+	}
+
+	return timeri;
+}
+
+/*
+ * find a timer instance from the given timer id
+ */
+static snd_timer_t *snd_timer_find(snd_timer_id_t *tid)
+{
+	snd_timer_t *timer = NULL;
+	struct list_head *p;
+
+	list_for_each(p, &snd_timer_list) {
+		timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+
+		if (timer->tmr_class != tid->dev_class)
+			continue;
+		if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
+		     timer->tmr_class == SNDRV_TIMER_CLASS_PCM) &&
+		    (timer->card == NULL ||
+		     timer->card->number != tid->card))
+			continue;
+		if (timer->tmr_device != tid->device)
+			continue;
+		if (timer->tmr_subdevice != tid->subdevice)
+			continue;
+		return timer;
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_KMOD
+
+static void snd_timer_request(snd_timer_id_t *tid)
+{
+	char str[32];
+	
+	switch (tid->dev_class) {
+	case SNDRV_TIMER_CLASS_GLOBAL:
+		sprintf(str, "snd-timer-%i", tid->device);
+		break;
+	case SNDRV_TIMER_CLASS_CARD:
+	case SNDRV_TIMER_CLASS_PCM:
+		sprintf(str, "snd-card-%i", tid->card);
+		break;
+	default:
+		return;
+	}
+	request_module(str);
+}
+
+#endif
+
+/*
+ * look for a master instance matching with the slave id of the given slave.
+ * when found, relink the open_link of the slave.
+ *
+ * call this with register_mutex down.
+ */
+static void snd_timer_check_slave(snd_timer_instance_t *slave)
+{
+	snd_timer_t *timer;
+	snd_timer_instance_t *master;
+	struct list_head *p, *q;
+
+	/* FIXME: it's really dumb to look up all entries.. */
+	list_for_each(p, &snd_timer_list) {
+		timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+		list_for_each(q, &timer->open_list_head) {
+			master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list);
+			if (slave->slave_class == master->slave_class &&
+			    slave->slave_id == master->slave_id) {
+				list_del(&slave->open_list);
+				list_add_tail(&slave->open_list, &master->slave_list_head);
+				slave->master = master;
+				return;
+			}
+		}
+	}
+}
+
+/*
+ * look for slave instances matching with the slave id of the given master.
+ * when found, relink the open_link of slaves.
+ *
+ * call this with register_mutex down.
+ */
+static void snd_timer_check_master(snd_timer_instance_t *master)
+{
+	snd_timer_instance_t *slave;
+	struct list_head *p, *n;
+	unsigned long flags;
+
+	/* check all pending slaves */
+	list_for_each_safe(p, n, &snd_timer_slave_list) {
+		slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list);
+		if (slave->slave_class == master->slave_class &&
+		    slave->slave_id == master->slave_id) {
+			list_del(p);
+			list_add_tail(p, &master->slave_list_head);
+			spin_lock_irqsave(&slave_active_lock, flags);
+			/* protected here so that timer_start() doesn't start
+			 * this slave yet.
+			 */
+			slave->master = master;
+			spin_unlock_irqrestore(&slave_active_lock, flags);
+		}
+	}
+}
+
+/*
+ * open a timer instance
+ * when opening a master, the slave id must be here given.
+ */
+snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid,
+				     unsigned int slave_id)
+{
+	snd_timer_t *timer;
+	snd_timer_instance_t *timeri = NULL;
+	
+	if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
+		/* open a slave instance */
+		if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
+		    tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
+			snd_printd("invalid slave class %i\n", tid->dev_sclass);
+			return NULL;
+		}
+		down(&register_mutex);
+		timeri = snd_timer_instance_new(owner, NULL);
+		timeri->slave_class = tid->dev_sclass;
+		timeri->slave_id = tid->device;
+		timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
+		list_add_tail(&timeri->open_list, &snd_timer_slave_list);
+		snd_timer_check_slave(timeri);
+		up(&register_mutex);
+		return timeri;
+	}
+
+	/* open a master instance */
+	down(&register_mutex);
+	timer = snd_timer_find(tid);
+#ifdef CONFIG_KMOD
+	if (timer == NULL) {
+		up(&register_mutex);
+		snd_timer_request(tid);
+		down(&register_mutex);
+		timer = snd_timer_find(tid);
+	}
+#endif
+	if (timer) {
+		timeri = snd_timer_instance_new(owner, timer);
+		if (timeri) {
+			timeri->slave_class = tid->dev_sclass;
+			timeri->slave_id = slave_id;
+			if (list_empty(&timer->open_list_head) && timer->hw.open)
+				timer->hw.open(timer);
+			list_add_tail(&timeri->open_list, &timer->open_list_head);
+			snd_timer_check_master(timeri);
+		}
+	}
+	up(&register_mutex);
+	return timeri;
+}
+
+
+/*
+ * close a timer instance
+ */
+int snd_timer_close(snd_timer_instance_t * timeri)
+{
+	snd_timer_t *timer = NULL;
+	unsigned long flags;
+	struct list_head *p, *n;
+	snd_timer_instance_t *slave;
+
+	snd_assert(timeri != NULL, return -ENXIO);
+
+	snd_timer_stop(timeri); /* force to stop the timer */
+
+	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
+		down(&register_mutex);
+		list_del(&timeri->open_list);
+		up(&register_mutex);
+	} else {
+		timer = timeri->timer;
+		down(&register_mutex);
+		list_del(&timeri->open_list);
+		if (timer && list_empty(&timer->open_list_head) && timer->hw.close)
+			timer->hw.close(timer);
+		/* remove slave links */
+		list_for_each_safe(p, n, &timeri->slave_list_head) {
+			slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list);
+			list_del(p);
+			list_add_tail(p, &snd_timer_slave_list);
+			spin_lock_irqsave(&slave_active_lock, flags);
+			slave->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+			list_del_init(&slave->active_list);
+			slave->master = NULL;
+			spin_unlock_irqrestore(&slave_active_lock, flags);
+		}
+		up(&register_mutex);
+	}
+	if (timeri->private_free)
+		timeri->private_free(timeri);
+	if (timeri->owner)
+		kfree(timeri->owner);
+	kfree(timeri);
+	if (timer && timer->card)
+		dec_mod_count(timer->card->module);
+	return 0;
+}
+
+unsigned long snd_timer_resolution(snd_timer_instance_t * timeri)
+{
+	snd_timer_t * timer;
+
+	if (timeri == NULL)
+		return 0;
+	if ((timer = timeri->timer) != NULL) {
+		if (timer->hw.c_resolution)
+			return timer->hw.c_resolution(timer);
+		return timer->hw.resolution;
+	}
+	return 0;
+}
+
+static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks)
+{
+	list_del(&timeri->active_list);
+	list_add_tail(&timeri->active_list, &timer->active_list_head);
+	if (timer->running) {
+		timer->flags |= SNDRV_TIMER_FLG_RESCHED;
+		timeri->flags |= SNDRV_TIMER_IFLG_START;
+		return 1;	/* delayed start */
+	} else {
+		timer->sticks = sticks;
+		timer->hw.start(timer);
+		timer->running++;
+		timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
+		return 0;
+	}
+}
+
+static int snd_timer_start_slave(snd_timer_instance_t *timeri)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&slave_active_lock, flags);
+	timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
+	if (timeri->master)
+		list_add_tail(&timeri->active_list, &timeri->master->slave_active_head);
+	spin_unlock_irqrestore(&slave_active_lock, flags);
+	return 1; /* delayed start */
+}
+
+int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks)
+{
+	snd_timer_t *timer;
+	int result = -EINVAL;
+	unsigned long flags;
+
+	if (timeri == NULL || ticks < 1)
+		return -EINVAL;
+	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+		return snd_timer_start_slave(timeri);
+	timer = timeri->timer;
+	if (timer == NULL)
+		return -EINVAL;
+	spin_lock_irqsave(&timer->lock, flags);
+	timeri->ticks = timeri->cticks = ticks;
+	result = snd_timer_start1(timer, timeri, ticks);
+	spin_unlock_irqrestore(&timer->lock, flags);
+	return result;
+}
+
+/*
+ * stop the timer instance.
+ *
+ * FIXME: do not call this from the timer callback!
+ */
+int snd_timer_stop(snd_timer_instance_t * timeri)
+{
+	snd_timer_t *timer;
+	unsigned long flags;
+
+	snd_assert(timeri != NULL, return -ENXIO);
+
+	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
+		spin_lock_irqsave(&slave_active_lock, flags);
+		if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+			timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+			list_del_init(&timeri->active_list);
+		}
+		spin_unlock_irqrestore(&slave_active_lock, flags);
+		return 0;
+	}
+
+	timer = timeri->timer;
+	if (! timer)
+		return -EINVAL;
+	while (atomic_read(&timeri->in_use))
+		udelay(10);
+	spin_lock_irqsave(&timer->lock, flags);
+	if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
+		timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+		list_del_init(&timeri->active_list);
+		if (!(--timer->running)) {
+			timer->hw.stop(timer);
+			if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
+				timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
+				snd_timer_reschedule(timer, 0);
+				if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
+					timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
+					timer->hw.start(timer);
+				}
+			}
+		}
+	}
+	spin_unlock_irqrestore(&timer->lock, flags);
+	return 0;
+}
+
+/*
+ * start again..  the tick is kept.
+ */
+int snd_timer_continue(snd_timer_instance_t * timeri)
+{
+	snd_timer_t *timer;
+	int result = -EINVAL;
+	unsigned long flags;
+
+	if (timeri == NULL)
+		return result;
+	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+		return snd_timer_start_slave(timeri);
+	timer = timeri->timer;
+	if (! timer)
+		return -EINVAL;
+	spin_lock_irqsave(&timer->lock, flags);
+	if (!timeri->cticks)
+		timeri->cticks = 1;
+	result = snd_timer_start1(timer, timeri, timer->sticks);
+	spin_unlock_irqrestore(&timer->lock, flags);
+	return result;
+}
+
+/*
+ * reschedule the timer
+ *
+ * start pending instances and check the scheduling ticks.
+ * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
+ */
+static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left)
+{
+	snd_timer_instance_t *ti;
+	unsigned long ticks = ~0UL;
+	struct list_head *p;
+
+	list_for_each(p, &timer->active_list_head) {
+		ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list);
+		if (ti->flags & SNDRV_TIMER_IFLG_START) {
+			ti->flags &= ~SNDRV_TIMER_IFLG_START;
+			ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
+			timer->running++;
+		}
+		if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
+			if (ticks > ti->cticks)
+				ticks = ti->cticks;
+		}
+	}
+	if (ticks == ~0UL) {
+		timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
+		return;
+	}
+	if (ticks > timer->hw.ticks)
+		ticks = timer->hw.ticks;
+	if (ticks_left != ticks)
+		timer->flags |= SNDRV_TIMER_FLG_CHANGE;
+	timer->sticks = ticks;
+}
+
+
+/*
+ * timer interrupt
+ *
+ * ticks_left is usually equal to timer->sticks.
+ *
+ */
+void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left)
+{
+	snd_timer_instance_t *ti, *ts;
+	unsigned long resolution;
+	LIST_HEAD(done_list_head);
+	struct list_head *p, *q, *n;
+
+	if (timer == NULL)
+		return;
+	spin_lock(&timer->lock);
+	/* loop for all active instances
+	 * here we cannot use list_for_each because the active_list of a processed
+	 * instance is relinked to done_list_head before callback is called.
+	 */
+	list_for_each_safe(p, n, &timer->active_list_head) {
+		ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list);
+		if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
+			if (ti->cticks < ticks_left)
+				ti->cticks = 0;
+			else
+				ti->cticks -= ticks_left;
+			if (!ti->cticks) { /* expired */
+				if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
+					ti->cticks = ti->ticks;
+				} else {
+					ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+					timer->running--;
+				}
+				/* relink to done_list */
+				list_del(p);
+				list_add_tail(p, &done_list_head);
+				atomic_inc(&ti->in_use);
+			}
+		}
+	}
+	if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
+		snd_timer_reschedule(timer, ticks_left);
+	if (timer->running) {
+		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
+			timer->hw.stop(timer);
+			timer->flags |= SNDRV_TIMER_FLG_CHANGE;
+		}
+		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
+		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
+			/* restart timer */
+			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
+			timer->hw.start(timer);
+		}
+	} else {
+		timer->hw.stop(timer);
+	}
+
+	/* remember the current resolution */
+	if (timer->hw.c_resolution)
+		resolution = timer->hw.c_resolution(timer);
+	else
+		resolution = timer->hw.resolution;
+
+	/* now process all callbacks */
+	list_for_each_safe(p, n, &done_list_head) {
+		ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list);
+		/* append to active_list */
+		list_del(p);
+		list_add_tail(p, &timer->active_list_head);
+		spin_unlock(&timer->lock);
+		if (ti->callback)
+			ti->callback(ti, resolution, ti->ticks, ti->callback_data);
+		spin_lock(&slave_active_lock);
+		/* call callbacks of slaves */
+		list_for_each(q, &ti->slave_active_head) {
+			ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list);
+			if (ts->callback)
+				ts->callback(ts, resolution, ti->ticks, ts->callback_data);
+		}
+		spin_unlock(&slave_active_lock);
+		atomic_dec(&ti->in_use);
+		spin_lock(&timer->lock);
+	}
+	spin_unlock(&timer->lock);
+}
+
+
+/*
+
+ */
+
+int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer)
+{
+	snd_timer_t *timer;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_timer_dev_free,
+		dev_register:	snd_timer_dev_register,
+		dev_unregister:	snd_timer_dev_unregister
+	};
+
+	snd_assert(tid != NULL, return -EINVAL);
+	snd_assert(rtimer != NULL, return -EINVAL);
+	*rtimer = NULL;
+	timer = snd_magic_kcalloc(snd_timer_t, 0, GFP_KERNEL);
+	if (timer == NULL)
+		return -ENOMEM;
+	timer->tmr_class = tid->dev_class;
+	timer->card = card;
+	timer->tmr_device = tid->device;
+	timer->tmr_subdevice = tid->subdevice;
+	if (id)
+		strncpy(timer->id, id, sizeof(timer->id) - 1);
+	INIT_LIST_HEAD(&timer->device_list);
+	INIT_LIST_HEAD(&timer->open_list_head);
+	INIT_LIST_HEAD(&timer->active_list_head);
+	spin_lock_init(&timer->lock);
+	if (card != NULL) {
+		if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) {
+			snd_timer_free(timer);
+			return err;
+		}
+	}
+	*rtimer = timer;
+	return 0;
+}
+
+static int snd_timer_free(snd_timer_t *timer)
+{
+	snd_assert(timer != NULL, return -ENXIO);
+	if (timer->private_free)
+		timer->private_free(timer);
+	snd_magic_kfree(timer);
+	return 0;
+}
+
+int snd_timer_dev_free(snd_device_t *device)
+{
+	snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO);
+	return snd_timer_free(timer);
+}
+
+int snd_timer_dev_register(snd_device_t *dev)
+{
+	snd_timer_t *timer = snd_magic_cast(snd_timer_t, dev->device_data, return -ENXIO);
+	snd_timer_t *timer1;
+	struct list_head *p;
+
+	snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO);
+	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
+	    !timer->hw.resolution && timer->hw.c_resolution == NULL)
+	    	return -EINVAL;
+
+	down(&register_mutex);
+	list_for_each(p, &snd_timer_list) {
+		timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+		if (timer1->tmr_class > timer->tmr_class)
+			break;
+		if (timer1->tmr_class < timer->tmr_class)
+			continue;
+		if (timer1->card && timer->card) {
+			if (timer1->card->number > timer->card->number)
+				break;
+			if (timer1->card->number < timer->card->number)
+				continue;
+		}
+		if (timer1->tmr_device > timer->tmr_device)
+			break;
+		if (timer1->tmr_device < timer->tmr_device)
+			continue;
+		if (timer1->tmr_subdevice > timer->tmr_subdevice)
+			break;
+		if (timer1->tmr_subdevice < timer->tmr_subdevice)
+			continue;
+		/* conflicts.. */
+		up(&register_mutex);
+		return -EBUSY;
+	}
+	list_add_tail(&timer->device_list, p);
+	up(&register_mutex);
+	return 0;
+}
+
+int snd_timer_unregister(snd_timer_t *timer)
+{
+	struct list_head *p, *n;
+	snd_timer_instance_t *ti;
+
+	snd_assert(timer != NULL, return -ENXIO);
+	down(&register_mutex);
+	if (! list_empty(&timer->open_list_head)) {
+		snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer);
+		list_for_each_safe(p, n, &timer->open_list_head) {
+			list_del_init(p);
+			ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list);
+			ti->timer = NULL;
+		}
+	}
+	list_del(&timer->device_list);
+	up(&register_mutex);
+	return snd_timer_free(timer);
+}
+
+static int snd_timer_dev_unregister(snd_device_t *device)
+{
+	snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO);
+	return snd_timer_unregister(timer);
+}
+
+/*
+ * exported functions for global timers
+ */
+int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer)
+{
+	snd_timer_id_t tid;
+	
+	tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = -1;
+	tid.device = device;
+	tid.subdevice = 0;
+	return snd_timer_new(NULL, id, &tid, rtimer);
+}
+
+int snd_timer_global_free(snd_timer_t *timer)
+{
+	return snd_timer_free(timer);
+}
+
+int snd_timer_global_register(snd_timer_t *timer)
+{
+	snd_device_t dev;
+
+	memset(&dev, 0, sizeof(dev));
+	dev.device_data = timer;
+	return snd_timer_dev_register(&dev);
+}
+
+int snd_timer_global_unregister(snd_timer_t *timer)
+{
+	return snd_timer_unregister(timer);
+}
+
+/* 
+ *  System timer
+ */
+
+unsigned int snd_timer_system_resolution(void)
+{
+	return 1000000000L / HZ;
+}
+
+static void snd_timer_s_function(unsigned long data)
+{
+	snd_timer_t *timer = (snd_timer_t *)data;
+	snd_timer_interrupt(timer, timer->sticks);
+}
+
+static int snd_timer_s_start(snd_timer_t * timer)
+{
+	struct timer_list *tlist;
+
+	tlist = (struct timer_list *) timer->private_data;
+	tlist->expires = jiffies + timer->sticks;
+	add_timer(tlist);
+	return 0;
+}
+
+static int snd_timer_s_stop(snd_timer_t * timer)
+{
+	struct timer_list *tlist;
+
+	tlist = (struct timer_list *) timer->private_data;
+	del_timer(tlist);
+	timer->sticks = tlist->expires - jiffies;
+	return 0;
+}
+
+static struct _snd_timer_hardware snd_timer_system =
+{
+	flags:		SNDRV_TIMER_HW_FIRST,
+	resolution:	1000000000L / HZ,
+	ticks:		10000000L,
+	start:		snd_timer_s_start,
+	stop:		snd_timer_s_stop
+};
+
+static void snd_timer_free_system(snd_timer_t *timer)
+{
+	if (timer->private_data)
+		kfree(timer->private_data);
+}
+
+static int snd_timer_register_system(void)
+{
+	snd_timer_t *timer;
+	struct timer_list *tlist;
+	int err;
+
+	if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0)
+		return err;
+	strcpy(timer->name, "system timer");
+	timer->hw = snd_timer_system;
+	tlist = (struct timer_list *) snd_kcalloc(sizeof(struct timer_list), GFP_KERNEL);
+	if (tlist == NULL) {
+		snd_timer_free(timer);
+		return -ENOMEM;
+	}
+	tlist->function = snd_timer_s_function;
+	tlist->data = (unsigned long) timer;
+	timer->private_data = tlist;
+	timer->private_free = snd_timer_free_system;
+	return snd_timer_global_register(timer);
+}
+
+/*
+ *  Info interface
+ */
+
+static void snd_timer_proc_read(snd_info_entry_t *entry,
+				snd_info_buffer_t * buffer)
+{
+	unsigned long flags;
+	snd_timer_t *timer;
+	snd_timer_instance_t *ti;
+	struct list_head *p, *q;
+
+	down(&register_mutex);
+	list_for_each(p, &snd_timer_list) {
+		timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+		switch (timer->tmr_class) {
+		case SNDRV_TIMER_CLASS_GLOBAL:
+			snd_iprintf(buffer, "G%i: ", timer->tmr_device);
+			break;
+		case SNDRV_TIMER_CLASS_CARD:
+			snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device);
+			break;
+		case SNDRV_TIMER_CLASS_PCM:
+			snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice);
+			break;
+		default:
+			snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice);
+		}
+		snd_iprintf(buffer, "%s :", timer->name);
+		if (timer->hw.resolution)
+			snd_iprintf(buffer, " %lu.%luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks);
+		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
+			snd_iprintf(buffer, " SLAVE");
+		snd_iprintf(buffer, "\n");
+		spin_lock_irqsave(&timer->lock, flags);
+		list_for_each(q, &timer->open_list_head) {
+			ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list);
+			snd_iprintf(buffer, "  Client %s : %s : lost interrupts %li\n",
+					ti->owner ? ti->owner : "unknown",
+					ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped",
+					ti->lost);
+		}
+		spin_unlock_irqrestore(&timer->lock, flags);
+	}
+	up(&register_mutex);
+}
+
+/*
+ *  USER SPACE interface
+ */
+
+static void snd_timer_user_interrupt(snd_timer_instance_t *timeri,
+				     unsigned long resolution,
+				     unsigned long ticks,
+				     void *data)
+{
+	unsigned long flags;
+	snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, data, return);
+	snd_timer_read_t *r;
+	
+	if (tu->qused >= tu->queue_size) {
+		tu->overrun++;
+	} else {
+		spin_lock_irqsave(&tu->qlock, flags);
+		r = &tu->queue[tu->qtail++];
+		tu->qtail %= tu->queue_size;
+		r->resolution = resolution;
+		r->ticks = ticks;
+		tu->qused++;
+		spin_unlock_irqrestore(&tu->qlock, flags);
+		wake_up(&tu->qchange_sleep);
+	}
+}
+
+static int snd_timer_user_open(struct inode *inode, struct file *file)
+{
+	snd_timer_user_t *tu;
+	
+	tu = snd_magic_kcalloc(snd_timer_user_t, 0, GFP_KERNEL);
+	if (tu == NULL)
+		return -ENOMEM;
+	spin_lock_init(&tu->qlock);
+	init_waitqueue_head(&tu->qchange_sleep);
+	tu->ticks = 1;
+	tu->queue_size = 128;
+	tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
+	if (tu->queue == NULL) {
+		snd_magic_kfree(tu);
+		return -ENOMEM;
+	}
+	file->private_data = tu;
+#ifdef LINUX_2_2
+	MOD_INC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static int snd_timer_user_release(struct inode *inode, struct file *file)
+{
+	snd_timer_user_t *tu;
+
+	if (file->private_data) {
+		tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+		file->private_data = NULL;
+		if (tu->timeri)
+			snd_timer_close(tu->timeri);
+		if (tu->queue)
+			kfree(tu->queue);
+		snd_magic_kfree(tu);
+	}
+#ifdef LINUX_2_2
+	MOD_DEC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static void snd_timer_user_zero_id(snd_timer_id_t *id)
+{
+	id->dev_class = SNDRV_TIMER_CLASS_NONE;
+	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	id->card = -1;
+	id->device = -1;
+	id->subdevice = -1;
+}
+
+static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer)
+{
+	id->dev_class = timer->tmr_class;
+	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	id->card = timer->card ? timer->card->number : -1;
+	id->device = timer->tmr_device;
+	id->subdevice = timer->tmr_subdevice;
+}
+
+static int snd_timer_user_next_device(snd_timer_id_t *_tid)
+{
+	snd_timer_id_t id;
+	snd_timer_t *timer;
+	struct list_head *p;
+	
+	if (copy_from_user(&id, _tid, sizeof(id)))
+		return -EFAULT;
+	down(&register_mutex);
+	if (id.dev_class < 0) {		/* first item */
+		if (list_empty(&snd_timer_list))
+			snd_timer_user_zero_id(&id);
+		else {
+			timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list);
+			snd_timer_user_copy_id(&id, timer);
+		}
+	} else {
+		switch (id.dev_class) {
+		case SNDRV_TIMER_CLASS_GLOBAL:
+			id.device = id.device < 0 ? 0 : id.device + 1;
+			list_for_each(p, &snd_timer_list) {
+				timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+				if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
+					snd_timer_user_copy_id(&id, timer);
+					break;
+				}
+				if (timer->tmr_device >= id.device) {
+					snd_timer_user_copy_id(&id, timer);
+					break;
+				}
+			}
+			if (p == &snd_timer_list)
+				snd_timer_user_zero_id(&id);
+			break;
+		case SNDRV_TIMER_CLASS_CARD:
+		case SNDRV_TIMER_CLASS_PCM:
+			if (id.card < 0) {
+				id.card = 0;
+			} else {
+				if (id.card < 0) {
+					id.card = 0;
+				} else {
+					if (id.device < 0) {
+						id.device = 0;
+					} else {
+						id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1;
+					}
+				}
+			}
+			list_for_each(p, &snd_timer_list) {
+				timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+				if (timer->tmr_class > id.dev_class) {
+					snd_timer_user_copy_id(&id, timer);
+					break;
+				}
+				if (timer->tmr_class < id.dev_class)
+					continue;
+				if (timer->card->number > id.card) {
+					snd_timer_user_copy_id(&id, timer);
+					break;
+				}
+				if (timer->card->number < id.card)
+					continue;
+				if (timer->tmr_device > id.device) {
+					snd_timer_user_copy_id(&id, timer);
+					break;
+				}
+				if (timer->tmr_device < id.device)
+					continue;
+				if (timer->tmr_subdevice > id.subdevice) {
+					snd_timer_user_copy_id(&id, timer);
+					break;
+				}
+				if (timer->tmr_subdevice < id.subdevice)
+					continue;
+				snd_timer_user_copy_id(&id, timer);
+				break;
+			}
+			if (p == &snd_timer_list)
+				snd_timer_user_zero_id(&id);
+			break;
+		default:
+			snd_timer_user_zero_id(&id);
+		}
+	}
+	up(&register_mutex);
+	if (copy_to_user(_tid, &id, sizeof(*_tid)))
+		return -EFAULT;
+	return 0;
+} 
+
+static int snd_timer_user_tselect(struct file *file, snd_timer_select_t *_tselect)
+{
+	snd_timer_user_t *tu;
+	snd_timer_select_t tselect;
+	char str[32];
+	
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	if (tu->timeri)
+		snd_timer_close(tu->timeri);
+	if (copy_from_user(&tselect, _tselect, sizeof(tselect)))
+		return -EFAULT;
+	sprintf(str, "application %i", current->pid);
+	if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
+		tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
+	if ((tu->timeri = snd_timer_open(str, &tselect.id, current->pid)) == NULL)
+		return -ENODEV;
+	tu->timeri->callback = snd_timer_user_interrupt;
+	tu->timeri->callback_data = (void *)tu;
+	return 0;
+}
+
+static int snd_timer_user_info(struct file *file, snd_timer_info_t *_info)
+{
+	snd_timer_user_t *tu;
+	snd_timer_info_t info;
+	snd_timer_t *t;
+
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	t = tu->timeri->timer;
+	snd_assert(t != NULL, return -ENXIO);
+	memset(&info, 0, sizeof(info));
+	info.card = t->card ? t->card->number : -1;
+	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
+		info.flags |= SNDRV_TIMER_FLG_SLAVE;
+	strncpy(info.id, t->id, sizeof(info.id)-1);
+	strncpy(info.name, t->name, sizeof(info.name)-1);
+	info.ticks = t->hw.ticks;
+	info.resolution = t->hw.resolution;
+	if (copy_to_user(_info, &info, sizeof(*_info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_timer_user_params(struct file *file, snd_timer_params_t *_params)
+{
+	unsigned long flags;
+	snd_timer_user_t *tu;
+	snd_timer_params_t params;
+	snd_timer_t *t;
+	snd_timer_read_t *tr;
+	int err;
+	
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	t = tu->timeri->timer;
+	snd_assert(t != NULL, return -ENXIO);
+	if (copy_from_user(&params, _params, sizeof(params)))
+		return -EFAULT;
+	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
+		err = -EINVAL;
+		goto _end;
+	}
+	if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) {
+		err = -EINVAL;
+		goto _end;
+	}
+	snd_timer_stop(tu->timeri);
+	spin_lock_irqsave(&t->lock, flags);
+	if (params.flags & SNDRV_TIMER_PSFLG_AUTO) {
+		tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+	} else {
+		tu->timeri->flags &= ~SNDRV_TIMER_IFLG_AUTO;
+	}
+	spin_unlock_irqrestore(&t->lock, flags);
+	if (params.queue_size > 0 && tu->queue_size != params.queue_size) {
+		tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
+		if (tr) {
+			kfree(tu->queue);
+			tu->queue_size = params.queue_size;
+			tu->queue = tr;
+		}
+	}
+	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) {
+		tu->ticks = 1;
+	} else {
+		tu->ticks = params.ticks;
+	}
+	err = 0;
+ _end:
+	if (copy_to_user(_params, &params, sizeof(params)))
+		return -EFAULT;
+	return err;
+}
+
+static int snd_timer_user_status(struct file *file, snd_timer_status_t *_status)
+{
+	unsigned long flags;
+	snd_timer_user_t *tu;
+	snd_timer_status_t status;
+	
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	memset(&status, 0, sizeof(status));
+	status.resolution = snd_timer_resolution(tu->timeri);
+	status.lost = tu->timeri->lost;
+	status.overrun = tu->overrun;
+	spin_lock_irqsave(&tu->qlock, flags);
+	status.queue = tu->qused;
+	spin_unlock_irqrestore(&tu->qlock, flags);
+	if (copy_to_user(_status, &status, sizeof(status)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_timer_user_start(struct file *file)
+{
+	int err;
+	snd_timer_user_t *tu;
+		
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	snd_timer_stop(tu->timeri);
+	tu->timeri->lost = 0;
+	return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_stop(struct file *file)
+{
+	int err;
+	snd_timer_user_t *tu;
+		
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_continue(struct file *file)
+{
+	int err;
+	snd_timer_user_t *tu;
+		
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	tu->timeri->lost = 0;
+	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_ioctl(struct inode *inode, struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	snd_timer_user_t *tu;
+	
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	switch (cmd) {
+	case SNDRV_TIMER_IOCTL_PVERSION:
+		return put_user(SNDRV_TIMER_VERSION, (int *)arg) ? -EFAULT : 0;
+	case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
+		return snd_timer_user_next_device((snd_timer_id_t *)arg);
+	case SNDRV_TIMER_IOCTL_SELECT:
+		return snd_timer_user_tselect(file, (snd_timer_select_t *)arg);
+	case SNDRV_TIMER_IOCTL_INFO:
+		return snd_timer_user_info(file, (snd_timer_info_t *)arg);
+	case SNDRV_TIMER_IOCTL_PARAMS:
+		return snd_timer_user_params(file, (snd_timer_params_t *)arg);
+	case SNDRV_TIMER_IOCTL_STATUS:
+		return snd_timer_user_status(file, (snd_timer_status_t *)arg);
+	case SNDRV_TIMER_IOCTL_START:
+		return snd_timer_user_start(file);
+	case SNDRV_TIMER_IOCTL_STOP:
+		return snd_timer_user_stop(file);
+	case SNDRV_TIMER_IOCTL_CONTINUE:
+		return snd_timer_user_continue(file);
+	}
+	return -ENOTTY;
+}
+
+static ssize_t snd_timer_user_read(struct file *file, char *buffer, size_t count, loff_t *offset)
+{
+	snd_timer_user_t *tu;
+	long result = 0;
+	int  err = 0;
+	
+	tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO);
+	while (count - result >= sizeof(snd_timer_read_t)) {
+		spin_lock_irq(&tu->qlock);
+		while (!tu->qused) {
+			wait_queue_t wait;
+
+			if (file->f_flags & O_NONBLOCK) {
+				spin_unlock_irq(&tu->qlock);
+				err = -EAGAIN;
+				break;
+			}
+
+			set_current_state(TASK_INTERRUPTIBLE);
+			init_waitqueue_entry(&wait, current);
+			add_wait_queue(&tu->qchange_sleep, &wait);
+
+			spin_unlock(&tu->qlock);
+			schedule();
+			spin_lock_irq(&tu->qlock);
+
+			remove_wait_queue(&tu->qchange_sleep, &wait);
+
+			if (signal_pending(current)) {
+				err = -ERESTARTSYS;
+				break;
+			}
+		}
+		spin_unlock_irq(&tu->qlock);
+		if (err < 0)
+			break;
+
+		if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) {
+			err = -EFAULT;
+			break;
+		}
+
+		tu->qhead %= tu->queue_size;
+		spin_lock_irq(&tu->qlock);
+		tu->qused--;
+		spin_unlock_irq(&tu->qlock);
+		result += sizeof(snd_timer_read_t);
+		buffer += sizeof(snd_timer_read_t);
+	}
+	return err? err: result;
+}
+
+static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
+{
+        unsigned int mask;
+        snd_timer_user_t *tu;
+
+        tu = snd_magic_cast(snd_timer_user_t, file->private_data, return 0);
+
+        poll_wait(file, &tu->qchange_sleep, wait);
+	
+	mask = 0;
+	if (tu->qused)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static struct file_operations snd_timer_f_ops =
+{
+#ifndef LINUX_2_2
+	owner:		THIS_MODULE,
+#endif
+	read:		snd_timer_user_read,
+	open:		snd_timer_user_open,
+	release:	snd_timer_user_release,
+	poll:		snd_timer_user_poll,
+	ioctl:		snd_timer_user_ioctl,
+};
+
+static snd_minor_t snd_timer_reg =
+{
+	comment:	"timer",
+	f_ops:		&snd_timer_f_ops,
+};
+
+/*
+ *  ENTRY functions
+ */
+
+static snd_info_entry_t *snd_timer_proc_entry = NULL;
+
+static int __init alsa_timer_init(void)
+{
+	int err;
+	snd_info_entry_t *entry;
+
+#ifdef CONFIG_SND_OSSEMUL
+	snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer");
+#endif
+	if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128;
+		entry->c.text.read = snd_timer_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	snd_timer_proc_entry = entry;
+	if ((err = snd_timer_register_system()) < 0)
+		snd_printk(KERN_ERR "unable to register system timer (%i)\n", err);
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER,
+					NULL, 0, &snd_timer_reg, "timer"))<0)
+		snd_printk(KERN_ERR "unable to register timer device (%i)\n", err);
+	return 0;
+}
+
+static void __exit alsa_timer_exit(void)
+{
+	struct list_head *p, *n;
+
+	snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0);
+	/* unregister the system timer */
+	list_for_each_safe(p, n, &snd_timer_list) {
+		snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+		snd_timer_unregister(timer);
+	}
+	if (snd_timer_proc_entry) {
+		snd_info_unregister(snd_timer_proc_entry);
+		snd_timer_proc_entry = NULL;
+	}
+#ifdef CONFIG_SND_OSSEMUL
+	snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
+#endif
+}
+
+module_init(alsa_timer_init)
+module_exit(alsa_timer_exit)
+
+EXPORT_SYMBOL(snd_timer_open);
+EXPORT_SYMBOL(snd_timer_close);
+EXPORT_SYMBOL(snd_timer_resolution);
+EXPORT_SYMBOL(snd_timer_start);
+EXPORT_SYMBOL(snd_timer_stop);
+EXPORT_SYMBOL(snd_timer_continue);
+EXPORT_SYMBOL(snd_timer_new);
+EXPORT_SYMBOL(snd_timer_global_new);
+EXPORT_SYMBOL(snd_timer_global_free);
+EXPORT_SYMBOL(snd_timer_global_register);
+EXPORT_SYMBOL(snd_timer_global_unregister);
+EXPORT_SYMBOL(snd_timer_interrupt);
+EXPORT_SYMBOL(snd_timer_system_resolution);
diff -Nru linux/sound/core/wrappers.c linux-2.4.19-pre5-mjc/sound/core/wrappers.c
--- linux/sound/core/wrappers.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/core/wrappers.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,62 @@
+/*
+ *  Various wrappers
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef ALSA_BUILD
+#include "config.h"
+#endif
+
+#define __NO_VERSION__
+#include <linux/version.h>
+#include <linux/config.h>
+#ifdef ALSA_BUILD
+#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__)
+#define MODVERSIONS
+#include <linux/modversions.h>
+#include "sndversions.h"
+#endif
+#endif
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+void *snd_wrapper_kmalloc(size_t size, int flags)
+{
+	return kmalloc(size, flags);
+}
+
+void snd_wrapper_kfree(const void *obj)
+{
+	kfree(obj);
+}
+
+void *snd_wrapper_vmalloc(unsigned long size)
+{
+	return vmalloc(size);
+}
+
+void snd_wrapper_vfree(void *obj)
+{
+	vfree(obj);
+}
+#endif
diff -Nru linux/sound/drivers/Config.help linux-2.4.19-pre5-mjc/sound/drivers/Config.help
--- linux/sound/drivers/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/Config.help	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+CONFIG_SND_DUMMY
+  Say 'Y' or 'M' to include dummy driver. This driver does nothing, but
+  emulates various mixer controls and PCM devices.
+  
+CONFIG_SND_VIRMIDI
+  Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to
+  connect applications using raw MIDI devices to sequencer.
+
+CONFIG_SND_MTPAV
+  Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport
+  MIDI adapter.
+
+CONFIG_SND_SERIAL_U16550
+  Say 'Y' or 'M' to include support for MIDI serial port driver. It works
+  with serial UARTs 16550 and better.
+
+CONFIG_SND_MPU401
+  Say 'Y' or 'M' to include support for MPU401 hardware using UART access.
diff -Nru linux/sound/drivers/Config.in linux-2.4.19-pre5-mjc/sound/drivers/Config.in
--- linux/sound/drivers/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/Config.in	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,12 @@
+# ALSA generic drivers
+
+mainmenu_option next_comment
+comment 'Generic devices'
+
+dep_tristate 'Dummy (/dev/null) soundcard' CONFIG_SND_DUMMY $CONFIG_SND
+dep_tristate 'Virtual MIDI soundcard' CONFIG_SND_VIRMIDI $CONFIG_SND $CONFIG_SND_SEQUENCER
+dep_tristate 'MOTU MidiTimePiece AV multiport MIDI' CONFIG_SND_MTPAV $CONFIG_SND
+dep_tristate 'UART16550 - MIDI only driver' CONFIG_SND_SERIAL_U16550 $CONFIG_SND
+dep_tristate 'Generic MPU-401 UART driver' CONFIG_SND_MPU401 $CONFIG_SND
+
+endmenu
diff -Nru linux/sound/drivers/Makefile linux-2.4.19-pre5-mjc/sound/drivers/Makefile
--- linux/sound/drivers/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,36 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := drivers.o
+
+subdir-y     := opl3 mpu401
+subdir-m     := $(subdir-y)
+
+list-multi   := snd-dummy.o snd-mtpav.o snd-serial-u16550.o snd-virmidi.o
+
+snd-dummy-objs := dummy.o
+snd-mtpav-objs := mtpav.o
+snd-serial-u16550-objs := serial-u16550.o
+snd-virmidi-objs := virmidi.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
+obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
+obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
+obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
+
+include $(TOPDIR)/Rules.make
+
+snd-dummy.o: $(snd-dummy-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-dummy-objs)
+
+snd-mtpav.o: $(snd-mtpav-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-mtpav-objs)
+
+snd-serial-u16550.o: $(snd-serial-u16550-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-serial-u16550-objs)
+
+snd-virmidi.o: $(snd-virmidi-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-virmidi-objs)
diff -Nru linux/sound/drivers/dummy.c linux-2.4.19-pre5-mjc/sound/drivers/dummy.c
--- linux/sound/drivers/dummy.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/dummy.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,613 @@
+/*
+ *  Dummy soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA,Dummy soundcard}}");
+
+#define MAX_PCM_DEVICES		4
+#define MAX_PCM_SUBSTREAMS	16
+#define MAX_MIDI_DEVICES	2
+
+#if 0 /* RME9652 emulation */
+#define MAX_BUFFER_SIZE		(26 * 64 * 1024)
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
+#define USE_CHANNELS_MIN	26
+#define USE_CHANNELS_MAX	26
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		2
+#endif
+
+/* defaults */
+#ifndef MAX_BUFFER_SIZE
+#define MAX_BUFFER_SIZE		(64*1024)
+#endif
+#ifndef USE_FORMATS
+#define USE_FORMATS 		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
+#endif
+#ifndef USE_CHANNELS_MIN
+#define USE_CHANNELS_MIN 	1
+#endif
+#ifndef USE_CHANNELS_MAX
+#define USE_CHANNELS_MAX 	2
+#endif
+#ifndef USE_PERIODS_MIN
+#define USE_PERIODS_MIN 	1
+#endif
+#ifndef USE_PERIODS_MAX
+#define USE_PERIODS_MAX 	1024
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int snd_pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
+//static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for dummy soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for dummy soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable this dummy soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_pcm_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_devs, "PCM devices # (0-4) for dummy driver.");
+MODULE_PARM_SYNTAX(snd_pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list");
+MODULE_PARM(snd_pcm_substreams, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
+MODULE_PARM_SYNTAX(snd_pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list");
+//MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+//MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (0-2) for dummy driver.");
+//MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list");
+
+#define MIXER_ADDR_MASTER	0
+#define MIXER_ADDR_LINE		1
+#define MIXER_ADDR_MIC		2
+#define MIXER_ADDR_SYNTH	3
+#define MIXER_ADDR_CD		4
+#define MIXER_ADDR_LAST		4
+
+typedef struct snd_card_dummy {
+	snd_card_t *card;
+	spinlock_t mixer_lock;
+	int mixer_volume[MIXER_ADDR_LAST+1][2];
+	int capture_source[MIXER_ADDR_LAST+1][2];
+} snd_card_dummy_t;
+
+typedef struct snd_card_dummy_pcm {
+	snd_card_dummy_t *dummy;
+	spinlock_t lock;
+	struct timer_list timer;
+	unsigned int pcm_size;
+	unsigned int pcm_count;
+	unsigned int pcm_bps;		/* bytes per second */
+	unsigned int pcm_jiffie;	/* bytes per one jiffie */
+	unsigned int pcm_irq_pos;	/* IRQ position */
+	unsigned int pcm_buf_pos;	/* position in buffer */
+	snd_pcm_substream_t *substream;
+} snd_card_dummy_pcm_t;
+
+static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream,
+				         unsigned int cmd,
+				         void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream,
+					unsigned int cmd,
+					void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return);
+
+	dpcm->timer.expires = 1 + jiffies;
+	add_timer(&dpcm->timer);
+}
+
+static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return);
+
+	del_timer(&dpcm->timer);
+}
+
+static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		snd_card_dummy_pcm_timer_start(substream);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		snd_card_dummy_pcm_timer_stop(substream);
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream,
+					  int cmd)
+{
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		snd_card_dummy_pcm_timer_start(substream);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		snd_card_dummy_pcm_timer_stop(substream);
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO);
+	unsigned int bps;
+
+	bps = runtime->rate * runtime->channels;
+	bps *= snd_pcm_format_width(runtime->format);
+	bps /= 8;
+	if (bps <= 0)
+		return -EINVAL;
+	dpcm->pcm_bps = bps;
+	dpcm->pcm_jiffie = bps / HZ;
+	dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+	dpcm->pcm_count = snd_pcm_lib_period_bytes(substream);
+	dpcm->pcm_irq_pos = 0;
+	dpcm->pcm_buf_pos = 0;
+	return 0;
+}
+
+static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream)
+{
+	return snd_card_dummy_pcm_prepare(substream);
+}
+
+static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream)
+{
+	return snd_card_dummy_pcm_prepare(substream);
+}
+
+static void snd_card_dummy_pcm_timer_function(unsigned long data)
+{
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, (void *)data, return);
+	
+	dpcm->timer.expires = 1 + jiffies;
+	add_timer(&dpcm->timer);
+	spin_lock_irq(&dpcm->lock);
+	dpcm->pcm_irq_pos += dpcm->pcm_jiffie;
+	dpcm->pcm_buf_pos += dpcm->pcm_jiffie;
+	dpcm->pcm_buf_pos %= dpcm->pcm_size;
+	while (dpcm->pcm_irq_pos >= dpcm->pcm_count) {
+		dpcm->pcm_irq_pos -= dpcm->pcm_count;
+		snd_pcm_period_elapsed(dpcm->substream);
+	}
+	spin_unlock_irq(&dpcm->lock);	
+}
+
+static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO);
+
+	return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
+}
+
+static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO);
+
+	return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
+}
+
+static snd_pcm_hardware_t snd_card_dummy_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		USE_FORMATS,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5500,
+	rate_max:		48000,
+	channels_min:		USE_CHANNELS_MIN,
+	channels_max:		USE_CHANNELS_MAX,
+	buffer_bytes_max:	MAX_BUFFER_SIZE,
+	period_bytes_min:	64,
+	period_bytes_max:	MAX_BUFFER_SIZE,
+	periods_min:		USE_PERIODS_MIN,
+	periods_max:		USE_PERIODS_MAX,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_card_dummy_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		USE_FORMATS,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5500,
+	rate_max:		48000,
+	channels_min:		USE_CHANNELS_MIN,
+	channels_max:		USE_CHANNELS_MAX,
+	buffer_bytes_max:	MAX_BUFFER_SIZE,
+	period_bytes_min:	64,
+	period_bytes_max:	MAX_BUFFER_SIZE,
+	periods_min:		USE_PERIODS_MIN,
+	periods_max:		USE_PERIODS_MAX,
+	fifo_size:		0,
+};
+
+static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime)
+{
+	snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return);
+	snd_magic_kfree(dpcm);
+}
+
+static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm;
+
+	dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL);
+	if (dpcm == NULL)
+		return -ENOMEM;
+	if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) {
+		snd_magic_kfree(dpcm);
+		return -ENOMEM;
+	}
+	dpcm->timer.data = (unsigned long) dpcm;
+	dpcm->timer.function = snd_card_dummy_pcm_timer_function;
+	spin_lock_init(&dpcm->lock);
+	dpcm->substream = substream;
+	runtime->private_data = dpcm;
+	runtime->private_free = snd_card_dummy_runtime_free;
+	runtime->hw = snd_card_dummy_playback;
+	if (substream->pcm->device & 1) {
+		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+	}
+	if (substream->pcm->device & 2)
+		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
+	return 0;
+}
+
+static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_card_dummy_pcm_t *dpcm;
+
+	dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL);
+	if (dpcm == NULL)
+		return -ENOMEM;
+	if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) {
+		snd_magic_kfree(dpcm);
+		return -ENOMEM;
+	}
+	memset(runtime->dma_area, 0, runtime->dma_bytes);
+	dpcm->timer.data = (unsigned long) dpcm;
+	dpcm->timer.function = snd_card_dummy_pcm_timer_function;
+	spin_lock_init(&dpcm->lock);
+	dpcm->substream = substream;
+	runtime->private_data = dpcm;
+	runtime->private_free = snd_card_dummy_runtime_free;
+	runtime->hw = snd_card_dummy_capture;
+	if (substream->pcm->device == 1) {
+		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+	}
+	if (substream->pcm->device & 2)
+		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
+	return 0;
+}
+
+static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+	return 0;
+}
+
+static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_card_dummy_playback_ops = {
+	open:			snd_card_dummy_playback_open,
+	close:			snd_card_dummy_playback_close,
+	ioctl:			snd_card_dummy_playback_ioctl,
+	prepare:		snd_card_dummy_playback_prepare,
+	trigger:		snd_card_dummy_playback_trigger,
+	pointer:		snd_card_dummy_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_card_dummy_capture_ops = {
+	open:			snd_card_dummy_capture_open,
+	close:			snd_card_dummy_capture_close,
+	ioctl:			snd_card_dummy_capture_ioctl,
+	prepare:		snd_card_dummy_capture_prepare,
+	trigger:		snd_card_dummy_capture_trigger,
+	pointer:		snd_card_dummy_capture_pointer,
+};
+
+static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
+	pcm->private_data = dummy;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Dummy PCM");
+	return 0;
+}
+
+#define DUMMY_VOLUME(xname, xindex, addr) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_dummy_volume_info, \
+  get: snd_dummy_volume_get, put: snd_dummy_volume_put, \
+  private_value: addr }
+
+static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 100;
+	return 0;
+}
+ 
+static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int addr = kcontrol->private_value;
+
+	spin_lock_irqsave(&dummy->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
+	ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
+	spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+	return 0;
+}                                                                                                                                                                                                                                                                                                            
+
+static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change, addr = kcontrol->private_value;
+	int left, right;
+
+	left = ucontrol->value.integer.value[0] % 101;
+	right = ucontrol->value.integer.value[1] % 101;
+	spin_lock_irqsave(&dummy->mixer_lock, flags);
+	change = dummy->mixer_volume[addr][0] != left &&
+	         dummy->mixer_volume[addr][1] != right;
+	dummy->mixer_volume[addr][0] = left;
+	dummy->mixer_volume[addr][1] = right;
+	spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+	return change;
+}                                                                                                                                                                                                                                                                                                            
+
+#define DUMMY_CAPSRC(xname, xindex, addr) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_dummy_capsrc_info, \
+  get: snd_dummy_capsrc_get, put: snd_dummy_capsrc_put, \
+  private_value: addr }
+
+static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+ 
+static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int addr = kcontrol->private_value;
+
+	spin_lock_irqsave(&dummy->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
+	ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
+	spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+	return 0;
+}
+
+static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change, addr = kcontrol->private_value;
+	int left, right;
+
+	left = ucontrol->value.integer.value[0] & 1;
+	right = ucontrol->value.integer.value[1] & 1;
+	spin_lock_irqsave(&dummy->mixer_lock, flags);
+	change = dummy->capture_source[addr][0] != left &&
+	         dummy->capture_source[addr][1] != right;
+	dummy->capture_source[addr][0] = left;
+	dummy->capture_source[addr][1] = right;
+	spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+	return change;
+}                                                                                                                                                                                                                                                                                                            
+
+#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_dummy_controls[] = {
+DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
+DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
+DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
+DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
+DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER)
+};
+
+int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy)
+{
+	snd_card_t *card = dummy->card;
+	int idx, err;
+
+	snd_assert(dummy != NULL, return -EINVAL);
+	spin_lock_init(&dummy->mixer_lock);
+	strcpy(card->mixername, "Dummy Mixer");
+
+	for (idx = 0; idx < DUMMY_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int __init snd_card_dummy_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_card_dummy *dummy;
+	int idx, err;
+
+	if (!snd_enable[dev])
+		return -ENODEV;
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_card_dummy));
+	if (card == NULL)
+		return -ENOMEM;
+	dummy = (struct snd_card_dummy *)card->private_data;
+	dummy->card = card;
+	for (idx = 0; idx < MAX_PCM_DEVICES && idx < snd_pcm_devs[dev]; idx++) {
+		if (snd_pcm_substreams[dev] < 1)
+			snd_pcm_substreams[dev] = 1;
+		if (snd_pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
+			snd_pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
+		if ((err = snd_card_dummy_pcm(dummy, idx, snd_pcm_substreams[dev])) < 0)
+			goto __nodev;
+	}
+	if ((err = snd_card_dummy_new_mixer(dummy)) < 0)
+		goto __nodev;
+	strcpy(card->driver, "Dummy");
+	strcpy(card->shortname, "Dummy");
+	sprintf(card->longname, "Dummy %i", dev + 1);
+	if ((err = snd_card_register(card)) == 0) {
+		snd_dummy_cards[dev] = card;
+		return 0;
+	}
+      __nodev:
+	snd_card_free(card);
+	return err;
+}
+
+static int __init alsa_card_dummy_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_card_dummy_probe(dev) < 0) {
+#ifdef MODULE
+			printk(KERN_ERR "Dummy soundcard #%i not found or device busy\n", dev + 1);
+#endif
+			break;
+		}
+		cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "Dummy soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_dummy_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_dummy_cards[idx]);
+}
+
+module_init(alsa_card_dummy_init)
+module_exit(alsa_card_dummy_exit)
+
+#ifndef MODULE
+
+/* format is: snd-dummy=snd_enable,snd_index,snd_id,
+			snd_pcm_devs,snd_pcm_substreams */
+
+static int __init alsa_card_dummy_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_devs[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_substreams[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-dummy=", alsa_card_dummy_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/drivers/mpu401/Makefile linux-2.4.19-pre5-mjc/sound/drivers/mpu401/Makefile
--- linux/sound/drivers/mpu401/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/mpu401/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,53 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _mpu401.o
+
+list-multi   := snd-mpu401.o snd-mpu401-uart.o
+
+export-objs  := mpu401_uart.o
+
+snd-mpu401-objs := mpu401.o
+snd-mpu401-uart-objs := mpu401_uart.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_MPU401) += snd-mpu401.o snd-mpu401-uart.o
+obj-$(CONFIG_SND_ALS100) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_AZT2320) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_DT0197H) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ES18XX) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_AD1816A) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_CS4231) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_CS4232) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_CS4236) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ES1688) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_OPTI93X) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_SB16) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_SBAWE) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_WAVEFRONT) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ALS4000) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_CMIPCI) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ES1938) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o
+
+include $(TOPDIR)/Rules.make
+
+snd-mpu401.o: $(snd-mpu401-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-objs)
+
+snd-mpu401-uart.o: $(snd-mpu401-uart-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-uart-objs)
diff -Nru linux/sound/drivers/mpu401/mpu401.c linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401.c
--- linux/sound/drivers/mpu401/mpu401.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,155 @@
+/*
+ *  Driver for generic MPU-401 boards (UART mode only)
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <linux/delay.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("MPU-401 UART");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* MPU-401 port number */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* MPU-401 IRQ */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for MPU-401 device.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for MPU-401 device.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable MPU-401 device.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for MPU-401 device.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for MPU-401 device.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+
+static snd_card_t *snd_mpu401_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static int __init snd_card_mpu401_probe(int dev)
+{
+	snd_card_t *card;
+	int err;
+
+	if (snd_port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify snd_port\n");
+		return -EINVAL;
+	}
+	if (snd_irq[dev] == SNDRV_AUTO_IRQ) {
+		snd_printk("specify or disable IRQ port\n");
+		return -EINVAL;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				snd_port[dev], 0,
+				snd_irq[dev], snd_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) {
+		printk(KERN_ERR "MPU401 not detected at 0x%lx\n", snd_port[dev]);
+		snd_card_free(card);
+		return -ENODEV;
+	}
+	strcpy(card->driver, "MPU-401 UART");
+	strcpy(card->shortname, card->driver);
+	sprintf(card->longname, "%s at 0x%lx, ", card->shortname, snd_port[dev]);
+	if (snd_irq[dev] >= 0) {
+		sprintf(card->longname + strlen(card->longname), "IRQ %d", snd_irq[dev]);
+	} else {
+		strcat(card->longname, "polled");
+	}
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_mpu401_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_mpu401_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		if (snd_card_mpu401_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "MPU-401 device not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_mpu401_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_mpu401_cards[idx]);
+}
+
+module_init(alsa_card_mpu401_init)
+module_exit(alsa_card_mpu401_exit)
+
+#ifndef MODULE
+
+/* format is: snd-mpu401=snd_enable,snd_index,snd_id,snd_port,snd_irq */
+
+static int __init alsa_card_mpu401_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-mpu401=", alsa_card_mpu401_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/drivers/mpu401/mpu401_uart.c linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401_uart.c
--- linux/sound/drivers/mpu401/mpu401_uart.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401_uart.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,445 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of MPU-401 in UART mode
+ *
+ *  MPU-401 supports UART mode which is not capable generate transmit
+ *  interrupts thus output is done via polling. Also, if irq < 0, then
+ *  input is done also via polling. Do not expect good performance.
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode");
+MODULE_LICENSE("GPL");
+
+static void snd_mpu401_uart_input_read(mpu401_t * mpu);
+static void snd_mpu401_uart_output_write(mpu401_t * mpu);
+
+/*
+
+ */
+
+#define snd_mpu401_input_avail(mpu)	(!(inb(MPU401C(mpu)) & 0x80))
+#define snd_mpu401_output_ready(mpu)	(!(inb(MPU401C(mpu)) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+static void snd_mpu401_uart_clear_rx(mpu401_t *mpu)
+{
+	int timeout = 100000;
+	for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--)
+		inb(MPU401D(mpu));
+#ifdef CONFIG_SND_DEBUG
+	if (timeout <= 0)
+		snd_printk("cmd: clear rx timeout (status = 0x%x)\n", inb(MPU401C(mpu)));
+#endif
+}
+
+static void _snd_mpu401_uart_interrupt(mpu401_t *mpu)
+{
+	if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
+		snd_mpu401_uart_input_read(mpu);
+	else
+		snd_mpu401_uart_clear_rx(mpu);
+	/* ok. for better Tx performance try do some output when input is done */
+	if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode))
+		snd_mpu401_uart_output_write(mpu);
+}
+
+void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return);
+	
+	if (mpu == NULL)
+		return;
+	_snd_mpu401_uart_interrupt(mpu);
+}
+
+static void snd_mpu401_uart_timer(unsigned long data)
+{
+	unsigned long flags;
+	mpu401_t *mpu = snd_magic_cast(mpu401_t, (void *)data, return);
+
+	spin_lock_irqsave(&mpu->timer_lock, flags);
+	/*mpu->mode |= MPU401_MODE_TIMER;*/
+	mpu->timer.expires = 1 + jiffies;
+	add_timer(&mpu->timer);
+	spin_unlock_irqrestore(&mpu->timer_lock, flags);
+	if (mpu->rmidi)
+		_snd_mpu401_uart_interrupt(mpu);
+}
+
+static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&mpu->timer_lock, flags);
+	if (mpu->timer_invoked == 0) {
+		mpu->timer.data = (unsigned long)mpu;
+		mpu->timer.function = snd_mpu401_uart_timer;
+		mpu->timer.expires = 1 + jiffies;
+		add_timer(&mpu->timer);
+	} 
+	mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER;
+	spin_unlock_irqrestore (&mpu->timer_lock, flags);
+}
+
+static void snd_mpu401_uart_remove_timer (mpu401_t *mpu, int input)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&mpu->timer_lock, flags);
+	if (mpu->timer_invoked) {
+		mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER;
+		if (! mpu->timer_invoked)
+			del_timer(&mpu->timer);
+	}
+	spin_unlock_irqrestore (&mpu->timer_lock, flags);
+}
+
+/*
+
+ */
+
+static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack)
+{
+	unsigned long flags;
+	int timeout, ok;
+
+	spin_lock_irqsave(&mpu->input_lock, flags);
+	if (mpu->hardware != MPU401_HW_TRID4DWAVE) {
+		outb(0x00, MPU401D(mpu));
+		/*snd_mpu401_uart_clear_rx(mpu);*/
+	}
+	/* ok. standard MPU-401 initialization */
+	if (mpu->hardware != MPU401_HW_SB) {
+		for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--)
+			udelay(10);
+#ifdef CONFIG_SND_DEBUG
+		if (!timeout)
+			snd_printk("cmd: tx timeout (status = 0x%x)\n", inb(MPU401C(mpu)));
+#endif
+	}
+	outb(cmd, MPU401C(mpu));
+	if (ack) {
+		ok = 0;
+		timeout = 10000;
+		while (!ok && timeout-- > 0) {
+			if (snd_mpu401_input_avail(mpu)) {
+				if (inb(MPU401D(mpu)) == MPU401_ACK)
+					ok = 1;
+			}
+		}
+		if (!ok && inb(MPU401D(mpu)) == MPU401_ACK)
+			ok = 1;
+	} else {
+		ok = 1;
+	}
+	spin_unlock_irqrestore(&mpu->input_lock, flags);
+	if (! ok)
+		snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu)));
+	// snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu)));
+}
+
+/*
+ * input/output open/close - protected by open_mutex in rawmidi.c
+ */
+static int snd_mpu401_uart_input_open(snd_rawmidi_substream_t * substream)
+{
+	mpu401_t *mpu;
+	int err;
+
+	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO);
+	if (mpu->open_input && (err = mpu->open_input(mpu)) < 0)
+		return err;
+	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1);
+		snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1);
+	}
+	mpu->substream_input = substream;
+	set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
+	return 0;
+}
+
+static int snd_mpu401_uart_output_open(snd_rawmidi_substream_t * substream)
+{
+	mpu401_t *mpu;
+	int err;
+
+	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO);
+	if (mpu->open_output && (err = mpu->open_output(mpu)) < 0)
+		return err;
+	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1);
+		snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1);
+	}
+	mpu->substream_output = substream;
+	set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
+	return 0;
+}
+
+static int snd_mpu401_uart_input_close(snd_rawmidi_substream_t * substream)
+{
+	mpu401_t *mpu;
+
+	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO);
+	clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
+	mpu->substream_input = NULL;
+	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode))
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);
+	if (mpu->close_input)
+		mpu->close_input(mpu);
+	return 0;
+}
+
+static int snd_mpu401_uart_output_close(snd_rawmidi_substream_t * substream)
+{
+	mpu401_t *mpu;
+
+	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO);
+	clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
+	mpu->substream_output = NULL;
+	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);
+	if (mpu->close_output)
+		mpu->close_output(mpu);
+	return 0;
+}
+
+/*
+ * trigger input
+ */
+static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	mpu401_t *mpu;
+	int max = 64;
+
+	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return);
+	spin_lock_irqsave(&mpu->input_lock, flags);
+	if (up) {
+		if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) {
+			/* flush FIFO */
+			while (max-- > 0)
+				inb(MPU401D(mpu));
+		}
+		if (mpu->irq < 0)
+			snd_mpu401_uart_add_timer(mpu, 1);
+	} else {
+		if (mpu->irq < 0)
+			snd_mpu401_uart_remove_timer(mpu, 1);
+		clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
+	}
+	spin_unlock_irqrestore(&mpu->input_lock, flags);
+	if (up)
+		snd_mpu401_uart_input_read(mpu);
+}
+
+static void snd_mpu401_uart_input_read(mpu401_t * mpu)
+{
+	int max = 128;
+	unsigned char byte;
+
+	/* prevent double enter via event callback */
+	if (test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode))
+		return;
+	spin_lock(&mpu->input_lock);
+	while (max-- > 0) {
+		if (snd_mpu401_input_avail(mpu)) {
+			byte = inb(MPU401D(mpu));
+			if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) {
+				spin_unlock(&mpu->input_lock);
+				snd_rawmidi_receive(mpu->substream_input, &byte, 1);
+				spin_lock(&mpu->input_lock);
+			}
+		} else {
+			break; /* input not available */
+		}
+	}
+	spin_unlock(&mpu->input_lock);
+	clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode);
+}
+
+/*
+ *  Tx FIFO sizes:
+ *    CS4237B			- 16 bytes
+ *    AudioDrive ES1688         - 12 bytes
+ *    S3 SonicVibes             -  8 bytes
+ *    SoundBlaster AWE 64       -  2 bytes (ugly hardware)
+ */
+
+static void snd_mpu401_uart_output_write(mpu401_t * mpu)
+{
+	unsigned char byte;
+	int max = 256, timeout;
+
+	if (!test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode))
+		return;
+	/* prevent double enter */
+	if (test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode))
+		return;
+	do {
+		spin_lock(&mpu->output_lock);
+		if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) {
+			for (timeout = 100; timeout > 0; timeout--) {
+				if (snd_mpu401_output_ready(mpu)) {
+					outb(byte, MPU401D(mpu));
+					snd_rawmidi_transmit_ack(mpu->substream_output, 1);
+					break;
+				}
+			}
+		} else {
+			snd_mpu401_uart_remove_timer (mpu, 0);
+			max = 1; /* no other data - leave the tx loop */
+		}
+		spin_unlock(&mpu->output_lock);
+	} while (--max > 0);
+	clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode);
+}
+
+static void snd_mpu401_uart_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	mpu401_t *mpu;
+
+	mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return);
+	spin_lock_irqsave(&mpu->output_lock, flags);
+	if (up) {
+		set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
+		snd_mpu401_uart_add_timer(mpu, 0);
+	} else {
+		snd_mpu401_uart_remove_timer(mpu, 0);
+		clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
+	}
+	spin_unlock_irqrestore(&mpu->output_lock, flags);
+	if (up)
+		snd_mpu401_uart_output_write(mpu);
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_mpu401_uart_output =
+{
+	open:		snd_mpu401_uart_output_open,
+	close:		snd_mpu401_uart_output_close,
+	trigger:	snd_mpu401_uart_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_mpu401_uart_input =
+{
+	open:		snd_mpu401_uart_input_open,
+	close:		snd_mpu401_uart_input_close,
+	trigger:	snd_mpu401_uart_input_trigger,
+};
+
+static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi)
+{
+	mpu401_t *mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return);
+	if (mpu->irq_flags && mpu->irq >= 0)
+		free_irq(mpu->irq, (void *) mpu);
+	if (mpu->res) {
+		release_resource(mpu->res);
+		kfree_nocheck(mpu->res);
+	}
+	snd_magic_kfree(mpu);
+}
+
+int snd_mpu401_uart_new(snd_card_t * card, int device,
+			unsigned short hardware,
+			unsigned long port, int integrated,
+			int irq, int irq_flags,
+			snd_rawmidi_t ** rrawmidi)
+{
+	mpu401_t *mpu;
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0)
+		return err;
+	mpu = snd_magic_kcalloc(mpu401_t, 0, GFP_KERNEL);
+	if (mpu == NULL) {
+		snd_device_free(card, rmidi);
+		return -ENOMEM;
+	}
+	rmidi->private_data = mpu;
+	rmidi->private_free = snd_mpu401_uart_free;
+	spin_lock_init(&mpu->input_lock);
+	spin_lock_init(&mpu->output_lock);
+	spin_lock_init(&mpu->timer_lock);
+	mpu->hardware = hardware;
+	if (!integrated) {
+		if ((mpu->res = request_region(port, 2, "MPU401 UART")) == NULL) {
+			snd_device_free(card, rmidi);
+			return -EBUSY;
+		}
+	}
+	mpu->port = port;
+	if (irq >= 0 && irq_flags) {
+		if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) {
+			snd_printk("unable to grab IRQ %d\n", irq);
+			snd_device_free(card, rmidi);
+			return -EBUSY;
+		}
+		mpu->irq = irq;
+		mpu->irq_flags = irq_flags;
+	}
+	strcpy(rmidi->name, "MPU-401 (UART)");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+	mpu->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
+EXPORT_SYMBOL(snd_mpu401_uart_new);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_mpu401_uart_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_mpu401_uart_exit(void)
+{
+}
+
+module_init(alsa_mpu401_uart_init)
+module_exit(alsa_mpu401_uart_exit)
diff -Nru linux/sound/drivers/mtpav.c linux-2.4.19-pre5-mjc/sound/drivers/mtpav.c
--- linux/sound/drivers/mtpav.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/mtpav.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,813 @@
+/*
+ *      MOTU Midi Timepiece ALSA Main routines
+ *      Copyright by Michael T. Mayers (c) Jan 09, 2000
+ *      mail: tweakoz@pacbell.net
+ *      Thanks to John Galbraith
+ *
+ *      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 is for the 'Mark Of The Unicorn' (MOTU)
+ *      MidiTimePiece AV multiport MIDI interface 
+ *
+ *      IOPORTS
+ *      -------
+ *      8 MIDI Ins and 8 MIDI outs
+ *      Video Sync In (BNC), Word Sync Out (BNC), 
+ *      ADAT Sync Out (DB9)
+ *      SMPTE in/out (1/4")
+ *      2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs.
+ *      Macintosh RS422 serial port
+ *      RS422 "network" port for ganging multiple MTP's
+ *      PC Parallel Port ( which this driver currently uses )
+ *
+ *      MISC FEATURES
+ *      -------------
+ *      Hardware MIDI routing, merging, and filtering   
+ *      MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources
+ *      128 'scene' memories, recallable from MIDI program change
+ *
+ *
+ * ChangeLog
+ * Jun 11 2001	Takashi Iwai <tiwai@suse.de>
+ *      - Recoded & debugged
+ *      - Added timer interrupt for midi outputs
+ *      - snd_hwports is between 1 and 8, which specifies the number of hardware ports.
+ *        The three global ports, computer, adat and broadcast ports, are created
+ *        always after h/w and remote ports.
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <linux/delay.h>
+
+/*
+ *      globals
+ */
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Michael T. Mayers");
+MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{MOTU,MidiTimePiece AV multiport MIDI}}");
+
+// io resources
+#define MTPAV_IOBASE		0x378
+#define MTPAV_IRQ		7
+#define MTPAV_MAX_PORTS		8
+
+static int snd_index = SNDRV_DEFAULT_IDX1;
+static char *snd_id = SNDRV_DEFAULT_STR1;
+static long snd_port = MTPAV_IOBASE;	/* 0x378, 0x278 */
+static int snd_irq = MTPAV_IRQ;		/* 7, 5 */
+static int snd_hwports = MTPAV_MAX_PORTS;	/* use hardware ports 1-8 */
+
+MODULE_PARM(snd_index, "i");
+MODULE_PARM_DESC(snd_index, "Index value for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "s");
+MODULE_PARM_DESC(snd_id, "ID string for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_port, "l");
+MODULE_PARM_DESC(snd_port, "Parallel port # for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x378},{0x278}},dialog:list");
+MODULE_PARM(snd_irq, "i");
+MODULE_PARM_DESC(snd_irq, "Parallel IRQ # for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_irq,  SNDRV_ENABLED ",allows:{{7},{5}},dialog:list");
+MODULE_PARM(snd_hwports, "i");
+MODULE_PARM_DESC(snd_hwports, "Hardware ports # for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_hwports, SNDRV_ENABLED ",allows:{{1,8}},dialog:list");
+
+/*
+ *      defines
+ */
+//#define USE_FAKE_MTP //       dont actually read/write to MTP device (for debugging without an actual unit) (does not work yet)
+
+// parallel port usage masks
+#define SIGS_BYTE 0x08
+#define SIGS_RFD 0x80
+#define SIGS_IRQ 0x40
+#define SIGS_IN0 0x10
+#define SIGS_IN1 0x20
+
+#define SIGC_WRITE 0x04
+#define SIGC_READ 0x08
+#define SIGC_INTEN 0x10
+
+#define DREG 0
+#define SREG 1
+#define CREG 2
+
+//
+#define MTPAV_MODE_INPUT_OPENED		0x01
+#define MTPAV_MODE_OUTPUT_OPENED	0x02
+#define MTPAV_MODE_INPUT_TRIGGERED	0x04
+#define MTPAV_MODE_OUTPUT_TRIGGERED	0x08
+
+#define NUMPORTS (0x12+1)
+
+
+/*
+ */
+
+typedef struct mtpav_port {
+	u8 number;
+	u8 hwport;
+	u8 mode;
+	snd_rawmidi_substream_t *input;
+	snd_rawmidi_substream_t *output;
+} mtpav_port_t;
+
+typedef struct mtpav {
+	snd_card_t *card;
+	unsigned long port;
+	struct resource *res_port;
+	int irq;			/* interrupt (for inputs) */
+	spinlock_t spinlock;
+	int share_irq;			/* number of accesses to input interrupts */
+	int istimer;			/* number of accesses to timer interrupts */
+	struct timer_list timer;	/* timer interrupts for outputs */
+	snd_rawmidi_t *rmidi;
+	int num_ports;		/* number of hw ports (1-8) */
+	mtpav_port_t ports[NUMPORTS];	/* all ports including computer, adat and bc */
+
+	u32 inmidiport;		/* selected input midi port */
+	u32 inmidistate;	/* during midi command 0xf5 */
+
+	u32 outmidihwport;	/* selected output midi hw port */
+} mtpav_t;
+
+
+/*
+ * global instance
+ * hey, we handle at most only one card..
+ */
+static mtpav_t *mtp_card;
+
+/*
+ * possible hardware ports (selected by 0xf5 port message)
+ *      0x00		all ports
+ *      0x01 .. 0x08    this MTP's ports 1..8
+ *      0x09 .. 0x10    networked MTP's ports (9..16)
+ *      0x11            networked MTP's computer port
+ *      0x63            to ADAT
+ *
+ * mappig:
+ *  subdevice 0 - (X-1)    ports
+ *            X - (2*X-1)  networked ports
+ *            X            computer
+ *            X+1          ADAT
+ *            X+2          all ports
+ *
+ *  where X = chip->num_ports
+ */
+
+#define MTPAV_PIDX_COMPUTER	0
+#define MTPAV_PIDX_ADAT		1
+#define MTPAV_PIDX_BROADCAST	2
+
+
+static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev)
+{
+	if (subdev < 0)
+		return 0x01; /* invalid - use port 0 as default */
+	else if (subdev < chip->num_ports)
+		return subdev + 1; /* single mtp port */
+	else if (subdev < chip->num_ports * 2)
+		return subdev - chip->num_ports + 0x09; /* remote port */
+	else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER)
+		return 0x11; /* computer port */
+	else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT)
+		return 0x63;		/* ADAT */
+	return 0; /* all ports */
+}
+
+static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport)
+{
+	int port;
+	if (hwport <= 0x00) /* all ports */
+		return chip->num_ports + MTPAV_PIDX_BROADCAST;
+	else if (hwport <= 0x08) { /* single port */
+		port = hwport - 1;
+		if (port >= chip->num_ports)
+			port = 0;
+		return port;
+	} else if (hwport <= 0x10) { /* remote port */
+		port = hwport - 0x09 + chip->num_ports;
+		if (port >= chip->num_ports * 2)
+			port = chip->num_ports;
+		return port;
+	} else if (hwport == 0x11)  /* computer port */
+		return chip->num_ports + MTPAV_PIDX_COMPUTER;
+	else  /* ADAT */
+		return chip->num_ports + MTPAV_PIDX_ADAT;
+}
+
+
+/*
+ */
+
+static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg)
+{
+	u8 rval = 0;
+
+	if (reg == SREG) {
+		rval = inb(chip->port + SREG);
+		rval = (rval & 0xf8);
+	} else if (reg == CREG) {
+		rval = inb(chip->port + CREG);
+		rval = (rval & 0x1c);
+	}
+
+	return rval;
+}
+
+/*
+ */
+
+static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val)
+{
+	if (reg == DREG) {
+		outb(val, chip->port + DREG);
+	} else if (reg == CREG) {
+		outb(val, chip->port + CREG);
+	}
+}
+
+/*
+ */
+
+static void snd_mtpav_wait_rfdhi(mtpav_t *chip)
+{
+	int counts = 10000;
+	u8 sbyte;
+
+	sbyte = snd_mtpav_getreg(chip, SREG);
+	while (!(sbyte & SIGS_RFD) && counts--) {
+		sbyte = snd_mtpav_getreg(chip, SREG);
+		udelay(10);
+	}
+}
+
+static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte)
+{
+	u8 tcbyt;
+	u8 clrwrite;
+	u8 setwrite;
+
+	snd_mtpav_wait_rfdhi(chip);
+
+	/////////////////
+
+	tcbyt = snd_mtpav_getreg(chip, CREG);
+	clrwrite = tcbyt & (SIGC_WRITE ^ 0xff);
+	setwrite = tcbyt | SIGC_WRITE;
+
+	snd_mtpav_mputreg(chip, DREG, byte);
+	snd_mtpav_mputreg(chip, CREG, clrwrite);	// clear write bit
+
+	snd_mtpav_mputreg(chip, CREG, setwrite);	// set write bit
+
+}
+
+
+/*
+ */
+
+/* call this with spin lock held */
+static void snd_mtpav_output_port_write(mtpav_port_t *port,
+					snd_rawmidi_substream_t *substream)
+{
+	u8 outbyte;
+
+	// send port change command if necessary
+
+	if (port->hwport != mtp_card->outmidihwport) {
+		mtp_card->outmidihwport = port->hwport;
+
+		snd_mtpav_send_byte(mtp_card, 0xf5);
+		snd_mtpav_send_byte(mtp_card, port->hwport);
+		//snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport);
+
+	}
+
+	// send data
+
+	while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1)
+		snd_mtpav_send_byte(mtp_card, outbyte);
+}
+
+static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream)
+{
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+	unsigned long flags;
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	snd_mtpav_output_port_write(port, substream);
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+}
+
+
+/*
+ *      mtpav control
+ */
+
+static void snd_mtpav_portscan(mtpav_t *chip)	// put mtp into smart routing mode
+{
+	u8 port;
+
+	for (port = 0; port < 8; port++) {
+		snd_mtpav_send_byte(chip, 0xf5);
+		snd_mtpav_send_byte(chip, port);
+		snd_mtpav_send_byte(chip, 0xfe);
+	}
+}
+
+/*
+ */
+
+static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	//printk("mtpav port: %d opened\n", (int) substream->number);
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	port->mode |= MTPAV_MODE_INPUT_OPENED;
+	port->input = substream;
+	if (mtp_card->share_irq++ == 0)
+		snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE));	// enable pport interrupts
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+}
+
+/*
+ */
+
+static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	//printk("mtpav port: %d closed\n", (int) port);
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+
+	port->mode &= (~MTPAV_MODE_INPUT_OPENED);
+	port->input = NULL;
+	if (--mtp_card->share_irq == 0)
+		snd_mtpav_mputreg(mtp_card, CREG, 0);	// disable pport interrupts
+
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+}
+
+/*
+ */
+
+static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	if (up)
+		port->mode |= MTPAV_MODE_INPUT_TRIGGERED;
+	else
+		port->mode &= ~MTPAV_MODE_INPUT_TRIGGERED;
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+
+}
+
+
+/*
+ * timer interrupt for outputs
+ */
+
+static void snd_mtpav_output_timer(unsigned long data)
+{
+	mtpav_t *chip = snd_magic_cast(mtpav_t, (void *)data, return);
+	int p;
+
+	spin_lock(&chip->spinlock);
+	/* reprogram timer */
+	chip->timer.expires = 1 + jiffies;
+	add_timer(&chip->timer);
+	/* process each port */
+	for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
+		mtpav_port_t *port = &mtp_card->ports[p];
+		if ((port->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && port->output)
+			snd_mtpav_output_port_write(port, port->output);
+	}
+	spin_unlock(&chip->spinlock);
+}
+
+static void snd_mtpav_add_output_timer(mtpav_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	chip->timer.function = snd_mtpav_output_timer;
+	chip->timer.data = (unsigned long) mtp_card;
+	chip->timer.expires = 1 + jiffies;
+	add_timer(&chip->timer);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static void snd_mtpav_remove_output_timer(mtpav_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	del_timer(&chip->timer);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+/*
+ */
+
+static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	port->mode |= MTPAV_MODE_OUTPUT_OPENED;
+	port->output = substream;
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+};
+
+/*
+ */
+
+static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	port->mode &= (~MTPAV_MODE_OUTPUT_OPENED);
+	port->output = NULL;
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+};
+
+/*
+ */
+
+static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	if (up) {
+		if (! (port->mode  & MTPAV_MODE_OUTPUT_TRIGGERED)) {
+			if (mtp_card->istimer++ == 0)
+				snd_mtpav_add_output_timer(mtp_card);
+			port->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
+		}
+	} else {
+		port->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
+		if (--mtp_card->istimer == 0)
+			snd_mtpav_remove_output_timer(mtp_card);
+	}
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+
+	if (up)
+		snd_mtpav_output_write(substream);
+}
+
+/*
+ * midi interrupt for inputs
+ */
+
+static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte)
+{
+	mtpav_port_t *port;
+
+	if (mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST)
+		return;
+
+	port = &mcrd->ports[mcrd->inmidiport];
+	if (port->mode & MTPAV_MODE_INPUT_TRIGGERED)
+		snd_rawmidi_receive(port->input, &inbyte, 1);
+}
+
+static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte)
+{
+	snd_assert(mcrd, return);
+
+	if (inbyte >= 0xf8) {
+		/* real-time midi code */
+		snd_mtpav_inmidi_process(mcrd, inbyte);
+		return;
+	}
+
+	if (mcrd->inmidistate == 0) {	// awaiting command
+		if (inbyte == 0xf5)	// MTP port #
+			mcrd->inmidistate = 1;
+		else
+			snd_mtpav_inmidi_process(mcrd, inbyte);
+	} else if (mcrd->inmidistate) {
+		mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte);
+		mcrd->inmidistate = 0;
+	}
+}
+
+static void snd_mtpav_read_bytes(mtpav_t * mcrd)
+{
+	u8 clrread, setread;
+	u8 mtp_read_byte;
+	u8 sr, cbyt;
+	int i;
+
+	u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
+
+	//printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt);
+
+	if (!(sbyt & SIGS_BYTE))
+		return;
+
+	cbyt = snd_mtpav_getreg(mcrd, CREG);
+	clrread = cbyt & (SIGC_READ ^ 0xff);
+	setread = cbyt | SIGC_READ;
+
+	do {
+
+		mtp_read_byte = 0;
+		for (i = 0; i < 4; i++) {
+			snd_mtpav_mputreg(mcrd, CREG, setread);
+			sr = snd_mtpav_getreg(mcrd, SREG);
+			snd_mtpav_mputreg(mcrd, CREG, clrread);
+
+			sr &= SIGS_IN0 | SIGS_IN1;
+			sr >>= 4;
+			mtp_read_byte |= sr << (i * 2);
+		}
+
+		snd_mtpav_inmidi_h(mcrd, mtp_read_byte);
+
+		sbyt = snd_mtpav_getreg(mcrd, SREG);
+
+	} while (sbyt & SIGS_BYTE);
+}
+
+static void snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs)
+{
+	mtpav_t *mcard = snd_magic_cast(mtpav_t, dev_id, return);
+
+	//printk("irqh()\n");
+	spin_lock(&mcard->spinlock);
+	snd_mtpav_read_bytes(mcard);
+	spin_unlock(&mcard->spinlock);
+}
+
+/*
+ * get ISA resources
+ */
+static int snd_mtpav_get_ISA(mtpav_t * mcard)
+{
+	if ((mcard->res_port = request_region(snd_port, 3, "MotuMTPAV MIDI")) == NULL) {
+		snd_printk("MTVAP port 0x%lx is busy\n", snd_port);
+		return -EBUSY;
+	}
+	mcard->port = snd_port;
+	if (request_irq(snd_irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) {
+		snd_printk("MTVAP IRQ %d busy\n", snd_irq);
+		return -EBUSY;
+	}
+	mcard->irq = snd_irq;
+	return 0;
+}
+
+
+/*
+ */
+
+static snd_rawmidi_ops_t snd_mtpav_output = {
+	open:		snd_mtpav_output_open,
+	close:		snd_mtpav_output_close,
+	trigger:	snd_mtpav_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_mtpav_input = {
+	open:		snd_mtpav_input_open,
+	close:		snd_mtpav_input_close,
+	trigger:	snd_mtpav_input_trigger,
+};
+
+
+/*
+ * get RAWMIDI resources
+ */
+
+static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream)
+{
+	if (substream->number >= 0 && substream->number < chip->num_ports)
+		sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1);
+	else if (substream->number >= 8 && substream->number < chip->num_ports * 2)
+		sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1);
+	else if (substream->number == chip->num_ports * 2)
+		strcpy(substream->name, "MTP computer");
+	else if (substream->number == chip->num_ports * 2 + 1)
+		strcpy(substream->name, "MTP ADAT");
+	else
+		strcpy(substream->name, "MTP broadcast");
+}
+
+static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard)
+{
+	int rval = 0;
+	snd_rawmidi_t *rawmidi;
+	snd_rawmidi_substream_t *substream;
+	struct list_head *list;
+
+	//printk("entering snd_mtpav_get_RAWMIDI\n");
+
+	if (snd_hwports < 1)
+		mcard->num_ports = 1;
+	else if (snd_hwports > 8)
+		mcard->num_ports = 8;
+	else
+		mcard->num_ports = snd_hwports;
+
+	if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
+				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+				    &mcard->rmidi)) < 0)
+		return rval;
+	rawmidi = mcard->rmidi;
+
+	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+		substream = list_entry(list, snd_rawmidi_substream_t, list);
+		snd_mtpav_set_name(mcard, substream);
+		substream->ops = &snd_mtpav_input;
+	}
+	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+		substream = list_entry(list, snd_rawmidi_substream_t, list);
+		snd_mtpav_set_name(mcard, substream);
+		substream->ops = &snd_mtpav_output;
+		mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number);
+	}
+	rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
+			       SNDRV_RAWMIDI_INFO_DUPLEX;
+	sprintf(rawmidi->name, "MTP AV MIDI");
+	//printk("exiting snd_mtpav_get_RAWMIDI() \n");
+	return 0;
+}
+
+/*
+ */
+
+static mtpav_t *new_mtpav(void)
+{
+	mtpav_t *ncrd = (mtpav_t *) snd_kcalloc(sizeof(mtpav_t), GFP_KERNEL);
+	if (ncrd != NULL) {
+		spin_lock_init(&ncrd->spinlock);
+
+		ncrd->card = NULL;
+		ncrd->irq = -1;
+		ncrd->share_irq = 0;
+
+		ncrd->inmidiport = 0xffffffff;
+		ncrd->inmidistate = 0;
+		ncrd->outmidihwport = 0xffffffff;
+	}
+	return ncrd;
+}
+
+/*
+ */
+
+static void free_mtpav(mtpav_t * crd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&crd->spinlock, flags);
+	if (crd->istimer > 0)
+		snd_mtpav_remove_output_timer(crd);
+	spin_unlock_irqrestore(&crd->spinlock, flags);
+	if (crd->irq >= 0)
+		free_irq(crd->irq, (void *)crd);
+	if (crd->res_port) {
+		release_resource(crd->res_port);
+		kfree_nocheck(crd->res_port);
+	}
+	if (crd != NULL)
+		kfree(crd);
+}
+
+/*
+ */
+
+static int __init alsa_card_mtpav_init(void)
+{
+	int err = 0;
+	char longname_buffer[80];
+
+	mtp_card = new_mtpav();
+	if (mtp_card == NULL)
+		return -ENOMEM;
+
+	mtp_card->card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0);
+	if (mtp_card->card == NULL) {
+		free_mtpav(mtp_card);
+		return -ENOMEM;
+	}
+
+	err = snd_mtpav_get_ISA(mtp_card);
+	//printk("snd_mtpav_get_ISA returned: %d\n", err);
+	if (err < 0)
+		goto __error;
+
+	strcpy(mtp_card->card->driver, "MTPAV");
+	strcpy(mtp_card->card->shortname, "MTPAV on parallel port");
+	memset(longname_buffer, 0, sizeof(longname_buffer));
+	sprintf(longname_buffer, "MTPAV on parallel port at");
+
+	err = snd_mtpav_get_RAWMIDI(mtp_card);
+	//snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err);
+	if (err < 0)
+		goto __error;
+
+	err = snd_card_register(mtp_card->card);	// dont snd_card_register until AFTER all cards reources done!
+
+	//printk("snd_card_register returned %d\n", err);
+	if (err < 0)
+		goto __error;
+
+
+	snd_mtpav_portscan(mtp_card);
+
+	printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", snd_irq, snd_port);
+
+	return 0;
+
+      __error:
+	snd_card_free(mtp_card->card);
+	free_mtpav(mtp_card);
+	return err;
+}
+
+/*
+ */
+
+static void __exit alsa_card_mtpav_exit(void)
+{
+	if (mtp_card == NULL)
+		return;
+	if (mtp_card->card)
+		snd_card_free(mtp_card->card);
+	free_mtpav(mtp_card);
+}
+
+/*
+ */
+
+module_init(alsa_card_mtpav_init)
+module_exit(alsa_card_mtpav_exit)
+
+#ifndef MODULE
+
+/* format is: snd-mtpav=snd_enable,snd_index,snd_id,
+			snd_port,snd_irq,snd_hwports */
+
+static int __init alsa_card_mtpav_setup(char *str)
+{
+        int __attribute__ ((__unused__)) enable = 1;
+
+	(void)(get_option(&str,&enable) == 2 &&
+	       get_option(&str,&snd_index) == 2 &&
+	       get_id(&str,&snd_id) == 2 &&
+	       get_option(&str,(int *)&snd_port) == 2 &&
+	       get_option(&str,&snd_irq) == 2 &&
+	       get_option(&str,&snd_hwports) == 2);
+	return 1;
+}
+
+__setup("snd-mtpav=", alsa_card_mtpav_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/drivers/opl3/Makefile linux-2.4.19-pre5-mjc/sound/drivers/opl3/Makefile
--- linux/sound/drivers/opl3/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,76 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _opl3.o
+
+list-multi   := snd-opl3-lib.o snd-opl3-synth.o
+
+export-objs  := opl3_lib.o
+
+snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
+snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+snd-opl3-synth-objs += opl3_oss.o
+endif
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS100) += snd-opl3-lib.o
+obj-$(CONFIG_SND_AZT2320) += snd-opl3-lib.o
+obj-$(CONFIG_SND_DT0197H) += snd-opl3-lib.o
+obj-$(CONFIG_SND_ES18XX) += snd-opl3-lib.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-lib.o
+obj-$(CONFIG_SND_AD1816A) += snd-opl3-lib.o
+obj-$(CONFIG_SND_CS4232) += snd-opl3-lib.o
+obj-$(CONFIG_SND_CS4236) += snd-opl3-lib.o
+obj-$(CONFIG_SND_ES1688) += snd-opl3-lib.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-lib.o
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-lib.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-lib.o
+obj-$(CONFIG_SND_OPTI93X) += snd-opl3-lib.o
+obj-$(CONFIG_SND_SB8) += snd-opl3-lib.o
+obj-$(CONFIG_SND_SB16) += snd-opl3-lib.o
+obj-$(CONFIG_SND_SBAWE) += snd-opl3-lib.o
+obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-lib.o
+obj-$(CONFIG_SND_ALS4000) += snd-opl3-lib.o
+obj-$(CONFIG_SND_CMIPCI) += snd-opl3-lib.o
+obj-$(CONFIG_SND_CS4281) += snd-opl3-lib.o
+obj-$(CONFIG_SND_ES1938) += snd-opl3-lib.o
+obj-$(CONFIG_SND_FM801) += snd-opl3-lib.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-lib.o
+obj-$(CONFIG_SND_YMFPCI) += snd-opl3-lib.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_ALS100) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_AZT2320) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_DT0197H) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_ES18XX) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_AD1816A) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_CS4232) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_CS4236) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_ES1688) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_OPTI93X) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_SB8) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_SB16) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_SBAWE) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_ALS4000) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_CMIPCI) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_CS4281) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_ES1938) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_FM801) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-synth.o
+  obj-$(CONFIG_SND_YMFPCI) += snd-opl3-synth.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-opl3-lib.o: $(snd-opl3-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-lib-objs)
+
+snd-opl3-synth.o: $(snd-opl3-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-synth-objs)
diff -Nru linux/sound/drivers/opl3/opl3_drums.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_drums.c
--- linux/sound/drivers/opl3/opl3_drums.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_drums.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,224 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *   OPL2/OPL3/OPL4 FM routines for internal percussion channels
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include "opl3_voice.h"
+
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+
+static char snd_opl3_drum_table[47] =
+{
+	OPL3_BASSDRUM_ON,  OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,	/* 35 - 37 */
+	OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON,     OPL3_SNAREDRUM_ON, /* 38 - 40 */
+	OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,     OPL3_BASSDRUM_ON,	/* 41 - 43 */
+	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,	/* 44 - 46 */
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,	/* 47 - 49 */
+
+	OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 50 - 52 */
+	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 53 - 55 */
+	OPL3_HIHAT_ON,     OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,	/* 56 - 58 */
+	OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 59 - 61 */
+	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 62 - 64 */
+
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 65 - 67 */
+	OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,     OPL3_HIHAT_ON,	/* 68 - 70 */
+	OPL3_HIHAT_ON,     OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,	/* 71 - 73 */
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 74 - 76 */
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 77 - 79 */
+	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON			/* 80 - 81 */
+};
+
+typedef struct snd_opl3_drum_voice {
+	int voice;
+	int op;
+	unsigned char am_vib;
+	unsigned char ksl_level;
+	unsigned char attack_decay;
+	unsigned char sustain_release;
+	unsigned char feedback_connection;
+	unsigned char wave_select;
+} snd_opl3_drum_voice_t;
+
+typedef struct snd_opl3_drum_note {
+	int voice;
+	unsigned char fnum;
+	unsigned char octave_f;
+	unsigned char feedback_connection;
+} snd_opl3_drum_note_t;
+
+static snd_opl3_drum_voice_t bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
+static snd_opl3_drum_voice_t bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
+static snd_opl3_drum_note_t bass_note = {6, 0x90, 0x09};
+
+static snd_opl3_drum_voice_t hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
+
+static snd_opl3_drum_voice_t snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
+static snd_opl3_drum_note_t snare_note = {7, 0xf4, 0x0d};
+
+static snd_opl3_drum_voice_t tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
+static snd_opl3_drum_note_t tomtom_note = {8, 0xf4, 0x09};
+
+static snd_opl3_drum_voice_t cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
+
+/*
+ * set drum voice characteristics
+ */
+void snd_opl3_drum_voice_set(opl3_t *opl3, snd_opl3_drum_voice_t *data)
+{
+	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
+	unsigned char voice_offset = data->voice;
+	unsigned short opl3_reg;
+	
+	/* Set OPL3 AM_VIB register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset);
+	opl3->command(opl3, opl3_reg, data->am_vib);
+
+	/* Set OPL3 KSL_LEVEL register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
+	opl3->command(opl3, opl3_reg, data->ksl_level);
+
+	/* Set OPL3 ATTACK_DECAY register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset);
+	opl3->command(opl3, opl3_reg, data->attack_decay);
+
+	/* Set OPL3 SUSTAIN_RELEASE register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
+	opl3->command(opl3, opl3_reg, data->sustain_release);
+
+	/* Set OPL3 FEEDBACK_CONNECTION register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, data->feedback_connection);
+
+	/* Select waveform */
+	opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset);
+	opl3->command(opl3, opl3_reg, data->wave_select);
+}
+
+/*
+ * Set drum voice pitch
+ */
+void snd_opl3_drum_note_set(opl3_t *opl3, snd_opl3_drum_note_t *data)
+{
+	unsigned char voice_offset = data->voice;
+	unsigned short opl3_reg;
+
+	/* Set OPL3 FNUM_LOW register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, data->fnum);
+
+	/* Set OPL3 KEYON_BLOCK register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, data->octave_f);
+}
+
+/*
+ * Set drum voice volume and position
+ */
+void snd_opl3_drum_vol_set(opl3_t *opl3, snd_opl3_drum_voice_t *data, int vel,
+			   snd_midi_channel_t *chan)
+{
+	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
+	unsigned char voice_offset = data->voice;
+	unsigned char reg_val;
+	unsigned short opl3_reg;
+
+	/* Set OPL3 KSL_LEVEL register */ 
+	reg_val = data->ksl_level;
+	snd_opl3_calc_volume(&reg_val, vel, chan);
+	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Set OPL3 FEEDBACK_CONNECTION register */ 
+	/* Set output voice connection */
+	reg_val = data->feedback_connection | OPL3_STEREO_BITS;
+	if (chan->gm_pan < 43)
+		reg_val &= ~OPL3_VOICE_TO_RIGHT;
+	if (chan->gm_pan > 85)
+		reg_val &= ~OPL3_VOICE_TO_LEFT;
+	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+}
+
+/*
+ * Loads drum voices at init time
+ */
+void snd_opl3_load_drums(opl3_t *opl3)
+{
+	snd_opl3_drum_voice_set(opl3, &bass_op0);
+	snd_opl3_drum_voice_set(opl3, &bass_op1);
+	snd_opl3_drum_note_set(opl3, &bass_note);
+
+	snd_opl3_drum_voice_set(opl3, &hihat);
+
+	snd_opl3_drum_voice_set(opl3, &snare);
+	snd_opl3_drum_note_set(opl3, &snare_note);
+
+	snd_opl3_drum_voice_set(opl3, &tomtom);
+	snd_opl3_drum_note_set(opl3, &tomtom_note);
+
+	snd_opl3_drum_voice_set(opl3, &cymbal);
+}
+
+/*
+ * Switch drum voice on or off
+ */
+void snd_opl3_drum_switch(opl3_t *opl3, int note, int vel, int on_off,
+			  snd_midi_channel_t *chan)
+{
+	unsigned char drum_mask;
+	snd_opl3_drum_voice_t *drum_voice;
+
+	if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
+		return;
+
+	if ((note < 35) || (note > 81))
+		return;
+	drum_mask = snd_opl3_drum_table[note - 35];
+
+	if (on_off) {
+		switch (drum_mask) {
+		case OPL3_BASSDRUM_ON:
+			drum_voice = &bass_op1;
+			break;
+		case OPL3_HIHAT_ON:
+			drum_voice = &hihat;
+			break;
+		case OPL3_SNAREDRUM_ON:
+			drum_voice = &snare;
+			break;
+		case OPL3_TOMTOM_ON:
+			drum_voice = &tomtom;
+			break;
+		case OPL3_CYMBAL_ON:
+			drum_voice = &cymbal;
+			break;
+		default:
+			drum_voice = &tomtom;
+		}
+
+		snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan);
+		opl3->drum_reg |= drum_mask;
+	} else {
+		opl3->drum_reg &= ~drum_mask;
+	}
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
+			 opl3->drum_reg);
+}
diff -Nru linux/sound/drivers/opl3/opl3_lib.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_lib.c
--- linux/sound/drivers/opl3/opl3_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,560 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                   Hannu Savolainen 1993-1996,
+ *                   Rob Hooft
+ *                   
+ *  Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)
+ *
+ *  Most if code is ported from OSS/Lite.
+ *
+ *   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
+ *
+ */
+
+#include <sound/opl3.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/minors.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Hannu Savolainen 1993-1996, Rob Hooft");
+MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)");
+MODULE_LICENSE("GPL");
+
+#define chip_t opl3_t
+
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+
+void snd_opl2_command(opl3_t * opl3, unsigned short cmd, unsigned char val)
+{
+	unsigned long flags;
+	unsigned long port;
+
+	/*
+	 * The original 2-OP synth requires a quite long delay
+	 * after writing to a register.
+	 */
+
+	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
+
+	spin_lock_irqsave(&opl3->reg_lock, flags);
+
+	outb((unsigned char) cmd, port);
+	udelay(10);
+
+	outb((unsigned char) val, port + 1);
+	udelay(30);
+
+	spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+void snd_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val)
+{
+	unsigned long flags;
+	unsigned long port;
+
+	/*
+	 * The OPL-3 survives with just two INBs
+	 * after writing to a register.
+	 */
+
+	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
+
+	spin_lock_irqsave(&opl3->reg_lock, flags);
+
+	outb((unsigned char) cmd, port);
+	inb(opl3->l_port);
+	inb(opl3->l_port);
+
+	outb((unsigned char) val, port + 1);
+	inb(opl3->l_port);
+	inb(opl3->l_port);
+
+	spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+void snd_opl3_cs4281_command(opl3_t * opl3, unsigned short cmd, unsigned char val)
+{
+	unsigned long flags;
+	unsigned long port;
+
+	/*
+	 * CS4281 requires a special access to I/O registers
+	 */
+
+	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
+
+	spin_lock_irqsave(&opl3->reg_lock, flags);
+
+	writel((unsigned int)cmd, port << 2);
+
+	writel((unsigned int)val, (port + 1) << 2);
+
+	spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+static int snd_opl3_detect(opl3_t * opl3)
+{
+	/*
+	 * This function returns 1 if the FM chip is present at the given I/O port
+	 * The detection algorithm plays with the timer built in the FM chip and
+	 * looks for a change in the status register.
+	 *
+	 * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+	 * boards.
+	 *
+	 * Note2! The chip is initialized if detected.
+	 */
+
+	unsigned char stat1, stat2, signature;
+
+	/* Reset timers 1 and 2 */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK);
+	/* Reset the IRQ of the FM chip */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
+	signature = stat1 = inb(opl3->l_port);	/* Status register */
+	if ((stat1 & 0xe0) != 0x00) {	/* Should be 0x00 */
+		snd_printd("OPL3: stat1 = 0x%x\n", stat1);
+		return -ENODEV;
+	}
+	/* Set timer1 to 0xff */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff);
+	/* Unmask and start timer 1 */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START);
+	/* Now we have to delay at least 80us */
+	udelay(200);
+	/* Read status after timers have expired */
+	stat2 = inb(opl3->l_port);
+	/* Stop the timers */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK);
+	/* Reset the IRQ of the FM chip */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
+	if ((stat2 & 0xe0) != 0xc0) {	/* There is no YM3812 */
+		snd_printd("OPL3: stat2 = 0x%x\n", stat2);
+		return -ENODEV;
+	}
+
+	/* If the toplevel code knows exactly the type of chip, don't try
+	   to detect it. */
+	if (opl3->hardware != OPL3_HW_AUTO)
+		return 0;
+
+	/* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */
+	if (signature == 0x06) {	/* OPL2 */
+		opl3->hardware = OPL3_HW_OPL2;
+	} else {
+		/*
+		 * Detect availability of OPL4 (_experimental_). Works probably
+		 * only after a cold boot. In addition the OPL4 port
+		 * of the chip may not be connected to the PC bus at all.
+		 */
+		snd_assert(opl3->r_port != 0, return -ENODEV);
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00);
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE);
+		if (inb(opl3->l_port) == 0x02) {	/* Have a OPL4 */
+			opl3->hardware = OPL3_HW_OPL4;
+		} else {
+			opl3->hardware = OPL3_HW_OPL3;
+		}
+	}
+	return 0;
+}
+
+/*
+ *  AdLib timers
+ */
+
+/*
+ *  Timer 1 - 80us
+ */
+
+static int snd_opl3_timer1_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	opl3_t *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	ticks = timer->sticks;
+	tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks);	/* timer 1 count */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* enable timer 1 IRQ */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+static int snd_opl3_timer1_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	opl3_t *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+/*
+ *  Timer 2 - 320us
+ */
+
+static int snd_opl3_timer2_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	opl3_t *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	ticks = timer->sticks;
+	tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks);	/* timer 1 count */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* enable timer 1 IRQ */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+static int snd_opl3_timer2_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	opl3_t *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+static struct _snd_timer_hardware snd_opl3_timer1 =
+{
+	flags:		SNDRV_TIMER_HW_STOP,
+	resolution:	80000,
+	ticks:		256,
+	start:		snd_opl3_timer1_start,
+	stop:		snd_opl3_timer1_stop,
+};
+
+static struct _snd_timer_hardware snd_opl3_timer2 =
+{
+	flags:		SNDRV_TIMER_HW_STOP,
+	resolution:	320000,
+	ticks:		256,
+	start:		snd_opl3_timer2_start,
+	stop:		snd_opl3_timer2_stop,
+};
+
+static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no)
+{
+	snd_timer_t *timer = NULL;
+	snd_timer_id_t tid;
+	int err;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = opl3->card->number;
+	tid.device = timer_no;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) {
+		strcpy(timer->name, "AdLib timer #1");
+		timer->private_data = opl3;
+		timer->hw = snd_opl3_timer1;
+	}
+	opl3->timer1 = timer;
+	return err;
+}
+
+static int snd_opl3_timer2_init(opl3_t * opl3, int timer_no)
+{
+	snd_timer_t *timer = NULL;
+	snd_timer_id_t tid;
+	int err;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = opl3->card->number;
+	tid.device = timer_no;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) {
+		strcpy(timer->name, "AdLib timer #2");
+		timer->private_data = opl3;
+		timer->hw = snd_opl3_timer2;
+	}
+	opl3->timer2 = timer;
+	return err;
+}
+
+/*
+
+ */
+
+void snd_opl3_interrupt(snd_hwdep_t * hw)
+{
+	unsigned char status;
+	opl3_t *opl3;
+	snd_timer_t *timer;
+
+	if (hw == NULL)
+		return;
+
+	opl3 = snd_magic_cast(opl3_t, hw->private_data, return);
+	status = inb(opl3->l_port);
+#if 0
+	snd_printk("AdLib IRQ status = 0x%x\n", status);
+#endif
+	if (!(status & 0x80))
+		return;
+
+	if (status & 0x40) {
+		timer = opl3->timer1;
+		snd_timer_interrupt(timer, timer->sticks);
+	}
+	if (status & 0x20) {
+		timer = opl3->timer2;
+		snd_timer_interrupt(timer, timer->sticks);
+	}
+}
+
+/*
+
+ */
+
+static int snd_opl3_free(opl3_t *opl3)
+{
+	if (opl3->res_l_port) {
+		release_resource(opl3->res_l_port);
+		kfree_nocheck(opl3->res_l_port);
+	}
+	if (opl3->res_r_port) {
+		release_resource(opl3->res_r_port);
+		kfree_nocheck(opl3->res_r_port);
+	}
+	snd_magic_kfree(opl3);
+	return 0;
+}
+
+static int snd_opl3_dev_free(snd_device_t *device)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, device->device_data, return -ENXIO);
+	return snd_opl3_free(opl3);
+}
+
+int snd_opl3_create(snd_card_t * card,
+		    unsigned long l_port,
+		    unsigned long r_port,
+		    unsigned short hardware,
+		    int integrated,
+		    opl3_t ** ropl3)
+{
+	opl3_t *opl3;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free: snd_opl3_dev_free,
+	};
+
+	*ropl3 = NULL;
+
+	opl3 = snd_magic_kcalloc(opl3_t, 0, GFP_KERNEL);
+	if (opl3 == NULL)
+		return -ENOMEM;
+
+	if (integrated)
+		goto __step1; /* ports are already reserved */
+
+	if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) {
+		snd_opl3_free(opl3);
+		return -EBUSY;
+	}
+	if (r_port != 0 &&
+	    (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) {
+		snd_opl3_free(opl3);
+		return -EBUSY;
+	}
+
+      __step1:
+
+	opl3->card = card;
+	opl3->hardware = hardware;
+	opl3->l_port = l_port;
+	opl3->r_port = r_port;
+
+	spin_lock_init(&opl3->reg_lock);
+	spin_lock_init(&opl3->timer_lock);
+	init_MUTEX(&opl3->access_mutex);
+
+	switch (opl3->hardware) {
+	/* some hardware doesn't support timers */
+	case OPL3_HW_OPL3_SV:
+	case OPL3_HW_OPL3_CS:
+	case OPL3_HW_OPL3_FM801:
+		opl3->command = &snd_opl3_command;
+		break;
+	case OPL3_HW_OPL3_CS4281:
+		opl3->command = &snd_opl3_cs4281_command;
+		break;
+	default:
+		opl3->command = &snd_opl2_command;
+		if ((err = snd_opl3_detect(opl3)) < 0) {
+			snd_opl3_free(opl3);
+			snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",
+				   opl3->l_port, opl3->r_port);
+			return err;
+		}
+		/* detect routine returns correct hardware type */
+		switch (opl3->hardware & OPL3_HW_MASK) {
+		case OPL3_HW_OPL3:
+		case OPL3_HW_OPL4:
+			opl3->command = &snd_opl3_command;
+		}
+	}
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);	/* Melodic mode */
+
+	switch (opl3->hardware & OPL3_HW_MASK) {
+	case OPL3_HW_OPL2:
+		opl3->max_voices = MAX_OPL2_VOICES;
+		break;
+	case OPL3_HW_OPL3:
+	case OPL3_HW_OPL4:
+		opl3->max_voices = MAX_OPL3_VOICES;
+		snd_assert(opl3->r_port != 0, snd_opl3_free(opl3); return -ENODEV);
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00);	/* Enter OPL2 mode */
+	}
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, opl3, &ops)) < 0) {
+		snd_opl3_free(opl3);
+		return err;
+	}
+
+	*ropl3 = opl3;
+	return 0;
+}
+
+int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev)
+{
+	int err;
+
+	if (timer1_dev >= 0)
+		if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0)
+			return err;
+	if (timer2_dev >= 0) {
+		if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) {
+			snd_device_free(opl3->card, opl3->timer1);
+			opl3->timer1 = NULL;
+			return err;
+		}
+	}
+	return 0;
+}
+
+int snd_opl3_hwdep_new(opl3_t * opl3,
+		       int device, int seq_device,
+		       snd_hwdep_t ** rhwdep)
+{
+	snd_hwdep_t *hw;
+	snd_card_t *card = opl3->card;
+	int err;
+
+	if (rhwdep)
+		*rhwdep = NULL;
+
+	/* create hardware dependent device (direct FM) */
+
+	if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) {
+		snd_device_free(card, opl3);
+		return err;
+	}
+	hw->private_data = opl3;
+#ifdef CONFIG_SND_OSSEMUL
+	if (device == 0) {
+		hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
+		sprintf(hw->oss_dev, "dmfm%i", card->number);
+	}
+#endif
+	strcpy(hw->name, hw->id);
+	switch (opl3->hardware & OPL3_HW_MASK) {
+	case OPL3_HW_OPL2:
+		strcpy(hw->name, "OPL2 FM");
+		hw->iface = SNDRV_HWDEP_IFACE_OPL2;
+		break;
+	case OPL3_HW_OPL3:
+		strcpy(hw->name, "OPL3 FM");
+		hw->iface = SNDRV_HWDEP_IFACE_OPL3;
+		break;
+	case OPL3_HW_OPL4:
+		strcpy(hw->name, "OPL4 FM");
+		hw->iface = SNDRV_HWDEP_IFACE_OPL4;
+		break;
+	}
+
+	/* operators - only ioctl */
+	hw->ops.open = snd_opl3_open;
+	hw->ops.ioctl = snd_opl3_ioctl;
+	hw->ops.release = snd_opl3_release;
+
+	opl3->seq_dev_num = seq_device;
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
+			       sizeof(opl3_t*), &opl3->seq_dev) >= 0) {
+		strcpy(opl3->seq_dev->name, hw->name);
+		*(opl3_t**)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3;
+	}
+#endif
+	if (rhwdep)
+		*rhwdep = hw;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_opl3_interrupt);
+EXPORT_SYMBOL(snd_opl3_create);
+EXPORT_SYMBOL(snd_opl3_timer_new);
+EXPORT_SYMBOL(snd_opl3_hwdep_new);
+
+/* opl3_synth.c */
+EXPORT_SYMBOL(snd_opl3_regmap);
+EXPORT_SYMBOL(snd_opl3_reset);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_opl3_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_opl3_exit(void)
+{
+}
+
+module_init(alsa_opl3_init)
+module_exit(alsa_opl3_exit)
diff -Nru linux/sound/drivers/opl3/opl3_midi.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_midi.c
--- linux/sound/drivers/opl3/opl3_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_midi.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,874 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Midi synth routines for OPL2/OPL3/OPL4 FM
+ *
+ *   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
+ *
+ */
+
+#undef DEBUG_ALLOC
+#undef DEBUG_MIDI
+
+#define __NO_VERSION__
+#include "opl3_voice.h"
+#include <sound/asoundef.h>
+
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+
+extern int use_internal_drums;
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
+ * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>)
+ */
+
+static char opl3_volume_table[128] =
+{
+	-63, -48, -40, -35, -32, -29, -27, -26,
+	-24, -23, -21, -20, -19, -18, -18, -17,
+	-16, -15, -15, -14, -13, -13, -12, -12,
+	-11, -11, -10, -10, -10, -9, -9, -8,
+	-8, -8, -7, -7, -7, -6, -6, -6,
+	-5, -5, -5, -5, -4, -4, -4, -4,
+	-3, -3, -3, -3, -2, -2, -2, -2,
+	-2, -1, -1, -1, -1, 0, 0, 0,
+	0, 0, 0, 1, 1, 1, 1, 1,
+	1, 2, 2, 2, 2, 2, 2, 2,
+	3, 3, 3, 3, 3, 3, 3, 4,
+	4, 4, 4, 4, 4, 4, 4, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	6, 6, 6, 6, 6, 6, 6, 6,
+	6, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 8, 8, 8, 8, 8
+};
+
+void snd_opl3_calc_volume(unsigned char *volbyte, int vel,
+			  snd_midi_channel_t *chan)
+{
+	int oldvol, newvol, n;
+	int volume;
+
+	volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127);
+	if (volume > 127)
+		volume = 127;
+
+	oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK);
+
+	newvol = opl3_volume_table[volume] + oldvol;
+	if (newvol > OPL3_TOTAL_LEVEL_MASK)
+		newvol = OPL3_TOTAL_LEVEL_MASK;
+	else if (newvol < 0)
+		newvol = 0;
+
+	n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK);
+
+	*volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK);
+}
+
+/*
+ * Converts the note frequency to block and fnum values for the FM chip
+ */
+static short opl3_note_table[16] =
+{
+	305, 323,	/* for pitch bending, -2 semitones */
+	343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,
+	686, 726	/* for pitch bending, +2 semitones */
+};
+
+static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
+				int note, snd_midi_channel_t *chan)
+{
+	int block = ((note / 12) & 0x07) - 1;
+	int idx = (note % 12) + 2;
+	int freq;
+
+	if (chan->midi_pitchbend) {
+		int pitchbend = chan->midi_pitchbend;
+		int segment;
+
+		if (pitchbend > 0x1FFF)
+			pitchbend = 0x1FFF;
+
+		segment = pitchbend / 0x1000;
+		freq = opl3_note_table[idx+segment];
+		freq += ((opl3_note_table[idx+segment+1] - freq) *
+			 (pitchbend % 0x1000)) / 0x1000;
+	} else {
+		freq = opl3_note_table[idx];
+	}
+
+	*fnum = (unsigned char) freq;
+	*blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) |
+		((block << 2) & OPL3_BLOCKNUM_MASK);
+}
+
+
+#ifdef DEBUG_ALLOC
+static void debug_alloc(opl3_t *opl3, char *s, int voice) {
+	int i;
+	char *str = "x.24";
+
+	printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
+	for (i = 0; i < opl3->max_voices; i++)
+		printk("%c", *(str + opl3->voices[i].state + 1));
+	printk("\n");
+}
+#endif
+
+/*
+ * Get a FM voice (channel) to play a note on.
+ */
+static int opl3_get_voice(opl3_t *opl3, int instr_4op,
+			  snd_midi_channel_t *chan) {
+	int chan_4op_1;		/* first voice for 4op instrument */
+	int chan_4op_2;		/* second voice for 4op instrument */
+
+	snd_opl3_voice_t *vp, *vp2;
+	unsigned int voice_time;
+	int i;
+
+#ifdef DEBUG_ALLOC
+	char *alloc_type[3] = { "FREE     ", "CHEAP    ", "EXPENSIVE" };
+#endif
+
+	/* This is our "allocation cost" table */
+	enum {
+		FREE = 0, CHEAP, EXPENSIVE, END
+	};
+
+	/* Keeps track of what we are finding */
+	struct best {
+		unsigned int time;
+		int voice;
+	} best[END];
+	struct best *bp;
+
+	for (i = 0; i < END; i++) {
+		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+		best[i].voice = -1;
+	}
+
+	/* Look through all the channels for the most suitable. */
+	for (i = 0; i < opl3->max_voices; i++) {
+		vp = &opl3->voices[i];
+
+		if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL)
+		  /* skip unavailable channels, allocated by
+		     drum voices or by bounded 4op voices) */
+			continue;
+
+		voice_time = vp->time;
+		bp = best;
+
+		chan_4op_1 = ((i < 3) || (i > 8 && i < 12));
+		chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15));
+		if (instr_4op) {
+			/* allocate 4op voice */
+			/* skip channels unavailable to 4op instrument */
+			if (!chan_4op_1)
+				continue;
+
+			if (vp->state)
+				/* kill one voice, CHEAP */
+				bp++;
+			/* get state of bounded 2op channel
+			   to be allocated for 4op instrument */
+			vp2 = &opl3->voices[i + 3];
+			if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {
+				/* kill two voices, EXPENSIVE */
+				bp++;
+				voice_time = (voice_time > vp->time) ?
+					voice_time : vp->time;
+			}
+		} else {
+			/* allocate 2op voice */
+			if ((chan_4op_1) || (chan_4op_2))
+				/* use bounded channels for 2op, CHEAP */
+				bp++;
+			else if (vp->state)
+				/* kill one voice on 2op channel, CHEAP */
+				bp++;
+			/* raise kill cost to EXPENSIVE for all channels */
+			if (vp->state)
+				bp++;
+		}
+		if (voice_time < bp->time) {
+			bp->time = voice_time;
+			bp->voice = i;
+		}
+	}
+
+	for (i = 0; i < END; i++) {
+		if (best[i].voice >= 0) {
+#ifdef DEBUG_ALLOC
+			printk("%s %iop allocation on voice %i\n",
+			       alloc_type[i], instr_4op ? 4 : 2,
+			       best[i].voice);
+#endif
+			return best[i].voice;
+		}
+	}
+	/* not found */
+	return -1;
+}
+
+/* ------------------------------ */
+
+/*
+ * System timer interrupt function
+ */
+void snd_opl3_timer_func(unsigned long data)
+{
+
+	opl3_t *opl3 = (opl3_t *)data;
+	int again = 0;
+	int i;
+
+	spin_lock(&opl3->sys_timer_lock);
+	for (i = 0; i < opl3->max_voices; i++) {
+		snd_opl3_voice_t *vp = &opl3->voices[i];
+		if (vp->state > 0 && vp->note_off_check) {
+			if (vp->note_off == jiffies)
+				snd_opl3_note_off(opl3, vp->note, 0, vp->chan);
+			else
+				again++;
+		}
+	}
+	if (again) {
+		opl3->tlist.expires = jiffies + 1;	/* invoke again */
+		add_timer(&opl3->tlist);
+	} else {
+		opl3->sys_timer_status = 0;
+	}
+	spin_unlock(&opl3->sys_timer_lock);
+}
+
+/*
+ * Start system timer
+ */
+void snd_opl3_start_timer(opl3_t *opl3)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+	if (! opl3->sys_timer_status) {
+		opl3->tlist.expires = jiffies + 1;
+		add_timer(&opl3->tlist);
+		opl3->sys_timer_status = 1;
+	}
+	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
+}
+
+/* ------------------------------ */
+
+
+static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
+	0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14
+};
+
+/*
+ * Start a note.
+ */
+void snd_opl3_note_on(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	opl3_t *opl3;
+	snd_seq_instr_t wanted;
+	snd_seq_kinstr_t *kinstr;
+	int instr_4op;
+
+	int voice;
+	snd_opl3_voice_t *vp, *vp2;
+	unsigned short connect_mask;
+	unsigned char connection;
+	unsigned char vol_op[4];
+
+	int extra_prg = 0;
+
+	unsigned short reg_side;
+	unsigned char op_offset;
+	unsigned char voice_offset;
+	unsigned short opl3_reg;
+	unsigned char reg_val;
+
+	int key = note;
+	unsigned char fnum, blocknum;
+	int i;
+
+	fm_instrument_t *fm;
+	unsigned long flags;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+
+#ifdef DEBUG_MIDI
+	snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",
+		   chan->number, chan->midi_program, note, vel);
+#endif
+	wanted.cluster = 0;
+	wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
+
+	/* in SYNTH mode, application takes care of voices */
+	/* in SEQ mode, drum voice numbers are notes on drum channel */
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		if (chan->drum_channel) {
+			/* percussion instruments are located in bank 128 */
+			wanted.bank = 128;
+			wanted.prg = note;
+		} else {
+			wanted.bank = chan->gm_bank_select;
+			wanted.prg = chan->midi_program;
+		}
+	} else {
+		/* Prepare for OSS mode */
+		if (chan->number >= MAX_OPL3_VOICES)
+			return;
+
+		/* OSS instruments are located in bank 127 */
+		wanted.bank = 127;
+		wanted.prg = chan->midi_program;
+	}
+
+	spin_lock_irqsave(&opl3->voice_lock, flags);
+
+	if (use_internal_drums) {
+		snd_opl3_drum_switch(opl3, note, vel, 1, chan);
+		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+		return;
+	}
+
+ __extra_prg:
+	kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0);
+	if (kinstr == NULL) {
+		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+		return;
+	}
+
+	fm = KINSTR_DATA(kinstr);
+
+	switch (fm->type) {
+	case FM_PATCH_OPL2:
+		instr_4op = 0;
+		break;
+	case FM_PATCH_OPL3:
+		if (opl3->hardware >= OPL3_HW_OPL3) {
+			instr_4op = 1;
+			break;
+		}
+	default:
+		snd_seq_instr_free_use(opl3->ilist, kinstr);
+		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+		return;
+	}
+
+#ifdef DEBUG_MIDI
+	snd_printk("  --> OPL%i instrument: %s\n",
+		   instr_4op ? 3 : 2, kinstr->name);
+#endif
+	/* in SYNTH mode, application takes care of voices */
+	/* in SEQ mode, allocate voice on free OPL3 channel */
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		voice = opl3_get_voice(opl3, instr_4op, chan);
+	} else {
+		/* remap OSS voice */
+		voice = snd_opl3_oss_map[chan->number];		
+	}
+
+	if (voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice;
+		connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice - MAX_OPL2_VOICES;
+		connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38;
+	}
+
+	/* kill voice on channel */
+	vp = &opl3->voices[voice];
+	if (vp->state > 0) {
+		opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+		reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
+		opl3->command(opl3, opl3_reg, reg_val);
+	}
+	if (instr_4op) {
+		vp2 = &opl3->voices[voice + 3];
+		if (vp->state > 0) {
+			opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +
+					       voice_offset + 3);
+			reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
+			opl3->command(opl3, opl3_reg, reg_val);
+		}
+	}
+
+	/* set connection register */
+	if (instr_4op) {
+		if ((opl3->connection_reg ^ connect_mask) & connect_mask) {
+			opl3->connection_reg |= connect_mask;
+			/* set connection bit */
+			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;
+			opl3->command(opl3, opl3_reg, opl3->connection_reg);
+		}
+	} else {
+		if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) {
+			opl3->connection_reg &= ~connect_mask;
+			/* clear connection bit */
+			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;
+			opl3->command(opl3, opl3_reg, opl3->connection_reg);
+		}
+	}
+
+#ifdef DEBUG_MIDI
+	snd_printk("  --> setting OPL3 connection: 0x%x\n",
+		   opl3->connection_reg);
+#endif
+	/*
+	 * calculate volume depending on connection
+	 * between FM operators (see include/opl3.h)
+	 */
+	for (i = 0; i < (instr_4op ? 4 : 2); i++)
+		vol_op[i] = fm->op[i].ksl_level;
+
+	connection = fm->feedback_connection[0] & 0x01;
+	if (instr_4op) {
+		connection <<= 1;
+		connection |= fm->feedback_connection[1] & 0x01;
+
+		snd_opl3_calc_volume(&vol_op[3], vel, chan);
+		switch (connection) {
+		case 0x03:
+			snd_opl3_calc_volume(&vol_op[2], vel, chan);
+			/* fallthru */
+		case 0x02:
+			snd_opl3_calc_volume(&vol_op[0], vel, chan);
+			break;
+		case 0x01:
+			snd_opl3_calc_volume(&vol_op[1], vel, chan);
+		}
+	} else {
+		snd_opl3_calc_volume(&vol_op[1], vel, chan);
+		if (connection)
+			snd_opl3_calc_volume(&vol_op[0], vel, chan);
+	}
+
+	/* Program the FM voice characteristics */
+	for (i = 0; i < (instr_4op ? 4 : 2); i++) {
+#ifdef DEBUG_MIDI
+		snd_printk("  --> programming operator %i\n", i);
+#endif
+		op_offset = snd_opl3_regmap[voice_offset][i];
+
+		/* Set OPL3 AM_VIB register of requested voice/operator */ 
+		reg_val = fm->op[i].am_vib;
+		opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Set OPL3 KSL_LEVEL register of requested voice/operator */ 
+		reg_val = vol_op[i];
+		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Set OPL3 ATTACK_DECAY register of requested voice/operator */ 
+		reg_val = fm->op[i].attack_decay;
+		opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ 
+		reg_val = fm->op[i].sustain_release;
+		opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Select waveform */
+		reg_val = fm->op[i].wave_select;
+		opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+	}
+
+	/* Set operator feedback and 2op inter-operator connection */
+	reg_val = fm->feedback_connection[0];
+	/* Set output voice connection */
+	reg_val |= OPL3_STEREO_BITS;
+	if (chan->gm_pan < 43)
+		reg_val &= ~OPL3_VOICE_TO_RIGHT;
+	if (chan->gm_pan > 85)
+		reg_val &= ~OPL3_VOICE_TO_LEFT;
+	opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	if (instr_4op) {
+		/* Set 4op inter-operator connection */
+		reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT;
+		/* Set output voice connection */
+		reg_val |= OPL3_STEREO_BITS;
+		if (chan->gm_pan < 43)
+			reg_val &= ~OPL3_VOICE_TO_RIGHT;
+		if (chan->gm_pan > 85)
+			reg_val &= ~OPL3_VOICE_TO_LEFT;
+		opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION +
+				       voice_offset + 3);
+		opl3->command(opl3, opl3_reg, reg_val);
+	}
+
+	/*
+	 * Special treatment of percussion notes for fm:
+	 * Requested pitch is really program, and pitch for
+	 * device is whatever was specified in the patch library.
+	 */
+	if (fm->fix_key)
+		note = fm->fix_key;
+	/*
+	 * use transpose if defined in patch library
+	 */
+	if (fm->trnsps)
+		note += (fm->trnsps - 64);
+
+	snd_opl3_calc_pitch(&fnum, &blocknum, note, chan);
+
+	/* Set OPL3 FNUM_LOW register of requested voice */
+	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, fnum);
+
+	opl3->voices[voice].keyon_reg = blocknum;
+
+	/* Set output sound flag */
+	blocknum |= OPL3_KEYON_BIT;
+
+#ifdef DEBUG_MIDI
+	snd_printk("  --> trigger voice %i\n", voice);
+#endif
+	/* Set OPL3 KEYON_BLOCK register of requested voice */ 
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, blocknum);
+
+	/* kill note after fixed duration (in centiseconds) */
+	if (fm->fix_dur) {
+		opl3->voices[voice].note_off = jiffies +
+			(fm->fix_dur * HZ) / 100;
+		snd_opl3_start_timer(opl3);
+		opl3->voices[voice].note_off_check = 1;
+	} else
+		opl3->voices[voice].note_off_check = 0;
+
+	/* get extra pgm, but avoid possible loops */
+	extra_prg = (extra_prg) ? 0 : fm->modes;
+
+	snd_seq_instr_free_use(opl3->ilist, kinstr);
+
+	/* do the bookkeeping */
+	vp->time = opl3->use_time++;
+	vp->note = key;
+	vp->chan = chan;
+
+	if (instr_4op) {
+		vp->state = SNDRV_OPL3_ST_ON_4OP;
+
+		vp2 = &opl3->voices[voice + 3];
+		vp2->time = opl3->use_time++;
+		vp2->note = key;
+		vp2->chan = chan;
+		vp2->state = SNDRV_OPL3_ST_NOT_AVAIL;
+	} else {
+		if (vp->state == SNDRV_OPL3_ST_ON_4OP) {
+			/* 4op killed by 2op, release bounded voice */
+			vp2 = &opl3->voices[voice + 3];
+			vp2->time = opl3->use_time++;
+			vp2->state = SNDRV_OPL3_ST_OFF;
+		}
+		vp->state = SNDRV_OPL3_ST_ON_2OP;
+	}
+
+#ifdef DEBUG_ALLOC
+	debug_alloc(opl3, "note on ", voice);
+#endif
+
+	/* allocate extra program if specified in patch library */
+	if (extra_prg) {
+		if (extra_prg > 128) {
+			wanted.bank = 128;
+			/* percussions start at 35 */
+			wanted.prg = extra_prg - 128 + 35 - 1;
+		} else {
+			wanted.bank = 0;
+			wanted.prg = extra_prg - 1;
+		}
+#ifdef DEBUG_MIDI
+		snd_printk(" *** allocating extra program\n");
+#endif
+		goto __extra_prg;
+	}
+	spin_unlock_irqrestore(&opl3->voice_lock, flags);
+}
+
+static void snd_opl3_kill_voice(opl3_t *opl3, int voice)
+{
+	unsigned short reg_side;
+	unsigned char voice_offset;
+	unsigned short opl3_reg;
+
+	snd_opl3_voice_t *vp, *vp2;
+
+	snd_assert(voice < MAX_OPL3_VOICES, return);
+
+	vp = &opl3->voices[voice];
+	if (voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice - MAX_OPL2_VOICES;
+	}
+
+	/* kill voice */
+#ifdef DEBUG_MIDI
+	snd_printk("  --> kill voice %i\n", voice);
+#endif
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	/* clear Key ON bit */
+	opl3->command(opl3, opl3_reg, vp->keyon_reg);
+
+	/* do the bookkeeping */
+	vp->time = opl3->use_time++;
+
+	if (vp->state == SNDRV_OPL3_ST_ON_4OP) {
+		vp2 = &opl3->voices[voice + 3];
+
+		vp2->time = opl3->use_time++;
+		vp2->state = SNDRV_OPL3_ST_OFF;
+	}
+	vp->state = SNDRV_OPL3_ST_OFF;
+#ifdef DEBUG_ALLOC
+	debug_alloc(opl3, "note off", voice);
+#endif
+
+}
+
+/*
+ * Release a note in response to a midi note off.
+ */
+void snd_opl3_note_off(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+  	opl3_t *opl3;
+
+	int voice;
+	snd_opl3_voice_t *vp;
+
+	unsigned long flags;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+
+#ifdef DEBUG_MIDI
+	snd_printk("Note off, ch %i, inst %i, note %i\n",
+		   chan->number, chan->midi_program, note);
+#endif
+
+	spin_lock_irqsave(&opl3->voice_lock, flags);
+
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		if (chan->drum_channel && use_internal_drums) {
+			snd_opl3_drum_switch(opl3, note, vel, 0, chan);
+			spin_unlock_irqrestore(&opl3->voice_lock, flags);
+			return;
+		}
+		/* this loop will hopefully kill all extra voices, because
+		   they are grouped by the same channel and note values */
+		for (voice = 0; voice < opl3->max_voices; voice++) {
+			vp = &opl3->voices[voice];
+			if (vp->state > 0 && vp->chan == chan && vp->note == note) {
+				snd_opl3_kill_voice(opl3, voice);
+			}
+		}
+	} else {
+		/* remap OSS voices */
+		if (chan->number < MAX_OPL3_VOICES) {
+			voice = snd_opl3_oss_map[chan->number];		
+			snd_opl3_kill_voice(opl3, voice);
+		}
+	}
+	spin_unlock_irqrestore(&opl3->voice_lock, flags);
+}
+
+/*
+ * key pressure change
+ */
+void snd_opl3_key_press(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+  	opl3_t *opl3;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+#ifdef DEBUG_MIDI
+	snd_printk("Key pressure, ch#: %i, inst#: %i\n",
+		   chan->number, chan->midi_program);
+#endif
+}
+
+/*
+ * terminate note
+ */
+void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan)
+{
+  	opl3_t *opl3;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+#ifdef DEBUG_MIDI
+	snd_printk("Terminate note, ch#: %i, inst#: %i\n",
+		   chan->number, chan->midi_program);
+#endif
+}
+
+static void snd_opl3_update_pitch(opl3_t *opl3, int voice)
+{
+	unsigned short reg_side;
+	unsigned char voice_offset;
+	unsigned short opl3_reg;
+
+	unsigned char fnum, blocknum;
+
+	snd_opl3_voice_t *vp;
+
+	snd_assert(voice < MAX_OPL3_VOICES, return);
+
+	vp = &opl3->voices[voice];
+	if (vp->chan == NULL)
+		return; /* not allocated? */
+
+	if (voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice - MAX_OPL2_VOICES;
+	}
+
+	snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan);
+
+	/* Set OPL3 FNUM_LOW register of requested voice */
+	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, fnum);
+
+	vp->keyon_reg = blocknum;
+
+	/* Set output sound flag */
+	blocknum |= OPL3_KEYON_BIT;
+
+	/* Set OPL3 KEYON_BLOCK register of requested voice */ 
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, blocknum);
+
+	vp->time = opl3->use_time++;
+}
+
+/*
+ * Update voice pitch controller
+ */
+static void snd_opl3_pitch_ctrl(opl3_t *opl3, snd_midi_channel_t *chan)
+{
+	int voice;
+	snd_opl3_voice_t *vp;
+
+	unsigned long flags;
+
+	spin_lock_irqsave(&opl3->voice_lock, flags);
+
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		for (voice = 0; voice < opl3->max_voices; voice++) {
+			vp = &opl3->voices[voice];
+			if (vp->state > 0 && vp->chan == chan) {
+				snd_opl3_update_pitch(opl3, voice);
+			}
+		}
+	} else {
+		/* remap OSS voices */
+		if (chan->number < MAX_OPL3_VOICES) {
+			voice = snd_opl3_oss_map[chan->number];		
+			snd_opl3_update_pitch(opl3, voice);
+		}
+	}
+	spin_unlock_irqrestore(&opl3->voice_lock, flags);
+}
+
+/*
+ * Deal with a controler type event.  This includes all types of
+ * control events, not just the midi controllers
+ */
+void snd_opl3_control(void *p, int type, snd_midi_channel_t *chan)
+{
+  	opl3_t *opl3;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+#ifdef DEBUG_MIDI
+	snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n",
+		   type, chan->number, chan->midi_program);
+#endif
+
+	switch (type) {
+	case MIDI_CTL_MSB_MODWHEEL:
+		if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63)
+			opl3->drum_reg |= OPL3_VIBRATO_DEPTH;
+		else 
+			opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH;
+		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
+				 opl3->drum_reg);
+		break;
+	case MIDI_CTL_E2_TREMOLO_DEPTH:
+		if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63)
+			opl3->drum_reg |= OPL3_TREMOLO_DEPTH;
+		else 
+			opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH;
+		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
+				 opl3->drum_reg);
+		break;
+	case MIDI_CTL_PITCHBEND:
+		snd_opl3_pitch_ctrl(opl3, chan);
+		break;
+	}
+}
+
+/*
+ * NRPN events
+ */
+void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan,
+		   snd_midi_channel_set_t *chset)
+{
+  	opl3_t *opl3;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+#ifdef DEBUG_MIDI
+	snd_printk("NRPN, ch#: %i, inst#: %i\n",
+		   chan->number, chan->midi_program);
+#endif
+}
+
+/*
+ * receive sysex
+ */
+void snd_opl3_sysex(void *p, unsigned char *buf, int len,
+		    int parsed, snd_midi_channel_set_t *chset)
+{
+  	opl3_t *opl3;
+
+	opl3 = snd_magic_cast(opl3_t, p, return);
+#ifdef DEBUG_MIDI
+	snd_printk("SYSEX\n");
+#endif
+}
diff -Nru linux/sound/drivers/opl3/opl3_oss.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_oss.c
--- linux/sound/drivers/opl3/opl3_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,357 @@
+/*
+ *  Interface for OSS sequencer emulation
+ *
+ *  Copyright (C) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include "opl3_voice.h"
+#include <linux/slab.h>
+
+static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure);
+static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg);
+static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg);
+static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count);
+static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg);
+
+/* */
+
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+
+/* operators */
+
+extern snd_midi_op_t opl3_ops;
+
+static snd_seq_oss_callback_t oss_callback = {
+	owner: THIS_MODULE,
+	open: snd_opl3_open_seq_oss,
+	close: snd_opl3_close_seq_oss,
+	ioctl: snd_opl3_ioctl_seq_oss,
+	load_patch: snd_opl3_load_patch_seq_oss,
+	reset: snd_opl3_reset_seq_oss,
+};
+
+static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct,
+				    void *private_data, int atomic, int hop)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL);
+
+	if (ev->type != SNDRV_SEQ_EVENT_OSS)
+		snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset);
+	return 0;
+}
+
+/* ------------------------------ */
+
+static void snd_opl3_oss_free_port(void *private_data)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return);
+
+	snd_midi_channel_free_set(opl3->oss_chset);
+}
+
+static int snd_opl3_oss_create_port(opl3_t * opl3)
+{
+	snd_seq_port_callback_t callbacks;
+	char name[32];
+	int voices, opl_ver;
+
+	voices = (opl3->hardware < OPL3_HW_OPL3) ?
+		MAX_OPL2_VOICES : MAX_OPL3_VOICES;
+	opl3->oss_chset = snd_midi_channel_alloc_set(voices);
+	if (opl3->oss_chset == NULL)
+		return -ENOMEM;
+	opl3->oss_chset->private_data = opl3;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.owner = THIS_MODULE;
+	callbacks.event_input = snd_opl3_oss_event_input;
+	callbacks.private_free = snd_opl3_oss_free_port;
+	callbacks.private_data = opl3;
+
+	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
+	sprintf(name, "OPL%i OSS Port", opl_ver);
+
+	opl3->oss_chset->client = opl3->seq_client;
+	opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
+							  SNDRV_SEQ_PORT_CAP_WRITE,
+							  SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+							  SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+							  SNDRV_SEQ_PORT_TYPE_SYNTH,
+							  name);
+	if (opl3->oss_chset->port < 0) {
+		snd_midi_channel_free_set(opl3->oss_chset);
+		return opl3->oss_chset->port;
+	}
+	return 0;
+}
+
+/* ------------------------------ */
+
+/* register OSS synth */
+void snd_opl3_init_seq_oss(opl3_t *opl3, char *name)
+{
+	snd_seq_oss_reg_t *arg;
+	snd_seq_device_t *dev;
+
+	if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS,
+			       sizeof(snd_seq_oss_reg_t), &dev) < 0)
+		return;
+
+	opl3->oss_seq_dev = dev;
+	strncpy(dev->name, name, sizeof(dev->name) - 1);
+	dev->name[sizeof(dev->name) - 1] = 0;
+	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	arg->type = SYNTH_TYPE_FM;
+	if (opl3->hardware < OPL3_HW_OPL3) {
+		arg->subtype = FM_TYPE_ADLIB;
+		arg->nvoices = MAX_OPL2_VOICES;
+	} else {
+		arg->subtype = FM_TYPE_OPL3;
+		arg->nvoices = MAX_OPL3_VOICES;
+	}
+	arg->oper = oss_callback;
+	arg->private_data = opl3;
+
+	snd_opl3_oss_create_port(opl3);
+
+	/* register to OSS synth table */
+	snd_device_register(opl3->card, dev);
+}
+
+/* unregister */
+void snd_opl3_free_seq_oss(opl3_t *opl3)
+{
+	if (opl3->oss_seq_dev) {
+		snd_device_free(opl3->card, opl3->oss_seq_dev);
+		opl3->oss_seq_dev = NULL;
+	}
+}
+
+/* ------------------------------ */
+
+/* open OSS sequencer */
+static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, closure, return -EINVAL);
+	int err;
+
+	snd_assert(arg != NULL, return -ENXIO);
+
+	if ((err = snd_opl3_synth_setup(opl3)) < 0)
+		return err;
+
+	/* fill the argument data */
+	arg->private_data = opl3;
+	arg->addr.client = opl3->oss_chset->client;
+	arg->addr.port = opl3->oss_chset->port;
+
+	if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+		return err;
+
+	opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH;
+	return 0;
+}
+
+/* close OSS sequencer */
+static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg)
+{
+	opl3_t *opl3;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL);
+
+	snd_opl3_synth_cleanup(opl3);
+
+	snd_opl3_synth_use_dec(opl3);
+	return 0;
+}
+
+/* load patch */
+
+/* offsets for SBI params */
+#define AM_VIB		0
+#define KSL_LEVEL	2
+#define ATTACK_DECAY	4
+#define SUSTAIN_RELEASE	6
+#define WAVE_SELECT	8
+
+/* offset for SBI instrument */
+#define CONNECTION	10
+#define OFFSET_4OP	11
+
+/* from sound_config.h */
+#define SBFM_MAXINSTR	256
+
+static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format,
+				       const char *buf, int offs, int count)
+{
+	opl3_t *opl3;
+	int err = -EINVAL;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL);
+
+	if ((format == FM_PATCH) || (format == OPL3_PATCH)) {
+		struct sbi_instrument sbi;
+
+		size_t size;
+		snd_seq_instr_header_t *put;
+		snd_seq_instr_data_t *data;
+		fm_xinstrument_t *xinstr;
+
+		snd_seq_event_t ev;
+		int i;
+
+		mm_segment_t fs;
+
+		if (count < sizeof(sbi)) {
+			snd_printk("FM Error: Patch record too short\n");
+			return -EINVAL;
+		}
+		if (copy_from_user(&sbi, buf, sizeof(sbi)))
+			return -EFAULT;
+
+		if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
+			snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel);
+			return -EINVAL;
+		}
+
+		size = sizeof(*put) + sizeof(fm_xinstrument_t);
+		put = (snd_seq_instr_header_t *)snd_kcalloc(size, GFP_KERNEL);
+		if (put == NULL)
+			return -ENOMEM;
+		/* build header */
+		data = &put->data;
+		data->type = SNDRV_SEQ_INSTR_ATYPE_DATA;
+		strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3);
+		/* build data section */
+		xinstr = (fm_xinstrument_t *)(data + 1);
+		xinstr->stype = FM_STRU_INSTR;
+        
+		for (i = 0; i < 2; i++) {
+			xinstr->op[i].am_vib = sbi.operators[AM_VIB + i];
+			xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i];
+			xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i];
+			xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i];
+			xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i];
+		}
+		xinstr->feedback_connection[0] = sbi.operators[CONNECTION];
+
+		if (format == OPL3_PATCH) {
+			xinstr->type = FM_PATCH_OPL3;
+			for (i = 0; i < 2; i++) {
+				xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i];
+				xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i];
+				xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i];
+				xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i];
+				xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i];
+			}
+			xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION];
+		} else {
+			xinstr->type = FM_PATCH_OPL2;
+		}
+
+		put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
+		put->id.instr.bank = 127;
+		put->id.instr.prg = sbi.channel;
+		put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE;
+
+		memset (&ev, 0, sizeof(ev));
+		ev.source.client = SNDRV_SEQ_CLIENT_OSS;
+		ev.dest = arg->addr; 
+
+		ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR;
+		ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
+
+		fs = snd_enter_user();
+	__again:
+		ev.type = SNDRV_SEQ_EVENT_INSTR_PUT;
+		ev.data.ext.len = size;
+		ev.data.ext.ptr = put;
+
+		err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
+				    opl3->seq_client, 0, 0);
+		if (err == -EBUSY) {
+			snd_seq_instr_header_t remove;
+
+			memset (&remove, 0, sizeof(remove));
+			remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE;
+			remove.id.instr = put->id.instr;
+
+			/* remove instrument */
+			ev.type = SNDRV_SEQ_EVENT_INSTR_FREE;
+			ev.data.ext.len = sizeof(remove);
+			ev.data.ext.ptr = &remove;
+
+			snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
+					    opl3->seq_client, 0, 0);
+			goto __again;
+		}
+		snd_leave_user(fs);
+
+		kfree(put);
+	}
+	return err;
+}
+
+/* ioctl */
+static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd,
+				  unsigned long ioarg)
+{
+	opl3_t *opl3;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL);
+	switch (cmd) {
+		case SNDCTL_FM_LOAD_INSTR:
+			snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+			return -EINVAL;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
+
+		case SNDCTL_FM_4OP_ENABLE:
+			// handled automatically by OPL instrument type
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+/* reset device */
+static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg)
+{
+	opl3_t *opl3;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL);
+
+	return 0;
+}
diff -Nru linux/sound/drivers/opl3/opl3_seq.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_seq.c
--- linux/sound/drivers/opl3/opl3_seq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_seq.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,321 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM
+ *
+ *  OPL2/3 FM instrument loader:
+ *   alsa-tools/seq/sbiload/
+ *
+ *   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
+ *
+ */
+
+#include "opl3_voice.h"
+#include <linux/init.h>
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
+MODULE_CLASSES("{sound}");
+
+int use_internal_drums = 0;
+MODULE_PARM(use_internal_drums, "i");
+MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+int snd_opl3_synth_use_inc(opl3_t * opl3)
+{
+	if (!try_inc_mod_count(opl3->card->module))
+		return -EFAULT;
+	return 0;
+
+}
+
+void snd_opl3_synth_use_dec(opl3_t * opl3)
+{
+	dec_mod_count(opl3->card->module);
+}
+
+int snd_opl3_synth_setup(opl3_t * opl3)
+{
+	int idx;
+
+	down(&opl3->access_mutex);
+	if (opl3->used) {
+		up(&opl3->access_mutex);
+		return -EBUSY;
+	}
+	opl3->used++;
+	up(&opl3->access_mutex);
+
+	snd_opl3_reset(opl3);
+
+	for (idx = 0; idx < MAX_OPL3_VOICES; idx++) {
+		opl3->voices[idx].state = SNDRV_OPL3_ST_OFF;
+		opl3->voices[idx].time = 0;
+		opl3->voices[idx].keyon_reg = 0x00;
+	}
+	opl3->use_time = 0;
+	opl3->connection_reg = 0x00;
+	if (opl3->hardware >= OPL3_HW_OPL3) {
+		/* Enter OPL3 mode */
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE);
+		/* Clear 4-op connections */
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT,
+				 opl3->connection_reg);
+		opl3->max_voices = MAX_OPL3_VOICES;
+	}
+	return 0;
+}
+
+void snd_opl3_synth_cleanup(opl3_t * opl3)
+{
+	unsigned long flags;
+
+	/* Stop system timer */
+	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+	if (opl3->sys_timer_status) {
+		del_timer(&opl3->tlist);
+		opl3->sys_timer_status = 0;
+	}
+	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
+
+	snd_opl3_reset(opl3);
+	down(&opl3->access_mutex);
+	opl3->used--;
+	up(&opl3->access_mutex);
+}
+
+int snd_opl3_synth_use(void *private_data, snd_seq_port_subscribe_t * info)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO);
+	int err;
+
+	if ((err = snd_opl3_synth_setup(opl3)) < 0)
+		return err;
+
+	if (use_internal_drums) {
+		/* Percussion mode */
+		opl3->voices[6].state = opl3->voices[7].state = 
+			opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL;
+		snd_opl3_load_drums(opl3);
+		opl3->drum_reg = OPL3_PERCUSSION_ENABLE;
+		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg);
+	} else {
+		opl3->drum_reg = 0x00;
+	}
+
+	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
+		if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+			return err;
+	}
+	opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
+	return 0;
+}
+
+int snd_opl3_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO);
+
+	snd_opl3_synth_cleanup(opl3);
+
+	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
+		snd_opl3_synth_use_dec(opl3);
+	return 0;
+}
+
+/*
+ * MIDI emulation operators
+ */
+snd_midi_op_t opl3_ops = {
+	snd_opl3_note_on,
+	snd_opl3_note_off,
+	snd_opl3_key_press,
+	snd_opl3_terminate_note,
+	snd_opl3_control,
+	snd_opl3_nrpn,
+	snd_opl3_sysex,
+};
+
+static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct,
+				      void *private_data, int atomic, int hop)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL);
+
+	if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN &&
+	    ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) {
+		if (direct) {
+			snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev,
+					    opl3->seq_client, atomic, hop);
+		}
+	} else {
+		snd_midi_process_event(&opl3_ops, ev, opl3->chset);
+	}
+	return 0;
+}
+
+/* ------------------------------ */
+
+static void snd_opl3_synth_free_port(void *private_data)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return);
+
+	snd_midi_channel_free_set(opl3->chset);
+}
+
+static int snd_opl3_synth_create_port(opl3_t * opl3)
+{
+	snd_seq_port_callback_t callbacks;
+	char name[32];
+	int opl_ver;
+
+	opl3->chset = snd_midi_channel_alloc_set(16);
+	if (opl3->chset == NULL)
+		return -ENOMEM;
+	opl3->chset->private_data = opl3;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.owner = THIS_MODULE;
+	callbacks.use = snd_opl3_synth_use;
+	callbacks.unuse = snd_opl3_synth_unuse;
+	callbacks.event_input = snd_opl3_synth_event_input;
+	callbacks.private_free = snd_opl3_synth_free_port;
+	callbacks.private_data = opl3;
+
+	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
+	sprintf(name, "OPL%i Port", opl_ver);
+
+	opl3->chset->client = opl3->seq_client;
+	opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
+						      SNDRV_SEQ_PORT_CAP_WRITE |
+						      SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+						      SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+						      SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+						      SNDRV_SEQ_PORT_TYPE_SYNTH,
+						      name);
+	if (opl3->chset->port < 0) {
+		snd_midi_channel_free_set(opl3->chset);
+		return opl3->chset->port;
+	}
+	return 0;
+}
+
+/* ------------------------------ */
+
+static int snd_opl3_seq_new_device(snd_seq_device_t *dev)
+{
+	opl3_t *opl3;
+	int client;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	int opl_ver;
+
+	opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (opl3 == NULL)
+		return -EINVAL;
+
+	spin_lock_init(&opl3->voice_lock);
+
+	opl3->seq_client = -1;
+
+	/* allocate new client */
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = opl3;
+	callbacks.allow_output = callbacks.allow_input = 1;
+	client = opl3->seq_client =
+	    snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, &callbacks);
+	if (client < 0)
+		return client;
+
+	/* change name of client */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
+	sprintf(cinfo.name, "OPL%i FM synth", opl_ver);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	snd_opl3_synth_create_port(opl3);
+
+	/* initialize instrument list */
+	opl3->ilist = snd_seq_instr_list_new();
+	if (opl3->ilist == NULL) {
+		snd_seq_delete_kernel_client(client);
+		opl3->seq_client = -1;
+		return -ENOMEM;
+	}
+	opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+	snd_seq_fm_init(&opl3->fm_ops, NULL);
+
+	/* setup system timer */
+	memset(&opl3->tlist, 0, sizeof(opl3->tlist));
+	opl3->tlist.function = snd_opl3_timer_func;
+	opl3->tlist.data = (unsigned long) opl3;
+	spin_lock_init(&opl3->sys_timer_lock);
+	opl3->sys_timer_status = 0;
+
+#ifdef CONFIG_SND_OSSEMUL
+	snd_opl3_init_seq_oss(opl3, cinfo.name);
+#endif
+	return 0;
+}
+
+static int snd_opl3_seq_delete_device(snd_seq_device_t *dev)
+{
+	opl3_t *opl3;
+
+	opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (opl3 == NULL)
+		return -EINVAL;
+
+#ifdef CONFIG_SND_OSSEMUL
+	snd_opl3_free_seq_oss(opl3);
+#endif
+	if (opl3->seq_client >= 0) {
+		snd_seq_delete_kernel_client(opl3->seq_client);
+		opl3->seq_client = -1;
+	}
+	if (opl3->ilist)
+		snd_seq_instr_list_free(&opl3->ilist);
+	return 0;
+}
+
+static int __init alsa_opl3_seq_init(void)
+{
+	static snd_seq_dev_ops_t ops =
+	{
+		snd_opl3_seq_new_device,
+		snd_opl3_seq_delete_device
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
+					      sizeof(opl3_t*));
+}
+
+static void __exit alsa_opl3_seq_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
+}
+
+module_init(alsa_opl3_seq_init)
+module_exit(alsa_opl3_seq_exit)
diff -Nru linux/sound/drivers/opl3/opl3_synth.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_synth.c
--- linux/sound/drivers/opl3/opl3_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_synth.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,456 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *                   
+ *  Routines for OPL2/OPL3/OPL4 control
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/opl3.h>
+#define __SND_OSS_COMPAT__
+#include <sound/asound_fm.h>
+
+/*
+ *    There is 18 possible 2 OP voices
+ *      (9 in the left and 9 in the right).
+ *      The first OP is the modulator and 2nd is the carrier.
+ *
+ *      The first three voices in the both sides may be connected
+ *      with another voice to a 4 OP voice. For example voice 0
+ *      can be connected with voice 3. The operators of voice 3 are
+ *      used as operators 3 and 4 of the new 4 OP voice.
+ *      In this case the 2 OP voice number 0 is the 'first half' and
+ *      voice 3 is the second.
+ */
+
+
+/*
+ *    Register offset table for OPL2/3 voices,
+ *    OPL2 / one OPL3 register array side only
+ */
+
+char snd_opl3_regmap[MAX_OPL2_VOICES][4] =
+{
+/*	  OP1   OP2   OP3   OP4		*/
+/*	 ------------------------	*/
+	{ 0x00, 0x03, 0x08, 0x0b },
+	{ 0x01, 0x04, 0x09, 0x0c },
+	{ 0x02, 0x05, 0x0a, 0x0d },
+
+	{ 0x08, 0x0b, 0x00, 0x00 },
+	{ 0x09, 0x0c, 0x00, 0x00 },
+	{ 0x0a, 0x0d, 0x00, 0x00 },
+
+	{ 0x10, 0x13, 0x00, 0x00 },	/* used by percussive voices */
+	{ 0x11, 0x14, 0x00, 0x00 },	/* if the percussive mode */
+	{ 0x12, 0x15, 0x00, 0x00 }	/* is selected (only left reg block) */
+};
+
+/*
+ * prototypes
+ */
+static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note);
+static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice);
+static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params);
+static int snd_opl3_set_mode(opl3_t * opl3, int mode);
+static int snd_opl3_set_connection(opl3_t * opl3, int connection);
+
+/* ------------------------------ */
+
+/*
+ * open the device exclusively
+ */
+int snd_opl3_open(snd_hwdep_t * hw, struct file *file)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO);
+
+	down(&opl3->access_mutex);
+	if (opl3->used) {
+		up(&opl3->access_mutex);
+		return -EAGAIN;
+	}
+	opl3->used++;
+	up(&opl3->access_mutex);
+
+	return 0;
+}
+
+/*
+ * ioctl for hwdep device:
+ */
+int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file,
+		   unsigned int cmd, unsigned long arg)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO);
+
+	snd_assert(opl3 != NULL, return -EINVAL);
+
+	switch (cmd) {
+		/* get information */
+	case SNDRV_DM_FM_IOCTL_INFO:
+		{
+			snd_dm_fm_info_t info;
+
+			info.fm_mode = opl3->fm_mode;
+			info.rhythm = opl3->rhythm;
+			if (copy_to_user((snd_dm_fm_info_t *) arg, &info, sizeof(snd_dm_fm_info_t)))
+				return -EFAULT;
+			return 0;
+		}
+
+	case SNDRV_DM_FM_IOCTL_RESET:
+#ifdef CONFIG_SND_OSSEMUL
+	case SNDRV_DM_FM_OSS_IOCTL_RESET:
+#endif
+		snd_opl3_reset(opl3);
+		return 0;
+
+	case SNDRV_DM_FM_IOCTL_PLAY_NOTE:
+#ifdef CONFIG_SND_OSSEMUL
+	case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE:
+#endif
+		{
+			snd_dm_fm_note_t note;
+			if (copy_from_user(&note, (snd_dm_fm_note_t *) arg, sizeof(snd_dm_fm_note_t)))
+				return -EFAULT;
+			return snd_opl3_play_note(opl3, &note);
+		}
+
+	case SNDRV_DM_FM_IOCTL_SET_VOICE:
+#ifdef CONFIG_SND_OSSEMUL
+	case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE:
+#endif
+		{
+			snd_dm_fm_voice_t voice;
+			if (copy_from_user(&voice, (snd_dm_fm_voice_t *) arg, sizeof(snd_dm_fm_voice_t)))
+				return -EFAULT;
+			return snd_opl3_set_voice(opl3, &voice);
+		}
+
+	case SNDRV_DM_FM_IOCTL_SET_PARAMS:
+#ifdef CONFIG_SND_OSSEMUL
+	case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS:
+#endif
+		{
+			snd_dm_fm_params_t params;
+			if (copy_from_user(&params, (snd_dm_fm_params_t *) arg, sizeof(snd_dm_fm_params_t)))
+				return -EFAULT;
+			return snd_opl3_set_params(opl3, &params);
+		}
+
+	case SNDRV_DM_FM_IOCTL_SET_MODE:
+#ifdef CONFIG_SND_OSSEMUL
+	case SNDRV_DM_FM_OSS_IOCTL_SET_MODE:
+#endif
+		return snd_opl3_set_mode(opl3, (int) arg);
+
+	case SNDRV_DM_FM_IOCTL_SET_CONNECTION:
+#ifdef CONFIG_SND_OSSEMUL
+	case SNDRV_DM_FM_OSS_IOCTL_SET_OPL:
+#endif
+		return snd_opl3_set_connection(opl3, (int) arg);
+
+#ifdef CONFIG_SND_DEBUG
+	default:
+		snd_printk("unknown IOCTL: 0x%x\n", cmd);
+#endif
+	}
+	return -ENOTTY;
+}
+
+/*
+ * close the device
+ */
+int snd_opl3_release(snd_hwdep_t * hw, struct file *file)
+{
+	opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO);
+
+	snd_opl3_reset(opl3);
+	down(&opl3->access_mutex);
+	opl3->used--;
+	up(&opl3->access_mutex);
+
+	return 0;
+}
+
+/* ------------------------------ */
+
+void snd_opl3_reset(opl3_t * opl3)
+{
+	unsigned short opl3_reg;
+
+	unsigned short reg_side;
+	unsigned char voice_offset;
+
+	int max_voices, i;
+
+	max_voices = (opl3->hardware < OPL3_HW_OPL3) ?
+		MAX_OPL2_VOICES : MAX_OPL3_VOICES;
+
+	for (i = 0; i < max_voices; i++) {
+		/* Get register array side and offset of voice */
+		if (i < MAX_OPL2_VOICES) {
+			/* Left register block for voices 0 .. 8 */
+			reg_side = OPL3_LEFT;
+			voice_offset = i;
+		} else {
+			/* Right register block for voices 9 .. 17 */
+			reg_side = OPL3_RIGHT;
+			voice_offset = i - MAX_OPL2_VOICES;
+		}
+		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]);
+		opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */
+		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]);
+		opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */
+
+		opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+		opl3->command(opl3, opl3_reg, 0x00);	/* Note off */
+	}
+
+	if (opl3->hardware >= OPL3_HW_OPL3)
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00);	/* Enter OPL2 mode */
+
+	opl3->max_voices = MAX_OPL2_VOICES;
+	opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2;
+
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);	/* Melodic mode */
+	opl3->rhythm = 0;
+}
+
+
+static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note)
+{
+	unsigned short reg_side;
+	unsigned char voice_offset;
+
+	unsigned short opl3_reg;
+	unsigned char reg_val;
+
+	/* Voices 0 -  8 in OPL2 mode */
+	/* Voices 0 - 17 in OPL3 mode */
+	if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ?
+			    MAX_OPL3_VOICES : MAX_OPL2_VOICES))
+		return -EINVAL;
+
+	/* Get register array side and offset of voice */
+	if (note->voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = note->voice;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = note->voice - MAX_OPL2_VOICES;
+	}
+
+	/* Set lower 8 bits of note frequency */
+	reg_val = (unsigned char) note->fnum;
+	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+	
+	reg_val = 0x00;
+	/* Set output sound flag */
+	if (note->key_on)
+		reg_val |= OPL3_KEYON_BIT;
+	/* Set octave */
+	reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK;
+	/* Set higher 2 bits of note frequency */
+	reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK;
+
+	/* Set OPL3 KEYON_BLOCK register of requested voice */ 
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	return 0;
+}
+
+
+static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice)
+{
+	unsigned short reg_side;
+	unsigned char op_offset;
+	unsigned char voice_offset;
+
+	unsigned short opl3_reg;
+	unsigned char reg_val;
+
+	/* Only operators 1 and 2 */
+	if (voice->op > 1)
+		return -EINVAL;
+	/* Voices 0 -  8 in OPL2 mode */
+	/* Voices 0 - 17 in OPL3 mode */
+	if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ?
+			     MAX_OPL3_VOICES : MAX_OPL2_VOICES))
+		return -EINVAL;
+
+	/* Get register array side and offset of voice */
+	if (voice->voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice->voice;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice->voice - MAX_OPL2_VOICES;
+	}
+	/* Get register offset of operator */
+	op_offset = snd_opl3_regmap[voice_offset][voice->op];
+
+	reg_val = 0x00;
+	/* Set amplitude modulation (tremolo) effect */
+	if (voice->am)
+		reg_val |= OPL3_TREMOLO_ON;
+	/* Set vibrato effect */
+	if (voice->vibrato)
+		reg_val |= OPL3_VIBRATO_ON;
+	/* Set sustaining sound phase */
+	if (voice->do_sustain)
+		reg_val |= OPL3_SUSTAIN_ON;
+	/* Set keyboard scaling bit */ 
+	if (voice->kbd_scale)
+		reg_val |= OPL3_KSR;
+	/* Set harmonic or frequency multiplier */
+	reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK;
+
+	/* Set OPL3 AM_VIB register of requested voice/operator */ 
+	opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Set decreasing volume of higher notes */
+	reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK;
+	/* Set output volume */
+	reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK;
+
+	/* Set OPL3 KSL_LEVEL register of requested voice/operator */ 
+	opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Set attack phase level */
+	reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK;
+	/* Set decay phase level */
+	reg_val |= voice->decay & OPL3_DECAY_MASK;
+
+	/* Set OPL3 ATTACK_DECAY register of requested voice/operator */ 
+	opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Set sustain phase level */
+	reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK;
+	/* Set release phase level */
+	reg_val |= voice->release & OPL3_RELEASE_MASK;
+
+	/* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ 
+	opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Set inter-operator feedback */
+	reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK;
+	/* Set inter-operator connection */
+	if (voice->connection)
+		reg_val |= OPL3_CONNECTION_BIT;
+	/* OPL-3 only */
+	if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) {
+		if (voice->left)
+			reg_val |= OPL3_VOICE_TO_LEFT;
+		if (voice->right)
+			reg_val |= OPL3_VOICE_TO_RIGHT;
+	}
+	/* Feedback/connection bits are applicable to voice */
+	opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Select waveform */
+	reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK;
+	opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	return 0;
+}
+
+static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params)
+{
+	unsigned char reg_val;
+
+	reg_val = 0x00;
+	/* Set keyboard split method */
+	if (params->kbd_split)
+		reg_val |= OPL3_KEYBOARD_SPLIT;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val);
+
+	reg_val = 0x00;
+	/* Set amplitude modulation (tremolo) depth */
+	if (params->am_depth)
+		reg_val |= OPL3_TREMOLO_DEPTH;
+	/* Set vibrato depth */
+	if (params->vib_depth)
+		reg_val |= OPL3_VIBRATO_DEPTH;
+	/* Set percussion mode */
+	if (params->rhythm) {
+		reg_val |= OPL3_PERCUSSION_ENABLE;
+		opl3->rhythm = 1;
+	} else {
+		opl3->rhythm = 0;
+	}
+	/* Play percussion instruments */
+	if (params->bass)
+		reg_val |= OPL3_BASSDRUM_ON;
+	if (params->snare)
+		reg_val |= OPL3_SNAREDRUM_ON;
+	if (params->tomtom)
+		reg_val |= OPL3_TOMTOM_ON;
+	if (params->cymbal)
+		reg_val |= OPL3_CYMBAL_ON;
+	if (params->hihat)
+		reg_val |= OPL3_HIHAT_ON;
+
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val);
+	return 0;
+}
+
+static int snd_opl3_set_mode(opl3_t * opl3, int mode)
+{
+	if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3))
+		return -EINVAL;
+
+	if (mode == SNDRV_DM_FM_MODE_OPL3) {
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE);	/* Enter OPL3 mode */
+		opl3->fm_mode = SNDRV_DM_FM_MODE_OPL3;
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00);	/* Clear 4-op connections */
+	} else {
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00);		/* Enter OPL2 mode */
+		opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2;
+	}
+
+	return 0;
+}
+
+static int snd_opl3_set_connection(opl3_t * opl3, int connection)
+{
+	unsigned char reg_val;
+
+	/* OPL-3 only */
+	if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3)
+		return -EINVAL;
+
+	reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 |
+				OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2);
+	/* Set 4-op connections */
+	opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val);
+
+	return 0;
+}
diff -Nru linux/sound/drivers/opl3/opl3_voice.h linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_voice.h
--- linux/sound/drivers/opl3/opl3_voice.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_voice.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,52 @@
+#ifndef __OPL3_VOICE_H
+#define __OPL3_VOICE_H
+
+/*
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *   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
+ */
+
+#include <sound/opl3.h>
+
+/* Prototypes for opl3_seq.c */
+int snd_opl3_synth_use_inc(opl3_t * opl3);
+void snd_opl3_synth_use_dec(opl3_t * opl3);
+int snd_opl3_synth_setup(opl3_t * opl3);
+void snd_opl3_synth_cleanup(opl3_t * opl3);
+
+/* Prototypes for opl3_midi.c */
+void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan);
+void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan);
+void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+
+void snd_opl3_calc_volume(unsigned char *reg, int vel, snd_midi_channel_t *chan);
+void snd_opl3_timer_func(unsigned long data);
+
+/* Prototypes for opl3_drums.c */
+void snd_opl3_load_drums(opl3_t *opl3);
+void snd_opl3_drum_switch(opl3_t *opl3, int note, int on_off, int vel, snd_midi_channel_t *chan);
+
+/* Prototypes for opl3_oss.c */
+#ifdef CONFIG_SND_OSSEMUL
+void snd_opl3_init_seq_oss(opl3_t *opl3, char *name);
+void snd_opl3_free_seq_oss(opl3_t *opl3);
+#endif
+
+#endif
diff -Nru linux/sound/drivers/serial-u16550.c linux-2.4.19-pre5-mjc/sound/drivers/serial-u16550.c
--- linux/sound/drivers/serial-u16550.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/serial-u16550.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,990 @@
+/*
+ *   serial.c
+ *   Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                    Isaku Yamahata <yamahata@private.email.ne.jp>,
+ *		      George Hansper <ghansper@apana.org.au>,
+ *		      Hannu Savolainen
+ *
+ *   This code is based on the code from ALSA 0.5.9, but heavily rewritten.
+ *
+ * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com 
+ *      Added support for the Midiator MS-124T and for the MS-124W in
+ *      Single Addressed (S/A) or Multiple Burst (M/B) mode, with
+ *      power derived either parasitically from the serial port or
+ *      from a separate power supply.
+ * 
+ *      The new snd_adaptor module parameter allows you to select
+ *      either the default Roland Soundcanvas support (0), which was
+ *      previously included in this driver but was not documented,
+ *      Midiator MS-124T support (1), Midiator MS-124W S/A mode
+ *      support (2), or MS-124W M/B mode support (3).  For the
+ *      Midiator MS-124W, you must set the physical M-S and A-B
+ *      switches on the Midiator to match the driver mode you select.
+ *  
+ *      - In Roland Soundcanvas mode, multiple ALSA raw MIDI
+ *      substreams are supported (midiCnD0-midiCnD15).  Whenever you
+ *      write to a different substream, the driver sends the
+ *      nonstandard MIDI command sequence F5 NN, where NN is the
+ *      substream number plus 1.  Roland modules use this command to
+ *      switch between different "parts", so this feature lets you
+ *      treat each part as a distinct raw MIDI substream.  The driver
+ *      provides no way to send F5 00 (no selection) or to not send
+ *      the F5 NN command sequence at all; perhaps it ought to.
+ * 
+ *      - In MS-124T mode, one raw MIDI substream is supported
+ *      (midiCnD0); the snd_outs module parameter is automatically set
+ *      to 1.  The driver sends the same data to all four MIDI Out
+ *      connectors.  Set the A-B switch and the snd_speed module
+ *      parameter to match (A=19200, B=9600).
+ * 
+ *      Usage example for MS-124T, with A-B switch in A position:
+ *        setserial /dev/ttyS0 uart none
+ *        /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \
+ *            snd_adaptor=1 snd_speed=19200
+ *
+ *      - In MS-124W S/A mode, one raw MIDI substream is supported
+ *      (midiCnD0); the snd_outs module parameter is automatically set
+ *      to 1.  The driver sends the same data to all four MIDI Out
+ *      connectors at full MIDI speed.
+ * 
+ *      Usage example for S/A mode:
+ *        setserial /dev/ttyS0 uart none
+ *        /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \
+ *            snd_adaptor=2
+ *
+ *      - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI
+ *      substreams; the snd_outs module parameter is automatically set
+ *      to 16.  The substream number gives a bitmask of which MIDI Out
+ *      connectors the data should be sent to, with midiCnD1 sending
+ *      to Out 1, midiCnD2 to Out 2, midiCnD4 to Out 3, and midiCnD8
+ *      to Out 4.  Thus midiCnD15 sends the data to all 4 ports.  As a
+ *      special case, midiCnD0 also sends to all ports, since it is
+ *      not useful to send the data to no ports.  M/B mode has extra
+ *      overhead to select the MIDI Out for each byte, so the
+ *      aggregate data rate across all four MIDI Outs is at most one
+ *      byte every 520 us, as compared with the full MIDI data rate of
+ *      one byte every 320 us per port.
+ * 
+ *      Usage example for M/B mode:
+ *        setserial /dev/ttyS0 uart none
+ *        /sbin/insmod snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \
+ *            snd_adaptor=3
+ *
+ *      - The MS-124W hardware's M/A mode is currently not supported.
+ *      This mode allows the MIDI Outs to act independently at double
+ *      the aggregate throughput of M/B, but does not allow sending
+ *      the same byte simultaneously to multiple MIDI Outs.  The M/A
+ *      protocol requires the driver to twiddle the modem control
+ *      lines under timing constraints, so it would be a bit more
+ *      complicated to implement than the other modes.
+ *
+ *      - Midiator models other than MS-124W and MS-124T are currently
+ *      not supported.  Note that the suffix letter is significant;
+ *      the MS-124 and MS-124B are not compatible, nor are the other
+ *      known models MS-101, MS-101B, MS-103, and MS-114.  I do have
+ *      documentation that partially covers these models, but no units
+ *      to experiment with.  The MS-124W support is tested with a real
+ *      unit.  The MS-124T support is untested, but should work.
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#include <linux/serial_reg.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("MIDI serial");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA, MIDI serial}}");
+
+#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */
+#define SNDRV_SERIAL_MS124T 1      /* Midiator MS-124T */
+#define SNDRV_SERIAL_MS124W_SA 2   /* Midiator MS-124W in S/A mode */
+#define SNDRV_SERIAL_MS124W_MB 3   /* Midiator MS-124W in M/B mode */
+#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_MS124W_MB
+static char *adaptor_names[] = {
+	"Soundcanvas",
+        "MS-124T",
+	"MS-124W S/A",
+	"MS-124W M/B"
+};
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* 0x3f8,0x2f8,0x3e8,0x2e8 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 3,4,5,7,9,10,11,14,15 */
+static int snd_speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */
+static int snd_base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */
+static int snd_outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};	/* 1 to 16 */
+static int snd_adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Serial MIDI.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Serial MIDI.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_enable, "Enable UART16550A chip.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for UART16550A chip.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for UART16550A chip.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_speed, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_speed, "Speed in bauds.");
+MODULE_PARM_SYNTAX(snd_speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list");
+MODULE_PARM(snd_base, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_base, "Base for divisor in bauds.");
+MODULE_PARM_SYNTAX(snd_base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list");
+MODULE_PARM(snd_outs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_outs, "Number of MIDI outputs.");
+MODULE_PARM_SYNTAX(snd_outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list");
+MODULE_PARM(snd_adaptor, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_adaptor, "Type of adaptor.");
+MODULE_PARM_SYNTAX(snd_adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B}},dialog:list");
+
+/*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/  /* Address outs as 0-3 instead of bitmap */
+
+#define SNDRV_SERIAL_MAX_OUTS	16		/* max 64, min 16 */
+
+#define TX_BUFF_SIZE		(1<<9)		/* Must be 2^n */
+#define TX_BUFF_MASK		(TX_BUFF_SIZE - 1)
+
+#define SERIAL_MODE_NOT_OPENED 		(0)
+#define SERIAL_MODE_INPUT_OPEN		(1 << 0)
+#define SERIAL_MODE_OUTPUT_OPEN		(1 << 1)
+#define SERIAL_MODE_INPUT_TRIGGERED	(1 << 2)
+#define SERIAL_MODE_OUTPUT_TRIGGERED	(1 << 3)
+
+typedef struct _snd_uart16550 {
+	snd_card_t *card;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS];
+	snd_rawmidi_substream_t *midi_input;
+
+	int filemode;		//open status of file
+
+	spinlock_t open_lock;
+
+	int irq;
+
+	unsigned long base;
+	struct resource *res_base;
+
+	unsigned int speed;
+	unsigned int speed_base;
+	unsigned char divisor;
+
+	unsigned char old_divisor_lsb;
+	unsigned char old_divisor_msb;
+	unsigned char old_line_ctrl_reg;
+
+	// parameter for using of write loop
+	short int fifo_limit;	//used in uart16550
+        short int fifo_count;	//used in uart16550
+
+	// type of adaptor
+	int adaptor;
+
+	// outputs
+	int prev_out;
+	unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS];
+
+	// write buffer and its writing/reading position
+	unsigned char tx_buff[TX_BUFF_SIZE];
+	int buff_in_count;
+        int buff_in;
+        int buff_out;
+
+	// wait timer
+	unsigned int timer_running:1;
+	struct timer_list buffer_timer;
+
+} snd_uart16550_t;
+
+static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+inline static void snd_uart16550_add_timer(snd_uart16550_t *uart)
+{
+	if (! uart->timer_running) {
+		/* timer 38600bps * 10bit * 16byte */
+		uart->buffer_timer.expires = jiffies + (HZ+255)/256;
+		uart->timer_running = 1;
+		add_timer(&uart->buffer_timer);
+	}
+}
+
+inline static void snd_uart16550_del_timer(snd_uart16550_t *uart)
+{
+	if (uart->timer_running) {
+		del_timer(&uart->buffer_timer);
+		uart->timer_running = 0;
+	}
+}
+
+/* This macro is only used in snd_uart16550_io_loop */
+inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart)
+{
+	unsigned short buff_out = uart->buff_out;
+	outb(uart->tx_buff[buff_out], uart->base + UART_TX);
+	uart->fifo_count++;
+	buff_out++;
+	buff_out &= TX_BUFF_MASK;
+	uart->buff_out = buff_out;
+	uart->buff_in_count--;
+}
+
+/* This loop should be called with interrupts disabled
+ * We don't want to interrupt this, 
+ * as we're already handling an interupt 
+ */
+static void snd_uart16550_io_loop(snd_uart16550_t * uart)
+{
+	unsigned char c, status;
+
+	/* Read Loop */
+	while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) {
+		/* while receive data ready */
+		c = inb(uart->base + UART_RX);
+		if (uart->filemode & SERIAL_MODE_INPUT_OPEN) {
+			snd_rawmidi_receive(uart->midi_input, &c, 1);
+		}
+		if (status & UART_LSR_OE)
+			snd_printk("%s: Overrun on device at 0x%lx\n",
+			       uart->rmidi->name, uart->base);
+	}
+
+	/* no need of check SERIAL_MODE_OUTPUT_OPEN because if not,
+	   buffer is never filled. */
+	/* Check write status */
+	if (status & UART_LSR_THRE) {
+		uart->fifo_count = 0;
+	}
+	if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
+		/* Can't use FIFO, must send only when CTS is true */
+		status = inb(uart->base + UART_MSR);
+		if (uart->fifo_count == 0 && (status & UART_MSR_CTS)
+		    && uart->buff_in_count > 0)
+			snd_uart16550_buffer_output(uart);
+	} else {
+		/* Write loop */
+		while (uart->fifo_count < uart->fifo_limit	/* Can we write ? */
+		       && uart->buff_in_count > 0)	/* Do we want to? */
+			snd_uart16550_buffer_output(uart);
+	}
+	if (uart->irq < 0 && uart->buff_in_count > 0)
+		snd_uart16550_add_timer(uart);
+}
+
+/* NOTES ON SERVICING INTERUPTS
+ * ---------------------------
+ * After receiving a interrupt, it is important to indicate to the UART that
+ * this has been done. 
+ * For a Rx interupt, this is done by reading the received byte.
+ * For a Tx interupt this is done by either:
+ * a) Writing a byte
+ * b) Reading the IIR
+ * It is particularly important to read the IIR if a Tx interupt is received
+ * when there is no data in tx_buff[], as in this case there no other
+ * indication that the interupt has been serviced, and it remains outstanding
+ * indefinitely. This has the curious side effect that and no further interupts
+ * will be generated from this device AT ALL!!.
+ * It is also desirable to clear outstanding interupts when the device is
+ * opened/closed.
+ *
+ *
+ * Note that some devices need OUT2 to be set before they will generate
+ * interrupts at all. (Possibly tied to an internal pull-up on CTS?)
+ */
+static void snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	snd_uart16550_t *uart;
+
+	uart = (snd_uart16550_t *) dev_id;
+	spin_lock(&uart->open_lock);
+	if (uart->filemode == SERIAL_MODE_NOT_OPENED) {
+		spin_unlock(&uart->open_lock);
+		return;
+	}
+	inb(uart->base + UART_IIR);		/* indicate to the UART that the interupt has been serviced */
+	snd_uart16550_io_loop(uart);
+	spin_unlock(&uart->open_lock);
+}
+
+/* When the polling mode, this function calls snd_uart16550_io_loop. */
+static void snd_uart16550_buffer_timer(unsigned long data)
+{
+	snd_uart16550_t *uart;
+
+	uart = (snd_uart16550_t *)data;
+	spin_lock(&uart->open_lock);
+	snd_uart16550_del_timer(uart);
+	snd_uart16550_io_loop(uart);
+	spin_unlock(&uart->open_lock);
+}
+
+/*
+ *  this method probes, if an uart sits on given port
+ *  return 0 if found
+ *  return negative error if not found
+ */
+static int __init snd_uart16550_detect(unsigned int io_base)
+{
+	int ok;
+	unsigned char c;
+
+	if (check_region(io_base, 8))
+		return -EBUSY;
+
+	/* Do some vague tests for the presence of the uart */
+	if (io_base == 0)
+		return -ENODEV;	/* Not configured */
+
+	ok = 1;			/* uart detected unless one of the following tests should fail */
+	/* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */
+	outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */
+	c = inb(io_base + UART_IER);
+	/* The top four bits of the IER should always == 0 */
+	if ((c & 0xf0) != 0)
+		ok = 0;		/* failed */
+
+	outb(0xaa, io_base + UART_SCR);
+	/* Write arbitrary data into the scratch reg */
+	c = inb(io_base + UART_SCR);
+	/* If it comes back, it's OK */
+	if (c != 0xaa)
+		ok = 0;		/* failed */
+
+	outb(0x55, io_base + UART_SCR);
+	/* Write arbitrary data into the scratch reg */
+	c = inb(io_base + UART_SCR);
+	/* If it comes back, it's OK */
+	if (c != 0x55)
+		ok = 0;		/* failed */
+
+	return ok;
+}
+
+static void snd_uart16550_do_open(snd_uart16550_t * uart)
+{
+	char byte;
+
+	/* Initialize basic variables */
+	uart->buff_in_count = 0;
+	uart->buff_in = 0;
+	uart->buff_out = 0;
+	uart->fifo_limit = 1;
+	uart->fifo_count = 0;
+	uart->timer_running = 0;
+
+	outb(UART_FCR_ENABLE_FIFO	/* Enable FIFO's (if available) */
+	     | UART_FCR_CLEAR_RCVR	/* Clear receiver FIFO */
+	     | UART_FCR_CLEAR_XMIT	/* Clear transmitter FIFO */
+	     | UART_FCR_TRIGGER_4	/* Set FIFO trigger at 4-bytes */
+	/* NOTE: interupt generated after T=(time)4-bytes
+	 * if less than UART_FCR_TRIGGER bytes received
+	 */
+	     ,uart->base + UART_FCR);	/* FIFO Control Register */
+
+	if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0)
+		uart->fifo_limit = 16;
+	if (uart->divisor != 0) {
+		uart->old_line_ctrl_reg = inb(uart->base + UART_LCR);
+		outb(UART_LCR_DLAB	/* Divisor latch access bit */
+		     ,uart->base + UART_LCR);	/* Line Control Register */
+		uart->old_divisor_lsb = inb(uart->base + UART_DLL);
+		uart->old_divisor_msb = inb(uart->base + UART_DLM);
+
+		outb(uart->divisor
+		     ,uart->base + UART_DLL);	/* Divisor Latch Low */
+		outb(0
+		     ,uart->base + UART_DLM);	/* Divisor Latch High */
+		/* DLAB is reset to 0 in next outb() */
+	}
+	/* Set serial parameters (parity off, etc) */
+	outb(UART_LCR_WLEN8	/* 8 data-bits */
+	     | 0		/* 1 stop-bit */
+	     | 0		/* parity off */
+	     | 0		/* DLAB = 0 */
+	     ,uart->base + UART_LCR);	/* Line Control Register */
+
+	switch (uart->adaptor) {
+	default:
+		outb(UART_MCR_RTS	/* Set Request-To-Send line active */
+		     | UART_MCR_DTR	/* Set Data-Terminal-Ready line active */
+		     | UART_MCR_OUT2	/* Set OUT2 - not always required, but when
+					 * it is, it is ESSENTIAL for enabling interrupts
+				 */
+		     ,uart->base + UART_MCR);	/* Modem Control Register */
+		break;
+	case SNDRV_SERIAL_MS124W_SA:
+	case SNDRV_SERIAL_MS124W_MB:
+		/* MS-124W can draw power from RTS and DTR if they
+		   are in opposite states. */ 
+		outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2,
+		     uart->base + UART_MCR);
+		break;
+	case SNDRV_SERIAL_MS124T:
+		/* MS-124T can draw power from RTS and/or DTR (preferably
+		   both) if they are both asserted. */
+		outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2,
+		     uart->base + UART_MCR);
+		break;
+	}
+
+	if (uart->irq < 0) {
+		byte = (0 & UART_IER_RDI)	/* Disable Receiver data interupt */
+		    |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interupt */
+		    ;
+	} else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
+		byte = UART_IER_RDI	/* Enable Receiver data interrupt */
+		    | UART_IER_MSI	/* Enable Modem status interrupt */
+		    ;
+	} else {
+		byte = UART_IER_RDI	/* Enable Receiver data interupt */
+		    | UART_IER_THRI	/* Enable Transmitter holding register empty interupt */
+		    ;
+	}
+	outb(byte, uart->base + UART_IER);	/* Interupt enable Register */
+
+	inb(uart->base + UART_LSR);	/* Clear any pre-existing overrun indication */
+	inb(uart->base + UART_IIR);	/* Clear any pre-existing transmit interrupt */
+	inb(uart->base + UART_RX);	/* Clear any pre-existing receive interrupt */
+}
+
+static void snd_uart16550_do_close(snd_uart16550_t * uart)
+{
+	if (uart->irq < 0)
+		snd_uart16550_del_timer(uart);
+
+	/* NOTE: may need to disable interrupts before de-registering out handler.
+	 * For now, the consequences are harmless.
+	 */
+
+	outb((0 & UART_IER_RDI)		/* Disable Receiver data interupt */
+	     |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interupt */
+	     ,uart->base + UART_IER);	/* Interupt enable Register */
+
+	switch (uart->adaptor) {
+	default:
+		outb((0 & UART_MCR_RTS)		/* Deactivate Request-To-Send line  */
+		     |(0 & UART_MCR_DTR)	/* Deactivate Data-Terminal-Ready line */
+		     |(0 & UART_MCR_OUT2)	/* Deactivate OUT2 */
+		     ,uart->base + UART_MCR);	/* Modem Control Register */
+	  break;
+	case SNDRV_SERIAL_MS124W_SA:
+	case SNDRV_SERIAL_MS124W_MB:
+		/* MS-124W can draw power from RTS and DTR if they
+		   are in opposite states; leave it powered. */ 
+		outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2),
+		     uart->base + UART_MCR);
+		break;
+	case SNDRV_SERIAL_MS124T:
+		/* MS-124T can draw power from RTS and/or DTR (preferably
+		   both) if they are both asserted; leave it powered. */
+		outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2),
+		     uart->base + UART_MCR);
+		break;
+	}
+
+	inb(uart->base + UART_IIR);	/* Clear any outstanding interupts */
+
+	/* Restore old divisor */
+	if (uart->divisor != 0) {
+		outb(UART_LCR_DLAB		/* Divisor latch access bit */
+		     ,uart->base + UART_LCR);	/* Line Control Register */
+		outb(uart->old_divisor_lsb
+		     ,uart->base + UART_DLL);	/* Divisor Latch Low */
+		outb(uart->old_divisor_msb
+		     ,uart->base + UART_DLM);	/* Divisor Latch High */
+		/* Restore old LCR (data bits, stop bits, parity, DLAB) */
+		outb(uart->old_line_ctrl_reg
+		     ,uart->base + UART_LCR);	/* Line Control Register */
+	}
+}
+
+static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+	if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+		snd_uart16550_do_open(uart);
+	uart->filemode |= SERIAL_MODE_INPUT_OPEN;
+	uart->midi_input = substream;
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+	return 0;
+}
+
+static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+	uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
+	uart->midi_input = NULL;
+	if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+		snd_uart16550_do_close(uart);
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+	return 0;
+}
+
+static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+	if (up) {
+		uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
+	} else {
+		uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
+	}
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+}
+
+static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+	if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+		snd_uart16550_do_open(uart);
+	uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;
+	uart->midi_output[substream->number] = substream;
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+	return 0;
+};
+
+static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+	uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
+	uart->midi_output[substream->number] = NULL;
+	if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+		snd_uart16550_do_close(uart);
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+	return 0;
+};
+
+inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte)
+{
+	unsigned short buff_in = uart->buff_in;
+	uart->tx_buff[buff_in] = byte;
+	buff_in++;
+	buff_in &= TX_BUFF_MASK;
+	uart->buff_in = buff_in;
+	uart->buff_in_count++;
+	if (uart->irq < 0) /* polling mode */
+		snd_uart16550_add_timer(uart); 
+}
+
+static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte)
+{
+	if (uart->buff_in_count == 0                            /* Buffer empty? */
+	    && (uart->adaptor != SNDRV_SERIAL_MS124W_SA ||
+		(uart->fifo_count == 0                               /* FIFO empty? */
+		 && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */
+
+	        /* Tx Buffer Empty - try to write immediately */
+		if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) {
+		        /* Transmitter holding register (and Tx FIFO) empty */
+		        uart->fifo_count = 1;
+			outb(midi_byte, uart->base + UART_TX);
+		} else {
+		        if (uart->fifo_count < uart->fifo_limit) {
+			        uart->fifo_count++;
+				outb(midi_byte, uart->base + UART_TX);
+			} else {
+			        /* Cannot write (buffer empty) - put char in buffer */
+				snd_uart16550_write_buffer(uart, midi_byte);
+			}
+		}
+	} else {
+		if (uart->buff_in_count >= TX_BUFF_SIZE) {
+			snd_printk("%s: Buffer overrun on device at 0x%lx\n",
+				   uart->rmidi->name, uart->base);
+			return;
+		}
+		snd_uart16550_write_buffer(uart, midi_byte);
+	}
+}
+
+static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	unsigned char midi_byte, addr_byte;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return);
+	char first;
+	
+	/* Interupts are disabled during the updating of the tx_buff,
+	 * since it is 'bad' to have two processes updating the same
+	 * variables (ie buff_in & buff_out)
+	 */
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+
+	if (uart->irq < 0)	//polling
+		snd_uart16550_io_loop(uart);
+
+	if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) {
+		while (1) {
+			/* buffer full? */
+			/* in this mode we need two bytes of space */
+			if (uart->buff_in_count > TX_BUFF_SIZE - 2)
+				break;
+			if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
+				break;
+#if SNDRV_SERIAL_MS124W_MB_NOCOMBO
+			/* select exactly one of the four ports */
+			addr_byte = (1 << (substream->number + 4)) | 0x08;
+#else
+			/* select any combination of the four ports */
+			addr_byte = (substream->number << 4) | 0x08;
+			/* ...except none */
+			if (addr_byte == 0x08) addr_byte = 0xf8;
+#endif
+			snd_uart16550_output_byte(uart, substream, addr_byte);
+			/* send midi byte */
+			snd_uart16550_output_byte(uart, substream, midi_byte);
+		}
+	} else {
+		first = 0;
+		while (1) {
+			/* buffer full? */
+			if (uart->buff_in_count >= TX_BUFF_SIZE)
+				break;
+			if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
+				break;
+			if (first == 0 && uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS &&
+			    uart->prev_out != substream->number) {
+				/* Roland Soundcanvas part selection */
+				/* If this substream of the data is different previous
+				   substream in this uart, send the change part event */
+				uart->prev_out = substream->number;
+				/* change part */
+				snd_uart16550_output_byte(uart, substream, 0xf5);
+				/* data */
+				snd_uart16550_output_byte(uart, substream, uart->prev_out + 1);
+				/* If midi_byte is a data byte, send the previous status byte */
+				if (midi_byte < 0x80)
+					snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]);
+			}
+			/* send midi byte */
+			snd_uart16550_output_byte(uart, substream, midi_byte);
+			if (midi_byte >= 0x80 && midi_byte < 0xf0)
+				uart->prev_status[uart->prev_out] = midi_byte;
+			first = 1;
+		}
+	}
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+}
+
+static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&uart->open_lock, flags);
+	if (up) {
+		uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
+	} else {
+		uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
+	}
+	spin_unlock_irqrestore(&uart->open_lock, flags);
+	if (up)
+		snd_uart16550_output_write(substream);
+}
+
+static snd_rawmidi_ops_t snd_uart16550_output =
+{
+	open:		snd_uart16550_output_open,
+	close:		snd_uart16550_output_close,
+	trigger:	snd_uart16550_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_uart16550_input =
+{
+	open:		snd_uart16550_input_open,
+	close:		snd_uart16550_input_close,
+	trigger:	snd_uart16550_input_trigger,
+};
+
+static int snd_uart16550_free(snd_uart16550_t *uart)
+{
+	if (uart->irq >= 0)
+		free_irq(uart->irq, (void *)uart);
+	if (uart->res_base) {
+		release_resource(uart->res_base);
+		kfree_nocheck(uart->res_base);
+	}
+	snd_magic_kfree(uart);
+	return 0;
+};
+
+static int snd_uart16550_dev_free(snd_device_t *device)
+{
+	snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, device->device_data, return -ENXIO);
+	return snd_uart16550_free(uart);
+}
+
+static int __init snd_uart16550_create(snd_card_t * card,
+				       unsigned long iobase,
+				       int irq,
+				       unsigned int speed,
+				       unsigned int base,
+				       int adaptor,
+				       snd_uart16550_t **ruart)
+{
+	static snd_device_ops_t ops = {
+		dev_free:       snd_uart16550_dev_free,
+	};
+	snd_uart16550_t *uart;
+	int err;
+
+
+	if ((uart = snd_magic_kcalloc(snd_uart16550_t, 0, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	uart->adaptor = adaptor;
+	uart->card = card;
+	spin_lock_init(&uart->open_lock);
+	uart->irq = -1;
+	if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) {
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1);
+		return -EBUSY;
+	}
+	uart->base = iobase;
+	if (irq >= 0) {
+		if (request_irq(irq, snd_uart16550_interrupt,
+				SA_INTERRUPT, "Serial MIDI", (void *) uart)) {
+			uart->irq = -1;
+			snd_printk("irq %d busy. Using Polling.\n", irq);
+		} else {
+			uart->irq = irq;
+		}
+	}
+	uart->divisor = base / speed;
+	uart->speed = base / (unsigned int)uart->divisor;
+	uart->speed_base = base;
+	uart->prev_out = -1;
+	memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS);
+	uart->buffer_timer.function = snd_uart16550_buffer_timer;
+	uart->buffer_timer.data = (unsigned long)uart;
+	uart->timer_running = 0;
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
+		snd_uart16550_free(uart);
+		return err;
+	}
+
+	switch (uart->adaptor) {
+	case SNDRV_SERIAL_MS124W_SA:
+	case SNDRV_SERIAL_MS124W_MB:
+		/* MS-124W can draw power from RTS and DTR if they
+		   are in opposite states. */ 
+		outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR);
+		break;
+	case SNDRV_SERIAL_MS124T:
+		/* MS-124T can draw power from RTS and/or DTR (preferably
+		   both) if they are asserted. */
+		outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR);
+		break;
+	default:
+		break;
+	}
+
+	if (ruart)
+		*ruart = uart;
+
+	return 0;
+}
+
+static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, snd_rawmidi_t **rmidi)
+{
+	snd_rawmidi_t *rrawmidi;
+	int err;
+
+	if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, 1, &rrawmidi)) < 0)
+		return err;
+	snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input);
+	snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output);
+	sprintf(rrawmidi->name, "uart16550 MIDI #%d", device);
+	rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+			       SNDRV_RAWMIDI_INFO_INPUT |
+			       SNDRV_RAWMIDI_INFO_DUPLEX;
+	rrawmidi->private_data = uart;
+	if (rmidi)
+		*rmidi = rrawmidi;
+	return 0;
+}
+
+static int __init snd_serial_probe(int dev)
+{
+	snd_card_t *card;
+	snd_uart16550_t *uart;
+	int err;
+
+	if (!snd_enable[dev])
+		return -ENOENT;
+
+	switch (snd_adaptor[dev]) {
+	case SNDRV_SERIAL_SOUNDCANVAS:
+		break;
+	case SNDRV_SERIAL_MS124T:
+	case SNDRV_SERIAL_MS124W_SA:
+		snd_outs[dev] = 1;
+		break;
+	case SNDRV_SERIAL_MS124W_MB:
+		snd_outs[dev] = 16;
+		break;
+	default:
+		snd_printk("Adaptor type is out of range 0-%d (%d)\n",
+			   SNDRV_SERIAL_MAX_ADAPTOR, snd_adaptor[dev]);
+		return -ENODEV;
+	}
+
+	if (snd_outs[dev] < 1 || snd_outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
+		snd_printk("Count of outputs is out of range 1-%d (%d)\n",
+			   SNDRV_SERIAL_MAX_OUTS, snd_outs[dev]);
+		return -ENODEV;
+	}
+
+	card  = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	strcpy(card->driver, "Serial");
+	strcpy(card->shortname, "Serial midi (uart16550A)");
+
+	if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) {
+		snd_card_free(card);
+		printk(KERN_ERR "no UART detected at 0x%lx\n", (long)snd_port[dev]);
+		return err;
+	}
+
+	if ((err = snd_uart16550_create(card,
+					snd_port[dev],
+					snd_irq[dev],
+					snd_speed[dev],
+					snd_base[dev],
+					snd_adaptor[dev],
+					&uart)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_uart16550_rmidi(uart, 0, snd_outs[dev], &uart->rmidi)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d adaptor %s",
+		card->shortname,
+		uart->base,
+		uart->irq,
+		uart->speed,
+		(int)uart->divisor,
+		snd_outs[dev],
+		adaptor_names[uart->adaptor]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_serial_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_serial_init(void)
+{
+	int dev = 0;
+	int cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (snd_serial_probe(dev) == 0)
+			cards++;
+	}
+
+	if (cards == 0) {
+#ifdef MODULE
+		printk(KERN_ERR "serial midi soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_serial_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (snd_serial_cards[dev] != NULL)
+			snd_card_free(snd_serial_cards[dev]);
+	}
+}
+
+module_init(alsa_card_serial_init)
+module_exit(alsa_card_serial_exit)
+
+#ifndef MODULE
+
+/* format is: snd-serial=snd_enable,snd_index,snd_id,
+			 snd_port,snd_irq,snd_speed,snd_base,snd_outs */
+
+static int __init alsa_card_serial_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_speed[nr_dev]) == 2 &&
+	       get_option(&str,&snd_base[nr_dev]) == 2 &&
+	       get_option(&str,&snd_outs[nr_dev]) == 2 &&
+	       get_option(&str,&snd_adaptor[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-serial=", alsa_card_serial_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/drivers/virmidi.c linux-2.4.19-pre5-mjc/sound/drivers/virmidi.c
--- linux/sound/drivers/virmidi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/drivers/virmidi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,185 @@
+/*
+ *  Dummy soundcard for virtual rawmidi devices
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+/*
+ * VIRTUAL RAW MIDI DEVICE CARDS
+ *
+ * This dummy card contains up to 4 virtual rawmidi devices.
+ * They are not real rawmidi devices but just associated with sequencer
+ * clients, so that any input/output sources can be connected as a raw
+ * MIDI device arbitrary.
+ * Also, multiple access is allowed to a single rawmidi device.
+ *
+ * Typical usage is like following:
+ * - Load snd-virmidi module.
+ *	# modprobe snd-virmidi snd_index=2
+ *   Then, sequencer clients 72:0 to 75:0 will be created, which are
+ *   mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively.
+ *
+ * - Connect input/output via aconnect.
+ *	% aconnect 64:0 72:0	# keyboard input redirection 64:0 -> 72:0
+ *	% aconnect 72:0 65:0	# output device redirection 72:0 -> 65:0
+ *
+ * - Run application using a midi device (eg. /dev/snd/midiC1D0)
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_virmidi.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA,Virtual rawmidi device}}");
+
+#define MAX_MIDI_DEVICES	8
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for virmidi soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for virmidi soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable this soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (1-8)");
+MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{1,8}}");
+
+typedef struct snd_card_virmidi {
+	snd_card_t *card;
+	snd_rawmidi_t *midi[MAX_MIDI_DEVICES];
+} snd_card_virmidi_t;
+
+static snd_card_t *snd_virmidi_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_card_virmidi_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_card_virmidi *vmidi;
+	int idx, err;
+
+	if (!snd_enable[dev])
+		return -ENODEV;
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_card_virmidi));
+	if (card == NULL)
+		return -ENOMEM;
+	vmidi = (struct snd_card_virmidi *)card->private_data;
+	vmidi->card = card;
+
+	if (snd_midi_devs[dev] > MAX_MIDI_DEVICES) {
+		snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES);
+		snd_midi_devs[dev] = MAX_MIDI_DEVICES;
+	}
+	for (idx = 0; idx < snd_midi_devs[dev]; idx++) {
+		snd_rawmidi_t *rmidi;
+		snd_virmidi_dev_t *rdev;
+		if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0)
+			goto __nodev;
+		rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue);
+		vmidi->midi[idx] = rmidi;
+		strcpy(rmidi->name, "Virtual Raw MIDI");
+		rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+	}
+	
+	strcpy(card->driver, "VirMIDI");
+	strcpy(card->shortname, "VirMIDI");
+	sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
+	if ((err = snd_card_register(card)) == 0) {
+		snd_virmidi_cards[dev] = card;
+		return 0;
+	}
+      __nodev:
+	snd_card_free(card);
+	return err;
+}
+
+static int __init alsa_card_virmidi_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_card_virmidi_probe(dev) < 0) {
+#ifdef MODULE
+			printk(KERN_ERR "Card-VirMIDI #%i not found or device busy\n", dev + 1);
+#endif
+			break;
+		}
+		cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_virmidi_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_virmidi_cards[dev]);
+}
+
+module_init(alsa_card_virmidi_init)
+module_exit(alsa_card_virmidi_exit)
+
+#ifndef MODULE
+
+/* format is: snd-virmidi=snd_enable,snd_index,snd_id,snd_midi_devs */
+
+static int __init alsa_card_virmidi_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_midi_devs[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-virmidi=", alsa_card_virmidi_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/i2c/Makefile linux-2.4.19-pre5-mjc/sound/i2c/Makefile
--- linux/sound/i2c/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/i2c/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,29 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _i2c.o
+
+list-multi   := snd-i2c.o snd-cs8427.o snd-tea6330t.o
+
+export-objs  := i2c.o cs8427.o tea6330t.o
+
+snd-i2c-objs := i2c.o
+snd-cs8427-objs := cs8427.o
+snd-tea6330t-objs := tea6330t.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o
+obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o
+
+include $(TOPDIR)/Rules.make
+
+snd-i2c.o: $(snd-i2c-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-i2c-objs)
+
+snd-cs8427.o: $(snd-cs8427-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs8427-objs)
+
+snd-tea6330t.o: $(snd-tea6330t-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-tea6330t-objs)
diff -Nru linux/sound/i2c/cs8427.c linux-2.4.19-pre5-mjc/sound/i2c/cs8427.c
--- linux/sound/i2c/cs8427.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/i2c/cs8427.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,470 @@
+/*
+ *  Routines for control of the CS8427 via i2c bus
+ *  IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/cs8427.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic");
+MODULE_LICENSE("GPL");
+
+#define chip_t snd_i2c_device_t
+
+#define CS8427_ADDR			(0x20>>1) /* fixed address */
+
+typedef struct {
+	snd_pcm_substream_t *substream;
+	char hw_status[24];		/* hardware status */
+	char def_status[24];		/* default status */
+	char pcm_status[24];		/* PCM private status */
+	char hw_udata[32];
+	snd_kcontrol_t *pcm_ctl;
+} cs8427_stream_t;
+
+typedef struct {
+	unsigned char regmap[0x14];	/* map of first 1 + 13 registers */
+	cs8427_stream_t playback;
+	cs8427_stream_t capture;
+} cs8427_t;
+
+static unsigned char swapbits(unsigned char val)
+{
+	int bit;
+	unsigned char res = 0;
+	for (bit = 0; bit < 8; bit++) {
+		res |= val & 1;
+		res <<= 1;
+		val >>= 1;
+	}
+	return res;
+}
+
+int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr)
+{
+	int res;
+
+	snd_i2c_lock(bus);
+	res = snd_i2c_probeaddr(bus, CS8427_ADDR | (addr & 7));
+	snd_i2c_unlock(bus);
+	return res;
+}
+
+static int snd_cs8427_reg_write(snd_i2c_device_t *device, unsigned char reg, unsigned char val)
+{
+	int err;
+	unsigned char buf[2];
+
+	buf[0] = reg & 0x7f;
+	buf[1] = val;
+	if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) {
+		snd_printk("unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err);
+		return err < 0 ? err : -EREMOTE;
+	}
+	return 0;
+}
+
+static int snd_cs8427_reg_read(snd_i2c_device_t *device, unsigned char reg)
+{
+	int err;
+	unsigned char buf;
+
+	if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
+		snd_printk("unable to send register 0x%x byte to CS8427\n", reg);
+		return err < 0 ? err : -EREMOTE;
+	}
+	if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) {
+		snd_printk("unable to read register 0x%x byte from CS8427\n", reg);
+		return err < 0 ? err : -EREMOTE;
+	}
+	return buf;
+}
+
+static int snd_cs8427_select_corudata(snd_i2c_device_t *device, int udata)
+{
+	cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO);
+	int err;
+
+	udata = udata ? CS8427_BSEL : 0;
+	if (udata != (chip->regmap[CS8427_REG_CSDATABUF] & udata)) {
+		chip->regmap[CS8427_REG_CSDATABUF] &= ~CS8427_BSEL;
+		chip->regmap[CS8427_REG_CSDATABUF] |= udata;
+		err = snd_cs8427_reg_write(device, CS8427_REG_CSDATABUF, chip->regmap[CS8427_REG_CSDATABUF]);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_cs8427_send_corudata(snd_i2c_device_t *device,
+				    int udata,
+				    unsigned char *ndata,
+				    int count)
+{
+	cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO);
+	char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status;
+	char data[32];
+	int err, idx;
+
+	if (!memcmp(hw_data, ndata, count))
+		return 0;
+	if ((err = snd_cs8427_select_corudata(device, udata)) < 0)
+		return err;
+	memcpy(hw_data, data, count);
+	if (udata) {
+		memset(data, 0, sizeof(data));
+		if (memcmp(hw_data, data, 32) == 0) {
+			chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK;
+			chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS | CS8427_EFTUI;
+			if ((err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF, chip->regmap[CS8427_REG_UDATABUF])) < 0)
+				return err;
+			return 0;
+		}
+	}
+	data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
+	for (idx = 0; idx < count; idx++)
+		data[idx + 1] = swapbits(ndata[idx]);
+	if (snd_i2c_sendbytes(device, data, count) != count)
+		return -EREMOTE;
+	return 1;
+}
+
+static void snd_cs8427_free(snd_i2c_device_t *device)
+{
+	if (device->private_data)
+		snd_magic_kfree(device->private_data);
+}
+
+int snd_cs8427_create(snd_i2c_bus_t *bus,
+		      unsigned char addr,
+		      snd_i2c_device_t **r_cs8427)
+{
+	static unsigned char initvals1[] = {
+	  CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC,
+	  /* CS8427_REG_CLOCKSOURCE: RMCK to OMCK, no validity, disable mutes, TCBL=output */
+	  CS8427_SWCLK,
+	  /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs, normal stereo operation */
+	  0x00,
+	  /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial, Rx=>serial */
+	  CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER,
+	  /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base =
+	     covered input clock, recovered input clock source is Envy24 */
+	  CS8427_INC,
+	  /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S, 24-bit, 64*Fsi */
+	  CS8427_SIDEL | CS8427_SILRPOL,
+	  /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format = I2S, 24-bit, 64*Fsi */
+	  CS8427_SODEL | CS8427_SOLRPOL,
+	};
+	static unsigned char initvals2[] = {
+	  CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC,
+	  /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */
+	  CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR,
+	  /* CS8427_REG_CSDATABUF:
+	     Registers 32-55 window to CS buffer
+	     Inhibit D->E transfers from overwriting first 5 bytes of CS data.
+	     Inhibit D->E transfers (all) of CS data.
+	     Allow E->F transfer of CS data.
+	     One byte mode; both A/B channels get same written CB data.
+	     A channel info is output to chip's EMPH* pin. */
+	  CS8427_CBMR | CS8427_DETCI,
+	  /* CS8427_REG_UDATABUF:
+	     Use internal buffer to transmit User (U) data.
+	     Chip's U pin is an output.
+	     Transmit all O's for user data.
+	     Inhibit D->E transfers.
+	     Inhibit E->F transfers. */
+	  CS8427_UD | CS8427_EFTUI | CS8427_DETUI,
+	};
+	int err;
+	cs8427_t *chip;
+	snd_i2c_device_t *device;
+	unsigned char buf[32 + 1];
+
+	if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0)
+		return err;
+	chip = device->private_data = snd_magic_kcalloc(cs8427_t, 0, GFP_KERNEL);
+	if (chip == NULL) {
+	      	snd_i2c_device_free(device);
+		return -ENOMEM;
+	}
+	device->private_free = snd_cs8427_free;
+	
+	snd_i2c_lock(bus);
+	if ((err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER)) != CS8427_VER8427A) {
+		snd_i2c_unlock(bus);
+		snd_printk("unable to find CS8427 signature (expected 0x%x, read 0x%x), initialization is not completed\n", CS8427_VER8427A, err);
+		return -EFAULT;
+	}
+	/* turn off run bit while making changes to configuration */
+	if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00)) < 0)
+		goto __fail;
+	/* send initial values */
+	memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
+	if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) {
+		err = err < 0 ? err : -EREMOTE;
+		goto __fail;
+	}
+	/* Turn off CS8427 interrupt stuff that is not used in hardware */
+	memset(buf, 0, 8);
+	/* from address 9 to 16 */
+	buf[0] = 9;	/* register */
+	if ((err = snd_i2c_sendbytes(device, buf, 8)) != 8)
+		goto __fail;
+	/* send transfer initialization sequence */
+	memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
+	if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) {
+		err = err < 0 ? err : -EREMOTE;
+		goto __fail;
+	}
+	/* write default channel status bytes */
+	buf[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;
+	buf[1] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0));
+	buf[2] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8));
+	buf[3] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16));
+	buf[4] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24));
+	memset(buf + 5, 0, sizeof(buf)-5);
+	memcpy(chip->playback.def_status, buf + 1, 24);
+	memcpy(chip->playback.pcm_status, buf + 1, 24);
+	if ((err = snd_i2c_sendbytes(device, buf, 33)) != 33)
+		goto __fail;
+	/* turn on run bit and rock'n'roll */
+	chip->regmap[CS8427_REG_CLOCKSOURCE] = initvals1[4] | CS8427_RUN;
+	if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE])) < 0)
+		goto __fail;
+
+#if 0	// it's nice for read tests
+	{
+	char buf[128];
+	buf[0] = 0x81;
+	snd_i2c_sendbytes(device, buf, 1);
+	snd_i2c_readbytes(device, buf, 127);
+	}
+#endif
+	
+	snd_i2c_unlock(bus);
+	if (r_cs8427)
+		*r_cs8427 = device;
+	return 0;
+
+      __fail:
+      	snd_i2c_unlock(bus);
+      	snd_i2c_device_free(device);
+      	return err < 0 ? err : -EREMOTE;
+}
+
+static int snd_cs8427_in_status_info(snd_kcontrol_t *kcontrol,
+				     snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_cs8427_in_status_get(snd_kcontrol_t *kcontrol,
+				    snd_ctl_elem_value_t *ucontrol)
+{
+	snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol);
+	int data;
+
+	snd_i2c_lock(device->bus);
+	data = snd_cs8427_reg_read(device, 15);
+	snd_i2c_unlock(device->bus);
+	if (data < 0)
+		return data;
+	ucontrol->value.integer.value[0] = data;
+	return 0;
+}
+
+static int snd_cs8427_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_cs8427_spdif_get(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol);
+	cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO);
+	
+	snd_i2c_lock(device->bus);
+	memcpy(ucontrol->value.iec958.status, chip->playback.def_status, 23);
+	snd_i2c_unlock(device->bus);
+	return 0;
+}
+
+static int snd_cs8427_spdif_put(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol);
+	cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO);
+	unsigned char *status = kcontrol->private_value ? chip->playback.pcm_status : chip->playback.def_status;
+	int err, change;
+
+	snd_i2c_lock(device->bus);
+	change = memcmp(ucontrol->value.iec958.status, status, 23) != 0;
+	memcpy(status, ucontrol->value.iec958.status, 23);
+	if (change && (kcontrol->private_value ? chip->playback.substream != NULL : chip->playback.substream == NULL)) {
+		err = snd_cs8427_send_corudata(device, 0, status, 23);
+		if (err < 0)
+			change = err;
+	}
+	snd_i2c_unlock(device->bus);
+	return change;
+}
+
+static int snd_cs8427_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_cs8427_spdif_mask_get(snd_kcontrol_t * kcontrol,
+				      snd_ctl_elem_value_t * ucontrol)
+{
+	memset(ucontrol->value.iec958.status, 0xff, 23);
+	return 0;
+}
+
+#define CONTROLS (sizeof(snd_cs8427_iec958_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs8427_iec958_controls[] = {
+{
+	iface: SNDRV_CTL_ELEM_IFACE_PCM,
+	info: snd_cs8427_in_status_info,
+	name: "IEC958 CS8427 Input Status",
+	access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	get: snd_cs8427_in_status_get,
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	info:		snd_cs8427_spdif_mask_info,
+	get:		snd_cs8427_spdif_mask_get,
+},
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_cs8427_spdif_info,
+	get:		snd_cs8427_spdif_get,
+	put:		snd_cs8427_spdif_put,
+	private_value:	0
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_cs8427_spdif_info,
+	get:		snd_cs8427_spdif_get,
+	put:		snd_cs8427_spdif_put,
+	private_value:	1
+}};
+
+int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427,
+			    snd_pcm_substream_t *play_substream,
+			    snd_pcm_substream_t *cap_substream)
+{
+	cs8427_t *chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO);
+	snd_kcontrol_t *kctl;
+	int idx, err;
+
+	snd_assert(play_substream && cap_substream, return -EINVAL);
+	for (idx = 0; idx < CONTROLS; idx++) {
+		kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.device = play_substream->pcm->device;
+		kctl->id.subdevice = play_substream->number;
+		err = snd_ctl_add(cs8427->bus->card, kctl);
+		if (err < 0)
+			return err;
+		if (!strcmp(kctl->id.name, SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM)))
+			chip->playback.pcm_ctl = kctl;
+	}
+
+	snd_assert(chip->playback.pcm_ctl, return -EIO);
+	return 0;
+}
+
+int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active)
+{
+	cs8427_t *chip;
+
+	snd_assert(cs8427, return -ENXIO);
+	chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO);
+	if (active)
+		memcpy(chip->playback.pcm_status, chip->playback.def_status, 24);
+	chip->playback.pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(cs8427->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
+					  SNDRV_CTL_EVENT_MASK_INFO, &chip->playback.pcm_ctl->id);
+	return 0;
+}
+
+int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate)
+{
+	cs8427_t *chip;
+	char *status;
+	int err;
+
+	snd_assert(cs8427, return -ENXIO);
+	chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO);
+	status = chip->playback.pcm_status;
+	snd_i2c_lock(cs8427->bus);
+	if (status[0] & IEC958_AES0_PROFESSIONAL) {
+		status[0] &= ~IEC958_AES0_PRO_FS;
+		switch (rate) {
+		case 32000: status[0] |= IEC958_AES0_PRO_FS_32000; break;
+		case 44100: status[0] |= IEC958_AES0_PRO_FS_44100; break;
+		case 48000: status[0] |= IEC958_AES0_PRO_FS_48000; break;
+		default: status[0] |= IEC958_AES0_PRO_FS_NOTID; break;
+		}
+	} else {
+		status[3] &= ~IEC958_AES3_CON_FS;
+		switch (rate) {
+		case 32000: status[3] |= IEC958_AES3_CON_FS_32000; break;
+		case 44100: status[3] |= IEC958_AES3_CON_FS_44100; break;
+		case 48000: status[3] |= IEC958_AES3_CON_FS_48000; break;
+		}
+	}
+	err = snd_cs8427_send_corudata(cs8427, 0, status, 23);
+	if (err > 0)
+		snd_ctl_notify(cs8427->bus->card,
+			       SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->playback.pcm_ctl->id);
+	snd_i2c_unlock(cs8427->bus);
+	return err < 0 ? err : 0;
+}
+
+EXPORT_SYMBOL(snd_cs8427_detect);
+EXPORT_SYMBOL(snd_cs8427_create);
+EXPORT_SYMBOL(snd_cs8427_iec958_build);
+EXPORT_SYMBOL(snd_cs8427_iec958_active);
+EXPORT_SYMBOL(snd_cs8427_iec958_pcm);
diff -Nru linux/sound/i2c/i2c.c linux-2.4.19-pre5-mjc/sound/i2c/i2c.c
--- linux/sound/i2c/i2c.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/i2c/i2c.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,324 @@
+/*
+ *   Generic i2c interface for ALSA
+ *
+ *   (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+ *   Modified for the ALSA driver by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/i2c.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Generic i2c interface for ALSA");
+MODULE_LICENSE("GPL");
+
+static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count);
+static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count);
+static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr);
+
+static snd_i2c_ops_t snd_i2c_bit_ops = {
+	sendbytes: snd_i2c_bit_sendbytes,
+	readbytes: snd_i2c_bit_readbytes,
+	probeaddr: snd_i2c_bit_probeaddr,
+};
+
+static int snd_i2c_bus_free(snd_i2c_bus_t *bus)
+{
+	snd_i2c_bus_t *slave;
+	snd_i2c_device_t *device;
+
+	snd_assert(bus != NULL, return -EINVAL);
+	while (!list_empty(&bus->devices)) {
+		device = snd_i2c_device(bus->devices.next);
+		snd_i2c_device_free(device);
+	}
+	if (bus->master)
+		list_del(&bus->buses);
+	else {
+		while (!list_empty(&bus->buses)) {
+			slave = snd_i2c_slave_bus(bus->buses.next);
+			snd_device_free(bus->card, slave);
+		}
+	}
+	if (bus->private_free)
+		bus->private_free(bus);
+	snd_magic_kfree(bus);
+	return 0;
+}
+
+static int snd_i2c_bus_dev_free(snd_device_t *device)
+{
+	snd_i2c_bus_t *bus = snd_magic_cast(snd_i2c_bus_t, device->device_data, return -ENXIO);
+	return snd_i2c_bus_free(bus);
+}
+
+int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c)
+{
+	snd_i2c_bus_t *bus;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:       snd_i2c_bus_dev_free,
+	};
+
+	*ri2c = NULL;
+	bus = (snd_i2c_bus_t *)snd_magic_kcalloc(snd_i2c_bus_t, 0, GFP_KERNEL);
+	if (bus == NULL)
+		return -ENOMEM;
+	spin_lock_init(&bus->lock);
+	INIT_LIST_HEAD(&bus->devices);
+	INIT_LIST_HEAD(&bus->buses);
+	bus->card = card;
+	bus->ops = &snd_i2c_bit_ops;
+	if (master) {
+		list_add_tail(&bus->buses, &master->buses);
+		bus->master = master;
+	}
+	strncpy(bus->name, name, sizeof(bus->name) - 1);
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) {
+		snd_i2c_bus_free(bus);
+		return err;
+	}
+	*ri2c = bus;
+	return 0;
+}
+
+int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice)
+{
+	snd_i2c_device_t *device;
+
+	*rdevice = NULL;
+	snd_assert(bus != NULL, return -EINVAL);
+	device = (snd_i2c_device_t *)snd_magic_kcalloc(snd_i2c_device_t, 0, GFP_KERNEL);
+	if (device == NULL)
+		return -ENOMEM;
+	device->addr = addr;
+	strncpy(device->name, name, sizeof(device->name)-1);
+	list_add_tail(&device->list, &bus->devices);
+	device->bus = bus;
+	*rdevice = device;
+	return 0;
+}
+
+int snd_i2c_device_free(snd_i2c_device_t *device)
+{
+	if (device->bus)
+		list_del(&device->list);
+	if (device->private_free)
+		device->private_free(device);
+	snd_magic_kfree(device);
+	return 0;
+}
+
+int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	return device->bus->ops->sendbytes(device, bytes, count);
+}
+
+
+int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	return device->bus->ops->readbytes(device, bytes, count);
+}
+
+int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
+{
+	return bus->ops->probeaddr(bus, addr);
+}
+
+/*
+ *  bit-operations
+ */
+
+static inline void snd_i2c_bit_hw_start(snd_i2c_bus_t *bus)
+{
+	if (bus->hw_ops.bit->start)
+		bus->hw_ops.bit->start(bus);
+}
+
+static inline void snd_i2c_bit_hw_stop(snd_i2c_bus_t *bus)
+{
+	if (bus->hw_ops.bit->stop)
+		bus->hw_ops.bit->stop(bus);
+}
+
+static void snd_i2c_bit_direction(snd_i2c_bus_t *bus, int clock, int data)
+{
+	if (bus->hw_ops.bit->direction)
+		bus->hw_ops.bit->direction(bus, clock, data);
+}
+
+static void snd_i2c_bit_set(snd_i2c_bus_t *bus, int clock, int data)
+{
+	bus->hw_ops.bit->setlines(bus, clock, data);
+}
+
+#if 0
+static int snd_i2c_bit_clock(snd_i2c_bus_t *bus)
+{
+	if (bus->hw_ops.bit->getclock)
+		return bus->hw_ops.bit->getclock(bus);
+	return -ENXIO;
+}
+#endif
+
+static int snd_i2c_bit_data(snd_i2c_bus_t *bus, int ack)
+{
+	return bus->hw_ops.bit->getdata(bus, ack);
+}
+
+static void snd_i2c_bit_start(snd_i2c_bus_t *bus)
+{
+	snd_i2c_bit_hw_start(bus);
+	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
+	snd_i2c_bit_set(bus, 1, 1);
+	snd_i2c_bit_set(bus, 1, 0);
+	snd_i2c_bit_set(bus, 0, 0);
+}
+
+static void snd_i2c_bit_stop(snd_i2c_bus_t *bus)
+{
+	snd_i2c_bit_set(bus, 0, 0);
+	snd_i2c_bit_set(bus, 1, 0);
+	snd_i2c_bit_set(bus, 1, 1);
+	snd_i2c_bit_hw_stop(bus);
+}
+
+static void snd_i2c_bit_send(snd_i2c_bus_t *bus, int data)
+{
+	snd_i2c_bit_set(bus, 0, data);
+	snd_i2c_bit_set(bus, 1, data);
+	snd_i2c_bit_set(bus, 0, data);
+}
+
+static int snd_i2c_bit_ack(snd_i2c_bus_t *bus)
+{
+	int ack;
+
+	snd_i2c_bit_set(bus, 0, 1);
+	snd_i2c_bit_set(bus, 1, 1);
+	snd_i2c_bit_direction(bus, 1, 0);	/* SCL - wr, SDA - rd */
+	ack = snd_i2c_bit_data(bus, 1);
+	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
+	snd_i2c_bit_set(bus, 0, 1);
+	return ack ? -EREMOTEIO : 0;
+}
+
+static int snd_i2c_bit_sendbyte(snd_i2c_bus_t *bus, unsigned char data)
+{
+	int i, err;
+
+	for (i = 7; i >= 0; i--)
+		snd_i2c_bit_send(bus, !!(data & (1 << i)));
+	if ((err = snd_i2c_bit_ack(bus)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_i2c_bit_readbyte(snd_i2c_bus_t *bus, int last)
+{
+	int i;
+	unsigned char data = 0;
+
+	snd_i2c_bit_set(bus, 0, 1);
+	snd_i2c_bit_direction(bus, 1, 0);	/* SCL - wr, SDA - rd */
+	for (i = 7; i >= 0; i--) {
+		snd_i2c_bit_set(bus, 1, 1);
+		if (snd_i2c_bit_data(bus, 0))
+			data |= (1 << i);
+		snd_i2c_bit_set(bus, 0, 1);
+	}
+	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
+	snd_i2c_bit_send(bus, !!last);
+	return data;
+}
+
+static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	snd_i2c_bus_t *bus = device->bus;
+	int err, res = 0;
+
+	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
+		return -EIO;		/* not yet implemented */
+	snd_i2c_bit_start(bus);
+	if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0)
+		return err;
+	while (count-- > 0) {
+		if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0)
+			return err;
+		res++;
+	}
+	snd_i2c_bit_stop(bus);
+	return res;
+}
+
+static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	snd_i2c_bus_t *bus = device->bus;
+	int err, res = 0;
+
+	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
+		return -EIO;		/* not yet implemented */
+	snd_i2c_bit_start(bus);
+	if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0)
+		return err;
+	while (count-- > 0) {
+		if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0)
+			return err;
+		*bytes++ = (unsigned char)err;
+		res++;
+	}
+	snd_i2c_bit_stop(bus);
+	return res;
+}
+
+static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
+{
+	int err;
+
+	if (addr & 0x8000)	/* 10-bit address */
+		return -EIO;	/* not yet implemented */
+	if (addr & 0x7f80)	/* invalid address */
+		return -EINVAL;
+	snd_i2c_bit_start(bus);
+	if ((err = snd_i2c_bit_sendbyte(bus, addr << 1)) < 0)
+		return err;
+	snd_i2c_bit_stop(bus);
+	return 1;		/* present */
+}
+
+EXPORT_SYMBOL(snd_i2c_bus_create);
+EXPORT_SYMBOL(snd_i2c_device_create);
+EXPORT_SYMBOL(snd_i2c_device_free);
+EXPORT_SYMBOL(snd_i2c_sendbytes);
+EXPORT_SYMBOL(snd_i2c_readbytes);
+EXPORT_SYMBOL(snd_i2c_probeaddr);
+
+static int __init alsa_i2c_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_i2c_exit(void)
+{
+}
+
+module_init(alsa_i2c_init)
+module_exit(alsa_i2c_exit)
diff -Nru linux/sound/i2c/tea6330t.c linux-2.4.19-pre5-mjc/sound/i2c/tea6330t.c
--- linux/sound/i2c/tea6330t.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/i2c/tea6330t.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,372 @@
+/*
+ *  Routines for control of the TEA6330T circuit via i2c bus
+ *  Sound fader control circuit for car radios by Philips Semiconductors
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/tea6330t.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of the TEA6330T circuit via i2c bus");
+MODULE_LICENSE("GPL");
+
+#define chip_t tea6330t_t
+
+#define TEA6330T_ADDR			(0x80>>1) /* fixed address */
+
+#define TEA6330T_SADDR_VOLUME_LEFT	0x00	/* volume left */
+#define TEA6330T_SADDR_VOLUME_RIGHT	0x01	/* volume right */
+#define TEA6330T_SADDR_BASS		0x02	/* bass control */
+#define TEA6330T_SADDR_TREBLE		0x03	/* treble control */
+#define TEA6330T_SADDR_FADER		0x04	/* fader control */
+#define   TEA6330T_MFN			0x20	/* mute control for selected channels */
+#define   TEA6330T_FCH			0x10	/* select fader channels - front or rear */
+#define TEA6330T_SADDR_AUDIO_SWITCH	0x05	/* audio switch */
+#define   TEA6330T_GMU			0x80	/* mute control, general mute */
+#define   TEA6330T_EQN			0x40	/* equalizer switchover (0=equalizer-on) */
+
+int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer)
+{
+	int res;
+
+	snd_i2c_lock(bus);
+	res = snd_i2c_probeaddr(bus, TEA6330T_ADDR);
+	snd_i2c_unlock(bus);
+	return res;
+}
+
+#if 0
+static void snd_tea6330t_set(tea6330t_t *tea,
+			     unsigned char addr, unsigned char value)
+{
+#if 0
+	printk("set - 0x%x/0x%x\n", addr, value);
+#endif
+	snd_i2c_write(tea->bus, TEA6330T_ADDR, addr, value, 1);
+}
+#endif
+
+#define TEA6330T_MASTER_VOLUME(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_tea6330t_info_master_volume, \
+  get: snd_tea6330t_get_master_volume, put: snd_tea6330t_put_master_volume }
+
+static int snd_tea6330t_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 43;
+	return 0;
+}
+
+static int snd_tea6330t_get_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	
+	snd_i2c_lock(tea->bus);
+	ucontrol->value.integer.value[0] = tea->mleft - 0x14;
+	ucontrol->value.integer.value[1] = tea->mright - 0x14;
+	snd_i2c_unlock(tea->bus);
+	return 0;
+}
+
+static int snd_tea6330t_put_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	int change, count, err;
+	unsigned char bytes[3];
+	unsigned char val1, val2;
+	
+	val1 = (ucontrol->value.integer.value[0] % 44) + 0x14;
+	val2 = (ucontrol->value.integer.value[1] % 44) + 0x14;
+	snd_i2c_lock(tea->bus);
+	change = val1 != tea->mleft || val2 != tea->mright;
+	tea->mleft = val1;
+	tea->mright = val2;
+	count = 0;
+	if (tea->regs[TEA6330T_SADDR_VOLUME_LEFT] != 0) {
+		bytes[count++] = TEA6330T_SADDR_VOLUME_LEFT;
+		bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = tea->mleft;
+	}
+	if (tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] != 0) {
+		if (count == 0)
+			bytes[count++] = TEA6330T_SADDR_VOLUME_RIGHT;
+		bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = tea->mright;
+	}
+	if (count > 0) {
+		if ((err = snd_i2c_sendbytes(tea->device, bytes, count)) < 0)
+			change = err;
+	}
+	snd_i2c_unlock(tea->bus);
+	return change;
+}
+
+#define TEA6330T_MASTER_SWITCH(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_tea6330t_info_master_switch, \
+  get: snd_tea6330t_get_master_switch, put: snd_tea6330t_put_master_switch }
+
+static int snd_tea6330t_info_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_tea6330t_get_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	
+	snd_i2c_lock(tea->bus);
+	ucontrol->value.integer.value[0] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1;
+	ucontrol->value.integer.value[1] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1;
+	snd_i2c_unlock(tea->bus);
+	return 0;
+}
+
+static int snd_tea6330t_put_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	int change, err;
+	unsigned char bytes[3];
+	unsigned char oval1, oval2, val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & 1;
+	val2 = ucontrol->value.integer.value[1] & 1;
+	snd_i2c_lock(tea->bus);
+	oval1 = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1;
+	oval2 = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1;
+	change = val1 != oval1 || val2 != oval2;
+	tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = val1 ? tea->mleft : 0;
+	tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = val2 ? tea->mright : 0;
+	bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
+	bytes[1] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT];
+	bytes[2] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT];
+	if ((err = snd_i2c_sendbytes(tea->device, bytes, 3)) < 0)
+		change = err;
+	snd_i2c_unlock(tea->bus);
+	return change;
+}
+
+#define TEA6330T_BASS(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_tea6330t_info_bass, \
+  get: snd_tea6330t_get_bass, put: snd_tea6330t_put_bass }
+
+static int snd_tea6330t_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = tea->max_bass;
+	return 0;
+}
+
+static int snd_tea6330t_get_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = tea->bass;
+	return 0;
+}
+
+static int snd_tea6330t_put_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	int change, err;
+	unsigned char bytes[2];
+	unsigned char val1;
+	
+	val1 = ucontrol->value.integer.value[0] % (tea->max_bass + 1);
+	snd_i2c_lock(tea->bus);
+	tea->bass = val1;
+	val1 += tea->equalizer ? 7 : 3;
+	change = tea->regs[TEA6330T_SADDR_BASS] != val1;
+	bytes[0] = TEA6330T_SADDR_BASS;
+	bytes[1] = tea->regs[TEA6330T_SADDR_BASS] = val1;
+	if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0)
+		change = err;
+	snd_i2c_unlock(tea->bus);
+	return change;
+}
+
+#define TEA6330T_TREBLE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_tea6330t_info_treble, \
+  get: snd_tea6330t_get_treble, put: snd_tea6330t_put_treble }
+
+static int snd_tea6330t_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = tea->max_treble;
+	return 0;
+}
+
+static int snd_tea6330t_get_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = tea->treble;
+	return 0;
+}
+
+static int snd_tea6330t_put_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	tea6330t_t *tea = snd_kcontrol_chip(kcontrol);
+	int change, err;
+	unsigned char bytes[2];
+	unsigned char val1;
+	
+	val1 = ucontrol->value.integer.value[0] % (tea->max_treble + 1);
+	snd_i2c_lock(tea->bus);
+	tea->treble = val1;
+	val1 += 3;
+	change = tea->regs[TEA6330T_SADDR_TREBLE] != val1;
+	bytes[0] = TEA6330T_SADDR_TREBLE;
+	bytes[1] = tea->regs[TEA6330T_SADDR_TREBLE] = val1;
+	if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0)
+		change = err;
+	snd_i2c_unlock(tea->bus);
+	return change;
+}
+
+#define TEA6330T_CONTROLS (sizeof(snd_tea6330t_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_tea6330t_controls[] = {
+TEA6330T_MASTER_SWITCH("Master Playback Switch", 0),
+TEA6330T_MASTER_VOLUME("Master Playback Volume", 0),
+TEA6330T_BASS("Tone Control - Bass", 0),
+TEA6330T_TREBLE("Tone Control - Treble", 0)
+};
+
+static void snd_tea6330_free(snd_i2c_device_t *device)
+{
+	tea6330t_t *tea = snd_magic_cast(tea6330t_t, device->private_data, return);
+	snd_magic_kfree(tea);
+}
+                                        
+int snd_tea6330t_update_mixer(snd_card_t * card,
+			      snd_i2c_bus_t *bus,
+			      int equalizer, int fader)
+{
+	snd_i2c_device_t *device;
+	tea6330t_t *tea;
+	snd_kcontrol_new_t *knew;
+	int idx, err = -ENOMEM;
+	u8 default_treble, default_bass;
+	unsigned char bytes[7];
+
+	tea = snd_magic_kcalloc(tea6330t_t, 0, GFP_KERNEL);
+	if (tea == NULL)
+		return -ENOMEM;
+	if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) {
+		snd_magic_kfree(tea);
+		return err;
+	}
+	tea->device = device;
+	tea->bus = bus;
+	tea->equalizer = equalizer;
+	tea->fader = fader;
+	device->private_data = tea;
+	device->private_free = snd_tea6330_free;
+
+	snd_i2c_lock(bus);
+
+	/* turn fader off and handle equalizer */
+	tea->regs[TEA6330T_SADDR_FADER] = 0x3f;
+	tea->regs[TEA6330T_SADDR_AUDIO_SWITCH] = equalizer ? 0 : TEA6330T_EQN;
+	/* initialize mixer */
+	if (!tea->equalizer) {
+		tea->max_bass = 9;
+		tea->max_treble = 8;
+		default_bass = 3 + 4;
+		tea->bass = 4;
+		default_treble = 3 + 4;
+		tea->treble = 4;
+	} else {
+		tea->max_bass = 5;
+		tea->max_treble = 0;
+		default_bass = 7 + 4;
+		tea->bass = 4;
+		default_treble = 3;
+		tea->treble = 0;
+	}
+	tea->mleft = tea->mright = 0x14;
+	tea->regs[TEA6330T_SADDR_BASS] = default_bass;
+	tea->regs[TEA6330T_SADDR_TREBLE] = default_treble;
+
+	/* compose I2C message and put the hardware to initial state */
+	bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
+	for (idx = 0; idx < 6; idx++)
+		bytes[idx+1] = tea->regs[idx];
+	if ((err = snd_i2c_sendbytes(device, bytes, 7)) < 0)
+		goto __error;
+
+	strcat(card->mixername, ",TEA6330T");
+	if ((err = snd_component_add(card, "TEA6330T")) < 0)
+		goto __error;
+
+	for (idx = 0; idx < TEA6330T_CONTROLS; idx++) {
+		knew = &snd_tea6330t_controls[idx];
+		if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble"))
+			continue;
+		if ((err = snd_ctl_add(card, snd_ctl_new1(knew, tea))) < 0)
+			goto __error;
+	}
+
+	snd_i2c_unlock(bus);
+	return 0;
+	
+      __error:
+      	snd_i2c_unlock(bus);
+      	snd_i2c_device_free(device);
+      	return err;
+}
+
+EXPORT_SYMBOL(snd_tea6330t_detect);
+EXPORT_SYMBOL(snd_tea6330t_update_mixer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_tea6330t_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_tea6330t_exit(void)
+{
+}
+
+module_init(alsa_tea6330t_init)
+module_exit(alsa_tea6330t_exit)
diff -Nru linux/sound/isa/Config.help linux-2.4.19-pre5-mjc/sound/isa/Config.help
--- linux/sound/isa/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/Config.help	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,99 @@
+CONFIG_SND_AD1816A
+  Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or
+  compatible sound chips.
+
+CONFIG_SND_AD1848
+  Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 
+  (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips
+  from Cirrus Logic, use CS4231, CS4232 or CS4236+ driver.
+
+CONFIG_SND_CS4231
+  Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic -
+  Crystal Semiconductors.
+
+CONFIG_SND_CS4232
+  Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic -
+  Crystal Semiconductors.
+
+CONFIG_SND_CS4236
+  Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239
+  chips from Cirrus Logic - Crystal Semiconductors.
+
+CONFIG_SND_ES968
+  Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip.
+
+CONFIG_SND_ES1688
+  Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips.
+
+CONFIG_SND_ES18XX
+  Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips.
+
+CONFIG_SND_GUSCLASSIC
+  Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard.
+
+CONFIG_SND_GUSEXTREME
+  Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard.
+
+CONFIG_SND_GUSMAX
+  Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard.
+
+CONFIG_SND_INTERWAVE
+  Say 'Y' or 'M' to include support for AMD InterWave based soundcards
+  (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro,
+  Panasonic PCA761AW).
+
+CONFIG_SND_INTERWAVE_STB
+  Say 'Y' or 'M' to include support for AMD InterWave based soundcards
+  with TEA6330T bass and treble regulator (UltraSound 32-Pro).
+
+CONFIG_SND_OPTI92X_AD1848
+  Say 'Y' or 'M' to include support for Opti92x soundcards equiped with
+  AD1848 codec.
+
+CONFIG_SND_OPTI92X_CS4231
+  Say 'Y' or 'M' to include support for Opti92x soundcards equiped with
+  CS4231 codec.
+
+CONFIG_SND_OPTI93X
+  Say 'Y' or 'M' to include support for Opti93x soundcards.
+
+CONFIG_SND_SB8
+  Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit)
+  soundcards or 100% compatible from Creative.
+
+CONFIG_SND_SB16
+  Say 'Y' or 'M' to include support for Sound Blaster 16 (including
+  Plug and Play version).
+
+CONFIG_SND_SBAWE
+  Say 'Y' or 'M' to include support for Sound Blaster AWE (including
+  Plug and Play version).
+
+CONFIG_SND_SB16_CSP
+  Say 'Y' to include support for CSP core. This special coprocessor
+  can do variable tasks like various compression and decompression
+  algorithms.
+
+CONFIG_SND_WAVEFRONT
+  Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez
+  and Tropez+ soundcards based on Wavefront chip.
+
+CONFIG_SND_ALS100
+  Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110,
+  ALS120 and ALS200 soundcards.
+
+CONFIG_SND_AZT2320
+  Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard.
+
+CONFIG_SND_CMI8330
+  Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards.
+
+CONFIG_SND_DT0197H
+  Say 'Y' or 'M' to include support for Diamond Technologies DT-0197H
+  soundcards.
+
+CONFIG_SND_OPL3SA2
+  Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips.
+
+CONFIG_SND_SGALAXY
+  Say 'Y' or 'M' to include support for Aztech Sound Galaxy.
diff -Nru linux/sound/isa/Config.in linux-2.4.19-pre5-mjc/sound/isa/Config.in
--- linux/sound/isa/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/Config.in	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,36 @@
+# ALSA ISA drivers
+
+mainmenu_option next_comment
+comment 'ISA devices'
+
+dep_tristate 'Analog Devices SoundPort AD1816A' CONFIG_SND_AD1816A $CONFIG_SND $CONFIG_ISAPNP
+dep_tristate 'Generic AD1848/CS4248 driver' CONFIG_SND_AD1848 $CONFIG_SND
+dep_tristate 'Generic Cirrus Logic CS4231 driver' CONFIG_SND_CS4231 $CONFIG_SND
+dep_tristate 'Generic Cirrus Logic CS4232 driver' CONFIG_SND_CS4232 $CONFIG_SND
+dep_tristate 'Generic Cirrus Logic CS4236+ driver' CONFIG_SND_CS4236 $CONFIG_SND
+dep_tristate 'Generic ESS ES968 driver' CONFIG_SND_ES968 $CONFIG_SND $CONFIG_ISAPNP
+dep_tristate 'Generic ESS ES688/ES1688 driver' CONFIG_SND_ES1688 $CONFIG_SND
+dep_tristate 'Generic ESS ES18xx driver' CONFIG_SND_ES18XX $CONFIG_SND
+dep_tristate 'Gravis UltraSound Classic' CONFIG_SND_GUSCLASSIC $CONFIG_SND
+dep_tristate 'Gravis UltraSound Extreme' CONFIG_SND_GUSEXTREME $CONFIG_SND
+dep_tristate 'Gravis UltraSound MAX' CONFIG_SND_GUSMAX $CONFIG_SND
+dep_tristate 'AMD InterWave, Gravis UltraSound PnP' CONFIG_SND_INTERWAVE $CONFIG_SND
+dep_tristate 'AMD InterWave + TEA6330T (UltraSound 32-Pro)' CONFIG_SND_INTERWAVE_STB $CONFIG_SND
+dep_tristate 'OPTi 82C92x - AD1848' CONFIG_SND_OPTI92X_AD1848 $CONFIG_SND
+dep_tristate 'OPTi 82C92x - CS4231' CONFIG_SND_OPTI92X_CS4231 $CONFIG_SND
+dep_tristate 'OPTi 82C93x' CONFIG_SND_OPTI93X $CONFIG_SND
+dep_tristate 'Sound Blaster 1.0/2.0/Pro (8-bit)' CONFIG_SND_SB8 $CONFIG_SND
+dep_tristate 'Sound Blaster 16 (PnP)' CONFIG_SND_SB16 $CONFIG_SND
+dep_tristate 'Sound Blaster AWE (32,64) (PnP)' CONFIG_SND_SBAWE $CONFIG_SND
+if [ "$CONFIG_SND_SB16" != "n" -o "$CONFIG_SND_SBAWE" != "n" ]; then
+  bool '  Sound Blaster 16/AWE CSP support' CONFIG_SND_SB16_CSP
+fi
+dep_tristate 'Turtle Beach Maui,Tropez,Tropez+ (Wavefront)' CONFIG_SND_WAVEFRONT $CONFIG_SND
+dep_tristate 'Avance Logic ALS100/ALS120' CONFIG_SND_ALS100 $CONFIG_SND $CONFIG_ISAPNP
+dep_tristate 'Aztech Systems AZT2320' CONFIG_SND_AZT2320 $CONFIG_SND $CONFIG_ISAPNP
+dep_tristate 'C-Media CMI8330' CONFIG_SND_CMI8330 $CONFIG_SND
+dep_tristate 'Diamond Technologies DT-0197H' CONFIG_SND_DT0197H $CONFIG_SND $CONFIG_ISAPNP
+dep_tristate 'Yamaha OPL3-SA2/SA3' CONFIG_SND_OPL3SA2 $CONFIG_SND
+dep_tristate 'Aztech Sound Galaxy' CONFIG_SND_SGALAXY $CONFIG_SND
+
+endmenu
diff -Nru linux/sound/isa/Makefile linux-2.4.19-pre5-mjc/sound/isa/Makefile
--- linux/sound/isa/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,52 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := isa.o
+
+subdir-y     := ad1816a ad1848 cs423x es1688 gus opti9xx sb wavefront
+subdir-m     := $(subdir-y)
+
+list-multi   := snd-als100.o snd-azt2320.o snd-cmi8330.o snd-dt0197h.o \
+		snd-es18xx.o snd-opl3sa2.o snd-sgalaxy.o
+
+snd-als100-objs := als100.o
+snd-azt2320-objs := azt2320.o
+snd-cmi8330-objs := cmi8330.o
+snd-dt0197h-objs := dt0197h.o
+snd-es18xx-objs := es18xx.o
+snd-opl3sa2-objs := opl3sa2.o
+snd-sgalaxy-objs := sgalaxy.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS100) += snd-als100.o
+obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
+obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
+obj-$(CONFIG_SND_DT0197H) += snd-dt0197h.o
+obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
+obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
+
+include $(TOPDIR)/Rules.make
+
+snd-als100.o: $(snd-als100-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-als100-objs)
+
+snd-azt2320.o: $(snd-azt2320-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-azt2320-objs)
+
+snd-cmi8330.o: $(snd-cmi8330-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cmi8330-objs)
+
+snd-dt0197h.o: $(snd-dt0197h-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-dt0197h-objs)
+
+snd-es18xx.o: $(snd-es18xx-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-es18xx-objs)
+
+snd-opl3sa2.o: $(snd-opl3sa2-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3sa2-objs)
+
+snd-sgalaxy.o: $(snd-sgalaxy-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sgalaxy-objs)
diff -Nru linux/sound/isa/ad1816a/Makefile linux-2.4.19-pre5-mjc/sound/isa/ad1816a/Makefile
--- linux/sound/isa/ad1816a/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/ad1816a/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,24 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _ad1816a.o
+
+list-multi   := snd-ad1816a-lib.o snd-ad1816a.o
+
+export-objs  := ad1816a_lib.o
+
+snd-ad1816a-lib-objs := ad1816a_lib.o
+snd-ad1816a-objs := ad1816a.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o snd-ad1816a-lib.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ad1816a-lib.o: $(snd-ad1816a-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-lib-objs)
+
+snd-ad1816a.o: $(snd-ad1816a-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-objs)
diff -Nru linux/sound/isa/ad1816a/ad1816a.c linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a.c
--- linux/sound/isa/ad1816a/ad1816a.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,399 @@
+
+/*
+    card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
+    Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    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
+*/
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/ad1816a.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+#define chip_t ad1816a_t
+
+#define PFX "ad1816a: "
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("AD1816A, AD1815");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Highscreen,Sound-Boostar 16 3D},"
+		"{Analog Devices,AD1815},"
+		"{Analog Devices,AD1816A},"
+		"{TerraTec,Base 64},"
+		"{TerraTec,AudioSystem EWS64S},"
+		"{Aztech/Newcom SC-16 3D},"
+		"{Shark Predator ISA}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 1-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ad1816a based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ad1816a based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ad1816a based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "1st DMA # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "2nd DMA # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+struct snd_card_ad1816a {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_ad1816a_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_ad1816a_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_ad1816a_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_AD1816A(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+			 ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
+	}
+
+static struct isapnp_card_id snd_ad1816a_pnpids[] __devinitdata = {
+	/* Highscreen Sound-Boostar 16 3D */
+	ISAPNP_AD1816A('M','D','K',0x1605,'A','D','S',0x7180,0x7181),
+	/* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */
+	ISAPNP_AD1816A('L','W','C',0x1061,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1815 */
+	ISAPNP_AD1816A('A','D','S',0x7150,'A','D','S',0x7150,0x7151),
+	/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
+	ISAPNP_AD1816A('A','D','S',0x7181,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1816A - Terratec Base 64 */
+	ISAPNP_AD1816A('T','E','R',0x1411,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1816A - Terratec AudioSystem EWS64S */
+	ISAPNP_AD1816A('T','E','R',0x1112,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
+	ISAPNP_AD1816A('A','Z','T',0x1022,'A','Z','T',0x1018,0x2002),
+	/* Shark Predator ISA - added by Ken Arromdee */
+	ISAPNP_AD1816A('S','M','M',0x7180,'A','D','S',0x7180,0x7181),
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_ad1816a_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define	DRIVER_NAME	"snd-card-ad1816a"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_ad1816a_isapnp(int dev,
+					  struct snd_card_ad1816a *acard)
+{
+	const struct isapnp_card_id *id = snd_ad1816a_isapnp_id[dev];
+	struct isapnp_card *card = snd_ad1816a_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_port[dev], 16);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev],
+			1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev) < 0) {
+		printk(KERN_ERR PFX "AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[2].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev) < 0) {
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+		return 0;
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev) < 0) {
+		/* not fatal error */
+		printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_ad1816a_deactivate(struct snd_card_ad1816a *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+	if (acard->devmpu) {
+		acard->devmpu->deactivate(acard->devmpu);
+		acard->devmpu = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void snd_card_ad1816a_free(snd_card_t *card)
+{
+	struct snd_card_ad1816a *acard = (struct snd_card_ad1816a *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_ad1816a_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_ad1816a_probe(int dev)
+{
+	int error;
+	snd_card_t *card;
+	struct snd_card_ad1816a *acard;
+	ad1816a_t *chip;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_ad1816a))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_ad1816a *)card->private_data;
+	card->private_free = snd_card_ad1816a_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_ad1816a_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	printk(KERN_ERR PFX "you have to enable ISA PnP support.\n");
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_ad1816a_create(card, snd_port[dev],
+					snd_irq[dev],
+					snd_dma1[dev],
+					snd_dma2[dev],
+					&chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_ad1816a_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+					snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT,
+					NULL) < 0)
+			printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "AD1816A");
+	strcpy(card->shortname, "ADI SoundPort AD1816A");
+	sprintf(card->longname, "%s soundcard, SS at 0x%lx, irq %d, dma %d&%d",
+		card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]);
+
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_ad1816a_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_ad1816a_isapnp_detect(struct isapnp_card *card,
+					    const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_ad1816a_isapnp_cards[dev] = card;
+		snd_ad1816a_isapnp_id[dev] = id;
+		res = snd_card_ad1816a_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+        return -ENODEV;
+}
+#endif
+
+static int __init alsa_card_ad1816a_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_ad1816a_pnpids, snd_ad1816a_isapnp_detect);
+#else
+	printk(KERN_ERR PFX "you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		printk(KERN_ERR "no AD1816A based soundcards found.\n");
+#endif	/* MODULE */
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_ad1816a_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_ad1816a_cards[dev]);
+}
+
+module_init(alsa_card_ad1816a_init)
+module_exit(alsa_card_ad1816a_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ad1816a=snd_enable,snd_index,snd_id,snd_port,
+			  snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq,
+			  snd_dma1,snd_dma2 */
+
+static int __init alsa_card_ad1816a_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-ad1816a=", alsa_card_ad1816a_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/ad1816a/ad1816a_lib.c linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a_lib.c
--- linux/sound/isa/ad1816a/ad1816a_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,965 @@
+
+/*
+    ad1816a.c - lowlevel code for Analog Devices AD1816A chip.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    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
+*/
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/ad1816a.h>
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip");
+MODULE_LICENSE("GPL");
+
+#define chip_t ad1816a_t
+
+static inline int snd_ad1816a_busy_wait(ad1816a_t *chip)
+{
+	int timeout;
+
+	for (timeout = 1000; timeout-- > 0; udelay(10))
+		if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY)
+			return 0;
+
+	snd_printk("chip busy.\n");
+	return -EBUSY;
+}
+
+inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg)
+{
+	snd_ad1816a_busy_wait(chip);
+	return inb(AD1816A_REG(reg));
+}
+
+inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg,
+			    unsigned char value)
+{
+	snd_ad1816a_busy_wait(chip);
+	outb(value, AD1816A_REG(reg));
+}
+
+inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg,
+				 unsigned char mask, unsigned char value)
+{
+	snd_ad1816a_out(chip, reg,
+		(value & mask) | (snd_ad1816a_in(chip, reg) & ~mask));
+}
+
+static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg)
+{
+	snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f);
+	return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) |
+		(snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8);
+}
+
+static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg,
+			      unsigned short value)
+{
+	snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f);
+	snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff);
+	snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff);
+}
+
+static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg,
+				   unsigned short mask, unsigned short value)
+{
+	snd_ad1816a_write(chip, reg,
+		(value & mask) | (snd_ad1816a_read(chip, reg) & ~mask));
+}
+
+
+static unsigned char snd_ad1816a_get_format(ad1816a_t *chip,
+					    unsigned int format, int channels)
+{
+	unsigned char retval = AD1816A_FMT_LINEAR_8;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		retval = AD1816A_FMT_ULAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		retval = AD1816A_FMT_ALAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		retval = AD1816A_FMT_LINEAR_16_LIT;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		retval = AD1816A_FMT_LINEAR_16_BIG;
+	}
+	return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval;
+}
+
+static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (chip->mode & mode) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return -EAGAIN;
+	}
+
+	switch ((mode &= AD1816A_MODE_OPEN)) {
+	case AD1816A_MODE_PLAYBACK:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_PLAYBACK_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff);
+		break;
+	case AD1816A_MODE_CAPTURE:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_CAPTURE_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_CAPTURE_IRQ_ENABLE, 0xffff);
+		break;
+	case AD1816A_MODE_TIMER:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_TIMER_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_TIMER_IRQ_ENABLE, 0xffff);
+	}
+	chip->mode |= mode;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	switch ((mode &= AD1816A_MODE_OPEN)) {
+	case AD1816A_MODE_PLAYBACK:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_PLAYBACK_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000);
+		break;
+	case AD1816A_MODE_CAPTURE:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_CAPTURE_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_CAPTURE_IRQ_ENABLE, 0x0000);
+		break;
+	case AD1816A_MODE_TIMER:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_TIMER_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_TIMER_IRQ_ENABLE, 0x0000);
+	}
+	if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN))
+		chip->mode = 0;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what,
+			       int channel, int cmd)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock(&chip->lock);
+		cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;
+		if (what & AD1816A_PLAYBACK_ENABLE)
+			snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+				AD1816A_PLAYBACK_ENABLE, cmd);
+		if (what & AD1816A_CAPTURE_ENABLE)
+			snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+				AD1816A_CAPTURE_ENABLE, cmd);
+		spin_unlock(&chip->lock);
+		break;
+	default:
+		snd_printk("invalid trigger mode 0x%x.\n", what);
+		error = -EINVAL;
+	}
+
+	return error;
+}
+
+static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE,
+		SNDRV_PCM_STREAM_PLAYBACK, cmd);
+}
+
+static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE,
+		SNDRV_PCM_STREAM_CAPTURE, cmd);
+}
+
+static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+		AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00);
+
+	snd_dma_program(chip->dma1, runtime->dma_addr, size,
+			DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate);
+	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+		AD1816A_FMT_ALL | AD1816A_FMT_STEREO,
+		snd_ad1816a_get_format(chip, runtime->format,
+			runtime->channels));
+
+	snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT,
+		snd_pcm_lib_period_bytes(substream) / 4 - 1);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+		AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00);
+
+	snd_dma_program(chip->dma2, runtime->dma_addr, size,
+			DMA_MODE_READ | DMA_AUTOINIT);
+
+	snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate);
+	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+		AD1816A_FMT_ALL | AD1816A_FMT_STEREO,
+		snd_ad1816a_get_format(chip, runtime->format,
+			runtime->channels));
+
+	snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT,
+		snd_pcm_lib_period_bytes(substream) / 4 - 1);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+
+static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	if (!(chip->mode & AD1816A_MODE_PLAYBACK))
+		return 0;
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma1);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	if (!(chip->mode & AD1816A_MODE_CAPTURE))
+		return 0;
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+
+static void snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, dev_id, return);
+	unsigned char status;
+
+	spin_lock(&chip->lock);
+	status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);
+	spin_unlock(&chip->lock);
+
+	if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream)
+		snd_pcm_period_elapsed(chip->playback_substream);
+
+	if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream)
+		snd_pcm_period_elapsed(chip->capture_substream);
+
+	if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer)
+		snd_timer_interrupt(chip->timer, chip->timer->sticks);
+
+	spin_lock(&chip->lock);
+	snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
+	spin_unlock(&chip->lock);
+}
+
+
+static snd_pcm_hardware_t snd_ad1816a_playback = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		55200,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ad1816a_capture = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		55200,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_ad1816a_timer_close(snd_timer_t *timer)
+{
+	ad1816a_t *chip = snd_timer_chip(timer);
+	snd_ad1816a_close(chip, AD1816A_MODE_TIMER);
+	return 0;
+}
+
+static int snd_ad1816a_timer_open(snd_timer_t *timer)
+{
+	ad1816a_t *chip = snd_timer_chip(timer);
+	snd_ad1816a_open(chip, AD1816A_MODE_TIMER);
+	return 0;
+}
+
+static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer)
+{
+	snd_assert(timer != NULL, return 0);
+
+	return 10000;
+}
+
+static int snd_ad1816a_timer_start(snd_timer_t *timer)
+{
+	unsigned short bits;
+	unsigned long flags;
+	ad1816a_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->lock, flags);
+	bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE);
+
+	if (!(bits & AD1816A_TIMER_ENABLE)) {
+		snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT,
+			timer->sticks & 0xffff);
+
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_TIMER_ENABLE, 0xffff);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_ad1816a_timer_stop(snd_timer_t *timer)
+{
+	unsigned long flags;
+	ad1816a_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->lock, flags);
+
+	snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+		AD1816A_TIMER_ENABLE, 0x0000);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static struct _snd_timer_hardware snd_ad1816a_timer_table = {
+	flags:		SNDRV_TIMER_HW_AUTO,
+	resolution:	10000,
+	ticks:		65535,
+	open:		snd_ad1816a_timer_open,
+	close:		snd_ad1816a_timer_close,
+	c_resolution:	snd_ad1816a_timer_resolution,
+	start:		snd_ad1816a_timer_start,
+	stop:		snd_ad1816a_timer_stop,
+};
+
+
+static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int error;
+
+	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0)
+		return error;
+	snd_pcm_set_sync(substream);
+	runtime->hw = snd_ad1816a_playback;
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+	chip->playback_substream = substream;
+	return 0;
+}
+
+static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int error;
+
+	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0)
+		return error;
+	snd_pcm_set_sync(substream);
+	runtime->hw = snd_ad1816a_capture;
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+	chip->capture_substream = substream;
+	return 0;
+}
+
+static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK);
+	return 0;
+}
+
+static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE);
+	return 0;
+}
+
+
+static void snd_ad1816a_init(ad1816a_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
+	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+		AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00);
+	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+		AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00);
+	snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000);
+	snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG,
+		AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff);
+	snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000);
+	snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int snd_ad1816a_probe(ad1816a_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) {
+	case 0:
+		chip->hardware = AD1816A_HW_AD1815;
+		break;
+	case 1:
+		chip->hardware = AD1816A_HW_AD18MAX10;
+		break;
+	case 3:
+		chip->hardware = AD1816A_HW_AD1816A;
+		break;
+	default:
+		chip->hardware = AD1816A_HW_AUTO;
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_ad1816a_free(ad1816a_t *chip)
+{
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma1 >= 0) {
+		snd_dma_disable(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (chip->dma2 >= 0) {
+		snd_dma_disable(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_ad1816a_dev_free(snd_device_t *device)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, device->device_data, return -ENXIO);
+	return snd_ad1816a_free(chip);
+}
+
+static const char *snd_ad1816a_chip_id(ad1816a_t *chip)
+{
+	switch (chip->hardware) {
+	case AD1816A_HW_AD1816A: return "AD1816A";
+	case AD1816A_HW_AD1815:	return "AD1815";
+	case AD1816A_HW_AD18MAX10: return "AD18max10";
+	default:
+		snd_printk("Unknown chip version %d:%d.\n",
+			chip->version, chip->hardware);
+		return "AD1816A - unknown";
+	}
+}
+
+int snd_ad1816a_create(snd_card_t *card,
+		       unsigned long port, int irq, int dma1, int dma2,
+		       ad1816a_t **rchip)
+{
+        static snd_device_ops_t ops = {
+		dev_free:       snd_ad1816a_dev_free,
+	};
+	int error;
+	ad1816a_t *chip;
+
+	*rchip = NULL;
+
+	chip = snd_magic_kcalloc(ad1816a_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->irq = -1;
+	chip->dma1 = -1;
+	chip->dma2 = -1;
+
+	if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (request_dma(dma1, "AD1816A - 1")) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+	if (request_dma(dma2, "AD1816A - 2")) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	chip->dma2 = dma2;
+
+	chip->card = card;
+	chip->port = port;
+	spin_lock_init(&chip->lock);
+
+	if ((error = snd_ad1816a_probe(chip))) {
+		snd_ad1816a_free(chip);
+		return error;
+	}
+
+	snd_ad1816a_init(chip);
+
+	/* Register device */
+	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_ad1816a_free(chip);
+		return error;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ad1816a_playback_ops = {
+	open:		snd_ad1816a_playback_open,
+	close:		snd_ad1816a_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ad1816a_hw_params,
+	hw_free:	snd_ad1816a_hw_free,
+	prepare:	snd_ad1816a_playback_prepare,
+	trigger:	snd_ad1816a_playback_trigger,
+	pointer:	snd_ad1816a_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ad1816a_capture_ops = {
+	open:		snd_ad1816a_capture_open,
+	close:		snd_ad1816a_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ad1816a_hw_params,
+	hw_free:	snd_ad1816a_hw_free,
+	prepare:	snd_ad1816a_capture_prepare,
+	trigger:	snd_ad1816a_capture_trigger,
+	pointer:	snd_ad1816a_capture_pointer,
+};
+
+static void snd_ad1816a_pcm_free(snd_pcm_t *pcm)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm)
+{
+	int error;
+	snd_pcm_t *pcm;
+
+	if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm)))
+		return error;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_ad1816a_pcm_free;
+	pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0;
+
+	strcpy(pcm->name, snd_ad1816a_chip_id(chip));
+	snd_ad1816a_init(chip);
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static void snd_ad1816a_timer_free(snd_timer_t *timer)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, timer->private_data, return);
+	chip->timer = NULL;
+}
+
+int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer)
+{
+	snd_timer_t *timer;
+	snd_timer_id_t tid;
+	int error;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = chip->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0)
+		return error;
+	strcpy(timer->name, snd_ad1816a_chip_id(chip));
+	timer->private_data = chip;
+	timer->private_free = snd_ad1816a_timer_free;
+	chip->timer = timer;
+	timer->hw = snd_ad1816a_timer_table;
+	if (rtimer)
+		*rtimer = timer;
+	return 0;
+}
+
+/*
+ *
+ */
+
+static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Line", "Mix", "CD", "Synth", "Video",
+		"Mic", "Phone",
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 7;
+	if (uinfo->value.enumerated.item > 6)
+		uinfo->value.enumerated.item = 6;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short val;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	ucontrol->value.enumerated.item[0] = (val >> 12) & 7;
+	ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
+	return 0;
+}
+
+static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short val;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 6 ||
+	    ucontrol->value.enumerated.item[1] > 6)
+		return -EINVAL;
+	val = (ucontrol->value.enumerated.item[0] << 12) |
+	      (ucontrol->value.enumerated.item[1] << 4);
+	spin_lock_irqsave(&chip->lock, flags);
+	change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val;
+	snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_single, \
+  get: snd_ad1816a_get_single, put: snd_ad1816a_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short old_val, val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->lock, flags);
+	old_val = snd_ad1816a_read(chip, reg);
+	val = (old_val & ~(mask << shift)) | val;
+	change = val != old_val;
+	snd_ad1816a_write(chip, reg, val);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_double, \
+  get: snd_ad1816a_get_double, put: snd_ad1816a_put_double, \
+  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+
+static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	val = snd_ad1816a_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short old_val, val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->lock, flags);
+	old_val = snd_ad1816a_read(chip, reg);
+	val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+	change = val1 != old_val;
+	snd_ad1816a_write(chip, reg, val1);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#define AD1816A_CONTROLS (sizeof(snd_ad1816a_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ad1816a_controls[] = {
+AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1),
+AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1),
+AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1),
+AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 63, 1),
+AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0),
+AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1),
+AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1),
+AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1),
+AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_ad1816a_info_mux,
+	get: snd_ad1816a_get_mux,
+	put: snd_ad1816a_put_mux,
+},
+AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1),
+AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0),
+AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
+AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
+};
+                                        
+int snd_ad1816a_mixer(ad1816a_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, snd_ad1816a_chip_id(chip));
+
+	for (idx = 0; idx < AD1816A_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_ad1816a_create);
+EXPORT_SYMBOL(snd_ad1816a_pcm);
+EXPORT_SYMBOL(snd_ad1816a_mixer);
+
+static int __init alsa_ad1816a_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ad1816a_exit(void)
+{
+}
+
+module_init(alsa_ad1816a_init)
+module_exit(alsa_ad1816a_exit)
+
diff -Nru linux/sound/isa/ad1848/Makefile linux-2.4.19-pre5-mjc/sound/isa/ad1848/Makefile
--- linux/sound/isa/ad1848/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/ad1848/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,27 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _ad1848.o
+
+list-multi   := snd-ad1848-lib.o snd-ad1848.o
+
+export-objs  := ad1848_lib.o
+
+snd-ad1848-lib-objs := ad1848_lib.o
+snd-ad1848-objs := ad1848.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o
+obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o
+obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ad1848-lib.o: $(snd-ad1848-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-lib-objs)
+
+snd-ad1848.o: $(snd-ad1848-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-objs)
diff -Nru linux/sound/isa/ad1848/ad1848.c linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848.c
--- linux/sound/isa/ad1848/ad1848.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,179 @@
+/*
+ *  Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha)
+ *  Copyright (c) by Tugrul Galatali <galatalt@stuy.edu>,
+ *                   Jaroslav Kysela <perex@suse.cz>
+ *  Based on card-4232.c by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/ad1848.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t ad1848_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Tugrul Galatali <galatalt@stuy.edu>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("AD1848/AD1847/CS4248");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Analog Devices,AD1848},"
+	        "{Analog Devices,AD1847},"
+		"{Crystal Semiconductors,CS4248}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for AD1848 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for AD1848 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable AD1848 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for AD1848 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for AD1848 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for AD1848 driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+
+static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_card_ad1848_probe(int dev)
+{
+	snd_card_t *card;
+	ad1848_t *chip;
+	snd_pcm_t *pcm;
+	int err;
+
+	if (snd_port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify snd_port\n");
+		return -EINVAL;
+	}
+	if (snd_irq[dev] == SNDRV_AUTO_IRQ) {
+		snd_printk("specify snd_irq\n");
+		return -EINVAL;
+	}
+	if (snd_dma1[dev] == SNDRV_AUTO_DMA) {
+		snd_printk("specify snd_dma1\n");
+		return -EINVAL;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ad1848_create(card, snd_port[dev],
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     AD1848_HW_DETECT,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ad1848_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ad1848_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	strcpy(card->driver, "AD1848");
+	strcpy(card->shortname, pcm->name);
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_ad1848_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_ad1848_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++)
+		if (snd_card_ad1848_probe(dev) >= 0)
+			cards++;
+
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "AD1848 soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ad1848_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_ad1848_cards[idx]);
+}
+
+module_init(alsa_card_ad1848_init)
+module_exit(alsa_card_ad1848_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ad1848=snd_enable,snd_index,snd_id,snd_port,
+			 snd_irq,snd_dma1 */
+
+static int __init alsa_card_ad1848_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-ad1848=", alsa_card_ad1848_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/ad1848/ad1848_lib.c linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848_lib.c
--- linux/sound/isa/ad1848/ad1848_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1174 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of AD1848/AD1847/CS4248
+ *
+ *
+ *   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
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/ad1848.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248");
+MODULE_LICENSE("GPL");
+
+#define chip_t ad1848_t
+
+#if 0
+#define SNDRV_DEBUG_MCE
+#endif
+
+/*
+ *  Some variables
+ */
+
+static unsigned char freq_bits[14] = {
+	/* 5510 */	0x00 | AD1848_XTAL2,
+	/* 6620 */	0x0E | AD1848_XTAL2,
+	/* 8000 */	0x00 | AD1848_XTAL1,
+	/* 9600 */	0x0E | AD1848_XTAL1,
+	/* 11025 */	0x02 | AD1848_XTAL2,
+	/* 16000 */	0x02 | AD1848_XTAL1,
+	/* 18900 */	0x04 | AD1848_XTAL2,
+	/* 22050 */	0x06 | AD1848_XTAL2,
+	/* 27042 */	0x04 | AD1848_XTAL1,
+	/* 32000 */	0x06 | AD1848_XTAL1,
+	/* 33075 */	0x0C | AD1848_XTAL2,
+	/* 37800 */	0x08 | AD1848_XTAL2,
+	/* 44100 */	0x0A | AD1848_XTAL2,
+	/* 48000 */	0x0C | AD1848_XTAL1
+};
+
+static unsigned int rates[14] = {
+	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
+	27042, 32000, 33075, 37800, 44100, 48000
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: 14,
+	list: rates,
+	mask: 0,
+};
+
+static unsigned char snd_ad1848_original_image[16] =
+{
+	0x00,			/* 00 - lic */
+	0x00,			/* 01 - ric */
+	0x9f,			/* 02 - la1ic */
+	0x9f,			/* 03 - ra1ic */
+	0x9f,			/* 04 - la2ic */
+	0x9f,			/* 05 - ra2ic */
+	0xbf,			/* 06 - loc */
+	0xbf,			/* 07 - roc */
+	0x20,			/* 08 - dfr */
+	AD1848_AUTOCALIB,	/* 09 - ic */
+	0x00,			/* 0a - pc */
+	0x00,			/* 0b - ti */
+	0x00,			/* 0c - mi */
+	0x00,			/* 0d - lbc */
+	0x00,			/* 0e - dru */
+	0x00,			/* 0f - drl */
+};
+
+/*
+ *  Basic I/O functions
+ */
+
+void snd_ad1848_out(ad1848_t *chip,
+			   unsigned char reg,
+			   unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
+	outb(chip->image[reg] = value, AD1848P(chip, REG));
+	mb();
+#if 0
+	printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value);
+#endif
+}
+
+void snd_ad1848_dout(ad1848_t *chip,
+		     unsigned char reg,
+		     unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
+	outb(value, AD1848P(chip, REG));
+	mb();
+}
+
+unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg)
+{
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("auto calibration time out - reg = 0x%x\n", reg);
+#endif
+	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
+	mb();
+	return inb(AD1848P(chip, REG));
+}
+
+#ifdef CONFIG_SND_DEBUG
+
+void snd_ad1848_debug(ad1848_t *chip)
+{
+	printk("AD1848 REGS:      INDEX = 0x%02x  ", inb(AD1848P(chip, REGSEL)));
+	printk("                 STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS)));
+	printk("  0x00: left input      = 0x%02x  ", snd_ad1848_in(chip, 0x00));
+	printk("  0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));
+	printk("  0x01: right input     = 0x%02x  ", snd_ad1848_in(chip, 0x01));
+	printk("  0x09: iface (CFIG 1)  = 0x%02x\n", snd_ad1848_in(chip, 0x09));
+	printk("  0x02: AUXA left       = 0x%02x  ", snd_ad1848_in(chip, 0x02));
+	printk("  0x0a: pin control     = 0x%02x\n", snd_ad1848_in(chip, 0x0a));
+	printk("  0x03: AUXA right      = 0x%02x  ", snd_ad1848_in(chip, 0x03));
+	printk("  0x0b: init & status   = 0x%02x\n", snd_ad1848_in(chip, 0x0b));
+	printk("  0x04: AUXB left       = 0x%02x  ", snd_ad1848_in(chip, 0x04));
+	printk("  0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));
+	printk("  0x05: AUXB right      = 0x%02x  ", snd_ad1848_in(chip, 0x05));
+	printk("  0x0d: loopback        = 0x%02x\n", snd_ad1848_in(chip, 0x0d));
+	printk("  0x06: left output     = 0x%02x  ", snd_ad1848_in(chip, 0x06));
+	printk("  0x0e: data upr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0e));
+	printk("  0x07: right output    = 0x%02x  ", snd_ad1848_in(chip, 0x07));
+	printk("  0x0f: data lwr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0f));
+}
+
+#endif
+
+/*
+ *  AD1848 detection / MCE routines
+ */
+
+void snd_ad1848_mce_up(ad1848_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("mce_up - auto calibration time out (0)\n");
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->mce_bit |= AD1848_MCE;
+	timeout = inb(AD1848P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if (!(timeout & AD1848_MCE))
+		outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+void snd_ad1848_mce_down(ad1848_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+	signed long time;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (timeout = 5; timeout > 0; timeout--)
+		inb(AD1848P(chip, REGSEL));
+	/* end of cleanup sequence */
+	for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#if 0
+	printk("(1) timeout = %i\n", timeout);
+#endif
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL));
+#endif
+	chip->mce_bit &= ~AD1848_MCE;
+	timeout = inb(AD1848P(chip, REGSEL));
+	outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & AD1848_MCE) == 0) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	/* calibration process */
+
+	for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--);
+	if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) {
+		snd_printd("mce_down - auto calibration time out (1)\n");
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+#if 0
+	printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies);
+#endif
+	time = HZ / 4;
+	while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (2)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+#if 0
+	printk("(3) jiffies = %li\n", jiffies);
+#endif
+	time = HZ / 10;
+	while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (3)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	printk("(4) jiffies = %li\n", jiffies);
+	snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL)));
+#endif
+}
+
+static unsigned int snd_ad1848_get_count(unsigned char format,
+				         unsigned int size)
+{
+	switch (format & 0xe0) {
+	case AD1848_LINEAR_16:
+		size >>= 1;
+		break;
+	}
+	if (format & AD1848_STEREO)
+		size >>= 1;
+	return size;
+}
+
+static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what,
+			      int channel, int cmd)
+{
+	int result = 0;
+
+#if 0
+	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS)));
+#endif
+	spin_lock(&chip->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->image[AD1848_IFACE_CTRL] & what) {
+			spin_unlock(&chip->reg_lock);
+			return 0;
+		}
+		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what);
+		chip->mode |= AD1848_MODE_RUNNING;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->image[AD1848_IFACE_CTRL] & what)) {
+			spin_unlock(&chip->reg_lock);
+			return 0;
+		}
+		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what);
+		chip->mode &= ~AD1848_MODE_RUNNING;
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+/*
+ *  CODEC I/O
+ */
+
+static unsigned char snd_ad1848_get_rate(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < 14; i++)
+		if (rate == rates[i])
+			return freq_bits[i];
+	snd_BUG();
+	return freq_bits[13];
+}
+
+static int snd_ad1848_ioctl(snd_pcm_substream_t * substream,
+			    unsigned int cmd, void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static unsigned char snd_ad1848_get_format(int format, int channels)
+{
+	unsigned char rformat;
+
+	rformat = AD1848_LINEAR_8;
+	switch (format) {
+	case SNDRV_PCM_FORMAT_A_LAW:	rformat = AD1848_ALAW_8; break;
+	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = AD1848_ULAW_8; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	rformat = AD1848_LINEAR_16; break;
+	}
+	if (channels > 1)
+		rformat |= AD1848_STEREO;
+#if 0
+	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+#endif
+	return rformat;
+}
+
+static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute)
+{
+	unsigned long flags;
+	
+	mute = mute ? 1 : 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->calibrate_mute == mute) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	if (!mute) {
+		snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]);
+		snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]);
+	}
+	snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]);
+	snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]);
+	chip->calibrate_mute = mute;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params)
+{
+	if (hw_params == NULL) {
+		chip->image[AD1848_DATA_FORMAT] = 0x20;
+	} else {
+		chip->image[AD1848_DATA_FORMAT] =
+		    snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) |
+		    snd_ad1848_get_rate(params_rate(hw_params));
+	}
+	// snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]);
+}
+
+static int snd_ad1848_open(ad1848_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	if (chip->mode & AD1848_MODE_OPEN) {
+		up(&chip->open_mutex);
+		return -EAGAIN;
+	}
+	snd_ad1848_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("open: (1)\n");
+#endif
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
+			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO |
+			     AD1848_CALIB_MODE);
+	chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;
+	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("open: (2)\n");
+#endif
+
+	snd_ad1848_set_data_format(chip, NULL);
+
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("open: (3)\n");
+#endif
+
+	/* ok. now enable and ack CODEC IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE;
+	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = mode;
+	up(&chip->open_mutex);
+
+	return 0;
+}
+
+static void snd_ad1848_close(ad1848_t *chip)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	if (!chip->mode) {
+		up(&chip->open_mutex);
+		return;
+	}
+	/* disable IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE;
+	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* now disable capture & playback */
+
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
+			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
+	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+
+	/* clear IRQ again */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = 0;
+	up(&chip->open_mutex);
+}
+
+/*
+ *  ok.. exported functions..
+ */
+
+static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd);
+}
+
+static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd);
+}
+
+static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	snd_ad1848_calibrate_mute(chip, 1);
+	snd_ad1848_set_data_format(chip, hw_params);
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+	snd_ad1848_calibrate_mute(chip, 0);
+	return 0;
+}
+
+static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO);
+	snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
+	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	snd_ad1848_calibrate_mute(chip, 1);
+	snd_ad1848_set_data_format(chip, hw_params);
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+	snd_ad1848_calibrate_mute(chip, 0);
+	return 0;
+}
+
+static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
+	snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
+	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ad1848_t *chip = snd_magic_cast(ad1848_t, dev_id, return);
+
+	if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream &&
+	    (chip->mode & AD1848_MODE_RUNNING))
+		snd_pcm_period_elapsed(chip->playback_substream);
+	if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream &&
+	    (chip->mode & AD1848_MODE_RUNNING))
+		snd_pcm_period_elapsed(chip->capture_substream);
+	outb(0, AD1848P(chip, STATUS));	/* clear global interrupt bit */
+}
+
+static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE))
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE))
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static int snd_ad1848_probe(ad1848_t * chip)
+{
+	unsigned long flags;
+	int i, id, rev, ad1847;
+	unsigned char *ptr;
+
+#if 0
+	snd_ad1848_debug(chip);
+#endif
+	id = ad1847 = 0;
+	for (i = 0; i < 1000; i++) {
+		mb();
+		if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+			udelay(500);
+		else {
+			spin_lock_irqsave(&chip->reg_lock, flags);
+			snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
+			snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa);
+			snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45);
+			rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT);
+			if (rev == 0x65) {
+				id = 1;
+				ad1847 = 1;
+				break;
+			}
+			if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) {
+				id = 1;
+				break;
+			}
+			spin_unlock_irqrestore(&chip->reg_lock, flags);
+		}
+	}
+	if (id != 1)
+		return -ENODEV;	/* no valid device found */
+	if (chip->hardware == AD1848_HW_DETECT) {
+		if (ad1847) {
+			chip->hardware = AD1848_HW_AD1847;
+		} else {
+			chip->hardware = AD1848_HW_AD1848;
+			rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
+			if (rev & 0x80) {
+				chip->hardware = AD1848_HW_CS4248;
+			} else if (rev & 0x0a) {
+				chip->hardware = AD1848_HW_CMI8330;
+			}
+		}
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	inb(AD1848P(chip, STATUS));	/* clear any pendings IRQ */
+	outb(0, AD1848P(chip, STATUS));
+	mb();
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->image[AD1848_MISC_INFO] = 0x00;
+	chip->image[AD1848_IFACE_CTRL] =
+	    (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA;
+	ptr = (unsigned char *) &chip->image;
+	snd_ad1848_mce_down(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 16; i++)	/* ok.. fill all AD1848 registers */
+		snd_ad1848_out(chip, i, *ptr++);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_up(chip);
+	snd_ad1848_mce_down(chip);
+	return 0;		/* all things are ok.. */
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_ad1848_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ad1848_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+
+ */
+
+static int snd_ad1848_playback_open(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0)
+		return err;
+	chip->playback_substream = substream;
+	runtime->hw = snd_ad1848_playback;
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ad1848_capture_open(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0)
+		return err;
+	chip->capture_substream = substream;
+	runtime->hw = snd_ad1848_capture;
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ad1848_playback_close(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->mode &= ~AD1848_MODE_PLAY;
+	chip->playback_substream = NULL;
+	snd_ad1848_close(chip);
+	return 0;
+}
+
+static int snd_ad1848_capture_close(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->mode &= ~AD1848_MODE_CAPTURE;
+	chip->capture_substream = NULL;
+	snd_ad1848_close(chip);
+	return 0;
+}
+
+static int snd_ad1848_free(ad1848_t *chip)
+{
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma >= 0) {
+		snd_dma_disable(chip->dma);
+		free_dma(chip->dma);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_ad1848_dev_free(snd_device_t *device)
+{
+	ad1848_t *chip = snd_magic_cast(ad1848_t, device->device_data, return -ENXIO);
+	return snd_ad1848_free(chip);
+}
+
+static const char *snd_ad1848_chip_id(ad1848_t *chip)
+{
+	switch (chip->hardware) {
+	case AD1848_HW_AD1847:	return "AD1847";
+	case AD1848_HW_AD1848:	return "AD1848";
+	case AD1848_HW_CS4248:	return "CS4248";
+	case AD1848_HW_CMI8330: return "CMI8330/C3D";
+	default:		return "???";
+	}
+}
+
+int snd_ad1848_create(snd_card_t * card,
+		      unsigned long port,
+		      int irq, int dma,
+		      unsigned short hardware,
+		      ad1848_t ** rchip)
+{
+	static snd_device_ops_t ops = {
+		dev_free:       snd_ad1848_dev_free,
+	};
+	ad1848_t *chip;
+	int err;
+
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(ad1848_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->open_mutex);
+	chip->card = card;
+	chip->port = port;
+	chip->irq = -1;
+	chip->dma = -1;
+	chip->hardware = hardware;
+	memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image));
+	
+	if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) {
+		snd_ad1848_free(chip);
+		return -EBUSY;
+	}
+	if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) {
+		snd_ad1848_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (request_dma(dma, "AD1848")) {
+		snd_ad1848_free(chip);
+		return -EBUSY;
+	}
+	chip->dma = dma;
+
+	if (snd_ad1848_probe(chip) < 0) {
+		snd_ad1848_free(chip);
+		return -ENODEV;
+	}
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_ad1848_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ad1848_playback_ops = {
+	open:		snd_ad1848_playback_open,
+	close:		snd_ad1848_playback_close,
+	ioctl:		snd_ad1848_ioctl,
+	hw_params:	snd_ad1848_playback_hw_params,
+	hw_free:	snd_ad1848_playback_hw_free,
+	prepare:	snd_ad1848_playback_prepare,
+	trigger:	snd_ad1848_playback_trigger,
+	pointer:	snd_ad1848_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ad1848_capture_ops = {
+	open:		snd_ad1848_capture_open,
+	close:		snd_ad1848_capture_close,
+	ioctl:		snd_ad1848_ioctl,
+	hw_params:	snd_ad1848_capture_hw_params,
+	hw_free:	snd_ad1848_capture_hw_free,
+	prepare:	snd_ad1848_capture_prepare,
+	trigger:	snd_ad1848_capture_trigger,
+	pointer:	snd_ad1848_capture_pointer,
+};
+
+static void snd_ad1848_pcm_free(snd_pcm_t *pcm)
+{
+	ad1848_t *chip = snd_magic_cast(ad1848_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops);
+
+	pcm->private_free = snd_ad1848_pcm_free;
+	pcm->private_data = chip;
+	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	strcpy(pcm->name, snd_ad1848_chip_id(chip));
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {
+		"Line", "Aux", "Mic", "Mix"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left;
+	right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right;
+	change = left != chip->image[AD1848_LEFT_INPUT] ||
+	         right != chip->image[AD1848_RIGHT_INPUT];
+	snd_ad1848_out(chip, AD1848_LEFT_INPUT, left);
+	snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_ad1848_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+		change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
+		snd_ad1848_out(chip, left_reg, val1);
+		snd_ad1848_out(chip, right_reg, val2);
+	} else {
+		val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != chip->image[left_reg];
+		snd_ad1848_out(chip, left_reg, val1);		
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define AD1848_CONTROLS (sizeof(snd_ad1848_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ad1848_controls[] = {
+AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
+AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_ad1848_info_mux,
+	get: snd_ad1848_get_mux,
+	put: snd_ad1848_put_mux,
+},
+AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
+AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0)
+};
+                                        
+int snd_ad1848_mixer(ad1848_t *chip)
+{
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
+
+	pcm = chip->pcm;
+	card = chip->card;
+
+	strcpy(card->mixername, pcm->name);
+
+	for (idx = 0; idx < AD1848_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1848_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_ad1848_in);
+EXPORT_SYMBOL(snd_ad1848_out);
+EXPORT_SYMBOL(snd_ad1848_dout);
+EXPORT_SYMBOL(snd_ad1848_mce_up);
+EXPORT_SYMBOL(snd_ad1848_mce_down);
+EXPORT_SYMBOL(snd_ad1848_interrupt);
+EXPORT_SYMBOL(snd_ad1848_create);
+EXPORT_SYMBOL(snd_ad1848_pcm);
+EXPORT_SYMBOL(snd_ad1848_mixer);
+EXPORT_SYMBOL(snd_ad1848_info_single);
+EXPORT_SYMBOL(snd_ad1848_get_single);
+EXPORT_SYMBOL(snd_ad1848_put_single);
+EXPORT_SYMBOL(snd_ad1848_info_double);
+EXPORT_SYMBOL(snd_ad1848_get_double);
+EXPORT_SYMBOL(snd_ad1848_put_double);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_ad1848_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ad1848_exit(void)
+{
+}
+
+module_init(alsa_ad1848_init)
+module_exit(alsa_ad1848_exit)
diff -Nru linux/sound/isa/als100.c linux-2.4.19-pre5-mjc/sound/isa/als100.c
--- linux/sound/isa/als100.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/als100.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,423 @@
+
+/*
+    card-als100.c - driver for Avance Logic ALS100 based soundcards.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    Thanks to Pierfrancesco 'qM2' Passerini.
+
+    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
+*/
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+
+#define chip_t sb_t
+
+#define PFX "als100: "
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("Avance Logic ALS1X0");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Avance Logic,ALS100 - PRO16PNP},"
+	        "{Avance Logic,ALS110},"
+	        "{Avance Logic,ALS120},"
+	        "{Avance Logic,ALS200},"
+	        "{3D Melody,MF1000},"
+	        "{Digimate,3D Sound},"
+	        "{Avance Logic,ALS120},"
+	        "{RTL,RTL3000}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for als100 based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for als100 based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable als100 based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC);
+
+struct snd_card_als100 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+	struct isapnp_dev *devopl;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_als100_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+static struct isapnp_card *snd_als100_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_als100_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_ALS100(_va, _vb, _vc, _device, _audio, _mpu401, _opl) \
+        { \
+                ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio), \
+                         ISAPNP_DEVICE_ID('@', 'X', '@', _mpu401), \
+			 ISAPNP_DEVICE_ID('@', 'H', '@', _opl) } \
+        }
+
+static struct isapnp_card_id snd_als100_pnpids[] __devinitdata = {
+	/* ALS100 - PRO16PNP */
+	ISAPNP_ALS100('A','L','S',0x0001,0x0001,0x0001,0x0001),
+	/* ALS110 - MF1000 - Digimate 3D Sound */
+	ISAPNP_ALS100('A','L','S',0x0110,0x1001,0x1001,0x1001),
+	/* ALS120 */
+	ISAPNP_ALS100('A','L','S',0x0120,0x2001,0x2001,0x2001),
+	/* ALS200 */
+	ISAPNP_ALS100('A','L','S',0x0200,0x0020,0x0020,0x0001),
+	/* RTL3000 */
+	ISAPNP_ALS100('R','T','L',0x3000,0x2001,0x2001,0x2001),
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_als100_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define DRIVER_NAME	"snd-card-als100"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_als100_isapnp(int dev, struct snd_card_als100 *acard)
+{
+	const struct isapnp_card_id *id = snd_als100_isapnp_id[dev];
+	struct isapnp_card *card = snd_als100_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+	acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+	if (acard->devopl->active) {
+		acard->dev = acard->devmpu = acard->devopl = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev],
+			1);
+	if (snd_dma16[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma8[dev] = pdev->dma_resource[1].start;
+	snd_dma16[dev] = pdev->dma_resource[0].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev)<0) {
+		snd_mpu_port[dev] = -1;
+		return 0;
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	pdev = acard->devopl;
+	if (pdev == NULL || pdev->prepare(pdev)<0) {
+		snd_fm_port[dev] = -1;
+		return 0;
+	}
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4);
+
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "OPL isapnp configure failure\n");
+		snd_fm_port[dev] = -1;
+		acard->devopl = NULL;
+	} else {
+		snd_fm_port[dev] = pdev->resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_als100_deactivate(struct snd_card_als100 *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+	if (acard->devmpu) {
+		acard->devmpu->deactivate(acard->devmpu);
+		acard->devmpu = NULL;
+	}
+	if (acard->devopl) {
+		acard->devopl->deactivate(acard->devopl);
+		acard->devopl = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void snd_card_als100_free(snd_card_t *card)
+{
+	struct snd_card_als100 *acard = (struct snd_card_als100 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_als100_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_als100_probe(int dev)
+{
+	int error;
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_card_als100 *acard;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_als100))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_als100 *)card->private_data;
+	card->private_free = snd_card_als100_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_als100_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	printk(KERN_ERR PFX "you have to enable PnP support ...\n");
+	snd_card_free(card);
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_sbdsp_create(card, snd_port[dev],
+				      snd_irq[dev],
+				      snd_sb16dsp_interrupt,
+				      snd_dma8[dev],
+				      snd_dma16[dev],
+				      SB_HW_ALS100, &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100,
+					snd_mpu_port[dev], 0, 
+					snd_mpu_irq[dev], SA_INTERRUPT,
+					NULL) < 0)
+			printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
+				snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "ALS100");
+	strcpy(card->shortname, "Avance Logic ALS100");
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d",
+		card->shortname, chip->name, chip->port,
+		snd_irq[dev], snd_dma8[dev], snd_dma16[dev]);
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_als100_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_als100_isapnp_detect(struct isapnp_card *card,
+					   const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_als100_isapnp_cards[dev] = card;
+		snd_als100_isapnp_id[dev] = id;
+		res = snd_card_als100_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif
+
+static int __init alsa_card_als100_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_als100_pnpids, snd_als100_isapnp_detect);
+#else
+	printk(KERN_ERR PFX "you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		printk(KERN_ERR "no ALS100 based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_als100_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_als100_cards[dev]);
+}
+
+module_init(alsa_card_als100_init)
+module_exit(alsa_card_als100_exit)
+
+#ifndef MODULE
+
+/* format is: snd-als100=snd_enable,snd_index,snd_id,snd_port,
+			 snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq,
+			 snd_dma8,snd_dma16 */
+
+static int __init alsa_card_als100_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma16[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-als100=", alsa_card_als100_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/azt2320.c linux-2.4.19-pre5-mjc/sound/isa/azt2320.c
--- linux/sound/isa/azt2320.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/azt2320.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,446 @@
+
+/*
+    card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    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 should provide support for most Aztech AZT2320 based cards.
+    Several AZT2316 chips are also supported/tested, but autoprobe doesn't
+    work: all module option have to be set.
+
+    No docs available for us at Aztech headquarters !!!   Unbelievable ...
+    No other help obtained.
+
+    Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS
+    activation method (full-duplex audio!).
+*/
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+#define chip_t cs4231_t
+
+#define PFX "azt2320: "
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("Aztech Systems AZT2320");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Aztech Systems,PRO16V},"
+		"{Aztech Systems,AZT2320},"
+		"{Aztech Systems,AZT3300},"
+		"{Aztech Systems,AZT2320},"
+		"{Aztech Systems,AZT3000}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for azt2320 based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for azt2320 based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable azt2320 based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wss_port, "WSS Port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "1st DMA # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "2nd DMA # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+struct snd_card_azt2320 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_azt2320_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_azt2320_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_azt2320_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_AZT2320(_va, _vb, _vc, _device, _audio, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401), } \
+	}
+
+static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = {
+	/* PRO16V */
+	ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001),
+	/* Aztech Sound Galaxy 16 */
+	ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002),
+	/* Packard Bell Sound III 336 AM/SP */
+	ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001),
+	/* AT3300 */
+	ISAPNP_AZT2320('A','Z','T',0x3002,0x1004,0x2001),
+	/* --- */
+	ISAPNP_AZT2320('A','Z','T',0x3005,0x1003,0x2001),
+	/* --- */
+	ISAPNP_AZT2320('A','Z','T',0x3011,0x1003,0x2001),
+	{ ISAPNP_CARD_END, }	/* end */
+};
+
+ISAPNP_CARD_TABLE(snd_azt2320_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define	DRIVER_NAME	"snd-card-azt2320"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_azt2320_isapnp(int dev, struct snd_card_azt2320 *acard)
+{
+	const struct isapnp_card_id *id = snd_azt2320_isapnp_id[dev];
+	struct isapnp_card *card = snd_azt2320_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_wss_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_wss_port[dev],
+			4);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev],
+			1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev) < 0) {
+		printk(KERN_ERR PFX "AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_wss_port[dev] = pdev->resource[2].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev) < 0) {
+		snd_mpu_port[dev] = -1;
+		return 0;
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev) < 0) {
+		/* not fatal error */
+		printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_azt2320_deactivate(struct snd_card_azt2320 *acard)
+{
+	if (acard->dev)
+		acard->dev->deactivate(acard->dev);
+	if (acard->devmpu)
+		acard->devmpu->deactivate(acard->devmpu);
+}
+#endif	/* __ISAPNP__ */
+
+/* same of snd_sbdsp_command by Jaroslav Kysela */
+static int __init snd_card_azt2320_command(unsigned long port, unsigned char val)
+{
+	int i;
+	unsigned long limit;
+
+	limit = jiffies + HZ / 10;
+	for (i = 50000; i && (limit - jiffies) > 0; i--)
+		if (!(inb(port + 0x0c) & 0x80)) {
+			outb(val, port + 0x0c);
+			return 0;
+		}
+	return -EBUSY;
+}
+
+static int __init snd_card_azt2320_enable_wss(unsigned long port)
+{
+	int error;
+
+	if ((error = snd_card_azt2320_command(port, 0x09)))
+		return error;
+	if ((error = snd_card_azt2320_command(port, 0x00)))
+		return error;
+
+	mdelay(5);
+	return 0;
+}
+
+static void snd_card_azt2320_free(snd_card_t *card)
+{
+	struct snd_card_azt2320 *acard = (struct snd_card_azt2320 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_azt2320_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_azt2320_probe(int dev)
+{
+	int error;
+	snd_card_t *card;
+	struct snd_card_azt2320 *acard;
+	cs4231_t *chip;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_azt2320))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_azt2320 *)card->private_data;
+	card->private_free = snd_card_azt2320_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_azt2320_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_card_azt2320_enable_wss(snd_port[dev]))) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_cs4231_create(card, snd_wss_port[dev], -1,
+				       snd_irq[dev],
+				       snd_dma1[dev],
+				       snd_dma2[dev],
+				       CS4231_HW_DETECT, 0, &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
+				snd_mpu_port[dev], 0,
+				snd_mpu_irq[dev], SA_INTERRUPT,
+				NULL) < 0)
+			printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n",
+				snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
+				snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "AZT2320");
+	strcpy(card->shortname, "Aztech AZT2320");
+	sprintf(card->longname, "%s soundcard, WSS at 0x%lx, irq %i, dma %i&%i",
+		card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]);
+
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_azt2320_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_azt2320_isapnp_detect(struct isapnp_card *card,
+                                            const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_azt2320_isapnp_cards[dev] = card;
+		snd_azt2320_isapnp_id[dev] = id;
+                res = snd_card_azt2320_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+        return -ENODEV;
+}
+#endif
+
+static int __init alsa_card_azt2320_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_azt2320_pnpids, snd_azt2320_isapnp_detect);
+#else
+	printk(KERN_ERR PFX "you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		printk(KERN_ERR "no AZT2320 based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_azt2320_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_azt2320_cards[dev]);
+}
+
+module_init(alsa_card_azt2320_init)
+module_exit(alsa_card_azt2320_exit)
+
+#ifndef MODULE
+
+/* format is: snd-azt2320=snd_enable,snd_index,snd_id,snd_port,
+			  snd_wss_port,snd_mpu_port,snd_fm_port,
+			  snd_irq,snd_mpu_irq,snd_dma1,snd_dma2 */
+
+static int __init alsa_card_azt2320_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-azt2320=", alsa_card_azt2320_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/cmi8330.c linux-2.4.19-pre5-mjc/sound/isa/cmi8330.c
--- linux/sound/isa/cmi8330.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cmi8330.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,537 @@
+/*
+ *  Driver for C-Media's CMI8330 soundcards.
+ *  Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
+ *    http://www.undergrad.math.uwaterloo.ca/~gstalusa
+ *
+ *   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
+ *
+ */
+
+/*
+ * NOTES
+ *
+ *  The extended registers contain mixer settings which are largely
+ *  untapped for the time being.
+ *
+ *  MPU401 and SPDIF are not supported yet.  I don't have the hardware
+ *  to aid in coding and testing, so I won't bother.
+ *
+ *  To quickly load the module,
+ *
+ *  modprobe -a snd-card-cmi8330 snd_sbport=0x220 snd_sbirq=5 snd_sbdma8=1
+ *    snd_sbdma16=5 snd_wssport=0x530 snd_wssirq=11 snd_wssdma=0
+ *
+ *  This card has two mixers and two PCM devices.  I've cheesed it such
+ *  that recording and playback can be done through the same device.
+ *  The driver "magically" routes the capturing to the AD1848 codec,
+ *  and playback to the SB16 codec.  This allows for full-duplex mode
+ *  to some extent.
+ *  The utilities in alsa-utils are aware of both devices, so passing
+ *  the appropriate parameters to amixer and alsactl will give you
+ *  full control over both mixers.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ad1848.h>
+#include <sound/sb.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
+MODULE_DESCRIPTION("C-Media CMI8330");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+#ifdef __ISAPNP__
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int snd_sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int snd_sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int snd_sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int snd_wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int snd_wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for CMI8330 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string  for CMI8330 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable CMI8330 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+
+MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sbport, "Port # for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list");
+MODULE_PARM(snd_sbirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_sbirq, "IRQ # for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{5},dialog:list");
+MODULE_PARM(snd_sbdma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_sbdma8, "DMA8 for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbdma8, SNDRV_DMA8_DESC ",prefers:{1}");
+MODULE_PARM(snd_sbdma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_sbdma16, "DMA16 for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbdma16, SNDRV_ENABLED ",allows:{{5},{7}},prefers:{5},dialog:list");
+
+MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wssport, "Port # for CMI8330 WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80,0xf40,0xc0}},prefers:{0x530},base:16,dialog:list");
+MODULE_PARM(snd_wssirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_wssirq, "IRQ # for CMI8330 WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{11},dialog:list");
+MODULE_PARM(snd_wssdma, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_wssdma, "DMA for CMI8330 WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssdma, SNDRV_DMA8_DESC ",prefers:{0}");
+
+#define CMI8330_RMUX3D    16
+#define CMI8330_MUTEMUX   17
+#define CMI8330_OUTPUTVOL 18
+#define CMI8330_MASTVOL   19
+#define CMI8330_LINVOL    20
+#define CMI8330_CDINVOL   21
+#define CMI8330_WAVVOL    22
+#define CMI8330_RECMUX    23
+#define CMI8330_WAVGAIN   24
+#define CMI8330_LINGAIN   25
+#define CMI8330_CDINGAIN  26
+
+static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
+{
+	0x0,			/* 16 - recording mux */
+	0x40,			/* 17 - mute mux */
+	0x0,			/* 18 - vol */
+	0x0,			/* 19 - master volume */
+	0x0,			/* 20 - line-in volume */
+	0x0,			/* 21 - cd-in volume */
+	0x0,			/* 22 - wave volume */
+	0x0,			/* 23 - mute/rec mux */
+	0x0,			/* 24 - wave rec gain */
+	0x0,			/* 25 - line-in rec gain */
+	0x0			/* 26 - cd-in rec gain */
+};
+
+struct snd_cmi8330 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *cap;
+	struct isapnp_dev *play;
+#endif
+};
+
+static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_cmi8330_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_cmi8330_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_CMI8330(_va, _vb, _vc, _device, _audio1, _audio2) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio1), \
+			 ISAPNP_DEVICE_ID('@', 'X', '@', _audio2), } \
+	}
+
+static struct isapnp_card_id snd_cmi8330_pnpids[] __devinitdata =
+{
+	ISAPNP_CMI8330('C','M','I',0x0001,0x0001,0x0001),
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_cmi8330_pnpids);
+
+#endif
+
+#define CMI8330_CONTROLS (sizeof(snd_cmi8330_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cmi8330_controls[] __devinitdata = {
+AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
+AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1),
+AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 0),
+AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
+AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
+AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
+AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0),
+AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0),
+AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0),
+AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0),
+AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0),
+AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0),
+AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0),
+AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0),
+AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0),
+AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0),
+AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0),
+AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0),
+AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
+AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1),
+AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0),
+AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1),
+AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1),
+AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1),
+};
+
+static int __init snd_cmi8330_mixer(snd_card_t *card, ad1848_t *chip)
+{
+	int idx, err;
+
+	strcpy(card->mixername, "CMI8330/C3D");
+
+	for (idx = 0; idx < CMI8330_CONTROLS; idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmi8330_controls[idx], chip))) < 0)
+			return err;
+
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_cmi8330_isapnp(int dev, struct snd_cmi8330 *acard)
+{
+	const struct isapnp_card_id *id = snd_cmi8330_isapnp_id[dev];
+	struct isapnp_card *card = snd_cmi8330_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->cap = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->cap->active) {
+		acard->cap = NULL;
+		return -EBUSY;
+	}
+	acard->play = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->play->active) {
+		acard->cap = acard->play = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->cap;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+	/* allocate AD1848 resources */
+	if (snd_wssport[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_wssport[dev], 8);
+	if (snd_wssdma[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_wssdma[dev], 1);
+	if (snd_wssirq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_wssirq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("(AD1848) PnP configure failure\n");
+		return -EBUSY;
+	}
+	snd_wssport[dev] = pdev->resource[0].start;
+	snd_wssdma[dev] = pdev->dma_resource[0].start;
+	snd_wssirq[dev] = pdev->irq_resource[0].start;
+
+	/* allocate SB16 resources */
+	pdev = acard->play;
+	if (pdev->prepare(pdev)<0) {
+		acard->cap->deactivate(acard->cap);
+		return -EAGAIN;
+	}
+	if (snd_sbport[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_sbport[dev], 16);
+	if (snd_sbdma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_sbdma8[dev], 1);
+	if (snd_sbdma16[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_sbdma16[dev], 1);
+	if (snd_sbirq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_sbirq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("CMI8330/C3D (SB16) PnP configure failure\n");
+		acard->cap->deactivate(acard->cap);
+		return -EBUSY;
+	}
+	snd_sbport[dev] = pdev->resource[0].start;
+	snd_sbdma8[dev] = pdev->dma_resource[0].start;
+	snd_sbdma16[dev] = pdev->dma_resource[1].start;
+	snd_sbirq[dev] = pdev->irq_resource[0].start;
+
+	return 0;
+}
+
+static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard)
+{
+	if (acard->cap) {
+		acard->cap->deactivate(acard->cap);
+		acard->cap = NULL;
+	}
+	if (acard->play) {
+		acard->play->deactivate(acard->play);
+		acard->play = NULL;
+	}
+}
+#endif
+
+static void snd_cmi8330_free(snd_card_t *card)
+{
+	struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_cmi8330_deactivate(acard);
+#endif
+	}
+}
+
+static int __init snd_cmi8330_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_cmi8330 *acard;
+	ad1848_t *chip_wss;
+	sb_t *chip_sb;
+	unsigned long flags;
+	int i, err;
+	snd_pcm_t *pcm, *wss_pcm, *sb_pcm;
+	snd_pcm_str_t *pstr;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_wssport[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_wssport\n");
+			return -EINVAL;
+		}
+		if (snd_sbport[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_sbport\n");
+			return -EINVAL;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_cmi8330));
+	if (card == NULL) {
+		snd_printk("could not get a new card\n");
+		return -ENOMEM;
+	}
+	acard = (struct snd_cmi8330 *)card->private_data;
+	card->private_free = snd_cmi8330_free;
+
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_cmi8330_isapnp(dev, acard)) < 0) {
+		snd_printk("PnP detection failed\n");
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	if ((err = snd_ad1848_create(card,
+				     snd_wssport[dev] + 4,
+				     snd_wssirq[dev],
+				     snd_wssdma[dev],
+				     AD1848_HW_DETECT,
+				     &chip_wss)) < 0) {
+		snd_printk("(AD1848) device busy??\n");
+		snd_card_free(card);
+		return err;
+	}
+	if (chip_wss->hardware != AD1848_HW_CMI8330) {
+		snd_printk("(AD1848) not found during probe\n");
+		snd_card_free(card);
+		return -ENODEV;
+	}
+	if ((err = snd_ad1848_pcm(chip_wss, 0, &wss_pcm)) < 0) {
+		snd_printk("(AD1848) no enough memory??\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_sbdsp_create(card, snd_sbport[dev],
+				    snd_sbirq[dev],
+				    snd_sb16dsp_interrupt,
+				    snd_sbdma8[dev],
+				    snd_sbdma16[dev],
+				    SB_HW_AUTO, &chip_sb)) < 0) {
+		snd_printk("(SB16) device busy??\n");
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sb16dsp_pcm(chip_sb, 1, &sb_pcm)) < 0) {
+		snd_printk("(SB16) no enough memory??\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	if (chip_sb->hardware != SB_HW_16) {
+		snd_printk("(SB16) not found during probe\n");
+		snd_card_free(card);
+		return -ENODEV;
+	}
+
+	memcpy(&chip_wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image));
+
+	spin_lock_irqsave(&chip_wss->reg_lock, flags);
+	snd_ad1848_out(chip_wss, AD1848_MISC_INFO,	/* switch on MODE2 */
+		       chip_wss->image[AD1848_MISC_INFO] |= 0x40);
+	spin_unlock_irqrestore(&chip_wss->reg_lock, flags);
+
+	if ((err = snd_cmi8330_mixer(card, chip_wss)) < 0) {
+		snd_printk("failed to create mixers\n");
+		snd_card_free(card);
+		return err;
+	}
+	spin_lock_irqsave(&chip_wss->reg_lock, flags);
+	for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++)
+		snd_ad1848_out(chip_wss, i, chip_wss->image[i]);
+	spin_unlock_irqrestore(&chip_wss->reg_lock, flags);
+
+	/*
+	 * KLUDGE ALERT
+	 *  disable AD1848 playback
+	 *  disable SB16 capture
+	 */
+	pcm = wss_pcm;
+	pstr = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	snd_magic_kfree(pstr->substream);
+	pstr->substream = 0;
+	pstr->substream_count = 0;
+
+	pcm = sb_pcm;
+	pstr = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+	snd_magic_kfree(pstr->substream);
+	pstr->substream = 0;
+	pstr->substream_count = 0;
+
+	strcpy(card->driver, "CMI8330/C3D");
+	strcpy(card->shortname, "C-Media CMI8330/C3D");
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		wss_pcm->name,
+		chip_wss->port,
+		snd_wssirq[dev],
+		snd_wssdma[dev]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_cmi8330_cards[dev] = card;
+	return 0;
+}
+
+static void __exit alsa_card_cmi8330_exit(void)
+{
+	int i;
+
+	for (i = 0; i < SNDRV_CARDS; i++)
+		snd_card_free(snd_cmi8330_cards[i]);
+}
+
+#ifdef __ISAPNP__
+static int __init snd_cmi8330_isapnp_detect(struct isapnp_card *card,
+                                            const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_cmi8330_isapnp_cards[dev] = card;
+		snd_cmi8330_isapnp_id[dev] = id;
+		res = snd_cmi8330_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_cmi8330_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_cmi8330_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_cmi8330_pnpids, snd_cmi8330_isapnp_detect);
+#endif
+
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "CMI8330 not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+module_init(alsa_card_cmi8330_init)
+module_exit(alsa_card_cmi8330_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cmi8330=snd_enable,snd_index,snd_id,snd_isapnp,
+			  snd_sbport,snd_sbirq,
+			  snd_sbdma8,snd_sbdma16,
+			  snd_wssport,snd_wssirq,
+			  snd_wssdma */
+
+static int __init alsa_card_cmi8330_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 &&
+	       get_option(&str,&snd_sbirq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_sbdma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_sbdma16[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 &&
+	       get_option(&str,&snd_wssirq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_wssdma[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-cmi8330=", alsa_card_cmi8330_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/cs423x/Makefile linux-2.4.19-pre5-mjc/sound/isa/cs423x/Makefile
--- linux/sound/isa/cs423x/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,46 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _cs423x.o
+
+list-multi   := snd-cs4231-lib.o snd-cs4236-lib.o \
+		snd-cs4231.o snd-cs4232.o snd-cs4236.o
+
+export-objs  := cs4231_lib.o cs4236_lib.o
+
+snd-cs4231-lib-objs := cs4231_lib.o
+snd-cs4236-lib-objs := cs4236_lib.o
+snd-cs4231-objs := cs4231.o
+snd-cs4232-objs := cs4232.o
+snd-cs4236-objs := cs4236.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o
+obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o
+obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o
+obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o
+
+include $(TOPDIR)/Rules.make
+
+snd-cs4231-lib.o: $(snd-cs4231-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-lib-objs)
+
+snd-cs4236-lib.o: $(snd-cs4236-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-lib-objs)
+
+snd-cs4231.o: $(snd-cs4231-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-objs)
+
+snd-cs4232.o: $(snd-cs4232-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4232-objs)
+
+snd-cs4236.o: $(snd-cs4236-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-objs)
diff -Nru linux/sound/isa/cs423x/cs4231.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231.c
--- linux/sound/isa/cs423x/cs4231.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,211 @@
+/*
+ *  Generic driver for CS4231 chips
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Originally the CS4232/CS4232A driver, modified for use on CS4231 by
+ *  Tugrul Galatali <galatalt@stuy.edu>
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Generic CS4231");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Crystal Semiconductors,CS4231}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for CS4231 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for CS4231 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable CS4231 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_card_cs4231_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_card_cs4231 *acard;
+	snd_pcm_t *pcm = NULL;
+	cs4231_t *chip;
+	int err;
+
+	if (snd_port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify snd_port\n");
+		return -EINVAL;
+	}
+	if (snd_irq[dev] == SNDRV_AUTO_IRQ) {
+		snd_printk("specify snd_irq\n");
+		return -EINVAL;
+	}
+	if (snd_dma1[dev] == SNDRV_AUTO_DMA) {
+		snd_printk("specify snd_dma1\n");
+		return -EINVAL;
+	}
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_cs4231 *)card->private_data;
+	if (snd_mpu_port[dev] < 0)
+		snd_mpu_port[dev] = SNDRV_AUTO_PORT;
+	if ((err = snd_cs4231_create(card, snd_port[dev], -1,
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     snd_dma2[dev],
+				     CS4231_HW_DETECT,
+				     0, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
+					snd_mpu_port[dev], 0,
+					snd_mpu_irq[dev], SA_INTERRUPT,
+					NULL) < 0)
+			printk(KERN_ERR "cs4231: MPU401 not detected\n");
+	}
+	strcpy(card->driver, "CS4231");
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]);
+	if (snd_dma2[dev] >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_cs4231_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_cs4231_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_card_cs4231_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "CS4231 soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cs4231_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_cs4231_cards[idx]);
+}
+
+module_init(alsa_card_cs4231_init)
+module_exit(alsa_card_cs4231_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cs4231=snd_enable,snd_index,snd_id,
+			 snd_port,snd_mpu_port,snd_irq,snd_mpu_irq,
+			 snd_dma1,snd_dma2 */
+
+static int __init alsa_card_cs4231_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-cs4231=", alsa_card_cs4231_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/cs423x/cs4231_lib.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231_lib.c
--- linux/sound/isa/cs423x/cs4231_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1838 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
+ *
+ *  Bugs:
+ *     - sometimes record brokes playback with WSS portion of 
+ *       Yamaha OPL3-SA3 chip
+ *     - CS4231 (GUS MAX) - still trouble with occasional noises
+ *                        - broken initialization?
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/cs4231.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
+MODULE_LICENSE("GPL");
+
+#define chip_t cs4231_t
+
+#if 0
+#define SNDRV_DEBUG_MCE
+#endif
+
+/*
+ *  Some variables
+ */
+
+static unsigned char freq_bits[14] = {
+	/* 5510 */	0x00 | CS4231_XTAL2,
+	/* 6620 */	0x0E | CS4231_XTAL2,
+	/* 8000 */	0x00 | CS4231_XTAL1,
+	/* 9600 */	0x0E | CS4231_XTAL1,
+	/* 11025 */	0x02 | CS4231_XTAL2,
+	/* 16000 */	0x02 | CS4231_XTAL1,
+	/* 18900 */	0x04 | CS4231_XTAL2,
+	/* 22050 */	0x06 | CS4231_XTAL2,
+	/* 27042 */	0x04 | CS4231_XTAL1,
+	/* 32000 */	0x06 | CS4231_XTAL1,
+	/* 33075 */	0x0C | CS4231_XTAL2,
+	/* 37800 */	0x08 | CS4231_XTAL2,
+	/* 44100 */	0x0A | CS4231_XTAL2,
+	/* 48000 */	0x0C | CS4231_XTAL1
+};
+
+static unsigned int rates[14] = {
+	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
+	27042, 32000, 33075, 37800, 44100, 48000
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: 14,
+	list: rates,
+	mask: 0,
+};
+
+static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime)
+{
+	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+}
+
+static unsigned char snd_cs4231_original_image[32] =
+{
+	0x00,			/* 00/00 - lic */
+	0x00,			/* 01/01 - ric */
+	0x9f,			/* 02/02 - la1ic */
+	0x9f,			/* 03/03 - ra1ic */
+	0x9f,			/* 04/04 - la2ic */
+	0x9f,			/* 05/05 - ra2ic */
+	0xbf,			/* 06/06 - loc */
+	0xbf,			/* 07/07 - roc */
+	0x20,			/* 08/08 - pdfr */
+	CS4231_AUTOCALIB,	/* 09/09 - ic */
+	0x00,			/* 0a/10 - pc */
+	0x00,			/* 0b/11 - ti */
+	CS4231_MODE2,		/* 0c/12 - mi */
+	0xfc,			/* 0d/13 - lbc */
+	0x00,			/* 0e/14 - pbru */
+	0x00,			/* 0f/15 - pbrl */
+	0x80,			/* 10/16 - afei */
+	0x01,			/* 11/17 - afeii */
+	0x9f,			/* 12/18 - llic */
+	0x9f,			/* 13/19 - rlic */
+	0x00,			/* 14/20 - tlb */
+	0x00,			/* 15/21 - thb */
+	0x00,			/* 16/22 - la3mic/reserved */
+	0x00,			/* 17/23 - ra3mic/reserved */
+	0x00,			/* 18/24 - afs */
+	0x00,			/* 19/25 - lamoc/version */
+	0xcf,			/* 1a/26 - mioc */
+	0x00,			/* 1b/27 - ramoc/reserved */
+	0x20,			/* 1c/28 - cdfr */
+	0x00,			/* 1d/29 - res4 */
+	0x00,			/* 1e/30 - cbru */
+	0x00,			/* 1f/31 - cbrl */
+};
+
+/*
+ *  Basic I/O functions
+ */
+
+void snd_cs4231_outm(cs4231_t *chip, unsigned char reg,
+		     unsigned char mask, unsigned char value)
+{
+	int timeout;
+	unsigned char tmp;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	if (chip->calibrate_mute) {
+		chip->image[reg] &= mask;
+		chip->image[reg] |= value;
+	} else {
+		outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+		mb();
+		tmp = (chip->image[reg] & mask) | value;
+		outb(tmp, CS4231P(chip, REG));
+		chip->image[reg] = tmp;
+		mb();
+	}
+}
+
+static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(10);
+	outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+	outb(value, CS4231P(chip, REG));
+	mb();
+}
+
+void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+	outb(value, CS4231P(chip, REG));
+	chip->image[reg] = value;
+	mb();
+#if 0
+	printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value);
+#endif
+}
+
+unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
+#endif
+	outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+	mb();
+	return inb(CS4231P(chip, REG));
+}
+
+void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val)
+{
+	outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL));
+	outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG));
+	outb(val, CS4231P(chip, REG));
+	chip->eimage[CS4236_REG(reg)] = val;
+#if 0
+	printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+#endif
+}
+
+unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg)
+{
+	outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL));
+	outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG));
+#if 1
+	return inb(CS4231P(chip, REG));
+#else
+	{
+		unsigned char res;
+		res = inb(CS4231P(chip, REG));
+		printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
+		return res;
+	}
+#endif
+}
+
+#ifdef CONFIG_SND_DEBUG
+
+void snd_cs4231_debug(cs4231_t *chip)
+{
+	printk("CS4231 REGS:      INDEX = 0x%02x  ", inb(CS4231P(chip, REGSEL)));
+	printk("                 STATUS = 0x%02x\n", inb(CS4231P(chip, STATUS)));
+	printk("  0x00: left input      = 0x%02x  ", snd_cs4231_in(chip, 0x00));
+	printk("  0x10: alt 1 (CFIG 2)  = 0x%02x\n", snd_cs4231_in(chip, 0x10));
+	printk("  0x01: right input     = 0x%02x  ", snd_cs4231_in(chip, 0x01));
+	printk("  0x11: alt 2 (CFIG 3)  = 0x%02x\n", snd_cs4231_in(chip, 0x11));
+	printk("  0x02: GF1 left input  = 0x%02x  ", snd_cs4231_in(chip, 0x02));
+	printk("  0x12: left line in    = 0x%02x\n", snd_cs4231_in(chip, 0x12));
+	printk("  0x03: GF1 right input = 0x%02x  ", snd_cs4231_in(chip, 0x03));
+	printk("  0x13: right line in   = 0x%02x\n", snd_cs4231_in(chip, 0x13));
+	printk("  0x04: CD left input   = 0x%02x  ", snd_cs4231_in(chip, 0x04));
+	printk("  0x14: timer low       = 0x%02x\n", snd_cs4231_in(chip, 0x14));
+	printk("  0x05: CD right input  = 0x%02x  ", snd_cs4231_in(chip, 0x05));
+	printk("  0x15: timer high      = 0x%02x\n", snd_cs4231_in(chip, 0x15));
+	printk("  0x06: left output     = 0x%02x  ", snd_cs4231_in(chip, 0x06));
+	printk("  0x16: left MIC (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x16));
+	printk("  0x07: right output    = 0x%02x  ", snd_cs4231_in(chip, 0x07));
+	printk("  0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17));
+	printk("  0x08: playback format = 0x%02x  ", snd_cs4231_in(chip, 0x08));
+	printk("  0x18: IRQ status      = 0x%02x\n", snd_cs4231_in(chip, 0x18));
+	printk("  0x09: iface (CFIG 1)  = 0x%02x  ", snd_cs4231_in(chip, 0x09));
+	printk("  0x19: left line out   = 0x%02x\n", snd_cs4231_in(chip, 0x19));
+	printk("  0x0a: pin control     = 0x%02x  ", snd_cs4231_in(chip, 0x0a));
+	printk("  0x1a: mono control    = 0x%02x\n", snd_cs4231_in(chip, 0x1a));
+	printk("  0x0b: init & status   = 0x%02x  ", snd_cs4231_in(chip, 0x0b));
+	printk("  0x1b: right line out  = 0x%02x\n", snd_cs4231_in(chip, 0x1b));
+	printk("  0x0c: revision & mode = 0x%02x  ", snd_cs4231_in(chip, 0x0c));
+	printk("  0x1c: record format   = 0x%02x\n", snd_cs4231_in(chip, 0x1c));
+	printk("  0x0d: loopback        = 0x%02x  ", snd_cs4231_in(chip, 0x0d));
+	printk("  0x1d: var freq (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x1d));
+	printk("  0x0e: ply upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0e));
+	printk("  0x1e: ply lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1e));
+	printk("  0x0f: rec upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0f));
+	printk("  0x1f: rec lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1f));
+}
+
+#endif
+
+/*
+ *  CS4231 detection / MCE routines
+ */
+
+static void snd_cs4231_busy_wait(cs4231_t *chip)
+{
+	int timeout;
+
+	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
+	for (timeout = 5; timeout > 0; timeout--)
+		inb(CS4231P(chip, REGSEL));
+	/* end of cleanup sequence */
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(10);
+}
+
+void snd_cs4231_mce_up(cs4231_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (timeout = 250; timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("mce_up - auto calibration time out (0)\n");
+#endif
+	chip->mce_bit |= CS4231_MCE;
+	timeout = inb(CS4231P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if (!(timeout & CS4231_MCE))
+		outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+void snd_cs4231_mce_down(cs4231_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+	signed long time;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_busy_wait(chip);
+#if 0
+	printk("(1) timeout = %i\n", timeout);
+#endif
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", CS4231P(chip, REGSEL));
+#endif
+	chip->mce_bit &= ~CS4231_MCE;
+	timeout = inb(CS4231P(chip, REGSEL));
+	outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & CS4231_MCE) == 0 ||
+	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	snd_cs4231_busy_wait(chip);
+
+	/* calibration process */
+
+	for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--)
+		udelay(10);
+	if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) {
+		snd_printd("cs4231_mce_down - auto calibration time out (1)\n");
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+#if 0
+	printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies);
+#endif
+	time = HZ / 4;
+	while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (2)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+#if 0
+	printk("(3) jiffies = %li\n", jiffies);
+#endif
+	time = HZ / 10;
+	while (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (3)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);		
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	printk("(4) jiffies = %li\n", jiffies);
+	snd_printk("mce_down - exit = 0x%x\n", inb(CS4231P(chip, REGSEL)));
+#endif
+}
+
+static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size)
+{
+	switch (format & 0xe0) {
+	case CS4231_LINEAR_16:
+	case CS4231_LINEAR_16_BIG:
+		size >>= 1;
+		break;
+	case CS4231_ADPCM_16:
+		return size >> 2;
+	}
+	if (format & CS4231_STEREO)
+		size >>= 1;
+	return size;
+}
+
+static int snd_cs4231_trigger(snd_pcm_substream_t *substream,
+			      int cmd)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+#if 0
+	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(CS4231P(card, STATUS)));
+#endif
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == chip->playback_substream) {
+				what |= CS4231_PLAYBACK_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == chip->capture_substream) {
+				what |= CS4231_RECORD_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&chip->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			chip->image[CS4231_IFACE_CTRL] |= what;
+		else
+			chip->image[CS4231_IFACE_CTRL] &= ~what;
+		snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+		spin_unlock(&chip->reg_lock);
+		break;
+	}
+	default:
+		result = -EINVAL;
+		break;
+	}
+#if 0
+	snd_cs4231_debug(chip);
+#endif
+	return result;
+}
+
+/*
+ *  CODEC I/O
+ */
+
+static unsigned char snd_cs4231_get_rate(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < 14; i++)
+		if (rate == rates[i])
+			return freq_bits[i];
+	// snd_BUG();
+	return freq_bits[13];
+}
+
+static unsigned char snd_cs4231_get_format(cs4231_t *chip,
+				           int format,
+                                           int channels)
+{
+	unsigned char rformat;
+
+	rformat = CS4231_LINEAR_8;
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break;
+	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break;
+	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break;
+	}
+	if (channels > 1)
+		rformat |= CS4231_STEREO;
+#if 0
+	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+#endif
+	return rformat;
+}
+
+static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute)
+{
+	unsigned long flags;
+
+	mute = mute ? 1 : 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->calibrate_mute == mute) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	if (!mute) {
+		snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]);
+		snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]);
+		snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]);
+	}
+	snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
+	snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
+	snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
+	snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
+	snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
+	if (chip->hardware == CS4231_HW_INTERWAVE) {
+		snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]);
+		snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]);		
+		snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]);
+		snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]);
+	}
+	chip->calibrate_mute = mute;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4231_playback_format(cs4231_t *chip,
+				       snd_pcm_hw_params_t *params,
+				       unsigned char pdfr)
+{
+	unsigned long flags;
+	int full_calib = 1;
+
+	down(&chip->mce_mutex);
+	snd_cs4231_calibrate_mute(chip, 1);
+	if (chip->hardware == CS4231_HW_CS4231A ||
+	    (chip->hardware & CS4231_HW_CS4232_MASK)) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
+			full_calib = 0;
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	if (full_calib) {
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) {
+			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
+					(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
+					(pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
+				        pdfr);
+		} else {
+			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+	}
+	snd_cs4231_calibrate_mute(chip, 0);
+	up(&chip->mce_mutex);
+}
+
+static void snd_cs4231_capture_format(cs4231_t *chip,
+				      snd_pcm_hw_params_t *params,
+                                      unsigned char cdfr)
+{
+	unsigned long flags;
+	int full_calib = 1;
+
+	down(&chip->mce_mutex);
+	snd_cs4231_calibrate_mute(chip, 1);
+	if (chip->hardware == CS4231_HW_CS4231A ||
+	    (chip->hardware & CS4231_HW_CS4232_MASK)) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */
+		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+			snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr);
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
+			full_calib = 0;
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	if (full_calib) {
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if (chip->hardware != CS4231_HW_INTERWAVE) {
+			if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+				snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
+					       ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
+					       (cdfr & 0x0f));
+				spin_unlock_irqrestore(&chip->reg_lock, flags);
+				snd_cs4231_mce_down(chip);
+				snd_cs4231_mce_up(chip);
+				spin_lock_irqsave(&chip->reg_lock, flags);
+			}
+		}
+		snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+	}
+	snd_cs4231_calibrate_mute(chip, 0);
+	up(&chip->mce_mutex);
+}
+
+/*
+ *  Timer interface
+ */
+
+static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer)
+{
+	cs4231_t *chip = snd_timer_chip(timer);
+	if (chip->hardware & CS4231_HW_CS4236B_MASK)
+		return 14467;
+	else
+		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
+}
+
+static int snd_cs4231_timer_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned int ticks;
+	cs4231_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ticks = timer->sticks;
+	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
+	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
+	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
+		snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8));
+		snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks);
+		snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4231_timer_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	cs4231_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static void snd_cs4231_init(cs4231_t *chip)
+{
+	unsigned long flags;
+
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (1)\n");
+#endif
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+			     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO |
+			     CS4231_CALIB_MODE);
+	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
+	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (2)\n");
+#endif
+
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]);
+#endif
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (4)\n");
+#endif
+
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (5)\n");
+#endif
+}
+
+static int snd_cs4231_open(cs4231_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	if ((chip->mode & mode) ||
+	    ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) {
+		up(&chip->open_mutex);
+		return -EAGAIN;
+	}
+	if (chip->mode & CS4231_MODE_OPEN) {
+		chip->mode |= mode;
+		up(&chip->open_mutex);
+		return 0;
+	}
+	/* ok. now enable and ack CODEC IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
+		       CS4231_RECORD_IRQ |
+		       CS4231_TIMER_IRQ);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
+	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
+		       CS4231_RECORD_IRQ |
+		       CS4231_TIMER_IRQ);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = mode;
+	up(&chip->open_mutex);
+	return 0;
+}
+
+static void snd_cs4231_close(cs4231_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	chip->mode &= ~mode;
+	if (chip->mode & CS4231_MODE_OPEN) {
+		up(&chip->open_mutex);
+		return;
+	}
+	snd_cs4231_calibrate_mute(chip, 1);
+
+	/* disable IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
+	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+
+	/* now disable record & playback */
+
+	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+		snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+
+	/* clear IRQ again */
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_cs4231_calibrate_mute(chip, 0);
+
+	chip->mode = 0;
+	up(&chip->open_mutex);
+}
+
+/*
+ *  timer open/close
+ */
+
+static int snd_cs4231_timer_open(snd_timer_t * timer)
+{
+	cs4231_t *chip = snd_timer_chip(timer);
+	snd_cs4231_open(chip, CS4231_MODE_TIMER);
+	return 0;
+}
+
+static int snd_cs4231_timer_close(snd_timer_t * timer)
+{
+	cs4231_t *chip = snd_timer_chip(timer);
+	snd_cs4231_close(chip, CS4231_MODE_TIMER);
+	return 0;
+}
+
+static struct _snd_timer_hardware snd_cs4231_timer_table =
+{
+	flags:		SNDRV_TIMER_HW_AUTO,
+	resolution:	9945,
+	ticks:		65535,
+	open:		snd_cs4231_timer_open,
+	close:		snd_cs4231_timer_close,
+	c_resolution:	snd_cs4231_timer_resolution,
+	start:		snd_cs4231_timer_start,
+	stop:		snd_cs4231_timer_stop,
+};
+
+/*
+ *  ok.. exported functions..
+ */
+
+static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	unsigned char new_pdfr;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
+		   snd_cs4231_get_rate(params_rate(hw_params));
+	chip->set_playback_format(chip, hw_params, new_pdfr);
+	return 0;
+}
+
+static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->p_dma_size = size;
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
+	snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
+	snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+	snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	snd_cs4231_debug(chip);
+#endif
+	return 0;
+}
+
+static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	unsigned char new_cdfr;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
+		   snd_cs4231_get_rate(params_rate(hw_params));
+	chip->set_capture_format(chip, hw_params, new_cdfr);
+	return 0;
+}
+
+static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->c_dma_size = size;
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+	snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+	count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1;
+	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
+		snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+		snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
+	} else {
+		snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
+		snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8));
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static void snd_cs4231_overrange(cs4231_t *chip)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	res = snd_cs4231_in(chip, CS4231_TEST_INIT);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */
+		chip->capture_substream->runtime->overrange++;
+}
+
+void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return);
+	unsigned char status;
+
+	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
+	if (status & CS4231_TIMER_IRQ) {
+		if (chip->timer)
+			snd_timer_interrupt(chip->timer, chip->timer->sticks);
+	}		
+	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
+		if (status & CS4231_PLAYBACK_IRQ) {
+			if (chip->mode & CS4231_MODE_PLAY)
+				snd_pcm_period_elapsed(chip->playback_substream);
+			if (chip->mode & CS4231_MODE_RECORD) {
+				snd_cs4231_overrange(chip);
+				snd_pcm_period_elapsed(chip->capture_substream);
+			}
+		}
+	} else {
+		if (status & CS4231_PLAYBACK_IRQ)
+			snd_pcm_period_elapsed(chip->playback_substream);
+		if (status & CS4231_RECORD_IRQ) {
+			snd_cs4231_overrange(chip);
+			snd_pcm_period_elapsed(chip->capture_substream);
+		}
+	}
+
+	spin_lock(&chip->reg_lock);
+	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
+	spin_unlock(&chip->reg_lock);
+}
+
+static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
+		return 0;
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma1);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
+		return 0;
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static int snd_cs4231_probe(cs4231_t *chip)
+{
+	unsigned long flags;
+	int i, id, rev;
+	unsigned char *ptr;
+	unsigned int hw;
+
+#if 0
+	snd_cs4231_debug(chip);
+#endif
+	id = 0;
+	for (i = 0; i < 50; i++) {
+		mb();
+		if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+			udelay(2000);
+		else {
+			spin_lock_irqsave(&chip->reg_lock, flags);
+			snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
+			id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
+			spin_unlock_irqrestore(&chip->reg_lock, flags);
+			if (id == 0x0a)
+				break;	/* this is valid value */
+		}
+	}
+	snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id);
+	if (id != 0x0a)
+		return -ENODEV;	/* no valid device found */
+
+	if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
+		rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7;
+		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
+		if (rev == 0x80) {
+			chip->hardware = CS4231_HW_CS4231;
+		} else if (rev == 0xa0) {
+			chip->hardware = CS4231_HW_CS4231A;
+		} else if (rev == 0xa2) {
+			chip->hardware = CS4231_HW_CS4232;
+		} else if (rev == 0xb2) {
+			chip->hardware = CS4231_HW_CS4232A;
+		} else if (rev == 0x83) {
+			chip->hardware = CS4231_HW_CS4236;
+		} else if (rev == 0x03) {
+			chip->hardware = CS4231_HW_CS4236B;
+		} else {
+			snd_printk("unknown CS chip with version 0x%x\n", rev);
+			return -ENODEV;		/* unknown CS4231 chip? */
+		}
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	inb(CS4231P(chip, STATUS));	/* clear any pendings IRQ */
+	outb(0, CS4231P(chip, STATUS));
+	mb();
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
+	switch (chip->hardware) {
+	case CS4231_HW_INTERWAVE:
+		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
+		break;
+	case CS4231_HW_CS4235:
+	case CS4231_HW_CS4236B:
+	case CS4231_HW_CS4237B:
+	case CS4231_HW_CS4238B:
+	case CS4231_HW_CS4239:
+		if (hw == CS4231_HW_DETECT3)
+			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
+		else
+			chip->hardware = CS4231_HW_CS4236;
+		break;
+	}
+
+	chip->image[CS4231_IFACE_CTRL] =
+	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
+	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
+	chip->image[CS4231_ALT_FEATURE_1] = 0x80;
+	chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
+	ptr = (unsigned char *) &chip->image;
+	snd_cs4231_mce_down(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 32; i++)	/* ok.. fill all CS4231 registers */
+		snd_cs4231_out(chip, i, *ptr++);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_up(chip);
+	snd_cs4231_mce_down(chip);
+
+	mdelay(2);
+
+	/* ok.. try check hardware version for CS4236+ chips */
+	if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
+		if (chip->hardware == CS4231_HW_CS4236B) {
+			rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
+			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
+			id = snd_cs4236_ext_in(chip, CS4236_VERSION);
+			snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
+			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
+			if ((id & 0x1f) == 0x1d) {	/* CS4235 */
+				chip->hardware = CS4231_HW_CS4235;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+					break;
+				default:
+					snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					chip->hardware = CS4231_HW_CS4236B;
+					break;
+				default:
+					snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */
+				chip->hardware = CS4231_HW_CS4237B;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					break;
+				default:
+					snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */
+				chip->hardware = CS4231_HW_CS4238B;
+				switch (id >> 5) {
+				case 5:
+				case 6:
+				case 7:
+					break;
+				default:
+					snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */
+				chip->hardware = CS4231_HW_CS4239;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+					break;
+				default:
+					snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else {
+				snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
+			}
+		}
+	}
+	return 0;		/* all things are ok.. */
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_cs4231_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_cs4231_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+
+ */
+
+static int snd_cs4231_playback_open(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	runtime->hw = snd_cs4231_playback;
+
+	/* hardware bug in InterWave chipset */
+	if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3)
+	    	runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
+	
+	/* hardware limitation of cheap chips */
+	if (chip->hardware == CS4231_HW_CS4235 ||
+	    chip->hardware == CS4231_HW_CS4239)
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
+
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+
+	if (chip->claim_dma) {
+		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
+			return err;
+	}
+
+	if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) {
+		if (chip->release_dma)
+			chip->release_dma(chip, chip->dma_private_data, chip->dma1);
+		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+		return err;
+	}
+	chip->playback_substream = substream;
+	snd_pcm_set_sync(substream);
+	chip->rate_constraint(runtime);
+	return 0;
+}
+
+static int snd_cs4231_capture_open(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	runtime->hw = snd_cs4231_capture;
+
+	/* hardware limitation of cheap chips */
+	if (chip->hardware == CS4231_HW_CS4235 ||
+	    chip->hardware == CS4231_HW_CS4239)
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
+
+	if (chip->claim_dma) {
+		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
+			return err;
+	}
+
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+
+	if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) {
+		if (chip->release_dma)
+			chip->release_dma(chip, chip->dma_private_data, chip->dma2);
+		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+		return err;
+	}
+	chip->capture_substream = substream;
+	snd_pcm_set_sync(substream);
+	chip->rate_constraint(runtime);
+	return 0;
+}
+
+static int snd_cs4231_playback_close(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_cs4231_close(chip, CS4231_MODE_PLAY);
+	return 0;
+}
+
+static int snd_cs4231_capture_close(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_cs4231_close(chip, CS4231_MODE_RECORD);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static void snd_cs4231_suspend(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++)
+		chip->image[reg] = snd_cs4231_in(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4231_resume(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	int timeout;
+	
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++) {
+		switch (reg) {
+		case CS4231_VERSION:
+			break;
+		default:
+			snd_cs4231_out(chip, reg, chip->image[reg]);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	snd_cs4231_mce_down(chip);
+#else
+	/* The following is a workaround to avoid freeze after resume on TP600E.
+	   This is the first half of copy of snd_cs4231_mce_down(), but doesn't
+	   include rescheduling.  -- iwai
+	   */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_busy_wait(chip);
+	chip->mce_bit &= ~CS4231_MCE;
+	timeout = inb(CS4231P(chip, REGSEL));
+	outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & CS4231_MCE) == 0 ||
+	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	snd_cs4231_busy_wait(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#endif
+}
+
+static int snd_cs4231_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, dev->data, return 0);
+
+	switch (rqst) {
+	case PM_SUSPEND:
+		if (chip->suspend)
+			(*chip->suspend)(chip);
+		break;
+	case PM_RESUME:
+		if (chip->resume)
+			(*chip->resume)(chip);
+		break;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static int snd_cs4231_free(cs4231_t *chip)
+{
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->res_cport) {
+		release_resource(chip->res_cport);
+		kfree_nocheck(chip->res_cport);
+	}
+	if (chip->irq >= 0) {
+		disable_irq(chip->irq);
+		if (!(chip->hwshare & CS4231_HWSHARE_IRQ))
+			free_irq(chip->irq, (void *) chip);
+	}
+	if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) {
+		snd_dma_disable(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
+		snd_dma_disable(chip->dma2);
+		free_dma(chip->dma2);
+	}
+#ifdef CONFIG_PM
+	if (chip->pm_dev)
+		pm_unregister(chip->pm_dev);
+#endif
+	if (chip->timer)
+		snd_device_free(chip->card, chip->timer);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_cs4231_dev_free(snd_device_t *device)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO);
+	return snd_cs4231_free(chip);	
+}
+
+const char *snd_cs4231_chip_id(cs4231_t *chip)
+{
+	switch (chip->hardware) {
+	case CS4231_HW_CS4231:	return "CS4231";
+	case CS4231_HW_CS4231A: return "CS4231A";
+	case CS4231_HW_CS4232:	return "CS4232";
+	case CS4231_HW_CS4232A:	return "CS4232A";
+	case CS4231_HW_CS4235:	return "CS4235";
+	case CS4231_HW_CS4236B: return "CS4236B";
+	case CS4231_HW_CS4237B: return "CS4237B";
+	case CS4231_HW_CS4238B: return "CS4238B";
+	case CS4231_HW_CS4239:	return "CS4239";
+	case CS4231_HW_INTERWAVE: return "AMD InterWave";
+	case CS4231_HW_OPL3SA2: return chip->card->shortname;
+	default: return "???";
+	}
+}
+
+int snd_cs4231_create(snd_card_t * card,
+	              unsigned long port,
+	              unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip)
+{
+	static snd_device_ops_t ops = {
+		dev_free:       snd_cs4231_dev_free,
+	};                                
+	cs4231_t *chip;
+	int err;
+
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->irq = -1;
+	chip->dma1 = -1;
+	chip->dma2 = -1;
+	chip->hardware = hardware;
+	chip->hwshare = hwshare;
+
+	if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	chip->port = port;
+	if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) {
+		snd_cs4231_free(chip);
+		return -ENODEV;
+	}
+	chip->cport = cport;
+	if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+	if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	if (dma1 == dma2 || dma2 < 0) {
+		chip->single_dma = 1;
+		chip->dma2 = chip->dma1;
+	} else
+		chip->dma2 = dma2;
+
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->mce_mutex);
+	init_MUTEX(&chip->open_mutex);
+	chip->card = card;
+	chip->rate_constraint = snd_cs4231_xrate;
+	chip->set_playback_format = snd_cs4231_playback_format;
+	chip->set_capture_format = snd_cs4231_capture_format;
+        memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image));
+
+	/* global setup */
+	if (snd_cs4231_probe(chip) < 0) {
+		snd_cs4231_free(chip);
+		return -ENODEV;
+	}
+	snd_cs4231_init(chip);
+
+	if (chip->hardware & CS4231_HW_CS4232_MASK) {
+		if (chip->res_cport == NULL)
+			snd_printk("CS4232 control port features are not accessible\n");
+	}
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_cs4231_free(chip);
+		return err;
+	}
+
+#ifdef CONFIG_PM
+	/* Power Management */
+	chip->suspend = snd_cs4231_suspend;
+	chip->resume = snd_cs4231_resume;
+	chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_cs4231_pm_callback);
+	if (chip->pm_dev)
+		chip->pm_dev->data = chip;
+#endif
+
+	*rchip = chip;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_cs4231_playback_ops = {
+	open:		snd_cs4231_playback_open,
+	close:		snd_cs4231_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cs4231_playback_hw_params,
+	hw_free:	snd_cs4231_playback_hw_free,
+	prepare:	snd_cs4231_playback_prepare,
+	trigger:	snd_cs4231_trigger,
+	pointer:	snd_cs4231_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_cs4231_capture_ops = {
+	open:		snd_cs4231_capture_open,
+	close:		snd_cs4231_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cs4231_capture_hw_params,
+	hw_free:	snd_cs4231_capture_hw_free,
+	prepare:	snd_cs4231_capture_prepare,
+	trigger:	snd_cs4231_trigger,
+	pointer:	snd_cs4231_capture_pointer,
+};
+
+static void snd_cs4231_pcm_free(snd_pcm_t *pcm)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_cs4231_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->mce_mutex);
+	init_MUTEX(&chip->open_mutex);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops);
+	
+	/* global setup */
+	pcm->private_data = chip;
+	pcm->private_free = snd_cs4231_pcm_free;
+	pcm->info_flags = 0;
+	if (chip->single_dma)
+		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+	if (chip->hardware != CS4231_HW_INTERWAVE)
+		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+	strcpy(pcm->name, snd_cs4231_chip_id(chip));
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static void snd_cs4231_timer_free(snd_timer_t *timer)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return);
+	chip->timer = NULL;
+}
+
+int snd_cs4231_timer(cs4231_t *chip, int device, snd_timer_t **rtimer)
+{
+	snd_timer_t *timer;
+	snd_timer_id_t tid;
+	int err;
+
+	/* Timer initialization */
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = chip->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
+		return err;
+	strcpy(timer->name, snd_cs4231_chip_id(chip));
+	timer->private_data = chip;
+	timer->private_free = snd_cs4231_timer_free;
+	timer->hw = snd_cs4231_timer_table;
+	chip->timer = timer;
+	if (rtimer)
+		*rtimer = timer;
+	return 0;
+}
+	
+/*
+ *  MIXER part
+ */
+
+static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {
+		"Line", "Aux", "Mic", "Mix"
+	};
+	static char *opl3sa_texts[4] = {
+		"Line", "CD", "Mic", "Mix"
+	};
+	static char *gusmax_texts[4] = {
+		"Line", "Synth", "Mic", "Mix"
+	};
+	char **ptexts = texts;
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+
+	snd_assert(chip->card != NULL, return -EINVAL);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	if (!strcmp(chip->card->driver, "GUS MAX"))
+		ptexts = gusmax_texts;
+	switch (chip->hardware) {
+	case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break;
+	case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break;
+	}
+	strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_cs4231_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4231_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
+	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
+	change = left != chip->image[CS4231_LEFT_INPUT] ||
+	         right != chip->image[CS4231_RIGHT_INPUT];
+	snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
+	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_cs4231_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
+	snd_cs4231_out(chip, left_reg, val1);
+	snd_cs4231_out(chip, right_reg, val2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4231_controls[] = {
+CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
+CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
+CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_cs4231_info_mux,
+	get: snd_cs4231_get_mux,
+	put: snd_cs4231_put_mux,
+},
+CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1)
+};
+                                        
+int snd_cs4231_mixer(cs4231_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, chip->pcm->name);
+
+	for (idx = 0; idx < CS4231_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_cs4231_out);
+EXPORT_SYMBOL(snd_cs4231_in);
+EXPORT_SYMBOL(snd_cs4231_outm);
+EXPORT_SYMBOL(snd_cs4236_ext_out);
+EXPORT_SYMBOL(snd_cs4236_ext_in);
+EXPORT_SYMBOL(snd_cs4231_mce_up);
+EXPORT_SYMBOL(snd_cs4231_mce_down);
+EXPORT_SYMBOL(snd_cs4231_interrupt);
+EXPORT_SYMBOL(snd_cs4231_chip_id);
+EXPORT_SYMBOL(snd_cs4231_create);
+EXPORT_SYMBOL(snd_cs4231_pcm);
+EXPORT_SYMBOL(snd_cs4231_mixer);
+EXPORT_SYMBOL(snd_cs4231_timer);
+EXPORT_SYMBOL(snd_cs4231_info_single);
+EXPORT_SYMBOL(snd_cs4231_get_single);
+EXPORT_SYMBOL(snd_cs4231_put_single);
+EXPORT_SYMBOL(snd_cs4231_info_double);
+EXPORT_SYMBOL(snd_cs4231_get_double);
+EXPORT_SYMBOL(snd_cs4231_put_double);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_cs4231_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_cs4231_exit(void)
+{
+}
+
+module_init(alsa_cs4231_init)
+module_exit(alsa_cs4231_exit)
diff -Nru linux/sound/isa/cs423x/cs4232.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4232.c
--- linux/sound/isa/cs423x/cs4232.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4232.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,2 @@
+#define CS4232
+#include "cs4236.c"
diff -Nru linux/sound/isa/cs423x/cs4236.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236.c
--- linux/sound/isa/cs423x/cs4236.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,641 @@
+/*
+ *  Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+#ifdef CS4232
+MODULE_DESCRIPTION("Cirrus Logic CS4232");
+MODULE_DEVICES("{{Turtle Beach,TBS-2000},"
+		"{Turtle Beach,Tropez Plus},"
+		"{SIC CrystalWave 32},"
+		"{Hewlett Packard,Omnibook 5500},"
+		"{TerraTec,Maestro 32/96},"
+		"{Philips,PCA70PS}}");
+#else
+MODULE_DESCRIPTION("Cirrus Logic CS4235-9");
+MODULE_DEVICES("{{Crystal Semiconductors,CS4235},"
+		"{Crystal Semiconductors,CS4236},"
+		"{Crystal Semiconductors,CS4237},"
+		"{Crystal Semiconductors,CS4238},"
+		"{Crystal Semiconductors,CS4239},"
+		"{Acer,AW37},"
+		"{Acer,AW35/Pro},"
+		"{Crystal,3D},"
+		"{Crystal Computer,TidalWave128},"
+		"{Dell,Optiplex GX1},"
+		"{Dell,Workstation 400 sound},"
+		"{EliteGroup,P5TX-LA sound},"
+		"{Gallant,SC-70P},"
+		"{Gateway,E1000 Onboard CS4236B},"
+		"{Genius,Sound Maker 3DJ},"
+		"{Hewlett Packard,HP6330 sound},"
+		"{IBM,PC 300PL sound},"
+		"{IBM,Aptiva 2137 E24},"
+		"{IBM,IntelliStation M Pro},"
+		"{Intel,Marlin Spike Mobo CS4235},"
+		"{Guillemot,MaxiSound 16 PnP},"
+		"{NewClear,3D},"
+		"{TerraTec,AudioSystem EWS64L/XL},"
+		"{Typhoon Soundsystem,CS4236B},"
+		"{Turtle Beach,Malibu},"
+		"{Unknown,Digital PC 5000 Onboard}}");
+#endif
+
+#ifdef CS4232
+#define IDENT "CS4232"
+#else
+#define IDENT "CS4236+"
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+#ifdef __ISAPNP__
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_cport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_cport, "Control port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_cport, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sb_port, "SB port # for " IDENT " driver (optional).");
+MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+struct snd_card_cs4236 {
+	struct resource *res_sb_port;
+#ifdef __ISAPNP__
+	struct isapnp_dev *wss;
+	struct isapnp_dev *ctrl;
+	struct isapnp_dev *mpu;
+#endif
+};
+
+static snd_card_t *snd_cs4236_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_cs4236_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_cs4236_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \
+        }
+#define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \
+			 ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \
+        }
+#define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \
+        }
+
+
+#ifdef CS4232
+static struct isapnp_card_id snd_card_pnpids[] __devinitdata = {
+	/* Philips PCA70PS */
+	ISAPNP_CS4232_1('C','S','C',0x0d32,0x0000,0x0010,0xb006),
+	/* TerraTec Maestro 32/96 (CS4232) */
+	ISAPNP_CS4232('C','S','C',0x1a32,0x0000,0x0010,0x0003),
+	/* HP Omnibook 5500 onboard */
+	ISAPNP_CS4232('C','S','C',0x4232,0x0000,0x0002,0x0003),
+	/* Turtle Beach TBS-2000 (CS4232) */
+	ISAPNP_CS4232('C','S','C',0x7532,0x0000,0x0010,0xb006),
+	/* Turtle Beach Tropez Plus (CS4232) */
+	ISAPNP_CS4232_1('C','S','C',0x7632,0x0000,0x0010,0xb006),
+	/* SIC CrystalWave 32 (CS4232) */
+	ISAPNP_CS4232('C','S','C',0xf032,0x0000,0x0010,0x0003),
+	/* --- */
+	{ ISAPNP_CARD_END, }	/* end */
+};
+#else /* CS4236 */
+static struct isapnp_card_id snd_card_pnpids[] __devinitdata = {
+	/* Intel Marlin Spike Motherboard - CS4235 */
+	ISAPNP_CS4232('C','S','C',0x0225,0x0000,0x0010,0x0003),
+	/* Intel Marlin Spike Motherboard (#2) - CS4235 */
+	ISAPNP_CS4232('C','S','C',0x0225,0x0100,0x0110,0x0103),
+	/* Genius Sound Maker 3DJ - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x0437,0x0000,0x0010,0x0003),
+	/* Digital PC 5000 Onboard - CS4236B */
+	ISAPNP_CS4232_WOMPU('C','S','C',0x0735,0x0000,0x0010),
+	/* some uknown CS4236B */
+	ISAPNP_CS4232('C','S','C',0x0b35,0x0000,0x0010,0x0003),
+	/* CS4235 on mainboard without MPU */
+	ISAPNP_CS4232_WOMPU('C','S','C',0x1425,0x0100,0x0110),
+	/* Gateway E1000 Onboard CS4236B */
+	ISAPNP_CS4232('C','S','C',0x1335,0x0000,0x0010,0x0003),
+	/* HP 6330 Onboard sound */
+	ISAPNP_CS4232('C','S','C',0x1525,0x0100,0x0110,0x0103),
+	/* Crystal Computer TidalWave128 */
+	ISAPNP_CS4232('C','S','C',0x1e37,0x0000,0x0010,0x0003),
+	/* ACER AW37 - CS4235 */
+	ISAPNP_CS4232('C','S','C',0x4236,0x0000,0x0010,0x0003),
+	/* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x4237,0x0000,0x0010,0x0003),
+	/* Crystal 3D - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x4336,0x0000,0x0010,0x0003),
+	/* Typhoon Soundsystem PnP - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x4536,0x0000,0x0010,0x0003),
+	/* TerraTec AudioSystem EWS64XL - CS4236B */
+	ISAPNP_CS4232('C','S','C',0xa836,0xa800,0xa810,0xa803),
+	/* Crystal Semiconductors CS4237B */
+	ISAPNP_CS4232('C','S','C',0x4637,0x0000,0x0010,0x0003),
+	/* NewClear 3D - CX4237B-XQ3 */
+	ISAPNP_CS4232('C','S','C',0x4837,0x0000,0x0010,0x0003),
+	/* Dell Optiplex GX1 - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x6835,0x0000,0x0010,0x0003),
+	/* Dell P410 motherboard - CS4236B */
+	ISAPNP_CS4232_WOMPU('C','S','C',0x6835,0x0000,0x0010),
+	/* Dell Workstation 400 Onboard - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x6836,0x0000,0x0010,0x0003),
+	/* Turtle Beach Malibu - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x7537,0x0000,0x0010,0x0003),
+	/* CS4235 - onboard */
+	ISAPNP_CS4232('C','S','C',0x8025,0x0100,0x0110,0x0103),
+	/* IBM PC 300PL Onboard - CS4236B */
+	ISAPNP_CS4232_WOMPU('C','S','C',0xe836,0x0000,0x0010),
+	/* IBM Aptiva 2137 E24 Onboard - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x8037,0x0000,0x0010,0x0003),
+	/* IBM IntelliStation M Pro motherboard */
+	ISAPNP_CS4232_WOMPU('C','S','C',0xc835,0x0000,0x0010),
+	/* Guillemot MaxiSound 16 PnP - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x9836,0x0000,0x0010,0x0003),
+	/* Gallant SC-70P */
+	ISAPNP_CS4232('C','S','C',0x9837,0x0000,0x0010,0x0003),
+	/* ACER AW37/Pro - CS4235 */
+	ISAPNP_CS4232('C','S','C',0xd925,0x0000,0x0010,0x0003),
+	/* ACER AW35/Pro - CS4237B */
+	ISAPNP_CS4232('C','S','C',0xd937,0x0000,0x0010,0x0003),
+	/* CS4235 without MPU401 */
+	ISAPNP_CS4232_WOMPU('C','S','C',0xe825,0x0100,0x0110),
+	/* CS4236B */
+	ISAPNP_CS4232('C','S','C',0xf235,0x0000,0x0010,0x0003),
+	/* CS4236B */
+	ISAPNP_CS4232('C','S','C',0xf238,0x0000,0x0010,0x0003),
+	/* --- */
+	{ ISAPNP_CARD_END, }	/* end */
+};
+#endif
+
+ISAPNP_CARD_TABLE(snd_card_pnpids);
+
+static int __init snd_card_cs4236_isapnp(int dev, struct snd_card_cs4236 *acard)
+{
+	const struct isapnp_card_id *id = snd_cs4236_isapnp_id[dev];
+	struct isapnp_card *card = snd_cs4236_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+	
+	acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->wss->active) {
+		acard->wss = NULL;
+		return -EBUSY;
+	}
+	acard->ctrl = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->ctrl->active) {
+		acard->wss = acard->ctrl = NULL;
+		return -EBUSY;
+	}
+	if (id->devs[2].vendor && id->devs[2].function) {
+		acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+		if (acard->mpu->active) {
+			acard->wss = acard->ctrl = acard->mpu = NULL;
+			return -EBUSY;
+		}
+	}
+
+	/* WSS initialization */
+	pdev = acard->wss;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 4);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_sb_port[dev], 16);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev] < 0 ? 4 : snd_dma2[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR IDENT " isapnp configure failed for WSS (out of resources?)\n");
+		return -EBUSY;
+	}
+	snd_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_sb_port[dev] = pdev->resource[2].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start == 4 ? -1 : pdev->dma_resource[1].start;
+	snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",
+			snd_port[dev], snd_fm_port[dev], snd_sb_port[dev]);
+	snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",
+			snd_irq[dev], snd_dma1[dev], snd_dma2[dev]);
+	/* CTRL initialization */
+	pdev = acard->ctrl;
+	if (pdev->prepare(pdev) < 0) {
+		acard->wss->deactivate(acard->wss);
+		return -EAGAIN;
+	}
+	if (snd_cport[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_cport[dev], 8);
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR IDENT " isapnp configure failed for control (out of resources?)\n");
+		acard->wss->deactivate(acard->wss);
+		return -EBUSY;
+	}
+	snd_cport[dev] = pdev->resource[0].start;
+	snd_printdd("isapnp CTRL: control port=0x%lx\n", snd_cport[dev]);
+	/* MPU initialization */
+	if (acard->mpu) {
+		pdev = acard->mpu;
+		if (pdev->prepare(pdev) < 0) {
+			acard->wss->deactivate(acard->wss);
+			acard->ctrl->deactivate(acard->ctrl);
+			return -EAGAIN;
+		}
+		if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+			isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], 2);
+		if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+			isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], 1);
+		if (pdev->activate(pdev)<0) {
+			snd_mpu_port[dev] = SNDRV_AUTO_PORT;
+			snd_mpu_irq[dev] = SNDRV_AUTO_IRQ;
+			printk(KERN_ERR IDENT " isapnp configure failed for MPU (out of resources?)\n");
+		} else {
+			snd_mpu_port[dev] = pdev->resource[0].start;
+			if (pdev->irq_resource[0].flags & IORESOURCE_IRQ) {
+				snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+			} else {
+				snd_mpu_irq[dev] = -1;	/* disable interrupt */
+			}
+		}
+		snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", snd_mpu_port[dev], snd_mpu_irq[dev]);
+	}
+	return 0;
+}
+
+static void snd_card_cs4236_deactivate(struct snd_card_cs4236 *acard)
+{
+	if (acard->wss) {
+		acard->wss->deactivate(acard->wss);
+		acard->wss = NULL;
+	}
+	if (acard->ctrl) {
+		acard->ctrl->deactivate(acard->ctrl);
+		acard->ctrl = NULL;
+	}
+	if (acard->mpu) {
+		acard->mpu->deactivate(acard->mpu);
+		acard->mpu = NULL;
+	}
+}
+#endif
+
+static void snd_card_cs4236_free(snd_card_t *card)
+{
+	struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_cs4236_deactivate(acard);
+#endif
+		if (acard->res_sb_port) {
+			release_resource(acard->res_sb_port);
+			kfree_nocheck(acard->res_sb_port);
+		}
+	}
+}
+
+static int __init snd_card_cs4236_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_card_cs4236 *acard;
+	snd_pcm_t *pcm = NULL;
+	cs4231_t *chip;
+	opl3_t *opl3;
+	int err;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_port\n");
+			return -EINVAL;
+		}
+		if (snd_cport[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_cport\n");
+			return -EINVAL;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_card_cs4236));
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_cs4236 *)card->private_data;
+	card->private_free = snd_card_cs4236_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_card_cs4236_isapnp(dev, acard))<0) {
+		printk(KERN_ERR "isapnp detection failed and probing for " IDENT " is not supported\n");
+		snd_card_free(card);
+		return -ENXIO;
+	}
+#endif
+	if (snd_mpu_port[dev] < 0)
+		snd_mpu_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_fm_port[dev] < 0)
+		snd_fm_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_sb_port[dev] < 0)
+		snd_sb_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
+		if ((acard->res_sb_port = request_region(snd_sb_port[dev], 16, IDENT " SB")) == NULL) {
+			printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", snd_sb_port[dev]);
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+
+#ifdef CS4232
+	if ((err = snd_cs4231_create(card,
+				     snd_port[dev],
+				     snd_cport[dev],
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     snd_dma2[dev],
+				     CS4231_HW_DETECT,
+				     0,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+#else /* CS4236 */
+	if ((err = snd_cs4236_create(card,
+				     snd_port[dev],
+				     snd_cport[dev],
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     snd_dma2[dev],
+				     CS4231_HW_DETECT,
+				     0,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4236_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_OPL3_CS, 0, &opl3) < 0) {
+			printk(KERN_ERR IDENT ": OPL3 not detected\n");
+		} else {
+			if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return err;
+			}
+		}
+	}
+
+	if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
+					snd_mpu_port[dev], 0,
+					snd_mpu_irq[dev],
+					snd_mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0)
+			printk(KERN_ERR IDENT ": MPU401 not detected\n");
+	}
+	strcpy(card->driver, pcm->name);
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i",
+		pcm->name,
+		chip->port,
+		snd_irq[dev],
+		snd_dma1[dev]);
+	if (snd_dma1[dev] >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_cs4236_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_cs4236_isapnp_detect(struct isapnp_card *card,
+                                           const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_cs4236_isapnp_cards[dev] = card;
+		snd_cs4236_isapnp_id[dev] = id;
+		res = snd_card_cs4236_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_cs423x_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_card_cs4236_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_card_pnpids, snd_cs4236_isapnp_detect);
+#endif
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR IDENT " soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cs423x_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_cs4236_cards[idx]);
+}
+
+module_init(alsa_card_cs423x_init)
+module_exit(alsa_card_cs423x_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cs4232=snd_enable,snd_index,snd_id,snd_isapnp,snd_port,
+			 snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port,
+			 snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size,
+			 snd_dma2,snd_dma2_size */
+/* format is: snd-cs4236=snd_enable,snd_index,snd_id,snd_isapnp,snd_port,
+			 snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port,
+			 snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size,
+			 snd_dma2,snd_dma2_size */
+
+static int __init alsa_card_cs423x_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_cport[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+#ifdef CS4232
+__setup("snd-cs4232=", alsa_card_cs423x_setup);
+#else /* CS4236 */
+__setup("snd-cs4236=", alsa_card_cs423x_setup);
+#endif
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/cs423x/cs4236_lib.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236_lib.c
--- linux/sound/isa/cs423x/cs4236_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,980 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of CS4235/4236B/4237B/4238B/4239 chips
+ *
+ *  Note:
+ *     -----
+ *
+ *  Bugs:
+ *     -----
+ *
+ *   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
+ *
+ */
+
+/*
+ *  Indirect control registers (CS4236B+)
+ * 
+ *  C0
+ *     D8: WSS reset (all chips)
+ *
+ *  C1 (all chips except CS4236)
+ *     D7-D5: version 
+ *     D4-D0: chip id
+ *             11101 - CS4235
+ *             01011 - CS4236B
+ *             01000 - CS4237B
+ *             01001 - CS4238B
+ *             11110 - CS4239
+ *
+ *  C2
+ *     D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239)
+ *     D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B)
+ * 
+ *  C3
+ *     D7: 3D Enable (CS4237B)
+ *     D6: 3D Mono Enable (CS4237B)
+ *     D5: 3D Serial Output (CS4237B,CS4238B)
+ *     D4: 3D Enable (CS4235,CS4238B,CS4239)
+ *
+ *  C4
+ *     D7: consumer serial port enable (CS4237B,CS4238B)
+ *     D6: channels status block reset (CS4237B,CS4238B)
+ *     D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B)
+ *     D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B)
+ * 
+ *  C5  lower channel status (digital serial data description) (CS4237B,CS4238B)
+ *     D7-D6: first two bits of category code
+ *     D5: lock
+ *     D4-D3: pre-emphasis (0 = none, 1 = 50/15us)
+ *     D2: copy/copyright (0 = copy inhibited)
+ *     D1: 0 = digital audio / 1 = non-digital audio
+ *     
+ *  C6  upper channel status (digital serial data description) (CS4237B,CS4238B)
+ *     D7-D6: sample frequency (0 = 44.1kHz)
+ *     D5: generation status (0 = no indication, 1 = original/commercially precaptureed data)
+ *     D4-D0: category code (upper bits)
+ *
+ *  C7  reserved (must write 0)
+ *
+ *  C8  wavetable control
+ *     D7: volume control interrupt enable (CS4235,CS4239)
+ *     D6: hardware volume control format (CS4235,CS4239)
+ *     D3: wavetable serial port enable (all chips)
+ *     D2: DSP serial port switch (all chips)
+ *     D1: disable MCLK (all chips)
+ *     D0: force BRESET low (all chips)
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/cs4231.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips");
+MODULE_LICENSE("GPL");
+
+#define chip_t cs4231_t
+
+/*
+ *
+ */
+
+static unsigned char snd_cs4236_ext_map[18] = {
+	/* CS4236_LEFT_LINE */		0xff,
+	/* CS4236_RIGHT_LINE */		0xff,
+	/* CS4236_LEFT_MIC */		0xdf,
+	/* CS4236_RIGHT_MIC */		0xdf,
+	/* CS4236_LEFT_MIX_CTRL */	0xe0 | 0x18,
+	/* CS4236_RIGHT_MIX_CTRL */	0xe0,
+	/* CS4236_LEFT_FM */		0xbf,
+	/* CS4236_RIGHT_FM */		0xbf,
+	/* CS4236_LEFT_DSP */		0xbf,
+	/* CS4236_RIGHT_DSP */		0xbf,
+	/* CS4236_RIGHT_LOOPBACK */	0xbf,
+	/* CS4236_DAC_MUTE */		0xe0,
+	/* CS4236_ADC_RATE */		0x01,	/* 48kHz */
+	/* CS4236_DAC_RATE */		0x01,	/* 48kHz */
+	/* CS4236_LEFT_MASTER */	0xbf,
+	/* CS4236_RIGHT_MASTER */	0xbf,
+	/* CS4236_LEFT_WAVE */		0xbf,
+	/* CS4236_RIGHT_WAVE */		0xbf
+};
+
+/*
+ *
+ */
+
+static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val)
+{
+	outb(reg, chip->cport + 3);
+	outb(chip->cimage[reg] = val, chip->cport + 4);
+}
+
+static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg)
+{
+	outb(reg, chip->cport + 3);
+	return inb(chip->cport + 4);
+}
+
+/*
+ *  PCM
+ */
+
+#define CLOCKS 8
+
+static ratnum_t clocks[CLOCKS] = {
+	{ num: 16934400, den_min: 353, den_max: 353, den_step: 1 },
+	{ num: 16934400, den_min: 529, den_max: 529, den_step: 1 },
+	{ num: 16934400, den_min: 617, den_max: 617, den_step: 1 },
+	{ num: 16934400, den_min: 1058, den_max: 1058, den_step: 1 },
+	{ num: 16934400, den_min: 1764, den_max: 1764, den_step: 1 },
+	{ num: 16934400, den_min: 2117, den_max: 2117, den_step: 1 },
+	{ num: 16934400, den_min: 2558, den_max: 2558, den_step: 1 },
+	{ num: 16934400/16, den_min: 21, den_max: 192, den_step: 1 }
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+	nrats: CLOCKS,
+	rats: clocks,
+};
+
+static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime)
+{
+	return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					     &hw_constraints_clocks);
+}
+
+static unsigned char divisor_to_rate_register(unsigned int divisor)
+{
+	switch (divisor) {
+	case 353:	return 1;
+	case 529:	return 2;
+	case 617:	return 3;
+	case 1058:	return 4;
+	case 1764:	return 5;
+	case 2117:	return 6;
+	case 2558:	return 7;
+	default:
+		snd_runtime_check(divisor >= 21 && divisor <= 192, return 192);
+		return divisor;
+	}
+}
+
+static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr)
+{
+	unsigned long flags;
+	unsigned char rate = divisor_to_rate_register(params->rate_den);
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* set fast playback format change and clean playback FIFO */
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
+	snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr)
+{
+	unsigned long flags;
+	unsigned char rate = divisor_to_rate_register(params->rate_den);
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* set fast capture format change and clean capture FIFO */
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+	snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
+	snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+#ifdef CONFIG_PM
+
+static void snd_cs4236_suspend(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++)
+		chip->image[reg] = snd_cs4231_in(chip, reg);
+	for (reg = 0; reg < 18; reg++)
+		chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
+	for (reg = 2; reg < 9; reg++)
+		chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4236_resume(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++) {
+		switch (reg) {
+		case CS4236_EXT_REG:
+		case CS4231_VERSION:
+		case 27:	/* why? CS4235 - master left */
+		case 29:	/* why? CS4235 - master right */
+			break;
+		default:
+			snd_cs4231_out(chip, reg, chip->image[reg]);
+			break;
+		}
+	}
+	for (reg = 0; reg < 18; reg++)
+		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
+	for (reg = 2; reg < 9; reg++) {
+		switch (reg) {
+		case 7:
+			break;
+		default:
+			snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+}
+
+#endif /* CONFIG_PM */
+
+int snd_cs4236_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip)
+{
+	cs4231_t *chip;
+	unsigned char ver1, ver2;
+	int err, reg;
+
+	*rchip = NULL;
+	if (hardware == CS4231_HW_DETECT)
+		hardware = CS4231_HW_DETECT3;
+	if (cport < 0x100) {
+		snd_printk("please, specify control port for CS4236+ chips\n");
+		return -ENODEV;
+	}
+	if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0)
+		return err;
+	if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) {
+		snd_device_free(card, chip);
+		return -ENODEV;
+	}
+#if 0
+	{
+		int idx;
+		for (idx = 0; idx < 8; idx++)
+			snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx));
+		for (idx = 0; idx < 9; idx++)
+			snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx));
+	}
+#endif
+	ver1 = snd_cs4236_ctrl_in(chip, 1);
+	ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
+	snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
+	if (ver1 != ver2) {
+		snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport);
+		snd_device_free(card, chip);
+		return -ENODEV;
+	}
+	snd_cs4236_ctrl_out(chip, 0, 0x00);
+	snd_cs4236_ctrl_out(chip, 2, 0xff);
+	snd_cs4236_ctrl_out(chip, 3, 0x00);
+	snd_cs4236_ctrl_out(chip, 4, 0x80);
+	snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE);
+	snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2);
+	snd_cs4236_ctrl_out(chip, 7, 0x00);
+	/* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */
+	/* is working with this setup, other hardware should have */
+	/* different signal paths and this value should be selectable */
+	/* in the future */
+	snd_cs4236_ctrl_out(chip, 8, 0x8c);
+	chip->rate_constraint = snd_cs4236_xrate;
+	chip->set_playback_format = snd_cs4236_playback_format;
+	chip->set_capture_format = snd_cs4236_capture_format;
+#ifdef CONFIG_PM
+	chip->suspend = snd_cs4236_suspend;
+	chip->resume = snd_cs4236_resume;
+#endif
+
+	/* initialize extended registers */
+	for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
+		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
+
+        /* initialize compatible but more featured registers */
+	snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40);
+	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40);
+	snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
+	snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
+	snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
+	snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
+	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+	snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff);
+	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+	switch (chip->hardware) {
+	case CS4231_HW_CS4235:
+	case CS4231_HW_CS4239:
+		snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff);
+		snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff);
+		break;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+int snd_cs4236_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+	
+	if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0)
+		return err;
+	pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER
+ */
+
+#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_single, \
+  get: snd_cs4236_get_single, put: snd_cs4236_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_cs4236_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_cs4236_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val;
+	change = val != chip->eimage[CS4236_REG(reg)];
+	snd_cs4236_ext_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_single, \
+  get: snd_cs4236_get_singlec, put: snd_cs4236_put_singlec, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_cs4236_put_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->cimage[reg] & ~(mask << shift)) | val;
+	change = val != chip->cimage[reg];
+	snd_cs4236_ctrl_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4236_get_double, put: snd_cs4236_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_cs4236_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_cs4236_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1;
+		val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
+		change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)];
+		snd_cs4236_ext_out(chip, left_reg, val1);
+		snd_cs4236_ext_out(chip, right_reg, val2);
+	} else {
+		val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != chip->eimage[CS4236_REG(left_reg)];
+		snd_cs4236_ext_out(chip, left_reg, val1);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4236_get_double1, put: snd_cs4236_put_double1, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_cs4236_put_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
+	snd_cs4231_out(chip, left_reg, val1);
+	snd_cs4236_ext_out(chip, right_reg, val2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4236_MASTER_DIGITAL(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4236_get_master_digital, put: snd_cs4236_put_master_digital, \
+  private_value: 71 << 24 }
+
+static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
+{
+	return (vol < 64) ? 63 - vol : 64 + (71 - vol);
+}        
+
+static int snd_cs4236_get_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f);
+	ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4236_put_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f);
+	val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1;
+	val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2;
+	change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)];
+	snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1);
+	snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4235_OUTPUT_ACCU(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4235_get_output_accu, put: snd_cs4235_put_output_accu, \
+  private_value: 3 << 24 }
+
+static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
+{
+	switch ((vol >> 5) & 3) {
+	case 0: return 1;
+	case 1: return 3;
+	case 2: return 2;
+	case 3: return 0;
+ 	}
+	return 3;
+}
+
+static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)
+{
+	switch (vol & 3) {
+	case 0: return 3 << 5;
+	case 1: return 0 << 5;
+	case 2: return 2 << 5;
+	case 3: return 1 << 5;
+	}
+	return 1 << 5;
+}
+
+static int snd_cs4235_get_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]);
+	ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4235_put_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]);
+	val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
+	val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
+	change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
+	snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1);
+	snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4236_CONTROLS (sizeof(snd_cs4236_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_controls[] = {
+
+CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+
+CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+
+CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+
+CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
+CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+
+CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
+CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+
+CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+
+CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
+
+CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
+CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
+
+CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
+
+CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+
+CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+
+CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+
+CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+};
+
+#define CS4235_CONTROLS (sizeof(snd_cs4235_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4235_controls[] = {
+
+CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
+
+CS4235_OUTPUT_ACCU("Playback Volume", 0),
+
+CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+
+CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+
+CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+
+CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+
+CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
+
+CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
+
+CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+
+CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
+CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
+
+CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+
+CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+
+CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+
+CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+
+CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+};
+
+#define CS4236_IEC958_ENABLE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_single, \
+  get: snd_cs4236_get_iec958_switch, put: snd_cs4236_put_iec958_switch, \
+  private_value: 1 << 16 }
+
+static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
+#if 0
+	printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+			snd_cs4236_ctrl_in(chip, 3),
+			snd_cs4236_ctrl_in(chip, 4),
+			snd_cs4236_ctrl_in(chip, 5),
+			snd_cs4236_ctrl_in(chip, 6),
+			snd_cs4236_ctrl_in(chip, 8));
+#endif
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4236_put_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short enable, val;
+	
+	enable = ucontrol->value.integer.value[0] & 1;
+
+	down(&chip->mce_mutex);
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
+	change = val != chip->image[CS4231_ALT_FEATURE_1];
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val);
+	val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
+	snd_cs4236_ctrl_out(chip, 4, val);
+	udelay(100);
+	val &= ~0x40;
+	snd_cs4236_ctrl_out(chip, 4, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+	up(&chip->mce_mutex);
+
+#if 0
+	printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+			snd_cs4236_ctrl_in(chip, 3),
+			snd_cs4236_ctrl_in(chip, 4),
+			snd_cs4236_ctrl_in(chip, 5),
+			snd_cs4236_ctrl_in(chip, 6),
+			snd_cs4236_ctrl_in(chip, 8));
+#endif
+	return change;
+}
+
+#define CS4236_IEC958_CONTROLS (sizeof(snd_cs4236_iec958_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_iec958_controls[] = {
+CS4236_IEC958_ENABLE("IEC958 Output Enable", 0),
+CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0),
+CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0),
+CS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0),
+CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0),
+CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0)
+};
+
+#define CS4236_3D_CONTROLS_CS4235 (sizeof(snd_cs4236_3d_controls_cs4235)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4235[] = {
+CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
+CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1)
+};
+
+#define CS4236_3D_CONTROLS_CS4237 (sizeof(snd_cs4236_3d_controls_cs4237)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4237[] = {
+CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0),
+CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
+CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1),
+CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0),
+CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
+};
+
+#define CS4236_3D_CONTROLS_CS4238 (sizeof(snd_cs4236_3d_controls_cs4238)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4238[] = {
+CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
+CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
+CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),
+CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
+};
+
+int snd_cs4236_mixer(cs4231_t *chip)
+{
+	snd_card_t *card;
+	int err, idx, count;
+	snd_kcontrol_new_t *kcontrol;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	card = chip->card;
+	strcpy(card->mixername, snd_cs4231_chip_id(chip));
+
+	if (chip->hardware == CS4231_HW_CS4235 ||
+	    chip->hardware == CS4231_HW_CS4239) {
+		for (idx = 0; idx < CS4235_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)
+				return err;
+		}
+	} else {
+		for (idx = 0; idx < CS4236_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	switch (chip->hardware) {
+	case CS4231_HW_CS4235:
+	case CS4231_HW_CS4239:
+		count = CS4236_3D_CONTROLS_CS4235;
+		kcontrol = snd_cs4236_3d_controls_cs4235;
+		break;
+	case CS4231_HW_CS4237B:
+		count = CS4236_3D_CONTROLS_CS4237;
+		kcontrol = snd_cs4236_3d_controls_cs4237;
+		break;
+	case CS4231_HW_CS4238B:
+		count = CS4236_3D_CONTROLS_CS4238;
+		kcontrol = snd_cs4236_3d_controls_cs4238;
+		break;
+	default:
+		count = -1;
+		kcontrol = NULL;
+	}
+	for (idx = 0; idx < count; idx++, kcontrol++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
+			return err;
+	}
+	if (chip->hardware == CS4231_HW_CS4237B ||
+	    chip->hardware == CS4231_HW_CS4238B) {
+		for (idx = 0; idx < CS4236_IEC958_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_cs4236_create);
+EXPORT_SYMBOL(snd_cs4236_pcm);
+EXPORT_SYMBOL(snd_cs4236_mixer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_cs4236_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_cs4236_exit(void)
+{
+}
+
+module_init(alsa_cs4236_init)
+module_exit(alsa_cs4236_exit)
diff -Nru linux/sound/isa/dt0197h.c linux-2.4.19-pre5-mjc/sound/isa/dt0197h.c
--- linux/sound/isa/dt0197h.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/dt0197h.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,393 @@
+
+/*
+    card-dt0197h.c - driver for Diamond Technologies DT-0197H based soundcards.
+    Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
+
+    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
+*/
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+
+#define chip_t sb_t
+
+#define PFX "dt0197h: "
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("Diamond Technologies DT-0197H");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Diamond Technologies,DT-0197H}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for dt0197h based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for dt0197h based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable dt0197h based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+struct snd_card_dt0197h {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+	struct isapnp_dev *devopl;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_dt0197h_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+static struct isapnp_card *snd_dt0197h_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_dt0197h_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+static struct isapnp_card_id snd_dt0197h_pnpids[] __devinitdata = {
+	/* DT197A30 */
+	{
+		ISAPNP_CARD_ID('R','W','B',0x1688),
+		devs: { ISAPNP_DEVICE_ID('@','@','@',0x0001),
+			ISAPNP_DEVICE_ID('@','X','@',0x0001),
+			ISAPNP_DEVICE_ID('@','H','@',0x0001) }
+	},
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_dt0197h_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define DRIVER_NAME	"snd-card-dt0197h"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_dt0197h_isapnp(int dev, struct snd_card_dt0197h *acard)
+{
+	const struct isapnp_card_id *id = snd_dt0197h_isapnp_id[dev];
+	struct isapnp_card *card = snd_dt0197h_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+	acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+	if (acard->devopl->active) {
+		acard->dev = acard->devmpu = acard->devopl = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma8[dev] = pdev->dma_resource[0].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev || pdev->prepare(pdev)<0)
+		return 0;
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	pdev = acard->devopl;
+	if (pdev == NULL || pdev->prepare(pdev)<0)
+		return 0;
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4);
+
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "OPL isapnp configure failure\n");
+		snd_fm_port[dev] = -1;
+		acard->devopl = NULL;
+	} else {
+		snd_fm_port[dev] = pdev->resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_dt0197h_deactivate(struct snd_card_dt0197h *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+	if (acard->devmpu) {
+		acard->devmpu->deactivate(acard->devmpu);
+		acard->devmpu = NULL;
+	}
+	if (acard->devopl) {
+		acard->devopl->deactivate(acard->devopl);
+		acard->devopl = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void snd_card_dt0197h_free(snd_card_t *card)
+{
+	struct snd_card_dt0197h *acard = (struct snd_card_dt0197h *)card->private_data;
+
+	if (acard != NULL) {
+#ifdef __ISAPNP__
+		snd_card_dt0197h_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_dt0197h_probe(int dev)
+{
+	int error;
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_card_dt0197h *acard;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_dt0197h))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_dt0197h *)card->private_data;
+	card->private_free = snd_card_dt0197h_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_dt0197h_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	printk(KERN_ERR PFX "you have to enable PnP support ...\n");
+	snd_card_free(card);
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_sbdsp_create(card, snd_port[dev],
+				      snd_irq[dev],
+				      snd_sb16dsp_interrupt,
+				      snd_dma8[dev],
+				      -1,
+				      SB_HW_AUTO,
+				      &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0,
+					MPU401_HW_MPU401,
+					snd_mpu_port[dev], 0,
+					snd_mpu_irq[dev],
+					SA_INTERRUPT,
+					NULL) < 0)
+			printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n",
+				snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev],
+				    snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx ?\n",
+				snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "DT-0197H");
+	strcpy(card->shortname, "Diamond Tech. DT-0197H");
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+		card->shortname, chip->name, chip->port,
+		snd_irq[dev], snd_dma8[dev]);
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_dt0197h_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_dt0197h_isapnp_detect(struct isapnp_card *card,
+					    const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_dt0197h_isapnp_cards[dev] = card;
+		snd_dt0197h_isapnp_id[dev] = id;
+		res = snd_card_dt0197h_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_dt0197h_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_dt0197h_pnpids, snd_dt0197h_isapnp_detect);
+#else
+	printk(KERN_ERR PFX "you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		printk(KERN_ERR "no DT-0197H based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_dt0197h_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_dt0197h_cards[dev]);
+}
+
+module_init(alsa_card_dt0197h_init)
+module_exit(alsa_card_dt0197h_exit)
+
+#ifndef MODULE
+
+/* format is: snd-dt0197h=snd_enable,snd_index,snd_id,snd_isapnp,
+			  snd_port,snd_mpu_port,snd_fm_port,
+			  snd_irq,snd_mpu_irq,snd_dma8,snd_dma8_size */
+
+static int __init alsa_card_dt0197h_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-dt0197h=", alsa_card_dt0197h_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/es1688/Makefile linux-2.4.19-pre5-mjc/sound/isa/es1688/Makefile
--- linux/sound/isa/es1688/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/es1688/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,25 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _es1688.o
+
+list-multi   := snd-es1688-lib.o snd-es1688.o
+
+export-objs  := es1688_lib.o
+
+snd-es1688-lib-objs := es1688_lib.o
+snd-es1688-objs := es1688.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-es1688-lib.o
+
+include $(TOPDIR)/Rules.make
+
+snd-es1688-lib.o: $(snd-es1688-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-lib-objs)
+
+snd-es1688.o: $(snd-es1688-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-objs)
diff -Nru linux/sound/isa/es1688/es1688.c linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688.c
--- linux/sound/isa/es1688/es1688.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,242 @@
+/*
+ *  Driver for generic ESS AudioDrive ESx688 soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/es1688.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t es1688_t
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ESS ESx688 AudioDrive");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100},"
+	        "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102},"
+	        "{ESS,ES688 AudioDrive,pnp:ESS6881},"
+	        "{ESS,ES1688 AudioDrive,pnp:ESS1681}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ESx688 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ESx688 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ESx688 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_audiodrive_probe(int dev)
+{
+	static int possible_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_dmas[] = {1, 3, 0, -1};
+	int irq, dma, mpu_irq;
+	snd_card_t *card;
+	es1688_t *chip;
+	opl3_t *opl3;
+	snd_pcm_t *pcm;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	mpu_irq = snd_mpu_irq[dev];
+	dma = snd_dma8[dev];
+	if (dma == SNDRV_AUTO_DMA) {
+		if ((dma = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev],
+				     irq, mpu_irq, dma,
+				     ES1688_HW_AUTO, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1688_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) {
+		printk(KERN_ERR "es1688: opl3 not detected at 0x%lx\n", chip->port);
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (mpu_irq >= 0) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
+					       chip->mpu_port, 0,
+					       mpu_irq,
+					       SA_INTERRUPT,
+					       NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	strcpy(card->driver, "ES1688");
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, irq, dma);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_audiodrive_cards[dev] = card;
+	return 0;
+
+}
+
+static int __init snd_audiodrive_legacy_auto_probe(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+	
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+		snd_port[dev] = port;
+		res = snd_audiodrive_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+static int __init alsa_card_es1688_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+	int dev, cards = 0;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_audiodrive_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "ESS AudioDrive ES1688 soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_es1688_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_audiodrive_cards[idx]);
+}
+
+module_init(alsa_card_es1688_init)
+module_exit(alsa_card_es1688_exit)
+
+#ifndef MODULE
+
+/* format is: snd-es1688=snd_enable,snd_index,snd_id,
+			 snd_port,snd_mpu_port,
+			 snd_irq,snd_mpu_irq,
+			 snd_dma8 */
+
+static int __init alsa_card_es1688_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-es1688=", alsa_card_es1688_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/es1688/es1688_lib.c linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688_lib.c
--- linux/sound/isa/es1688/es1688_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688_lib.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1060 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of ESS ES1688/688/488 chip
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/es1688.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ESS ESx688 lowlevel module");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+
+#define chip_t es1688_t
+
+static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val)
+{
+	int i;
+
+	for (i = 10000; i; i--)
+		if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) {
+			outb(val, ES1688P(chip, COMMAND));
+			return 1;
+		}
+#ifdef CONFIG_SND_DEBUG
+	printk("snd_es1688_dsp_command: timeout (0x%x)\n", val);
+#endif
+	return 0;
+}
+
+static int snd_es1688_dsp_get_byte(es1688_t *chip)
+{
+	int i;
+
+	for (i = 1000; i; i--)
+		if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80)
+			return inb(ES1688P(chip, READ));
+	snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
+	return -ENODEV;
+}
+
+static int snd_es1688_write(es1688_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	if (!snd_es1688_dsp_command(chip, reg))
+		return 0;
+	return snd_es1688_dsp_command(chip, data);
+}
+
+int snd_es1688_read(es1688_t *chip, unsigned char reg)
+{
+	/* Read a byte from an extended mode register of ES1688 */
+	if (!snd_es1688_dsp_command(chip, 0xc0))
+		return -1;
+	if (!snd_es1688_dsp_command(chip, reg))
+		return -1;
+	return snd_es1688_dsp_get_byte(chip);
+}
+
+void snd_es1688_mixer_write(es1688_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	outb(reg, ES1688P(chip, MIXER_ADDR));
+	udelay(10);
+	outb(data, ES1688P(chip, MIXER_DATA));
+	udelay(10);
+}
+
+unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg)
+{
+	unsigned char result;
+
+	outb(reg, ES1688P(chip, MIXER_ADDR));
+	udelay(10);
+	result = inb(ES1688P(chip, MIXER_DATA));
+	udelay(10);
+	return result;
+}
+
+static int snd_es1688_reset(es1688_t *chip)
+{
+	int i;
+
+	outb(3, ES1688P(chip, RESET));		/* valid only for ESS chips, SB -> 1 */
+	udelay(10);
+	outb(0, ES1688P(chip, RESET));
+	udelay(30);
+	for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++);
+	if (inb(ES1688P(chip, READ)) != 0xaa) {
+		snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port);
+		return -ENODEV;
+	}
+	snd_es1688_dsp_command(chip, 0xc6);	/* enable extended mode */
+	return 0;
+}
+
+static int snd_es1688_probe(es1688_t *chip)
+{
+	unsigned long flags;
+	unsigned short major, minor, hw;
+	int i;
+
+	/*
+	 *  initialization sequence
+	 */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);	/* Some ESS1688 cards need this */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE2));	/* ENABLE2 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE2));	/* ENABLE2 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE2));	/* ENABLE2 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE0));	/* ENABLE0 */
+
+	if (snd_es1688_reset(chip) < 0) {
+		snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ)));
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return -ENODEV;
+	}
+	snd_es1688_dsp_command(chip, 0xe7);	/* return identification */
+
+	for (i = 1000, major = minor = 0; i; i--) {
+		if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
+			if (major == 0) {
+				major = inb(ES1688P(chip, READ));
+			} else {
+				minor = inb(ES1688P(chip, READ));
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor);
+
+	chip->version = (major << 8) | minor;
+	if (!chip->version)
+		return -ENODEV;	/* probably SB */
+
+	hw = ES1688_HW_AUTO;
+	switch (chip->version & 0xfff0) {
+	case 0x4880:
+		snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port);
+		return -ENODEV;
+	case 0x6880:
+		hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688;
+		break;
+	default:
+		snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_write(chip, 0xb1, 0x10);	/* disable IRQ */
+	snd_es1688_write(chip, 0xb2, 0x00);	/* disable DMA */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* enable joystick, but disable OPL3 */
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	snd_es1688_mixer_write(chip, 0x40, 0x01);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+	return 0;
+}
+
+static int snd_es1688_init(es1688_t * chip, int enable)
+{
+	static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
+	unsigned long flags;
+	int cfg, irq_bits, dma, dma_bits, tmp, tmp1;
+
+	/* ok.. setup MPU-401 port and joystick and OPL3 */
+	cfg = 0x01;		/* enable joystick, but disable OPL3 */
+	if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) {
+		tmp = (chip->mpu_port & 0x0f0) >> 4;
+		if (tmp <= 3) {
+			switch (chip->mpu_irq) {
+			case 9:
+				tmp1 = 4;
+				break;
+			case 5:
+				tmp1 = 5;
+				break;
+			case 7:
+				tmp1 = 6;
+				break;
+			case 10:
+				tmp1 = 7;
+				break;
+			default:
+				tmp1 = 0;
+			}
+			if (tmp1) {
+				cfg |= (tmp << 3) | (tmp1 << 5);
+			}
+		}
+	}
+#if 0
+	snd_printk("mpu cfg = 0x%x\n", cfg);
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_mixer_write(chip, 0x40, cfg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	/* --- */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_read(chip, 0xb1);
+	snd_es1688_read(chip, 0xb2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (enable) {
+		cfg = 0xf0;	/* enable only DMA counter interrupt */
+		irq_bits = irqs[chip->irq & 0x0f];
+		if (irq_bits < 0) {
+			snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq);
+#if 0
+			irq_bits = 0;
+			cfg = 0x10;
+#endif
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		cfg = 0xf0;	/* extended mode DMA enable */
+		dma = chip->dma8;
+		if (dma > 3 || dma == 2) {
+			snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma);
+#if 0
+			dma_bits = 0;
+			cfg = 0x00;	/* disable all DMA */
+#endif
+			return -EINVAL;
+		} else {
+			dma_bits = dma;
+			if (dma != 3)
+				dma_bits++;
+		}
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	} else {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_es1688_write(chip, 0xb1, 0x10);	/* disable IRQ */
+		snd_es1688_write(chip, 0xb2, 0x00);	/* disable DMA */
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_read(chip, 0xb1);
+	snd_es1688_read(chip, 0xb2);
+	snd_es1688_reset(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+static ratnum_t clocks[2] = {
+	{
+		num: 795444,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 397722,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
+	nrats: 2,
+	rats: clocks,
+};
+
+static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int bits, divider;
+
+	if (runtime->rate_num == clocks[0].num)
+		bits = 256 - runtime->rate_den;
+	else
+		bits = 128 - runtime->rate_den;
+	/* set filter register */
+	divider = 256 - 7160000*20/(8*82*runtime->rate);
+	/* write result to hardware */
+	snd_es1688_write(chip, 0xa1, bits);
+	snd_es1688_write(chip, 0xa2, divider);
+}
+
+static int snd_es1688_ioctl(snd_pcm_substream_t * substream,
+			    unsigned int cmd, void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value)
+{
+	int val;
+
+	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		value = 0x00;
+	} else if (cmd != SNDRV_PCM_TRIGGER_START) {
+		return -EINVAL;
+	}
+	spin_lock(&chip->reg_lock);
+	chip->trigger_value = value;
+	val = snd_es1688_read(chip, 0xb8);
+	if ((val < 0) || (val & 0x0f) == value) {
+		spin_unlock(&chip->reg_lock);
+		return -EINVAL;	/* something is wrong */
+	}
+#if 0
+	printk("trigger: val = 0x%x, value = 0x%x\n", val, value);
+	printk("trigger: residue = 0x%x\n", get_dma_residue(chip->dma8));
+#endif
+	snd_es1688_write(chip, 0xb8, (val & 0xf0) | value);
+	spin_unlock(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_es1688_hw_params(snd_pcm_substream_t * substream,
+				snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_es1688_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_reset(chip);
+	snd_es1688_set_rate(chip, substream);
+	snd_es1688_write(chip, 0xb8, 4);	/* auto init DMA mode */
+	snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+	snd_es1688_write(chip, 0xb9, 2);	/* demand mode (4 bytes/request) */
+	if (runtime->channels == 1) {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit mono */
+			snd_es1688_write(chip, 0xb6, 0x80);
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0xd0);
+		} else {
+			/* 16. bit mono */
+			snd_es1688_write(chip, 0xb6, 0x00);
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xf4);
+		}
+	} else {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit stereo */
+			snd_es1688_write(chip, 0xb6, 0x80);
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0x98);
+		} else {
+			/* 16. bit stereo */
+			snd_es1688_write(chip, 0xb6, 0x00);
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xbc);
+		}
+	}
+	snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+	snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
+	snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	/* --- */
+	count = -count;
+	snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_write(chip, 0xa4, (unsigned char) count);
+	snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	return snd_es1688_trigger(chip, cmd, 0x05);
+}
+
+static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_reset(chip);
+	snd_es1688_set_rate(chip, substream);
+	snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
+	snd_es1688_write(chip, 0xb8, 0x0e);	/* auto init DMA mode */
+	snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+	snd_es1688_write(chip, 0xb9, 2);	/* demand mode (4 bytes/request) */
+	if (runtime->channels == 1) {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit mono */
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0xd0);
+		} else {
+			/* 16. bit mono */
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xf4);
+		}
+	} else {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit stereo */
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0x98);
+		} else {
+			/* 16. bit stereo */
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xbc);
+		}
+	}
+	snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+	snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	/* --- */
+	count = -count;
+	snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_write(chip, 0xa4, (unsigned char) count);
+	snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	return snd_es1688_trigger(chip, cmd, 0x0f);
+}
+
+void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es1688_t *chip = snd_magic_cast(es1688_t, dev_id, return);
+
+	if (chip->trigger_value == 0x05)	/* ok.. playback is active */
+		snd_pcm_period_elapsed(chip->playback_substream);
+	if (chip->trigger_value == 0x0f)	/* ok.. capture is active */
+		snd_pcm_period_elapsed(chip->capture_substream);
+
+	inb(ES1688P(chip, DATA_AVAIL));	/* ack interrupt */
+}
+
+static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (chip->trigger_value != 0x05)
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma8);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (chip->trigger_value != 0x0f)
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma8);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_es1688_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_es1688_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+
+ */
+
+static int snd_es1688_playback_open(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (chip->capture_substream != NULL)
+		return -EAGAIN;
+	chip->playback_substream = substream;
+	runtime->hw = snd_es1688_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	return 0;
+}
+
+static int snd_es1688_capture_open(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (chip->playback_substream != NULL)
+		return -EAGAIN;
+	chip->capture_substream = substream;
+	runtime->hw = snd_es1688_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	return 0;
+}
+
+static int snd_es1688_playback_close(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	return 0;
+}
+
+static int snd_es1688_capture_close(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	return 0;
+}
+
+static int snd_es1688_free(es1688_t *chip)
+{
+	if (chip->res_port) {
+		snd_es1688_init(chip, 0);
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma8 >= 0) {
+		disable_dma(chip->dma8);
+		free_dma(chip->dma8);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es1688_dev_free(snd_device_t *device)
+{
+	es1688_t *chip = snd_magic_cast(es1688_t, device->device_data, return -ENXIO);
+	return snd_es1688_free(chip);
+}
+
+static const char *snd_es1688_chip_id(es1688_t *chip)
+{
+	static char tmp[16];
+	sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f);
+	return tmp;
+}
+
+int snd_es1688_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long mpu_port,
+		      int irq,
+		      int mpu_irq,
+		      int dma8,
+		      unsigned short hardware,
+		      es1688_t **rchip)
+{
+	static snd_device_ops_t ops = {
+		dev_free:	snd_es1688_dev_free,
+	};
+                                
+	es1688_t *chip;
+	int err;
+
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(es1688_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->irq = -1;
+	chip->dma8 = -1;
+	
+	if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
+		snd_es1688_free(chip);
+		return -EBUSY;
+	}
+	if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) {
+		snd_es1688_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (request_dma(dma8, "ES1688")) {
+		snd_es1688_free(chip);
+		return -EBUSY;
+	}
+	chip->dma8 = dma8;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->mixer_lock);
+	chip->card = card;
+	chip->port = port;
+	mpu_port &= ~0x000f;
+	if (mpu_port < 0x300 || mpu_port > 0x330)
+		mpu_port = 0;
+	chip->mpu_port = mpu_port;
+	chip->mpu_irq = mpu_irq;
+	chip->hardware = hardware;
+
+	if ((err = snd_es1688_probe(chip)) < 0) {
+		snd_es1688_free(chip);
+		return err;
+	}
+	if ((err = snd_es1688_init(chip, 1)) < 0) {
+		snd_es1688_free(chip);
+		return err;
+	}
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es1688_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_es1688_playback_ops = {
+	open:			snd_es1688_playback_open,
+	close:			snd_es1688_playback_close,
+	ioctl:			snd_es1688_ioctl,
+	hw_params:		snd_es1688_hw_params,
+	hw_free:		snd_es1688_hw_free,
+	prepare:		snd_es1688_playback_prepare,
+	trigger:		snd_es1688_playback_trigger,
+	pointer:		snd_es1688_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es1688_capture_ops = {
+	open:			snd_es1688_capture_open,
+	close:			snd_es1688_capture_close,
+	ioctl:			snd_es1688_ioctl,
+	hw_params:		snd_es1688_hw_params,
+	hw_free:		snd_es1688_hw_free,
+	prepare:		snd_es1688_capture_prepare,
+	trigger:		snd_es1688_capture_trigger,
+	pointer:		snd_es1688_capture_pointer,
+};
+
+static void snd_es1688_pcm_free(snd_pcm_t *pcm)
+{
+	es1688_t *chip = snd_magic_cast(es1688_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_es1688_pcm_free;
+	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	sprintf(pcm->name, snd_es1688_chip_id(chip));
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[9] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7;
+	return 0;
+}
+
+static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char oval, nval;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 8)
+		return -EINVAL;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV);
+	nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15);
+	change = nval != oval;
+	if (change)
+		snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1688_info_single, \
+  get: snd_es1688_get_single, put: snd_es1688_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned char oval, nval;
+	
+	nval = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		nval = mask - nval;
+	nval <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_es1688_mixer_read(chip, reg);
+	nval = (oval & ~(mask << shift)) | nval;
+	change = nval != oval;
+	if (change)
+		snd_es1688_mixer_write(chip, reg, nval);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1688_info_double, \
+  get: snd_es1688_get_double, put: snd_es1688_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg < 0xa0)
+		left = snd_es1688_mixer_read(chip, left_reg);
+	else
+		left = snd_es1688_read(chip, left_reg);
+	if (left_reg != right_reg) {
+		if (right_reg < 0xa0) 
+			right = snd_es1688_mixer_read(chip, right_reg);
+		else
+			right = snd_es1688_read(chip, right_reg);
+	} else
+		right = left;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, oval1, oval2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		if (left_reg < 0xa0)
+			oval1 = snd_es1688_mixer_read(chip, left_reg);
+		else
+			oval1 = snd_es1688_read(chip, left_reg);
+		if (right_reg < 0xa0)
+			oval2 = snd_es1688_mixer_read(chip, right_reg);
+		else
+			oval2 = snd_es1688_read(chip, right_reg);
+		val1 = (oval1 & ~(mask << shift_left)) | val1;
+		val2 = (oval2 & ~(mask << shift_right)) | val2;
+		change = val1 != oval1 || val2 != oval2;
+		if (change) {
+			if (left_reg < 0xa0)
+				snd_es1688_mixer_write(chip, left_reg, val1);
+			else
+				snd_es1688_write(chip, left_reg, val1);
+			if (right_reg < 0xa0)
+				snd_es1688_mixer_write(chip, right_reg, val1);
+			else
+				snd_es1688_write(chip, right_reg, val1);
+		}
+	} else {
+		if (left_reg < 0xa0)
+			oval1 = snd_es1688_mixer_read(chip, left_reg);
+		else
+			oval1 = snd_es1688_read(chip, left_reg);
+		val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != oval1;
+		if (change) {
+			if (left_reg < 0xa0)
+				snd_es1688_mixer_write(chip, left_reg, val1);
+			else
+				snd_es1688_write(chip, left_reg, val1);
+		}
+			
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define ES1688_CONTROLS (sizeof(snd_es1688_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_es1688_controls[] = {
+ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
+ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
+ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
+ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_es1688_info_mux,
+	get: snd_es1688_get_mux,
+	put: snd_es1688_put_mux,
+},
+};
+
+#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2)
+
+static unsigned char snd_es1688_init_table[][2] = {
+	{ ES1688_MASTER_DEV, 0 },
+	{ ES1688_PCM_DEV, 0 },
+	{ ES1688_LINE_DEV, 0 },
+	{ ES1688_CD_DEV, 0 },
+	{ ES1688_FM_DEV, 0 },
+	{ ES1688_MIC_DEV, 0 },
+	{ ES1688_AUX_DEV, 0 },
+	{ ES1688_SPEAKER_DEV, 0 },
+	{ ES1688_RECLEV_DEV, 0 },
+	{ ES1688_REC_DEV, 0x17 }
+};
+                                        
+int snd_es1688_mixer(es1688_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+	unsigned char reg, val;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, snd_es1688_chip_id(chip));
+
+	for (idx = 0; idx < ES1688_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0)
+			return err;
+	}
+	for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) {
+		reg = snd_es1688_init_table[idx][0];
+		val = snd_es1688_init_table[idx][1];
+		if (reg < 0xa0)
+			snd_es1688_mixer_write(chip, reg, val);
+		else
+			snd_es1688_write(chip, reg, val);
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_es1688_mixer_write);
+EXPORT_SYMBOL(snd_es1688_mixer_read);
+EXPORT_SYMBOL(snd_es1688_interrupt);
+EXPORT_SYMBOL(snd_es1688_create);
+EXPORT_SYMBOL(snd_es1688_pcm);
+EXPORT_SYMBOL(snd_es1688_mixer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_es1688_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_es1688_exit(void)
+{
+}
+
+module_init(alsa_es1688_init)
+module_exit(alsa_es1688_exit)
diff -Nru linux/sound/isa/es18xx.c linux-2.4.19-pre5-mjc/sound/isa/es18xx.c
--- linux/sound/isa/es18xx.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/es18xx.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2315 @@
+/*
+ *  Driver for generic ESS AudioDrive ES18xx soundcards
+ *  Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>
+ *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+/* GENERAL NOTES:
+ *
+ * BUGS:
+ * - There are pops (we can't delay in trigger function, cause midlevel 
+ *   often need to trigger down and then up very quickly).
+ *   Any ideas?
+ * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it.
+ */
+
+/*
+ * ES1868  NOTES:
+ * - The chip has one half duplex pcm (with very limited full duplex support).
+ *
+ * - Duplex stereophonic sound is impossible.
+ * - Record and playback must share the same frequency rate.
+ *
+ * - The driver use dma2 for playback and dma1 for capture.
+ */
+
+/*
+ * ES1869 NOTES:
+ *
+ * - there are a first full duplex pcm and a second playback only pcm
+ *   (incompatible with first pcm capture)
+ * 
+ * - there is support for the capture volume and ESS Spatializer 3D effect.
+ *
+ * - contrarily to some pages in DS_1869.PDF the rates can be set
+ *   independently.
+ *
+ * BUGS:
+ *
+ * - There is a major trouble I noted:
+ *
+ *   using both channel for playback stereo 16 bit samples at 44100 Hz
+ *   the second pcm (Audio1) DMA slows down irregularly and sound is garbled.
+ *   
+ *   The same happens using Audio1 for captureing.
+ *
+ *   The Windows driver does not suffer of this (although it use Audio1
+ *   only for captureing). I'm unable to discover why.
+ *
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define PFX "es18xx: "
+
+struct _snd_es18xx {
+	unsigned long port;		/* port of ESS chip */
+	unsigned long mpu_port;		/* MPU-401 port of ESS chip */
+	unsigned long fm_port;		/* FM port */
+	unsigned long ctrl_port;	/* Control port of ESS chip */
+	struct resource *res_port;
+	struct resource *res_mpu_port;
+	struct resource *res_ctrl_port;
+	int irq;			/* IRQ number of ESS chip */
+	int dma1;			/* DMA1 */
+	int dma2;			/* DMA2 */
+	unsigned short version;		/* version of ESS chip */
+	int caps;			/* Chip capabilities */
+	unsigned short audio2_vol;	/* volume level of audio2 */
+
+	unsigned short active;		/* active channel mask */
+	unsigned int dma1_size;
+	unsigned int dma2_size;
+	unsigned int dma1_shift;
+	unsigned int dma2_shift;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_a_substream;
+	snd_pcm_substream_t *capture_a_substream;
+	snd_pcm_substream_t *playback_b_substream;
+
+	snd_rawmidi_t *rmidi;
+
+	snd_kcontrol_t *hw_volume;
+	snd_kcontrol_t *hw_switch;
+	snd_kcontrol_t *master_volume;
+	snd_kcontrol_t *master_switch;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+	spinlock_t ctrl_lock;
+#ifdef CONFIG_PM
+	struct pm_dev *pm_dev;
+	unsigned char pm_reg;
+#endif
+};
+
+#define AUDIO1_IRQ	0x01
+#define AUDIO2_IRQ	0x02
+#define HWV_IRQ		0x04
+#define MPU_IRQ		0x08
+
+#define ES18XX_PCM2	0x0001	/* Has two useable PCM */
+#define ES18XX_SPATIALIZER 0x0002	/* Has 3D Spatializer */
+#define ES18XX_RECMIX	0x0004	/* Has record mixer */
+#define ES18XX_DUPLEX_MONO 0x0008	/* Has mono duplex only */
+#define ES18XX_DUPLEX_SAME 0x0010	/* Playback and record must share the same rate */
+#define ES18XX_NEW_RATE	0x0020	/* More precise rate setting */
+#define ES18XX_AUXB	0x0040	/* AuxB mixer control */
+#define ES18XX_HWV	0x0080	/* Has hardware volume */
+#define ES18XX_MONO	0x0100	/* Mono_in mixer control */
+#define ES18XX_I2S	0x0200	/* I2S mixer control */
+#define ES18XX_MUTEREC	0x0400	/* Record source can be muted */
+#define ES18XX_CONTROL	0x0800	/* Has control ports */
+
+/* Power Management */
+#define ES18XX_PM	0x07
+#define ES18XX_PM_GPO0	0x01
+#define ES18XX_PM_GPO1	0x02
+#define ES18XX_PM_PDR	0x03
+#define ES18XX_PM_ANA	0x04
+#define ES18XX_PM_FM	0x06
+#define ES18XX_PM_SUS	0x08
+
+typedef struct _snd_es18xx es18xx_t;
+
+#define chip_t es18xx_t
+
+/* Lowlevel */
+
+#define DAC1 0x01
+#define ADC1 0x02
+#define DAC2 0x04
+#define MILLISECOND 10000
+
+static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val)
+{
+        int i;
+
+        for(i = MILLISECOND; i; i--)
+                if ((inb(chip->port + 0x0C) & 0x80) == 0) {
+                        outb(val, chip->port + 0x0C);
+                        return 0;
+                }
+        snd_printk("dsp_command: timeout (0x%x)\n", val);
+        return -EINVAL;
+}
+
+static int snd_es18xx_dsp_get_byte(es18xx_t *chip)
+{
+        int i;
+
+        for(i = MILLISECOND/10; i; i--)
+                if (inb(chip->port + 0x0C) & 0x40)
+                        return inb(chip->port + 0x0A);
+        snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A));
+        return -ENODEV;
+}
+
+#undef REG_DEBUG
+
+static int snd_es18xx_write(es18xx_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	unsigned long flags;
+	int ret;
+	
+        spin_lock_irqsave(&chip->reg_lock, flags);
+	ret = snd_es18xx_dsp_command(chip, reg);
+	if (ret < 0)
+		goto end;
+        ret = snd_es18xx_dsp_command(chip, data);
+ end:
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x set to %02x\n", reg, data);
+#endif
+	return ret;
+}
+
+static int snd_es18xx_read(es18xx_t *chip, unsigned char reg)
+{
+	unsigned long flags;
+	int ret, data;
+        spin_lock_irqsave(&chip->reg_lock, flags);
+	ret = snd_es18xx_dsp_command(chip, 0xC0);
+	if (ret < 0)
+		goto end;
+        ret = snd_es18xx_dsp_command(chip, reg);
+	if (ret < 0)
+		goto end;
+	data = snd_es18xx_dsp_get_byte(chip);
+	ret = data;
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret);
+#endif
+ end:
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return ret;
+}
+
+/* Return old value */
+static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg,
+			   unsigned char mask, unsigned char val)
+{
+        int ret;
+	unsigned char old, new, oval;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->reg_lock, flags);
+        ret = snd_es18xx_dsp_command(chip, 0xC0);
+	if (ret < 0)
+		goto end;
+        ret = snd_es18xx_dsp_command(chip, reg);
+	if (ret < 0)
+		goto end;
+	ret = snd_es18xx_dsp_get_byte(chip);
+	if (ret < 0) {
+		goto end;
+	}
+	old = ret;
+	oval = old & mask;
+	if (val != oval) {
+		ret = snd_es18xx_dsp_command(chip, reg);
+		if (ret < 0)
+			goto end;
+		new = (old & ~mask) | (val & mask);
+		ret = snd_es18xx_dsp_command(chip, new);
+		if (ret < 0)
+			goto end;
+#ifdef REG_DEBUG
+		snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret);
+#endif
+	}
+	ret = oval;
+ end:
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return ret;
+}
+
+inline void snd_es18xx_mixer_write(es18xx_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	unsigned long flags;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+        outb(data, chip->port + 0x05);
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x set to %02x\n", reg, data);
+#endif
+}
+
+inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg)
+{
+	unsigned long flags;
+	int data;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+	data = inb(chip->port + 0x05);
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x now is %02x\n", reg, data);
+#endif
+        return data;
+}
+
+/* Return old value */
+static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg,
+					unsigned char mask, unsigned char val)
+{
+	unsigned char old, new, oval;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+	old = inb(chip->port + 0x05);
+	oval = old & mask;
+	if (val != oval) {
+		new = (old & ~mask) | (val & mask);
+		outb(new, chip->port + 0x05);
+#ifdef REG_DEBUG
+		snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+	}
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	return oval;
+}
+
+static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg,
+					    unsigned char mask)
+{
+	int old, expected, new;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+	old = inb(chip->port + 0x05);
+	expected = old ^ mask;
+	outb(expected, chip->port + 0x05);
+	new = inb(chip->port + 0x05);
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new);
+#endif
+	return expected == new;
+}
+
+
+static int snd_es18xx_reset(es18xx_t *chip)
+{
+	int i;
+        outb(0x03, chip->port + 0x06);
+        inb(chip->port + 0x06);
+        outb(0x00, chip->port + 0x06);
+        for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++);
+        if (inb(chip->port + 0x0A) != 0xAA)
+                return -1;
+	return 0;
+}
+
+static int snd_es18xx_reset_fifo(es18xx_t *chip)
+{
+        outb(0x02, chip->port + 0x06);
+        inb(chip->port + 0x06);
+        outb(0x00, chip->port + 0x06);
+	return 0;
+}
+
+static ratnum_t new_clocks[2] = {
+	{
+		num: 793800,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 768000,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = {
+	nrats: 2,
+	rats: new_clocks,
+};
+
+static ratnum_t old_clocks[2] = {
+	{
+		num: 795444,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 397722,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks  = {
+	nrats: 2,
+	rats: old_clocks,
+};
+
+
+static void snd_es18xx_rate_set(es18xx_t *chip, 
+				snd_pcm_substream_t *substream,
+				int mode)
+{
+	unsigned int bits, div0;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (chip->caps & ES18XX_NEW_RATE) {
+		if (runtime->rate_num == new_clocks[0].num)
+			bits = 128 - runtime->rate_den;
+		else
+			bits = 256 - runtime->rate_den;
+	} else {
+		if (runtime->rate_num == old_clocks[0].num)
+			bits = 256 - runtime->rate_den;
+		else
+			bits = 128 - runtime->rate_den;
+	}
+
+	/* set filter register */
+	div0 = 256 - 7160000*20/(8*82*runtime->rate);
+		
+	if ((chip->caps & ES18XX_PCM2) && mode == DAC2) {
+		snd_es18xx_mixer_write(chip, 0x70, bits);
+		snd_es18xx_mixer_write(chip, 0x72, div0);
+	} else {
+		snd_es18xx_write(chip, 0xA1, bits);
+		snd_es18xx_write(chip, 0xA2, div0);
+	}
+}
+
+static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int shift, err;
+
+	shift = 0;
+	if (params_channels(hw_params) == 2)
+		shift++;
+	if (snd_pcm_format_width(params_format(hw_params)) == 16)
+		shift++;
+
+	switch (substream->number) {
+	case 0:
+		if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+		    (chip->capture_a_substream) &&
+		    params_channels(hw_params) != 1) {
+			_snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
+			return -EBUSY;
+		}
+		chip->dma2_shift = shift;
+		break;
+	case 1:
+		chip->dma1_shift = shift;
+		break;
+	}
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_es18xx_playback1_prepare(es18xx_t *chip,
+					snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma2_size = size;
+
+        snd_es18xx_rate_set(chip, substream, DAC2);
+
+        /* Transfer Count Reload */
+        count = 0x10000 - count;
+        snd_es18xx_mixer_write(chip, 0x74, count & 0xff);
+        snd_es18xx_mixer_write(chip, 0x76, count >> 8);
+
+	/* Set format */
+        snd_es18xx_mixer_bits(chip, 0x7A, 0x07,
+			      ((runtime->channels == 1) ? 0x00 : 0x02) |
+			      (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) |
+			      (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04));
+
+        /* Set DMA controller */
+        snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	return 0;
+}
+
+static int snd_es18xx_playback1_trigger(es18xx_t *chip,
+					snd_pcm_substream_t * substream,
+					int cmd)
+{
+        if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->active & DAC2)
+			return 0;
+		chip->active |= DAC2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->active & DAC2))
+			return 0;
+		chip->active &= ~DAC2;
+	} else {
+		return -EINVAL;
+	}
+
+	if (cmd == SNDRV_PCM_TRIGGER_START) {	
+                /* Start DMA */
+		if (chip->dma2 >= 4)
+			snd_es18xx_mixer_write(chip, 0x78, 0xb3);
+		else
+			snd_es18xx_mixer_write(chip, 0x78, 0x93);
+#ifdef AVOID_POPS
+		/* Avoid pops */
+                udelay(100000);
+		if (chip->caps & ES18XX_PCM2)
+			/* Restore Audio 2 volume */
+			snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol);
+		else
+			/* Enable PCM output */
+			snd_es18xx_dsp_command(chip, 0xD1);
+#endif
+        }
+        else {
+                /* Stop DMA */
+                snd_es18xx_mixer_write(chip, 0x78, 0x00);
+#ifdef AVOID_POPS
+                udelay(25000);
+		if (chip->caps & ES18XX_PCM2)
+			/* Set Audio 2 volume to 0 */
+			snd_es18xx_mixer_write(chip, 0x7C, 0);
+		else
+			/* Disable PCM output */
+			snd_es18xx_dsp_command(chip, 0xD3);
+#endif
+        }
+
+	return 0;
+}
+
+static int snd_es18xx_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int shift, err;
+
+	shift = 0;
+	if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+	    chip->playback_a_substream &&
+	    params_channels(hw_params) != 1) {
+		_snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
+		return -EBUSY;
+	}
+	if (params_channels(hw_params) == 2)
+		shift++;
+	if (snd_pcm_format_width(params_format(hw_params)) == 16)
+		shift++;
+	chip->dma1_shift = shift;
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_es18xx_capture_prepare(snd_pcm_substream_t *substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+
+	snd_es18xx_reset_fifo(chip);
+
+        /* Set stereo/mono */
+        snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01);
+
+        snd_es18xx_rate_set(chip, substream, ADC1);
+
+        /* Transfer Count Reload */
+	count = 0x10000 - count;
+	snd_es18xx_write(chip, 0xA4, count & 0xff);
+	snd_es18xx_write(chip, 0xA5, count >> 8);
+
+#ifdef AVOID_POPS
+	udelay(100000);
+#endif
+
+        /* Set format */
+        snd_es18xx_write(chip, 0xB7, 
+                         snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71);
+        snd_es18xx_write(chip, 0xB7, 0x90 |
+                         ((runtime->channels == 1) ? 0x40 : 0x08) |
+                         (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) |
+                         (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20));
+
+        /* Set DMA controler */
+        snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+
+	return 0;
+}
+
+static int snd_es18xx_capture_trigger(snd_pcm_substream_t *substream,
+				      int cmd)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+        if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->active & ADC1)
+			return 0;
+		chip->active |= ADC1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->active & ADC1))
+			return 0;
+		chip->active &= ~ADC1;
+	} else {
+		return -EINVAL;
+	}
+
+	if (cmd == SNDRV_PCM_TRIGGER_START)
+                /* Start DMA */
+                snd_es18xx_write(chip, 0xB8, 0x0f);
+        else
+                /* Stop DMA */
+                snd_es18xx_write(chip, 0xB8, 0x00);
+	return 0;
+}
+
+static int snd_es18xx_playback2_prepare(es18xx_t *chip,
+					snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+
+	snd_es18xx_reset_fifo(chip);
+
+        /* Set stereo/mono */
+        snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01);
+
+        snd_es18xx_rate_set(chip, substream, DAC1);
+
+        /* Transfer Count Reload */
+	count = 0x10000 - count;
+	snd_es18xx_write(chip, 0xA4, count & 0xff);
+	snd_es18xx_write(chip, 0xA5, count >> 8);
+
+        /* Set format */
+        snd_es18xx_write(chip, 0xB6,
+                         snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00);
+        snd_es18xx_write(chip, 0xB7, 
+                         snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71);
+        snd_es18xx_write(chip, 0xB7, 0x90 |
+                         (runtime->channels == 1 ? 0x40 : 0x08) |
+                         (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) |
+                         (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20));
+
+        /* Set DMA controler */
+        snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	return 0;
+}
+
+static int snd_es18xx_playback2_trigger(es18xx_t *chip,
+					snd_pcm_substream_t *substream,
+					int cmd)
+{
+        if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->active & DAC1)
+			return 0;
+		chip->active |= DAC1;
+        } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->active & DAC1))
+			return 0;
+		chip->active &= ~DAC1;
+	} else {
+		return -EINVAL;
+	}
+
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+                /* Start DMA */
+                snd_es18xx_write(chip, 0xB8, 0x05);
+#ifdef AVOID_POPS
+		/* Avoid pops */
+                udelay(100000);
+                /* Enable Audio 1 */
+                snd_es18xx_dsp_command(chip, 0xD1);
+#endif
+        }
+        else {
+                /* Stop DMA */
+                snd_es18xx_write(chip, 0xB8, 0x00);
+#ifdef AVOID_POPS
+		/* Avoid pops */
+                udelay(25000);
+                /* Disable Audio 1 */
+                snd_es18xx_dsp_command(chip, 0xD3);
+#endif
+        }
+	return 0;
+}
+
+static int snd_es18xx_playback_prepare(snd_pcm_substream_t *substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	switch (substream->number) {
+	case 0:
+		return snd_es18xx_playback1_prepare(chip, substream);
+	case 1:
+		return snd_es18xx_playback2_prepare(chip, substream);
+	}
+	return -EINVAL;
+}
+
+static int snd_es18xx_playback_trigger(snd_pcm_substream_t *substream,
+				       int cmd)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	switch (substream->number) {
+	case 0:
+		return snd_es18xx_playback1_trigger(chip, substream, cmd);
+	case 1:
+		return snd_es18xx_playback2_trigger(chip, substream, cmd);
+	}
+	return -EINVAL;
+}
+
+static void snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, dev_id, return);
+	unsigned char status;
+
+
+	if (chip->caps & ES18XX_CONTROL) {
+		/* Read Interrupt status */
+		status = inb(chip->ctrl_port + 6);
+	} else {
+		/* Read Interrupt status */
+		status = snd_es18xx_mixer_read(chip, 0x7f) >> 4;
+	}
+#if 0
+	else {
+		status = 0;
+		if (inb(chip->port + 0x0C) & 0x01)
+			status |= AUDIO1_IRQ;
+		if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80)
+			status |= AUDIO2_IRQ;
+		if ((chip->caps & ES18XX_HWV) &&
+		    snd_es18xx_mixer_read(chip, 0x64) & 0x10)
+			status |= HWV_IRQ;
+	}
+#endif
+
+	/* Audio 1 & Audio 2 */
+        if (status & AUDIO2_IRQ) {
+                if (chip->active & DAC2)
+                	snd_pcm_period_elapsed(chip->playback_a_substream);
+		/* ack interrupt */
+                snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00);
+        }
+        if (status & AUDIO1_IRQ) {
+                /* ok.. capture is active */
+                if (chip->active & ADC1)
+                	snd_pcm_period_elapsed(chip->capture_a_substream);
+                /* ok.. playback2 is active */
+                else if (chip->active & DAC1)
+                	snd_pcm_period_elapsed(chip->playback_b_substream);
+		/* ack interrupt */
+		inb(chip->port + 0x0E);
+        }
+
+	/* MPU */
+	if ((status & MPU_IRQ) && chip->rmidi)
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+
+	/* Hardware volume */
+	if (status & HWV_IRQ) {
+		int split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+		if (!split) {
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+		}
+		/* ack interrupt */
+		snd_es18xx_mixer_write(chip, 0x66, 0x00);
+	}
+
+}
+
+static snd_pcm_uframes_t snd_es18xx_playback_pointer(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int pos;
+
+	switch (substream->number) {
+	case 0:
+		if (!(chip->active & DAC2))
+			return 0;
+		pos = chip->dma2_size - snd_dma_residue(chip->dma2);
+		return pos >> chip->dma2_shift;
+	case 1:
+		if (!(chip->active & DAC1))
+			return 0;
+		pos = chip->dma1_size - snd_dma_residue(chip->dma1);
+		return pos >> chip->dma1_shift;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_es18xx_capture_pointer(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int pos;
+
+        if (!(chip->active & ADC1))
+                return 0;
+	pos = chip->dma1_size - snd_dma_residue(chip->dma1);
+	return pos >> chip->dma1_shift;
+}
+
+static snd_pcm_hardware_t snd_es18xx_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_es18xx_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_es18xx_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+	switch (substream->number) {
+	case 0:
+		if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+		    chip->capture_a_substream && 
+		    chip->capture_a_substream->runtime->channels != 1)
+			return -EAGAIN;
+		chip->playback_a_substream = substream;
+		break;
+	case 1:
+		if (chip->capture_a_substream)
+			return -EAGAIN;
+		chip->playback_b_substream = substream;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	substream->runtime->hw = snd_es18xx_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks);
+        return 0;
+}
+
+static int snd_es18xx_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+        if (chip->playback_b_substream)
+                return -EAGAIN;
+	if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+	    chip->playback_a_substream &&
+	    chip->playback_a_substream->runtime->channels != 1)
+		return -EAGAIN;
+        chip->capture_a_substream = substream;
+	substream->runtime->hw = snd_es18xx_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks);
+        return 0;
+}
+
+static int snd_es18xx_playback_close(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+	switch (substream->number) {
+	case 0:
+		chip->playback_a_substream = NULL;
+		break;
+	case 1:
+		chip->playback_b_substream = NULL;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_es18xx_capture_close(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+        chip->capture_a_substream = NULL;
+	snd_pcm_lib_free_pages(substream);
+        return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_es18xx_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es18xx_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es18xx_mixer_read(chip, 0x1c) & 0x07;
+	return 0;
+}
+
+static int snd_es18xx_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = ucontrol->value.enumerated.item[0];
+	
+	if (val > 7)
+		return -EINVAL;
+	return snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val;
+}
+
+static int snd_es18xx_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es18xx_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = snd_es18xx_mixer_read(chip, 0x50);
+	ucontrol->value.integer.value[0] = !!(val & 8);
+	return 0;
+}
+
+static int snd_es18xx_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char oval, nval;
+	int change;
+	nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
+	oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c;
+	change = nval != oval;
+	if (change) {
+		snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04);
+		snd_es18xx_mixer_write(chip, 0x50, nval);
+	}
+	return change;
+}
+
+static int snd_es18xx_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 63;
+	return 0;
+}
+
+static int snd_es18xx_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f;
+	ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f;
+	return 0;
+}
+
+static int snd_es18xx_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es18xx_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40);
+	ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40);
+	return 0;
+}
+
+static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, _snd_kcontrol_chip(kcontrol), return);
+	chip->master_volume = NULL;
+	chip->master_switch = NULL;
+	chip->hw_volume = NULL;
+	chip->hw_switch = NULL;
+}
+
+static int snd_es18xx_reg_bits(es18xx_t *chip, unsigned char reg,
+			       unsigned char mask, unsigned char val)
+{
+	if (reg < 0xa0)
+		return snd_es18xx_mixer_bits(chip, reg, mask, val);
+	else
+		return snd_es18xx_bits(chip, reg, mask, val);
+}
+
+static int snd_es18xx_reg_read(es18xx_t *chip, unsigned char reg)
+{
+	if (reg < 0xa0)
+		return snd_es18xx_mixer_read(chip, reg);
+	else
+		return snd_es18xx_read(chip, reg);
+}
+
+#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es18xx_info_single, \
+  get: snd_es18xx_get_single, put: snd_es18xx_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es18xx_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int val;
+	
+	val = snd_es18xx_reg_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es18xx_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	mask <<= shift;
+	val <<= shift;
+	return snd_es18xx_reg_bits(chip, reg, mask, val) != val;
+}
+
+#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es18xx_info_double, \
+  get: snd_es18xx_get_double, put: snd_es18xx_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es18xx_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	left = snd_es18xx_reg_read(chip, left_reg);
+	if (left_reg != right_reg)
+		right = snd_es18xx_reg_read(chip, right_reg);
+	else
+		right = left;
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es18xx_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, mask1, mask2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	mask1 = mask << shift_left;
+	mask2 = mask << shift_right;
+	if (left_reg != right_reg) {
+		change = 0;
+		if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1)
+			change = 1;
+		if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2)
+			change = 1;
+	} else {
+		change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, 
+					      val1 | val2) != (val1 | val2));
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_es18xx_base_controls[] = {
+ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
+ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
+ES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
+ES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
+ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0),
+ES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
+ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_es18xx_info_mux,
+	get: snd_es18xx_get_mux,
+	put: snd_es18xx_put_mux,
+}
+};
+
+static snd_kcontrol_new_t snd_es18xx_mono_in_control = 
+ES18XX_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0);
+
+static snd_kcontrol_new_t snd_es18xx_recmix_controls[] = {
+ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
+ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
+ES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
+ES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
+ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
+};
+
+static snd_kcontrol_new_t snd_es18xx_pcm1_controls[] = {
+ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),
+};
+
+static snd_kcontrol_new_t snd_es18xx_pcm2_controls[] = {
+ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
+ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)
+};
+
+static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = {
+ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "3D Control - Switch",
+	info: snd_es18xx_info_spatializer_enable,
+	get: snd_es18xx_get_spatializer_enable,
+	put: snd_es18xx_put_spatializer_enable,
+}
+};
+
+static snd_kcontrol_new_t snd_es18xx_micpre1_control = 
+ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);
+
+static snd_kcontrol_new_t snd_es18xx_micpre2_control =
+ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);
+
+static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = {
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Volume",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es18xx_info_hw_volume,
+	get: snd_es18xx_get_hw_volume,
+},
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Switch",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es18xx_info_hw_switch,
+	get: snd_es18xx_get_hw_switch,
+},
+ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
+};
+
+#if 0
+static int __init snd_es18xx_config_read(es18xx_t *chip, unsigned char reg)
+{
+	int data;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->ctrl_lock, flags);
+	outb(reg, chip->ctrl_port);
+	data = inb(chip->ctrl_port + 1);
+        spin_unlock_irqrestore(&chip->ctrl_lock, flags);
+	return data;
+}
+#endif
+
+static void __init snd_es18xx_config_write(es18xx_t *chip, 
+					   unsigned char reg, unsigned char data)
+{
+	/* No need for spinlocks, this function is used only in
+	   otherwise protected init code */
+	outb(reg, chip->ctrl_port);
+	outb(data, chip->ctrl_port + 1);
+#ifdef REG_DEBUG
+	snd_printk("Config reg %02x set to %02x\n", reg, data);
+#endif
+}
+
+static int __init snd_es18xx_initialize(es18xx_t *chip)
+{
+	int mask = 0;
+
+        /* enable extended mode */
+        snd_es18xx_dsp_command(chip, 0xC6);
+	/* Reset mixer registers */
+	snd_es18xx_mixer_write(chip, 0x00, 0x00);
+
+        /* Audio 1 DMA demand mode (4 bytes/request) */
+        snd_es18xx_write(chip, 0xB9, 2);
+	if (chip->caps & ES18XX_CONTROL) {
+		/* Hardware volume IRQ */
+		snd_es18xx_config_write(chip, 0x27, chip->irq);
+		if (chip->fm_port > SNDRV_AUTO_PORT) {
+			/* FM I/O */
+			snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8);
+			snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff);
+		}
+		if (chip->mpu_port > SNDRV_AUTO_PORT) {
+			/* MPU-401 I/O */
+			snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8);
+			snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff);
+			/* MPU-401 IRQ */
+			snd_es18xx_config_write(chip, 0x28, chip->irq);
+		}
+		/* Audio1 IRQ */
+		snd_es18xx_config_write(chip, 0x70, chip->irq);
+		/* Audio2 IRQ */
+		snd_es18xx_config_write(chip, 0x72, chip->irq);
+		/* Audio1 DMA */
+		snd_es18xx_config_write(chip, 0x74, chip->dma1);
+		/* Audio2 DMA */
+		snd_es18xx_config_write(chip, 0x75, chip->dma2);
+
+		/* Enable Audio 1 IRQ */
+		snd_es18xx_write(chip, 0xB1, 0x50);
+		/* Enable Audio 2 IRQ */
+		snd_es18xx_mixer_write(chip, 0x7A, 0x40);
+		/* Enable Audio 1 DMA */
+		snd_es18xx_write(chip, 0xB2, 0x50);
+		/* Enable MPU and hardware volume interrupt */
+		snd_es18xx_mixer_write(chip, 0x64, 0x42);
+	}
+	else {
+		int irqmask, dma1mask, dma2mask;
+		switch (chip->irq) {
+		case 2:
+		case 9:
+			irqmask = 0;
+			break;
+		case 5:
+			irqmask = 1;
+			break;
+		case 7:
+			irqmask = 2;
+			break;
+		case 10:
+			irqmask = 3;
+			break;
+		default:
+			snd_printk("invalid irq %d\n", chip->irq);
+			return -ENODEV;
+		}
+		switch (chip->dma1) {
+		case 0:
+			dma1mask = 1;
+			break;
+		case 1:
+			dma1mask = 2;
+			break;
+		case 3:
+			dma1mask = 3;
+			break;
+		default:
+			snd_printk("invalid dma1 %d\n", chip->dma1);
+			return -ENODEV;
+		}
+		switch (chip->dma2) {
+		case 0:
+			dma2mask = 0;
+			break;
+		case 1:
+			dma2mask = 1;
+			break;
+		case 3:
+			dma2mask = 2;
+			break;
+		case 5:
+			dma2mask = 3;
+			break;
+		default:
+			snd_printk("invalid dma2 %d\n", chip->dma2);
+			return -ENODEV;
+		}
+
+		/* Enable and set Audio 1 IRQ */
+		snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2));
+		/* Enable and set Audio 1 DMA */
+		snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2));
+		/* Set Audio 2 DMA */
+		snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask);
+		/* Enable Audio 2 IRQ and DMA
+		   Set capture mixer input */
+		snd_es18xx_mixer_write(chip, 0x7A, 0x68);
+		/* Enable and set hardware volume interrupt */
+		snd_es18xx_mixer_write(chip, 0x64, 0x06);
+		if (chip->mpu_port > SNDRV_AUTO_PORT) {
+			/* MPU401 share irq with audio
+			   Joystick enabled
+			   FM enabled */
+			snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1);
+		}
+		snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
+	}
+	if (chip->caps & ES18XX_NEW_RATE) {
+		/* Change behaviour of register A1
+		   4x oversampling
+		   2nd channel DAC asynchronous */
+		snd_es18xx_mixer_write(chip, 0x71, 0x32);
+	}
+	if (!(chip->caps & ES18XX_PCM2)) {
+		/* Enable DMA FIFO */
+		snd_es18xx_write(chip, 0xB7, 0x80);
+	}
+	if (chip->caps & ES18XX_SPATIALIZER) {
+		/* Set spatializer parameters to recommended values */
+		snd_es18xx_mixer_write(chip, 0x54, 0x8f);
+		snd_es18xx_mixer_write(chip, 0x56, 0x95);
+		snd_es18xx_mixer_write(chip, 0x58, 0x94);
+		snd_es18xx_mixer_write(chip, 0x5a, 0x80);
+	}
+	/* Mute input source */
+	if (chip->caps & ES18XX_MUTEREC)
+		mask = 0x10;
+	if (chip->caps & ES18XX_RECMIX)
+		snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask);
+	else {
+		snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask);
+		snd_es18xx_write(chip, 0xb4, 0x00);
+	}
+#ifndef AVOID_POPS
+	/* Enable PCM output */
+	snd_es18xx_dsp_command(chip, 0xD1);
+#endif
+
+        return 0;
+}
+
+static int __init snd_es18xx_identify(es18xx_t *chip)
+{
+	int hi,lo;
+
+	/* reset */
+	if (snd_es18xx_reset(chip) < 0) {
+                snd_printk("reset at 0x%lx failed!!!\n", chip->port);
+		return -ENODEV;
+	}
+
+	snd_es18xx_dsp_command(chip, 0xe7);
+	hi = snd_es18xx_dsp_get_byte(chip);
+	if (hi < 0) {
+		return hi;
+	}
+	lo = snd_es18xx_dsp_get_byte(chip);
+	if ((lo & 0xf0) != 0x80) {
+		return -ENODEV;
+	}
+	if (hi == 0x48) {
+		chip->version = 0x488;
+		return 0;
+	}
+	if (hi != 0x68) {
+		return -ENODEV;
+	}
+	if ((lo & 0x0f) < 8) {
+		chip->version = 0x688;
+		return 0;
+	}
+			
+        outb(0x40, chip->port + 0x04);
+	hi = inb(chip->port + 0x05);
+	lo = inb(chip->port + 0x05);
+	if (hi != lo) {
+		chip->version = hi << 8 | lo;
+		chip->ctrl_port = inb(chip->port + 0x05) << 8;
+		chip->ctrl_port += inb(chip->port + 0x05);
+
+		if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL)
+			return -EBUSY;
+
+		return 0;
+	}
+
+	/* If has Hardware volume */
+	if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) {
+		/* If has Audio2 */
+		if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) {
+			/* If has volume count */
+			if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) {
+				chip->version = 0x1887;
+			} else {
+				chip->version = 0x1888;
+			}
+		} else {
+			chip->version = 0x1788;
+		}
+	}
+	else
+		chip->version = 0x1688;
+	return 0;
+}
+
+static int __init snd_es18xx_probe(es18xx_t *chip)
+{
+	if (snd_es18xx_identify(chip) < 0) {
+		printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
+                return -ENODEV;
+	}
+
+	switch (chip->version) {
+	case 0x1868:
+		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1869:
+		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1878:
+		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1879:
+		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1887:
+		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV;
+		break;
+	case 0x1888:
+		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV;
+		break;
+	default:
+                snd_printk("[0x%lx] unsupported chip ES%x\n",
+                           chip->port, chip->version);
+                return -ENODEV;
+        }
+
+        snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version);
+
+        return snd_es18xx_initialize(chip);
+}
+
+static snd_pcm_ops_t snd_es18xx_playback_ops = {
+	open:		snd_es18xx_playback_open,
+	close:		snd_es18xx_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_es18xx_playback_hw_params,
+	prepare:	snd_es18xx_playback_prepare,
+	trigger:	snd_es18xx_playback_trigger,
+	pointer:	snd_es18xx_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es18xx_capture_ops = {
+	open:		snd_es18xx_capture_open,
+	close:		snd_es18xx_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_es18xx_capture_hw_params,
+	prepare:	snd_es18xx_capture_prepare,
+	trigger:	snd_es18xx_capture_trigger,
+	pointer:	snd_es18xx_capture_pointer,
+};
+
+static void snd_es18xx_pcm_free(snd_pcm_t *pcm)
+{
+	es18xx_t *codec = snd_magic_cast(es18xx_t, pcm->private_data, return);
+	codec->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __init snd_es18xx_pcm(es18xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+        snd_pcm_t *pcm;
+	char str[16];
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	sprintf(str, "ES%x", chip->version);
+	if (chip->caps & ES18XX_PCM2) {
+		err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm);
+	} else {
+		err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm);
+	}
+        if (err < 0)
+                return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops);
+
+	/* global setup */
+        pcm->private_data = chip;
+	pcm->private_free = snd_es18xx_pcm_free;
+        pcm->info_flags = 0;
+	if (chip->caps & ES18XX_DUPLEX_SAME)
+		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+	sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version);
+        chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+
+        if (rpcm)
+        	*rpcm = pcm;
+	return 0;
+}
+
+/* Power Management support functions */
+#ifdef CONFIG_PM
+static void snd_es18xx_suspend(es18xx_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	snd_pcm_suspend_all(chip->pcm);
+
+	/* power down */
+	chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM);
+	chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS);
+	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg);
+	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void snd_es18xx_resume(es18xx_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	/* restore PM register, we won't wake till (not 0x07) i/o activity though */
+	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+/* callback for control API */
+static int snd_es18xx_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	es18xx_t *chip = (es18xx_t *) card->power_state_private_data;
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_es18xx_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_es18xx_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_es18xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, dev->data, return 0);
+
+	switch (rqst) {
+	case PM_SUSPEND:
+		snd_es18xx_suspend(chip);
+		break;
+	case PM_RESUME:
+		snd_es18xx_resume(chip);
+		break;
+	}
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_es18xx_free(es18xx_t *chip)
+{
+#ifdef CONFIG_PM
+	if (chip->pm_dev)
+		pm_unregister(chip->pm_dev);
+#endif
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->res_ctrl_port) {
+		release_resource(chip->res_ctrl_port);
+		kfree_nocheck(chip->res_ctrl_port);
+	}
+	if (chip->res_mpu_port) {
+		release_resource(chip->res_mpu_port);
+		kfree_nocheck(chip->res_mpu_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma1 >= 0) {
+		disable_dma(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (chip->dma2 >= 0) {
+		disable_dma(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es18xx_dev_free(snd_device_t *device)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, device->device_data, return -ENXIO);
+	return snd_es18xx_free(chip);
+}
+
+static int __init snd_es18xx_new_device(snd_card_t * card,
+					unsigned long port,
+					unsigned long mpu_port,
+					unsigned long fm_port,
+					int irq, int dma1, int dma2,
+					es18xx_t ** rchip)
+{
+        es18xx_t *chip;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_es18xx_dev_free,
+        };
+	int err;
+
+	*rchip = NULL;
+        chip = snd_magic_kcalloc(es18xx_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+ 	spin_lock_init(&chip->mixer_lock);
+ 	spin_lock_init(&chip->ctrl_lock);
+        chip->card = card;
+        chip->port = port;
+        chip->mpu_port = mpu_port;
+        chip->fm_port = fm_port;
+        chip->irq = -1;
+        chip->dma1 = -1;
+        chip->dma2 = -1;
+        chip->audio2_vol = 0x00;
+	chip->active = 0;
+
+	if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) {
+		snd_es18xx_free(chip);
+		printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) {
+		snd_es18xx_free(chip);
+		printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+
+	if (request_dma(dma1, "ES18xx DMA 1")) {
+		snd_es18xx_free(chip);
+		printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+
+	if (request_dma(dma2, "ES18xx DMA 2")) {
+		snd_es18xx_free(chip);
+		printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
+		return -EBUSY;
+	}
+	chip->dma2 = dma2;
+
+        if (snd_es18xx_probe(chip) < 0) {
+                snd_es18xx_free(chip);
+                return -ENODEV;
+        }
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es18xx_free(chip);
+		return err;
+	}
+        *rchip = chip;
+        return 0;
+}
+
+static int __init snd_es18xx_mixer(es18xx_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+
+	card = chip->card;
+
+	strcpy(card->mixername, chip->pcm->name);
+
+	for (idx = 0; idx < sizeof(snd_es18xx_base_controls) / 
+		     sizeof(snd_es18xx_base_controls[0]); idx++) {
+		snd_kcontrol_t *kctl;
+		kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip);
+		if (chip->caps & ES18XX_HWV) {
+			switch (idx) {
+			case 0:
+				chip->master_volume = kctl;
+				kctl->private_free = snd_es18xx_hwv_free;
+				break;
+			case 1:
+				chip->master_switch = kctl;
+				kctl->private_free = snd_es18xx_hwv_free;
+				break;
+			}
+		}
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+	if (chip->caps & ES18XX_PCM2) {
+		for (idx = 0; idx < sizeof(snd_es18xx_pcm2_controls) / 
+			     sizeof(snd_es18xx_pcm2_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0)
+				return err;
+		} 
+	} else {
+		for (idx = 0; idx < sizeof(snd_es18xx_pcm1_controls) / 
+			     sizeof(snd_es18xx_pcm1_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+
+	if (chip->caps & ES18XX_MONO) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_mono_in_control, chip))) < 0)
+			return err;
+	}
+	if (chip->caps & ES18XX_RECMIX) {
+		for (idx = 0; idx < sizeof(snd_es18xx_recmix_controls) / 
+			     sizeof(snd_es18xx_recmix_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	switch (chip->version) {
+	default:
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0)
+			return err;
+		break;
+	case 0x1869:
+	case 0x1879:
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0)
+			return err;
+		break;
+	}
+	if (chip->caps & ES18XX_SPATIALIZER) {
+		for (idx = 0; idx < sizeof(snd_es18xx_spatializer_controls) / 
+			     sizeof(snd_es18xx_spatializer_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	if (chip->caps & ES18XX_HWV) {
+		for (idx = 0; idx < sizeof(snd_es18xx_hw_volume_controls) / 
+			     sizeof(snd_es18xx_hw_volume_controls[0]); idx++) {
+			snd_kcontrol_t *kctl;
+			kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip);
+			if (idx == 0)
+				chip->hw_volume = kctl;
+			else
+				chip->hw_switch = kctl;
+			kctl->private_free = snd_es18xx_hwv_free;
+			if ((err = snd_ctl_add(card, kctl)) < 0)
+				return err;
+			
+		}
+	}
+	return 0;
+}
+       
+
+/* Card level */
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>");  
+MODULE_DESCRIPTION("ESS ES18xx AudioDrive");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,ES1868 PnP AudioDrive},"
+		"{ESS,ES1869 PnP AudioDrive},"
+		"{ESS,ES1878 PnP AudioDrive},"
+		"{ESS,ES1879 PnP AudioDrive},"
+		"{ESS,ES1887 PnP AudioDrive},"
+		"{ESS,ES1888 PnP AudioDrive},"
+		"{ESS,ES1887 AudioDrive},"
+		"{ESS,ES1888 AudioDrive}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+#ifdef __ISAPNP__
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
+#ifndef __ISAPNP__
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+#else
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#endif
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ES18xx soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ES18xx soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ES18xx soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list");
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x330,0x30},{0x800,0xffe,0x2}},prefers:{0x330,0x300},base:16,dialog:combo");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x800,0xffc,0x4}},prefers:{0x388},base:16,dialog:combo");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC ",prefers:{5}");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA 1 # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC ",prefers:{1}");
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA 2 # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{0},{1},{3},{5}},dialog:list,prefers:{0}");
+
+struct snd_audiodrive {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devc;
+#endif
+};
+
+static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_audiodrive_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_audiodrive_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_ES18XX(_va, _vb, _vc, _device, _audio, _control) \
+        { \
+                ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _control) } \
+        }
+
+static struct isapnp_card_id snd_audiodrive_pnpids[] __devinitdata = {
+	/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
+	ISAPNP_ES18XX('E','S','S',0x1868,0x1868,0x0000),
+	/* ESS 1868 (integrated on Maxisound Cards) */
+	ISAPNP_ES18XX('E','S','S',0x1868,0x8601,0x8600),
+	/* ESS 1868 (integrated on Maxisound Cards) */
+	ISAPNP_ES18XX('E','S','S',0x1868,0x8611,0x8610),
+	/* ESS ES1869 Plug and Play AudioDrive */
+	ISAPNP_ES18XX('E','S','S',0x0003,0x1869,0x0006),
+	/* ESS 1869 */
+	ISAPNP_ES18XX('E','S','S',0x1869,0x1869,0x0006),
+	/* ESS 1878 */
+	ISAPNP_ES18XX('E','S','S',0x1878,0x1878,0x0004),
+	/* ESS 1879 */
+	ISAPNP_ES18XX('E','S','S',0x1879,0x1879,0x0009),
+	/* --- */
+	{ ISAPNP_CARD_END, } /* end */
+};
+
+ISAPNP_CARD_TABLE(snd_audiodrive_pnpids);
+
+static int __init snd_audiodrive_isapnp(int dev, struct snd_audiodrive *acard)
+{
+	const struct isapnp_card_id *id = snd_audiodrive_isapnp_id[dev];
+	struct isapnp_card *card = snd_audiodrive_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devc->active) {
+		acard->dev = acard->devc = NULL;
+		return -EBUSY;
+	}
+	/* Control port initialization */
+	if (acard->devc->prepare(acard->devc)<0)
+		return -EAGAIN;
+	if (acard->devc->activate(acard->devc)<0) {
+		printk(KERN_ERR PFX "isapnp control configure failure (out of resources?)\n");
+		return -EAGAIN;
+	}
+	snd_printdd("isapnp: port=0x%lx\n", acard->devc->resource[0].start);
+	/* PnP initialization */
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0) {
+		acard->devc->deactivate(acard->devc);
+		return -EAGAIN;
+	}
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_mpu_port[dev], 2);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n");
+		acard->devc->deactivate(acard->devc);
+		return -EBUSY;
+	}
+	/* ok. hack using Vendor-Defined Card-Level registers */
+	/* skip csn and logdev initialization - already done in isapnp_configure */
+	isapnp_cfg_begin(pdev->bus->number, pdev->devfn);
+	isapnp_write_byte(0x27, pdev->irq_resource[0].start);	/* Hardware Volume IRQ Number */
+	if (snd_mpu_port[dev] > SNDRV_AUTO_PORT)
+		isapnp_write_byte(0x28, pdev->irq);		/* MPU-401 IRQ Number */
+	isapnp_write_byte(0x72, pdev->irq_resource[0].start);	/* second IRQ */
+	isapnp_cfg_end();
+	snd_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_mpu_port[dev] = pdev->resource[2].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", snd_port[dev], snd_fm_port[dev], snd_mpu_port[dev]);
+	snd_printdd("isapnp ES18xx: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]);
+	return 0;
+}
+
+static void snd_audiodrive_deactivate(struct snd_audiodrive *acard)
+{
+	if (acard->devc) {
+		acard->devc->deactivate(acard->devc);
+		acard->devc = NULL;
+	}
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+}
+#endif /* __ISAPNP__ */
+
+static void snd_audiodrive_free(snd_card_t *card)
+{
+	struct snd_audiodrive *acard = (struct snd_audiodrive *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_audiodrive_deactivate(acard);
+#endif
+	}
+}
+
+static int __init snd_audiodrive_probe(int dev)
+{
+	static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
+	static int possible_dmas[] = {1, 0, 3, 5, -1};
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_audiodrive *acard;
+	snd_rawmidi_t *rmidi = NULL;
+	es18xx_t *chip;
+	opl3_t *opl3;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_audiodrive));
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_audiodrive *)card->private_data;
+	card->private_free = snd_audiodrive_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_audiodrive_isapnp(dev, acard)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+        dma1 = snd_dma1[dev];
+        if (dma1 == SNDRV_AUTO_DMA) {
+                if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+                        snd_card_free(card);
+                        snd_printk("unable to find a free DMA1\n");
+                        return -EBUSY;
+                }
+        }
+        dma2 = snd_dma2[dev];
+        if (dma2 == SNDRV_AUTO_DMA) {
+                if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+                        snd_card_free(card);
+                        snd_printk("unable to find a free DMA2\n");
+                        return -EBUSY;
+                }
+        }
+
+	if ((err = snd_es18xx_new_device(card,
+					 snd_port[dev],
+					 snd_mpu_port[dev],
+					 snd_fm_port[dev],
+					 irq, dma1, dma2,
+					 &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es18xx_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) {
+		printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->port);
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
+					       chip->mpu_port, 0,
+					       irq, 0,
+					       &rmidi)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		chip->rmidi = rmidi;
+	}
+
+#ifdef CONFIG_PM
+	/* Power Management */
+	chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_es18xx_pm_callback);
+	if (chip->pm_dev) {
+		chip->pm_dev->data = chip;
+		/* set control api callback */
+		card->set_power_state = snd_es18xx_set_power_state;
+		card->power_state_private_data = chip;
+	}
+#endif
+	sprintf(card->driver, "ES%x", chip->version);
+	sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version);
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d",
+		card->shortname,
+		chip->port,
+		irq, dma1, dma2);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_audiodrive_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_audiodrive_probe_legacy_port(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		snd_port[dev] = port;
+		res = snd_audiodrive_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+
+#ifdef __ISAPNP__
+static int __init snd_audiodrive_isapnp_detect(struct isapnp_card *card,
+					       const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_audiodrive_isapnp_cards[dev] = card;
+                snd_audiodrive_isapnp_id[dev] = id;
+                res = snd_audiodrive_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_es18xx_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1};
+	int dev, cards = 0;
+
+	/* legacy non-auto cards at first */
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_audiodrive_probe(dev) >= 0)
+			cards++;
+	}
+	/* legacy auto configured cards */
+	cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_probe_legacy_port);
+#ifdef __ISAPNP__
+	/* ISA PnP cards at last */
+	cards += isapnp_probe_cards(snd_audiodrive_pnpids, snd_audiodrive_isapnp_detect);
+#endif
+	if(!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "ESS AudioDrive ES18xx soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_es18xx_exit(void)
+{
+	int idx;
+
+	for(idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_audiodrive_cards[idx]);
+}
+
+module_init(alsa_card_es18xx_init)
+module_exit(alsa_card_es18xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-es18xx=snd_enable,snd_index,snd_id,snd_isapnp,
+			 snd_port,snd_mpu_port,snd_fm_port,snd_irq,
+			 snd_dma1,snd_dma2 */
+
+static int __init alsa_card_es18xx_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-es18xx=", alsa_card_es18xx_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/gus/Makefile linux-2.4.19-pre5-mjc/sound/isa/gus/Makefile
--- linux/sound/isa/gus/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,62 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _gus.o
+
+list-multi   := snd-gus-lib.o snd-gusclassic.o snd-gusextreme.o \
+		snd-gusmax.o snd-interwave.o snd-interwave-stb.o
+
+export-objs  := gus_main.o gus_volume.o
+
+snd-gus-lib-objs := gus_main.o \
+		    gus_io.o gus_irq.o gus_timer.o \
+		    gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \
+		    gus_pcm.o gus_mixer.o \
+		    gus_uart.o \
+		    gus_reset.o
+snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o
+
+snd-gusclassic-objs := gusclassic.o
+snd-gusextreme-objs := gusextreme.o
+snd-gusmax-objs := gusmax.o
+snd-interwave-objs := interwave.o
+snd-interwave-stb-objs := interwave-stb.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
+obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o
+obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_GUSCLASSIC) += snd-gus-synth.o
+  obj-$(CONFIG_SND_GUSMAX) += snd-gus-synth.o
+  obj-$(CONFIG_SND_GUSEXTREME) += snd-gus-synth.o
+  obj-$(CONFIG_SND_INTERWAVE) += snd-gus-synth.o
+  obj-$(CONFIG_SND_INTERWAVE_STB) += snd-gus-synth.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-gus-lib.o: $(snd-gus-lib-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-lib-objs)
+
+snd-gus-synth.o: $(snd-gus-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-synth-objs)
+
+snd-gusclassic.o: $(snd-gusclassic-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-gusclassic-objs)
+
+snd-gusextreme.o: $(snd-gusextreme-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-gusextreme-objs)
+
+snd-gusmax.o: $(snd-gusmax-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-gusmax-objs)
+
+snd-interwave.o: $(snd-interwave-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-objs)
+
+snd-interwave-stb.o: $(snd-interwave-stb-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-stb-objs)
diff -Nru linux/sound/isa/gus/gus_dma.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dma.c
--- linux/sound/isa/gus/gus_dma.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dma.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,245 @@
+/*
+ *  Routines for GF1 DMA control
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+void snd_gf1_dma_ack(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
+	snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_dma_program(snd_gus_card_t * gus,
+			 unsigned int addr,
+			 unsigned long buf_addr,
+			 unsigned int count,
+			 unsigned int cmd)
+{
+	unsigned long flags;
+	unsigned int address;
+	unsigned char dma_cmd;
+	unsigned int address_high;
+
+	// snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count);
+
+	if (gus->gf1.dma1 > 3) {
+		if (gus->gf1.enh_mode) {
+			address = addr >> 1;
+		} else {
+			if (addr & 0x1f) {
+				snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
+				return;
+			}
+			address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
+		}
+	} else {
+		address = addr;
+	}
+
+	dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
+#if 0
+	dma_cmd |= 0x08;
+#endif
+	if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
+		count++;
+		count &= ~1;	/* align */
+	}
+	if (gus->gf1.dma1 > 3) {
+		dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
+		count++;
+		count &= ~1;	/* align */
+	}
+	snd_gf1_dma_ack(gus);
+	snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
+#if 0
+	snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd);
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	if (gus->gf1.enh_mode) {
+		address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
+		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
+		snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
+	} else
+		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus)
+{
+	snd_gf1_dma_block_t *block;
+
+	/* PCM block have bigger priority than synthesizer one */
+	if (gus->gf1.dma_data_pcm) {
+		block = gus->gf1.dma_data_pcm;
+		if (gus->gf1.dma_data_pcm_last == block) {
+			gus->gf1.dma_data_pcm =
+			gus->gf1.dma_data_pcm_last = NULL;
+		} else {
+			gus->gf1.dma_data_pcm = block->next;
+		}
+	} else if (gus->gf1.dma_data_synth) {
+		block = gus->gf1.dma_data_synth;
+		if (gus->gf1.dma_data_synth_last == block) {
+			gus->gf1.dma_data_synth =
+			gus->gf1.dma_data_synth_last = NULL;
+		} else {
+			gus->gf1.dma_data_synth = block->next;
+		}
+	} else {
+		block = NULL;
+	}
+	if (block) {
+		gus->gf1.dma_ack = block->ack;
+		gus->gf1.dma_private_data = block->private_data;
+	}
+	return block;
+}
+
+
+static void snd_gf1_dma_interrupt(snd_gus_card_t * gus)
+{
+	snd_gf1_dma_block_t *block;
+
+	snd_gf1_dma_ack(gus);
+	if (gus->gf1.dma_ack)
+		gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
+	spin_lock(&gus->dma_lock);
+	if (gus->gf1.dma_data_pcm == NULL &&
+	    gus->gf1.dma_data_synth == NULL) {
+	    	gus->gf1.dma_ack = NULL;
+		gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
+		spin_unlock(&gus->dma_lock);
+		return;
+	}
+	block = snd_gf1_dma_next_block(gus);
+	spin_unlock(&gus->dma_lock);
+	snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
+	kfree(block);
+#if 0
+	printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd);
+#endif
+}
+
+int snd_gf1_dma_init(snd_gus_card_t * gus)
+{
+	down(&gus->dma_mutex);
+	gus->gf1.dma_shared++;
+	if (gus->gf1.dma_shared > 1) {
+		up(&gus->dma_mutex);
+		return 0;
+	}
+	gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
+	gus->gf1.dma_data_pcm = 
+	gus->gf1.dma_data_pcm_last =
+	gus->gf1.dma_data_synth = 
+	gus->gf1.dma_data_synth_last = NULL;
+	up(&gus->dma_mutex);
+	return 0;
+}
+
+int snd_gf1_dma_done(snd_gus_card_t * gus)
+{
+	snd_gf1_dma_block_t *block;
+
+	down(&gus->dma_mutex);
+	gus->gf1.dma_shared--;
+	if (!gus->gf1.dma_shared) {
+		snd_dma_disable(gus->gf1.dma1);
+		snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
+		snd_gf1_dma_ack(gus);
+		while ((block = gus->gf1.dma_data_pcm)) {
+			gus->gf1.dma_data_pcm = block->next;
+			kfree(block);
+		}
+		while ((block = gus->gf1.dma_data_synth)) {
+			gus->gf1.dma_data_synth = block->next;
+			kfree(block);
+		}
+		gus->gf1.dma_data_pcm_last =
+		gus->gf1.dma_data_synth_last = NULL;
+	}
+	up(&gus->dma_mutex);
+	return 0;
+}
+
+int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
+			       snd_gf1_dma_block_t * __block,
+			       int atomic,
+			       int synth)
+{
+	unsigned long flags;
+	snd_gf1_dma_block_t *block;
+
+	block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
+	if (block == NULL) {
+		snd_printk("gf1: DMA transfer failure; not enough memory\n");
+		return -ENOMEM;
+	}
+	*block = *__block;
+	block->next = NULL;
+#if 0
+	printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd);
+#endif
+#if 0
+	printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last);
+	printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm);
+#endif
+	spin_lock_irqsave(&gus->dma_lock, flags);
+	if (synth) {
+		if (gus->gf1.dma_data_synth_last) {
+			gus->gf1.dma_data_synth_last->next = block;
+			gus->gf1.dma_data_synth_last = block;
+		} else {
+			gus->gf1.dma_data_synth = 
+			gus->gf1.dma_data_synth_last = block;
+		}
+	} else {
+		if (gus->gf1.dma_data_pcm_last) {
+			gus->gf1.dma_data_pcm_last->next = block;
+			gus->gf1.dma_data_pcm_last = block;
+		} else {
+			gus->gf1.dma_data_pcm = 
+			gus->gf1.dma_data_pcm_last = block;
+		}
+	}
+	if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
+		gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
+		block = snd_gf1_dma_next_block(gus);
+		spin_unlock_irqrestore(&gus->dma_lock, flags);
+		if (block == NULL)
+			return 0;
+		snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
+		kfree(block);
+		return 0;
+	}
+	spin_unlock_irqrestore(&gus->dma_lock, flags);
+	return 0;
+}
diff -Nru linux/sound/isa/gus/gus_dram.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dram.c
--- linux/sound/isa/gus/gus_dram.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dram.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,104 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  DRAM access routines
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+
+static int snd_gus_dram_poke(snd_gus_card_t *gus, char *_buffer,
+			     unsigned int address, unsigned int size)
+{
+	unsigned long flags;
+	unsigned int size1, size2;
+	char buffer[512], *pbuffer;
+
+	while (size > 0) {
+		if (copy_from_user(buffer, _buffer, 512))
+			return -EFAULT;
+		size1 = size > 512 ? 512 : size;
+		if (gus->interwave) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+			snd_gf1_dram_addr(gus, address);
+			outsb(GUSP(gus, DRAM), buffer, size1);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			address += size1;
+		} else {
+			pbuffer = buffer;
+			size2 = size1;
+			while (size2--)
+				snd_gf1_poke(gus, address++, *pbuffer++);
+		}
+		size -= size1;
+		_buffer += size1;
+	}
+	return 0;
+}
+
+
+int snd_gus_dram_write(snd_gus_card_t *gus, char *buffer,
+		       unsigned int address, unsigned int size)
+{
+	return snd_gus_dram_poke(gus, buffer, address, size);
+}
+
+static int snd_gus_dram_peek(snd_gus_card_t *gus, char *_buffer,
+			     unsigned int address, unsigned int size,
+			     int rom)
+{
+	unsigned long flags;
+	unsigned int size1, size2;
+	char buffer[512], *pbuffer;
+
+	while (size > 0) {
+		size1 = size > 512 ? 512 : size;
+		if (gus->interwave) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01);
+			snd_gf1_dram_addr(gus, address);
+			insb(GUSP(gus, DRAM), buffer, size1);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			address += size1;
+		} else {
+			pbuffer = buffer;
+			size2 = size1;
+			while (size2--)
+				*pbuffer++ = snd_gf1_peek(gus, address++);
+		}
+		if (copy_to_user(_buffer, buffer, size1))
+			return -EFAULT;
+		size -= size1;
+		_buffer += size1;
+	}
+	return 0;
+}
+
+int snd_gus_dram_read(snd_gus_card_t *gus, char *buffer,
+		      unsigned int address, unsigned int size,
+		      int rom)
+{
+	return snd_gus_dram_peek(gus, buffer, address, size, rom);
+}
diff -Nru linux/sound/isa/gus/gus_instr.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_instr.c
--- linux/sound/isa/gus/gus_instr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_instr.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,174 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Synthesizer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+/*
+ *
+ */
+
+int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+	snd_gf1_mem_block_t *block;
+	int err;
+
+	if (wave->format & IWFFFF_WAVE_ROM)
+		return 0;	/* it's probably ok - verify the address? */
+	if (wave->format & IWFFFF_WAVE_STEREO)
+		return -EINVAL;	/* not supported */
+	block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+				  SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF,
+				  NULL, wave->size,
+				  wave->format & IWFFFF_WAVE_16BIT, 1,
+				  wave->share_id);
+	if (block == NULL)
+		return -ENOMEM;
+	err = snd_gus_dram_write(gus, data,
+				 block->ptr, wave->size);
+	if (err < 0) {
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+		snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+		return err;
+	}
+	wave->address.memory = block->ptr;
+	return 0;
+}
+
+int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gus_dram_read(gus, data, wave->address.memory, wave->size,
+				 wave->format & IWFFFF_WAVE_ROM ? 1 : 0);
+}
+
+int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave,
+				 int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	if (wave->format & IWFFFF_WAVE_ROM)
+		return 0;	/* it's probably ok - verify the address? */	
+	return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
+}
+
+/*
+ *
+ */
+
+int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+	snd_gf1_mem_block_t *block;
+	int err;
+
+	if (wave->format & GF1_WAVE_STEREO)
+		return -EINVAL;	/* not supported */
+	block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+				  SNDRV_GF1_MEM_OWNER_WAVE_GF1,
+				  NULL, wave->size,
+				  wave->format & GF1_WAVE_16BIT, 1,
+				  wave->share_id);
+	if (block == NULL)
+		return -ENOMEM;
+	err = snd_gus_dram_write(gus, data,
+				 block->ptr, wave->size);
+	if (err < 0) {
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+		snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+		return err;
+	}
+	wave->address.memory = block->ptr;
+	return 0;
+}
+
+int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0);
+}
+
+int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave,
+			      int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
+}
+
+/*
+ *
+ */
+
+int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+	snd_gf1_mem_block_t *block;
+	int err;
+
+	if (instr->format & SIMPLE_WAVE_STEREO)
+		return -EINVAL;	/* not supported */
+	block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+				  SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE,
+				  NULL, instr->size,
+				  instr->format & SIMPLE_WAVE_16BIT, 1,
+				  instr->share_id);
+	if (block == NULL)
+		return -ENOMEM;
+	err = snd_gus_dram_write(gus, data, block->ptr, instr->size);
+	if (err < 0) {
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+		snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+		return err;
+	}
+	instr->address.memory = block->ptr;
+	return 0;
+}
+
+int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0);
+}
+
+int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr,
+			         int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory);
+}
diff -Nru linux/sound/isa/gus/gus_io.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_io.c
--- linux/sound/isa/gus/gus_io.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_io.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,532 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  I/O routines for GF1/InterWave synthesizer chips
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+void snd_gf1_delay(snd_gus_card_t * gus)
+{
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		mb();
+		inb(GUSP(gus, DRAM));
+	}
+}
+
+/*
+ *  =======================================================================
+ */
+
+/*
+ *  ok.. stop of control registers (wave & ramp) need some special things..
+ *       big UltraClick (tm) elimination...
+ */
+
+static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned char value;
+
+	outb(reg | 0x80, gus->gf1.reg_regsel);
+	mb();
+	value = inb(gus->gf1.reg_data8);
+	mb();
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8);
+	mb();
+}
+
+static inline void __snd_gf1_write8(snd_gus_card_t * gus,
+				    unsigned char reg,
+				    unsigned char data)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	outb(data, gus->gf1.reg_data8);
+	mb();
+}
+
+static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus,
+					    unsigned char reg)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	return inb(gus->gf1.reg_data8);
+}
+
+static inline void __snd_gf1_write16(snd_gus_card_t * gus,
+				     unsigned char reg, unsigned int data)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) data, gus->gf1.reg_data16);
+	mb();
+}
+
+static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus,
+					      unsigned char reg)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	return inw(gus->gf1.reg_data16);
+}
+
+static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus,
+					 unsigned char reg, unsigned char data)
+{
+	outb(reg, gus->gf1.reg_timerctrl);
+	inb(gus->gf1.reg_timerctrl);
+	inb(gus->gf1.reg_timerctrl);
+	outb(data, gus->gf1.reg_timerdata);
+	inb(gus->gf1.reg_timerctrl);
+	inb(gus->gf1.reg_timerctrl);
+}
+
+static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg,
+                                        unsigned int addr, int w_16bit)
+{
+	if (gus->gf1.enh_mode) {
+		if (w_16bit)
+			addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f);
+		__snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03));
+	} else if (w_16bit)
+		addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1);
+	__snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11));
+	__snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5));
+}
+
+static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus,
+					       unsigned char reg, short w_16bit)
+{
+	unsigned int res;
+
+	res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800;
+	res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff;
+	if (gus->gf1.enh_mode) {
+		res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26;
+		if (w_16bit)
+			res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f);
+	} else if (w_16bit)
+		res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f);
+	return res;
+}
+
+
+/*
+ *  =======================================================================
+ */
+
+void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+	__snd_gf1_ctrl_stop(gus, reg);
+}
+
+void snd_gf1_write8(snd_gus_card_t * gus,
+		    unsigned char reg,
+		    unsigned char data)
+{
+	__snd_gf1_write8(gus, reg, data);
+}
+
+unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return __snd_gf1_look8(gus, reg);
+}
+
+void snd_gf1_write16(snd_gus_card_t * gus,
+		     unsigned char reg,
+		     unsigned int data)
+{
+	__snd_gf1_write16(gus, reg, data);
+}
+
+unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return __snd_gf1_look16(gus, reg);
+}
+
+void snd_gf1_adlib_write(snd_gus_card_t * gus,
+                         unsigned char reg,
+                         unsigned char data)
+{
+	__snd_gf1_adlib_write(gus, reg, data);
+}
+
+void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg,
+                        unsigned int addr, short w_16bit)
+{
+	__snd_gf1_write_addr(gus, reg, addr, w_16bit);
+}
+
+unsigned int snd_gf1_read_addr(snd_gus_card_t * gus,
+                               unsigned char reg,
+                               short w_16bit)
+{
+	return __snd_gf1_read_addr(gus, reg, w_16bit);
+}
+
+/*
+
+ */
+
+void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_ctrl_stop(gus, reg);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_i_write8(snd_gus_card_t * gus,
+		      unsigned char reg,
+                      unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_write8(gus, reg, data);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	res = __snd_gf1_look8(gus, reg);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_i_write16(snd_gus_card_t * gus,
+		       unsigned char reg,
+		       unsigned int data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_write16(gus, reg, data);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned short res;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	res = __snd_gf1_look16(gus, reg);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_i_adlib_write(snd_gus_card_t * gus,
+		           unsigned char reg,
+		           unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_adlib_write(gus, reg, data);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg,
+			  unsigned int addr, short w_16bit)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_write_addr(gus, reg, addr, w_16bit);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus,
+				 unsigned char reg, short w_16bit)
+{
+	unsigned int res;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	res = __snd_gf1_read_addr(gus, reg, w_16bit);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+/*
+
+ */
+
+void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr)
+{
+	outb(0x43, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(0x44, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+}
+
+void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(data, gus->gf1.reg_dram);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	res = inb(gus->gf1.reg_dram);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_SND_DEBUG
+	if (!gus->interwave)
+		snd_printk("snd_gf1_pokew - GF1!!!\n");
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+	mb();
+	outw(data, gus->gf1.reg_data16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr)
+{
+	unsigned long flags;
+	unsigned short res;
+
+#ifdef CONFIG_SND_DEBUG
+	if (!gus->interwave)
+		snd_printk("snd_gf1_peekw - GF1!!!\n");
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+	mb();
+	res = inw(gus->gf1.reg_data16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr,
+			 unsigned short value, unsigned int count)
+{
+	unsigned long port;
+	unsigned long flags;
+
+#ifdef CONFIG_SND_DEBUG
+	if (!gus->interwave)
+		snd_printk("snd_gf1_dram_setmem - GF1!!!\n");
+#endif
+	addr &= ~1;
+	count >>= 1;
+	port = GUSP(gus, GF1DATALOW);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+	while (count--)
+		outw(value, port);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+/*
+
+ */
+
+void snd_gf1_select_active_voices(snd_gus_card_t * gus)
+{
+	unsigned short voices;
+
+	static unsigned short voices_tbl[32 - 14 + 1] =
+	{
+	    44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843,
+	    25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293
+	};
+
+	voices = gus->gf1.active_voices;
+	if (voices > 32)
+		voices = 32;
+	if (voices < 14)
+		voices = 14;
+	if (gus->gf1.enh_mode)
+		voices = 32;
+	gus->gf1.active_voices = voices;
+	gus->gf1.playback_freq =
+	    gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14];
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1));
+		udelay(100);
+	}
+}
+
+#ifdef CONFIG_SND_DEBUG
+
+void snd_gf1_print_voice_registers(snd_gus_card_t * gus)
+{
+	unsigned char mode;
+	int voice, ctrl;
+
+	voice = gus->gf1.active_voice;
+	printk(" -%i- GF1  voice ctrl, ramp ctrl  = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d));
+	printk(" -%i- GF1  frequency              = 0x%x\n", voice, snd_gf1_i_read16(gus, 1));
+	printk(" -%i- GF1  loop start, end        = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4));
+	printk(" -%i- GF1  ramp start, end, rate  = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6));
+	printk(" -%i- GF1  volume                 = 0x%x\n", voice, snd_gf1_i_read16(gus, 9));
+	printk(" -%i- GF1  position               = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4));
+	if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) {	/* enhanced mode */
+		mode = snd_gf1_i_read8(gus, 0x15);
+		printk(" -%i- GFA1 mode                   = 0x%x\n", voice, mode);
+		if (mode & 0x01) {	/* Effect processor */
+			printk(" -%i- GFA1 effect address         = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4));
+			printk(" -%i- GFA1 effect volume          = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16));
+			printk(" -%i- GFA1 effect volume final    = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d));
+			printk(" -%i- GFA1 effect acumulator      = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14));
+		}
+		if (mode & 0x20) {
+			printk(" -%i- GFA1 left offset            = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4);
+			printk(" -%i- GFA1 left offset final      = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4);
+			printk(" -%i- GFA1 right offset           = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4);
+			printk(" -%i- GFA1 right offset final     = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4);
+		} else
+			printk(" -%i- GF1  pan                    = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
+	} else
+		printk(" -%i- GF1  pan                    = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
+}
+
+void snd_gf1_print_global_registers(snd_gus_card_t * gus)
+{
+	unsigned char global_mode = 0x00;
+
+	printk(" -G- GF1 active voices            = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES));
+	if (gus->interwave) {
+		global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE);
+		printk(" -G- GF1 global mode              = 0x%x\n", global_mode);
+	}
+	if (global_mode & 0x02)	/* LFO enabled? */
+		printk(" -G- GF1 LFO base                 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE));
+	printk(" -G- GF1 voices IRQ read          = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ));
+	printk(" -G- GF1 DRAM DMA control         = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL));
+	printk(" -G- GF1 DRAM DMA high/low        = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW));
+	printk(" -G- GF1 DRAM IO high/low         = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW));
+	if (!gus->interwave)
+		printk(" -G- GF1 record DMA control       = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL));
+	printk(" -G- GF1 DRAM IO 16               = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16));
+	if (gus->gf1.enh_mode) {
+		printk(" -G- GFA1 memory config           = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG));
+		printk(" -G- GFA1 memory control          = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL));
+		printk(" -G- GFA1 FIFO record base        = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR));
+		printk(" -G- GFA1 FIFO playback base      = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR));
+		printk(" -G- GFA1 interleave control      = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE));
+	}
+}
+
+void snd_gf1_print_setup_registers(snd_gus_card_t * gus)
+{
+	printk(" -S- mix control                  = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG)));
+	printk(" -S- IRQ status                   = 0x%x\n", inb(GUSP(gus, IRQSTAT)));
+	printk(" -S- timer control                = 0x%x\n", inb(GUSP(gus, TIMERCNTRL)));
+	printk(" -S- timer data                   = 0x%x\n", inb(GUSP(gus, TIMERDATA)));
+	printk(" -S- status read                  = 0x%x\n", inb(GUSP(gus, REGCNTRLS)));
+	printk(" -S- Sound Blaster control        = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL));
+	printk(" -S- AdLib timer 1/2              = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2));
+	printk(" -S- reset                        = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET));
+	if (gus->interwave) {
+		printk(" -S- compatibility                = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY));
+		printk(" -S- decode control               = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL));
+		printk(" -S- version number               = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER));
+		printk(" -S- MPU-401 emul. control A/B    = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B));
+		printk(" -S- emulation IRQ                = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ));
+	}
+}
+
+void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit)
+{
+	if (!w_16bit) {
+		while (count-- > 0)
+			printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++));
+	} else {
+		while (count-- > 0) {
+			printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8));
+			addr += 2;
+		}
+	}
+}
+
+#endif
diff -Nru linux/sound/isa/gus/gus_irq.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_irq.c
--- linux/sound/isa/gus/gus_irq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_irq.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,158 @@
+/*
+ *  Routine for IRQ handling from GF1/InterWave chip
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/gus.h>
+
+#ifdef CONFIG_SND_DEBUG
+#define STAT_ADD(x)	((x)++)
+#else
+#define STAT_ADD(x)	while (0) { ; }
+#endif
+
+void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return);
+	unsigned char status;
+	int loop = 100;
+	
+      __again:
+	status = inb(gus->gf1.reg_irqstat);
+	if (status == 0)
+		return;
+	// snd_printk("IRQ: status = 0x%x\n", status);
+	if (status & 0x02) {
+		STAT_ADD(gus->gf1.interrupt_stat_midi_in);
+		gus->gf1.interrupt_handler_midi_in(gus);
+	}
+	if (status & 0x01) {
+		STAT_ADD(gus->gf1.interrupt_stat_midi_out);
+		gus->gf1.interrupt_handler_midi_out(gus);
+	}
+	if (status & (0x20 | 0x40)) {
+		unsigned int already, _current_;
+		unsigned char voice_status, voice;
+		snd_gus_voice_t *pvoice;
+
+		already = 0;
+		while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) {
+			voice = voice_status & 0x1f;
+			_current_ = 1 << voice;
+			if (already & _current_)
+				continue;	/* multi request */
+			already |= _current_;	/* mark request */
+#if 0
+			printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE)));
+#endif
+			pvoice = &gus->gf1.voices[voice]; 
+			if (pvoice->use) {
+				if (!(voice_status & 0x80)) {	/* voice position IRQ */
+					STAT_ADD(pvoice->interrupt_stat_wave);
+					pvoice->handler_wave(gus, pvoice);
+				}
+				if (!(voice_status & 0x40)) {	/* volume ramp IRQ */
+					STAT_ADD(pvoice->interrupt_stat_volume);
+					pvoice->handler_volume(gus, pvoice);
+				}
+			} else {
+				STAT_ADD(gus->gf1.interrupt_stat_voice_lost);
+				snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+				snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+			}
+		}
+	}
+	if (status & 0x04) {
+		STAT_ADD(gus->gf1.interrupt_stat_timer1);
+		gus->gf1.interrupt_handler_timer1(gus);
+	}
+	if (status & 0x08) {
+		STAT_ADD(gus->gf1.interrupt_stat_timer2);
+		gus->gf1.interrupt_handler_timer2(gus);
+	}
+	if (status & 0x80) {
+		if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) {
+			STAT_ADD(gus->gf1.interrupt_stat_dma_write);
+			gus->gf1.interrupt_handler_dma_write(gus);
+		}
+		if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) {
+			STAT_ADD(gus->gf1.interrupt_stat_dma_read);
+			gus->gf1.interrupt_handler_dma_read(gus);
+		}
+	}
+	if (--loop > 0)
+		goto __again;
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_gus_irq_info_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	snd_gus_card_t *gus;
+	snd_gus_voice_t *pvoice;
+	int idx;
+
+	gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return);
+	snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out);
+	snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in);
+	snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1);
+	snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2);
+	snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write);
+	snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read);
+	snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost);
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n",
+					idx,
+					pvoice->interrupt_stat_wave,
+					pvoice->interrupt_stat_volume);
+	}
+}
+
+void snd_gus_irq_profile_init(snd_gus_card_t *gus)
+{
+	snd_info_entry_t *entry;
+
+	gus->irq_entry = NULL;
+	entry = snd_info_create_card_entry(gus->card, "gusirq", gus->card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = snd_gus_irq_info_read;
+		entry->private_data = gus;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	gus->irq_entry = entry;	
+}
+
+void snd_gus_irq_profile_done(snd_gus_card_t *gus)
+{
+	if (gus->irq_entry) {
+		snd_info_unregister(gus->irq_entry);
+		gus->irq_entry = NULL;
+	}
+}
+#endif
diff -Nru linux/sound/isa/gus/gus_lfo.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_lfo.c
--- linux/sound/isa/gus/gus_lfo.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_lfo.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,430 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of LFO generators (tremolo & vibrato) for
+ *  GF1/InterWave chips...
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+/*
+ *  called by engine routines
+ */
+
+static signed char snd_gf1_lfo_compute_value(snd_gus_card_t * gus,
+					     unsigned char *ptr)
+{
+	unsigned int twaveinc, depth_delta;
+	signed int result;
+	unsigned short control, twave, depth, depth_final;
+	unsigned char *ptr1;
+
+	control = *(unsigned short *) (ptr + 0x00);
+	ptr1 = ptr + ((control & 0x4000) >> 12);
+	/* 1. add TWAVEINC to TWAVE and write the result back */
+	/* LFO update rate is 689Hz, effect timer is in ms */
+	if (gus->gf1.timer_slave)
+		twaveinc = (689 * gus->gf1.timer_master_gus->gf1.effect_timer) / 1000;
+	else
+		twaveinc = (689 * gus->gf1.effect_timer) / 1000;
+	if (!twaveinc)
+		twaveinc++;
+#if 0
+	printk("twaveinc = 0x%x, effect_timer = %i\n", twaveinc, gus->gf1.effect_timer);
+#endif
+
+	depth = *(unsigned short *) (ptr1 + 0x0a);
+	depth_final = *(unsigned char *) (ptr + 0x02) << 5;
+	if (depth != depth_final) {
+		depth_delta = ((twaveinc * *(ptr + 0x03)) + *(unsigned short *) (ptr + 0x04));
+		*(unsigned short *) (ptr + 0x04) = depth_delta % 8000;
+		depth_delta /= 8000;
+		if (depth < depth_final) {
+			if (depth + depth_delta > depth_final)
+				depth = depth_final;
+			else
+				depth += depth_delta;
+		}
+		if (depth > depth_final) {
+			if (depth - depth_delta < depth_final)
+				depth = depth_final;
+			else
+				depth -= depth_delta;
+		}
+		*(unsigned short *) (ptr1 + 0x0a) = depth;
+	}
+	twaveinc *= (unsigned int) control & 0x7ff;
+	twaveinc += *(unsigned short *) (ptr + 0x06);
+	*(unsigned short *) (ptr + 0x06) = twaveinc % 1000;
+
+	twave = *(unsigned short *) (ptr1 + 0x08);
+	twave += (unsigned short) (twaveinc / (unsigned int) 1000);
+	*(unsigned short *) (ptr1 + 0x08) = twave;
+
+	if (!(control & 0x2000)) {
+		/* 2. if shift is low */
+		if (twave & 0x4000) {	/* bit 14 high -> invert TWAVE 13-0 */
+			twave ^= 0x3fff;
+			twave &= ~0x4000;
+		}
+		/* TWAVE bit 15 is exclusive or'd with the invert bit (12) */
+		twave ^= (control & 0x1000) << 3;
+	} else {
+		/* 2. if shift is high */
+		if (twave & 0x8000)	/* bit 15 high -> invert TWAVE 14-0 */
+			twave ^= 0x7fff;
+		/* the invert bit (12) is used as sign bit */
+		if (control & 0x1000)
+			twave |= 0x8000;
+		else
+			twave &= ~0x8000;
+	}
+	/* 3. multiply the 14-bit LFO waveform magnitude by 13-bit DEPTH */
+#if 0
+	printk("c=0x%x,tw=0x%x,to=0x%x,d=0x%x,df=0x%x,di=0x%x,r=0x%x,r1=%i\n",
+	       control, twave,
+	       *(unsigned short *) (ptr1 + 0x08),
+	       depth, depth_final, *(ptr + 0x03),
+	     (twave & 0x7fff) * depth, ((twave & 0x7fff) * depth) >> 21);
+#endif
+	result = (twave & 0x7fff) * depth;
+	if (result) {
+		/* shift */
+		result >>= 21;
+		result &= 0x3f;
+	}
+	/* add sign */
+	if (twave & 0x8000)
+		result = -result;
+#if 0
+	printk("lfo final value = %i\n", result);
+#endif
+	return result;
+}
+
+static void snd_gf1_lfo_register_setup(snd_gus_card_t * gus,
+				       snd_gf1_voice_t * voice,
+				       int lfo_type)
+{
+	unsigned long flags;
+
+	if (gus->gf1.enh_mode) {
+		CLI(&flags);
+		gf1_select_voice(gus, voice->number);
+		if (lfo_type & 1) {
+			snd_gf1_write8(gus, GF1_VB_FREQUENCY_LFO, voice->lfo_fc);
+			voice->lfo_fc = 0;
+		}
+		if (lfo_type & 2) {
+			snd_gf1_write8(gus, GF1_VB_VOLUME_LFO, voice->lfo_volume);
+			voice->lfo_volume = 0;
+		}
+		STI(&flags);
+	} else {
+		/*
+		 * ok.. with old GF1 chip can be only vibrato emulated...
+		 * volume register can be in volume ramp state, so tremolo isn't simple..
+		 */
+		if (!(lfo_type & 1))
+			return;
+#if 0
+		if (voice->lfo_fc)
+			printk("setup - %i = %i\n", voice->number, voice->lfo_fc);
+#endif
+		CLI(&flags);
+		gf1_select_voice(gus, voice->number);
+		snd_gf1_write16(gus, GF1_VW_FREQUENCY, voice->fc_register + voice->lfo_fc);
+		STI(&flags);
+	}
+}
+
+void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice)
+{
+	unsigned char *ptr;
+
+#if 0
+	if (voice->number != 0)
+		return;
+#endif
+	ptr = gus->gf1.lfos + ((voice->number) << 5);
+	/* 1. vibrato */
+	if (*(unsigned short *) (ptr + 0x00) & 0x8000)
+		voice->lfo_fc = snd_gf1_lfo_compute_value(gus, ptr);
+	/* 2. tremolo */
+	ptr += 16;
+	if (*(unsigned short *) (ptr + 0x00) & 0x8000)
+		voice->lfo_volume = snd_gf1_lfo_compute_value(gus, ptr);
+	/* 3. register setup */
+	snd_gf1_lfo_register_setup(gus, voice, 3);
+}
+
+/*
+
+ */
+
+void snd_gf1_lfo_init(snd_gus_card_t * gus)
+{
+	if (gus->gf1.hw_lfo) {
+		snd_gf1_i_write16(gus, GF1_GW_LFO_BASE, 0x0000);
+		snd_gf1_dram_setmem(gus, 0, 0x0000, 1024);
+		/* now enable LFO */
+		snd_gf1_i_write8(gus, GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, GF1_GB_GLOBAL_MODE) | 0x02);
+	}
+	if (gus->gf1.sw_lfo) {
+#if 1
+		gus->gf1.lfos = snd_calloc(1024);
+		if (!gus->gf1.lfos)
+#endif
+			gus->gf1.sw_lfo = 0;
+	}
+}
+
+void snd_gf1_lfo_done(snd_gus_card_t * gus)
+{
+	if (gus->gf1.sw_lfo) {
+		if (gus->gf1.lfos) {
+			snd_gf1_free(gus->gf1.lfos, 1024);
+			gus->gf1.lfos = NULL;
+		}
+	}
+}
+
+void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type,
+                         struct ULTRA_STRU_IW_LFO_PROGRAM *program)
+{
+	unsigned int lfo_addr, wave_select;
+
+	wave_select = (program->freq_and_control & 0x4000) >> 12;
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo) {
+#if 0
+		printk("LMCI = 0x%x\n", snd_gf1_i_look8(gus, 0x53));
+		printk("lfo_program: lfo_addr=0x%x,wave_sel=0x%x,fac=0x%x,df=0x%x,di=0x%x,twave=0x%x,depth=0x%x\n",
+		       lfo_addr, wave_select,
+		       program->freq_and_control,
+		       program->depth_final,
+		       program->depth_inc,
+		       program->twave,
+		       program->depth);
+#endif
+		snd_gf1_poke(gus, lfo_addr + 0x02, program->depth_final);
+		snd_gf1_poke(gus, lfo_addr + 0x03, program->depth_inc);
+		snd_gf1_pokew(gus, lfo_addr + 0x08 + wave_select, program->twave);
+		snd_gf1_pokew(gus, lfo_addr + 0x0a + wave_select, program->depth);
+		snd_gf1_pokew(gus, lfo_addr + 0x00, program->freq_and_control);
+#if 0
+		{
+			int i = 0;
+			for (i = 0; i < 16; i++)
+				printk("%02x:", snd_gf1_peek(gus, lfo_addr + i));
+			printk("\n");
+		}
+#endif
+	}
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		*(ptr + 0x02) = program->depth_final;
+		*(ptr + 0x03) = program->depth_inc;
+		*(unsigned short *) (ptr + 0x08 + wave_select) = program->twave;
+		*(unsigned short *) (ptr + 0x0a + wave_select) = program->depth;
+		*(unsigned short *) (ptr + 0x00) = program->freq_and_control;
+	}
+}
+
+void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type)
+{
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo)
+		snd_gf1_pokew(gus, lfo_addr + 0x00, snd_gf1_peekw(gus, lfo_addr + 0x00) | 0x8000);
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		*(unsigned short *) (ptr + 0x00) |= 0x8000;
+	}
+}
+
+void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type)
+{
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo)
+		snd_gf1_pokew(gus, lfo_addr + 0x00,
+				snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x8000);
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		*(unsigned short *) (ptr + 0x00) &= ~0x8000;
+	}
+}
+
+void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice,
+		             int lfo_type, int freq)
+{
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo)
+		snd_gf1_pokew(gus, lfo_addr + 0x00,
+				(snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x7ff) | (freq & 0x7ff));
+	if (gus->gf1.sw_lfo) {
+		unsigned long flags;
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		CLI(&flags);
+		*(unsigned short *) (ptr + 0x00) &= ~0x7ff;
+		*(unsigned short *) (ptr + 0x00) |= freq & 0x7ff;
+		STI(&flags);
+	}
+}
+
+void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice,
+			      int lfo_type, int depth)
+{
+	unsigned long flags;
+	unsigned int lfo_addr;
+	unsigned short control = 0;
+	unsigned char *ptr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	ptr = gus->gf1.lfos + lfo_addr;
+	if (gus->gf1.hw_lfo)
+		control = snd_gf1_peekw(gus, lfo_addr + 0x00);
+	if (gus->gf1.sw_lfo)
+		control = *(unsigned short *) (ptr + 0x00);
+	if (depth < 0) {
+		control |= 0x1000;
+		depth = -depth;
+	} else
+		control &= ~0x1000;
+	if (gus->gf1.hw_lfo) {
+		CLI(&flags);
+		snd_gf1_poke(gus, lfo_addr + 0x02, (unsigned char) depth);
+		snd_gf1_pokew(gus, lfo_addr + 0x0a + ((control & 0x4000) >> 12), depth << 5);
+		snd_gf1_pokew(gus, lfo_addr + 0x00, control);
+		STI(&flags);
+	}
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		CLI(&flags);
+		*(ptr + 0x02) = (unsigned char) depth;
+		*(unsigned short *) (ptr + 0x0a + ((control & 0x4000) >> 12)) = depth << 5;
+		*(unsigned short *) (ptr + 0x00) = control;
+		STI(&flags);
+	}
+}
+
+void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type,
+		       int freq, int current_depth, int depth, int sweep,
+		       int shape)
+{
+	struct ULTRA_STRU_IW_LFO_PROGRAM program;
+
+	program.freq_and_control = 0x8000 | (freq & 0x7ff);
+	if (shape & ULTRA_STRU_IW_LFO_SHAPE_POSTRIANGLE)
+		program.freq_and_control |= 0x2000;
+	if (depth < 0) {
+		program.freq_and_control |= 0x1000;
+		depth = -depth;
+	}
+	program.twave = 0;
+	program.depth = current_depth;
+	program.depth_final = depth;
+	if (sweep) {
+		program.depth_inc = (unsigned char) (((int) ((depth << 5) - current_depth) << 9) / (sweep * 4410L));
+		if (!program.depth_inc)
+			program.depth_inc++;
+	} else
+		program.depth = (unsigned short) (depth << 5);
+	snd_gf1_lfo_program(gus, voice, lfo_type, &program);
+}
+
+void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type)
+{
+	unsigned long flags;
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo) {
+		snd_gf1_pokew(gus, lfo_addr + 0x00, 0x0000);
+		CLI(&flags);
+		gf1_select_voice(gus, voice);
+		snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0);
+		STI(&flags);
+	}
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+		snd_gf1_voice_t *pvoice;
+
+		*(unsigned short *) (ptr + 0x00) = 0;
+		*(unsigned short *) (ptr + 0x04) = 0;
+		*(unsigned short *) (ptr + 0x06) = 0;
+		if (gus->gf1.syn_voices) {
+			pvoice = gus->gf1.syn_voices + voice;
+			if (lfo_type == ULTRA_LFO_VIBRATO)
+				pvoice->lfo_fc = 0;
+			else
+				pvoice->lfo_volume = 0;
+			snd_gf1_lfo_register_setup(gus, pvoice, lfo_type == ULTRA_LFO_VIBRATO ? 1 : 2);
+		} else if (gus->gf1.enh_mode) {
+			CLI(&flags);
+			gf1_select_voice(gus, voice);
+			snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0);
+			STI(&flags);
+		}
+	}
+}
+
+void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *data)
+{
+	int lfo_type;
+	int lfo_command;
+	int temp1, temp2;
+
+	lfo_type = *data >> 7;
+	lfo_command = *data & 0x7f;
+	switch (lfo_command) {
+	case ULTRA_LFO_SETUP:	/* setup */
+		temp1 = snd_gf1_get_word(data, 2);
+		temp2 = snd_gf1_get_word(data, 4);
+		snd_gf1_lfo_setup(gus, voice, lfo_type, temp1 & 0x7ff, 0, temp2 > 255 ? 255 : temp2, snd_gf1_get_byte(data, 1), (temp1 & 0x2000) >> 13);
+		break;
+	case ULTRA_LFO_FREQ:	/* freq */
+		snd_gf1_lfo_change_depth(gus, voice, lfo_type, snd_gf1_get_word(data, 2));
+		break;
+	case ULTRA_LFO_DEPTH:	/* depth */
+		snd_gf1_lfo_change_freq(gus, voice, lfo_type, snd_gf1_get_word(data, 2));
+		break;
+	case ULTRA_LFO_ENABLE:	/* enable */
+		snd_gf1_lfo_enable(gus, voice, lfo_type);
+		break;
+	case ULTRA_LFO_DISABLE:	/* disable */
+		snd_gf1_lfo_disable(gus, voice, lfo_type);
+		break;
+	case ULTRA_LFO_SHUTDOWN:	/* shutdown */
+		snd_gf1_lfo_shutdown(gus, voice, lfo_type);
+		break;
+	}
+}
diff -Nru linux/sound/isa/gus/gus_main.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_main.c
--- linux/sound/isa/gus/gus_main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_main.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,519 @@
+/*
+ *  Routines for Gravis UltraSound soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/control.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards");
+MODULE_LICENSE("GPL");
+
+#define chip_t snd_gus_card_t
+
+static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches);
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+int snd_gus_use_inc(snd_gus_card_t * gus)
+{
+	MOD_INC_USE_COUNT;
+	if (!try_inc_mod_count(gus->card->module)) {
+		MOD_DEC_USE_COUNT;
+		return 0;
+	}
+	return 1;
+}
+
+void snd_gus_use_dec(snd_gus_card_t * gus)
+{
+	dec_mod_count(gus->card->module);
+	MOD_DEC_USE_COUNT;
+}
+
+static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+
+static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = gus->joystick_dac & 31;
+	return 0;
+}
+
+static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval;
+	
+	nval = ucontrol->value.integer.value[0] & 31;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	change = gus->joystick_dac != nval;
+	gus->joystick_dac = nval;
+	snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_gus_joystick_control = {
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	name: "Joystick Speed",
+	info: snd_gus_joystick_info,
+	get: snd_gus_joystick_get,
+	put: snd_gus_joystick_put
+};
+
+static void snd_gus_init_control(snd_gus_card_t *gus)
+{
+	if (!gus->ace_flag)
+		snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus));
+}
+
+/*
+ *
+ */
+
+static int snd_gus_free(snd_gus_card_t *gus)
+{
+	if (gus->gf1.res_port2 == NULL)
+		goto __hw_end;
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (gus->seq_dev) {
+		snd_device_free(gus->card, gus->seq_dev);
+		gus->seq_dev = NULL;
+	}
+#endif
+	snd_gf1_stop(gus);
+	snd_gus_init_dma_irq(gus, 0);
+      __hw_end:
+	if (gus->gf1.res_port1) {
+		release_resource(gus->gf1.res_port1);
+		kfree_nocheck(gus->gf1.res_port1);
+	}
+	if (gus->gf1.res_port2) {
+		release_resource(gus->gf1.res_port2);
+		kfree_nocheck(gus->gf1.res_port2);
+	}
+	if (gus->gf1.irq >= 0)
+		free_irq(gus->gf1.irq, (void *) gus);
+	if (gus->gf1.dma1 >= 0) {
+		disable_dma(gus->gf1.dma1);
+		free_dma(gus->gf1.dma1);
+	}
+	if (!gus->equal_dma && gus->gf1.dma2 >= 0) {
+		disable_dma(gus->gf1.dma2);
+		free_dma(gus->gf1.dma2);
+	}
+	snd_magic_kfree(gus);
+	return 0;
+}
+
+static int snd_gus_dev_free(snd_device_t *device)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, device->device_data, return -ENXIO);
+	return snd_gus_free(gus);
+}
+
+int snd_gus_create(snd_card_t * card,
+		   unsigned long port,
+		   int irq, int dma1, int dma2,
+		   int timer_dev,
+		   int voices,
+		   int pcm_channels,
+		   int effect,
+		   snd_gus_card_t **rgus)
+{
+	snd_gus_card_t *gus;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_gus_dev_free,
+	};
+
+	*rgus = NULL;
+	gus = snd_magic_kcalloc(snd_gus_card_t, 0, GFP_KERNEL);
+	if (gus == NULL)
+		return -ENOMEM;
+	gus->gf1.irq = -1;
+	gus->gf1.dma1 = -1;
+	gus->gf1.dma2 = -1;
+	gus->card = card;
+	gus->gf1.port = port;
+	/* fill register variables for speedup */
+	gus->gf1.reg_page = GUSP(gus, GF1PAGE);
+	gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL);
+	gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH);
+	gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW);
+	gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT);
+	gus->gf1.reg_dram = GUSP(gus, DRAM);
+	gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL);
+	gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA);
+	/* allocate resources */
+	if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	gus->gf1.irq = irq;
+	if (request_dma(dma1, "GUS - 1")) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	gus->gf1.dma1 = dma1;
+	if (dma2 >= 0 && dma1 != dma2) {
+		if (request_dma(dma2, "GUS - 2")) {
+			snd_gus_free(gus);
+			return -EBUSY;
+		}
+		gus->gf1.dma2 = dma2;
+	} else {
+		gus->gf1.dma2 = gus->gf1.dma1;
+		gus->equal_dma = 1;
+	}
+	gus->timer_dev = timer_dev;
+	if (voices < 14)
+		voices = 14;
+	if (voices > 32)
+		voices = 32;
+	if (pcm_channels < 0)
+		pcm_channels = 0;
+	if (pcm_channels > 8)
+		pcm_channels = 8;
+	pcm_channels++;
+	pcm_channels &= ~1;
+	gus->gf1.effect = effect ? 1 : 0;
+	gus->gf1.active_voices = voices;
+	gus->gf1.pcm_channels = pcm_channels;
+	gus->gf1.volume_ramp = 25;
+	gus->gf1.smooth_pan = 1;
+	spin_lock_init(&gus->reg_lock);
+	spin_lock_init(&gus->voice_alloc);
+	spin_lock_init(&gus->active_voice_lock);
+	spin_lock_init(&gus->event_lock);
+	spin_lock_init(&gus->dma_lock);
+	spin_lock_init(&gus->pcm_volume_level_lock);
+	spin_lock_init(&gus->uart_cmd_lock);
+	init_MUTEX(&gus->dma_mutex);
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) {
+		snd_gus_free(gus);
+		return err;
+	}
+	*rgus = gus;
+	return 0;
+}
+
+/*
+ *  Memory detection routine for plain GF1 soundcards
+ */
+
+static int snd_gus_detect_memory(snd_gus_card_t * gus)
+{
+	int l, idx, local;
+	unsigned char d;
+
+	snd_gf1_poke(gus, 0L, 0xaa);
+	snd_gf1_poke(gus, 1L, 0x55);
+	if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) {
+		snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port);
+		return -ENOMEM;
+	}
+	for (idx = 1, d = 0xab; idx < 4; idx++, d++) {
+		local = idx << 18;
+		snd_gf1_poke(gus, local, d);
+		snd_gf1_poke(gus, local + 1, d + 1);
+		if (snd_gf1_peek(gus, local) != d ||
+		    snd_gf1_peek(gus, local + 1) != d + 1 ||
+		    snd_gf1_peek(gus, 0L) != 0xaa)
+			break;
+	}
+#if 1
+	gus->gf1.memory = idx << 18;
+#else
+	gus->gf1.memory = 256 * 1024;
+#endif
+	for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) {
+		gus->gf1.mem_alloc.banks_8[l].address =
+		    gus->gf1.mem_alloc.banks_8[l].size = 0;
+		gus->gf1.mem_alloc.banks_16[l].address = l << 18;
+		gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0;
+	}
+	gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory;
+	return 0;		/* some memory were detected */
+}
+
+static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches)
+{
+	snd_card_t *card;
+	unsigned long flags;
+	int irq, dma1, dma2;
+	static unsigned char irqs[16] =
+		{0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
+	static unsigned char dmas[8] =
+		{6, 1, 0, 2, 0, 3, 4, 5};
+
+	snd_assert(gus != NULL, return -EINVAL);
+	card = gus->card;
+	snd_assert(card != NULL, return -EINVAL);
+
+	gus->mix_cntrl_reg &= 0xf8;
+	gus->mix_cntrl_reg |= 0x01;	/* disable MIC, LINE IN, enable LINE OUT */
+	if (gus->codec_flag || gus->ess_flag) {
+		gus->mix_cntrl_reg &= ~1;	/* enable LINE IN */
+		gus->mix_cntrl_reg |= 4;	/* enable MIC */
+	}
+	dma1 = gus->gf1.dma1;
+	dma1 = dma1 < 0 ? -dma1 : dma1;
+	dma1 = dmas[dma1 & 7];
+	dma2 = gus->gf1.dma2;
+	dma2 = dma2 < 0 ? -dma2 : dma2;
+	dma2 = dmas[dma2 & 7];
+#if 0
+	printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2);
+#endif
+	dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3);
+
+	if ((dma1 & 7) == 0 || (dma2 & 7) == 0) {
+		snd_printk("Error! DMA isn't defined.\n");
+		return -EINVAL;
+	}
+	irq = gus->gf1.irq;
+	irq = irq < 0 ? -irq : irq;
+	irq = irqs[irq & 0x0f];
+	if (irq == 0) {
+		snd_printk("Error! IRQ isn't defined.\n");
+		return -EINVAL;
+	}
+	irq |= 0x40;
+#if 0
+	card->mixer.mix_ctrl_reg |= 0x10;
+#endif
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(5, GUSP(gus, REGCNTRLS));
+	outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(0x00, GUSP(gus, IRQDMACNTRLREG));
+	outb(0, GUSP(gus, REGCNTRLS));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	udelay(100);
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+	if (latches) {
+		outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+		outb(irq, GUSP(gus, IRQDMACNTRLREG));
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	udelay(100);
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+	if (latches) {
+		outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+		outb(irq, GUSP(gus, IRQDMACNTRLREG));
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	snd_gf1_delay(gus);
+
+	if (latches)
+		gus->mix_cntrl_reg |= 0x08;	/* enable latches */
+	else
+		gus->mix_cntrl_reg &= ~0x08;	/* disable latches */
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(0, GUSP(gus, GF1PAGE));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	return 0;
+}
+
+static int snd_gus_check_version(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+	unsigned char val, rev;
+	snd_card_t *card;
+
+	card = gus->card;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(0x20, GUSP(gus, REGCNTRLS));
+	val = inb(GUSP(gus, REGCNTRLS));
+	rev = inb(GUSP(gus, BOARDVERSION));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
+	strcpy(card->driver, "GUS");
+	strcpy(card->longname, "Gravis UltraSound Classic (2.4)");
+	if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) {
+		if (rev >= 5 && rev <= 9) {
+			gus->ics_flag = 1;
+			if (rev == 5)
+				gus->ics_flipped = 1;
+			card->longname[27] = '3';
+			card->longname[29] = rev == 5 ? '5' : '7';
+		}
+		if (rev >= 10 && rev != 255) {
+			if (rev >= 10 && rev <= 11) {
+				strcpy(card->driver, "GUS MAX");
+				strcpy(card->longname, "Gravis UltraSound MAX");
+				gus->max_flag = 1;
+			} else if (rev == 0x30) {
+				strcpy(card->driver, "GUS ACE");
+				strcpy(card->longname, "Gravis UltraSound Ace");
+				gus->ace_flag = 1;
+			} else if (rev == 0x50) {
+				strcpy(card->driver, "GUS Extreme");
+				strcpy(card->longname, "Gravis UltraSound Extreme");
+				gus->ess_flag = 1;
+			} else {
+				snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val);
+				snd_printk("  please - report to <perex@suse.cz>\n");
+			}
+		}
+	}
+	strcpy(card->shortname, card->longname);
+	gus->uart_enable = 1;	/* standard GUSes doesn't have midi uart trouble */
+	snd_gus_init_control(gus);
+	return 0;
+}
+
+static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, seq_dev->private_data, return);
+	gus->seq_dev = NULL;
+}
+
+int snd_gus_initialize(snd_gus_card_t *gus)
+{
+	int err;
+
+	if (!gus->interwave) {
+		if ((err = snd_gus_check_version(gus)) < 0) {
+			snd_printk("version check failed\n");
+			return err;
+		}
+		if ((err = snd_gus_detect_memory(gus)) < 0)
+			return err;
+	}
+	if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
+		return err;
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS,
+			       sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) {
+		strcpy(gus->seq_dev->name, "GUS");
+		*(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus;
+		gus->seq_dev->private_data = gus;
+		gus->seq_dev->private_free = snd_gus_seq_dev_free;
+	}
+#endif
+	snd_gf1_start(gus);
+	gus->initialized = 1;
+	return 0;
+}
+
+  /* gus_io.c */
+EXPORT_SYMBOL(snd_gf1_delay);
+EXPORT_SYMBOL(snd_gf1_write8);
+EXPORT_SYMBOL(snd_gf1_look8);
+EXPORT_SYMBOL(snd_gf1_write16);
+EXPORT_SYMBOL(snd_gf1_look16);
+EXPORT_SYMBOL(snd_gf1_i_write8);
+EXPORT_SYMBOL(snd_gf1_i_look8);
+EXPORT_SYMBOL(snd_gf1_i_write16);
+EXPORT_SYMBOL(snd_gf1_i_look16);
+EXPORT_SYMBOL(snd_gf1_dram_addr);
+EXPORT_SYMBOL(snd_gf1_write_addr);
+EXPORT_SYMBOL(snd_gf1_poke);
+EXPORT_SYMBOL(snd_gf1_peek);
+  /* gus_reset.c */
+EXPORT_SYMBOL(snd_gf1_alloc_voice);
+EXPORT_SYMBOL(snd_gf1_free_voice);
+EXPORT_SYMBOL(snd_gf1_ctrl_stop);
+EXPORT_SYMBOL(snd_gf1_stop_voice);
+EXPORT_SYMBOL(snd_gf1_start);
+EXPORT_SYMBOL(snd_gf1_stop);
+  /* gus_mixer.c */
+EXPORT_SYMBOL(snd_gf1_new_mixer);
+  /* gus_pcm.c */
+EXPORT_SYMBOL(snd_gf1_pcm_new);
+  /* gus.c */
+EXPORT_SYMBOL(snd_gus_use_inc);
+EXPORT_SYMBOL(snd_gus_use_dec);
+EXPORT_SYMBOL(snd_gus_create);
+EXPORT_SYMBOL(snd_gus_initialize);
+  /* gus_irq.c */
+EXPORT_SYMBOL(snd_gus_interrupt);
+  /* gus_uart.c */
+EXPORT_SYMBOL(snd_gf1_rawmidi_new);
+  /* gus_dram.c */
+EXPORT_SYMBOL(snd_gus_dram_write);
+EXPORT_SYMBOL(snd_gus_dram_read);
+  /* gus_volume.c */
+EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw);
+EXPORT_SYMBOL(snd_gf1_translate_freq);
+  /* gus_mem.c */
+EXPORT_SYMBOL(snd_gf1_mem_alloc);
+EXPORT_SYMBOL(snd_gf1_mem_xfree);
+EXPORT_SYMBOL(snd_gf1_mem_free);
+EXPORT_SYMBOL(snd_gf1_mem_lock);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_gus_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_gus_exit(void)
+{
+}
+
+module_init(alsa_gus_init)
+module_exit(alsa_gus_exit)
diff -Nru linux/sound/isa/gus/gus_mem.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem.c
--- linux/sound/isa/gus/gus_mem.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,368 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  GUS's memory allocation routines / bottom layer
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_gf1_mem_info_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer);
+#endif
+
+void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup)
+{
+	if (!xup) {
+		down(&alloc->memory_mutex);
+	} else {
+		up(&alloc->memory_mutex);
+	}
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc,
+				        snd_gf1_mem_block_t * block)
+{
+	snd_gf1_mem_block_t *pblock, *nblock;
+
+	nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL);
+	if (nblock == NULL)
+		return NULL;
+	*nblock = *block;
+	pblock = alloc->first;
+	while (pblock) {
+		if (pblock->ptr > nblock->ptr) {
+			nblock->prev = pblock->prev;
+			nblock->next = pblock;
+			pblock->prev = nblock;
+			if (pblock == alloc->first)
+				alloc->first = nblock;
+			else
+				nblock->prev->next = nblock;
+			up(&alloc->memory_mutex);
+			return 0;
+		}
+		pblock = pblock->next;
+	}
+	nblock->next = NULL;
+	if (alloc->last == NULL) {
+		nblock->prev = NULL;
+		alloc->first = alloc->last = nblock;
+	} else {
+		nblock->prev = alloc->last;
+		alloc->last->next = nblock;
+		alloc->last = nblock;
+	}
+	return nblock;
+}
+
+int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block)
+{
+	if (block->share) {	/* ok.. shared block */
+		block->share--;
+		up(&alloc->memory_mutex);
+		return 0;
+	}
+	if (alloc->first == block) {
+		alloc->first = block->next;
+		if (block->next)
+			block->next->prev = NULL;
+	} else {
+		block->prev->next = block->next;
+		if (block->next)
+			block->next->prev = block->prev;
+	}
+	if (alloc->last == block) {
+		alloc->last = block->prev;
+		if (block->prev)
+			block->prev->next = NULL;
+	} else {
+		block->next->prev = block->prev;
+		if (block->prev)
+			block->prev->next = block->next;
+	}
+	if (block->name)
+		kfree(block->name);
+	kfree(block);
+	return 0;
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+				      unsigned int address)
+{
+	snd_gf1_mem_block_t *block;
+
+	for (block = alloc->first; block; block = block->next) {
+		if (block->ptr == address) {
+			return block;
+		}
+	}
+	return NULL;
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+				       unsigned int *share_id)
+{
+	snd_gf1_mem_block_t *block;
+
+	if (!share_id[0] && !share_id[1] &&
+	    !share_id[2] && !share_id[3])
+		return NULL;
+	for (block = alloc->first; block; block = block->next)
+		if (!memcmp(share_id, block->share_id, sizeof(share_id)))
+			return block;
+	return NULL;
+}
+
+static int snd_gf1_mem_find(snd_gf1_mem_t * alloc,
+			    snd_gf1_mem_block_t * block,
+			    unsigned int size, int w_16, int align)
+{
+	snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8;
+	unsigned int idx, boundary;
+	int size1;
+	snd_gf1_mem_block_t *pblock;
+	unsigned int ptr1, ptr2;
+
+	align--;
+	if (w_16 && align < 1)
+		align = 1;
+	block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0;
+	block->owner = SNDRV_GF1_MEM_OWNER_DRIVER;
+	block->share = 0;
+	block->share_id[0] = block->share_id[1] =
+	block->share_id[2] = block->share_id[3] = 0;
+	block->name = NULL;
+	block->prev = block->next = NULL;
+	for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) {
+		while (pblock->ptr >= (boundary = info[idx].address + info[idx].size))
+			idx++;
+		while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size))
+			idx++;
+		ptr2 = boundary;
+		if (pblock->next) {
+			if (pblock->ptr + pblock->size == pblock->next->ptr)
+				continue;
+			if (pblock->next->ptr < boundary)
+				ptr2 = pblock->next->ptr;
+		}
+		ptr1 = (pblock->ptr + pblock->size + align) & ~align;
+		if (ptr1 >= ptr2)
+			continue;
+		size1 = ptr2 - ptr1;
+		if (size <= size1) {
+			block->ptr = ptr1;
+			block->size = size;
+			return 0;
+		}
+	}
+	while (++idx < 4) {
+		if (size <= info[idx].size) {
+			/* I assume that bank address is already aligned.. */
+			block->ptr = info[idx].address;
+			block->size = size;
+			return 0;
+		}
+	}
+	return -ENOMEM;
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
+				       char *name, int size, int w_16, int align,
+				       unsigned int *share_id)
+{
+	snd_gf1_mem_block_t block, *nblock;
+
+	snd_gf1_mem_lock(alloc, 0);
+	if (share_id != NULL) {
+		nblock = snd_gf1_mem_share(alloc, share_id);
+		if (nblock != NULL) {
+			if (size != nblock->size) {
+				/* TODO: remove in the future */
+				snd_printk("snd_gf1_mem_alloc - share: sizes differ\n");
+				goto __std;
+			}
+			nblock->share++;
+			snd_gf1_mem_lock(alloc, 1);
+			return NULL;
+		}
+	}
+      __std:
+	if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) {
+		snd_gf1_mem_lock(alloc, 1);
+		return NULL;
+	}
+	if (share_id != NULL)
+		memcpy(&block.share_id, share_id, sizeof(block.share_id));
+	block.owner = owner;
+	block.name = snd_kmalloc_strdup(name, GFP_KERNEL);
+	nblock = snd_gf1_mem_xalloc(alloc, &block);
+	snd_gf1_mem_lock(alloc, 1);
+	return nblock;
+}
+
+int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address)
+{
+	int result;
+	snd_gf1_mem_block_t *block;
+
+	snd_gf1_mem_lock(alloc, 0);
+	if ((block = snd_gf1_mem_look(alloc, address)) != NULL) {
+		result = snd_gf1_mem_xfree(alloc, block);
+		snd_gf1_mem_lock(alloc, 1);
+		return result;
+	}
+	snd_gf1_mem_lock(alloc, 1);
+	return -EINVAL;
+}
+
+int snd_gf1_mem_init(snd_gus_card_t * gus)
+{
+	snd_gf1_mem_t *alloc;
+	snd_gf1_mem_block_t block;
+#ifdef CONFIG_SND_DEBUG
+	snd_info_entry_t *entry;
+#endif
+
+	alloc = &gus->gf1.mem_alloc;
+	init_MUTEX(&alloc->memory_mutex);
+	alloc->first = alloc->last = NULL;
+	if (!gus->gf1.memory)
+		return 0;
+
+	memset(&block, 0, sizeof(block));
+	block.owner = SNDRV_GF1_MEM_OWNER_DRIVER;
+	if (gus->gf1.enh_mode) {
+		block.ptr = 0;
+		block.size = 1024;
+		block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL);
+		if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+			return -ENOMEM;
+	}
+	block.ptr = gus->gf1.default_voice_address;
+	block.size = 4;
+	block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL);
+	if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+		return -ENOMEM;
+#ifdef CONFIG_SND_DEBUG
+	alloc->info_entry = NULL;
+	entry = snd_info_create_card_entry(gus->card, "gusmem", gus->card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 256 * 1024;
+		entry->c.text.read = snd_gf1_mem_info_read;
+		entry->private_data = gus;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	alloc->info_entry = entry;
+#endif
+	return 0;
+}
+
+int snd_gf1_mem_done(snd_gus_card_t * gus)
+{
+	snd_gf1_mem_t *alloc;
+	snd_gf1_mem_block_t *block, *nblock;
+
+	alloc = &gus->gf1.mem_alloc;
+	block = alloc->first;
+	while (block) {
+		nblock = block->next;
+		snd_gf1_mem_xfree(alloc, block);
+		block = nblock;
+	}
+#ifdef CONFIG_SND_DEBUG
+	if (alloc->info_entry)
+		snd_info_unregister(alloc->info_entry);
+#endif
+	return 0;
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_gf1_mem_info_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	snd_gus_card_t *gus;
+	snd_gf1_mem_t *alloc;
+	snd_gf1_mem_block_t *block;
+	unsigned int total, used;
+	int i;
+
+	gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return);
+	alloc = &gus->gf1.mem_alloc;
+	down(&alloc->memory_mutex);
+	snd_iprintf(buffer, "8-bit banks       : \n    ");
+	for (i = 0; i < 4; i++)
+		snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : "");
+	snd_iprintf(buffer, "\n"
+		    "16-bit banks      : \n    ");
+	for (i = total = 0; i < 4; i++) {
+		snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : "");
+		total += alloc->banks_16[i].size;
+	}
+	snd_iprintf(buffer, "\n");
+	used = 0;
+	for (block = alloc->first, i = 0; block; block = block->next, i++) {
+		used += block->size;
+		snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size);
+		if (block->share ||
+		    block->share_id[0] || block->share_id[1] ||
+		    block->share_id[2] || block->share_id[3])
+			snd_iprintf(buffer, "  Share           : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n",
+				block->share,
+				block->share_id[0], block->share_id[1],
+				block->share_id[2], block->share_id[3]);
+		snd_iprintf(buffer, "  Flags           :%s\n",
+		block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : "");
+		snd_iprintf(buffer, "  Owner           : ");
+		switch (block->owner) {
+		case SNDRV_GF1_MEM_OWNER_DRIVER:
+			snd_iprintf(buffer, "driver - %s\n", block->name);
+			break;
+		case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE:
+			snd_iprintf(buffer, "SIMPLE wave\n");
+			break;
+		case SNDRV_GF1_MEM_OWNER_WAVE_GF1:
+			snd_iprintf(buffer, "GF1 wave\n");
+			break;
+		case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF:
+			snd_iprintf(buffer, "IWFFFF wave\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+		}
+	}
+	snd_iprintf(buffer, "  Total: memory = %i, used = %i, free = %i\n",
+		    total, used, total - used);
+	up(&alloc->memory_mutex);
+#if 0
+	ultra_iprintf(buffer, "  Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n",
+		      ultra_memory_free_size(card, &card->gf1.mem_alloc),
+		  ultra_memory_free_block(card, &card->gf1.mem_alloc, 0),
+		 ultra_memory_free_block(card, &card->gf1.mem_alloc, 1));
+#endif
+}
+#endif
diff -Nru linux/sound/isa/gus/gus_mem_proc.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem_proc.c
--- linux/sound/isa/gus/gus_mem_proc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem_proc.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  GUS's memory access via proc filesystem
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+typedef struct gus_proc_private {
+	int rom;		/* data are in ROM */
+	unsigned int address;
+	unsigned int size;
+	snd_gus_card_t * gus;
+} gus_proc_private_t;
+
+static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data,
+			          struct file *file, char *buf, long count)
+{
+	long size;
+	gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO);
+	snd_gus_card_t *gus = priv->gus;
+	int err;
+
+	size = count;
+	if (file->f_pos + size > priv->size)
+		size = (long)priv->size - file->f_pos;
+	if (size > 0) {
+		if ((err = snd_gus_dram_read(gus, buf, file->f_pos, size, priv->rom)) < 0)
+			return err;
+		file->f_pos += size;
+		return size;
+	}
+	return 0;
+}			
+
+static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry,
+					void *private_file_data,
+					struct file *file,
+					long long offset,
+					int orig)
+{
+	gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO);
+
+	switch (orig) {
+	case 0:	/* SEEK_SET */
+		file->f_pos = offset;
+		break;
+	case 1:	/* SEEK_CUR */
+		file->f_pos += offset;
+		break;
+	case 2: /* SEEK_END */
+		file->f_pos = priv->size - offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (file->f_pos > priv->size)
+		file->f_pos = priv->size;
+	return file->f_pos;
+}
+
+static void snd_gf1_mem_proc_free(snd_info_entry_t *entry)
+{
+	gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return);
+	snd_magic_kfree(priv);
+}
+
+static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
+	read: snd_gf1_mem_proc_dump,
+	llseek: snd_gf1_mem_proc_llseek,
+};
+
+int snd_gf1_mem_proc_init(snd_gus_card_t * gus)
+{
+	int idx;
+	char name[16];
+	gus_proc_private_t *priv;
+	snd_info_entry_t *entry;
+
+	memset(&gus->gf1.rom_entries, 0, sizeof(gus->gf1.rom_entries));
+	memset(&gus->gf1.ram_entries, 0, sizeof(gus->gf1.ram_entries));
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.mem_alloc.banks_8[idx].size > 0) {
+			priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL);
+			if (priv == NULL) {
+				snd_gf1_mem_proc_done(gus);
+				return -ENOMEM;
+			}
+			priv->gus = gus;
+			sprintf(name, "gus-ram-%i", idx);
+			entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root);
+			if (entry) {
+				entry->content = SNDRV_INFO_CONTENT_DATA;
+				entry->private_data = priv;
+				entry->private_free = snd_gf1_mem_proc_free;
+				entry->c.ops = &snd_gf1_mem_proc_ops;
+				priv->address = gus->gf1.mem_alloc.banks_8[idx].address;
+				priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size;
+				if (snd_info_register(entry) < 0) {
+					snd_info_free_entry(entry);
+					entry = NULL;
+				}
+			}
+			gus->gf1.ram_entries[idx] = entry;
+		}
+	}
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.rom_present & (1 << idx)) {
+			priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL);
+			if (priv == NULL) {
+				snd_gf1_mem_proc_done(gus);
+				return -ENOMEM;
+			}
+			priv->rom = 1;
+			priv->gus = gus;
+			sprintf(name, "gus-rom-%i", idx);
+			entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root);
+			if (entry) {
+				entry->content = SNDRV_INFO_CONTENT_DATA;
+				entry->private_data = priv;
+				entry->private_free = snd_gf1_mem_proc_free;
+				entry->c.ops = &snd_gf1_mem_proc_ops;
+				priv->address = idx * 4096 * 1024;
+				priv->size = entry->size = gus->gf1.rom_memory;
+				if (snd_info_register(entry) < 0) {
+					snd_info_free_entry(entry);
+					entry = NULL;
+				}
+			}
+			gus->gf1.rom_entries[idx] = entry;
+		}
+	}
+	return 0;
+}
+
+int snd_gf1_mem_proc_done(snd_gus_card_t * gus)
+{
+	int idx;
+
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.ram_entries[idx])
+			snd_info_unregister(gus->gf1.ram_entries[idx]);
+	}
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.rom_entries[idx])
+			snd_info_unregister(gus->gf1.rom_entries[idx]);
+	}
+	return 0;
+}
diff -Nru linux/sound/isa/gus/gus_mixer.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mixer.c
--- linux/sound/isa/gus/gus_mixer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mixer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,205 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of ICS 2101 chip and "mixer" in GF1 chip
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/gus.h>
+
+#define chip_t snd_gus_card_t
+
+/*
+ *
+ */
+
+#define GF1_SINGLE(xname, xindex, shift, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_gf1_info_single, \
+  get: snd_gf1_get_single, put: snd_gf1_put_single, \
+  private_value: shift | (invert << 8) }
+
+static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	
+	ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1;
+	if (invert)
+		ucontrol->value.integer.value[0] ^= 1;
+	return 0;
+}
+
+static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	int change;
+	unsigned char oval, nval;
+	
+	nval = ucontrol->value.integer.value[0] & 1;
+	if (invert)
+		nval ^= 1;
+	nval <<= shift;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	oval = gus->mix_cntrl_reg;
+	nval = (oval & ~(1 << shift)) | nval;
+	change = nval != oval;
+	outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG));
+	outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return change;
+}
+
+#define ICS_DOUBLE(xname, xindex, addr) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ics_info_double, \
+  get: snd_ics_get_double, put: snd_ics_put_double, \
+  private_value: addr }
+
+static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int addr = kcontrol->private_value & 0xff;
+	unsigned char left, right;
+	
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	left = gus->gf1.ics_regs[addr][0];
+	right = gus->gf1.ics_regs[addr][1];
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	ucontrol->value.integer.value[0] = left & 127;
+	ucontrol->value.integer.value[1] = right & 127;
+	return 0;
+}
+
+static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int addr = kcontrol->private_value & 0xff;
+	int change;
+	unsigned char val1, val2, oval1, oval2, tmp;
+	
+	val1 = ucontrol->value.integer.value[0] & 127;
+	val2 = ucontrol->value.integer.value[1] & 127;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	oval1 = gus->gf1.ics_regs[addr][0];
+	oval2 = gus->gf1.ics_regs[addr][1];
+	change = val1 != oval1 || val2 != oval2;
+	gus->gf1.ics_regs[addr][0] = val1;
+	gus->gf1.ics_regs[addr][1] = val2;
+	if (gus->ics_flag && gus->ics_flipped &&
+	    (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) {
+		tmp = val1;
+		val1 = val2;
+		val2 = tmp;
+	}
+	addr <<= 3;
+	outb(addr | 0, GUSP(gus, MIXCNTRLPORT));
+	outb(1, GUSP(gus, MIXDATAPORT));
+	outb(addr | 2, GUSP(gus, MIXCNTRLPORT));
+	outb((unsigned char) val1, GUSP(gus, MIXDATAPORT));
+	outb(addr | 1, GUSP(gus, MIXCNTRLPORT));
+	outb(2, GUSP(gus, MIXDATAPORT));
+	outb(addr | 3, GUSP(gus, MIXCNTRLPORT));
+	outb((unsigned char) val2, GUSP(gus, MIXDATAPORT));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return change;
+}
+
+#define GF1_CONTROLS (sizeof(snd_gf1_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_gf1_controls[] = {
+GF1_SINGLE("Master Playback Switch", 0, 1, 1),
+GF1_SINGLE("Line Switch", 0, 0, 1),
+GF1_SINGLE("Mic Switch", 0, 2, 0)
+};
+
+#define ICS_CONTROLS (sizeof(snd_ics_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ics_controls[] = {
+GF1_SINGLE("Master Playback Switch", 0, 1, 1),
+ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV),
+ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV),
+GF1_SINGLE("Line Switch", 0, 0, 1),
+ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV),
+GF1_SINGLE("Mic Switch", 0, 2, 0),
+ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV),
+ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV)
+};
+
+int snd_gf1_new_mixer(snd_gus_card_t * gus)
+{
+	snd_card_t *card;
+	int idx, err, max;
+
+	snd_assert(gus != NULL, return -EINVAL);
+	card = gus->card;
+	snd_assert(card != NULL, return -EINVAL);
+
+	if (gus->ics_flag)
+		snd_component_add(card, "ICS2101");
+	if (card->mixername[0] == '\0') {
+		strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
+	} else {
+		if (gus->ics_flag)
+			strcat(card->mixername, ",ICS2101");
+		strcat(card->mixername, ",GF1");
+	}
+
+	if (!gus->ics_flag) {
+		max = gus->ess_flag ? 1 : GF1_CONTROLS;
+		for (idx = 0; idx < max; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0)
+				return err;
+		}
+	} else {
+		for (idx = 0; idx < ICS_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
diff -Nru linux/sound/isa/gus/gus_pcm.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_pcm.c
--- linux/sound/isa/gus/gus_pcm.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_pcm.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,890 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of GF1 chip (PCM things)
+ *
+ *  InterWave chips supports interleaved DMA, but this feature isn't used in
+ *  this code.
+ *  
+ *  This code emulates autoinit DMA transfer for playback, recording by GF1
+ *  chip doesn't support autoinit DMA.
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/gus.h>
+#include "gus_tables.h"
+
+#define chip_t snd_gus_card_t
+
+/* maximum rate */
+
+#define SNDRV_GF1_PCM_RATE		48000
+
+#define SNDRV_GF1_PCM_PFLG_NONE		0
+#define SNDRV_GF1_PCM_PFLG_ACTIVE	(1<<0)
+#define SNDRV_GF1_PCM_PFLG_NEUTRAL	(2<<0)
+
+typedef struct {
+	snd_gus_card_t * gus;
+	snd_pcm_substream_t * substream;
+	spinlock_t lock;
+	int voices;
+	snd_gus_voice_t *pvoices[2];
+	unsigned int memory;
+	unsigned short flags;
+	unsigned char voice_ctrl, ramp_ctrl;
+	unsigned int bpos;
+	unsigned int blocks;
+	unsigned int block_size;
+	unsigned int dma_size;
+	wait_queue_head_t sleep;
+	atomic_t dma_count;
+	int final_volume;
+} gus_pcm_private_t;
+
+static int snd_gf1_pcm_use_dma = 1;
+
+static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data)
+{
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, private_data, return);
+
+	if (pcmp) {
+		atomic_dec(&pcmp->dma_count);
+		wake_up(&pcmp->sleep);
+	}
+}
+
+static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream,
+				    unsigned int offset,
+				    unsigned int addr,
+				    unsigned int count)
+{
+	snd_gf1_dma_block_t block;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+
+	count += offset & 31;
+	offset &= ~31;
+	// snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count);
+	memset(&block, 0, sizeof(block));
+	block.cmd = SNDRV_GF1_DMA_IRQ;
+	if (snd_pcm_format_unsigned(runtime->format))
+		block.cmd |= SNDRV_GF1_DMA_UNSIGNED;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		block.cmd |= SNDRV_GF1_DMA_16BIT;
+	block.addr = addr & ~31;
+	block.buffer = runtime->dma_area + offset;
+	block.buf_addr = runtime->dma_addr + offset;
+	block.count = count;
+	block.private_data = pcmp;
+	block.ack = snd_gf1_pcm_block_change_ack;
+	if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0))
+		atomic_inc(&pcmp->dma_count);
+	return 0;
+}
+
+static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return);
+	snd_gus_card_t * gus = pcmp->gus;
+	unsigned long flags;
+	unsigned char voice_ctrl, ramp_ctrl;
+	unsigned short rate;
+	unsigned int curr, begin, end;
+	unsigned short vol;
+	unsigned char pan;
+	unsigned int voice;
+
+	if (substream == NULL)
+		return;
+	spin_lock_irqsave(&pcmp->lock, flags);
+	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
+		spin_unlock_irqrestore(&pcmp->lock, flags);
+		return;
+	}
+	pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
+	pcmp->final_volume = 0;
+	spin_unlock_irqrestore(&pcmp->lock, flags);
+	rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
+	/* enable WAVE IRQ */
+	voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
+	/* enable RAMP IRQ + rollover */
+	ramp_ctrl = 0x24;
+	if (pcmp->blocks == 1) {
+		voice_ctrl |= 0x08;	/* loop enable */
+		ramp_ctrl &= ~0x04;	/* disable rollover */
+	}
+	for (voice = 0; voice < pcmp->voices; voice++) {
+		begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels);
+		curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
+		end = curr + (pcmp->block_size / runtime->channels);
+		end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
+		// snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate);
+		pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
+		vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+		if (!gus->gf1.enh_mode) {
+			snd_gf1_delay(gus);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+		}
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+	}
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	for (voice = 0; voice < pcmp->voices; voice++) {
+		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
+		if (gus->gf1.enh_mode)
+			snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00);	/* deactivate voice */
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+		voice_ctrl &= ~0x20;
+	}
+	voice_ctrl |= 0x20;
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_delay(gus);
+		for (voice = 0; voice < pcmp->voices; voice++) {
+			snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+			voice_ctrl &= ~0x20;	/* disable IRQ for next voice */
+		}
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice)
+{
+	gus_pcm_private_t * pcmp;
+	snd_pcm_runtime_t * runtime;
+	unsigned char voice_ctrl, ramp_ctrl;
+	int idx;
+	unsigned int end, step;
+
+	if (!pvoice->private_data) {
+		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+		snd_gf1_smart_stop_voice(gus, pvoice->number);
+		return;
+	}
+	pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return);
+	if (pcmp == NULL) {
+		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+		snd_gf1_smart_stop_voice(gus, pvoice->number);
+		return;
+	}		
+	gus = pcmp->gus;
+	runtime = pcmp->substream->runtime;
+
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, pvoice->number);
+	voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
+	ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
+#if 0
+	snd_gf1_select_voice(gus, pvoice->number);
+	printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+	snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
+	printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+	snd_gf1_select_voice(gus, pvoice->number);
+#endif
+	pcmp->bpos++;
+	pcmp->bpos %= pcmp->blocks;
+	if (pcmp->bpos + 1 >= pcmp->blocks) {	/* last block? */
+		voice_ctrl |= 0x08;	/* enable loop */
+	} else {
+		ramp_ctrl |= 0x04;	/* enable rollover */
+	}
+	end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
+	end -= voice_ctrl & 4 ? 2 : 1;
+	step = pcmp->dma_size / runtime->channels;
+	voice_ctrl |= 0x20;
+	if (!pcmp->final_volume) {
+		ramp_ctrl |= 0x20;
+		ramp_ctrl &= ~0x03;
+	}
+	for (idx = 0; idx < pcmp->voices; idx++, end += step) {
+		snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+		voice_ctrl &= ~0x20;
+	}
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_delay(gus);
+		voice_ctrl |= 0x20;
+		for (idx = 0; idx < pcmp->voices; idx++) {
+			snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+			voice_ctrl &= ~0x20;
+		}
+	}
+	spin_unlock(&gus->reg_lock);
+
+	snd_pcm_period_elapsed(pcmp->substream);
+#if 0
+	if ((runtime->flags & SNDRV_PCM_FLG_MMAP) &&
+	    *runtime->state == SNDRV_PCM_STATE_RUNNING) {
+		end = pcmp->bpos * pcmp->block_size;
+		if (runtime->channels > 1) {
+			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2);
+			snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2);
+		} else {
+			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size);
+		}
+	}
+#endif
+}
+
+static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice)
+{
+	unsigned short vol;
+	int cvoice;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return);
+
+	/* stop ramp, but leave rollover bit untouched */
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, pvoice->number);
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+	spin_unlock(&gus->reg_lock);
+	if (pcmp == NULL)
+		return;
+	/* are we active? */
+	if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
+		return;
+	/* load real volume - better precision */
+	cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1;
+	if (pcmp->substream == NULL)
+		return;
+	vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, pvoice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
+	pcmp->final_volume = 1;
+	spin_unlock(&gus->reg_lock);
+}
+
+static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus)
+{
+}
+
+static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf,
+				  unsigned int pos, unsigned int count,
+				  int w16, int invert)
+{
+	unsigned int len;
+	unsigned long flags;
+
+	// printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port);
+	while (count > 0) {
+		len = count;
+		if (len > 512)		/* limit, to allow IRQ */
+			len = 512;
+		count -= len;
+		if (gus->interwave) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
+			snd_gf1_dram_addr(gus, pos);
+			if (w16) {
+				outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL));
+				outsw(GUSP(gus, GF1DATALOW), buf, len >> 1);
+			} else {
+				outsb(GUSP(gus, DRAM), buf, len);
+			}
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			buf += 512;
+			pos += 512;
+		} else {
+			invert = invert ? 0x80 : 0x00;
+			if (w16) {
+				len >>= 1;
+				while (len--) {
+					snd_gf1_poke(gus, pos++, *buf++);
+					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
+				}
+			} else {
+				while (len--)
+					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
+			}
+		}
+		schedule_timeout(1);
+		if (signal_pending(current))
+			return -EAGAIN;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream,
+				     int voice,
+				     snd_pcm_uframes_t pos,
+				     void *src,
+				     snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned int bpos, len;
+	
+	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
+	len = samples_to_bytes(runtime, count);
+	snd_assert(bpos <= pcmp->dma_size, return -EIO);
+	snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+	if (copy_from_user(runtime->dma_area + bpos, src, len))
+		return -EFAULT;
+	if (snd_gf1_pcm_use_dma && len > 32) {
+		return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
+	} else {
+		snd_gus_card_t *gus = pcmp->gus;
+		int err, w16, invert;
+
+		w16 = (snd_pcm_format_width(runtime->format) == 16);
+		invert = snd_pcm_format_unsigned(runtime->format);
+		if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream,
+					int voice,
+					snd_pcm_uframes_t pos,
+					snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned int bpos, len;
+	
+	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
+	len = samples_to_bytes(runtime, count);
+	snd_assert(bpos <= pcmp->dma_size, return -EIO);
+	snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+	snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count);
+	if (snd_gf1_pcm_use_dma && len > 32) {
+		return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
+	} else {
+		snd_gus_card_t *gus = pcmp->gus;
+		int err, w16, invert;
+
+		w16 = (snd_pcm_format_width(runtime->format) == 16);
+		invert = snd_pcm_format_unsigned(runtime->format);
+		if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream,
+					  snd_pcm_hw_params_t * hw_params)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	int err;
+	
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0) {	/* change */
+		snd_gf1_mem_block_t *block;
+		if (pcmp->memory > 0) {
+			snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
+			pcmp->memory = 0;
+		}
+		if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+		                               SNDRV_GF1_MEM_OWNER_DRIVER,
+					       "GF1 PCM",
+		                               runtime->dma_bytes, 1, 32,
+		                               NULL)) == NULL)
+			return -ENOMEM;
+		pcmp->memory = block->ptr;
+	}
+	pcmp->voices = params_channels(hw_params);
+	if (pcmp->pvoices[0] == NULL) {
+		if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+			return -ENOMEM;
+		pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
+		pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
+		pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change;
+		pcmp->pvoices[0]->private_data = pcmp;
+	}
+	if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
+		if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+			return -ENOMEM;
+		pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
+		pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
+		pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change;
+		pcmp->pvoices[1]->private_data = pcmp;
+	} else if (pcmp->voices == 1) {
+		if (pcmp->pvoices[1]) {
+			snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
+			pcmp->pvoices[1] = NULL;
+		}
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+
+	snd_pcm_lib_free_pages(substream);
+	if (pcmp->pvoices[0]) {
+		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
+		pcmp->pvoices[0] = NULL;
+	}
+	if (pcmp->pvoices[1]) {
+		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
+		pcmp->pvoices[1] = NULL;
+	}
+	if (pcmp->memory > 0) {
+		snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory);
+		pcmp->memory = 0;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+
+	pcmp->bpos = 0;
+	pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream);
+	pcmp->block_size = snd_pcm_lib_period_bytes(substream);
+	pcmp->blocks = pcmp->dma_size / pcmp->block_size;
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	int voice;
+
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		snd_gf1_pcm_trigger_up(substream);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		spin_lock(&pcmp->lock);
+		pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
+		spin_unlock(&pcmp->lock);
+		voice = pcmp->pvoices[0]->number;
+		snd_gf1_stop_voices(gus, voice, voice);
+		if (pcmp->pvoices[1]) {
+			voice = pcmp->pvoices[1]->number;
+			snd_gf1_stop_voices(gus, voice, voice);
+		}
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned int pos;
+	unsigned char voice_ctrl;
+
+	pos = 0;
+	spin_lock(&gus->reg_lock);
+	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
+		snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
+		voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+		pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory;
+		if (substream->runtime->channels > 1)
+			pos <<= 1;
+		pos = bytes_to_frames(runtime, pos);
+	}
+	spin_unlock(&gus->reg_lock);
+	return pos;
+}
+
+static ratnum_t clock = {
+	num: 9878400/16,
+	den_min: 2,
+	den_max: 257,
+	den_step: 1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
+	nrats: 1,
+	rats: &clock,
+};
+
+static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+
+	gus->c_dma_size = params_buffer_bytes(hw_params);
+	gus->c_period_size = params_period_bytes(hw_params);
+	gus->c_pos = 0;
+	gus->gf1.pcm_rcntrl_reg = 0x21;		/* IRQ at end, enable & start */
+	if (params_channels(hw_params) > 1)
+		gus->gf1.pcm_rcntrl_reg |= 2;
+	if (gus->gf1.dma2 > 3)
+		gus->gf1.pcm_rcntrl_reg |= 4;
+	if (snd_pcm_format_unsigned(params_format(hw_params)))
+		gus->gf1.pcm_rcntrl_reg |= 0x80;
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
+	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
+	snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ);
+	return 0;
+}
+
+static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	int val;
+	
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		val = gus->gf1.pcm_rcntrl_reg;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		val = 0;
+	} else {
+		return -EINVAL;
+	}
+
+	spin_lock(&gus->reg_lock);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
+	snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
+	spin_unlock(&gus->reg_lock);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	int pos = gus->c_period_size - snd_dma_residue(gus->gf1.dma2);
+	pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size);
+	return pos;
+}
+
+static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
+	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
+	if (gus->pcm_cap_substream != NULL) {
+		snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); 
+		snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START);
+		gus->c_pos += gus->c_period_size;
+		snd_pcm_period_elapsed(gus->pcm_cap_substream);
+	}
+}
+
+static snd_pcm_hardware_t snd_gf1_pcm_playback =
+{
+	info:			SNDRV_PCM_INFO_NONINTERLEAVED,
+	formats:		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_gf1_pcm_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	rate_min:		5510,
+	rate_max:		44100,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime)
+{
+	gus_pcm_private_t * pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return);
+	snd_magic_kfree(pcmp);
+}
+
+static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream)
+{
+	gus_pcm_private_t *pcmp;
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	pcmp = snd_magic_kcalloc(gus_pcm_private_t, 0, GFP_KERNEL);
+	if (pcmp == NULL)
+		return -ENOMEM;
+	pcmp->gus = gus;
+	spin_lock_init(&pcmp->lock);
+	init_waitqueue_head(&pcmp->sleep);
+	atomic_set(&pcmp->dma_count, 0);
+
+	runtime->private_data = pcmp;
+	runtime->private_free = snd_gf1_pcm_playback_free;
+
+#if 0
+	printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
+#endif
+	if ((err = snd_gf1_dma_init(gus)) < 0)
+		return err;
+	pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
+	pcmp->substream = substream;
+	runtime->hw = snd_gf1_pcm_playback;
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned long jiffies_old;
+
+	jiffies_old = jiffies;
+	while (atomic_read(&pcmp->dma_count) > 0) {
+		interruptible_sleep_on_timeout(&pcmp->sleep, 1);
+		if ((signed long)(jiffies - jiffies_old) > 2*HZ) {
+			snd_printk("gf1 pcm - serious DMA problem\n");
+			break;
+		}
+	}
+	snd_gf1_dma_done(gus);	
+	return 0;
+}
+
+static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+
+	gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read;
+	gus->pcm_cap_substream = substream;
+	substream->runtime->hw = snd_gf1_pcm_capture;
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	return 0;
+}
+
+static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+
+	gus->pcm_cap_substream = NULL;
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ);
+	return 0;
+}
+
+static void snd_gf1_pcm_free(snd_pcm_t *pcm)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, pcm->private_data, return);
+	gus->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
+	ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
+	ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
+	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change, idx;
+	unsigned short val1, val2, vol;
+	gus_pcm_private_t *pcmp;
+	snd_gus_voice_t *pvoice;
+	
+	val1 = ucontrol->value.integer.value[0] & 127;
+	val2 = ucontrol->value.integer.value[1] & 127;
+	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
+	change = val1 != gus->gf1.pcm_volume_level_left1 ||
+	         val2 != gus->gf1.pcm_volume_level_right1;
+	gus->gf1.pcm_volume_level_left1 = val1;
+	gus->gf1.pcm_volume_level_right1 = val2;
+	gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
+	gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
+	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
+	/* are we active? */
+	spin_lock_irqsave(&gus->voice_alloc, flags);
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (!pvoice->pcm)
+			continue;
+		pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return -ENXIO);
+		if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
+			continue;
+		/* load real volume - better precision */
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, pvoice->number);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+		vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
+		pcmp->final_volume = 1;
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&gus->voice_alloc, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_gf1_pcm_volume_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "PCM Playback Volume",
+	info: snd_gf1_pcm_volume_info,
+	get: snd_gf1_pcm_volume_get,
+	put: snd_gf1_pcm_volume_put
+};
+
+static snd_pcm_ops_t snd_gf1_pcm_playback_ops = {
+	open:		snd_gf1_pcm_playback_open,
+	close:		snd_gf1_pcm_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_gf1_pcm_playback_hw_params,
+	hw_free:	snd_gf1_pcm_playback_hw_free,
+	prepare:	snd_gf1_pcm_playback_prepare,
+	trigger:	snd_gf1_pcm_playback_trigger,
+	pointer:	snd_gf1_pcm_playback_pointer,
+	copy:		snd_gf1_pcm_playback_copy,
+	silence:	snd_gf1_pcm_playback_silence,
+};
+
+static snd_pcm_ops_t snd_gf1_pcm_capture_ops = {
+	open:		snd_gf1_pcm_capture_open,
+	close:		snd_gf1_pcm_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_gf1_pcm_capture_hw_params,
+	hw_free:	snd_gf1_pcm_capture_hw_free,
+	prepare:	snd_gf1_pcm_capture_prepare,
+	trigger:	snd_gf1_pcm_capture_trigger,
+	pointer:	snd_gf1_pcm_capture_pointer,
+};
+
+int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm)
+{
+	snd_card_t *card;
+	snd_kcontrol_t *kctl;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *substream;
+	int capture, err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	card = gus->card;
+	capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0;
+	err = snd_pcm_new(card,
+			  gus->interwave ? "AMD InterWave" : "GF1",
+			  pcm_dev,
+			  gus->gf1.pcm_channels / 2,
+			  capture,
+			  &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = gus;
+	pcm->private_free = snd_gf1_pcm_free;
+	/* playback setup */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+		snd_pcm_lib_preallocate_isa_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
+	
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	if (capture) {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
+		if (gus->gf1.dma2 == gus->gf1.dma1)
+			pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+		snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
+	}
+	strcpy(pcm->name, pcm->id);
+	if (gus->interwave) {
+		sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
+	}
+	strcat(pcm->name, " (synth)");
+	gus->pcm = pcm;
+
+	if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus))) < 0)
+		return err;
+	kctl->id.index = control_index;
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
diff -Nru linux/sound/isa/gus/gus_reset.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_reset.c
--- linux/sound/isa/gus/gus_reset.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_reset.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,423 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+extern void snd_gf1_timers_init(snd_gus_card_t * gus);
+extern void snd_gf1_timers_done(snd_gus_card_t * gus);
+extern int snd_gf1_synth_init(snd_gus_card_t * gus);
+extern void snd_gf1_synth_done(snd_gus_card_t * gus);
+
+/*
+ *  ok.. default interrupt handlers...
+ */
+
+static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus)
+{
+	snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20);
+}
+
+static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus)
+{
+	snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80);
+}
+
+static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4);
+}
+
+static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8);
+}
+
+static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice)
+{
+	snd_gf1_i_ctrl_stop(gus, 0x00);
+	snd_gf1_i_ctrl_stop(gus, 0x0d);
+}
+
+static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, 0x41, 0x00);
+}
+
+static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, 0x49, 0x00);
+}
+
+void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what)
+{
+	if (what & SNDRV_GF1_HANDLER_MIDI_OUT)
+		gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out;
+	if (what & SNDRV_GF1_HANDLER_MIDI_IN)
+		gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in;
+	if (what & SNDRV_GF1_HANDLER_TIMER1)
+		gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1;
+	if (what & SNDRV_GF1_HANDLER_TIMER2)
+		gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2;
+	if (what & SNDRV_GF1_HANDLER_VOICE) {
+		snd_gus_voice_t *voice;
+		
+		voice = &gus->gf1.voices[what & 0xffff];
+		voice->handler_wave =
+		voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume;
+		voice->handler_effect = NULL;
+		voice->volume_change = NULL;
+	}
+	if (what & SNDRV_GF1_HANDLER_DMA_WRITE)
+		gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write;
+	if (what & SNDRV_GF1_HANDLER_DMA_READ)
+		gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read;
+}
+
+/*
+
+ */
+
+static void snd_gf1_clear_regs(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	inb(GUSP(gus, IRQSTAT));
+	snd_gf1_write8(gus, 0x41, 0);	/* DRAM DMA Control Register */
+	snd_gf1_write8(gus, 0x45, 0);	/* Timer Control */
+	snd_gf1_write8(gus, 0x49, 0);	/* Sampling Control Register */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void snd_gf1_look_regs(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_look8(gus, 0x41);	/* DRAM DMA Control Register */
+	snd_gf1_look8(gus, 0x49);	/* Sampling Control Register */
+	inb(GUSP(gus, IRQSTAT));
+	snd_gf1_read8(gus, 0x0f);	/* IRQ Source Register */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+/*
+ *  put selected GF1 voices to initial stage...
+ */
+
+void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice);
+#if 0
+	printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+#endif
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice);
+#if 0
+	printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+#endif
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+	if (gus->gf1.enh_mode)
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+#if 0
+	snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO);
+	snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO);
+#endif
+}
+
+void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max)
+{
+	unsigned long flags;
+	unsigned int daddr;
+	unsigned short i, w_16;
+
+	daddr = gus->gf1.default_voice_address << 4;
+	for (i = v_min; i <= v_max; i++) {
+#if 0
+		if (gus->gf1.syn_voices)
+			gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC;
+#endif
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, i);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);	/* Voice Control Register = voice stop */
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);	/* Volume Ramp Control Register = ramp off */
+		if (gus->gf1.enh_mode)
+			snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82);	/* Deactivate voice */
+		w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04;
+		snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7);
+		if (gus->gf1.enh_mode) {
+			snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0);
+		}
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+#if 0
+		snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO);
+		snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO);
+#endif
+	}
+}
+
+void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max)
+{
+	unsigned long flags;
+	short i, ramp_ok;
+	unsigned short ramp_end;
+	long time;
+
+	if (!in_interrupt()) {	/* this can't be done in interrupt */
+		for (i = v_min, ramp_ok = 0; i <= v_max; i++) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, i);
+			ramp_end = snd_gf1_read16(gus, 9) >> 8;
+			if (ramp_end > SNDRV_GF1_MIN_OFFSET) {
+				ramp_ok++;
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20);	/* ramp rate */
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);	/* ramp start */
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end);	/* ramp end */
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);	/* ramp down */
+				if (gus->gf1.enh_mode) {
+					snd_gf1_delay(gus);
+					snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);
+				}
+			}
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+		}
+		time = HZ / 20;
+		while (time > 0 && !signal_pending(current)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			time = schedule_timeout(time);
+		}
+	}
+	snd_gf1_clear_voices(gus, v_min, v_max);
+}
+
+static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, 
+				    snd_gus_voice_t * pvoice,
+				    int type, int client, int port)
+{
+	pvoice->use = 1;
+	switch (type) {
+	case SNDRV_GF1_VOICE_TYPE_PCM:
+		gus->gf1.pcm_alloc_voices++;
+		pvoice->pcm = 1;
+		break;
+	case SNDRV_GF1_VOICE_TYPE_SYNTH:
+		pvoice->synth = 1;
+		pvoice->client = client;
+		pvoice->port = port;
+		break;
+	case SNDRV_GF1_VOICE_TYPE_MIDI:
+		pvoice->midi = 1;
+		pvoice->client = client;
+		pvoice->port = port;
+		break;
+	}
+}
+
+snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port)
+{
+	snd_gus_voice_t *pvoice;
+	unsigned long flags;
+	int idx;
+
+	spin_lock_irqsave(&gus->voice_alloc, flags);
+	if (type == SNDRV_GF1_VOICE_TYPE_PCM) {
+		if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) {
+			spin_unlock_irqrestore(&gus->voice_alloc, flags);
+			return NULL;
+		}
+	}
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (!pvoice->use) {
+			snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
+			spin_unlock_irqrestore(&gus->voice_alloc, flags);
+			return pvoice;
+		}
+	} 
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (pvoice->midi && !pvoice->client) {
+			snd_gf1_clear_voices(gus, pvoice->number, pvoice->number);
+			snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
+			spin_unlock_irqrestore(&gus->voice_alloc, flags);
+			return pvoice;
+		}
+	} 
+	spin_unlock_irqrestore(&gus->voice_alloc, flags);
+	return NULL;
+}
+
+void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice)
+{
+	unsigned long flags;
+	void (*private_free)(snd_gus_voice_t *voice);
+	void *private_data;
+
+	if (voice == NULL || !voice->use)
+		return;
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number);
+	snd_gf1_clear_voices(gus, voice->number, voice->number);
+	spin_lock_irqsave(&gus->voice_alloc, flags);
+	private_free = voice->private_free;
+	private_data = voice->private_data;
+	voice->private_free = NULL;
+	voice->private_data = NULL;
+	if (voice->pcm)
+		gus->gf1.pcm_alloc_voices--;
+	voice->use = voice->pcm = 0;
+	voice->sample_ops = NULL;
+	spin_unlock_irqrestore(&gus->voice_alloc, flags);
+	if (private_free)
+		private_free(voice);
+}
+
+/*
+ *  call this function only by start of driver
+ */
+
+int snd_gf1_start(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+	unsigned int i;
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* disable IRQ & DAC */
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
+
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL);
+	for (i = 0; i < 32; i++) {
+		gus->gf1.voices[i].number = i;
+		snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i);
+	}
+
+	snd_gf1_uart_cmd(gus, 0x03);	/* huh.. this cleanup took me some time... */
+
+	if (gus->gf1.enh_mode) {	/* enhanced mode !!!! */
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+	}
+	snd_gf1_clear_regs(gus);
+	snd_gf1_select_active_voices(gus);
+	snd_gf1_delay(gus);
+	gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8;
+	/* initialize LFOs & clear LFOs memory */
+	if (gus->gf1.enh_mode && gus->gf1.memory) {
+		gus->gf1.hw_lfo = 1;
+		gus->gf1.default_voice_address += 1024;
+	} else {
+		gus->gf1.sw_lfo = 1;
+	}
+#if 0
+	snd_gf1_lfo_init(gus);
+#endif
+	if (gus->gf1.memory > 0)
+		for (i = 0; i < 4; i++)
+			snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0);
+	snd_gf1_clear_regs(gus);
+	snd_gf1_clear_voices(gus, 0, 31);
+	snd_gf1_look_regs(gus);
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7);	/* Reset Register = IRQ enable, DAC enable */
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7);	/* Reset Register = IRQ enable, DAC enable */
+	if (gus->gf1.enh_mode) {	/* enhanced mode !!!! */
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+	}
+	while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0);
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
+	outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	snd_gf1_timers_init(gus);
+	snd_gf1_look_regs(gus);
+	snd_gf1_mem_init(gus);
+	snd_gf1_mem_proc_init(gus);
+#ifdef CONFIG_SND_DEBUG
+	snd_gus_irq_profile_init(gus);
+#endif
+
+#if 0
+	if (gus->pnp_flag) {
+		if (gus->chip.playback_fifo_size > 0)
+			snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8);
+		if (gus->chip.record_fifo_size > 0)
+			snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8);
+		snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg);
+	}
+#endif
+
+	return 0;
+}
+
+/*
+ *  call this function only by shutdown of driver
+ */
+
+int snd_gf1_stop(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */
+	snd_gf1_stop_voices(gus, 0, 31);		/* stop all voices */
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* disable IRQ & DAC */
+	snd_gf1_timers_done(gus);
+#ifdef CONFIG_SND_DEBUG
+	snd_gus_irq_profile_done(gus);
+#endif
+	snd_gf1_mem_proc_done(gus);
+	snd_gf1_mem_done(gus);
+#if 0
+	snd_gf1_lfo_done(gus);
+#endif
+	return 0;
+}
diff -Nru linux/sound/isa/gus/gus_sample.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_sample.c
--- linux/sound/isa/gus/gus_sample.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_sample.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,156 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Sample support
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+/*
+ *
+ */
+
+static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v)
+{
+	snd_seq_kinstr_t *instr;
+
+#if 0
+	printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n",
+					v->instr.cluster,
+					v->instr.std,
+					v->instr.bank,
+					v->instr.prg);
+#endif
+	instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1);
+	if (instr != NULL) {
+		if (instr->ops) {
+			if (instr->ops->instr_type == snd_seq_simple_id)
+				snd_gf1_simple_init(v);
+		}
+		snd_seq_instr_free_use(gus->gf1.ilist, instr);
+	}
+}
+
+/*
+ *
+ */
+
+static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
+	v->instr.std = ev->data.sample.param.sample.std;
+	if (v->instr.std & 0xff000000) {        /* private instrument */
+		v->instr.std &= 0x00ffffff;
+		v->instr.std |= (unsigned int)ev->source.client << 24;
+	}                                                
+	v->instr.bank = ev->data.sample.param.sample.bank;
+	v->instr.prg = ev->data.sample.param.sample.prg;
+	select_instrument(p->gus, v);
+}
+
+static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
+	v->instr.cluster = ev->data.sample.param.cluster.cluster;
+	select_instrument(p->gus, v);
+}
+
+static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_start)
+		v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position);
+}
+
+static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode);
+}
+
+static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_freq)
+		v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency);
+}
+
+static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_volume)
+		v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume);
+}
+
+static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_loop)
+		v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop);
+}
+
+static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_pos)
+		v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position);
+}
+
+static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_private1)
+		v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8);
+}
+
+typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v);
+
+static gus_sample_event_handler_t *gus_sample_event_handlers[9] = {
+	event_sample,
+	event_cluster,
+	event_start,
+	event_stop,
+	event_freq,
+	event_volume,
+	event_loop,
+	event_position,
+	event_private1
+};
+
+void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p)
+{
+	int idx, voice;
+	snd_gus_card_t *gus = p->gus;
+	snd_gus_voice_t *v;
+	unsigned long flags;
+	
+	idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+	if (idx < 0 || idx > 8)
+		return;
+	for (voice = 0; voice < 32; voice++) {
+		v = &gus->gf1.voices[voice];
+		if (v->use && v->client == ev->source.client &&
+		    v->port == ev->source.port &&
+		    v->index == ev->data.sample.channel) {
+		    	spin_lock_irqsave(&gus->event_lock, flags);
+			gus_sample_event_handlers[idx](ev, p, v);
+			spin_unlock_irqrestore(&gus->event_lock, flags);
+			return;
+		}
+	}
+}
diff -Nru linux/sound/isa/gus/gus_simple.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_simple.c
--- linux/sound/isa/gus/gus_simple.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_simple.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,635 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Simple instrument handlers
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include "gus_tables.h"
+
+/*
+ *
+ */
+
+static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+
+static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position);
+static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode);
+static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq);
+static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume);
+static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop);
+static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position);
+static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data);
+
+static snd_gus_sample_ops_t sample_ops = {
+	sample_start,
+	sample_stop,
+	sample_freq,
+	sample_volume,
+	sample_loop,
+	sample_pos,
+	sample_private1
+};
+
+#if 0
+
+static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait);
+static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice);
+
+static struct snd_gus_note_handlers note_commands = {
+	note_stop,
+	note_wait,
+	note_off,
+	note_volume,
+	note_pitchbend,
+	note_vibrato,
+	note_tremolo
+};
+
+static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority );
+static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note );
+static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 );
+
+static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = {
+  chn_trigger_down,
+  chn_trigger_up,
+  chn_control
+};
+
+#endif
+
+static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice);
+
+/*
+ *
+ */
+
+static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	spin_lock(&gus->event_lock);
+	snd_gf1_stop_voice(gus, voice->number);
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
+	spin_unlock(&gus->reg_lock);
+	voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+	spin_unlock(&gus->event_lock);
+}
+
+static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	spin_lock(&gus->event_lock);
+	if (voice->flags & SNDRV_GF1_VFLG_RUNNING)
+		do_volume_envelope(gus, voice);
+	else
+		snd_gf1_stop_voice(gus, voice->number);
+	spin_unlock(&gus->event_lock);
+}
+
+static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	spin_lock(&gus->event_lock);
+	if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) ==
+	                    (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1))
+		do_pan_envelope(gus, voice);
+	spin_unlock(&gus->event_lock);
+}
+
+/*
+ *
+ */
+
+static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	unsigned short next, rate, old_volume;
+	int program_next_ramp;
+	unsigned long flags;
+  
+	if (!gus->gf1.volume_ramp) {
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, voice->number);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume);
+		printk("gf1_volume = 0x%x\n", voice->gf1_volume);
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+		return;
+	}
+	program_next_ramp = 0;
+	rate = next = 0;
+	while (1) {
+		program_next_ramp = 0;
+		rate = next = 0;
+		switch (voice->venv_state) {
+		case VENV_BEFORE:
+			voice->venv_state = VENV_ATTACK;
+			voice->venv_value_next = 0;
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, voice->number);
+			snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			break;
+		case VENV_ATTACK:
+			voice->venv_state = VENV_SUSTAIN;
+			program_next_ramp++;
+			next = 255;
+			rate = gus->gf1.volume_ramp;
+			break;
+		case VENV_SUSTAIN:
+			voice->venv_state = VENV_RELEASE;
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, voice->number);
+			snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+ 			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			return;
+		case VENV_RELEASE:
+			voice->venv_state = VENV_DONE;
+			program_next_ramp++;
+			next = 0;
+			rate = gus->gf1.volume_ramp;
+			break;
+		case VENV_DONE:
+			snd_gf1_stop_voice(gus, voice->number);
+			voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+			return;
+		case VENV_VOLUME:
+			program_next_ramp++;
+			next = voice->venv_value_next;
+			rate = gus->gf1.volume_ramp;
+			voice->venv_state = voice->venv_state_prev;
+			break;
+		}
+		voice->venv_value_next = next;
+		if (!program_next_ramp)
+			continue;
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, voice->number);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+		old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8;
+		if (!rate) {
+			spin_unlock_irqrestore(&gus->reg_lock, flags);			
+			continue;
+		}
+		next = (((int)voice->gf1_volume * (int)next) / 255) >> 8;
+		if (old_volume < SNDRV_GF1_MIN_OFFSET)
+			old_volume = SNDRV_GF1_MIN_OFFSET;
+		if (next < SNDRV_GF1_MIN_OFFSET)
+			next = SNDRV_GF1_MIN_OFFSET;
+		if (next > SNDRV_GF1_MAX_OFFSET)
+			next = SNDRV_GF1_MAX_OFFSET;
+		if (old_volume == next) {
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			continue;
+		}
+		voice->volume_control &= ~0xc3;
+		voice->volume_control |= 0x20;
+		if (old_volume > next) {
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume);
+			voice->volume_control |= 0x40;
+		} else {
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next);
+		}
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
+		if (!gus->gf1.enh_mode) {
+			snd_gf1_delay(gus);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
+		}
+		spin_unlock_irqrestore(&gus->reg_lock, flags);			
+		return;
+	}
+}
+
+static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	unsigned long flags;
+	unsigned char old_pan;
+
+#if 0
+	snd_gf1_select_voice(gus, voice->number);
+	printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
+		voice->number,
+		voice->flags,
+		voice->gf1_pan,
+		snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f);
+#endif
+	if (gus->gf1.enh_mode) {
+		voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
+		return;
+	}
+	if (!gus->gf1.smooth_pan) {
+		spin_lock_irqsave(&gus->reg_lock, flags);			
+		snd_gf1_select_voice(gus, voice->number);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+		return;
+	}
+	if (!(voice->flags & SNDRV_GF1_VFLG_PAN))		/* before */
+		voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN;
+	spin_lock_irqsave(&gus->reg_lock, flags);			
+	snd_gf1_select_voice(gus, voice->number);
+	old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f;
+	if (old_pan > voice->gf1_pan )
+		old_pan--;
+	if (old_pan < voice->gf1_pan)
+		old_pan++;
+	snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	if (old_pan == voice->gf1_pan)			/* the goal was reached */
+		voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
+#if 0
+	snd_gf1_select_voice(gus, voice->number);
+	printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
+	       voice->number,
+	       voice->flags,
+	       voice->gf1_pan,
+	       snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f);
+#endif
+}
+
+static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan)
+{
+	unsigned long flags;
+	unsigned short vlo, vro;
+  
+	vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan);
+	vro = SNDRV_GF1_ATTEN(pan);
+	if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) {
+		vlo >>= 1;
+		vro >>= 1;
+	}
+	vlo <<= 4;
+	vro <<= 4;
+#if 0
+	printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n",
+			vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT),
+			vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT));
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);			
+	snd_gf1_select_voice(gus, voice->number);
+        snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);			
+	voice->vlo = vlo;
+	voice->vro = vro;
+}
+
+/*
+ *
+ */
+
+static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position)
+{
+	unsigned long flags;
+	unsigned int begin, addr, addr_end, addr_start;
+	int w_16;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+
+	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+	begin = simple->address.memory << 4;
+	w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0;
+	addr_start = simple->loop_start;
+	if (simple->format & SIMPLE_WAVE_LOOP) {
+		addr_end = simple->loop_end;
+	} else {
+		addr_end = (simple->size << 4) - (w_16 ? 40 : 24);
+	}
+	if (simple->format & SIMPLE_WAVE_BACKWARD) {
+		addr = simple->loop_end;
+		if (position < simple->loop_end)
+			addr -= position;
+	} else {
+		addr = position;
+	}
+	voice->control = 0x00;
+	voice->mode = 0x20;		/* enable offset registers */
+	if (simple->format & SIMPLE_WAVE_16BIT)
+		voice->control |= 0x04;
+	if (simple->format & SIMPLE_WAVE_BACKWARD)
+		voice->control |= 0x40;
+	if (simple->format & SIMPLE_WAVE_LOOP) {
+		voice->control |= 0x08;
+	} else {
+		voice->control |= 0x20;
+	}
+	if (simple->format & SIMPLE_WAVE_BIDIR)
+		voice->control |= 0x10;
+	if (simple->format & SIMPLE_WAVE_ULAW)
+		voice->mode |= 0x40;
+	if (w_16) {
+		addr = ((addr << 1) & ~0x1f) | (addr & 0x0f);
+		addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f);
+		addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f);
+	}
+	addr += begin;
+	addr_start += begin;
+	addr_end += begin;
+	snd_gf1_stop_voice(gus, voice->number);	
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
+	voice->venv_state = VENV_BEFORE;
+	voice->volume_control = 0x03;
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
+	} else {
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume);
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	do_volume_envelope(gus, voice);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	if (gus->gf1.enh_mode)
+		snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode);
+	snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control);
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_delay(gus);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control );
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+#if 0
+	snd_gf1_print_voice_registers(gus);
+#endif
+	voice->flags |= SNDRV_GF1_VFLG_RUNNING;
+	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+}
+
+static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode)
+{
+	unsigned char control;
+	unsigned long flags;
+
+	if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING))
+		return;
+	switch (mode) {
+	default:
+		if (gus->gf1.volume_ramp > 0) {
+			if (voice->venv_state < VENV_RELEASE) {
+				voice->venv_state = VENV_RELEASE;
+				do_volume_envelope(gus, voice);
+			}
+		}
+		if (mode != SAMPLE_STOP_VENVELOPE) {
+			snd_gf1_stop_voice(gus, voice->number);
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, voice->number);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+		}
+		break;
+	case SAMPLE_STOP_LOOP:		/* disable loop only */
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, voice->number);
+		control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+		control &= ~(0x83 | 0x04);
+		control |= 0x20;
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control);
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+		break;
+	}
+}
+
+static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	voice->fc_register = snd_gf1_translate_freq(gus, freq);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume)
+{
+	if (volume->volume >= 0) {
+		volume->volume &= 0x3fff;
+		voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4;
+		voice->venv_state_prev = VENV_SUSTAIN;
+		voice->venv_state = VENV_VOLUME;
+		do_volume_envelope(gus, voice);
+        }
+	if (volume->lr >= 0) {
+		volume->lr &= 0x3fff;
+		if (!gus->gf1.enh_mode) {
+			voice->gf1_pan = (volume->lr >> 10) & 15;
+			if (!gus->gf1.full_range_pan) {
+				if (voice->gf1_pan == 0)
+					voice->gf1_pan++;
+				if (voice->gf1_pan == 15)
+					voice->gf1_pan--;
+			}
+			voice->flags &= ~SNDRV_GF1_VFLG_PAN;	/* before */
+			do_pan_envelope(gus, voice);
+		} else {
+			set_enhanced_pan(gus, voice, volume->lr >> 7);
+		}
+	}
+}
+
+static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop)
+{
+	unsigned long flags;
+	int w_16 = voice->control & 0x04;
+	unsigned int begin, addr_start, addr_end;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+
+#if 0
+	printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
+#endif
+	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+	begin = simple->address.memory;
+	addr_start = loop->start;
+	addr_end = loop->end;
+	addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin;
+	addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+}
+
+static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position)
+{
+	unsigned long flags;
+	int w_16 = voice->control & 0x04;
+	unsigned int begin, addr;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+
+#if 0
+	printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
+#endif
+	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+	begin = simple->address.memory;
+	addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+}
+
+#if 0
+
+static unsigned char get_effects_mask( ultra_card_t *card, int value )
+{
+  if ( value > 7 ) return 0;
+  if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE )
+    return card -> gf1.effects -> chip.interwave.voice_output[ value ];
+  return 0;
+}
+
+#endif
+
+static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data)
+{
+#if 0
+  unsigned long flags;
+  unsigned char uc;
+
+  switch ( *data ) {
+    case ULTRA_PRIV1_IW_EFFECT:
+      uc = get_effects_mask( card, ultra_get_byte( data, 4 ) );
+      uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 );
+      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) );
+      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 );
+      voice -> data.simple.effect_accumulator = uc;
+      voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4;
+      if ( !card -> gf1.enh_mode ) return;
+      if ( voice -> flags & VFLG_WAIT_FOR_START ) return;
+      if ( voice -> flags & VFLG_RUNNING )
+        {
+          CLI( &flags );
+          gf1_select_voice( card, voice -> number );
+          ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator );
+          ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume );
+          STI( &flags );
+        }
+      break;
+   case ULTRA_PRIV1_IW_LFO:
+     ultra_lfo_command( card, voice -> number, data );
+  }
+#endif
+}
+
+#if 0
+
+/*
+ *
+ */
+
+static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait )
+{
+}
+
+static void note_wait( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_off( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_volume( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+/*
+ *
+ */
+ 
+static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority )
+{
+}
+
+static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note )
+{
+}
+
+static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 )
+{
+}
+
+/*
+ *
+ */
+ 
+#endif
+
+void snd_gf1_simple_init(snd_gus_voice_t *voice)
+{
+	voice->handler_wave = interrupt_wave;
+	voice->handler_volume = interrupt_volume;
+	voice->handler_effect = interrupt_effect;
+	voice->volume_change = NULL;
+	voice->sample_ops = &sample_ops;
+}
diff -Nru linux/sound/isa/gus/gus_synth.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_synth.c
--- linux/sound/isa/gus/gus_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_synth.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,324 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Synthesizer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/seq_device.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer");
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
+
+/*
+ *
+ */
+
+static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port)
+{
+	int idx;
+	snd_gus_voice_t * voice;
+	
+	for (idx = 0; idx < 32; idx++) {
+		voice = &gus->gf1.voices[idx];
+		if (voice->use && voice->client == client && voice->port == port)
+			snd_gf1_free_voice(gus, voice);
+	}
+}
+
+static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_gus_port_t * port = (snd_gus_port_t *)private_data;
+	snd_gus_card_t * gus = port->gus;
+	snd_gus_voice_t * voice;
+	int idx;
+
+	if (info->voices > 32)
+		return -EINVAL;
+	down(&gus->register_mutex);
+	if (!snd_gus_use_inc(gus)) {
+		up(&gus->register_mutex);
+		return -EFAULT;
+	}
+	for (idx = 0; idx < info->voices; idx++) {
+		voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+		if (voice == NULL) {
+			snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
+			up(&gus->register_mutex);
+			return -EBUSY;
+		}
+		voice->index = idx;
+	}
+	up(&gus->register_mutex);
+	return 0;
+}
+
+static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_gus_port_t * port = (snd_gus_port_t *)private_data;
+	snd_gus_card_t * gus = port->gus;
+
+	down(&gus->register_mutex);
+	snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
+	up(&gus->register_mutex);
+	return 0;
+}
+
+/*
+ *
+ */
+
+static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client)
+{
+	snd_seq_instr_header_t ifree;
+
+	memset(&ifree, 0, sizeof(ifree));
+	ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+	snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
+}
+ 
+int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
+{
+	snd_gus_port_t * p = (snd_gus_port_t *) private_data;
+	
+	snd_assert(p != NULL, return -EINVAL);
+	if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+	    ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+		snd_gus_sample_event(ev, p);
+		return 0;
+	}
+	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+	    ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+		if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+			snd_gus_synth_free_private_instruments(p, ev->data.addr.client);
+			return 0;
+		}
+	}
+	if (direct) {
+		if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+			snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops,
+					    p->gus->gf1.ilist,
+					    ev,
+					    p->gus->gf1.seq_client,
+					    atomic, hop);
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static void snd_gus_synth_instr_notify(void *private_data,
+				       snd_seq_kinstr_t *instr,
+				       int what)
+{
+	int idx;
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return);
+	snd_gus_voice_t *pvoice;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&gus->event_lock, flags);
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+			if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+				pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY);
+			} else {
+				snd_gf1_stop_voice(gus, pvoice->number);
+				pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&gus->event_lock, flags);
+}
+
+/*
+ *
+ */
+
+static void snd_gus_synth_free_port(void *private_data)
+{
+	snd_gus_port_t * p = (snd_gus_port_t *)private_data;
+	
+	if (p)
+		snd_midi_channel_free_set(p->chset);
+}
+
+static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx)
+{
+	snd_gus_port_t * p;
+	snd_seq_port_callback_t callbacks;
+	char name[32];
+	int result;
+	
+	p = &gus->gf1.seq_ports[idx];
+	p->chset = snd_midi_channel_alloc_set(16);
+	if (p->chset == NULL)
+		return -ENOMEM;
+	p->chset->private_data = p;
+	p->gus = gus;
+	p->client = gus->gf1.seq_client;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.owner = THIS_MODULE;
+	callbacks.use = snd_gus_synth_use;
+	callbacks.unuse = snd_gus_synth_unuse;
+	callbacks.event_input = snd_gus_synth_event_input;
+	callbacks.private_free = snd_gus_synth_free_port;
+	callbacks.private_data = p;
+	
+	sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx);
+	p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client,
+						   &callbacks,
+						   SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GS |
+						   SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+						   SNDRV_SEQ_PORT_TYPE_SYNTH,
+						   name);
+	if (p->chset->port < 0) {
+		result = p->chset->port;
+		snd_gus_synth_free_port(p);
+		return result;
+	}
+	p->port = p->chset->port;
+	return 0;
+}						 
+
+/*
+ *
+ */
+
+static int snd_gus_synth_new_device(snd_seq_device_t *dev)
+{
+	snd_gus_card_t *gus;
+	int client, i;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	snd_seq_port_subscribe_t sub;
+	snd_iwffff_ops_t *iwops;
+	snd_gf1_ops_t *gf1ops;
+	snd_simple_ops_t *simpleops;
+
+	gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (gus == NULL)
+		return -EINVAL;
+
+	init_MUTEX(&gus->register_mutex);
+	gus->gf1.seq_client = -1;
+	
+	/* allocate new client */
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = gus;
+	callbacks.allow_output = callbacks.allow_input = 1;
+	client = gus->gf1.seq_client =
+			snd_seq_create_kernel_client(gus->card, 1, &callbacks);
+	if (client < 0)
+		return client;
+
+	/* change name of client */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	sprintf(cinfo.name, gus->interwave ? "AMD InterWave" : "GF1");
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	for (i = 0; i < 4; i++)
+		snd_gus_synth_create_port(gus, i);
+		
+	gus->gf1.ilist = snd_seq_instr_list_new();
+	if (gus->gf1.ilist == NULL) {
+		snd_seq_delete_kernel_client(client);	
+		gus->gf1.seq_client = -1;
+		return -ENOMEM;
+	}
+	gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+
+	simpleops = &gus->gf1.simple_ops;
+	snd_seq_simple_init(simpleops, gus, NULL);
+	simpleops->put_sample = snd_gus_simple_put_sample;
+	simpleops->get_sample = snd_gus_simple_get_sample;
+	simpleops->remove_sample = snd_gus_simple_remove_sample;
+	simpleops->notify = snd_gus_synth_instr_notify;
+
+	gf1ops = &gus->gf1.gf1_ops;
+	snd_seq_gf1_init(gf1ops, gus, &simpleops->kops);
+	gf1ops->put_sample = snd_gus_gf1_put_sample;
+	gf1ops->get_sample = snd_gus_gf1_get_sample;
+	gf1ops->remove_sample = snd_gus_gf1_remove_sample;
+	gf1ops->notify = snd_gus_synth_instr_notify;
+
+	iwops = &gus->gf1.iwffff_ops;
+	snd_seq_iwffff_init(iwops, gus, &gf1ops->kops);
+	iwops->put_sample = snd_gus_iwffff_put_sample;
+	iwops->get_sample = snd_gus_iwffff_get_sample;
+	iwops->remove_sample = snd_gus_iwffff_remove_sample;
+	iwops->notify = snd_gus_synth_instr_notify;
+
+	memset(&sub, 0, sizeof(sub));
+	sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	sub.dest.client = client;
+	sub.dest.port = 0;
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+
+	return 0;
+}
+
+static int snd_gus_synth_delete_device(snd_seq_device_t *dev)
+{
+	snd_gus_card_t *gus;
+
+	gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (gus == NULL)
+		return -EINVAL;
+
+	if (gus->gf1.seq_client >= 0) {
+		snd_seq_delete_kernel_client(gus->gf1.seq_client);	
+		gus->gf1.seq_client = -1;
+	}
+	if (gus->gf1.ilist)
+		snd_seq_instr_list_free(&gus->gf1.ilist);
+	return 0;
+}
+
+static int __init alsa_gus_synth_init(void)
+{
+	static snd_seq_dev_ops_t ops = {
+		snd_gus_synth_new_device,
+		snd_gus_synth_delete_device
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops,
+					      sizeof(snd_gus_card_t*));
+}
+
+static void __exit alsa_gus_synth_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS);
+}
+
+module_init(alsa_gus_synth_init)
+module_exit(alsa_gus_synth_exit)
diff -Nru linux/sound/isa/gus/gus_tables.h linux-2.4.19-pre5-mjc/sound/isa/gus/gus_tables.h
--- linux/sound/isa/gus/gus_tables.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_tables.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define SNDRV_GF1_SCALE_TABLE_SIZE	128
+#define SNDRV_GF1_ATTEN_TABLE_SIZE	128
+
+#ifdef __GUS_TABLES_ALLOC__
+
+unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] =
+{
+      8372,      8870,      9397,      9956,     10548,     11175,
+     11840,     12544,     13290,     14080,     14917,     15804,
+     16744,     17740,     18795,     19912,     21096,     22351,
+     23680,     25088,     26580,     28160,     29834,     31609,
+     33488,     35479,     37589,     39824,     42192,     44701,
+     47359,     50175,     53159,     56320,     59669,     63217,
+     66976,     70959,     75178,     79649,     84385,     89402,
+     94719,    100351,    106318,    112640,    119338,    126434,
+    133952,    141918,    150356,    159297,    168769,    178805,
+    189437,    200702,    212636,    225280,    238676,    252868,
+    267905,    283835,    300713,    318594,    337539,    357610,
+    378874,    401403,    425272,    450560,    477352,    505737,
+    535809,    567670,    601425,    637188,    675077,    715219,
+    757749,    802807,    850544,    901120,    954703,   1011473,
+   1071618,   1135340,   1202851,   1274376,   1350154,   1430439,
+   1515497,   1605613,   1701088,   1802240,   1909407,   2022946,
+   2143237,   2270680,   2405702,   2548752,   2700309,   2860878,
+   3030994,   3211227,   3402176,   3604480,   3818814,   4045892,
+   4286473,   4541360,   4811404,   5097505,   5400618,   5721755,
+   6061989,   6422453,   6804352,   7208960,   7637627,   8091784,
+   8572947,   9082720,   9622807,  10195009,  10801236,  11443511,
+  12123977,  12844906
+};
+
+unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = {
+  4095 /* 0   */,1789 /* 1   */,1533 /* 2   */,1383 /* 3   */,1277 /* 4   */,
+  1195 /* 5   */,1127 /* 6   */,1070 /* 7   */,1021 /* 8   */,978  /* 9   */,
+  939  /* 10  */,903  /* 11  */,871  /* 12  */,842  /* 13  */,814  /* 14  */,
+  789  /* 15  */,765  /* 16  */,743  /* 17  */,722  /* 18  */,702  /* 19  */,
+  683  /* 20  */,665  /* 21  */,647  /* 22  */,631  /* 23  */,615  /* 24  */,
+  600  /* 25  */,586  /* 26  */,572  /* 27  */,558  /* 28  */,545  /* 29  */,
+  533  /* 30  */,521  /* 31  */,509  /* 32  */,498  /* 33  */,487  /* 34  */,
+  476  /* 35  */,466  /* 36  */,455  /* 37  */,446  /* 38  */,436  /* 39  */,
+  427  /* 40  */,418  /* 41  */,409  /* 42  */,400  /* 43  */,391  /* 44  */,
+  383  /* 45  */,375  /* 46  */,367  /* 47  */,359  /* 48  */,352  /* 49  */,
+  344  /* 50  */,337  /* 51  */,330  /* 52  */,323  /* 53  */,316  /* 54  */,
+  309  /* 55  */,302  /* 56  */,296  /* 57  */,289  /* 58  */,283  /* 59  */,
+  277  /* 60  */,271  /* 61  */,265  /* 62  */,259  /* 63  */,253  /* 64  */,
+  247  /* 65  */,242  /* 66  */,236  /* 67  */,231  /* 68  */,225  /* 69  */,
+  220  /* 70  */,215  /* 71  */,210  /* 72  */,205  /* 73  */,199  /* 74  */,
+  195  /* 75  */,190  /* 76  */,185  /* 77  */,180  /* 78  */,175  /* 79  */,
+  171  /* 80  */,166  /* 81  */,162  /* 82  */,157  /* 83  */,153  /* 84  */,
+  148  /* 85  */,144  /* 86  */,140  /* 87  */,135  /* 88  */,131  /* 89  */,
+  127  /* 90  */,123  /* 91  */,119  /* 92  */,115  /* 93  */,111  /* 94  */,
+  107  /* 95  */,103  /* 96  */,100  /* 97  */,96   /* 98  */,92   /* 99  */,
+  88   /* 100 */,85   /* 101 */,81   /* 102 */,77   /* 103 */,74   /* 104 */,
+  70   /* 105 */,67   /* 106 */,63   /* 107 */,60   /* 108 */,56   /* 109 */,
+  53   /* 110 */,50   /* 111 */,46   /* 112 */,43   /* 113 */,40   /* 114 */,
+  37   /* 115 */,33   /* 116 */,30   /* 117 */,27   /* 118 */,24   /* 119 */,
+  21   /* 120 */,18   /* 121 */,15   /* 122 */,12   /* 123 */,9    /* 124 */,
+  6    /* 125 */,3    /* 126 */,0    /* 127 */,
+};
+
+#else
+
+extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE];
+extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE];
+
+#endif
diff -Nru linux/sound/isa/gus/gus_timer.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_timer.c
--- linux/sound/isa/gus/gus_timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_timer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,207 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Timers
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *  GUS have similar timers as AdLib (OPL2/OPL3 chips).
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+#define chip_t snd_gus_card_t
+
+/*
+ *  Timer 1 - 80us
+ */
+
+static int snd_gf1_timer1_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	ticks = timer->sticks;
+	tmp = (gus->gf1.timer_enabled |= 4);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks);	/* timer 1 count */
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* enable timer 1 IRQ */
+	snd_gf1_adlib_write(gus, 0x04, tmp >> 2);	/* timer 2 start */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_timer1_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	tmp = (gus->gf1.timer_enabled &= ~4);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+/*
+ *  Timer 2 - 320us
+ */
+
+static int snd_gf1_timer2_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	ticks = timer->sticks;
+	tmp = (gus->gf1.timer_enabled |= 8);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks);	/* timer 2 count */
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* enable timer 2 IRQ */
+	snd_gf1_adlib_write(gus, 0x04, tmp >> 2);	/* timer 2 start */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_timer2_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	tmp = (gus->gf1.timer_enabled &= ~8);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus)
+{
+	snd_timer_t *timer = gus->gf1.timer1;
+
+	if (timer == NULL)
+		return;
+	snd_timer_interrupt(timer, timer->sticks);
+}
+
+static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus)
+{
+	snd_timer_t *timer = gus->gf1.timer2;
+
+	if (timer == NULL)
+		return;
+	snd_timer_interrupt(timer, timer->sticks);
+}
+
+/*
+
+ */
+
+static struct _snd_timer_hardware snd_gf1_timer1 =
+{
+	flags:          SNDRV_TIMER_HW_STOP,
+	resolution:     80000,
+	ticks:          256,
+	start:          snd_gf1_timer1_start,
+	stop:           snd_gf1_timer1_stop,
+};
+
+static struct _snd_timer_hardware snd_gf1_timer2 =
+{
+	flags:          SNDRV_TIMER_HW_STOP,
+	resolution:     320000,
+	ticks:          256,
+	start:          snd_gf1_timer2_start,
+	stop:           snd_gf1_timer2_stop,
+};
+
+static void snd_gf1_timer1_free(snd_timer_t *timer)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return);
+	gus->gf1.timer1 = NULL;
+}
+
+static void snd_gf1_timer2_free(snd_timer_t *timer)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return);
+	gus->gf1.timer2 = NULL;
+}
+
+void snd_gf1_timers_init(snd_gus_card_t * gus)
+{
+	snd_timer_t *timer;
+	snd_timer_id_t tid;
+
+	if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL)
+		return;
+
+	gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1;
+	gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = gus->card->number;
+	tid.device = gus->timer_dev;
+	tid.subdevice = 0;
+
+	if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
+		strcpy(timer->name, "GF1 timer #1");
+		timer->private_data = gus;
+		timer->private_free = snd_gf1_timer1_free;
+		timer->hw = snd_gf1_timer1;
+	}
+	gus->gf1.timer1 = timer;
+
+	tid.device++;
+
+	if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
+		strcpy(timer->name, "GF1 timer #2");
+		timer->private_data = gus;
+		timer->private_free = snd_gf1_timer2_free;
+		timer->hw = snd_gf1_timer2;
+	}
+	gus->gf1.timer2 = timer;
+}
+
+void snd_gf1_timers_done(snd_gus_card_t * gus)
+{
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2);
+	if (gus->gf1.timer1) {
+		snd_device_free(gus->card, gus->gf1.timer1);
+		gus->gf1.timer1 = NULL;
+	}
+	if (gus->gf1.timer2) {
+		snd_device_free(gus->card, gus->gf1.timer2);
+		gus->gf1.timer2 = NULL;
+	}
+}
diff -Nru linux/sound/isa/gus/gus_uart.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_uart.c
--- linux/sound/isa/gus/gus_uart.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_uart.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,260 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for the GF1 MIDI interface - like UART 6850
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus)
+{
+	int count;
+	unsigned char stat, data, byte;
+	unsigned long flags;
+
+	count = 10;
+	while (count) {
+		spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+		stat = snd_gf1_uart_stat(gus);
+		if (!(stat & 0x01)) {	/* data in Rx FIFO? */
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			count--;
+			continue;
+		}
+		count = 100;	/* arm counter to new value */
+		data = snd_gf1_uart_get(gus);
+		if (!(gus->gf1.uart_cmd & 0x80)) {
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			continue;
+		}			
+		if (stat & 0x10) {	/* framing error */
+			gus->gf1.uart_framing++;
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			snd_rawmidi_receive_reset(gus->midi_substream_input);
+			continue;
+		}
+		byte = snd_gf1_uart_get(gus);
+		spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
+		if (stat & 0x20) {
+			gus->gf1.uart_overrun++;
+			snd_rawmidi_receive_reset(gus->midi_substream_input);
+		}
+	}
+}
+
+static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus)
+{
+	char byte;
+	unsigned long flags;
+
+	/* try unlock output */
+	if (snd_gf1_uart_stat(gus) & 0x01)
+		snd_gf1_interrupt_midi_in(gus);
+
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (snd_gf1_uart_stat(gus) & 0x02) {	/* Tx FIFO free? */
+		if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {	/* no other bytes or error */
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
+		} else {
+			snd_gf1_uart_put(gus, byte);
+		}
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+}
+
+static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close)
+{
+	snd_gf1_uart_cmd(gus, 0x03);	/* reset */
+	if (!close && gus->uart_enable) {
+		udelay(160);
+		snd_gf1_uart_cmd(gus, 0x00);	/* normal operations */
+	}
+}
+
+static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (!(gus->gf1.uart_cmd & 0x80)) {	/* input active? */
+		snd_gf1_uart_reset(gus, 0);
+	}
+	gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
+	gus->midi_substream_output = substream;
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+#if 0
+	snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+#endif
+	return 0;
+}
+
+static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+	int i;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
+		snd_gf1_uart_reset(gus, 0);
+	}
+	gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
+	gus->midi_substream_input = substream;
+	if (gus->uart_enable) {
+		for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
+			snd_gf1_uart_get(gus);	/* clean Rx */
+		if (i >= 1000)
+			snd_printk("gus midi uart init read - cleanup error\n");
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+#if 0
+	snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+	snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
+#endif
+	return 0;
+}
+
+static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
+		snd_gf1_uart_reset(gus, 1);
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
+	gus->midi_substream_output = NULL;
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
+		snd_gf1_uart_reset(gus, 1);
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
+	gus->midi_substream_input = NULL;
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+	return 0;
+}
+
+static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	snd_gus_card_t *gus;
+	unsigned long flags;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (up) {
+		if ((gus->gf1.uart_cmd & 0x80) == 0)
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
+	} else {
+		if (gus->gf1.uart_cmd & 0x80)
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+}
+
+static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+	char byte;
+	int timeout;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (up) {
+		if ((gus->gf1.uart_cmd & 0x20) == 0) {
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			/* wait for empty Rx - Tx is probably unlocked */
+			timeout = 10000;
+			while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
+			/* Tx FIFO free? */
+			spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+			if (gus->gf1.uart_cmd & 0x20) {
+				spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+				return;
+			}
+			if (snd_gf1_uart_stat(gus) & 0x02) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+					return;
+				}
+				snd_gf1_uart_put(gus, byte);
+			}
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);	/* enable Tx interrupt */
+		}
+	} else {
+		if (gus->gf1.uart_cmd & 0x20)
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_gf1_uart_output =
+{
+	open:		snd_gf1_uart_output_open,
+	close:		snd_gf1_uart_output_close,
+	trigger:	snd_gf1_uart_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_gf1_uart_input =
+{
+	open:           snd_gf1_uart_input_open,
+	close:          snd_gf1_uart_input_close,
+	trigger:        snd_gf1_uart_input_trigger,
+};
+
+int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = gus;
+	gus->midi_uart = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return err;
+}
diff -Nru linux/sound/isa/gus/gus_volume.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_volume.c
--- linux/sound/isa/gus/gus_volume.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_volume.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,211 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#define __GUS_TABLES_ALLOC__
+#include "gus_tables.h"
+
+EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */
+
+unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol)
+{
+	unsigned short e, m, tmp;
+
+	if (vol > 65535)
+		vol = 65535;
+	tmp = vol;
+	e = 7;
+	if (tmp < 128) {
+		while (e > 0 && tmp < (1 << e))
+			e--;
+	} else {
+		while (tmp > 255) {
+			tmp >>= 1;
+			e++;
+		}
+	}
+	m = vol - (1 << e);
+	if (m > 0) {
+		if (e > 8)
+			m >>= e - 8;
+		else if (e < 8)
+			m <<= 8 - e;
+		m &= 255;
+	}
+	return (e << 8) | m;
+}
+
+unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol)
+{
+	unsigned int rvol;
+	unsigned short e, m;
+
+	if (!gf1_vol)
+		return 0;
+	e = gf1_vol >> 8;
+	m = (unsigned char) gf1_vol;
+	rvol = 1 << e;
+	if (e > 8)
+		return rvol | (m << (e - 8));
+	return rvol | (m >> (8 - e));
+}
+
+unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
+				    unsigned short start,
+				    unsigned short end,
+				    unsigned int us)
+{
+	static unsigned char vol_rates[19] =
+	{
+		23, 24, 26, 28, 29, 31, 32, 34,
+		36, 37, 39, 40, 42, 44, 45, 47,
+		49, 50, 52
+	};
+	unsigned short range, increment, value, i;
+
+	start >>= 4;
+	end >>= 4;
+	if (start < end)
+		us /= end - start;
+	else
+		us /= start - end;
+	range = 4;
+	value = gus->gf1.enh_mode ?
+	    vol_rates[0] :
+	    vol_rates[gus->gf1.active_voices - 14];
+	for (i = 0; i < 3; i++) {
+		if (us < value) {
+			range = i;
+			break;
+		} else
+			value <<= 3;
+	}
+	if (range == 4) {
+		range = 3;
+		increment = 1;
+	} else
+		increment = (value + (value >> 1)) / us;
+	return (range << 6) | (increment & 0x3f);
+}
+
+unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16)
+{
+	freq16 >>= 3;
+	if (freq16 < 50)
+		freq16 = 50;
+	if (freq16 & 0xf8000000) {
+		freq16 = ~0xf8000000;
+		snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16);
+	}
+	return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
+}
+
+short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
+{
+	static short vibrato_table[] =
+	{
+		0, 0, 32, 592, 61, 1175, 93, 1808,
+		124, 2433, 152, 3007, 182, 3632, 213, 4290,
+		241, 4834, 255, 5200
+	};
+
+	long depth;
+	short *vi1, *vi2, pcents, v1;
+
+	pcents = cents < 0 ? -cents : cents;
+	for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2);
+	v1 = *(vi1 + 1);
+	/* The FC table above is a list of pairs. The first number in the pair     */
+	/* is the cents index from 0-255 cents, and the second number in the       */
+	/* pair is the FC adjustment needed to change the pitch by the indexed     */
+	/* number of cents. The table was created for an FC of 32768.              */
+	/* The following expression does a linear interpolation against the        */
+	/* approximated log curve in the table above, and then scales the number   */
+	/* by the FC before the LFO. This calculation also adjusts the output      */
+	/* value to produce the appropriate depth for the hardware. The depth      */
+	/* is 2 * desired FC + 1.                                                  */
+	depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14;
+	if (depth)
+		depth++;
+	if (depth > 255)
+		depth = 255;
+	return cents < 0 ? -(short) depth : (short) depth;
+}
+
+unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens)
+{
+	static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
+	int wheel, sensitivity;
+	unsigned int mantissa, f1, f2;
+	unsigned short semitones, f1_index, f2_index, f1_power, f2_power;
+	char bend_down = 0;
+	int bend;
+
+	if (!sens)
+		return 1024;
+	wheel = (int) pitchbend - 8192;
+	sensitivity = ((int) sens * wheel) / 128;
+	if (sensitivity < 0) {
+		bend_down = 1;
+		sensitivity = -sensitivity;
+	}
+	semitones = (unsigned int) (sensitivity >> 13);
+	mantissa = sensitivity % 8192;
+	f1_index = semitones % 12;
+	f2_index = (semitones + 1) % 12;
+	f1_power = semitones / 12;
+	f2_power = (semitones + 1) / 12;
+	f1 = log_table[f1_index] << f1_power;
+	f2 = log_table[f2_index] << f2_power;
+	bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1);
+	if (bend_down)
+		bend = 1048576L / bend;
+	return bend;
+}
+
+unsigned short snd_gf1_compute_freq(unsigned int freq,
+				    unsigned int rate,
+				    unsigned short mix_rate)
+{
+	unsigned int fc;
+	int scale = 0;
+
+	while (freq >= 4194304L) {
+		scale++;
+		freq >>= 1;
+	}
+	fc = (freq << 10) / rate;
+	if (fc > 97391L) {
+		fc = 97391;
+		snd_printk("patch: (1) fc frequency overflow - %u\n", fc);
+	}
+	fc = (fc * 44100UL) / mix_rate;
+	while (scale--)
+		fc <<= 1;
+	if (fc > 65535L) {
+		fc = 65535;
+		snd_printk("patch: (2) fc frequency overflow - %u\n", fc);
+	}
+	return (unsigned short) fc;
+}
diff -Nru linux/sound/isa/gus/gusclassic.c linux-2.4.19-pre5-mjc/sound/isa/gus/gusclassic.c
--- linux/sound/isa/gus/gusclassic.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gusclassic.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,302 @@
+/*
+ *  Driver for Gravis UltraSound Classic soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Gravis UltraSound Classic");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound Classic}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x230,0x240,0x250,0x260 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for GUS Classic soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for GUS Classic soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable GUS Classic soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x10}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_channels,  SNDRV_ENABLED ",allows:{{14,32}}");
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+
+static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_gusclassic_detect(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -ENODEV;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -ENODEV;
+#endif
+
+	return 0;
+}
+
+static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus)
+{
+	gus->equal_irq = 0;
+	gus->codec_flag = 0;
+	gus->max_flag = 0;
+	gus->joystick_dac = snd_joystick_dac[dev];
+}
+
+static int __init snd_gusclassic_probe(int dev)
+{
+	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
+	static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_gusclassic *guscard;
+	snd_gus_card_t *gus = NULL;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	guscard = (struct snd_gusclassic *)card->private_data;
+	if (snd_pcm_channels[dev] < 2)
+		snd_pcm_channels[dev] = 2;
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+	if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	dma2 = snd_dma2[dev];
+	if (dma2 == SNDRV_AUTO_DMA) {
+		if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+	}
+
+
+	if ((err = snd_gus_create(card,
+				  snd_port[dev],
+				  irq, dma1, dma2,
+			          0, snd_channels[dev], snd_pcm_channels[dev],
+			          0, &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusclassic_detect(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusclassic_init(dev, gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (gus->max_flag || gus->ess_flag) {
+		snd_card_free(card);
+		snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port);
+		return -ENODEV;
+	}
+	if ((err = snd_gf1_new_mixer(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (!gus->ace_flag) {
+		if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, irq, dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusclassic_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_gusclassic_legacy_auto_probe(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+		snd_port[dev] = port;
+		res = snd_gusclassic_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+static int __init alsa_card_gusclassic_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_gusclassic_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "GUS Classic soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_gusclassic_exit(void)
+{
+	int idx;
+	
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_gusclassic_cards[idx]);
+}
+
+module_init(alsa_card_gusclassic_init)
+module_exit(alsa_card_gusclassic_exit)
+
+#ifndef MODULE
+
+/* format is: snd-gusclassic=snd_enable,snd_index,snd_id,
+			     snd_port,snd_irq,
+			     snd_dma1,snd_dma2,
+			     snd_joystick_dac,
+			     snd_channels,snd_pcm_channels */
+
+static int __init alsa_card_gusclassic_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_joystick_dac[nr_dev]) == 2 &&
+	       get_option(&str,&snd_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-gusclassic=", alsa_card_gusclassic_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/gus/gusextreme.c linux-2.4.19-pre5-mjc/sound/isa/gus/gusextreme.c
--- linux/sound/isa/gus/gusextreme.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gusextreme.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,436 @@
+/*
+ *  Driver for Gravis UltraSound Extreme soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/es1688.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Gravis UltraSound Extreme");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound Extreme}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+static long snd_gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for GUS Extreme soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for GUS Extreme soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable GUS Extreme soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x20}},dialog:list");
+MODULE_PARM(snd_gf1_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_gf1_port, "GF1 port # for GUS Extreme driver (optional).");
+MODULE_PARM_SYNTAX(snd_gf1_port, SNDRV_ENABLED ",allows:{{0x210,0x270,0x10}},dialog:list");
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x320,0x10}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list");
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list");
+MODULE_PARM(snd_gf1_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_gf1_irq, "GF1 IRQ # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_gf1_irq, SNDRV_ENABLED ",allows:{{2},{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "GF1 DMA # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}");
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+
+static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_gusextreme_detect(int dev,
+					snd_card_t * card,
+					snd_gus_card_t * gus,
+					es1688_t *es1688)
+{
+	unsigned long flags;
+
+	/*
+	 * This is main stuff - enable access to GF1 chip...
+	 * I'm not sure, if this will work for card which have
+	 * ES1688 chip in another place than 0x220.
+         *
+         * I used reverse-engineering in DOSEMU. [--jk]
+	 *
+	 * ULTRINIT.EXE:
+	 * 0x230 = 0,2,3
+	 * 0x240 = 2,0,1
+	 * 0x250 = 2,0,3
+	 * 0x260 = 2,2,1
+	 */
+
+	spin_lock_irqsave(&es1688->mixer_lock, flags);
+	snd_es1688_mixer_write(es1688, 0x40, 0x0b);	/* don't change!!! */
+	spin_unlock_irqrestore(&es1688->mixer_lock, flags);
+	spin_lock_irqsave(&es1688->reg_lock, flags);
+	outb(snd_gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
+	outb(0, 0x201);
+	outb(snd_gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
+	outb(0, 0x201);
+	outb(snd_gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
+	spin_unlock_irqrestore(&es1688->reg_lock, flags);
+
+	udelay(100);
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -EIO;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -EIO;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -EIO;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -EIO;
+#endif
+
+	return 0;
+}
+
+static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus)
+{
+	gus->joystick_dac = snd_joystick_dac[dev];
+}
+
+static int __init snd_gusextreme_mixer(es1688_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUX to SYNTHESIZER */
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Synth Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign Master Playback Switch to Synth Playback Switch */
+	strcpy(id1.name, "Master Playback Switch");
+	strcpy(id2.name, "Synth Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	return 0;
+}
+
+static int __init snd_gusextreme_probe(int dev)
+{
+	static int possible_ess_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_ess_dmas[] = {1, 3, 0, -1};
+	static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+	static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1};
+	int gf1_irq, gf1_dma, ess_irq, mpu_irq, ess_dma;
+	snd_card_t *card;
+	struct snd_gusextreme *acard;
+	snd_gus_card_t *gus;
+	es1688_t *es1688;
+	opl3_t *opl3;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_gusextreme *)card->private_data;
+
+	gf1_irq = snd_gf1_irq[dev];
+	if (gf1_irq == SNDRV_AUTO_IRQ) {
+		if ((gf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ for GF1\n");
+			return -EBUSY;
+		}
+	}
+	ess_irq = snd_irq[dev];
+	if (ess_irq == SNDRV_AUTO_IRQ) {
+		if ((ess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ for ES1688\n");
+			return -EBUSY;
+		}
+	}
+	if (snd_mpu_port[dev] == SNDRV_AUTO_PORT)
+		snd_mpu_port[dev] = 0;
+	mpu_irq = snd_mpu_irq[dev];
+	if (mpu_irq > 15)
+		mpu_irq = -1;
+	gf1_dma = snd_dma1[dev];
+	if (gf1_dma == SNDRV_AUTO_DMA) {
+		if ((gf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA for GF1\n");
+			return -EBUSY;
+		}
+	}
+	ess_dma = snd_dma8[dev];
+	if (ess_dma == SNDRV_AUTO_DMA) {
+		if ((ess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA for ES1688\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev],
+				     ess_irq, mpu_irq, ess_dma,
+				     ES1688_HW_1688, &es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_gf1_port[dev] < 0)
+		snd_gf1_port[dev] = snd_port[dev] + 0x20;
+	if ((err = snd_gus_create(card,
+				  snd_gf1_port[dev],
+				  gf1_irq,
+				  gf1_dma,
+				  -1,
+				  0, snd_channels[dev],
+				  snd_pcm_channels[dev], 0,
+				  &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusextreme_init(dev, gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (!gus->ess_flag) {
+		snd_card_free(card);
+		snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port);
+		return -ENODEV;
+	}
+	if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1688_mixer(es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_component_add(card, "ES1688");
+	if (snd_pcm_channels[dev] > 0) {
+		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_gf1_new_mixer(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusextreme_mixer(es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_opl3_create(card, es1688->port, es1688->port + 2,
+			    OPL3_HW_OPL3, 0, &opl3) < 0) {
+		printk(KERN_ERR "gusextreme: opl3 not detected at 0x%lx\n", es1688->port);
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (es1688->mpu_port >= 0x300) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
+					       es1688->mpu_port, 0,
+					       mpu_irq,
+					       SA_INTERRUPT,
+					       NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i",
+		es1688->port, gf1_irq, ess_irq, gf1_dma, ess_dma);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusextreme_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_gusextreme_legacy_auto_probe(unsigned long port)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+                        continue;
+                snd_port[dev] = port;
+                res = snd_gusextreme_probe(dev);
+                if (res < 0)
+                        snd_port[dev] = SNDRV_AUTO_PORT;
+                return res;
+        }
+        return -ENODEV;
+}
+
+static int __init alsa_card_gusextreme_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_gusextreme_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "GUS Extreme soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_gusextreme_exit(void)
+{
+	int idx;
+	snd_card_t *card;
+	struct snd_gusextreme *acard;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		card = snd_gusextreme_cards[idx];
+		if (card == NULL)
+			continue;
+		acard = (struct snd_gusextreme *)card->private_data;
+		snd_card_free(snd_gusextreme_cards[idx]);
+	}
+}
+
+module_init(alsa_card_gusextreme_init)
+module_exit(alsa_card_gusextreme_exit)
+
+#ifndef MODULE
+
+/* format is: snd-gusextreme=snd_enable,snd_index,snd_id,
+			     snd_port,snd_gf1_port,snd_mpu_port,
+			     snd_irq,snd_gf1_irq,snd_mpu_irq,
+			     snd_dma8,snd_dma1,
+			     snd_joystick_dac,
+			     snd_channels,snd_pcm_channels */
+
+static int __init alsa_card_gusextreme_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_gf1_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_gf1_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-gusextreme=", alsa_card_gusextreme_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/gus/gusmax.c linux-2.4.19-pre5-mjc/sound/isa/gus/gusmax.c
--- linux/sound/isa/gus/gusmax.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/gusmax.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,438 @@
+/*
+ *  Driver for Gravis UltraSound MAX soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/cs4231.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Gravis UltraSound MAX");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound MAX}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x230,0x240,0x250,0x260 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for GUS MAX soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for GUS MAX soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable GUS MAX soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x230},{0x240},{0x250},{0x260}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_channels, "Used GF1 channels for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}");
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+
+struct snd_gusmax {
+	int irq;
+	snd_card_t *card;
+	snd_gus_card_t *gus;
+	cs4231_t *cs4231;
+	unsigned short gus_status_reg;
+	unsigned short pcm_status_reg;
+};
+
+static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_gusmax_detect(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -ENODEV;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -ENODEV;
+#endif
+	return 0;
+}
+
+static void snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id;
+	int loop, max = 5;
+
+	do {
+		loop = 0;
+		if (inb(maxcard->gus_status_reg)) {
+			snd_gus_interrupt(irq, maxcard->gus, regs);
+			loop++;
+		}
+		if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */
+			snd_cs4231_interrupt(irq, maxcard->cs4231, regs);
+			loop++;
+		}
+	} while (loop && --max > 0);
+}
+
+static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus)
+{
+	gus->equal_irq = 1;
+	gus->codec_flag = 1;
+	gus->joystick_dac = snd_joystick_dac[dev];
+	/* init control register */
+	gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f;
+	if (gus->gf1.dma1 > 3)
+		gus->max_cntrl_val |= 0x10;
+	if (gus->gf1.dma2 > 3)
+		gus->max_cntrl_val |= 0x20;
+	gus->max_cntrl_val |= 0x40;
+	outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
+}
+
+#define CS4231_PRIVATE( left, right, shift, mute ) \
+			((left << 24)|(right << 16)|(shift<<8)|mute)
+
+static int __init snd_gusmax_mixer(cs4231_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int err;
+	
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUXA to SYNTHESIZER */
+	strcpy(id1.name, "Aux Playback Switch");
+	strcpy(id2.name, "Synth Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Synth Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign AUXB to CD */
+	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+	strcpy(id2.name, "CD Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "CD Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+#if 0
+	/* reassign Mono Input to MIC */
+	if (snd_mixer_group_rename(mixer,
+				SNDRV_MIXER_IN_MONO, 0,
+				SNDRV_MIXER_IN_MIC, 0) < 0)
+		goto __error;
+	if (snd_mixer_elem_rename(mixer,
+				SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT,
+				SNDRV_MIXER_IN_MIC, 0) < 0)
+		goto __error;
+	if (snd_mixer_elem_rename(mixer,
+				"Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1,
+				"Mic Capture Volume", 0) < 0)
+		goto __error;
+	if (snd_mixer_elem_rename(mixer,
+				"Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1,
+				"Mic Capture Switch", 0) < 0)
+		goto __error;
+#endif
+	return 0;
+}
+
+static void snd_gusmax_free(snd_card_t *card)
+{
+	struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data;
+	
+	if (maxcard == NULL)
+		return;
+	if (maxcard->irq >= 0)
+		free_irq(maxcard->irq, (void *)maxcard);
+}
+
+static int __init snd_gusmax_probe(int dev)
+{
+	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+	static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+	int irq, dma1, dma2, err;
+	snd_card_t *card;
+	snd_gus_card_t *gus = NULL;
+	cs4231_t *cs4231;
+	struct snd_gusmax *maxcard;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_gusmax));
+	if (card == NULL)
+		return -ENOMEM;
+	card->private_free = snd_gusmax_free;
+	maxcard = (struct snd_gusmax *)card->private_data;
+	maxcard->card = card;
+	maxcard->irq = -1;
+	
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+	if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	dma2 = snd_dma2[dev];
+	if (dma2 == SNDRV_AUTO_DMA) {
+		if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_gus_create(card,
+				  snd_port[dev],
+				  -irq, dma1, dma2,
+				  0, snd_channels[dev],
+				  snd_pcm_channels[dev],
+				  0, &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusmax_detect(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	maxcard->gus_status_reg = gus->gf1.reg_irqstat;
+	maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
+	snd_gusmax_init(dev, card, gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (!gus->max_flag) {
+		snd_card_free(card);
+		printk(KERN_ERR "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
+		return -ENODEV;
+	}
+
+	if (request_irq(irq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) {
+		snd_card_free(card);
+		printk(KERN_ERR "gusmax: unable to grab IRQ %d\n", irq);
+		return -EBUSY;
+	}
+	maxcard->irq = irq;
+	
+	if ((err = snd_cs4231_create(card,
+				     gus->gf1.port + 0x10c, -1, irq,
+				     dma2 < 0 ? dma1 : dma2, dma1,
+				     CS4231_HW_DETECT,
+				     CS4231_HWSHARE_IRQ |
+				     CS4231_HWSHARE_DMA1 |
+				     CS4231_HWSHARE_DMA2,
+				     &cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_pcm_channels[dev] > 0) {
+		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_gusmax_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, irq, dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%i", dma2);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+		
+	maxcard->gus = gus;
+	maxcard->cs4231 = cs4231;
+	snd_gusmax_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_gusmax_legacy_auto_probe(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+		snd_port[dev] = port;
+		res = snd_gusmax_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+static int __init alsa_card_gusmax_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_gusmax_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "GUS MAX soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_gusmax_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_gusmax_cards[idx]);
+}
+
+module_init(alsa_card_gusmax_init)
+module_exit(alsa_card_gusmax_exit)
+
+#ifndef MODULE
+
+/* format is: snd-gusmax=snd_enable,snd_index,snd_id,
+			 snd_port,snd_irq,
+			 snd_dma1,snd_dma2,
+			 snd_joystick_dac,
+			 snd_channels,snd_pcm_channels */
+
+static int __init alsa_card_gusmax_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_joystick_dac[nr_dev]) == 2 &&
+	       get_option(&str,&snd_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-gusmax=", alsa_card_gusmax_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/gus/interwave-stb.c linux-2.4.19-pre5-mjc/sound/isa/gus/interwave-stb.c
--- linux/sound/isa/gus/interwave-stb.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/interwave-stb.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,2 @@
+#define SNDRV_STB
+#include "interwave.c"
diff -Nru linux/sound/isa/gus/interwave.c linux-2.4.19-pre5-mjc/sound/isa/gus/interwave.c
--- linux/sound/isa/gus/interwave.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/gus/interwave.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1025 @@
+/*
+ *  Driver for AMD InterWave soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ *   1999/07/22		Erik Inge Bolso <knan@mo.himolde.no>
+ *			* mixer group handlers
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/cs4231.h>
+#ifdef SNDRV_STB
+#include <sound/tea6330t.h>
+#endif
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+#ifndef SNDRV_STB
+MODULE_DESCRIPTION("AMD InterWave");
+MODULE_DEVICES("{{Gravis,UltraSound Plug & Play},"
+		"{STB,SoundRage32},"
+		"{MED,MED3210},"
+		"{Dynasonix,Dynasonix Pro},"
+		"{Panasonic,PCA761AW}}");
+#else
+MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T");
+MODULE_DEVICES("{{AMD,InterWave STB with TEA6330T}}");
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+#ifdef __ISAPNP__
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x210,0x220,0x230,0x240,0x250,0x260 */
+#ifdef SNDRV_STB
+static long snd_port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x350,0x360,0x370,0x380 */
+#endif
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+static int snd_effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for InterWave soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for InterWave soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable InterWave soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x210,0x260,0x10}},dialog:list");
+#ifdef SNDRV_STB
+MODULE_PARM(snd_port_tc, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_port_tc, SNDRV_ENABLED ",allows:{{0x350,0x380,0x10}},dialog:list");
+#endif
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_joystic_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_midi, "MIDI UART enable for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_midi, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+MODULE_PARM(snd_effect, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_effect, "Effects enable for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_effect, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+
+struct snd_interwave {
+	int irq;
+	snd_card_t *card;
+	snd_gus_card_t *gus;
+	cs4231_t *cs4231;
+#ifdef SNDRV_STB
+	struct resource *i2c_res;
+#endif
+	unsigned short gus_status_reg;
+	unsigned short pcm_status_reg;
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+#ifdef SNDRV_STB
+	struct isapnp_dev *devtc;
+#endif
+#endif
+};
+
+static snd_card_t *snd_interwave_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_interwave_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_interwave_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_INTERWAVE(_va, _vb, _vc, _device, _audio) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \
+	}
+#define ISAPNP_INTERWAVE_STB(_va, _vb, _vc, _device, _audio, _tone) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _tone), } \
+	}
+
+static struct isapnp_card_id snd_interwave_pnpids[] __devinitdata = {
+#ifndef SNDRV_STB
+	/* Gravis UltraSound Plug & Play */
+	ISAPNP_INTERWAVE('G','R','V',0x0001,0x0000),
+	/* STB SoundRage32 */
+	ISAPNP_INTERWAVE('S','T','B',0x011a,0x0010),
+	/* MED3210 */
+	ISAPNP_INTERWAVE('D','X','P',0x3201,0x0010),
+	/* Dynasonic Pro */
+	/* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */
+	ISAPNP_INTERWAVE('C','D','C',0x1111,0x1112),
+	/* Panasonic PCA761AW Audio Card */
+	ISAPNP_INTERWAVE('A','D','V',0x55ff,0x0010),
+#else
+	/* InterWave STB with TEA6330T */
+	ISAPNP_INTERWAVE_STB('A','D','V',0x550a,0x0010,0x0015),
+#endif
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_interwave_pnpids);
+
+#endif /* __ISAPNP__ */
+
+
+#ifdef SNDRV_STB
+static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data)
+{
+	unsigned long port = bus->private_value;
+
+#if 0
+	printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
+#endif
+	outb((data << 1) | ctrl, port);
+	udelay(10);
+}
+
+static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus)
+{
+	unsigned long port = bus->private_value;
+	unsigned char res;
+
+	res = inb(port) & 1;
+#if 0
+	printk("i2c_getclockline - 0x%lx -> %i\n", port, res);
+#endif
+	return res;
+}
+
+static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack)
+{
+	unsigned long port = bus->private_value;
+	unsigned char res;
+
+	if (ack)
+		udelay(10);
+	res = (inb(port) & 2) >> 1;
+#if 0
+	printk("i2c_getdataline - 0x%lx -> %i\n", port, res);
+#endif
+	return res;
+}
+
+static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = {
+	setlines: snd_interwave_i2c_setlines,
+	getclock: snd_interwave_i2c_getclockline,
+	getdata:  snd_interwave_i2c_getdataline,
+};
+
+static int __init snd_interwave_detect_stb(struct snd_interwave *iwcard,
+					   snd_gus_card_t * gus, int dev,
+					   snd_i2c_bus_t **rbus)
+{
+	unsigned long port;
+	snd_i2c_bus_t *bus;
+	snd_card_t *card = iwcard->card;
+	char name[32];
+	int err;
+
+	*rbus = NULL;
+	port = snd_port_tc[dev];
+	if (port == SNDRV_AUTO_PORT) {
+		port = 0x350;
+		if (gus->gf1.port == 0x250) {
+			port = 0x360;
+		}
+		while (port <= 0x380) {
+			if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL)
+				break;
+			port += 0x10;
+		}
+		if (port > 0x380)
+			return -ENODEV;
+	} else {
+		if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL)
+			return -ENODEV;
+	}
+	sprintf(name, "InterWave-%i", card->number);
+	if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0)
+		return err;
+	bus->private_value = port;
+	bus->hw_ops.bit = &snd_interwave_i2c_bit_ops;
+	if ((err = snd_tea6330t_detect(bus, 0)) < 0)
+		return err;
+	*rbus = bus;
+	return 0;
+}
+#endif
+
+static int __init snd_interwave_detect(struct snd_interwave *iwcard,
+				       snd_gus_card_t * gus,
+				       int dev
+#ifdef SNDRV_STB
+				       , snd_i2c_bus_t **rbus
+#endif
+				       )
+{
+	unsigned long flags;
+	unsigned char rev1, rev2;
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		int d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -ENODEV;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		int d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -ENODEV;
+#endif
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
+	rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2);
+	if ((rev1 & 0xf0) == (rev2 & 0xf0) &&
+	    (rev1 & 0x0f) != (rev2 & 0x0f)) {
+		snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port);
+		gus->interwave = 1;
+		strcpy(gus->card->shortname, "AMD InterWave");
+		gus->revision = rev1 >> 4;
+#ifndef SNDRV_STB
+		return 0;	/* ok.. We have an InterWave board */
+#else
+		return snd_interwave_detect_stb(iwcard, gus, dev, rbus);
+#endif
+	}
+	snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port);
+	return -ENODEV;
+}
+
+static void snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct snd_interwave *iwcard = (struct snd_interwave *) dev_id;
+	int loop, max = 5;
+
+	do {
+		loop = 0;
+		if (inb(iwcard->gus_status_reg)) {
+			snd_gus_interrupt(irq, iwcard->gus, regs);
+			loop++;
+		}
+		if (inb(iwcard->pcm_status_reg) & 0x01) {	/* IRQ bit is set? */
+			snd_cs4231_interrupt(irq, iwcard->cs4231, regs);
+			loop++;
+		}
+	} while (loop && --max > 0);
+}
+
+static void __init snd_interwave_reset(snd_gus_card_t * gus)
+{
+	snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00);
+	udelay(160);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01);
+	udelay(160);
+}
+
+static void __init snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes)
+{
+	unsigned int idx;
+	unsigned int local;
+	unsigned char d;
+
+	for (idx = 0; idx < 4; idx++) {
+		sizes[idx] = 0;
+		d = 0x55;
+		for (local = idx << 22;
+		     local < (idx << 22) + 0x400000;
+		     local += 0x40000, d++) {
+			snd_gf1_poke(gus, local, d);
+			snd_gf1_poke(gus, local + 1, d + 1);
+#if 0
+			printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n",
+			       d,
+			       snd_gf1_peek(gus, local),
+			       snd_gf1_peek(gus, local + 1),
+			       snd_gf1_peek(gus, idx << 22));
+#endif
+			if (snd_gf1_peek(gus, local) != d ||
+			    snd_gf1_peek(gus, local + 1) != d + 1 ||
+			    snd_gf1_peek(gus, idx << 22) != 0x55)
+				break;
+			sizes[idx]++;
+		}
+	}
+#if 0
+	printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]);
+#endif
+}
+
+struct rom_hdr {
+	/* 000 */ unsigned char iwave[8];
+	/* 008 */ unsigned char rom_hdr_revision;
+	/* 009 */ unsigned char series_number;
+	/* 010 */ unsigned char series_name[16];
+	/* 026 */ unsigned char date[10];
+	/* 036 */ unsigned short vendor_revision_major;
+	/* 038 */ unsigned short vendor_revision_minor;
+	/* 040 */ unsigned int rom_size;
+	/* 044 */ unsigned char copyright[128];
+	/* 172 */ unsigned char vendor_name[64];
+	/* 236 */ unsigned char rom_description[128];
+	/* 364 */ unsigned char pad[147];
+	/* 511 */ unsigned char csum;
+};
+
+static void __init snd_interwave_detect_memory(snd_gus_card_t * gus)
+{
+	static unsigned int lmc[13] =
+	{
+		0x00000001, 0x00000101, 0x01010101, 0x00000401,
+		0x04040401, 0x00040101, 0x04040101, 0x00000004,
+		0x00000404, 0x04040404, 0x00000010, 0x00001010,
+		0x10101010
+	};
+
+	int bank_pos, pages;
+	unsigned int i, lmct;
+	int psizes[4];
+	unsigned char csum;
+	struct rom_hdr romh;
+
+	snd_interwave_reset(gus);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);		/* enhanced mode */
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);	/* DRAM I/O cycles selected */
+	snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c);
+	/* ok.. simple test of memory size */
+	pages = 0;
+	snd_gf1_poke(gus, 0, 0x55);
+	snd_gf1_poke(gus, 1, 0xaa);
+#if 1
+	if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa)
+#else
+	if (0)			/* ok.. for testing of 0k RAM */
+#endif
+	{
+		snd_interwave_bank_sizes(gus, psizes);
+		lmct = (psizes[3] << 24) | (psizes[2] << 16) |
+		    (psizes[1] << 8) | psizes[0];
+#if 0
+		printk("lmct = 0x%08x\n", lmct);
+#endif
+		for (i = 0; i < sizeof(lmc) / sizeof(unsigned int); i++)
+			if (lmct == lmc[i]) {
+#if 0
+				printk("found !!! %i\n", i);
+#endif
+				snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
+				snd_interwave_bank_sizes(gus, psizes);
+				break;
+			}
+		if (i >= sizeof(lmc) / sizeof(unsigned int) && !gus->gf1.enh_mode)
+			 snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2);
+		for (i = 0; i < 4; i++) {
+			gus->gf1.mem_alloc.banks_8[i].address =
+			    gus->gf1.mem_alloc.banks_16[i].address = i << 22;
+			gus->gf1.mem_alloc.banks_8[i].size =
+			    gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18;
+			pages += psizes[i];
+		}
+	}
+	pages <<= 18;
+	gus->gf1.memory = pages;
+
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03);	/* select ROM */
+	snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5));
+	gus->gf1.rom_banks = 0;
+	gus->gf1.rom_memory = 0;
+	for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
+		for (i = 0; i < sizeof(struct rom_hdr); i++)
+			*(((unsigned char *) &romh) + i) = snd_gf1_peek(gus, i + bank_pos);
+#ifdef CONFIG_SND_DEBUG_ROM
+		printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos,
+		       romh.iwave[0], romh.iwave[1], romh.iwave[2], romh.iwave[3],
+		       romh.iwave[4], romh.iwave[5], romh.iwave[6], romh.iwave[7]);
+#endif
+		if (strncmp(romh.iwave, "INTRWAVE", 8))
+			continue;	/* first check */
+		csum = 0;
+		for (i = 0; i < sizeof(struct rom_hdr) - 1; i++)
+			csum += *(((unsigned char *) &romh) + i);
+#ifdef CONFIG_SND_DEBUG_ROM
+		printk("ROM checksum = 0x%x == 0x%x (computed)\n", romh.csum, (unsigned char) (256 - csum));
+#endif
+		if (256 - csum != romh.csum)
+			continue;	/* not valid rom */
+		gus->gf1.rom_banks++;
+		gus->gf1.rom_present |= 1 << (bank_pos >> 22);
+#ifdef SNDRV_LITTLE_ENDIAN
+		gus->gf1.rom_memory = romh.rom_size;
+#else
+		gus->gf1.rom_memory = ((romh.rom_size >> 24) & 0x000000ff) |
+				      ((romh.rom_size >>  8) & 0x0000ff00) |
+				      ((romh.rom_size <<  8) & 0x00ff0000) |
+				      ((romh.rom_size << 24) & 0xff000000);
+#endif
+	}
+#if 0
+	if (gus->gf1.rom_memory > 0) {
+		if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8)
+			gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC;
+	}
+#endif
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00);	/* select RAM */
+
+	if (!gus->gf1.enh_mode)
+		snd_interwave_reset(gus);
+}
+
+static void __init snd_interwave_init(int dev, snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	/* ok.. some InterWave specific initialization */
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	gus->equal_irq = 1;
+	gus->codec_flag = 1;
+	gus->interwave = 1;
+	gus->max_flag = 1;
+	gus->joystick_dac = snd_joystick_dac[dev];
+
+}
+
+#define INTERWAVE_CONTROLS (sizeof(snd_interwave_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_interwave_controls[] = {
+CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
+};
+
+static int __init snd_interwave_mixer(cs4231_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int idx, err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+#if 0
+	/* remove mono microphone controls */
+	strcpy(id1.name, "Mic Playback Switch");
+	if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+		return err;
+	strcpy(id1.name, "Mic Playback Volume");
+	if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+		return err;
+#endif
+	/* add new master and mic controls */
+	for (idx = 0; idx < INTERWAVE_CONTROLS; idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0)
+			return err;
+	snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
+	snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
+	snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
+	snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
+	/* reassign AUXA to SYNTHESIZER */
+	strcpy(id1.name, "Aux Playback Switch");
+	strcpy(id2.name, "Synth Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Synth Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign AUXB to CD */
+	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+	strcpy(id2.name, "CD Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "CD Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+
+static int __init snd_interwave_isapnp(int dev, struct snd_interwave *iwcard)
+{
+	const struct isapnp_card_id *id = snd_interwave_isapnp_id[dev];
+	struct isapnp_card *card = snd_interwave_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	iwcard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (iwcard->dev->active) {
+		iwcard->dev = NULL;
+		return -EBUSY;
+	}
+#ifdef SNDRV_STB
+	iwcard->devtc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (iwcard->devtc->active) {
+		iwcard->dev = iwcard->devtc = NULL;
+		return -EBUSY;
+	}
+#endif
+	/* Synth & Codec initialization */
+	pdev = iwcard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+	if (snd_port[dev] != SNDRV_AUTO_PORT) {
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+		isapnp_resource_change(&pdev->resource[1], snd_port[dev] + 0x100, 12);
+		isapnp_resource_change(&pdev->resource[2], snd_port[dev] + 0x10c, 4);
+	}
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_dma2[dev] < 0)
+		isapnp_resource_change(&pdev->dma_resource[1], 4, 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("isapnp configure failure (out of resources?)\n");
+		return -EBUSY;
+	}
+	if (pdev->resource[0].start + 0x100 != pdev->resource[1].start ||
+	    pdev->resource[0].start + 0x10c != pdev->resource[2].start) {
+		snd_printk("isapnp configure failure (wrong ports)\n");
+		pdev->deactivate(pdev);
+		return -ENOENT;
+	}
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	if (snd_dma2[dev] >= 0)
+		snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n",
+				pdev->resource[0].start,
+				pdev->resource[1].start,
+				pdev->resource[2].start);
+	snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]);
+#ifdef SNDRV_STB
+	/* Tone Control initialization */
+	pdev = iwcard->devtc;
+	if (pdev->prepare(pdev)<0) {
+		iwcard->dev->deactivate(iwcard->dev);
+		return -EAGAIN;
+	}
+	if (snd_port_tc[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port_tc[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("Tone Control isapnp configure failure (out of resources?)\n");
+		iwcard->dev->deactivate(iwcard->dev);
+		return -EBUSY;
+	}
+	snd_port_tc[dev] = pdev->resource[0].start;
+	snd_printdd("isapnp IW: tone control port=0x%lx\n", snd_port_tc[dev]);
+#endif
+	return 0;
+}
+
+static void snd_interwave_deactivate(struct snd_interwave *iwcard)
+{
+	if (iwcard->dev) {
+		iwcard->dev->deactivate(iwcard->dev);
+		iwcard->dev = NULL;
+	}
+#ifdef SNDRV_STB
+	if (iwcard->devtc) {
+		iwcard->devtc->deactivate(iwcard->devtc);
+		iwcard->devtc = NULL;
+	}
+#endif
+}
+
+#endif /* __ISAPNP__ */
+
+static void snd_interwave_free(snd_card_t *card)
+{
+	struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data;
+
+	if (iwcard == NULL)
+		return;
+#ifdef __ISAPNP__
+	snd_interwave_deactivate(iwcard);
+#endif
+#ifdef SNDRV_STB
+	if (iwcard->i2c_res) {
+		release_resource(iwcard->i2c_res);
+		kfree_nocheck(iwcard->i2c_res);
+	}
+#endif
+	if (iwcard->irq >= 0)
+		free_irq(iwcard->irq, (void *)iwcard);
+}
+
+static int __init snd_interwave_probe(int dev)
+{
+	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+	static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_interwave *iwcard;
+	cs4231_t *cs4231;
+	snd_gus_card_t *gus;
+#ifdef SNDRV_STB
+	snd_i2c_bus_t *i2c_bus;
+#endif
+	snd_pcm_t *pcm;
+	char *str;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_interwave));
+	if (card == NULL)
+		return -ENOMEM;
+	iwcard = (struct snd_interwave *)card->private_data;
+	iwcard->card = card;
+	iwcard->irq = -1;
+	card->private_free = snd_interwave_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && snd_interwave_isapnp(dev, iwcard)) {
+		snd_card_free(card);
+		return -ENODEV;
+	}
+#endif
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+	if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	dma2 = snd_dma2[dev];
+	if (dma2 == SNDRV_AUTO_DMA) {
+		if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_gus_create(card,
+				  snd_port[dev],
+				  -irq, dma1, dma2,
+				  0, 32,
+				  snd_pcm_channels[dev], snd_effect[dev], &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_interwave_detect(iwcard, gus, dev
+#ifdef SNDRV_STB
+            , &i2c_bus
+#endif
+	    )) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	iwcard->gus_status_reg = gus->gf1.reg_irqstat;
+	iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
+
+	snd_interwave_init(dev, gus);
+	snd_interwave_detect_memory(gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (request_irq(irq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) {
+		snd_card_free(card);
+		snd_printk("unable to grab IRQ %d\n", irq);
+		return -EBUSY;
+	}
+	iwcard->irq = irq;
+
+	if ((err = snd_cs4231_create(card,
+				     gus->gf1.port + 0x10c, -1, irq,
+				     dma2 < 0 ? dma1 : dma2, dma1,
+				     CS4231_HW_INTERWAVE,
+				     CS4231_HWSHARE_IRQ |
+				     CS4231_HWSHARE_DMA1 |
+				     CS4231_HWSHARE_DMA2,
+				     &cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
+	strcat(pcm->name, " (chip)");
+	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_pcm_channels[dev] > 0) {
+		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_interwave_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef SNDRV_STB
+	{
+		snd_ctl_elem_id_t id1, id2;
+		memset(&id1, 0, sizeof(id1));
+		memset(&id2, 0, sizeof(id2));
+		id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		strcpy(id1.name, "Master Playback Switch");
+		strcpy(id2.name, id1.name);
+		id2.index = 1;
+		if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		strcpy(id1.name, "Master Playback Volume");
+		strcpy(id2.name, id1.name);
+		if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+#endif
+
+	gus->uart_enable = snd_midi[dev];
+	if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+#ifndef SNDRV_STB
+	str = "AMD InterWave";
+	if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8)
+		str = "Dynasonic 3-D";
+#else
+	str = "InterWave STB";
+#endif
+	strcpy(card->driver, str);
+	strcpy(card->shortname, str);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d",
+		str,
+		gus->gf1.port,
+		irq,
+		dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	iwcard->cs4231 = cs4231;
+	iwcard->gus = gus;
+	snd_interwave_cards[dev++] = card;
+	return 0;
+}
+
+static int __init snd_interwave_probe_legacy_port(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+                        continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		snd_port[dev] = port;
+		res = snd_interwave_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+#ifdef __ISAPNP__
+
+static int __init snd_interwave_isapnp_detect(struct isapnp_card *card,
+					      const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_interwave_isapnp_cards[dev] = card;
+		snd_interwave_isapnp_id[dev] = id;
+		res = snd_interwave_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+
+        return -ENODEV;
+}
+
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_interwave_init(void)
+{
+	int cards = 0;
+	static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1};
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (!snd_interwave_probe(dev)) {
+			cards++;
+			continue;
+		}
+#ifdef MODULE
+		printk(KERN_ERR "InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]);
+#endif
+	}
+	/* legacy auto configured cards */
+	cards += snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port);
+#ifdef __ISAPNP__
+        /* ISA PnP cards */
+        cards += isapnp_probe_cards(snd_interwave_pnpids, snd_interwave_isapnp_detect);
+#endif
+
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "InterWave soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_interwave_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_interwave_cards[dev]);
+}
+
+module_init(alsa_card_interwave_init)
+module_exit(alsa_card_interwave_exit)
+
+#ifndef MODULE
+
+/* format is: snd-interwave=snd_enable,snd_index,snd_id,snd_isapnp,
+			    snd_port[,snd_port_tc],snd_irq,
+			    snd_dma1,snd_dma2,
+			    snd_joystick_dac,snd_midi,
+			    snd_pcm_channels,snd_effect */
+
+static int __init alsa_card_interwave_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+#ifdef SNDRV_STB
+	       get_option(&str,(int *)&snd_port_tc[nr_dev]) == 2 &&
+#endif
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_joystick_dac[nr_dev]) == 2 &&
+	       get_option(&str,&snd_midi[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_effect[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+#ifndef SNDRV_STB
+__setup("snd-interwave=", alsa_card_interwave_setup);
+#else
+__setup("snd-interwave-stb=", alsa_card_interwave_setup);
+#endif
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/opl3sa2.c linux-2.4.19-pre5-mjc/sound/isa/opl3sa2.c
--- linux/sound/isa/opl3sa2.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/opl3sa2.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,951 @@
+/*
+ *  Driver for Yamaha OPL3-SA[2,3] soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Yamaha OPL3SA2+");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Yamaha,YMF719E-S},"
+		"{Genius,Sound Maker 3DX},"
+		"{Yamaha,OPL3SA3},"
+		"{Intel,AL440LX sound},"
+	        "{NeoMagic,MagicWave 3DX}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+#ifdef __ISAPNP__
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0xf86,0x370,0x100 */
+static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x388 */
+static long snd_midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 0,1,3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 };   /* 0,1,2,3 */ /*SL Added*/
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for OPL3-SA soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for OPL3-SA soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable OPL3-SA soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0xf86},{0x370},{0x100}},dialog:list");
+MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sb_port, "SB port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260}},dialog:list");
+MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wss_port, "WSS port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388}},dialog:list");
+MODULE_PARM(snd_midi_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_midi_port, "MIDI port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_midi_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{0},{1},{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_opl3sa3_ymode, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); /* SL Added */
+MODULE_PARM_DESC(snd_opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi.");
+MODULE_PARM_SYNTAX(snd_opl3sa3_ymode, SNDRV_ENABLED ",allows:{{0,3}},dialog:list");  /* SL Added */
+
+/* control ports */
+#define OPL3SA2_PM_CTRL		0x01
+#define OPL3SA2_SYS_CTRL		0x02
+#define OPL3SA2_IRQ_CONFIG	0x03
+#define OPL3SA2_IRQ_STATUS	0x04
+#define OPL3SA2_DMA_CONFIG	0x06
+#define OPL3SA2_MASTER_LEFT	0x07
+#define OPL3SA2_MASTER_RIGHT	0x08
+#define OPL3SA2_MIC		0x09
+#define OPL3SA2_MISC		0x0A
+
+/* opl3sa3 only */
+#define OPL3SA3_DGTL_DOWN	0x12
+#define OPL3SA3_ANLG_DOWN	0x13
+#define OPL3SA3_WIDE		0x14
+#define OPL3SA3_BASS		0x15
+#define OPL3SA3_TREBLE		0x16
+
+/* power management bits */
+#define OPL3SA2_PM_ADOWN		0x20
+#define OPL3SA2_PM_PSV		0x04		
+#define OPL3SA2_PM_PDN		0x02
+#define OPL3SA2_PM_PDX		0x01
+
+#define OPL3SA2_PM_D0	0x00
+#define OPL3SA2_PM_D3	(OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX)
+
+typedef struct snd_opl3sa2 opl3sa2_t;
+#define chip_t opl3sa2_t
+
+struct snd_opl3sa2 {
+	snd_card_t *card;
+	int version;		/* 2 or 3 */
+	unsigned long port;	/* control port */
+	struct resource *res_port; /* control port resource */
+	int irq;
+	int single_dma;
+	spinlock_t reg_lock;
+	snd_hwdep_t *synth;
+	snd_rawmidi_t *rmidi;
+	cs4231_t *cs4231;
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+#endif
+	unsigned char ctlregs[0x20];
+	int ymode;		/* SL added */
+	snd_kcontrol_t *master_switch;
+	snd_kcontrol_t *master_volume;
+#ifdef CONFIG_PM
+	struct pm_dev *pm_dev;
+	void (*cs4231_suspend)(cs4231_t *);
+	void (*cs4231_resume)(cs4231_t *);
+#endif
+};
+
+static snd_card_t *snd_opl3sa2_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_opl3sa2_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_opl3sa2_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_OPL3SA2(_va, _vb, _vc, _device, _function) \
+        { \
+                ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _function), } \
+        }
+
+static struct isapnp_card_id snd_opl3sa2_pnpids[] __devinitdata = {
+	/* Yamaha YMF719E-S (Genius Sound Maker 3DX) */
+	ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021),
+	/* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */
+	ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021),
+	/* Yamaha OPL3-SA2 */
+	ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021),
+	/* NeoMagic MagicWave 3DX */
+	ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210),
+	/* --- */
+	{ ISAPNP_CARD_END, }	/* end */
+};
+
+ISAPNP_CARD_TABLE(snd_opl3sa2_pnpids);
+
+#endif /* __ISAPNP__ */
+
+
+/* read control port (w/o spinlock) */
+static unsigned char __snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg)
+{
+	unsigned char result;
+#if 0
+	outb(0x1d, port);	/* password */
+	printk("read [0x%lx] = 0x%x\n", port, inb(port));
+#endif
+	outb(reg, chip->port);	/* register */
+	result = inb(chip->port + 1);
+#if 0
+	printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port));
+#endif
+	return result;
+}
+
+/* read control port (with spinlock) */
+static unsigned char snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char result;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	result = __snd_opl3sa2_read(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return result;
+}
+
+/* write control port (w/o spinlock) */
+static void __snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value)
+{
+#if 0
+	outb(0x1d, port);	/* password */
+#endif
+	outb(reg, chip->port);	/* register */
+	outb(value, chip->port + 1);
+	chip->ctlregs[reg] = value;
+}
+
+/* write control port (with spinlock) */
+static void snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	__snd_opl3sa2_write(chip, reg, value);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static int __init snd_opl3sa2_detect(opl3sa2_t *chip)
+{
+	snd_card_t *card;
+	unsigned long port;
+	unsigned char tmp, tmp1;
+	char str[2];
+
+	card = chip->card;
+	port = chip->port;
+	if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL)
+		return -EBUSY;
+	// snd_printk("REG 0A = 0x%x\n", snd_opl3sa2_read(chip, 0x0a));
+	chip->version = 0;
+	tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC);
+	if (tmp == 0xff) {
+		snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
+		return -ENODEV;
+	}
+	switch (tmp & 0x07) {
+	case 0x01:
+		chip->version = 2; /* YMF711 */
+		break;
+	default:
+		chip->version = 3;
+		/* 0x02 - standard */
+		/* 0x03 - YM715B */
+		/* 0x04 - YM719 - OPL-SA4? */
+		/* 0x05 - OPL3-SA3 - Libretto 100 */
+		break;
+	}
+	str[0] = chip->version + '0';
+	str[1] = 0;
+	strcat(card->shortname, str);
+	snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7);
+	if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) {
+		snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
+		return -ENODEV;
+	}
+	/* try if the MIC register is accesible */
+	tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC);
+	snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a);
+	if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) {
+		snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
+		return -ENODEV;
+	}
+	snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f);
+	/* initialization */
+	/* Power Management - full on */
+	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0);
+	if (chip->version > 2) {
+		/* ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */
+		snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, (chip->ymode << 4));
+	} else {
+		/* default for opl3sa2 versions */
+		snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, 0x00);
+	}
+	snd_opl3sa2_write(chip, OPL3SA2_IRQ_CONFIG, 0x0d);	/* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */
+	if (chip->single_dma) {
+		snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x03);	/* DMA Configuration - DMA A = WSS-R + WSS-P */
+	} else {
+		snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x21);	/* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */
+	}
+	snd_opl3sa2_write(chip, OPL3SA2_MISC, 0x80 | (tmp & 7));	/* Miscellaneous - default */
+	if (chip->version > 2) {
+		snd_opl3sa2_write(chip, OPL3SA3_DGTL_DOWN, 0x00);	/* Digital Block Partial Power Down - default */
+		snd_opl3sa2_write(chip, OPL3SA3_ANLG_DOWN, 0x00);	/* Analog Block Partial Power Down - default */
+	}
+	return 0;
+}
+
+static void snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned short status;
+	opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev_id, return);
+
+	if (chip == NULL || chip->card == NULL)
+		return;
+
+	status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS);
+
+	if (status & 0x20)
+		snd_opl3_interrupt(chip->synth);
+
+	if ((status & 0x10) && chip->rmidi != NULL)
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+
+	if (status & 0x07)	/* TI,CI,PI */
+		snd_cs4231_interrupt(irq, chip->cs4231, regs);
+
+	if (status & 0x40) { /* hardware volume change */
+		/* reading from Master Lch register at 0x07 clears this bit */
+		snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT);
+		snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT);
+		if (chip->master_switch && chip->master_volume) {
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+		}
+	}
+}
+
+#define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opl3sa2_info_single, \
+  get: snd_opl3sa2_get_single, put: snd_opl3sa2_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_opl3sa2_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_opl3sa2_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opl3sa2_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+int snd_opl3sa2_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opl3sa2_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val, oval;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = chip->ctlregs[reg];
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	__snd_opl3sa2_write(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opl3sa2_info_double, \
+  get: snd_opl3sa2_get_double, put: snd_opl3sa2_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+int snd_opl3sa2_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_opl3sa2_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opl3sa2_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+int snd_opl3sa2_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opl3sa2_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2, oval1, oval2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		oval1 = chip->ctlregs[left_reg];
+		oval2 = chip->ctlregs[right_reg];
+		val1 = (oval1 & ~(mask << shift_left)) | val1;
+		val2 = (oval2 & ~(mask << shift_right)) | val2;
+		change = val1 != oval1 || val2 != oval2;
+		__snd_opl3sa2_write(chip, left_reg, val1);
+		__snd_opl3sa2_write(chip, right_reg, val2);
+	} else {
+		oval1 = chip->ctlregs[left_reg];
+		val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != oval1;
+		__snd_opl3sa2_write(chip, left_reg, val1);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define OPL3SA2_CONTROLS (sizeof(snd_opl3sa2_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_opl3sa2_controls[] = {
+OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
+OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1),
+OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
+OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1)
+};
+
+#define OPL3SA2_TONE_CONTROLS (sizeof(snd_opl3sa2_tone_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_opl3sa2_tone_controls[] = {
+OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0),
+OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0),
+OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0)
+};
+
+static void snd_opl3sa2_master_free(snd_kcontrol_t *kcontrol)
+{
+	opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, _snd_kcontrol_chip(kcontrol), return);
+	chip->master_switch = NULL;
+	chip->master_volume = NULL;
+}
+
+static int __init snd_opl3sa2_mixer(opl3sa2_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	snd_kcontrol_t *kctl;
+	int idx, err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUX0 to CD */
+        strcpy(id1.name, "Aux Playback Switch");
+        strcpy(id2.name, "CD Playback Switch");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+        strcpy(id1.name, "Aux Playback Volume");
+        strcpy(id2.name, "CD Playback Volume");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+	/* reassign AUX1 to FM */
+        strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+        strcpy(id2.name, "FM Playback Switch");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+        strcpy(id1.name, "Aux Playback Volume");
+        strcpy(id2.name, "FM Playback Volume");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+	/* add OPL3SA2 controls */
+	for (idx = 0; idx < OPL3SA2_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0)
+			return err;
+		switch (idx) {
+		case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break;
+		case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break;
+		}
+	}
+	if (chip->version > 2) {
+		for (idx = 0; idx < OPL3SA2_TONE_CONTROLS; idx++)
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0)
+				return err;
+	}
+	return 0;
+}
+
+/* Power Management support functions */
+#ifdef CONFIG_PM
+static void snd_opl3sa2_suspend(opl3sa2_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	/* FIXME: is this order ok? */
+	chip->cs4231_suspend(chip->cs4231);
+	snd_pcm_suspend_all(chip->cs4231->pcm);
+
+	/* power down */
+	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void snd_opl3sa2_resume(opl3sa2_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int i;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	/* power up */
+	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0);
+
+	/* restore registers */
+	for (i = 2; i <= 0x0a; i++) {
+		if (i != OPL3SA2_IRQ_STATUS)
+			snd_opl3sa2_write(chip, i, chip->ctlregs[i]);
+	}
+	if (chip->version > 2) {
+		for (i = 0x12; i <= 0x16; i++)
+			snd_opl3sa2_write(chip, i, chip->ctlregs[i]);
+	}
+	/* restore cs4231 */
+	chip->cs4231_resume(chip->cs4231);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+/* callback for control API */
+static int snd_opl3sa2_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	opl3sa2_t *chip = (opl3sa2_t *) card->power_state_private_data;
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_opl3sa2_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_opl3sa2_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_opl3sa2_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev->data, return 0);
+
+	switch (rqst) {
+	case PM_SUSPEND:
+		snd_opl3sa2_suspend(chip);
+		break;
+	case PM_RESUME:
+		snd_opl3sa2_resume(chip);
+		break;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+#ifdef __ISAPNP__
+static int __init snd_opl3sa2_isapnp(int dev, opl3sa2_t *chip)
+{
+        const struct isapnp_card_id *id = snd_opl3sa2_isapnp_id[dev];
+        struct isapnp_card *card = snd_opl3sa2_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	chip->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (chip->dev->active) {
+		chip->dev = NULL;
+		return -EBUSY;
+	}
+	/* PnP initialization */
+	pdev = chip->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_sb_port[dev], 16);
+	if (snd_wss_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_wss_port[dev], 8);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4);
+	if (snd_midi_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[3], snd_midi_port[dev], 2);
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[4], snd_port[dev], 2);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("isapnp configure failure (out of resources?)\n");
+		return -EBUSY;
+	}
+	snd_sb_port[dev] = pdev->resource[0].start;
+	snd_wss_port[dev] = pdev->resource[1].start;
+	snd_fm_port[dev] = pdev->resource[2].start;
+	snd_midi_port[dev] = pdev->resource[3].start;
+	snd_port[dev] = pdev->resource[4].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
+		snd_sb_port[dev], snd_wss_port[dev], snd_fm_port[dev], snd_midi_port[dev]);
+	snd_printdd("isapnp OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
+		snd_port[dev], snd_dma1[dev], snd_dma2[dev], snd_irq[dev]);
+	return 0;
+}
+
+static void snd_opl3sa2_deactivate(opl3sa2_t *chip)
+{
+	if (chip->dev) {
+		chip->dev->deactivate(chip->dev);
+		chip->dev = NULL;
+	}
+}
+#endif /* __ISAPNP__ */
+
+static int snd_opl3sa2_free(opl3sa2_t *chip)
+{
+#ifdef __ISAPNP__
+	snd_opl3sa2_deactivate(chip);
+#endif
+#ifdef CONFIG_PM
+	if (chip->pm_dev)
+		pm_unregister(chip->pm_dev);
+#endif
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_opl3sa2_dev_free(snd_device_t *device)
+{
+	opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, device->device_data, return -ENXIO);
+	return snd_opl3sa2_free(chip);
+}
+
+static int __init snd_opl3sa2_probe(int dev)
+{
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_opl3sa2 *chip;
+	cs4231_t *cs4231;
+	opl3_t *opl3;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_opl3sa2_dev_free,
+	};
+	int err;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_port\n");
+			return -EINVAL;
+		}
+		if (snd_wss_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_wss_port\n");
+			return -EINVAL;
+		}
+		if (snd_fm_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_fm_port\n");
+			return -EINVAL;
+		}
+		if (snd_midi_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_midi_port\n");
+			return -EINVAL;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	strcpy(card->driver, "OPL3SA2");
+	strcpy(card->shortname, "Yamaha OPL3-SA2");
+	chip = snd_magic_kcalloc(opl3sa2_t, 0, GFP_KERNEL);
+	if (chip == NULL) {
+		err = -ENOMEM;
+		goto __error;
+	}
+	chip->irq = -1;
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+		goto __error;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_opl3sa2_isapnp(dev, chip)) < 0)
+		goto __error;
+#endif
+	chip->ymode = snd_opl3sa3_ymode[dev] & 0x03 ; /* initialise this card from supplied (or default) parameter*/ 
+	chip->card = card;
+	chip->port = snd_port[dev];
+	irq = snd_irq[dev];
+	dma1 = snd_dma1[dev];
+	dma2 = snd_dma2[dev];
+	if (dma2 < 0)
+		chip->single_dma = 1;
+	if ((err = snd_opl3sa2_detect(chip)) < 0)
+		goto __error;
+	if (request_irq(irq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2/3", (void *)chip)) {
+		err = -ENODEV;
+		goto __error;
+	}
+	chip->irq = irq;
+	if ((err = snd_cs4231_create(card,
+				     snd_wss_port[dev] + 4, -1,
+				     irq, dma1, dma2,
+				     CS4231_HW_OPL3SA2,
+				     CS4231_HWSHARE_IRQ,
+				     &cs4231)) < 0) {
+		snd_printd("Oops, WSS not detected at 0x%lx\n", snd_wss_port[dev] + 4);
+		goto __error;
+	}
+	chip->cs4231 = cs4231;
+	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0)
+		goto __error;
+	if ((err = snd_cs4231_mixer(cs4231)) < 0)
+		goto __error;
+	if ((err = snd_opl3sa2_mixer(chip)) < 0)
+		goto __error;
+	if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0)
+		goto __error;
+	if (snd_fm_port[dev] >= 0x340 && snd_fm_port[dev] < 0x400) {
+		if ((err = snd_opl3_create(card, snd_fm_port[dev],
+					   snd_fm_port[dev] + 2,
+					   OPL3_HW_OPL3, 0, &opl3)) < 0)
+			goto __error;
+		if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0)
+			goto __error;
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0)
+			goto __error;
+	}
+	if (snd_midi_port[dev] >= 0x300 && snd_midi_port[dev] < 0x340) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
+					       snd_midi_port[dev], 0,
+					       irq, 0, &chip->rmidi)) < 0)
+			goto __error;
+	}
+#ifdef CONFIG_PM
+	/* Power Management */
+	chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_opl3sa2_pm_callback);
+	if (chip->pm_dev) {
+		chip->pm_dev->data = chip;
+		/* remember callbacks for cs4231 - they are called inside
+		 * opl3sa2 pm callback
+		 */
+		chip->cs4231_suspend = chip->cs4231->suspend;
+		chip->cs4231_resume = chip->cs4231->resume;
+		/* now clear callbacks for cs4231 */
+		chip->cs4231->suspend = NULL;
+		chip->cs4231->resume = NULL;
+		/* set control api callback */
+		card->set_power_state = snd_opl3sa2_set_power_state;
+		card->power_state_private_data = chip;
+	}
+#endif
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		card->shortname, chip->port, irq, dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2);
+
+	if ((err = snd_card_register(card)) < 0)
+		goto __error;
+
+	snd_opl3sa2_cards[dev] = card;
+	return 0;
+
+ __error:
+	snd_card_free(card);
+	return err;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_opl3sa2_isapnp_detect(struct isapnp_card *card,
+					    const struct isapnp_card_id *id)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev])
+                        continue;
+                snd_opl3sa2_isapnp_cards[dev] = card;
+                snd_opl3sa2_isapnp_id[dev] = id;
+                res = snd_opl3sa2_probe(dev);
+                if (res < 0)
+                        return res;
+                dev++;
+                return 0;
+        }
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_opl3sa2_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_opl3sa2_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_opl3sa2_pnpids, snd_opl3sa2_isapnp_detect);
+#endif
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_opl3sa2_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_opl3sa2_cards[idx]);
+}
+
+module_init(alsa_card_opl3sa2_init)
+module_exit(alsa_card_opl3sa2_exit)
+
+#ifndef MODULE
+
+/* format is: snd-opl3sa2=snd_enable,snd_index,snd_id,snd_isapnp,
+			  snd_port,snd_sb_port,snd_wss_port,snd_fm_port,
+			  snd_midi_port,snd_irq,snd_dma1,snd_dma2,
+			  snd_opl3sa3_ymode */
+
+static int __init alsa_card_opl3sa2_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_midi_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_opl3sa3_ymode[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-opl3sa2=", alsa_card_opl3sa2_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/opti9xx/Makefile linux-2.4.19-pre5-mjc/sound/isa/opti9xx/Makefile
--- linux/sound/isa/opti9xx/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,28 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _opti9xx.o
+
+list-multi   := snd-opti92x-ad1848.o snd-opti92x-cs4231.o snd-opti93x.o
+
+snd-opti92x-ad1848-objs := opti92x-ad1848.o
+snd-opti92x-cs4231-objs := opti92x-cs4231.o
+snd-opti93x-objs := opti93x.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
+obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o
+obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o
+
+include $(TOPDIR)/Rules.make
+
+snd-opti92x-ad1848.o: $(snd-opti92x-ad1848-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-ad1848-objs)
+
+snd-opti92x-cs4231.o: $(snd-opti92x-cs4231-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-cs4231-objs)
+
+snd-opti93x.o: $(snd-opti93x-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-opti93x-objs)
diff -Nru linux/sound/isa/opti9xx/opti92x-ad1848.c linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-ad1848.c
--- linux/sound/isa/opti9xx/opti92x-ad1848.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-ad1848.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2218 @@
+/*
+    card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
+    Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    Part of this code was developed at the Italian Ministry of Air Defence,
+    Sixth Division (oh, che pace ...), Rome.
+
+    Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
+
+    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
+*/
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#ifdef CS4231
+#include <sound/cs4231.h>
+#else
+#ifndef OPTi93X
+#include <sound/ad1848.h>
+#else
+#include <sound/control.h>
+#include <sound/pcm.h>
+#endif	/* OPTi93X */
+#endif	/* CS4231 */
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+#ifdef OPTi93X
+MODULE_DESCRIPTION("OPTi93X");
+MODULE_DEVICES("{{OPTi,82C931/3}}");
+#else	/* OPTi93X */
+#ifdef CS4231
+MODULE_DESCRIPTION("OPTi92X - CS4231");
+MODULE_DEVICES("{{OPTi,82C924 (CS4231)},"
+		"{OPTi,82C925 (CS4231)}}");
+#else	/* CS4231 */
+MODULE_DESCRIPTION("OPTi92X - AD1848");
+MODULE_DEVICES("{{OPTi,82C924 (AD1848)},"
+		"{OPTi,82C925 (AD1848)},"
+	        "{OAK,Mozart}}");
+#endif	/* CS4231 */
+#endif	/* OPTi93X */
+
+static int snd_index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
+static char *snd_id = SNDRV_DEFAULT_STR1;		/* ID for this card */
+//static int snd_enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
+static int snd_isapnp = 1;			/* Enable ISA PnP detection */
+static long snd_port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
+static long snd_mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
+static long snd_fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
+static int snd_irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
+static int snd_mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
+static int snd_dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
+#if defined(CS4231) || defined(OPTi93X)
+static int snd_dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
+#endif	/* CS4231 || OPTi93X */
+
+MODULE_PARM(snd_index, "i");
+MODULE_PARM_DESC(snd_index, "Index value for opti9xx based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "s");
+MODULE_PARM_DESC(snd_id, "ID string for opti9xx based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+//MODULE_PARM(snd_enable, "i");
+//MODULE_PARM_DESC(snd_enable, "Enable opti9xx soundcard.");
+//MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_isapnp, "i");
+MODULE_PARM_DESC(snd_isapnp, "Enable ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+MODULE_PARM(snd_port, "l");
+MODULE_PARM_DESC(snd_port, "WSS port # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_mpu_port, "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_fm_port, "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_irq, "i");
+MODULE_PARM_DESC(snd_irq, "WSS irq # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 irq # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "i");
+MODULE_PARM_DESC(snd_dma1, "1st dma # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+#if defined(CS4231) || defined(OPTi93X)
+MODULE_PARM(snd_dma2, "i");
+MODULE_PARM_DESC(snd_dma2, "2nd dma # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+#endif	/* CS4231 || OPTi93X */
+
+#define OPTi9XX_HW_DETECT	0
+#define OPTi9XX_HW_82C928	1
+#define OPTi9XX_HW_82C929	2
+#define OPTi9XX_HW_82C924	3
+#define OPTi9XX_HW_82C925	4
+#define OPTi9XX_HW_82C930	5
+#define OPTi9XX_HW_82C931	6
+#define OPTi9XX_HW_82C933	7
+#define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
+
+#define OPTi9XX_MC_REG(n)	n
+
+typedef struct _snd_opti9xx opti9xx_t;
+
+#ifdef OPTi93X
+
+#define OPTi93X_INDEX			0x00
+#define OPTi93X_DATA			0x01
+#define OPTi93X_STATUS			0x02
+#define OPTi93X_DDATA			0x03
+#define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
+
+#define OPTi93X_MIXOUT_LEFT		0x00
+#define OPTi93X_MIXOUT_RIGHT		0x01
+#define OPTi93X_CD_LEFT_INPUT		0x02
+#define OPTi93X_CD_RIGHT_INPUT		0x03
+#define OPTi930_AUX_LEFT_INPUT		0x04
+#define OPTi930_AUX_RIGHT_INPUT		0x05
+#define OPTi931_FM_LEFT_INPUT		0x04
+#define OPTi931_FM_RIGHT_INPUT		0x05
+#define OPTi93X_DAC_LEFT		0x06
+#define OPTi93X_DAC_RIGHT		0x07
+#define OPTi93X_PLAY_FORMAT		0x08
+#define OPTi93X_IFACE_CONF		0x09
+#define OPTi93X_PIN_CTRL		0x0a
+#define OPTi93X_ERR_INIT		0x0b
+#define OPTi93X_ID			0x0c
+#define OPTi93X_PLAY_UPR_CNT		0x0e
+#define OPTi93X_PLAY_LWR_CNT		0x0f
+#define OPTi931_AUX_LEFT_INPUT		0x10
+#define OPTi931_AUX_RIGHT_INPUT		0x11
+#define OPTi93X_LINE_LEFT_INPUT		0x12
+#define OPTi93X_LINE_RIGHT_INPUT	0x13
+#define OPTi93X_MIC_LEFT_INPUT		0x14
+#define OPTi93X_MIC_RIGHT_INPUT		0x15
+#define OPTi93X_OUT_LEFT		0x16
+#define OPTi93X_OUT_RIGHT		0x17
+#define OPTi93X_CAPT_FORMAT		0x1c
+#define OPTi93X_CAPT_UPR_CNT		0x1e
+#define OPTi93X_CAPT_LWR_CNT		0x1f
+
+#define OPTi93X_TRD			0x20
+#define OPTi93X_MCE			0x40
+#define OPTi93X_INIT			0x80
+
+#define OPTi93X_MIXOUT_MIC_GAIN		0x20
+#define OPTi93X_MIXOUT_LINE		0x00
+#define OPTi93X_MIXOUT_CD		0x40
+#define OPTi93X_MIXOUT_MIC		0x80
+#define OPTi93X_MIXOUT_MIXER		0xc0
+
+#define OPTi93X_STEREO			0x10
+#define OPTi93X_LINEAR_8		0x00
+#define OPTi93X_ULAW_8			0x20
+#define OPTi93X_LINEAR_16_LIT		0x40
+#define OPTi93X_ALAW_8			0x60
+#define OPTi93X_ADPCM_16		0xa0
+#define OPTi93X_LINEAR_16_BIG		0xc0
+
+#define OPTi93X_CAPTURE_PIO		0x80
+#define OPTi93X_PLAYBACK_PIO		0x40
+#define OPTi93X_AUTOCALIB		0x08
+#define OPTi93X_SINGLE_DMA		0x04
+#define OPTi93X_CAPTURE_ENABLE		0x02
+#define OPTi93X_PLAYBACK_ENABLE		0x01
+
+#define OPTi93X_IRQ_ENABLE		0x02
+
+#define OPTi93X_DMA_REQUEST		0x10
+#define OPTi93X_CALIB_IN_PROGRESS	0x20
+
+#define OPTi93X_IRQ_PLAYBACK		0x04
+#define OPTi93X_IRQ_CAPTURE		0x08
+
+
+typedef struct _snd_opti93x opti93x_t;
+
+struct _snd_opti93x {
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+	int dma1;
+	int dma2;
+
+	opti9xx_t *chip;
+	unsigned short hardware;
+	unsigned char image[32];
+
+	unsigned char mce_bit;
+	unsigned short mode;
+	int mute;
+
+	spinlock_t lock;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+};
+
+#define OPTi93X_MODE_NONE	0x00
+#define OPTi93X_MODE_PLAY	0x01
+#define OPTi93X_MODE_CAPTURE	0x02
+#define OPTi93X_MODE_OPEN	(OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE)
+
+#endif /* OPTi93X */
+
+struct _snd_opti9xx {
+	unsigned short hardware;
+	unsigned char password;
+	char name[7];
+
+	unsigned long mc_base;
+	struct resource *res_mc_base;
+	unsigned long mc_base_size;
+#ifdef OPTi93X
+	unsigned long mc_indir_index;
+#endif	/* OPTi93X */
+	unsigned long pwd_reg;
+
+	spinlock_t lock;
+
+	long wss_base;
+	int irq;
+	int dma1;
+#if defined(CS4231) || defined(OPTi93X)
+	int dma2;
+#endif	/* CS4231 || OPTi93X */
+
+	long fm_port;
+
+	long mpu_port;
+	int mpu_irq;
+
+#if defined(OPTi93X)
+	opti93x_t *opti93x;
+#elif defined(CS4231)
+	cs4231_t *cs4231;
+#else
+	ad1848_t *ad1848;
+#endif	/* AD1848 */
+	snd_rawmidi_t *rmidi;
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_opti9xx_card = SNDRV_DEFAULT_PTR1;
+
+#ifdef __ISAPNP__
+
+#define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs: { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+			ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
+	}
+
+static struct isapnp_card_id snd_card_opti9xx_pnpids[] = {
+#ifndef OPTi93X
+	/* OPTi 82C924 */
+	ISAPNP_OPTI9XX('O','P','T',0x0924,'O','P','T',0x0000,0x0002),
+	/* OPTi 82C925 */
+	ISAPNP_OPTI9XX('O','P','T',0x0925,'O','P','T',0x9250,0x0002),
+#else
+	/* OPTi 82C931/3 */
+	ISAPNP_OPTI9XX('O','P','T',0x0931,'O','P','T',0x9310,0x0002),
+#endif	/* OPTi93X */
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_card_opti9xx_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#ifdef OPTi93X
+#define DRIVER_NAME	"snd-card-opti93x"
+#else
+#define DRIVER_NAME	"snd-card-opti92x"
+#endif	/* OPTi93X */
+
+static char * snd_opti9xx_names[] = {
+	"unkown",
+	"82C928",	"82C929",
+	"82C924",	"82C925",
+	"82C930",	"82C931",	"82C933"
+};
+
+
+static int __init snd_opti9xx_init(opti9xx_t *chip, unsigned short hardware)
+{
+	chip->hardware = hardware;
+	strcpy(chip->name, snd_opti9xx_names[hardware]);
+
+	spin_lock_init(&chip->lock);
+
+	chip->wss_base = -1;
+	chip->irq = -1;
+	chip->dma1 = -1;
+#if defined(CS4231) || defined (OPTi93X)
+	chip->dma2 = -1;
+#endif 	/* CS4231 || OPTi93X */
+	chip->fm_port = -1;
+	chip->mpu_port = -1;
+	chip->mpu_irq = -1;
+
+	switch (hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		chip->mc_base = 0xf8c;
+		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
+		chip->pwd_reg = 3;
+		break;
+
+	case OPTi9XX_HW_82C924:
+	case OPTi9XX_HW_82C925:
+		chip->mc_base = 0xf8c;
+		chip->password = 0xe5;
+		chip->pwd_reg = 3;
+		break;
+#else	/* OPTi93X */
+
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
+		chip->mc_indir_index = 0xe0e;
+		chip->password = 0xe4;
+		chip->pwd_reg = 0;
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", hardware);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static unsigned char snd_opti9xx_read(opti9xx_t *chip,
+				      unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char retval = 0xff;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	outb(chip->password, chip->mc_base + chip->pwd_reg);
+
+	switch (chip->hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C924:
+	case OPTi9XX_HW_82C925:
+		if (reg > 7) {
+			outb(reg, chip->mc_base + 8);
+			outb(chip->password, chip->mc_base + chip->pwd_reg);
+			retval = inb(chip->mc_base + 9);
+			break;
+		}
+
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		retval = inb(chip->mc_base + reg);
+		break;
+#else	/* OPTi93X */
+
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		outb(reg, chip->mc_indir_index);
+		outb(chip->password, chip->mc_base + chip->pwd_reg);
+		retval = inb(chip->mc_indir_index + 1);
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", chip->hardware);
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return retval;
+}
+	
+static void snd_opti9xx_write(opti9xx_t *chip, unsigned char reg,
+			      unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	outb(chip->password, chip->mc_base + chip->pwd_reg);
+
+	switch (chip->hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C924:
+	case OPTi9XX_HW_82C925:
+		if (reg > 7) {
+			outb(reg, chip->mc_base + 8);
+			outb(chip->password, chip->mc_base + chip->pwd_reg);
+			outb(value, chip->mc_base + 9);
+			break;
+		}
+
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		outb(value, chip->mc_base + reg);
+		break;
+#else	/* OPTi93X */
+
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		outb(reg, chip->mc_indir_index);
+		outb(chip->password, chip->mc_base + chip->pwd_reg);
+		outb(value, chip->mc_indir_index + 1);
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", chip->hardware);
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+#define snd_opti9xx_write_mask(chip, reg, value, mask)	\
+	snd_opti9xx_write(chip, reg,			\
+		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+
+
+static int __init snd_opti9xx_configure(opti9xx_t *chip)
+{
+	unsigned char wss_base_bits;
+	unsigned char irq_bits;
+	unsigned char dma_bits;
+	unsigned char mpu_port_bits = 0;
+	unsigned char mpu_irq_bits;
+	unsigned long flags;
+
+	switch (chip->hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C924:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
+
+	case OPTi9XX_HW_82C925:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
+#ifdef CS4231
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+#else
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
+#endif	/* CS4231 */
+		break;
+
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
+		/*
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
+		*/
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
+#ifdef CS4231
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+#else
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
+#endif	/* CS4231 */
+		break;
+
+#else	/* OPTi93X */
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
+			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
+			0x34);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", chip->hardware);
+		return -EINVAL;
+	}
+
+	switch (chip->wss_base) {
+	case 0x530:
+		wss_base_bits = 0x00;
+		break;
+	case 0x604:
+		wss_base_bits = 0x03;
+		break;
+	case 0xe80:
+		wss_base_bits = 0x01;
+		break;
+	case 0xf40:
+		wss_base_bits = 0x02;
+		break;
+	default:
+		snd_printk("WSS port 0x%lx not valid\n", chip->wss_base);
+		goto __skip_base;
+	}
+	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
+
+__skip_base:
+	switch (chip->irq) {
+#ifdef OPTi93X
+	case 5:
+		irq_bits = 0x05;
+		break;
+#endif	/* OPTi93X */
+	case 7:
+		irq_bits = 0x01;
+		break;
+	case 9:
+		irq_bits = 0x02;
+		break;
+	case 10:
+		irq_bits = 0x03;
+		break;
+	case 11:
+		irq_bits = 0x04;
+		break;
+	default:
+		snd_printk("WSS irq # %d not valid\n", chip->irq);
+		goto __skip_resources;
+	}
+
+	switch (chip->dma1) {
+	case 0:
+		dma_bits = 0x01;
+		break;
+	case 1:
+		dma_bits = 0x02;
+		break;
+	case 3:
+		dma_bits = 0x03;
+		break;
+	default:
+		snd_printk("WSS dma1 # %d not valid\n", chip->dma1);
+		goto __skip_resources;
+	}
+
+#if defined(CS4231) || defined(OPTi93X)
+	if (chip->dma1 == chip->dma2) {
+		snd_printk("don't want to share dmas\n");
+		return -EBUSY;
+	}
+
+	switch (chip->dma2) {
+	case 0:
+	case 1:
+		break;
+	default:
+		snd_printk("WSS dma2 # %d not valid\n", chip->dma2);
+		goto __skip_resources;
+	}
+	dma_bits |= 0x04;
+#endif	/* CS4231 || OPTi93X */
+
+	spin_lock_irqsave(&chip->lock, flags);
+	outb(irq_bits << 3 | dma_bits, chip->wss_base);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+__skip_resources:
+	if (chip->hardware > OPTi9XX_HW_82C928) {
+		switch (chip->mpu_port) {
+		case -1:
+			break;
+		case 0x300:
+			mpu_port_bits = 0x03;
+			break;
+		case 0x310:
+			mpu_port_bits = 0x02;
+			break;
+		case 0x320:
+			mpu_port_bits = 0x01;
+			break;
+		case 0x330:
+			mpu_port_bits = 0x00;
+			break;
+		default:
+			snd_printk("MPU-401 port 0x%lx not valid\n",
+				chip->mpu_port);
+			goto __skip_mpu;
+		}
+
+		switch (chip->mpu_irq) {
+		case 5:
+			mpu_irq_bits = 0x02;
+			break;
+		case 7:
+			mpu_irq_bits = 0x03;
+			break;
+		case 9:
+			mpu_irq_bits = 0x00;
+			break;
+		case 10:
+			mpu_irq_bits = 0x01;
+			break;
+		default:
+			snd_printk("MPU-401 irq # %d not valid\n",
+				chip->mpu_irq);
+			goto __skip_mpu;
+		}
+
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
+			(chip->mpu_port == -1) ? 0x00 :
+				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
+			0xf8);
+	}
+__skip_mpu:
+
+	return 0;
+}
+
+#ifdef OPTi93X
+
+#define chip_t opti93x_t
+
+static unsigned char snd_opti93x_default_image[32] =
+{
+	0x00,		/* 00/00 - l_mixout_outctrl */
+	0x00,		/* 01/01 - r_mixout_outctrl */
+	0x88,		/* 02/02 - l_cd_inctrl */
+	0x88,		/* 03/03 - r_cd_inctrl */
+	0x88,		/* 04/04 - l_a1/fm_inctrl */
+	0x88,		/* 05/05 - r_a1/fm_inctrl */
+	0x80,		/* 06/06 - l_dac_inctrl */
+	0x80,		/* 07/07 - r_dac_inctrl */
+	0x00,		/* 08/08 - ply_dataform_reg */
+	0x00,		/* 09/09 - if_conf */
+	0x00,		/* 0a/10 - pin_ctrl */
+	0x00,		/* 0b/11 - err_init_reg */
+	0x0a,		/* 0c/12 - id_reg */
+	0x00,		/* 0d/13 - reserved */
+	0x00,		/* 0e/14 - ply_upcount_reg */
+	0x00,		/* 0f/15 - ply_lowcount_reg */
+	0x88,		/* 10/16 - reserved/l_a1_inctrl */
+	0x88,		/* 11/17 - reserved/r_a1_inctrl */
+	0x88,		/* 12/18 - l_line_inctrl */
+	0x88,		/* 13/19 - r_line_inctrl */
+	0x88,		/* 14/20 - l_mic_inctrl */
+	0x88,		/* 15/21 - r_mic_inctrl */
+	0x80,		/* 16/22 - l_out_outctrl */
+	0x80,		/* 17/23 - r_out_outctrl */
+	0x00,		/* 18/24 - reserved */
+	0x00,		/* 19/25 - reserved */
+	0x00,		/* 1a/26 - reserved */
+	0x00,		/* 1b/27 - reserved */
+	0x00,		/* 1c/28 - cap_dataform_reg */
+	0x00,		/* 1d/29 - reserved */
+	0x00,		/* 1e/30 - cap_upcount_reg */
+	0x00		/* 1f/31 - cap_lowcount_reg */
+};
+
+
+static int snd_opti93x_busy_wait(opti93x_t *chip)
+{
+	int timeout;
+
+	for (timeout = 250; timeout-- > 0; udelay(10))
+		if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT))
+			return 0;
+
+	snd_printk("chip still busy.\n");
+	return -EBUSY;
+}
+
+static unsigned char snd_opti93x_in(opti93x_t *chip, unsigned char reg)
+{
+	snd_opti93x_busy_wait(chip);
+	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
+	return inb(OPTi93X_PORT(chip, DATA));
+}
+
+static void snd_opti93x_out(opti93x_t *chip, unsigned char reg,
+			    unsigned char value)
+{
+	snd_opti93x_busy_wait(chip);
+	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
+	outb(value, OPTi93X_PORT(chip, DATA));
+}
+
+static void snd_opti93x_out_image(opti93x_t *chip, unsigned char reg,
+				  unsigned char value)
+{
+	snd_opti93x_out(chip, reg, chip->image[reg] = value);
+}
+
+static void snd_opti93x_out_mask(opti93x_t *chip, unsigned char reg,
+				 unsigned char mask, unsigned char value)
+{
+	snd_opti93x_out_image(chip, reg,
+		(chip->image[reg] & ~mask) | (value & mask));
+}
+
+
+static void snd_opti93x_mce_up(opti93x_t *chip)
+{
+	snd_opti93x_busy_wait(chip);
+
+	chip->mce_bit = OPTi93X_MCE;
+	if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE))
+		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
+}
+
+static void snd_opti93x_mce_down(opti93x_t *chip)
+{
+	snd_opti93x_busy_wait(chip);
+
+	chip->mce_bit = 0;
+	if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)
+		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
+}
+
+#define snd_opti93x_mute_reg(chip, reg, mute)	\
+	snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]);
+
+static void snd_opti93x_mute(opti93x_t *chip, int mute)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	mute = mute ? 1 : 0;
+	if (chip->mute == mute) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return;
+	}
+	chip->mute = mute;
+
+	snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute);
+	switch (chip->hardware) {
+	case OPTi9XX_HW_82C930:
+		snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute);
+		break;
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute);
+	}
+	snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+static unsigned int snd_opti93x_get_count(unsigned char format,
+					  unsigned int size)
+{
+	switch (format & 0xe0) {
+	case OPTi93X_LINEAR_16_LIT:
+	case OPTi93X_LINEAR_16_BIG:
+		size >>= 1;
+		break;
+	case OPTi93X_ADPCM_16:
+		return size >> 2;
+	}
+	return (format & OPTi93X_STEREO) ? (size >> 1) : size;
+}
+
+unsigned int rates[] = {  5512,  6615,  8000,  9600, 11025, 16000, 18900,
+			 22050, 27428, 32000, 33075, 37800, 44100, 48000 };
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: RATES,
+	list: rates,
+	mask: 0,
+};
+
+unsigned char bits[] = {  0x01,  0x0f,  0x00,  0x0e,  0x03,  0x02,  0x05,
+			  0x07,  0x04,  0x06,  0x0d,  0x09,  0x0b,  0x0c};
+
+static unsigned char snd_opti93x_get_freq(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < RATES; i++) {
+		if (rate == rates[i])
+			return bits[i];
+	}
+	snd_BUG();
+	return bits[RATES-1];
+}
+
+static unsigned char snd_opti93x_get_format(opti93x_t *chip,
+					    unsigned int format, int channels)
+{
+	unsigned char retval = OPTi93X_LINEAR_8;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		retval = OPTi93X_ULAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		retval = OPTi93X_ALAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		retval = OPTi93X_LINEAR_16_LIT;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		retval = OPTi93X_LINEAR_16_BIG;
+		break;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:
+		retval = OPTi93X_ADPCM_16;
+	}
+	return (channels > 1) ? (retval | OPTi93X_STEREO) : retval;
+}
+
+
+static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt)
+{
+	unsigned long flags;
+	unsigned char mask;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_opti93x_mute(chip, 1);
+
+	snd_opti93x_mce_up(chip);
+	mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff;
+	snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt);
+	snd_opti93x_mce_down(chip);
+
+	snd_opti93x_mute(chip, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_opti93x_mute(chip, 1);
+
+	snd_opti93x_mce_up(chip);
+	if (!(chip->mode & OPTi93X_MODE_PLAY))
+		snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt);
+	else
+		fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0;
+	snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt);
+	snd_opti93x_mce_down(chip);
+
+	snd_opti93x_mute(chip, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+static int snd_opti93x_open(opti93x_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (chip->mode & mode) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return -EAGAIN;
+	}
+
+	if (!(chip->mode & OPTi93X_MODE_OPEN)) {
+		outb(0x00, OPTi93X_PORT(chip, STATUS));
+		snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL,
+			OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE);
+		chip->mode = mode;
+	}
+	else
+		chip->mode |= mode;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static void snd_opti93x_close(opti93x_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->mode &= ~mode;
+	if (chip->mode & OPTi93X_MODE_OPEN) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return;
+	}
+
+	snd_opti93x_mute(chip, 1);
+
+	outb(0, OPTi93X_PORT(chip, STATUS));
+	snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE,
+		~OPTi93X_IRQ_ENABLE);
+
+	snd_opti93x_mce_up(chip);
+	snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00);
+	snd_opti93x_mce_down(chip);
+	chip->mode = 0;
+
+	snd_opti93x_mute(chip, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int snd_opti93x_trigger(snd_pcm_substream_t *substream, 
+			       unsigned char what, int cmd)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == chip->playback_substream) {
+				what |= OPTi93X_PLAYBACK_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == chip->capture_substream) {
+				what |= OPTi93X_CAPTURE_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&chip->lock);
+		if (SNDRV_PCM_TRIGGER_START)
+			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
+		else
+			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00);
+		spin_unlock(&chip->lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_opti93x_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	return snd_opti93x_trigger(substream,
+				   OPTi93X_PLAYBACK_ENABLE, cmd);
+}
+
+static int snd_opti93x_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	return snd_opti93x_trigger(substream,
+				   OPTi93X_CAPTURE_ENABLE, cmd);
+}
+
+static int snd_opti93x_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+
+static int snd_opti93x_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+
+static int snd_opti93x_playback_prepare(snd_pcm_substream_t * substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned char format;
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->p_dma_size = size;
+	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
+		OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO,
+		~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO));
+
+	snd_dma_program(chip->dma1, runtime->dma_addr, size,
+		DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	format = snd_opti93x_get_freq(runtime->rate);
+	format |= snd_opti93x_get_format(chip, runtime->format,
+		runtime->channels);
+	snd_opti93x_playback_format(chip, format);
+	format = chip->image[OPTi93X_PLAY_FORMAT];
+
+	count = snd_opti93x_get_count(format, count) - 1;
+	snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count);
+	snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_opti93x_capture_prepare(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned char format;
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->c_dma_size = size;
+	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
+		OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO,
+		(unsigned char)~(OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO));
+
+	snd_dma_program(chip->dma2, runtime->dma_addr, size,
+		DMA_MODE_READ | DMA_AUTOINIT);
+
+	format = snd_opti93x_get_freq(runtime->rate);
+	format |= snd_opti93x_get_format(chip, runtime->format,
+		runtime->channels);
+	snd_opti93x_capture_format(chip, format);
+	format = chip->image[OPTi93X_CAPT_FORMAT];
+
+	count = snd_opti93x_get_count(format, count) - 1;
+	snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count);
+	snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_opti93x_playback_pointer(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE))
+		return 0;
+
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma1);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_opti93x_capture_pointer(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE))
+		return 0;
+
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+
+static void snd_opti93x_overrange(opti93x_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02))
+		chip->capture_substream->runtime->overrange++;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+void snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	opti93x_t *codec = snd_magic_cast(opti93x_t, dev_id, return);
+	unsigned char status;
+
+	status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11));
+	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
+		snd_pcm_period_elapsed(codec->playback_substream);
+	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
+		snd_opti93x_overrange(codec);
+		snd_pcm_period_elapsed(codec->capture_substream);
+	}
+	outb(0x00, OPTi93X_PORT(codec, STATUS));
+}
+
+
+static snd_pcm_hardware_t snd_opti93x_playback = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_opti93x_capture = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_opti93x_playback_open(snd_pcm_substream_t *substream)
+{
+	int error;
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0)
+		return error;
+	snd_pcm_set_sync(substream);
+	chip->playback_substream = substream;
+	runtime->hw = snd_opti93x_playback;
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return error;
+}
+
+static int snd_opti93x_capture_open(snd_pcm_substream_t *substream)
+{
+	int error;
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0)
+		return error;
+	runtime->hw = snd_opti93x_capture;
+	snd_pcm_set_sync(substream);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return error;
+}
+
+static int snd_opti93x_playback_close(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_opti93x_close(chip, OPTi93X_MODE_PLAY);
+	return 0;
+}
+
+static int snd_opti93x_capture_close(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE);
+	return 0;
+}
+
+
+static void snd_opti93x_init(opti93x_t *chip)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_opti93x_mce_up(chip);
+
+	for (i = 0; i < 32; i++)
+		snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]);
+
+	snd_opti93x_mce_down(chip);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int snd_opti93x_probe(opti93x_t *chip)
+{
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return (val == 0x0a) ? 0 : -ENODEV;
+}
+
+static int snd_opti93x_free(opti93x_t *chip)
+{
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->dma1 >= 0) {
+		disable_dma(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (chip->dma2 >= 0) {
+		disable_dma(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_opti93x_dev_free(snd_device_t *device)
+{
+	opti93x_t *chip = snd_magic_cast(opti93x_t, device->device_data, return -ENXIO);
+	return snd_opti93x_free(chip);
+}
+
+static const char *snd_opti93x_chip_id(opti93x_t *codec)
+{
+	switch (codec->hardware) {
+	case OPTi9XX_HW_82C930: return "82C930";
+	case OPTi9XX_HW_82C931: return "82C931";
+	case OPTi9XX_HW_82C933: return "82C933";
+	default:		return "???";
+	}
+}
+
+int snd_opti93x_create(snd_card_t *card, opti9xx_t *chip,
+		       int dma1, int dma2,
+		       opti93x_t **rcodec)
+{
+	static snd_device_ops_t ops = {
+		dev_free:	snd_opti93x_dev_free,
+	};
+	int error;
+	opti93x_t *codec;
+
+	*rcodec = NULL;
+	codec = snd_magic_kcalloc(opti93x_t, 0, GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+	codec->irq = -1;
+	codec->dma1 = -1;
+	codec->dma2 = -1;
+
+	if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) {
+		snd_opti93x_free(codec);
+		return -EBUSY;
+	}
+	if (request_dma(dma1, "OPTI93x - 1")) {
+		snd_opti93x_free(codec);
+		return -EBUSY;
+	}
+	codec->dma1 = chip->dma1;
+	if (request_dma(dma2, "OPTI93x - 2")) {
+		snd_opti93x_free(codec);
+		return -EBUSY;
+	}
+	codec->dma2 = chip->dma2;
+
+	codec->card = card;
+	codec->port = chip->wss_base + 4;
+	codec->irq = chip->irq;
+
+	spin_lock_init(&codec->lock);
+	codec->hardware = chip->hardware;
+	codec->chip = chip;
+
+	if ((error = snd_opti93x_probe(codec))) {
+		snd_opti93x_free(codec);
+		return error;
+	}
+
+	snd_opti93x_init(codec);
+
+	/* Register device */
+	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_opti93x_free(codec);
+		return error;
+	}
+
+	*rcodec = codec;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_opti93x_playback_ops = {
+	open:		snd_opti93x_playback_open,
+	close:		snd_opti93x_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_opti93x_hw_params,
+	hw_free:	snd_opti93x_hw_free,
+	prepare:	snd_opti93x_playback_prepare,
+	trigger:	snd_opti93x_playback_trigger,
+	pointer:	snd_opti93x_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_opti93x_capture_ops = {
+	open:		snd_opti93x_capture_open,
+	close:		snd_opti93x_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_opti93x_hw_params,
+	hw_free:	snd_opti93x_hw_free,
+	prepare:	snd_opti93x_capture_prepare,
+	trigger:	snd_opti93x_capture_trigger,
+	pointer:	snd_opti93x_capture_pointer,
+};
+
+static void snd_opti93x_pcm_free(snd_pcm_t *pcm)
+{
+	opti93x_t *codec = snd_magic_cast(opti93x_t, pcm->private_data, return);
+	codec->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_opti93x_pcm(opti93x_t *codec, int device, snd_pcm_t **rpcm)
+{
+	int error;
+	snd_pcm_t *pcm;
+
+	if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm)))
+		return error;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops);
+
+	pcm->private_data = codec;
+	pcm->private_free = snd_opti93x_pcm_free;
+	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+	strcpy(pcm->name, snd_opti93x_chip_id(codec));
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024);
+
+	codec->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_opti93x_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {
+		"Line1", "Aux", "Mic", "Mix"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_opti93x_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_opti93x_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->lock, flags);
+	left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left;
+	right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right;
+	change = left != chip->image[OPTi93X_MIXOUT_LEFT] ||
+	         right != chip->image[OPTi93X_MIXOUT_RIGHT];
+	snd_opti93x_out(chip, OPTi93X_MIXOUT_LEFT, left);
+	snd_opti93x_out(chip, OPTi93X_MIXOUT_RIGHT, right);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#if 0
+
+#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opti93x_info_single, \
+  get: snd_opti93x_get_single, put: snd_opti93x_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_opti93x_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_opti93x_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_opti93x_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#endif /* single */
+
+#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opti93x_info_double, \
+  get: snd_opti93x_get_double, put: snd_opti93x_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
+	do { xctl.private_value ^= 22; } while (0)
+#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \
+	do { xctl.private_value &= ~0x0000ffff; \
+	     xctl.private_value |= left_reg | (right_reg << 8); } while (0)
+
+static int snd_opti93x_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_opti93x_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_opti93x_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->lock, flags);
+	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
+	snd_opti93x_out(chip, left_reg, val1);
+	snd_opti93x_out(chip, right_reg, val1);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#define OPTi93X_CONTROLS (sizeof(snd_opti93x_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_opti93x_controls[] = {
+OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 0, 0, 31, 1),
+OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 0),
+OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1),
+OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_opti93x_info_mux,
+	get: snd_opti93x_get_mux,
+	put: snd_opti93x_put_mux,
+}
+};
+                                        
+int snd_opti93x_mixer(opti93x_t *chip)
+{
+	snd_card_t *card;
+	snd_kcontrol_new_t knew;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, snd_opti93x_chip_id(chip));
+
+	for (idx = 0; idx < OPTi93X_CONTROLS; idx++) {
+		knew = snd_opti93x_controls[idx];
+		if (chip->hardware == OPTi9XX_HW_82C930) {
+			if (strstr(knew.name, "FM"))	/* skip FM controls */
+				continue;
+			else if (strcmp(knew.name, "Mic Playback Volume"))
+				OPTi93X_DOUBLE_INVERT_INVERT(knew);
+			else if (strstr(knew.name, "Aux"))
+				OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT);
+			else if (strcmp(knew.name, "PCM Playback Volume"))
+				OPTi93X_DOUBLE_INVERT_INVERT(knew);
+			else if (strcmp(knew.name, "Master Playback Volume"))
+				OPTi93X_DOUBLE_INVERT_INVERT(knew);
+		}
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+#endif /* OPTi93X */
+
+static int __init snd_card_opti9xx_detect(snd_card_t *card, opti9xx_t *chip)
+{
+	int i, err;
+	static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+
+#ifndef OPTi93X
+	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
+		unsigned char value;
+
+		if ((err = snd_opti9xx_init(chip, i)) < 0)
+			return err;
+		chip->mc_base_size = opti9xx_mc_size[i];
+		if (check_region(chip->mc_base, chip->mc_base_size))
+			continue;
+
+		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
+		if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
+			if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
+				return 1;
+	}
+#else	/* OPTi93X */
+	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
+		unsigned long flags;
+		unsigned char value;
+
+		if ((err = snd_opti9xx_init(chip, i)) < 0)
+			return err;
+		chip->mc_base_size = opti9xx_mc_size[i];
+		if (check_region(chip->mc_base, chip->mc_base_size))
+			continue;
+
+		spin_lock_irqsave(&chip->lock, flags);
+		outb(chip->password, chip->mc_base + chip->pwd_reg);
+		outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
+			((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
+		spin_unlock_irqrestore(&chip->lock, flags);
+
+		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
+		snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
+		if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
+			return 1;
+	}
+#endif	/* OPTi93X */
+
+	return -ENODEV;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_card_opti9xx_isapnp(opti9xx_t *chip)
+{
+	struct isapnp_dev *pdev = NULL;
+	const struct isapnp_card_id *pid = snd_card_opti9xx_pnpids-1;
+	static struct isapnp_card *card = NULL;
+
+      __again:
+	while (1) {
+		pid++;
+		if (pid->card_vendor == 0)
+			return -ENODEV;
+		if ((card = isapnp_find_card(pid->card_vendor, pid->card_device, card)))
+			break;
+	}
+	if (card == NULL)
+		return -ENODEV;
+
+	chip->dev = isapnp_find_dev(card, pid->devs[0].vendor, pid->devs[0].function, NULL);
+	if (chip->dev == NULL)
+		goto __again;
+
+	chip->devmpu = isapnp_find_dev(card, pid->devs[1].vendor, pid->devs[1].function, NULL);
+
+	pdev = chip->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+
+#ifdef OPTi93X
+	if (snd_port != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port + 4, 4);
+#else
+	if ((pid->card_device != ISAPNP_DEVICE(0x0924)) && (snd_port != SNDRV_AUTO_PORT))
+		isapnp_resource_change(&pdev->resource[1], snd_port, 4);
+#endif	/* OPTi93X */
+	if (snd_irq != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq, 1);
+	if (snd_dma1 != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1, 1);
+#if defined(CS4231) || defined(OPTi93X)
+	if (snd_dma2 != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2, 1);
+#endif	/* CS4231 || OPTi93X */
+	if (snd_fm_port != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port, 4);
+
+	if (pdev->activate(pdev) < 0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+#ifdef OPTi93X
+	snd_port = pdev->resource[0].start - 4;
+	snd_fm_port = pdev->resource[1].start;
+#else
+	if (pid->card_device != ISAPNP_DEVICE(0x0924))
+		snd_port = pdev->resource[1].start;
+	snd_fm_port = pdev->resource[2].start;
+#endif	/* OPTi93X */
+	snd_irq = pdev->irq_resource[0].start;
+	snd_dma1 = pdev->dma_resource[0].start;
+#if defined(CS4231) || defined(OPTi93X)
+	snd_dma2 = pdev->dma_resource[1].start;
+#endif	/* CS4231 || OPTi93X */
+
+	pdev = chip->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev) < 0) {
+		snd_mpu_port = -1;
+		chip->devmpu = NULL;
+		return pid->card_device;
+	}
+
+	if (snd_mpu_port != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port, 2);
+	if (snd_mpu_irq != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq, 1);
+
+	if (pdev->activate(pdev) < 0) {
+		snd_printk("MPU-401 isapnp configure failure\n");
+		snd_mpu_port = -1;
+		chip->devmpu = NULL;
+	} else {
+		snd_mpu_port = pdev->resource[0].start;
+		snd_mpu_irq = pdev->irq_resource[0].start;
+	}
+	return pid->card_device;
+}
+
+static void snd_card_opti9xx_deactivate(opti9xx_t *chip)
+{
+	if (chip->dev)
+		chip->dev->deactivate(chip->dev);
+	if (chip->devmpu)
+		chip->devmpu->deactivate(chip->devmpu);
+}
+#endif	/* __ISAPNP__ */
+
+#if 0
+static int __init snd_card_opti9xx_resources(struct snd_card_opti9xx *chip,
+					     snd_card_t *card)
+{
+	int error, i, pnp = 0;
+
+#ifdef __ISAPNP__
+	pnp = chip->dev != NULL;
+#endif	/* __ISAPNP__ */
+
+#ifndef OPTi93X
+	if (chip->chip->hardware == OPTi9XX_HW_82C928)
+		snd_mpu_port = -1;
+#endif	/* OPTi93X */
+	error = 0;
+	if (!pnp && (snd_mpu_port == SNDRV_DEFAULT_PORT1)) {
+		for (i = 0; possible_mpu_ports[i] != -1; i++)
+			if (!snd_register_ioport(card, possible_mpu_ports[i], 2,
+					DRIVER_NAME" - MPU-401", NULL)) {
+				snd_mpu_port = possible_mpu_ports[i];
+				break;
+			}
+		if (snd_mpu_port == SNDRV_DEFAULT_PORT1)
+			error = -EBUSY;
+	}
+	else
+		error = (snd_mpu_port == -1) ? -ENODEV :
+			snd_register_ioport(card, snd_mpu_port, 2,
+			DRIVER_NAME" - MPU-401", NULL);
+	if (error)
+		chip->chip->mpu_port = -1;
+	else if (pnp && (snd_irq == snd_mpu_irq))
+		chip->chip->mpu_irq = snd_mpu_irq;
+	else if (!snd_register_interrupt(card,
+			DRIVER_NAME" - MPU-401",
+			snd_mpu_irq, SNDRV_IRQ_TYPE_ISA,
+			snd_card_opti9xx_mpu_interrupt, chip,
+			pnp ? no_alternatives : possible_mpu_irqs,
+			&chip->mpuirqptr)) {
+		chip->chip->mpu_port = snd_mpu_port;
+		chip->chip->mpu_irq = chip->mpuirqptr->irq;
+	}
+	else
+		chip->chip->mpu_port = -1;
+
+	if (!pnp && (snd_port == SNDRV_DEFAULT_PORT1)) {
+		for (i = 0; possible_ports[i] != -1; i++)
+			if (!snd_register_ioport(card, possible_ports[i], 8,
+					DRIVER_NAME" - WSS", NULL)) {
+				snd_port = possible_ports[i];
+				break;
+			}
+		if (snd_port == SNDRV_DEFAULT_PORT1)
+			return -EBUSY;
+	}
+	else if ((error = snd_register_ioport(card, snd_port, 8,
+			DRIVER_NAME" - WSS", NULL)) < 0)
+		return error;
+	chip->chip->wss_base = snd_port;
+	if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS",
+			snd_irq, SNDRV_IRQ_TYPE_ISA,
+			snd_card_opti9xx_interrupt, chip,
+			pnp ? no_alternatives : possible_irqs,
+			&chip->irqptr)) < 0)
+		return error;
+	chip->chip->irq = chip->irqptr->irq;
+	if ((error = snd_register_dma_channel(card,
+#if defined(CS4231) || defined(OPTi93X)
+			DRIVER_NAME" - WSS playback",
+#else
+			DRIVER_NAME" - WSS",
+#endif	/* CS4231 || OPTi93X */
+			snd_dma1, SNDRV_DMA_TYPE_ISA, snd_dma1_size,
+			pnp ? no_alternatives : possible_dma1s,
+			&chip->dma1ptr)) < 0)
+		return error;
+	chip->chip->dma1 = chip->dma1ptr->dma;
+#if defined(CS4231) || defined(OPTi93X)
+	if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture",
+			snd_dma2, SNDRV_DMA_TYPE_ISA, snd_dma2_size,
+			pnp ? no_alternatives :
+				possible_dma2s[chip->dma1ptr->dma],
+			&chip->dma2ptr)) < 0)
+		return error;
+	chip->chip->dma2 = chip->dma2ptr->dma;
+#endif	/* CS4231 || OPTi93X */
+
+	if (snd_register_ioport(card,
+			pnp ? snd_fm_port : snd_fm_port = 0x388, 4,
+			DRIVER_NAME" - OPL", NULL) < 0)
+		snd_fm_port = -1;
+	chip->chip->fm_port = snd_fm_port;
+
+	return 0;
+}
+#endif
+
+static void snd_card_opti9xx_free(snd_card_t *card)
+{
+	opti9xx_t *chip = (opti9xx_t *)card->private_data;
+        
+	if (chip) {
+#ifdef __ISAPNP__
+		snd_card_opti9xx_deactivate(chip);
+#endif	/* __ISAPNP__ */
+		if (chip->res_mc_base) {
+			release_resource(chip->res_mc_base);
+			kfree_nocheck(chip->res_mc_base);
+		}
+	}
+}
+
+static int __init snd_card_opti9xx_probe(void)
+{
+	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+	static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x310, -1};
+#ifdef OPTi93X
+	static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
+#else
+	static int possible_irqs[] = {9, 10, 11, 7, -1};
+#endif	/* OPTi93X */
+	static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_dma1s[] = {3, 1, 0, -1};
+#if defined(CS4231) || defined(OPTi93X)
+	static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
+#endif	/* CS4231 || OPTi93X */
+	int error;
+	opti9xx_t *chip;
+#if defined(OPTi93X)
+	opti93x_t *codec;
+#elif defined(CS4231)
+	cs4231_t *codec;
+	snd_timer_t *timer;
+#else
+	ad1848_t *codec;
+#endif
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *rmidi;
+	snd_hwdep_t *synth;
+#ifdef __ISAPNP__
+	int hw;
+#endif	/* __ISAPNP__ */
+
+	if (!(card = snd_card_new(snd_index, snd_id, THIS_MODULE,
+				  sizeof(opti9xx_t))))
+		return -ENOMEM;
+	card->private_free = snd_card_opti9xx_free;
+	chip = (opti9xx_t *)card->private_data;
+
+#ifdef __ISAPNP__
+	if (snd_isapnp && (hw = snd_card_opti9xx_isapnp(chip)) > 0) {
+		switch (hw) {
+		case ISAPNP_DEVICE(0x0924):
+			hw = OPTi9XX_HW_82C924;
+			break;
+		case ISAPNP_DEVICE(0x0925):
+			hw = OPTi9XX_HW_82C925;
+			break;
+		case ISAPNP_DEVICE(0x0931):
+			hw = OPTi9XX_HW_82C931;
+			break;
+		default:
+			snd_card_free(card);
+			return -ENODEV;
+		}
+
+		if ((error = snd_opti9xx_init(chip, hw))) {
+			snd_card_free(card);
+			return error;
+		}
+		if (hw <= OPTi9XX_HW_82C930)
+			chip->mc_base -= 0x80;
+	} else {
+#endif	/* __ISAPNP__ */
+		if ((error = snd_card_opti9xx_detect(card, chip)) < 0) {
+			snd_card_free(card);
+			return error;
+		}
+#ifdef __ISAPNP__
+	}
+#endif	/* __ISAPNP__ */
+
+	if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) {
+		snd_card_free(card);
+		return -ENOMEM;
+	}
+
+	chip->wss_base = snd_port;
+	chip->fm_port = snd_fm_port;
+	chip->mpu_port = snd_mpu_port;
+	chip->irq = snd_irq;
+	chip->mpu_irq = snd_mpu_irq;
+	chip->dma1 = snd_dma1;
+#if defined(CS4231) || defined(OPTi93X)
+	chip->dma2 = snd_dma2;
+#endif
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp) {
+#endif
+	if (chip->wss_base == SNDRV_AUTO_PORT) {
+		if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free WSS port\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->mpu_port == SNDRV_AUTO_PORT) {
+		if ((chip->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free MPU401 port\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->irq == SNDRV_AUTO_IRQ) {
+		if ((chip->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->mpu_irq == SNDRV_AUTO_IRQ) {
+		if ((chip->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free MPU401 IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->dma1 == SNDRV_AUTO_DMA) {
+                if ((chip->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
+                        snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+        }
+#if defined(CS4231) || defined(OPTi93X)
+	if (chip->dma2 == SNDRV_AUTO_DMA) {
+                if ((chip->dma2 = snd_legacy_find_free_dma(possible_dma2s[chip->dma1 % 4])) < 0) {
+                        snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+        }
+#endif
+
+#ifdef __ISAPNP__
+	}
+#endif
+
+	if ((error = snd_opti9xx_configure(chip))) {
+		snd_card_free(card);
+		return error;
+	}
+
+#if defined(OPTi93X)
+	if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_opti93x_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+#elif defined(CS4231)
+	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
+				       chip->irq, chip->dma1, chip->dma2,
+				       CS4231_HW_DETECT,
+				       0,
+				       &codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	if ((error = snd_ad1848_create(card, chip->wss_base + 4,
+				       chip->irq, chip->dma1,
+				       AD1848_HW_DETECT, &codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_ad1848_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+#endif
+
+	if (chip->mpu_port <= 0)
+		rmidi = NULL;
+	else
+		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT,
+				&rmidi)))
+			snd_printk("no MPU-401 device at 0x%lx?\n", chip->mpu_port);
+
+	if (chip->fm_port > 0) {
+		opl3_t *opl3;
+		if (snd_opl3_create(card,
+				    chip->fm_port,
+				    chip->fm_port + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			snd_printk("no OPL device at 0x%lx-0x%lx\n",
+				chip->fm_port, chip->fm_port + 4 - 1);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3,
+#ifdef CS4231
+							1, 2)) < 0) {
+#else
+							0, 1)) < 0) {
+#endif	/* CS4231 */
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, chip->name);
+	sprintf(card->shortname, "OPTi %s", card->driver);
+#if defined(CS4231) || defined(OPTi93X)
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d",
+		card->shortname, pcm->name, chip->wss_base + 4,
+		chip->irq, chip->dma1, chip->dma2);
+#else
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+		card->shortname, pcm->name, chip->wss_base + 4,
+		chip->irq, chip->dma1);
+#endif	/* CS4231 || OPTi93X */
+	if ((error = snd_card_register(card))) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_opti9xx_card = card;
+	return 0;
+}
+
+static int __init alsa_card_opti9xx_init(void)
+{
+	int error;
+
+	if ((error = snd_card_opti9xx_probe())) {
+#ifdef MODULE
+#ifdef OPTi93X
+		printk(KERN_ERR "no OPTi 82C93x soundcard found\n");
+#else
+		printk(KERN_ERR "no OPTi 82C92x soundcard found\n");
+#endif	/* OPTi93X */
+#endif
+		return error;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_opti9xx_exit(void)
+{
+	if (snd_opti9xx_card)
+		snd_card_free(snd_opti9xx_card);
+}
+
+module_init(alsa_card_opti9xx_init)
+module_exit(alsa_card_opti9xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-opti9xx=snd_enable,snd_index,snd_id,snd_isapnp,
+			  snd_port,snd_mpu_port,snd_fm_port,
+			  snd_irq,snd_mpu_irq,
+			  snd_dma1,[snd_dma2] */
+
+static int __init alsa_card_opti9xx_setup(char *str)
+{
+	int __attribute__ ((__unused__)) enable = 1;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	(void)(get_option(&str,&enable) == 2 &&
+	       get_option(&str,&snd_index) == 2 &&
+	       get_id(&str,&snd_id) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port) == 2 &&
+	       get_option(&str,&snd_irq) == 2 &&
+	       get_option(&str,&snd_mpu_irq) == 2 &&
+	       get_option(&str,&snd_dma1) == 2
+#if defined(CS4231) || defined(OPTi93X)
+	       &&
+	       get_option(&str,&snd_dma2) == 2
+#endif
+	       );
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp = pnp;
+#endif
+	return 1;
+}
+
+#if defined(OPTi93X)
+__setup("snd-opti93x=", alsa_card_opti9xx_setup);
+#elif defined(CS4231)
+__setup("snd-opti92x-cs4231=", alsa_card_opti9xx_setup);
+#else
+__setup("snd-opti92x-ad1848=", alsa_card_opti9xx_setup);
+#endif
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/opti9xx/opti92x-cs4231.c linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-cs4231.c
--- linux/sound/isa/opti9xx/opti92x-cs4231.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-cs4231.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,2 @@
+#define CS4231
+#include "opti92x-ad1848.c"
diff -Nru linux/sound/isa/opti9xx/opti93x.c linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti93x.c
--- linux/sound/isa/opti9xx/opti93x.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti93x.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,3 @@
+#define OPTi93X
+#include "opti92x-ad1848.c"
+
diff -Nru linux/sound/isa/sb/Makefile linux-2.4.19-pre5-mjc/sound/isa/sb/Makefile
--- linux/sound/isa/sb/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,67 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _sb.o
+
+list-multi   := snd-sb-common.o snd-sb8-dsp.o snd-sb16-dsp.o snd-sb16-csp.o \
+	        snd-sb8.o snd-sb16.o snd-sbawe.o snd-emu8000-synth.o snd-es968.o
+
+export-objs  := emu8000.o emu8000_synth.o sb_common.o sb8_main.o sb16_main.o sb16_csp.o
+
+snd-sb-common-objs := sb_common.o sb_mixer.o
+snd-sb8-dsp-objs := sb8_main.o sb8_midi.o
+snd-sb16-dsp-objs := sb16_main.o
+snd-sb16-csp-objs := sb16_csp.o
+snd-sb8-objs := sb8.o
+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
+snd-es968-objs := es968.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_DT0197H) += snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o
+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
+ifeq ($(CONFIG_SND_SB16_CSP),y)
+  obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
+  obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
+endif
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-sb-common.o: $(snd-sb-common-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sb-common-objs)
+
+snd-sb8-dsp.o: $(snd-sb8-dsp-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-dsp-objs)
+
+snd-sb16-dsp.o: $(snd-sb16-dsp-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-dsp-objs)
+
+snd-sb16-csp.o: $(snd-sb16-csp-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-csp-objs)
+
+snd-sb8.o: $(snd-sb8-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-objs)
+
+snd-sb16.o: $(snd-sb16-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-objs)
+
+snd-sbawe.o: $(snd-sbawe-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sbawe-objs)
+
+snd-emu8000-synth.o: $(snd-emu8000-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-emu8000-synth-objs)
+
+snd-es968.o: $(snd-es968-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-es968-objs)
diff -Nru linux/sound/isa/sb/emu8000.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000.c
--- linux/sound/isa/sb/emu8000.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1192 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *     and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU8000 chip
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/emu8000.h>
+#include <sound/emu8000_reg.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#if 0
+MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
+MODULE_DESCRIPTION("Routines for control of EMU8000 chip");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+#endif
+
+/*
+ * emu8000 register controls
+ */
+
+/*
+ * The following routines read and write registers on the emu8000.  They
+ * should always be called via the EMU8000*READ/WRITE macros and never
+ * directly.  The macros handle the port number and command word.
+ */
+/* Write a word */
+void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	outw((unsigned short)val, port); /* Send data */
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+/* Read a word */
+unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg)
+{
+	unsigned short res;
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	res = inw(port);	/* Read data */
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return res;
+}
+
+/* Write a double word */
+void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	outw((unsigned short)val, port); /* Send low word of data */
+	outw((unsigned short)(val>>16), port+2); /* Send high word of data */
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+/* Read a double word */
+unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg)
+{
+	unsigned short low;
+	unsigned int res;
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	low = inw(port);	/* Read low word of data */
+	res = low + (inw(port+2) << 16);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return res;
+}
+
+/*
+ * Set up / close a channel to be used for DMA.
+ */
+/*exported*/ void
+snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode)
+{
+	if (mode == EMU8000_RAM_CLOSE) {
+		EMU8000_CCCA_WRITE(emu, ch, 0);
+		EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F);
+		return;
+	}
+	EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
+	EMU8000_VTFT_WRITE(emu, ch, 0);
+	EMU8000_CVCF_WRITE(emu, ch, 0);
+	EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
+	EMU8000_CPF_WRITE(emu, ch, 0x40000000);
+	EMU8000_PSST_WRITE(emu, ch, 0);
+	EMU8000_CSL_WRITE(emu, ch, 0);
+	if (mode == EMU8000_RAM_WRITE) /* DMA write */
+		EMU8000_CCCA_WRITE(emu, ch, 0x06000000);
+	else	   /* DMA read */
+		EMU8000_CCCA_WRITE(emu, ch, 0x04000000);
+}
+
+/*
+ */
+static void /*__init*/
+snd_emu8000_read_wait(emu8000_t *emu)
+{
+	while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+}
+
+/*
+ */
+static void /*__init*/
+snd_emu8000_write_wait(emu8000_t *emu)
+{
+	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+}
+
+/*
+ * detect a card at the given port
+ */
+static int /*__init*/
+snd_emu8000_detect(emu8000_t *emu)
+{
+	/* Initialise */
+	EMU8000_HWCF1_WRITE(emu, 0x0059);
+	EMU8000_HWCF2_WRITE(emu, 0x0020);
+	EMU8000_HWCF3_WRITE(emu, 0x0000);
+	/* Check for a recognisable emu8000 */
+	/*
+	if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c)
+		return -ENODEV;
+		*/
+	if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058)
+		return -ENODEV;
+	if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
+		return -ENODEV;
+
+	snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
+                    emu->port1);
+	return 0;
+}
+
+
+/*
+ * intiailize audio channels
+ */
+static void /*__init*/
+init_audio(emu8000_t *emu)
+{
+	int ch;
+
+	/* turn off envelope engines */
+	for (ch = 0; ch < EMU8000_CHANNELS; ch++)
+		EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
+  
+	/* reset all other parameters to zero */
+	for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
+		EMU8000_ENVVOL_WRITE(emu, ch, 0);
+		EMU8000_ENVVAL_WRITE(emu, ch, 0);
+		EMU8000_DCYSUS_WRITE(emu, ch, 0);
+		EMU8000_ATKHLDV_WRITE(emu, ch, 0);
+		EMU8000_LFO1VAL_WRITE(emu, ch, 0);
+		EMU8000_ATKHLD_WRITE(emu, ch, 0);
+		EMU8000_LFO2VAL_WRITE(emu, ch, 0);
+		EMU8000_IP_WRITE(emu, ch, 0);
+		EMU8000_IFATN_WRITE(emu, ch, 0);
+		EMU8000_PEFE_WRITE(emu, ch, 0);
+		EMU8000_FMMOD_WRITE(emu, ch, 0);
+		EMU8000_TREMFRQ_WRITE(emu, ch, 0);
+		EMU8000_FM2FRQ2_WRITE(emu, ch, 0);
+		EMU8000_PTRX_WRITE(emu, ch, 0);
+		EMU8000_VTFT_WRITE(emu, ch, 0);
+		EMU8000_PSST_WRITE(emu, ch, 0);
+		EMU8000_CSL_WRITE(emu, ch, 0);
+		EMU8000_CCCA_WRITE(emu, ch, 0);
+	}
+
+	for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
+		EMU8000_CPF_WRITE(emu, ch, 0);
+		EMU8000_CVCF_WRITE(emu, ch, 0);
+	}
+}
+
+
+/*
+ * initialize DMA address
+ */
+static void /*__init*/
+init_dma(emu8000_t *emu)
+{
+	EMU8000_SMALR_WRITE(emu, 0);
+	EMU8000_SMARR_WRITE(emu, 0);
+	EMU8000_SMALW_WRITE(emu, 0);
+	EMU8000_SMARW_WRITE(emu, 0);
+}
+
+/*
+ * initialization arrays; from ADIP
+ */
+static unsigned short init1[128] /*__devinitdata*/ = {
+	0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
+	0x13ff, 0x0430,  0x17ff, 0x0530, 0x1bff, 0x0630,  0x1fff, 0x0730,
+	0x23ff, 0x0830,  0x27ff, 0x0930, 0x2bff, 0x0a30,  0x2fff, 0x0b30,
+	0x33ff, 0x0c30,  0x37ff, 0x0d30, 0x3bff, 0x0e30,  0x3fff, 0x0f30,
+
+	0x43ff, 0x0030,  0x47ff, 0x0130, 0x4bff, 0x0230,  0x4fff, 0x0330,
+	0x53ff, 0x0430,  0x57ff, 0x0530, 0x5bff, 0x0630,  0x5fff, 0x0730,
+	0x63ff, 0x0830,  0x67ff, 0x0930, 0x6bff, 0x0a30,  0x6fff, 0x0b30,
+	0x73ff, 0x0c30,  0x77ff, 0x0d30, 0x7bff, 0x0e30,  0x7fff, 0x0f30,
+
+	0x83ff, 0x0030,  0x87ff, 0x0130, 0x8bff, 0x0230,  0x8fff, 0x0330,
+	0x93ff, 0x0430,  0x97ff, 0x0530, 0x9bff, 0x0630,  0x9fff, 0x0730,
+	0xa3ff, 0x0830,  0xa7ff, 0x0930, 0xabff, 0x0a30,  0xafff, 0x0b30,
+	0xb3ff, 0x0c30,  0xb7ff, 0x0d30, 0xbbff, 0x0e30,  0xbfff, 0x0f30,
+
+	0xc3ff, 0x0030,  0xc7ff, 0x0130, 0xcbff, 0x0230,  0xcfff, 0x0330,
+	0xd3ff, 0x0430,  0xd7ff, 0x0530, 0xdbff, 0x0630,  0xdfff, 0x0730,
+	0xe3ff, 0x0830,  0xe7ff, 0x0930, 0xebff, 0x0a30,  0xefff, 0x0b30,
+	0xf3ff, 0x0c30,  0xf7ff, 0x0d30, 0xfbff, 0x0e30,  0xffff, 0x0f30,
+};
+
+static unsigned short init2[128] /*__devinitdata*/ = {
+	0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
+	0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
+	0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
+	0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
+
+	0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
+	0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
+	0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
+	0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
+
+	0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
+	0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
+	0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
+	0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
+	0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
+	0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
+	0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
+	0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
+};
+
+static unsigned short init3[128] /*__devinitdata*/ = {
+	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
+	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
+	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
+
+	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
+	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
+	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
+	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
+
+	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
+	0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
+	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
+	0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
+
+	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
+	0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
+	0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
+	0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+static unsigned short init4[128] /*__devinitdata*/ = {
+	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
+	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
+	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
+
+	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
+	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
+	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
+	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
+
+	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
+	0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
+	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
+	0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
+
+	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
+	0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
+	0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
+	0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+/* send an initialization array
+ * Taken from the oss driver, not obvious from the doc how this
+ * is meant to work
+ */
+static void /*__init*/
+send_array(emu8000_t *emu, unsigned short *data, int size)
+{
+	int i;
+	unsigned short *p;
+
+	p = data;
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT1_WRITE(emu, i, *p);
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT2_WRITE(emu, i, *p);
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT3_WRITE(emu, i, *p);
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT4_WRITE(emu, i, *p);
+}
+
+#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+
+/*
+ * Send initialization arrays to start up, this just follows the
+ * initialisation sequence in the adip.
+ */
+static void /*__init*/
+init_arrays(emu8000_t *emu)
+{
+	send_array(emu, init1, NELEM(init1)/4);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */
+	send_array(emu, init2, NELEM(init2)/4);
+	send_array(emu, init3, NELEM(init3)/4);
+
+	EMU8000_HWCF4_WRITE(emu, 0);
+	EMU8000_HWCF5_WRITE(emu, 0x83);
+	EMU8000_HWCF6_WRITE(emu, 0x8000);
+
+	send_array(emu, init4, NELEM(init4)/4);
+}
+
+
+#define UNIQUE_ID1	0xa5b9
+#define UNIQUE_ID2	0x9d53
+
+/*
+ * Size the onboard memory.
+ * This is written so as not to need arbitary delays after the write. It
+ * seems that the only way to do this is to use the one channel and keep
+ * reallocating between read and write.
+ */
+static void /*__init*/
+size_dram(emu8000_t *emu)
+{
+	int i, size;
+
+	if (emu->dram_checked)
+		return;
+
+	size = 0;
+
+	/* write out a magic number */
+	snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
+	snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
+	EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
+	EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
+	snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
+
+	while (size < EMU8000_MAX_DRAM) {
+
+		size += 512 * 1024;  /* increment 512kbytes */
+
+		/* Write a unique data on the test address.
+		 * if the address is out of range, the data is written on
+		 * 0x200000(=EMU8000_DRAM_OFFSET).  Then the id word is
+		 * changed by this data.
+		 */
+		/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
+		EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
+		EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
+		snd_emu8000_write_wait(emu);
+
+		/*
+		 * read the data on the just written DRAM address
+		 * if not the same then we have reached the end of ram.
+		 */
+		/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
+		EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
+		/*snd_emu8000_read_wait(emu);*/
+		EMU8000_SMLD_READ(emu); /* discard stale data  */
+		if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
+			break; /* we must have wrapped around */
+
+		snd_emu8000_read_wait(emu);
+
+		/*
+		 * If it is the same it could be that the address just
+		 * wraps back to the beginning; so check to see if the
+		 * initial value has been overwritten.
+		 */
+		EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
+		EMU8000_SMLD_READ(emu); /* discard stale data  */
+		if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
+			break; /* we must have wrapped around */
+		snd_emu8000_read_wait(emu);
+	}
+
+	/* wait until FULL bit in SMAxW register is false */
+	for (i = 0; i < 10000; i++) {
+		if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+	snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
+	snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
+
+	snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
+		    emu->port1, size/1024);
+
+	emu->mem_size = size;
+	emu->dram_checked = 1;
+}
+
+
+/*
+ * Initiailise the FM section.  You have to do this to use sample RAM
+ * and therefore lose 2 voices.
+ */
+/*exported*/ void
+snd_emu8000_init_fm(emu8000_t *emu)
+{
+	unsigned long flags;
+
+	/* Initialize the last two channels for DRAM refresh and producing
+	   the reverb and chorus effects for Yamaha OPL-3 synthesizer */
+
+	/* 31: FM left channel, 0xffffe0-0xffffe8 */
+	EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
+	EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
+	EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
+	EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
+	EMU8000_CPF_WRITE(emu, 30, 0);
+	EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
+
+	/* 32: FM right channel, 0xfffff0-0xfffff8 */
+	EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
+	EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
+	EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
+	EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
+	EMU8000_CPF_WRITE(emu, 31, 0x8000);
+	EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
+
+	snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	while (!(inw(EMU8000_PTR(emu)) & 0x1000))
+		;
+	while ((inw(EMU8000_PTR(emu)) & 0x1000))
+		;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
+	/* this is really odd part.. */
+	outb(0x3C, EMU8000_PTR(emu));
+	outb(0, EMU8000_DATA1(emu));
+
+	/* skew volume & cutoff */
+	EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF);
+	EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF);
+}
+
+
+/*
+ * The main initialization routine.
+ */
+static void /*__init*/
+snd_emu8000_init_hw(emu8000_t *emu)
+{
+	int i;
+
+	emu->last_reg = 0xffff; /* reset the last register index */
+
+	/* initialize hardware configuration */
+	EMU8000_HWCF1_WRITE(emu, 0x0059);
+	EMU8000_HWCF2_WRITE(emu, 0x0020);
+
+	/* disable audio; this seems to reduce a clicking noise a bit.. */
+	EMU8000_HWCF3_WRITE(emu, 0);
+
+	/* initialize audio channels */
+	init_audio(emu);
+
+	/* initialize DMA */
+	init_dma(emu);
+
+	/* initialize init arrays */
+	init_arrays(emu);
+
+	/*
+	 * Initialize the FM section of the AWE32, this is needed
+	 * for DRAM refresh as well
+	 */
+	snd_emu8000_init_fm(emu);
+
+	/* terminate all voices */
+	for (i = 0; i < EMU8000_DRAM_VOICES; i++)
+		EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F);
+	
+	/* check DRAM memory size */
+	size_dram(emu);
+
+	/* enable audio */
+	EMU8000_HWCF3_WRITE(emu, 0x4);
+
+	/* set equzlier, chorus and reverb modes */
+	snd_emu8000_update_equalizer(emu);
+	snd_emu8000_update_chorus_mode(emu);
+	snd_emu8000_update_reverb_mode(emu);
+}
+
+
+/*----------------------------------------------------------------
+ * Bass/Treble Equalizer
+ *----------------------------------------------------------------*/
+
+static unsigned short bass_parm[12][3] = {
+	{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+	{0xD25B, 0xD35B, 0x0000}, /*  -8 */
+	{0xD24C, 0xD34C, 0x0000}, /*  -6 */
+	{0xD23D, 0xD33D, 0x0000}, /*  -4 */
+	{0xD21F, 0xD31F, 0x0000}, /*  -2 */
+	{0xC208, 0xC308, 0x0001}, /*   0 (HW default) */
+	{0xC219, 0xC319, 0x0001}, /*  +2 */
+	{0xC22A, 0xC32A, 0x0001}, /*  +4 */
+	{0xC24C, 0xC34C, 0x0001}, /*  +6 */
+	{0xC26E, 0xC36E, 0x0001}, /*  +8 */
+	{0xC248, 0xC384, 0x0002}, /* +10 */
+	{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+	{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+	{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+	{0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+	{0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}  /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+/*exported*/ void
+snd_emu8000_update_equalizer(emu8000_t *emu)
+{
+	unsigned short w;
+	int bass = emu->bass_level;
+	int treble = emu->treble_level;
+
+	if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+		return;
+	EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]);
+	EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]);
+	EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]);
+	EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]);
+	EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]);
+	EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]);
+	EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]);
+	EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]);
+	EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]);
+	EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]);
+	w = bass_parm[bass][2] + treble_parm[treble][8];
+	EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262));
+	EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362));
+}
+
+
+/*----------------------------------------------------------------
+ * Chorus mode control
+ *----------------------------------------------------------------*/
+
+/*
+ * chorus mode parameters
+ */
+#define SNDRV_EMU8000_CHORUS_1		0
+#define	SNDRV_EMU8000_CHORUS_2		1
+#define	SNDRV_EMU8000_CHORUS_3		2
+#define	SNDRV_EMU8000_CHORUS_4		3
+#define	SNDRV_EMU8000_CHORUS_FEEDBACK	4
+#define	SNDRV_EMU8000_CHORUS_FLANGER	5
+#define	SNDRV_EMU8000_CHORUS_SHORTDELAY	6
+#define	SNDRV_EMU8000_CHORUS_SHORTDELAY2	7
+#define SNDRV_EMU8000_CHORUS_PREDEFINED	8
+/* user can define chorus modes up to 32 */
+#define SNDRV_EMU8000_CHORUS_NUMBERS	32
+
+typedef struct soundfont_chorus_fx_t {
+	unsigned short feedback;	/* feedback level (0xE600-0xE6FF) */
+	unsigned short delay_offset;	/* delay (0-0x0DA3) [1/44100 sec] */
+	unsigned short lfo_depth;	/* LFO depth (0xBC00-0xBCFF) */
+	unsigned int delay;	/* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
+	unsigned int lfo_freq;		/* LFO freq LFO freq (0-0xFFFFFFFF) */
+} soundfont_chorus_fx_t;
+
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
+static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
+	{0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+	{0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+	{0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+	{0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+	{0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+	{0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+	{0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */
+	{0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */
+};
+
+/*exported*/ int
+snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len)
+{
+	soundfont_chorus_fx_t rec;
+	if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
+		snd_printk("illegal chorus mode %d for uploading\n", mode);
+		return -EINVAL;
+	}
+	if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
+		return -EFAULT;
+	chorus_parm[mode] = rec;
+	chorus_defined[mode] = 1;
+	return 0;
+}
+
+/*exported*/ void
+snd_emu8000_update_chorus_mode(emu8000_t *emu)
+{
+	int effect = emu->chorus_mode;
+	if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
+	    (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
+		return;
+	EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback);
+	EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset);
+	EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth);
+	EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
+	EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
+	EMU8000_HWCF6_WRITE(emu, 0x8000);
+	EMU8000_HWCF7_WRITE(emu, 0x0000);
+}
+
+/*----------------------------------------------------------------
+ * Reverb mode control
+ *----------------------------------------------------------------*/
+
+/*
+ * reverb mode parameters
+ */
+#define	SNDRV_EMU8000_REVERB_ROOM1	0
+#define SNDRV_EMU8000_REVERB_ROOM2	1
+#define	SNDRV_EMU8000_REVERB_ROOM3	2
+#define	SNDRV_EMU8000_REVERB_HALL1	3
+#define	SNDRV_EMU8000_REVERB_HALL2	4
+#define	SNDRV_EMU8000_REVERB_PLATE	5
+#define	SNDRV_EMU8000_REVERB_DELAY	6
+#define	SNDRV_EMU8000_REVERB_PANNINGDELAY 7
+#define SNDRV_EMU8000_REVERB_PREDEFINED	8
+/* user can define reverb modes up to 32 */
+#define SNDRV_EMU8000_REVERB_NUMBERS	32
+
+typedef struct soundfont_reverb_fx_t {
+	unsigned short parms[28];
+} soundfont_reverb_fx_t;
+
+/* reverb mode settings; write the following 28 data of 16 bit length
+ *   on the corresponding ports in the reverb_cmds array
+ */
+static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
+static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
+{{  /* room 1 */
+	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* room 2 */
+	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* room 3 */
+	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+}},
+{{  /* hall 1 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+}},
+{{  /* hall 2 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* plate */
+	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* delay */
+	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+{{  /* panning delay */
+	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+};
+
+enum { DATA1, DATA2 };
+#define AWE_INIT1(c)	EMU8000_CMD(2,c), DATA1
+#define AWE_INIT2(c)	EMU8000_CMD(2,c), DATA2
+#define AWE_INIT3(c)	EMU8000_CMD(3,c), DATA1
+#define AWE_INIT4(c)	EMU8000_CMD(3,c), DATA2
+
+static struct reverb_cmd_pair {
+	unsigned short cmd, port;
+} reverb_cmds[28] = {
+  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
+};
+
+/*exported*/ int
+snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len)
+{
+	soundfont_reverb_fx_t rec;
+
+	if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
+		snd_printk("illegal reverb mode %d for uploading\n", mode);
+		return -EINVAL;
+	}
+	if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
+		return -EFAULT;
+	reverb_parm[mode] = rec;
+	reverb_defined[mode] = 1;
+	return 0;
+}
+
+/*exported*/ void
+snd_emu8000_update_reverb_mode(emu8000_t *emu)
+{
+	int effect = emu->reverb_mode;
+	int i;
+
+	if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
+	    (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
+		return;
+	for (i = 0; i < 28; i++) {
+		int port;
+		if (reverb_cmds[i].port == DATA1)
+			port = EMU8000_DATA1(emu);
+		else
+			port = EMU8000_DATA2(emu);
+		snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
+	}
+}
+
+
+/*----------------------------------------------------------------
+ * mixer interface
+ *----------------------------------------------------------------*/
+
+#define chip_t	emu8000_t
+
+/*
+ * bass/treble
+ */
+static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 11;
+	return 0;
+}
+
+static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
+	return 0;
+}
+
+static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1;
+	
+	val1 = ucontrol->value.integer.value[0] % 12;
+	spin_lock_irqsave(&emu->control_lock, flags);
+	if (kcontrol->private_value) {
+		change = val1 != emu->treble_level;
+		emu->treble_level = val1;
+	} else {
+		change = val1 != emu->bass_level;
+		emu->bass_level = val1;
+	}
+	spin_unlock_irqrestore(&emu->control_lock, flags);
+	snd_emu8000_update_equalizer(emu);
+	return change;
+}
+
+static snd_kcontrol_new_t mixer_bass_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Synth Tone Control - Bass",
+	info: mixer_bass_treble_info,
+	get: mixer_bass_treble_get,
+	put: mixer_bass_treble_put,
+	private_value: 0,
+};
+
+static snd_kcontrol_new_t mixer_treble_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Synth Tone Control - Treble",
+	info: mixer_bass_treble_info,
+	get: mixer_bass_treble_get,
+	put: mixer_bass_treble_put,
+	private_value: 1,
+};
+
+/*
+ * chorus/reverb mode
+ */
+static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1);
+	return 0;
+}
+
+static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
+	return 0;
+}
+
+static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1;
+	
+	spin_lock_irqsave(&emu->control_lock, flags);
+	if (kcontrol->private_value) {
+		val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
+		change = val1 != emu->chorus_mode;
+		emu->chorus_mode = val1;
+	} else {
+		val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
+		change = val1 != emu->reverb_mode;
+		emu->reverb_mode = val1;
+	}
+	spin_unlock_irqrestore(&emu->control_lock, flags);
+	if (change) {
+		if (kcontrol->private_value)
+			snd_emu8000_update_chorus_mode(emu);
+		else
+			snd_emu8000_update_reverb_mode(emu);
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t mixer_chorus_mode_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Chorus Mode",
+	info: mixer_chorus_reverb_info,
+	get: mixer_chorus_reverb_get,
+	put: mixer_chorus_reverb_put,
+	private_value: 1,
+};
+
+static snd_kcontrol_new_t mixer_reverb_mode_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Reverb Mode",
+	info: mixer_chorus_reverb_info,
+	get: mixer_chorus_reverb_get,
+	put: mixer_chorus_reverb_put,
+	private_value: 0,
+};
+
+/*
+ * FM OPL3 chorus/reverb depth
+ */
+static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
+	return 0;
+}
+
+static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1;
+	
+	val1 = ucontrol->value.integer.value[0] % 256;
+	spin_lock_irqsave(&emu->control_lock, flags);
+	if (kcontrol->private_value) {
+		change = val1 != emu->fm_chorus_depth;
+		emu->fm_chorus_depth = val1;
+	} else {
+		change = val1 != emu->fm_reverb_depth;
+		emu->fm_reverb_depth = val1;
+	}
+	spin_unlock_irqrestore(&emu->control_lock, flags);
+	if (change)
+		snd_emu8000_init_fm(emu);
+	return change;
+}
+
+static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "FM Chorus Depth",
+	info: mixer_fm_depth_info,
+	get: mixer_fm_depth_get,
+	put: mixer_fm_depth_put,
+	private_value: 1,
+};
+
+static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "FM Reverb Depth",
+	info: mixer_fm_depth_info,
+	get: mixer_fm_depth_get,
+	put: mixer_fm_depth_put,
+	private_value: 0,
+};
+
+
+static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = {
+	&mixer_bass_control,
+	&mixer_treble_control,
+	&mixer_chorus_mode_control,
+	&mixer_reverb_mode_control,
+	&mixer_fm_chorus_depth_control,
+	&mixer_fm_reverb_depth_control,
+};
+
+/*
+ * create and attach mixer elements for WaveTable treble/bass controls
+ */
+static int /*__init*/
+snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu)
+{
+	int i, err = 0;
+
+	snd_assert(emu != NULL && card != NULL, return -EINVAL);
+
+	spin_lock_init(&emu->control_lock);
+
+	memset(emu->controls, 0, sizeof(emu->controls));
+	for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
+		if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
+			goto __error;
+	}
+	return 0;
+
+__error:
+	for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
+		if (emu->controls[i])
+			snd_ctl_remove(card, emu->controls[i]);
+	}
+	return err;
+}
+
+
+/*
+ * free resources
+ */
+static int snd_emu8000_free(emu8000_t *hw)
+{
+	if (hw->res_port1) {
+		release_resource(hw->res_port1);
+		kfree_nocheck(hw->res_port1);
+	}
+	if (hw->res_port2) {
+		release_resource(hw->res_port2);
+		kfree_nocheck(hw->res_port2);
+	}
+	if (hw->res_port3) {
+		release_resource(hw->res_port3);
+		kfree_nocheck(hw->res_port3);
+	}
+	snd_magic_kfree(hw);
+	return 0;
+}
+
+/*
+ */
+static int snd_emu8000_dev_free(snd_device_t *device)
+{
+	emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO);
+	return snd_emu8000_free(hw);
+}
+
+/*
+ * initialize and register emu8000 synth device.
+ */
+/*exported*/ int
+snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret)
+{
+	snd_seq_device_t *awe;
+	emu8000_t *hw;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free: snd_emu8000_dev_free,
+	};
+
+	if (awe_ret)
+		*awe_ret = NULL;
+
+	if (seq_ports <= 0)
+		return 0;
+
+	hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL);
+	if (hw == NULL)
+		return -ENOMEM;
+	spin_lock_init(&hw->reg_lock);
+	hw->index = index;
+	hw->port1 = port;
+	hw->port2 = port + 0x400;
+	hw->port3 = port + 0x800;
+	if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
+	    !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
+	    !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
+		snd_emu8000_free(hw);
+		return -EBUSY;
+	}
+	hw->mem_size = 0;
+	hw->card = card;
+	hw->seq_ports = seq_ports;
+	hw->bass_level = 5;
+	hw->treble_level = 9;
+	hw->chorus_mode = 2;
+	hw->reverb_mode = 4;
+	hw->fm_chorus_depth = 0;
+	hw->fm_reverb_depth = 0;
+
+	if (snd_emu8000_detect(hw) < 0) {
+		snd_emu8000_free(hw);
+		return -ENODEV;
+	}
+
+	snd_emu8000_init_hw(hw);
+	if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
+		snd_emu8000_free(hw);
+		return err;
+	}
+	
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hw, &ops)) < 0) {
+		snd_emu8000_free(hw);
+		return err;
+	}
+	if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
+			       sizeof(emu8000_t*), &awe) >= 0) {
+		strcpy(awe->name, "EMU-8000");
+		*(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
+	}
+	if (awe_ret)
+		*awe_ret = awe;
+
+	return 0;
+}
+
+
+/*
+ * exported stuff
+ */
+
+EXPORT_SYMBOL(snd_emu8000_new);
+EXPORT_SYMBOL(snd_emu8000_poke);
+EXPORT_SYMBOL(snd_emu8000_peek);
+EXPORT_SYMBOL(snd_emu8000_poke_dw);
+EXPORT_SYMBOL(snd_emu8000_peek_dw);
+EXPORT_SYMBOL(snd_emu8000_dma_chan);
+EXPORT_SYMBOL(snd_emu8000_init_fm);
+EXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
+EXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
+EXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
+EXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
+EXPORT_SYMBOL(snd_emu8000_update_equalizer);
+
+#if 0
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emu8000_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_emu8000_exit(void)
+{
+}
+
+module_init(alsa_emu8000_init)
+module_exit(alsa_emu8000_exit)
+#endif
diff -Nru linux/sound/isa/sb/emu8000_callback.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_callback.c
--- linux/sound/isa/sb/emu8000_callback.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_callback.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,540 @@
+/*
+ *  synth callback routines for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include "emu8000_local.h"
+#include <sound/asoundef.h>
+
+/*
+ * prototypes
+ */
+static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port);
+static int start_voice(snd_emux_voice_t *vp);
+static void trigger_voice(snd_emux_voice_t *vp);
+static void release_voice(snd_emux_voice_t *vp);
+static void update_voice(snd_emux_voice_t *vp, int update);
+static void reset_voice(snd_emux_t *emu, int ch);
+static void terminate_voice(snd_emux_voice_t *vp);
+static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+#ifdef CONFIG_SND_OSSEMUL
+static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2);
+#endif
+static int load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len);
+
+static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp);
+static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch);
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+
+/*
+ * set up operators
+ */
+static snd_emux_operators_t emu8000_ops = {
+	owner:		THIS_MODULE,
+	get_voice:	get_voice,
+	prepare:	start_voice,
+	trigger:	trigger_voice,
+	release:	release_voice,
+	update:		update_voice,
+	terminate:	terminate_voice,
+	reset:		reset_voice,
+	sample_new:	snd_emu8000_sample_new,
+	sample_free:	snd_emu8000_sample_free,
+	sample_reset:	snd_emu8000_sample_reset,
+	load_fx:	load_fx,
+	sysex:		sysex,
+#ifdef CONFIG_SND_OSSEMUL
+	oss_ioctl:	oss_ioctl,
+#endif
+};
+
+void
+snd_emu8000_ops_setup(emu8000_t *hw)
+{
+	hw->emu->ops = emu8000_ops;
+}
+
+
+
+/*
+ * Terminate a voice
+ */
+static void
+release_voice(snd_emux_voice_t *vp)
+{
+	int dcysusv;
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+	EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;
+	EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);
+}
+
+
+/*
+ */
+static void
+terminate_voice(snd_emux_voice_t *vp)
+{
+	emu8000_t *hw; 
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);
+}
+
+
+/*
+ */
+static void
+update_voice(snd_emux_voice_t *vp, int update)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		set_volume(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		set_pitch(hw, vp);
+	if ((update & SNDRV_EMUX_UPDATE_PAN) &&
+	    vp->port->ctrls[EMUX_MD_REALTIME_PAN])
+		set_pan(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_FMMOD)
+		set_fmmod(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+		set_tremfreq(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+		set_fm2frq2(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_Q)
+		set_filterQ(hw, vp);
+}
+
+
+/*
+ * Find a channel (voice) within the EMU that is not in use or at least
+ * less in use than other channels.  Always returns a valid pointer
+ * no matter what.  If there is a real shortage of voices then one
+ * will be cut. Such is life.
+ *
+ * The channel index (vp->ch) must be initialized in this routine.
+ * In Emu8k, it is identical with the array index.
+ */
+static snd_emux_voice_t *
+get_voice(snd_emux_t *emu, snd_emux_port_t *port)
+{
+	int  i;
+	snd_emux_voice_t *vp;
+	emu8000_t *hw;
+
+	/* what we are looking for, in order of preference */
+	enum {
+		OFF=0, RELEASED, PLAYING, END
+	};
+
+	/* Keeps track of what we are finding */
+	struct best {
+		unsigned int  time;
+		int voice;
+	} best[END];
+	struct best *bp;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return NULL);
+
+	for (i = 0; i < END; i++) {
+		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+		best[i].voice = -1;
+	}
+
+	/*
+	 * Go through them all and get a best one to use.
+	 */
+	for (i = 0; i < emu->max_voices; i++) {
+		int state, val;
+
+		vp = &emu->voices[i];
+		state = vp->state;
+
+		if (state == SNDRV_EMUX_ST_OFF)
+			bp = best + OFF;
+		else if (state == SNDRV_EMUX_ST_RELEASED ||
+			 state == SNDRV_EMUX_ST_PENDING) {
+			bp = best + RELEASED;
+			val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;
+			if (! val)
+				bp = best + OFF;
+		}
+		else if (state & SNDRV_EMUX_ST_ON)
+			bp = best + PLAYING;
+		else
+			continue;
+
+		/* check if sample is finished playing (non-looping only) */
+		if (state != SNDRV_EMUX_ST_OFF &&
+		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+			val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
+			if (val >= vp->reg.loopstart)
+				bp = best + OFF;
+		}
+
+		if (vp->time < bp->time) {
+			bp->time = vp->time;
+			bp->voice = i;
+		}
+	}
+
+	for (i = 0; i < END; i++) {
+		if (best[i].voice >= 0) {
+			vp = &emu->voices[best[i].voice];
+			vp->ch = best[i].voice;
+			return vp;
+		}
+	}
+
+	/* not found */
+	return NULL;
+}
+
+/*
+ */
+static int
+start_voice(snd_emux_voice_t *vp)
+{
+	unsigned int temp;
+	int ch;
+	int addr;
+	snd_midi_channel_t *chan;
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return -EINVAL);
+	ch = vp->ch;
+	chan = vp->chan;
+
+	/* channel to be silent and idle */
+	EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
+	EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
+	EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
+	EMU8000_PTRX_WRITE(hw, ch, 0);
+	EMU8000_CPF_WRITE(hw, ch, 0);
+
+	/* set pitch offset */
+	set_pitch(hw, vp);
+
+	/* set envelope parameters */
+	EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);
+	EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);
+	EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);
+	EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);
+	EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);
+	/* decay/sustain parameter for volume envelope is used
+	   for triggerg the voice */
+
+	/* cutoff and volume */
+	set_volume(hw, vp);
+
+	/* modulation envelope heights */
+	EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);
+
+	/* lfo1/2 delay */
+	EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);
+	EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);
+
+	/* lfo1 pitch & cutoff shift */
+	set_fmmod(hw, vp);
+	/* lfo1 volume & freq */
+	set_tremfreq(hw, vp);
+	/* lfo2 pitch & freq */
+	set_fm2frq2(hw, vp);
+	/* pan & loop start */
+	set_pan(hw, vp);
+
+	/* chorus & loop end (chorus 8bit, MSB) */
+	addr = vp->reg.loopend - 1;
+	temp = vp->reg.parm.chorus;
+	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp <<24) | (unsigned int)addr;
+	EMU8000_CSL_WRITE(hw, ch, temp);
+
+	/* Q & current address (Q 4bit value, MSB) */
+	addr = vp->reg.start - 1;
+	temp = vp->reg.parm.filterQ;
+	temp = (temp<<28) | (unsigned int)addr;
+	EMU8000_CCCA_WRITE(hw, ch, temp);
+
+	/* clear unknown registers */
+	EMU8000_00A0_WRITE(hw, ch, 0);
+	EMU8000_0080_WRITE(hw, ch, 0);
+
+	/* reset volume */
+	temp = vp->vtarget << 16;
+	EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);
+	EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);
+
+	return 0;
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(snd_emux_voice_t *vp)
+{
+	int ch = vp->ch;
+	unsigned int temp;
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+
+	/* set reverb and pitch target */
+	temp = vp->reg.parm.reverb;
+	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;
+	EMU8000_PTRX_WRITE(hw, ch, temp);
+	EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);
+	EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);
+}
+
+/*
+ * reset voice parameters
+ */
+static void
+reset_voice(snd_emux_t *emu, int ch)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return);
+	EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
+	snd_emu8000_tweak_voice(hw, ch);
+}
+
+/*
+ * Set the pitch of a possibly playing note.
+ */
+static void
+set_pitch(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);
+}
+
+/*
+ * Set the volume of a possibly already playing note
+ */
+static void
+set_volume(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	int  ifatn;
+
+	ifatn = (unsigned char)vp->acutoff;
+	ifatn = (ifatn << 8);
+	ifatn |= (unsigned char)vp->avol;
+	EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);
+}
+
+/*
+ * Set pan and loop start address.
+ */
+static void
+set_pan(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned int temp;
+
+	temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);
+	EMU8000_PSST_WRITE(hw, vp->ch, temp);
+}
+
+#define MOD_SENSE 18
+
+static void
+set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned short fmmod;
+	short pitch;
+	unsigned char cutoff;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fmmod>>8);
+	cutoff = (vp->reg.parm.fmmod & 0xff);
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fmmod = ((unsigned char)pitch<<8) | cutoff;
+	EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);
+}
+
+/* set tremolo (lfo1) volume & frequency */
+static void
+set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned short fm2frq2;
+	short pitch;
+	unsigned char freq;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fm2frq2>>8);
+	freq = vp->reg.parm.fm2frq2 & 0xff;
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fm2frq2 = ((unsigned char)pitch<<8) | freq;
+	EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned int addr;
+	addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
+	addr |= (vp->reg.parm.filterQ << 28);
+	EMU8000_CCCA_WRITE(hw, vp->ch, addr);
+}
+
+/*
+ * set the envelope & LFO parameters to the default values
+ */
+static void
+snd_emu8000_tweak_voice(emu8000_t *emu, int i)
+{
+	/* set all mod/vol envelope shape to minimum */
+	EMU8000_ENVVOL_WRITE(emu, i, 0x8000);
+	EMU8000_ENVVAL_WRITE(emu, i, 0x8000);
+	EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);
+	EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);
+	EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);
+	EMU8000_PEFE_WRITE(emu, i, 0);  /* mod envelope height to zero */
+	EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */
+	EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);
+	EMU8000_IP_WRITE(emu, i, 0xE000);	/* no pitch shift */
+	EMU8000_IFATN_WRITE(emu, i, 0xFF00);	/* volume to minimum */
+	EMU8000_FMMOD_WRITE(emu, i, 0);
+	EMU8000_TREMFRQ_WRITE(emu, i, 0);
+	EMU8000_FM2FRQ2_WRITE(emu, i, 0);
+}
+
+/*
+ * sysex callback
+ */
+static void
+sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return);
+
+	switch (parsed) {
+	case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:
+		hw->chorus_mode = chset->gs_chorus_mode;
+		snd_emu8000_update_chorus_mode(hw);
+		break;
+
+	case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:
+		hw->reverb_mode = chset->gs_reverb_mode;
+		snd_emu8000_update_reverb_mode(hw);
+		break;
+	}
+}
+
+
+#ifdef CONFIG_SND_OSSEMUL
+/*
+ * OSS ioctl callback
+ */
+static int
+oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL);
+
+	switch (cmd) {
+	case _EMUX_OSS_REVERB_MODE:
+		hw->reverb_mode = p1;
+		snd_emu8000_update_reverb_mode(hw);
+		break;
+
+	case _EMUX_OSS_CHORUS_MODE:
+		hw->chorus_mode = p1;
+		snd_emu8000_update_chorus_mode(hw);
+		break;
+
+	case _EMUX_OSS_INITIALIZE_CHIP:
+		/* snd_emu8000_init(hw); */ /*ignored*/
+		break;
+
+	case _EMUX_OSS_EQUALIZER:
+		hw->bass_level = p1;
+		hw->treble_level = p2;
+		snd_emu8000_update_equalizer(hw);
+		break;
+	}
+	return 0;
+}
+#endif
+
+
+/*
+ * additional patch keys
+ */
+
+#define SNDRV_EMU8000_LOAD_CHORUS_FX	0x10	/* optarg=mode */
+#define SNDRV_EMU8000_LOAD_REVERB_FX	0x11	/* optarg=mode */
+
+
+/*
+ * callback routine
+ */
+
+static int
+load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len)
+{
+	emu8000_t *hw;
+	hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL);
+
+	switch (type) {
+	case SNDRV_EMU8000_LOAD_CHORUS_FX:
+		return snd_emu8000_load_chorus_fx(hw, mode, buf, len);
+	case SNDRV_EMU8000_LOAD_REVERB_FX:
+		return snd_emu8000_load_reverb_fx(hw, mode, buf, len);
+	}
+	return -EINVAL;
+}
+
diff -Nru linux/sound/isa/sb/emu8000_local.h linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_local.h
--- linux/sound/isa/sb/emu8000_local.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_local.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,42 @@
+#ifndef __EMU8000_LOCAL_H
+#define __EMU8000_LOCAL_H
+/*
+ *  Local defininitons for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emu8000.h>
+#include <sound/emu8000_reg.h>
+
+#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+/* emu8000_patch.c */
+int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count);
+int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr);
+void snd_emu8000_sample_reset(snd_emux_t *rec);
+
+/* emu8000_callback.c */
+void snd_emu8000_ops_setup(emu8000_t *emu);
+
+#endif	/* __EMU8000_LOCAL_H */
diff -Nru linux/sound/isa/sb/emu8000_patch.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_patch.c
--- linux/sound/isa/sb/emu8000_patch.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_patch.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,308 @@
+/*
+ *  Patch routines for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include "emu8000_local.h"
+#include <asm/uaccess.h>
+
+MODULE_PARM(emu8000_reset_addr, "i");
+MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
+
+int emu8000_reset_addr = 0;
+
+
+/*
+ * Open up channels.
+ */
+static int
+snd_emu8000_open_dma(emu8000_t *emu, int write)
+{
+	int i;
+
+	/* reserve all 30 voices for loading */
+	for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
+		snd_emux_lock_voice(emu->emu, i);
+		snd_emu8000_dma_chan(emu, i, write);
+	}
+
+	/* assign voice 31 and 32 to ROM */
+	EMU8000_VTFT_WRITE(emu, 30, 0);
+	EMU8000_PSST_WRITE(emu, 30, 0x1d8);
+	EMU8000_CSL_WRITE(emu, 30, 0x1e0);
+	EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
+	EMU8000_VTFT_WRITE(emu, 31, 0);
+	EMU8000_PSST_WRITE(emu, 31, 0x1d8);
+	EMU8000_CSL_WRITE(emu, 31, 0x1e0);
+	EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
+
+	return 0;
+}
+
+/*
+ * Close all dram channels.
+ */
+static void
+snd_emu8000_close_dma(emu8000_t *emu)
+{
+	int i;
+
+	for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
+		snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
+		snd_emux_unlock_voice(emu->emu, i);
+	}
+}
+
+/*
+ */
+
+#define BLANK_LOOP_START	4
+#define BLANK_LOOP_END		8
+#define BLANK_LOOP_SIZE		12
+#define BLANK_HEAD_SIZE		48
+
+/*
+ * Read a word from userland, taking care of conversions from
+ * 8bit samples etc.
+ */
+static unsigned short
+read_word(const void *buf, int offset, int mode)
+{
+	unsigned short c;
+	if (mode & SNDRV_SFNT_SAMPLE_8BITS) {
+		unsigned char cc;
+		get_user(cc, (unsigned char*)buf + offset);
+		c = cc << 8; /* convert 8bit -> 16bit */
+	} else {
+#ifdef SNDRV_LITTLE_ENDIAN
+		get_user(c, (unsigned short*)buf + offset);
+#else
+		unsigned short cc;
+		get_user(cc, (unsigned short*)buf + offset);
+		c = swab16(cc);
+#endif
+	}
+	if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED)
+		c ^= 0x8000; /* unsigned -> signed */
+	return c;
+}
+
+/*
+ */
+static void
+snd_emu8000_write_wait(emu8000_t *emu)
+{
+	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+}
+
+/*
+ * write sample word data
+ *
+ * You should not have to keep resetting the address each time
+ * as the chip is supposed to step on the next address automatically.
+ * It mostly does, but during writes of some samples at random it
+ * completely loses words (every one in 16 roughly but with no
+ * obvious pattern).
+ *
+ * This is therefore much slower than need be, but is at least
+ * working.
+ */
+inline static void
+write_word(emu8000_t *emu, int *offset, unsigned short data)
+{
+	if (emu8000_reset_addr) {
+		if (emu8000_reset_addr > 1)
+			snd_emu8000_write_wait(emu);
+		EMU8000_SMALW_WRITE(emu, *offset);
+	}
+	EMU8000_SMLD_WRITE(emu, data);
+	*offset += 1;
+}
+
+/*
+ * Write the sample to EMU800 memory.  This routine is invoked out of
+ * the generic soundfont routines as a callback.
+ */
+int
+snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp,
+		       snd_util_memhdr_t *hdr, const void *data, long count)
+{
+	int  i;
+	int  rc;
+	int  offset;
+	int  truesize;
+	int  dram_offset, dram_start;
+	emu8000_t *emu;
+
+	emu = snd_magic_cast(emu8000_t, rec->hw, return -EINVAL);
+	snd_assert(sp != NULL, return -EINVAL);
+
+	if (sp->v.size == 0)
+		return 0;
+
+	/* be sure loop points start < end */
+	if (sp->v.loopstart > sp->v.loopend) {
+		int tmp = sp->v.loopstart;
+		sp->v.loopstart = sp->v.loopend;
+		sp->v.loopend = tmp;
+	}
+
+	/* compute true data size to be loaded */
+	truesize = sp->v.size;
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+		truesize += sp->v.loopend - sp->v.loopstart;
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+		truesize += BLANK_LOOP_SIZE;
+
+	sp->block = snd_util_mem_alloc(hdr, truesize * 2);
+	if (sp->block == NULL) {
+		/*snd_printd("EMU8000: out of memory\n");*/
+		/* not ENOMEM (for compatibility) */
+		return -ENOSPC;
+	}
+
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
+		if (verify_area(VERIFY_READ, data, sp->v.size))
+			return -EFAULT;
+	} else {
+		if (verify_area(VERIFY_READ, data, sp->v.size * 2))
+			return -EFAULT;
+	}
+
+	/* recalculate address offset */
+	sp->v.end -= sp->v.start;
+	sp->v.loopstart -= sp->v.start;
+	sp->v.loopend -= sp->v.start;
+	sp->v.start = 0;
+
+	/* dram position (in word) -- mem_offset is byte */
+	dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
+	dram_start = dram_offset;
+
+	/* set the total size (store onto obsolete checksum value) */
+	sp->v.truesize = truesize * 2; /* in bytes */
+
+	snd_emux_terminate_all(emu->emu);
+	if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0)
+		return rc;
+
+	/* Set the address to start writing at */
+	snd_emu8000_write_wait(emu);
+	EMU8000_SMALW_WRITE(emu, dram_offset);
+
+	/*snd_emu8000_init_fm(emu);*/
+
+#if 0
+	/* first block - write 48 samples for silence */
+	if (! sp->block->offset) {
+		for (i = 0; i < BLANK_HEAD_SIZE; i++) {
+			write_word(emu, &dram_offset, 0);
+		}
+	}
+#endif
+
+	offset = 0;
+	for (i = 0; i < sp->v.size; i++) {
+		unsigned short s;
+
+		s = read_word(data, offset, sp->v.mode_flags);
+		offset++;
+		write_word(emu, &dram_offset, s);
+
+		/* we may take too long time in this loop.
+		 * so give controls back to kernel if needed.
+		 */
+		if (current->need_resched) {
+			if (current->state != TASK_RUNNING)
+				set_current_state(TASK_RUNNING);
+			schedule();
+		}
+
+		if (i == sp->v.loopend &&
+		    (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)))
+		{
+			int looplen = sp->v.loopend - sp->v.loopstart;
+			int k;
+
+			/* copy reverse loop */
+			for (k = 1; k <= looplen; k++) {
+				s = read_word(data, offset - k, sp->v.mode_flags);
+				write_word(emu, &dram_offset, s);
+			}
+			if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+				sp->v.loopend += looplen;
+			} else {
+				sp->v.loopstart += looplen;
+				sp->v.loopend += looplen;
+			}
+			sp->v.end += looplen;
+		}
+	}
+
+	/* if no blank loop is attached in the sample, add it */
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+		for (i = 0; i < BLANK_LOOP_SIZE; i++) {
+			write_word(emu, &dram_offset, 0);
+		}
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+		}
+	}
+
+	/* add dram offset */
+	sp->v.start += dram_start;
+	sp->v.end += dram_start;
+	sp->v.loopstart += dram_start;
+	sp->v.loopend += dram_start;
+
+	snd_emu8000_close_dma(emu);
+	snd_emu8000_init_fm(emu);
+
+	return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr)
+{
+	if (sp->block) {
+		snd_util_mem_free(hdr, sp->block);
+		sp->block = NULL;
+	}
+	return 0;
+}
+
+
+/*
+ * sample_reset callback - terminate voices
+ */
+void
+snd_emu8000_sample_reset(snd_emux_t *rec)
+{
+	snd_emux_terminate_all(rec);
+}
diff -Nru linux/sound/isa/sb/emu8000_synth.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_synth.c
--- linux/sound/isa/sb/emu8000_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_synth.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,130 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *     and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Emu8000 synth plug-in routine
+ *
+ *   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
+ */
+
+#include "emu8000_local.h"
+#include <linux/init.h>
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
+MODULE_DESCRIPTION("Emu8000 synth plug-in routine");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+/*----------------------------------------------------------------*/
+
+/*
+ * create a new hardware dependent device for Emu8000
+ */
+static int snd_emu8000_new_device(snd_seq_device_t *dev)
+{
+	emu8000_t *hw;
+	snd_emux_t *emu;
+
+	hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (hw == NULL)
+		return -EINVAL;
+
+	if (hw->emu)
+		return -EBUSY; /* already exists..? */
+
+	if (snd_emux_new(&emu) < 0)
+		return -ENOMEM;
+
+	hw->emu = emu;
+	snd_emu8000_ops_setup(hw);
+
+	emu->hw = hw;
+	emu->max_voices = EMU8000_DRAM_VOICES;
+	emu->num_ports = hw->seq_ports;
+
+	if (hw->memhdr) {
+		snd_printk("memhdr is already initialized!?\n");
+		snd_util_memhdr_free(hw->memhdr);
+	}
+	hw->memhdr = snd_util_memhdr_new(hw->mem_size);
+	if (hw->memhdr == NULL) {
+		snd_emux_free(emu);
+		hw->emu = NULL;
+		return -ENOMEM;
+	}
+
+	emu->memhdr = hw->memhdr;
+	emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */
+	emu->midi_devidx = 1;
+
+	if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) {
+		snd_emux_free(emu);
+		snd_util_memhdr_free(hw->memhdr);
+		hw->emu = NULL;
+		hw->memhdr = NULL;
+		return -ENOMEM;
+	}
+
+	dev->driver_data = hw;
+
+	return 0;
+}
+
+
+/*
+ * free all resources
+ */
+static int snd_emu8000_delete_device(snd_seq_device_t *dev)
+{
+	emu8000_t *hw;
+
+	if (dev->driver_data == NULL)
+		return 0; /* no synth was allocated actually */
+
+	hw = dev->driver_data;
+	if (hw->emu)
+		snd_emux_free(hw->emu);
+	if (hw->memhdr)
+		snd_util_memhdr_free(hw->memhdr);
+	hw->emu = NULL;
+	hw->memhdr = NULL;
+	return 0;
+}
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emu8000_init(void)
+{
+	
+	static snd_seq_dev_ops_t ops = {
+		snd_emu8000_new_device,
+		snd_emu8000_delete_device,
+	};
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*));
+}
+
+static void __exit alsa_emu8000_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
+}
+
+module_init(alsa_emu8000_init)
+module_exit(alsa_emu8000_exit)
diff -Nru linux/sound/isa/sb/es968.c linux-2.4.19-pre5-mjc/sound/isa/sb/es968.c
--- linux/sound/isa/sb/es968.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/es968.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,302 @@
+
+/*
+    card-es968.c - driver for ESS AudioDrive ES968 based soundcards.
+    Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
+
+    Thanks to Pierfrancesco 'qM2' Passerini.
+
+    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
+*/
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/sb.h>
+
+#define chip_t sb_t
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("ESS AudioDrive ES968");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,AudioDrive ES968}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for es968 based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for es968 based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable es968 based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for es968 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for es968 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for es968 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+struct snd_card_es968 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_es968_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+static struct isapnp_card *snd_es968_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_es968_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+static struct isapnp_card_id snd_es968_pnpids[] __devinitdata = {
+        {
+                ISAPNP_CARD_ID('E','S','S',0x0968),
+                devs: { ISAPNP_DEVICE_ID('E','S','S',0x0968), }
+        },
+        { ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_es968_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define	DRIVER_NAME	"snd-card-es968"
+
+
+static void snd_card_es968_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+	sb_t *chip = snd_magic_cast(sb_t, dev_id, return);
+
+	if (chip->open & SB_OPEN_PCM) {
+		snd_sb8dsp_interrupt(chip);
+	} else {
+		snd_sb8dsp_midi_interrupt(chip);
+	}
+}
+
+#ifdef __ISAPNP__
+static int __init snd_card_es968_isapnp(int dev, struct snd_card_es968 *acard)
+{
+	const struct isapnp_card_id *id = snd_es968_isapnp_id[dev];
+	struct isapnp_card *card = snd_es968_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma8[dev] = pdev->dma_resource[0].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	return 0;
+}
+
+static void snd_card_es968_deactivate(struct snd_card_es968 *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void __exit snd_card_es968_free(snd_card_t *card)
+{
+	struct snd_card_es968 *acard = (struct snd_card_es968 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_es968_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_es968_probe(int dev)
+{
+	int error;
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_card_es968 *acard;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_es968))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_es968 *)card->private_data;
+	card->private_free = snd_card_es968_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_es968_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	snd_printk("you have to enable PnP support ...\n");
+	snd_card_free(card);
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_sbdsp_create(card, snd_port[dev],
+				      snd_irq[dev],
+				      snd_card_es968_interrupt,
+				      snd_dma8[dev],
+				      -1,
+				      SB_HW_AUTO, &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	strcpy(card->driver, "ES968");
+	strcpy(card->shortname, "ESS ES968");
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+		card->shortname, chip->name, chip->port, snd_irq[dev], snd_dma8[dev]);
+
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_es968_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_es968_isapnp_detect(struct isapnp_card *card,
+                                          const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_es968_isapnp_cards[dev] = card;
+		snd_es968_isapnp_id[dev] = id;
+		res = snd_card_es968_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_es968_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_es968_pnpids, snd_es968_isapnp_detect);
+#else
+	snd_printk("you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		snd_printk("no ES968 based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_es968_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_es968_cards[dev]);
+}
+
+module_init(alsa_card_es968_init)
+module_exit(alsa_card_es968_exit)
+
+#ifndef MODULE
+
+/* format is: snd-es968=snd_enable,snd_index,snd_id,
+			snd_port,snd_irq,snd_dma1 */
+
+static int __init alsa_card_es968_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-es968=", alsa_card_es968_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/sb/sb16.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb16.c
--- linux/sound/isa/sb/sb16.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,711 @@
+/*
+ *  Driver for SoundBlaster 16/AWE32/AWE64 soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/sb16_csp.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/emu8000.h>
+#include <sound/seq_device.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t sb_t
+
+#ifdef SNDRV_SBAWE
+#define PFX "sbawe: "
+#else
+#define PFX "sb16: "
+#endif
+
+#ifndef SNDRV_SBAWE
+EXPORT_NO_SYMBOLS;
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+#ifndef SNDRV_SBAWE
+MODULE_DESCRIPTION("Sound Blaster 16");
+MODULE_DEVICES("{{Creative Labs,SB 16},"
+		"{Creative Labs,SB Vibra16S},"
+		"{Creative Labs,SB Vibra16C},"
+		"{Creative Labs,SB Vibra16CL},"
+		"{Creative Labs,SB Vibra16X}}");
+#else
+MODULE_DESCRIPTION("Sound Blaster AWE");
+MODULE_DEVICES("{{Creative Labs,SB AWE 32},"
+		"{Creative Labs,SB AWE 64},"
+		"{Creative Labs,SB AWE 64 Gold}}");
+#endif
+
+#if 0
+#define SNDRV_DEBUG_IRQ
+#endif
+
+#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE))
+#define SNDRV_SBAWE_EMU8000
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+#ifdef __ISAPNP__
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
+static long snd_mpu_port[SNDRV_CARDS] = {0x330, 0x300,[2 ... (SNDRV_CARDS - 1)] = -1};
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#ifdef SNDRV_SBAWE_EMU8000
+static long snd_awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#endif
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 5,6,7 */
+static int snd_mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#ifdef CONFIG_SND_SB16_CSP
+static int snd_csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for SoundBlaster 16 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for SoundBlaster 16 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable SoundBlaster 16 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260},{0x280}},dialog:list");
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for SB16 PnP driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x38c},{0x390},{0x394}},dialog:list");
+#ifdef SNDRV_SBAWE_EMU8000
+MODULE_PARM(snd_awe_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_awe_port, "AWE port # for SB16 PnP driver.");
+MODULE_PARM_SYNTAX(snd_awe_port, SNDRV_ENABLED ",allows:{{0x620},{0x640},{0x660},{0x680}},dialog:list");
+#endif
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC);
+MODULE_PARM(snd_mic_agc, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mic_agc, "Mic Auto-Gain-Control switch.");
+MODULE_PARM_SYNTAX(snd_mic_agc, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+#ifdef CONFIG_SND_SB16_CSP
+MODULE_PARM(snd_csp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_csp, "ASP/CSP chip support.");
+MODULE_PARM_SYNTAX(snd_csp, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_seq_ports, "Number of sequencer ports for WaveTable synth.");
+MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED ",allows:{{0,8}},skill:advanced");
+#endif
+
+struct snd_sb16 {
+	struct resource *fm_res;	/* used to block FM i/o region for legacy cards */
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+#ifdef SNDRV_SBAWE_EMU8000
+	struct isapnp_dev *devwt;
+#endif
+#endif
+};
+
+static snd_card_t *snd_sb16_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_sb16_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_sb16_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#define ISAPNP_SB16(_va, _vb, _vc, _device, _audio) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \
+	}
+#define ISAPNP_SBAWE(_va, _vb, _vc, _device, _audio, _awe) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _awe), } \
+	}
+
+static struct isapnp_card_id snd_sb16_pnpids[] __devinitdata = {
+#ifndef SNDRV_SBAWE
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x0024,0x0031),
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x0026,0x0031),
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x0027,0x0031),
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x0028,0x0031),
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x0029,0x0031),
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x002a,0x0031),
+	/* Sound Blaster 16 PnP */
+	/* Note: This card has also a CTL0051:StereoEnhance device!!! */
+	ISAPNP_SB16('C','T','L',0x002b,0x0031),
+	/* Sound Blaster 16 PnP */
+	ISAPNP_SB16('C','T','L',0x002c,0x0031),	
+	/* Sound Blaster Vibra16S */
+	ISAPNP_SB16('C','T','L',0x0051,0x0001),
+	/* Sound Blaster Vibra16C */
+	ISAPNP_SB16('C','T','L',0x0070,0x0001),
+	/* Sound Blaster Vibra16CL - added by ctm@ardi.com */
+	ISAPNP_SB16('C','T','L',0x0080,0x0041),
+	/* Sound Blaster Vibra16X */
+	ISAPNP_SB16('C','T','L',0x00f0,0x0043),
+#else  /* SNDRV_SBAWE defined */
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0035,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0039,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0042,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0043,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	/* Note: This card has also a CTL0051:StereoEnhance device!!! */
+	ISAPNP_SBAWE('C','T','L',0x0044,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	/* Note: This card has also a CTL0051:StereoEnhance device!!! */
+	ISAPNP_SBAWE('C','T','L',0x0045,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0047,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0048,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x0054,0x0031,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x009a,0x0041,0x0021),
+	/* Sound Blaster AWE 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x009c,0x0041,0x0021),
+	/* Sound Blaster 32 PnP */
+	ISAPNP_SBAWE('C','T','L',0x009f,0x0041,0x0021),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x009d,0x0042,0x0022),
+	/* Sound Blaster AWE 64 PnP Gold */
+	ISAPNP_SBAWE('C','T','L',0x009e,0x0044,0x0023),
+	/* Sound Blaster AWE 64 PnP Gold */
+	ISAPNP_SBAWE('C','T','L',0x00b2,0x0044,0x0023),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x00c1,0x0042,0x0022),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x00c3,0x0045,0x0022),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x00c5,0x0045,0x0022),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x00c7,0x0045,0x0022),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x00e4,0x0045,0x0022),
+	/* Sound Blaster AWE 64 PnP */
+	ISAPNP_SBAWE('C','T','L',0x00e9,0x0045,0x0022),
+	/* Sound Blaster 16 PnP (AWE) */
+	ISAPNP_SBAWE('C','T','L',0x00ed,0x0041,0x0070),
+	/* Generic entries */
+	ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0031,0x0021),
+	ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0041,0x0021),
+	ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0042,0x0022),
+	ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0044,0x0023),
+	ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0045,0x0022),
+#endif /* SNDRV_SBAWE */
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_sb16_pnpids);
+
+static int __init snd_sb16_isapnp(int dev, struct snd_sb16 *acard)
+{
+	const struct isapnp_card_id *id = snd_sb16_isapnp_id[dev];
+	struct isapnp_card *card = snd_sb16_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+#ifdef SNDRV_SBAWE_EMU8000
+	acard->devwt = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devwt->active) {
+		acard->dev = acard->devwt = NULL;
+		return -EBUSY;
+	}
+#endif	
+	/* Audio initialization */
+	pdev = acard->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_mpu_port[dev], 2);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4);
+	if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], 1);
+	if (snd_dma16[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev], 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev) < 0) {
+		printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n");
+		return -EBUSY;
+	}
+	snd_port[dev] = pdev->resource[0].start;
+	snd_mpu_port[dev] = pdev->resource[1].start;
+	snd_fm_port[dev] = pdev->resource[2].start;
+	snd_dma8[dev] = pdev->dma_resource[0].start;
+	snd_dma16[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
+			snd_port[dev], snd_mpu_port[dev], snd_fm_port[dev]);
+	snd_printdd("isapnp SB16: dma1=%i, dma2=%i, irq=%i\n",
+			snd_dma8[dev], snd_dma16[dev], snd_irq[dev]);
+#ifdef SNDRV_SBAWE_EMU8000
+	/* WaveTable initialization */
+	pdev = acard->devwt;
+	if (pdev->prepare(pdev)<0) {
+		acard->dev->deactivate(acard->dev);
+		return -EAGAIN;
+	}
+	if (snd_awe_port[dev] != SNDRV_AUTO_PORT) {
+		isapnp_resource_change(&pdev->resource[0], snd_awe_port[dev], 4);
+		isapnp_resource_change(&pdev->resource[1], snd_awe_port[dev] + 0x400, 4);
+		isapnp_resource_change(&pdev->resource[2], snd_awe_port[dev] + 0x800, 4);
+	}
+	if (pdev->activate(pdev)<0) {
+		printk(KERN_ERR PFX "WaveTable isapnp configure failure (out of resources?)\n");
+		acard->dev->deactivate(acard->dev);		
+		return -EBUSY;
+	}
+	snd_awe_port[dev] = pdev->resource[0].start;
+	snd_printdd("isapnp SB16: wavetable port=0x%lx\n", pdev->resource[0].start);
+#endif
+	return 0;
+}
+
+static void snd_sb16_deactivate(struct snd_sb16 *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+#ifdef SNDRV_SBAWE_EMU8000
+	if (acard->devwt) {
+		acard->devwt->deactivate(acard->devwt);
+		acard->devwt = NULL;
+	}
+#endif
+}
+
+#endif /* __ISAPNP__ */
+
+static void snd_sb16_free(snd_card_t *card)
+{
+	struct snd_sb16 *acard = (struct snd_sb16 *)card->private_data;
+
+	if (acard == NULL)
+		return;
+	if (acard->fm_res) {
+		release_resource(acard->fm_res);
+		kfree_nocheck(acard->fm_res);
+	}
+#ifdef __ISAPNP__
+	snd_sb16_deactivate(acard);
+#endif
+}
+
+static int __init snd_sb16_probe(int dev)
+{
+	static int possible_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_dmas8[] = {1, 3, 0, -1};
+	static int possible_dmas16[] = {5, 6, 7, -1};
+	int irq, dma8, dma16;
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_sb16 *acard;
+	opl3_t *opl3;
+	snd_hwdep_t *synth = NULL;
+#ifdef CONFIG_SND_SB16_CSP
+	snd_hwdep_t *csp = NULL;
+#endif
+	unsigned long flags;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_sb16));
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_sb16 *) card->private_data;
+	card->private_free = snd_sb16_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && snd_sb16_isapnp(dev, acard) < 0) {
+		snd_card_free(card);
+		return -EBUSY;
+	}
+#endif
+
+	irq = snd_irq[dev];
+	dma8 = snd_dma8[dev];
+	dma16 = snd_dma16[dev];
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			printk(KERN_ERR PFX "unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (dma8 == SNDRV_AUTO_DMA) {
+		if ((dma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) {
+			snd_card_free(card);
+			printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n");
+			return -EBUSY;
+		}
+	}
+	if (dma16 == SNDRV_AUTO_DMA) {
+		if ((dma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) {
+			snd_card_free(card);
+			printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n");
+			return -EBUSY;
+		}
+	}
+	/* non-PnP FM port address is hardwired with base port address */
+	snd_fm_port[dev] = snd_port[dev];
+	/* block the 0x388 port to avoid PnP conflicts */
+	acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+#ifdef SNDRV_SBAWE_EMU8000
+	/* non-PnP AWE port address is hardwired with base port address */
+	snd_awe_port[dev] = snd_port[dev] + 0x400;
+#endif
+#ifdef __ISAPNP__
+	}
+#endif
+
+	if ((err = snd_sbdsp_create(card,
+				    snd_port[dev],
+				    irq,
+				    snd_sb16dsp_interrupt,
+				    dma8,
+				    dma16,
+				    SB_HW_AUTO,
+				    &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (chip->hardware != SB_HW_16) {
+		snd_card_free(card);
+		snd_printdd("SB 16 chip was not detected at 0x%lx\n", snd_port[dev]);
+		return -ENODEV;
+	}
+	chip->mpu_port = snd_mpu_port[dev];
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) {
+#else
+	if ((err = snd_sb16dsp_configure(chip)) < 0) {
+#endif
+		snd_card_free(card);
+		return -ENXIO;
+	}
+	if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return -ENXIO;
+	}
+
+	if (chip->mpu_port) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
+					       chip->mpu_port, 0,
+					       irq, 0, &chip->rmidi)) < 0) {
+			snd_card_free(card);
+			return -ENXIO;
+		}
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_OPL3, snd_fm_port[dev] == snd_port[dev],
+				    &opl3) < 0) {
+			printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
+				   snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+#ifdef SNDRV_SBAWE_EMU8000
+			int seqdev = snd_awe_port[dev] > 0 ? 2 : 1;
+#else
+			int seqdev = 1;
+#endif
+			if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) {
+				snd_card_free(card);
+				return -ENXIO;
+			}
+		}
+	}
+
+	if ((err = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return -ENXIO;
+	}
+
+#ifdef CONFIG_SND_SB16_CSP
+	/* CSP chip on SB16ASP/AWE32 */
+	if ((chip->hardware == SB_HW_16) && snd_csp[dev]) {
+		snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &csp);
+		if (csp) {
+			chip->csp = csp->private_data;
+			chip->hardware = SB_HW_16CSP;
+		} else {
+			printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1);
+		}
+	}
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+	if (snd_awe_port[dev] > 0) {
+		if (snd_emu8000_new(card, 1, snd_awe_port[dev],
+				    snd_seq_ports[dev], NULL) < 0) {
+			printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", snd_awe_port[dev]);
+			snd_card_free(card);
+			return -ENXIO;
+		}
+	}
+#endif
+
+	/* setup Mic AGC */
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
+		(snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
+		(snd_mic_agc[dev] ? 0x00 : 0x01));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+	strcpy(card->driver, 
+#ifdef SNDRV_SBAWE_EMU8000
+			snd_awe_port[dev] > 0 ? "SB AWE" :
+#endif
+			"SB16");
+	strcpy(card->shortname, chip->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma ",
+		chip->name,
+		chip->port,
+		irq);
+	if (dma8 >= 0)
+		sprintf(card->longname + strlen(card->longname), "%d", dma8);
+	if (dma16 >= 0)
+		sprintf(card->longname + strlen(card->longname), "%s%d",
+			dma8 >= 0 ? "&" : "", dma16);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_sb16_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_sb16_probe_legacy_port(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		snd_port[dev] = port;
+		res = snd_sb16_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+#ifdef __ISAPNP__
+
+static int __init snd_sb16_isapnp_detect(struct isapnp_card *card,
+					 const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_sb16_isapnp_cards[dev] = card;
+		snd_sb16_isapnp_id[dev] = id;
+		res = snd_sb16_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_sb16_init(void)
+{
+	int dev, cards = 0;
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1};
+
+	/* legacy non-auto cards at first */
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (!snd_sb16_probe(dev)) {
+			cards++;
+			continue;
+		}
+#ifdef MODULE
+		printk(KERN_ERR "Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]);
+#endif			
+	}
+	/* legacy auto configured cards */
+	cards += snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port);
+#ifdef __ISAPNP__
+	/* ISA PnP cards at last */
+	cards += isapnp_probe_cards(snd_sb16_pnpids, snd_sb16_isapnp_detect);
+#endif
+
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n");
+#ifdef SNDRV_SBAWE_EMU8000
+		printk(KERN_ERR "In case, if you have non-AWE card, try snd-card-sb16 module\n");
+#else
+		printk(KERN_ERR "In case, if you have AWE card, try snd-card-sbawe module\n");
+#endif
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_sb16_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_sb16_cards[dev]);
+}
+
+module_init(alsa_card_sb16_init)
+module_exit(alsa_card_sb16_exit)
+
+#ifndef MODULE
+
+/* format is: snd-sb16=snd_enable,snd_index,snd_id,snd_isapnp,
+		       snd_port,snd_mpu_port,snd_fm_port,
+		       snd_irq,snd_dma8,snd_dma16,
+		       snd_mic_agc,snd_csp,
+		       [snd_awe_port,snd_seq_ports] */
+
+static int __init alsa_card_sb16_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+	int __attribute__ ((__unused__)) csp = INT_MAX;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma16[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mic_agc[nr_dev]) == 2
+#ifdef CONFIG_SND_SB16_CSP
+	       &&
+	       get_option(&str,&snd_csp[nr_dev]) == 2
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+	       &&
+	       get_option(&str,(int *)&snd_awe_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_seq_ports[nr_dev]) == 2
+#endif
+	       );
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+#ifdef CONFIG_SND_SB16_CSP
+	if (csp != INT_MAX)
+		snd_csp[nr_dev] = csp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+#ifndef SNDRV_SBAWE_EMU8000
+__setup("snd-sb16=", alsa_card_sb16_setup);
+#else
+__setup("snd-sbawe=", alsa_card_sb16_setup);
+#endif
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/sb/sb16_csp.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp.c
--- linux/sound/isa/sb/sb16_csp.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1196 @@
+/*
+ *  Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
+ *                        Takashi Iwai <tiwai@suse.de>
+ *
+ *  SB16ASP/AWE32 CSP control
+ *
+ *  CSP microcode loader:
+ *   alsa-tools/sb16_csp/ 
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/sb16_csp.h>
+#include <sound/initval.h>
+
+#define chip_t snd_sb_csp_t
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define CSP_HDR_VALUE(a,b,c,d)	((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
+#else
+#define CSP_HDR_VALUE(a,b,c,d)	((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
+#endif
+#define LE_SHORT(v)		le16_to_cpu(v)
+#define LE_INT(v)		le32_to_cpu(v)
+
+#define RIFF_HEADER	CSP_HDR_VALUE('R', 'I', 'F', 'F')
+#define CSP__HEADER	CSP_HDR_VALUE('C', 'S', 'P', ' ')
+#define LIST_HEADER	CSP_HDR_VALUE('L', 'I', 'S', 'T')
+#define FUNC_HEADER	CSP_HDR_VALUE('f', 'u', 'n', 'c')
+#define CODE_HEADER	CSP_HDR_VALUE('c', 'o', 'd', 'e')
+#define INIT_HEADER	CSP_HDR_VALUE('i', 'n', 'i', 't')
+#define MAIN_HEADER	CSP_HDR_VALUE('m', 'a', 'i', 'n')
+
+/*
+ * RIFF data format
+ */
+typedef struct riff_header {
+	__u32 name;
+	__u32 len;
+} riff_header_t;
+
+typedef struct desc_header {
+	riff_header_t info;
+	__u16 func_nr;
+	__u16 VOC_type;
+	__u16 flags_play_rec;
+	__u16 flags_16bit_8bit;
+	__u16 flags_stereo_mono;
+	__u16 flags_rates;
+} desc_header_t;
+
+/*
+ * prototypes
+ */
+static void snd_sb_csp_free(snd_hwdep_t *hw);
+static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file);
+static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg);
+static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file);
+
+static int csp_detect(sb_t *chip, int *version);
+static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val);
+static int set_register(sb_t *chip, unsigned char reg, unsigned char val);
+static int read_register(sb_t *chip, unsigned char reg);
+static int set_mode_register(sb_t *chip, unsigned char mode);
+static int get_version(sb_t *chip);
+
+static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * code);
+static int snd_sb_csp_unload(snd_sb_csp_t * p);
+static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags);
+static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode);
+static int snd_sb_csp_check_version(snd_sb_csp_t * p);
+
+static int snd_sb_csp_use(snd_sb_csp_t * p);
+static int snd_sb_csp_unuse(snd_sb_csp_t * p);
+static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels);
+static int snd_sb_csp_stop(snd_sb_csp_t * p);
+static int snd_sb_csp_pause(snd_sb_csp_t * p);
+static int snd_sb_csp_restart(snd_sb_csp_t * p);
+
+static int snd_sb_qsound_build(snd_sb_csp_t * p);
+static void snd_sb_qsound_destroy(snd_sb_csp_t * p);
+static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p);
+
+static int init_proc_entry(snd_sb_csp_t * p, int device);
+static void delete_proc_entry(snd_sb_csp_t * p);
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+
+/*
+ * Detect CSP chip and create a new instance
+ */
+int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep)
+{
+	snd_sb_csp_t *p;
+	int version, err;
+	snd_hwdep_t *hw;
+
+	if (rhwdep)
+		*rhwdep = NULL;
+
+	if (csp_detect(chip, &version))
+		return -ENODEV;
+
+	if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0)
+		return err;
+
+	if ((p = snd_magic_kcalloc(snd_sb_csp_t, 0, GFP_KERNEL)) == NULL) {
+		snd_device_free(chip->card, hw);
+		return -ENOMEM;
+	}
+	p->chip = chip;
+	p->version = version;
+
+	/* CSP operators */
+	p->ops.csp_use = snd_sb_csp_use;
+	p->ops.csp_unuse = snd_sb_csp_unuse;
+	p->ops.csp_autoload = snd_sb_csp_autoload;
+	p->ops.csp_start = snd_sb_csp_start;
+	p->ops.csp_stop = snd_sb_csp_stop;
+	p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer;
+
+	init_MUTEX(&p->access_mutex);
+	sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f));
+	hw->iface = SNDRV_HWDEP_IFACE_SB16CSP;
+	hw->private_data = p;
+	hw->private_free = snd_sb_csp_free;
+
+	/* operators - only write/ioctl */
+	hw->ops.open = snd_sb_csp_open;
+	hw->ops.ioctl = snd_sb_csp_ioctl;
+	hw->ops.release = snd_sb_csp_release;
+
+	/* create a proc entry */
+	init_proc_entry(p, device);
+	if (rhwdep)
+		*rhwdep = hw;
+	return 0;
+}
+
+/*
+ * free_private for hwdep instance
+ */
+static void snd_sb_csp_free(snd_hwdep_t *hwdep)
+{
+	snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hwdep->private_data, return);
+	if (p) {
+		if (p->running & SNDRV_SB_CSP_ST_RUNNING)
+			snd_sb_csp_stop(p);
+		delete_proc_entry(p);
+		snd_magic_kfree(p);
+	}
+}
+
+/* ------------------------------ */
+
+/*
+ * open the device exclusively
+ */
+static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file)
+{
+	snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO);
+	return (snd_sb_csp_use(p));
+}
+
+/*
+ * ioctl for hwdep device:
+ */
+static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO);
+	snd_sb_csp_info_t info;
+	snd_sb_csp_start_t start_info;
+	int err;
+
+	snd_assert(p != NULL, return -EINVAL);
+
+	if (snd_sb_csp_check_version(p))
+		return -ENODEV;
+
+	switch (cmd) {
+		/* get information */
+	case SNDRV_SB_CSP_IOCTL_INFO:
+		*info.codec_name = *p->codec_name;
+		info.func_nr = p->func_nr;
+		info.acc_format = p->acc_format;
+		info.acc_channels = p->acc_channels;
+		info.acc_width = p->acc_width;
+		info.acc_rates = p->acc_rates;
+		info.csp_mode = p->mode;
+		info.run_channels = p->run_channels;
+		info.run_width = p->run_width;
+		info.version = p->version;
+		info.state = p->running;
+		err = copy_to_user((void *) arg, &info, sizeof(info));
+		break;
+
+		/* load CSP microcode */
+	case SNDRV_SB_CSP_IOCTL_LOAD_CODE:
+		err = (p->running & SNDRV_SB_CSP_ST_RUNNING ?
+		       -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t *) arg));
+		break;
+	case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE:
+		err = (p->running & SNDRV_SB_CSP_ST_RUNNING ?
+		       -EBUSY : snd_sb_csp_unload(p));
+		break;
+
+		/* change CSP running state */
+	case SNDRV_SB_CSP_IOCTL_START:
+		if (copy_from_user(&start_info, (void *) arg, sizeof(start_info)))
+			err = -EFAULT;
+		else
+			err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels);
+		break;
+	case SNDRV_SB_CSP_IOCTL_STOP:
+		err = snd_sb_csp_stop(p);
+		break;
+	case SNDRV_SB_CSP_IOCTL_PAUSE:
+		err = snd_sb_csp_pause(p);
+		break;
+	case SNDRV_SB_CSP_IOCTL_RESTART:
+		err = snd_sb_csp_restart(p);
+		break;
+	default:
+		err = -ENOTTY;
+		break;
+	}
+
+	return err;
+}
+
+/*
+ * close the device
+ */
+static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file)
+{
+	snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO);
+	return (snd_sb_csp_unuse(p));
+}
+
+/* ------------------------------ */
+
+/*
+ * acquire device
+ */
+static int snd_sb_csp_use(snd_sb_csp_t * p)
+{
+	down(&p->access_mutex);
+	if (p->used) {
+		up(&p->access_mutex);
+		return -EAGAIN;
+	}
+	p->used++;
+	up(&p->access_mutex);
+
+	return 0;
+
+}
+
+/*
+ * release device
+ */
+static int snd_sb_csp_unuse(snd_sb_csp_t * p)
+{
+	down(&p->access_mutex);
+	p->used--;
+	up(&p->access_mutex);
+
+	return 0;
+}
+
+/*
+ * load microcode via ioctl: 
+ * code is user-space pointer
+ */
+static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * mcode)
+{
+	snd_sb_csp_mc_header_t info;
+
+	unsigned char *data_ptr, *data_end;
+	unsigned short func_nr = 0;
+
+	riff_header_t file_h, item_h, code_h;
+	__u32 item_type;
+	desc_header_t funcdesc_h;
+
+	unsigned long flags;
+	int err;
+
+	if (copy_from_user(&info, mcode, sizeof(info)))
+		return -EFAULT;
+	data_ptr = mcode->data;
+
+	if (copy_from_user(&file_h, data_ptr, sizeof(file_h)))
+		return -EFAULT;
+	if ((file_h.name != RIFF_HEADER) ||
+	    (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
+		snd_printd("%s: Invalid RIFF header\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	data_ptr += sizeof(file_h);
+	data_end = data_ptr + LE_INT(file_h.len);
+
+	if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
+		return -EFAULT;
+	if (item_type != CSP__HEADER) {
+		snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	data_ptr += sizeof (item_type);
+
+	for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) {
+		if (copy_from_user(&item_h, data_ptr, sizeof(item_h)))
+			return -EFAULT;
+		data_ptr += sizeof(item_h);
+		if (item_h.name != LIST_HEADER)
+			continue;
+
+		if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
+			 return -EFAULT;
+		switch (item_type) {
+		case FUNC_HEADER:
+			if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h)))
+				return -EFAULT;
+			func_nr = LE_SHORT(funcdesc_h.func_nr);
+			break;
+		case CODE_HEADER:
+			if (func_nr != info.func_req)
+				break;	/* not required function, try next */
+			data_ptr += sizeof(item_type);
+
+			/* destroy QSound mixer element */
+			if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+				snd_sb_qsound_destroy(p);
+			}
+			/* Clear all flags */
+			p->running = 0;
+			p->mode = 0;
+
+			/* load microcode blocks */
+			for (;;) {
+				if (data_ptr >= data_end)
+					return -EINVAL;
+				if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
+					return -EFAULT;
+
+				/* init microcode blocks */
+				if (code_h.name != INIT_HEADER)
+					break;
+				data_ptr += sizeof(code_h);
+				err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len),
+						      SNDRV_SB_CSP_LOAD_INITBLOCK | SNDRV_SB_CSP_LOAD_FROMUSER);
+				if (err)
+					return err;
+				data_ptr += LE_INT(code_h.len);
+			}
+			/* main microcode block */
+			if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
+				return -EFAULT;
+
+			if (code_h.name != MAIN_HEADER) {
+				snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__);
+				return -EINVAL;
+			}
+			data_ptr += sizeof(code_h);
+			err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len),
+					      SNDRV_SB_CSP_LOAD_FROMUSER);
+			if (err)
+				return err;
+
+			/* fill in codec header */
+			strncpy(p->codec_name, info.codec_name, sizeof(p->codec_name) - 1);
+			p->codec_name[sizeof(p->codec_name) - 1] = 0;
+			p->func_nr = func_nr;
+			p->mode = LE_SHORT(funcdesc_h.flags_play_rec);
+			switch (LE_SHORT(funcdesc_h.VOC_type)) {
+			case 0x0001:	/* QSound decoder */
+				if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) {
+					if (snd_sb_qsound_build(p) == 0)
+						/* set QSound flag and clear all other mode flags */
+						p->mode = SNDRV_SB_CSP_MODE_QSOUND;
+				}
+				p->acc_format = 0;
+				break;
+			case 0x0006:	/* A Law codec */
+				p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
+				break;
+			case 0x0007:	/* Mu Law codec */
+				p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
+				break;
+			case 0x0011:	/* what Creative thinks is IMA ADPCM codec */
+			case 0x0200:	/* Creative ADPCM codec */
+				p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
+				break;
+			case    201:	/* Text 2 Speech decoder */
+				/* TODO: Text2Speech handling routines */
+				p->acc_format = 0;
+				break;
+			case 0x0202:	/* Fast Speech 8 codec */
+			case 0x0203:	/* Fast Speech 10 codec */
+				p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL;
+				break;
+			default:	/* other codecs are unsupported */
+				p->acc_format = p->acc_width = p->acc_rates = 0;
+				p->mode = 0;
+				snd_printd("%s: Unsupported CSP codec type: 0x%04x\n",
+					   __FUNCTION__,
+					   LE_SHORT(funcdesc_h.VOC_type));
+				return -EINVAL;
+			}
+			p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono);
+			p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit);
+			p->acc_rates = LE_SHORT(funcdesc_h.flags_rates);
+
+			/* Decouple CSP from IRQ and DMAREQ lines */
+			spin_lock_irqsave(&p->chip->reg_lock, flags);
+			set_mode_register(p->chip, 0xfc);
+			set_mode_register(p->chip, 0x00);
+			spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+
+			/* finished loading successfully */
+			p->running = SNDRV_SB_CSP_ST_LOADED;	/* set LOADED flag */
+			return 0;
+		}
+	}
+	snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req);
+	return -EINVAL;
+}
+
+/*
+ * unload CSP microcode
+ */
+static int snd_sb_csp_unload(snd_sb_csp_t * p)
+{
+	if (p->running & SNDRV_SB_CSP_ST_RUNNING)
+		return -EBUSY;
+	if (!(p->running & SNDRV_SB_CSP_ST_LOADED))
+		return -ENXIO;
+
+	/* clear supported formats */
+	p->acc_format = 0;
+	p->acc_channels = p->acc_width = p->acc_rates = 0;
+	/* destroy QSound mixer element */
+	if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+		snd_sb_qsound_destroy(p);
+	}
+	/* clear all flags */
+	p->running = 0;
+	p->mode = 0;
+	return 0;
+}
+
+/*
+ * send command sequence to DSP
+ */
+static inline int command_seq(sb_t *chip, const unsigned char *seq, int size)
+{
+	int i;
+	for (i = 0; i < size; i++) {
+		if (!snd_sbdsp_command(chip, seq[i]))
+			return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * set CSP codec parameter
+ */
+static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val)
+{
+	unsigned char dsp_cmd[3];
+
+	dsp_cmd[0] = 0x05;	/* CSP set codec parameter */
+	dsp_cmd[1] = val;	/* Parameter value */
+	dsp_cmd[2] = par;	/* Parameter */
+	command_seq(chip, dsp_cmd, 3);
+	snd_sbdsp_command(chip, 0x03);	/* DSP read? */
+	if (snd_sbdsp_get_byte(chip) != par)
+		return -EIO;
+	return 0;
+}
+
+/*
+ * set CSP register
+ */
+static int set_register(sb_t *chip, unsigned char reg, unsigned char val)
+{
+	unsigned char dsp_cmd[3];
+
+	dsp_cmd[0] = 0x0e;	/* CSP set register */
+	dsp_cmd[1] = reg;	/* CSP Register */
+	dsp_cmd[2] = val;	/* value */
+	return command_seq(chip, dsp_cmd, 3);
+}
+
+/*
+ * read CSP register
+ * return < 0 -> error
+ */
+static int read_register(sb_t *chip, unsigned char reg)
+{
+	unsigned char dsp_cmd[2];
+
+	dsp_cmd[0] = 0x0f;	/* CSP read register */
+	dsp_cmd[1] = reg;	/* CSP Register */
+	command_seq(chip, dsp_cmd, 2);
+	return snd_sbdsp_get_byte(chip);	/* Read DSP value */
+}
+
+/*
+ * set CSP mode register
+ */
+static int set_mode_register(sb_t *chip, unsigned char mode)
+{
+	unsigned char dsp_cmd[2];
+
+	dsp_cmd[0] = 0x04;	/* CSP set mode register */
+	dsp_cmd[1] = mode;	/* mode */
+	return command_seq(chip, dsp_cmd, 2);
+}
+
+/*
+ * Detect CSP
+ * return 0 if CSP exists.
+ */
+static int csp_detect(sb_t *chip, int *version)
+{
+	unsigned char csp_test1, csp_test2;
+	unsigned long flags;
+	int result = -ENODEV;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	set_codec_parameter(chip, 0x00, 0x00);
+	set_mode_register(chip, 0xfc);		/* 0xfc = ?? */
+
+	csp_test1 = read_register(chip, 0x83);
+	set_register(chip, 0x83, ~csp_test1);
+	csp_test2 = read_register(chip, 0x83);
+	if (csp_test2 != (csp_test1 ^ 0xff))
+		goto __fail;
+
+	set_register(chip, 0x83, csp_test1);
+	csp_test2 = read_register(chip, 0x83);
+	if (csp_test2 != csp_test1)
+		goto __fail;
+
+	set_mode_register(chip, 0x00);		/* 0x00 = ? */
+
+	*version = get_version(chip);
+	snd_sbdsp_reset(chip);	/* reset DSP after getversion! */
+	if (*version >= 0x10 && *version <= 0x1f)
+		result = 0;		/* valid version id */
+
+      __fail:
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return result;
+}
+
+/*
+ * get CSP version number
+ */
+static int get_version(sb_t *chip)
+{
+	unsigned char dsp_cmd[2];
+
+	dsp_cmd[0] = 0x08;	/* SB_DSP_!something! */
+	dsp_cmd[1] = 0x03;	/* get chip version id? */
+	command_seq(chip, dsp_cmd, 2);
+
+	return (snd_sbdsp_get_byte(chip));
+}
+
+/*
+ * check if the CSP version is valid
+ */
+static int snd_sb_csp_check_version(snd_sb_csp_t * p)
+{
+	if (p->version < 0x10 || p->version > 0x1f) {
+		snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * download microcode to CSP (microcode should have one "main" block).
+ */
+static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags)
+{
+	int status, i;
+	int err;
+	int result = -EIO;
+	unsigned long flags;
+
+	spin_lock_irqsave(&p->chip->reg_lock, flags);
+	snd_sbdsp_command(p->chip, 0x01);	/* CSP download command */
+	if (snd_sbdsp_get_byte(p->chip)) {
+		snd_printd("%s: Download command failed\n", __FUNCTION__);
+		goto __fail;
+	}
+	/* Send CSP low byte (size - 1) */
+	snd_sbdsp_command(p->chip, (unsigned char)(size - 1));
+	/* Send high byte */
+	snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8));
+	/* send microcode sequence */
+	if (load_flags & SNDRV_SB_CSP_LOAD_FROMUSER) {
+		/* copy microcode from user space */
+		unsigned char *kbuf, *_kbuf;
+		_kbuf = kbuf = kmalloc (size, GFP_KERNEL);
+		if (copy_from_user(kbuf, buf, size)) {
+			result = -EFAULT;
+			kfree (_kbuf);
+			goto __fail;
+		}
+		while (size--) {
+			if (!snd_sbdsp_command(p->chip, *kbuf++)) {
+				kfree (_kbuf);
+				goto __fail;
+			}
+		}
+		kfree (_kbuf);
+	} else {
+		/* load from kernel space */
+		while (size--) {
+			if (!snd_sbdsp_command(p->chip, *buf++))
+				goto __fail;
+		}
+	}
+	if (snd_sbdsp_get_byte(p->chip))
+		goto __fail;
+
+	if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) {
+		i = 0;
+		/* some codecs (FastSpeech) take some time to initialize */
+		while (1) {
+			snd_sbdsp_command(p->chip, 0x03);
+			status = snd_sbdsp_get_byte(p->chip);
+			if (status == 0x55 || ++i >= 10)
+				break;
+			udelay (10);
+		}
+		if (status != 0x55) {
+			snd_printd("%s: Microcode initialization failed\n", __FUNCTION__);
+			goto __fail;
+		}
+	} else {
+		/*
+		 * Read mixer register SB_DSP4_DMASETUP after loading 'main' code.
+		 * Start CSP chip if no 16bit DMA channel is set - some kind
+		 * of autorun or perhaps a bugfix?
+		 */
+		spin_lock(&p->chip->mixer_lock);
+		status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
+		spin_unlock(&p->chip->mixer_lock);
+		if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) {
+			err = (set_codec_parameter(p->chip, 0xaa, 0x00) ||
+			       set_codec_parameter(p->chip, 0xff, 0x00));
+			snd_sbdsp_reset(p->chip);		/* really! */
+			if (err)
+				goto __fail;
+			set_mode_register(p->chip, 0xc0);	/* c0 = STOP */
+			set_mode_register(p->chip, 0x70);	/* 70 = RUN */
+		}
+	}
+	result = 0;
+
+      __fail:
+	spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+	return result;
+}
+
+#include "sb16_csp_codecs.h"
+
+/*
+ * autoload hardware codec if necessary
+ * return 0 if CSP is loaded and ready to run (p->running != 0)
+ */
+static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode)
+{
+	unsigned long flags;
+	int err = 0;
+
+	/* if CSP is running or manually loaded then exit */
+	if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED)) 
+		return -EBUSY;
+
+	/* autoload microcode only if requested hardware codec is not already loaded */
+	if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
+		p->running = SNDRV_SB_CSP_ST_AUTO;
+	} else {
+		switch (pcm_sfmt) {
+		case SNDRV_PCM_FORMAT_MU_LAW:
+			err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0);
+			p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
+			p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
+			break;
+		case SNDRV_PCM_FORMAT_A_LAW:
+			err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0);
+			p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
+			p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
+			break;
+		case SNDRV_PCM_FORMAT_IMA_ADPCM:
+			err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init),
+					      SNDRV_SB_CSP_LOAD_INITBLOCK);
+			if (err)
+				break;
+			if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) {
+				err = snd_sb_csp_load(p, &ima_adpcm_playback[0],
+						      sizeof(ima_adpcm_playback), 0);
+				p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE;
+			} else {
+				err = snd_sb_csp_load(p, &ima_adpcm_capture[0],
+						      sizeof(ima_adpcm_capture), 0);
+				p->mode = SNDRV_SB_CSP_MODE_DSP_READ;
+			}
+			p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
+			break;				  
+		default:
+			/* Decouple CSP from IRQ and DMAREQ lines */
+			if (p->running & SNDRV_SB_CSP_ST_AUTO) {
+				spin_lock_irqsave(&p->chip->reg_lock, flags);
+				set_mode_register(p->chip, 0xfc);
+				set_mode_register(p->chip, 0x00);
+				spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+				p->running = 0;			/* clear autoloaded flag */
+			}
+			return -EINVAL;
+		}
+		if (err) {
+			p->acc_format = 0;
+			p->acc_channels = p->acc_width = p->acc_rates = 0;
+
+			p->running = 0;				/* clear autoloaded flag */
+			p->mode = 0;
+			return (err);
+		} else {
+			p->running = SNDRV_SB_CSP_ST_AUTO;	/* set autoloaded flag */
+			p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT;	/* only 16 bit data */
+			p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO;
+			p->acc_rates = SNDRV_SB_CSP_RATE_ALL;	/* HW codecs accept all rates */
+		}   
+
+	}
+	return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO;
+}
+
+/*
+ * start CSP
+ */
+static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels)
+{
+	unsigned char s_type;	/* sample type */
+	unsigned char mixL, mixR;
+	int result = -EIO;
+	unsigned long flags;
+
+	if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
+		snd_printd("%s: Microcode not loaded\n", __FUNCTION__);
+		return -ENXIO;
+	}
+	if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
+		snd_printd("%s: CSP already running\n", __FUNCTION__);
+		return -EBUSY;
+	}
+	if (!(sample_width & p->acc_width)) {
+		snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__);
+		return -EINVAL;
+	}
+	if (!(channels & p->acc_channels)) {
+		snd_printd("%s: Invalid number of channels\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	/* Mute PCM volume */
+	spin_lock_irqsave(&p->chip->mixer_lock, flags);
+	mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+	mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+
+	spin_lock(&p->chip->reg_lock);
+	set_mode_register(p->chip, 0xc0);	/* c0 = STOP */
+	set_mode_register(p->chip, 0x70);	/* 70 = RUN */
+
+	s_type = 0x00;
+	if (channels == SNDRV_SB_CSP_MONO)
+		s_type = 0x11;	/* 000n 000n    (n = 1 if mono) */
+	if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
+		s_type |= 0x22;	/* 00dX 00dX    (d = 1 if 8 bit samples) */
+
+	if (set_codec_parameter(p->chip, 0x81, s_type)) {
+		snd_printd("%s: Set sample type command failed\n", __FUNCTION__);
+		goto __fail;
+	}
+	if (set_codec_parameter(p->chip, 0x80, 0x00)) {
+		snd_printd("%s: Codec start command failed\n", __FUNCTION__);
+		goto __fail;
+	}
+	p->run_width = sample_width;
+	p->run_channels = channels;
+
+	p->running |= SNDRV_SB_CSP_ST_RUNNING;
+
+	if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
+		set_codec_parameter(p->chip, 0xe0, 0x01);
+		/* enable QSound decoder */
+		set_codec_parameter(p->chip, 0x00, 0xff);
+		set_codec_parameter(p->chip, 0x01, 0xff);
+		p->running |= SNDRV_SB_CSP_ST_QSOUND;
+		/* set QSound startup value */
+		snd_sb_csp_qsound_transfer(p);
+	}
+	result = 0;
+
+      __fail:
+	spin_unlock(&p->chip->reg_lock);
+
+	/* restore PCM volume */
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+	spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+
+	return result;
+}
+
+/*
+ * stop CSP
+ */
+static int snd_sb_csp_stop(snd_sb_csp_t * p)
+{
+	int result;
+	unsigned char mixL, mixR;
+	unsigned long flags;
+
+	if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
+		return 0;
+
+	/* Mute PCM volume */
+	spin_lock_irqsave(&p->chip->mixer_lock, flags);
+	mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+	mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+
+	spin_lock(&p->chip->reg_lock);
+	if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+		set_codec_parameter(p->chip, 0xe0, 0x01);
+		/* disable QSound decoder */
+		set_codec_parameter(p->chip, 0x00, 0x00);
+		set_codec_parameter(p->chip, 0x01, 0x00);
+
+		p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+	}
+	result = set_mode_register(p->chip, 0xc0);	/* c0 = STOP */
+	spin_unlock(&p->chip->reg_lock);
+
+	/* restore PCM volume */
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+	snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+	spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+
+	if (!(result))
+		p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING);
+	return result;
+}
+
+/*
+ * pause CSP codec and hold DMA transfer
+ */
+static int snd_sb_csp_pause(snd_sb_csp_t * p)
+{
+	int result;
+	unsigned long flags;
+
+	if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
+		return -EBUSY;
+
+	spin_lock_irqsave(&p->chip->reg_lock, flags);
+	result = set_codec_parameter(p->chip, 0x80, 0xff);
+	spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+	if (!(result))
+		p->running |= SNDRV_SB_CSP_ST_PAUSED;
+
+	return result;
+}
+
+/*
+ * restart CSP codec and resume DMA transfer
+ */
+static int snd_sb_csp_restart(snd_sb_csp_t * p)
+{
+	int result;
+	unsigned long flags;
+
+	if (!(p->running & SNDRV_SB_CSP_ST_PAUSED))
+		return -EBUSY;
+
+	spin_lock_irqsave(&p->chip->reg_lock, flags);
+	result = set_codec_parameter(p->chip, 0x80, 0x00);
+	spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+	if (!(result))
+		p->running &= ~SNDRV_SB_CSP_ST_PAUSED;
+
+	return result;
+}
+
+/* ------------------------------ */
+
+/*
+ * QSound mixer control for PCM
+ */
+
+static int snd_sb_qsound_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0;
+	return 0;
+}
+
+static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval;
+	
+	nval = ucontrol->value.integer.value[0] & 0x01;
+	spin_lock_irqsave(&p->q_lock, flags);
+	change = p->q_enabled != nval;
+	p->q_enabled = nval;
+	spin_unlock_irqrestore(&p->q_lock, flags);
+	return change;
+}
+
+static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+	return 0;
+}
+
+static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&p->q_lock, flags);
+	ucontrol->value.integer.value[0] = p->qpos_left;
+	ucontrol->value.integer.value[1] = p->qpos_right;
+	spin_unlock_irqrestore(&p->q_lock, flags);
+	return 0;
+}
+
+static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval1, nval2;
+	
+	nval1 = ucontrol->value.integer.value[0];
+	if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
+		nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+	nval2 = ucontrol->value.integer.value[1];
+	if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
+		nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+	spin_lock_irqsave(&p->q_lock, flags);
+	change = p->qpos_left != nval1 || p->qpos_right != nval2;
+	p->qpos_left = nval1;
+	p->qpos_right = nval2;
+	p->qpos_changed = change;
+	spin_unlock_irqrestore(&p->q_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_sb_qsound_switch = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "3D Control - Switch",
+	info: snd_sb_qsound_switch_info,
+	get: snd_sb_qsound_switch_get,
+	put: snd_sb_qsound_switch_put
+};
+
+static snd_kcontrol_new_t snd_sb_qsound_space = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "3D Control - Space",
+	info: snd_sb_qsound_space_info,
+	get: snd_sb_qsound_space_get,
+	put: snd_sb_qsound_space_put
+};
+
+static int snd_sb_qsound_build(snd_sb_csp_t * p)
+{
+	snd_card_t * card;
+	int err;
+
+	snd_assert(p != NULL, return -EINVAL);
+
+	card = p->chip->card;
+	p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2;
+	p->qpos_changed = 0;
+
+	spin_lock_init(&p->q_lock);
+
+	if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0)
+		goto __error;
+	if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0)
+		goto __error;
+
+	return 0;
+
+     __error:
+	snd_sb_qsound_destroy(p);
+	return err;
+}
+
+static void snd_sb_qsound_destroy(snd_sb_csp_t * p)
+{
+	snd_card_t * card;
+	unsigned long flags;
+
+	snd_assert(p != NULL, return);
+
+	card = p->chip->card;	
+	
+	if (p->qsound_switch)
+		snd_ctl_remove(card, p->qsound_switch);
+	if (p->qsound_space)
+		snd_ctl_remove(card, p->qsound_space);
+
+	/* cancel pending transfer of QSound parameters */
+	spin_lock_irqsave (&p->q_lock, flags);
+	p->qpos_changed = 0;
+	spin_unlock_irqrestore (&p->q_lock, flags);
+}
+
+/*
+ * Transfer qsound parameters to CSP,
+ * function should be called from interrupt routine
+ */
+static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p)
+{
+	int err = -ENXIO;
+
+	spin_lock(&p->q_lock);
+	if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+		set_codec_parameter(p->chip, 0xe0, 0x01);
+		/* left channel */
+		set_codec_parameter(p->chip, 0x00, p->qpos_left);
+		set_codec_parameter(p->chip, 0x02, 0x00);
+		/* right channel */
+		set_codec_parameter(p->chip, 0x00, p->qpos_right);
+		set_codec_parameter(p->chip, 0x03, 0x00);
+		err = 0;
+	}
+	p->qpos_changed = 0;
+	spin_unlock(&p->q_lock);
+	return err;
+}
+
+/* ------------------------------ */
+
+/*
+ * proc interface
+ */
+static int init_proc_entry(snd_sb_csp_t * p, int device)
+{
+	char name[16];
+	snd_info_entry_t *entry;
+	sprintf(name, "cspD%d", device);
+	entry = p->proc = snd_info_create_card_entry(p->chip->card, name, p->chip->card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = info_read;
+		entry->private_data = p;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			p->proc = NULL;
+		}
+	}
+	return 0;
+}
+
+static void delete_proc_entry(snd_sb_csp_t * p)
+{
+	if (p->proc) {
+		snd_info_unregister(p->proc);
+		p->proc = NULL;
+	}
+}
+
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return);
+
+	snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f));
+	snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'),
+		    ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'),
+		    ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'),
+		    ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-'));
+	if (p->running & SNDRV_SB_CSP_ST_LOADED) {
+		snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr);
+		snd_iprintf(buffer, "Sample rates: ");
+		if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) {
+			snd_iprintf(buffer, "All\n");
+		} else {
+			snd_iprintf(buffer, "%s%s%s%s\n",
+				    ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""),
+				    ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""),
+				    ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""),
+				    ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : ""));
+		}
+		if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+			snd_iprintf(buffer, "QSound decoder %sabled\n",
+				    p->q_enabled ? "en" : "dis");
+		} else {
+			snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n",
+				    p->acc_format,
+				    ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"),
+				    ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"),
+				    ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"),
+				    ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"),
+				    ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"),
+				    ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-"));
+		}
+	}
+	if (p->running & SNDRV_SB_CSP_ST_AUTO) {
+		snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n");
+	}
+	if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
+		snd_iprintf(buffer, "Processing %dbit %s PCM samples\n",
+			    ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8),
+			    ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo"));
+	}
+	if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+		snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n",
+			    p->qpos_left, p->qpos_right);
+	}
+}
+
+/* */
+
+EXPORT_SYMBOL(snd_sb_csp_new);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_sb_csp_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_sb_csp_exit(void)
+{
+}
+
+module_init(alsa_sb_csp_init)
+module_exit(alsa_sb_csp_exit)
diff -Nru linux/sound/isa/sb/sb16_csp_codecs.h linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp_codecs.h
--- linux/sound/isa/sb/sb16_csp_codecs.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp_codecs.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,949 @@
+/*
+ *  Copyright (c) 1994 Creative Technology Ltd.
+ *  Microcode files for SB16 Advanced Signal Processor
+ *
+ *   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
+ *
+ */
+
+static unsigned char mulaw_main[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
+	0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+	0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
+	0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
+	0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
+	0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
+	0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
+	0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
+	0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
+	0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
+	0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
+	0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
+	0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
+	0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
+	0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
+	0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
+	0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
+	0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
+	0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
+	0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
+	0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
+	0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
+	0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
+	0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
+	0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
+	0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
+	0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
+	0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
+	0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
+	0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
+	0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54,
+	0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
+	0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b,
+	0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
+	0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
+	0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+	0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+	0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
+	0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+	0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b,
+	0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+	0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
+	0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
+	0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
+	0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
+	0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
+	0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+	0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
+	0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
+	0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
+	0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
+	0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c,
+	0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
+	0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
+	0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
+	0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
+	0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80,
+	0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39,
+	0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
+	0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
+	0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
+	0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
+	0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
+	0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
+	0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
+	0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
+	0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
+	0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
+	0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
+	0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
+	0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
+	0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
+	0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
+	0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0,
+	0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
+	0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b,
+	0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c,
+	0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e,
+	0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
+	0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18,
+	0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10,
+	0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80,
+	0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10,
+	0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10,
+	0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
+	0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
+	0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
+	0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
+	0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54,
+	0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44,
+	0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54,
+	0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
+	0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54,
+	0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44,
+	0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54,
+	0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
+	0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54,
+	0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44,
+	0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54,
+	0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+	0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54,
+	0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44,
+	0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54,
+	0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00
+};
+
+static unsigned char alaw_main[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
+	0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+	0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
+	0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
+	0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
+	0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
+	0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
+	0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
+	0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
+	0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
+	0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
+	0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
+	0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
+	0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
+	0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
+	0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
+	0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
+	0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
+	0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
+	0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
+	0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
+	0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
+	0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
+	0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
+	0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
+	0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
+	0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
+	0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
+	0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
+	0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
+	0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54,
+	0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
+	0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b,
+	0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
+	0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
+	0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+	0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+	0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
+	0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+	0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b,
+	0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80,
+	0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
+	0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
+	0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
+	0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
+	0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
+	0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+	0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
+	0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
+	0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
+	0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
+	0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c,
+	0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
+	0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
+	0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
+	0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
+	0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49,
+	0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39,
+	0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49,
+	0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39,
+	0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
+	0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
+	0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
+	0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
+	0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
+	0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
+	0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
+	0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
+	0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
+	0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
+	0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
+	0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
+	0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
+	0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
+	0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
+	0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0,
+	0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44,
+	0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b,
+	0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c,
+	0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e,
+	0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
+	0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10,
+	0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b,
+	0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80,
+	0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80,
+	0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19,
+	0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49,
+	0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18,
+	0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb,
+	0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80,
+	0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
+	0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
+	0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
+	0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
+	0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54,
+	0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44,
+	0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54,
+	0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
+	0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54,
+	0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44,
+	0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54,
+	0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
+	0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54,
+	0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44,
+	0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54,
+	0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+	0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54,
+	0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44,
+	0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54,
+	0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00
+};
+
+
+static unsigned char ima_adpcm_init[] = {
+	0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e,
+	0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+	0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19,
+	0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19,
+	0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
+	0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49,
+	0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00
+};
+
+static unsigned char ima_adpcm_playback[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44,
+	0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80,
+	0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b,
+	0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b,
+	0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
+	0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+	0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80,
+	0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+	0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+	0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14,
+	0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8,
+	0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49,
+	0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
+	0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
+	0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
+	0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
+	0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
+	0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4,
+	0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
+	0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
+	0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
+	0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
+	0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
+	0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
+	0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
+	0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
+	0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
+	0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
+	0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
+	0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
+	0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
+	0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
+	0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
+	0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
+	0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
+	0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
+	0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
+	0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
+	0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
+	0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
+	0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
+	0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
+	0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
+	0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
+	0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84,
+	0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39,
+	0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19,
+	0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19,
+	0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
+	0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+	0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19,
+	0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+	0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+	0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54,
+	0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44,
+	0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44,
+	0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84,
+	0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+	0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+	0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49,
+	0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a,
+	0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+	0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19,
+	0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b,
+	0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19,
+	0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39,
+	0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a,
+	0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
+	0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
+	0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
+	0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11,
+	0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10,
+	0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18,
+	0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44,
+	0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4,
+	0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19,
+	0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
+	0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
+	0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+	0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
+	0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
+	0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+	0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19,
+	0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44,
+	0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
+	0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a,
+	0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39,
+	0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39,
+	0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
+	0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e,
+	0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44,
+	0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44,
+	0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
+	0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+	0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
+	0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b,
+	0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80,
+	0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e,
+	0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf,
+	0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b,
+	0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+	0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b,
+	0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+	0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b,
+	0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+	0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80,
+	0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b,
+	0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80,
+	0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb,
+	0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+	0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19,
+	0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a,
+	0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e,
+	0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44,
+	0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44,
+	0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60,
+	0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f,
+	0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80,
+	0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2,
+	0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4,
+	0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+	0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4,
+	0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+	0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4,
+	0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+	0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11,
+	0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4,
+	0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+	0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22,
+	0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80,
+	0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b,
+	0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a,
+	0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02,
+	0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60,
+	0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82,
+	0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb,
+	0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b,
+	0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f,
+	0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f,
+	0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a,
+	0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
+	0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
+	0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02,
+	0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09,
+	0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a,
+	0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
+	0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
+	0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39,
+	0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a,
+	0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19,
+	0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a,
+	0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a,
+	0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54,
+	0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44,
+	0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54,
+	0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+	0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54,
+	0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44,
+	0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54,
+	0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44,
+	0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf,
+	0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80,
+	0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22,
+	0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82,
+	0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82,
+	0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44,
+	0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+	0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
+	0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
+	0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
+	0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09,
+	0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80,
+	0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a,
+	0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
+	0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a,
+	0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+	0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+	0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+	0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+	0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80,
+	0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
+	0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80,
+	0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00
+};
+
+static unsigned char ima_adpcm_capture[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44,
+	0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b,
+	0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19,
+	0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b,
+	0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b,
+	0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19,
+	0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19,
+	0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19,
+	0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+	0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
+	0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14,
+	0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8,
+	0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24,
+	0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44,
+	0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18,
+	0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19,
+	0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8,
+	0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8,
+	0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
+	0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a,
+	0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+	0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
+	0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
+	0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
+	0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4,
+	0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
+	0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
+	0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
+	0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
+	0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
+	0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
+	0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
+	0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
+	0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
+	0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
+	0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
+	0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
+	0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
+	0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
+	0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
+	0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
+	0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
+	0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
+	0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
+	0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
+	0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
+	0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
+	0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
+	0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
+	0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
+	0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
+	0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84,
+	0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19,
+	0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+	0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19,
+	0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+	0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
+	0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+	0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19,
+	0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
+	0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+	0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+	0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14,
+	0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80,
+	0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49,
+	0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80,
+	0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b,
+	0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b,
+	0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80,
+	0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44,
+	0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44,
+	0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
+	0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4,
+	0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44,
+	0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e,
+	0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84,
+	0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10,
+	0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
+	0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10,
+	0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80,
+	0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a,
+	0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4,
+	0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4,
+	0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4,
+	0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44,
+	0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18,
+	0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
+	0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19,
+	0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19,
+	0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
+	0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02,
+	0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
+	0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f,
+	0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44,
+	0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
+	0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44,
+	0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f,
+	0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b,
+	0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b,
+	0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80,
+	0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84,
+	0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
+	0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a,
+	0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+	0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a,
+	0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44,
+	0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18,
+	0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb,
+	0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a,
+	0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8,
+	0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
+	0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
+	0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44,
+	0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80,
+	0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4,
+	0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+	0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
+	0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
+	0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
+	0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02,
+	0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09,
+	0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44,
+	0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f,
+	0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82,
+	0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49,
+	0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+	0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+	0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
+	0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f,
+	0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e,
+	0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80,
+	0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14,
+	0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
+	0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
+	0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
+	0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4,
+	0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+	0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+	0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
+	0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e,
+	0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
+	0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+	0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+	0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+	0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60,
+	0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46,
+	0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0,
+	0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14,
+	0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
+	0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
+	0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
+	0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4,
+	0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+	0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+	0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
+	0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e,
+	0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
+	0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+	0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+	0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+	0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46,
+	0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80,
+	0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
+	0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46,
+	0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14,
+	0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a,
+	0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19,
+	0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a,
+	0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19,
+	0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a,
+	0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19,
+	0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a,
+	0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19,
+	0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a,
+	0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19,
+	0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a,
+	0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19,
+	0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a,
+	0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19,
+	0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a,
+	0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19,
+	0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b,
+	0xa6, 0xc5, 0x11, 0x00
+};
diff -Nru linux/sound/isa/sb/sb16_main.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_main.c
--- linux/sound/isa/sb/sb16_main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_main.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,899 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of 16-bit SoundBlaster cards and clones
+ *  Note: This is very ugly hardware which uses one 8-bit DMA channel and
+ *        second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't
+ *        transfer 16-bit samples and 16-bit DMA channels can't transfer
+ *        8-bit samples. This make full duplex more complicated than
+ *        can be... People, don't buy these soundcards for full 16-bit
+ *        duplex!!!
+ *  Note: 16-bit wide is assigned to first direction which made request.
+ *        With full duplex - playback is preferred with abstract layer.
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/sb16_csp.h>
+#include <sound/mpu401.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones");
+MODULE_LICENSE("GPL");
+
+#define chip_t sb_t
+
+#ifdef CONFIG_SND_SB16_CSP
+static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+	if (chip->hardware == SB_HW_16CSP) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+			/* manually loaded codec */
+			if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) &&
+			    ((1 << runtime->format) == csp->acc_format)) {
+				/* Supported runtime PCM format for playback */
+				if (csp->ops.csp_use(csp) == 0) {
+					/* If CSP was successfully acquired */
+					goto __start_CSP;
+				}
+			} else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) {
+				/* QSound decoder is loaded and enabled */
+				if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+							      SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) {
+					/* Only for simple PCM formats */
+					if (csp->ops.csp_use(csp) == 0) {
+						/* If CSP was successfully acquired */
+						goto __start_CSP;
+					}
+				}
+			}
+		} else if (csp->ops.csp_use(csp) == 0) {
+			/* Acquire CSP and try to autoload hardware codec */
+			if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) {
+				/* Unsupported format, release CSP */
+				csp->ops.csp_unuse(csp);
+			} else {
+		      __start_CSP:
+				/* Try to start CSP */
+				if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ?
+						       SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT,
+						       (runtime->channels > 1) ?
+						       SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) {
+					/* Failed, release CSP */
+					csp->ops.csp_unuse(csp);
+				} else {
+					/* Success, CSP acquired and running */
+					chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE;
+				}
+			}
+		}
+	}
+}
+
+static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+	if (chip->hardware == SB_HW_16CSP) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+			/* manually loaded codec */
+			if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) &&
+			    ((1 << runtime->format) == csp->acc_format)) {
+				/* Supported runtime PCM format for capture */
+				if (csp->ops.csp_use(csp) == 0) {
+					/* If CSP was successfully acquired */
+					goto __start_CSP;
+				}
+			}
+		} else if (csp->ops.csp_use(csp) == 0) {
+			/* Acquire CSP and try to autoload hardware codec */
+			if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) {
+				/* Unsupported format, release CSP */
+				csp->ops.csp_unuse(csp);
+			} else {
+		      __start_CSP:
+				/* Try to start CSP */
+				if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ?
+						       SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT,
+						       (runtime->channels > 1) ?
+						       SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) {
+					/* Failed, release CSP */
+					csp->ops.csp_unuse(csp);
+				} else {
+					/* Success, CSP acquired and running */
+					chip->open = SNDRV_SB_CSP_MODE_DSP_READ;
+				}
+			}
+		}
+	}
+}
+
+static void snd_sb16_csp_update(sb_t *chip)
+{
+	if (chip->hardware == SB_HW_16CSP) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->qpos_changed) {
+			spin_lock(&chip->reg_lock);
+			csp->ops.csp_qsound_transfer (csp);
+			spin_unlock(&chip->reg_lock);
+		}
+	}
+}
+
+static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+	/* CSP decoders (QSound excluded) support only 16bit transfers */
+	if (chip->hardware == SB_HW_16CSP) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+			/* manually loaded codec */
+			if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) {
+				runtime->hw.formats |= csp->acc_format;
+			}
+		} else {
+			/* autoloaded codecs */
+			runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+					       SNDRV_PCM_FMTBIT_IMA_ADPCM;
+		}
+	}
+}
+
+static void snd_sb16_csp_playback_close(sb_t *chip)
+{
+	if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->ops.csp_stop(csp) == 0) {
+			csp->ops.csp_unuse(csp);
+			chip->open = 0;
+		}
+	}
+}
+
+static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+	/* CSP coders support only 16bit transfers */
+	if (chip->hardware == SB_HW_16CSP) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+			/* manually loaded codec */
+			if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) {
+				runtime->hw.formats |= csp->acc_format;
+			}
+		} else {
+			/* autoloaded codecs */
+			runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+					       SNDRV_PCM_FMTBIT_IMA_ADPCM;
+		}
+	}
+}
+
+static void snd_sb16_csp_capture_close(sb_t *chip)
+{
+	if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) {
+		snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return);
+
+		if (csp->ops.csp_stop(csp) == 0) {
+			csp->ops.csp_unuse(csp);
+			chip->open = 0;
+		}
+	}
+}
+#else
+#define snd_sb16_csp_playback_prepare(chip, runtime)	/*nop*/
+#define snd_sb16_csp_capture_prepare(chip, runtime)	/*nop*/
+#define snd_sb16_csp_update(chip)			/*nop*/
+#define snd_sb16_csp_playback_open(chip, runtime)	/*nop*/
+#define snd_sb16_csp_playback_close(chip)		/*nop*/
+#define snd_sb16_csp_capture_open(chip, runtime)	/*nop*/
+#define snd_sb16_csp_capture_close(chip)      	 	/*nop*/
+#endif
+
+
+static void snd_sb16_setup_rate(sb_t *chip,
+				unsigned short rate,
+				int channel)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16))
+		snd_sb_ack_16bit(chip);
+	else
+		snd_sb_ack_8bit(chip);
+	if (!(chip->mode & SB_RATE_LOCK)) {
+		chip->locked_rate = rate;
+		snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN);
+		snd_sbdsp_command(chip, rate >> 8);
+		snd_sbdsp_command(chip, rate & 0xff);
+		snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
+		snd_sbdsp_command(chip, rate >> 8);
+		snd_sbdsp_command(chip, rate & 0xff);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static int snd_sb16_hw_params(snd_pcm_substream_t * substream,
+			      snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sb16_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned char format;
+	unsigned int size, count, dma;
+
+	snd_sb16_csp_playback_prepare(chip, runtime);
+	if (snd_pcm_format_unsigned(runtime->format) > 0) {
+		format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO;
+	} else {
+		format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO;
+	}
+
+	snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK);
+	size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16;
+	snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	count = snd_pcm_lib_period_bytes(substream);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->mode & SB_MODE_PLAYBACK_16) {
+		count >>= 1;
+		count--;
+		snd_sbdsp_command(chip, SB_DSP4_OUT16_AI);
+		snd_sbdsp_command(chip, format);
+		snd_sbdsp_command(chip, count & 0xff);
+		snd_sbdsp_command(chip, count >> 8);
+		snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+	} else {
+		count--;
+		snd_sbdsp_command(chip, SB_DSP4_OUT8_AI);
+		snd_sbdsp_command(chip, format);
+		snd_sbdsp_command(chip, count & 0xff);
+		snd_sbdsp_command(chip, count >> 8);
+		snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream,
+				     int cmd)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		chip->mode |= SB_RATE_LOCK_PLAYBACK;
+		snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF);
+		/* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */
+		if (chip->mode & SB_RATE_LOCK_CAPTURE)
+			snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+		chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
+		break;
+	default:
+		result = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned char format;
+	unsigned int size, count, dma;
+
+	snd_sb16_csp_capture_prepare(chip, runtime);
+	if (snd_pcm_format_unsigned(runtime->format) > 0) {
+		format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO;
+	} else {
+		format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO;
+	}
+	snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE);
+	size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16;
+	snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+
+	count = snd_pcm_lib_period_bytes(substream);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->mode & SB_MODE_CAPTURE_16) {
+		count >>= 1;
+		count--;
+		snd_sbdsp_command(chip, SB_DSP4_IN16_AI);
+		snd_sbdsp_command(chip, format);
+		snd_sbdsp_command(chip, count & 0xff);
+		snd_sbdsp_command(chip, count >> 8);
+		snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+	} else {
+		count--;
+		snd_sbdsp_command(chip, SB_DSP4_IN8_AI);
+		snd_sbdsp_command(chip, format);
+		snd_sbdsp_command(chip, count & 0xff);
+		snd_sbdsp_command(chip, count >> 8);
+		snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream,
+				    int cmd)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		chip->mode |= SB_RATE_LOCK_CAPTURE;
+		snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF);
+		/* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */
+		if (chip->mode & SB_RATE_LOCK_PLAYBACK)
+			snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+		chip->mode &= ~SB_RATE_LOCK_CAPTURE;
+		break;
+	default:
+		result = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	sb_t *chip = snd_magic_cast(sb_t, dev_id, return);
+	unsigned char status;
+	int ok;
+
+	spin_lock(&chip->mixer_lock);
+	status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+	spin_unlock(&chip->mixer_lock);
+	if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi)
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+	if (status & SB_IRQTYPE_8BIT) {
+		ok = 0;
+		if (chip->mode & SB_MODE_PLAYBACK_8) {
+			snd_pcm_period_elapsed(chip->playback_substream);
+			snd_sb16_csp_update(chip);
+			ok++;
+		}
+		if (chip->mode & SB_MODE_CAPTURE_8) {
+			snd_pcm_period_elapsed(chip->capture_substream);
+			ok++;
+		}
+		spin_lock(&chip->reg_lock);
+		if (!ok)
+			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+		snd_sb_ack_8bit(chip);
+		spin_unlock(&chip->reg_lock);
+	}
+	if (status & SB_IRQTYPE_16BIT) {
+		ok = 0;
+		if (chip->mode & SB_MODE_PLAYBACK_16) {
+			snd_pcm_period_elapsed(chip->playback_substream);
+			snd_sb16_csp_update(chip);
+			ok++;
+		}
+		if (chip->mode & SB_MODE_CAPTURE_16) {
+			snd_pcm_period_elapsed(chip->capture_substream);
+			ok++;
+		}
+		spin_lock(&chip->reg_lock);
+		if (!ok)
+			snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+		snd_sb_ack_16bit(chip);
+		spin_unlock(&chip->reg_lock);
+	}
+}
+
+/*
+
+ */
+
+static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int dma;
+	size_t ptr;
+
+	dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16;
+	ptr = chip->p_dma_size - snd_dma_residue(dma);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int dma;
+	size_t ptr;
+
+	dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16;
+	ptr = chip->c_dma_size - snd_dma_residue(dma);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_sb16_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		0,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	rate_min:		4000,
+	rate_max:		44100,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sb16_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		0,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	rate_min:		4000,
+	rate_max:		44100,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *  open/close
+ */
+
+int snd_sb16_playback_open(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (chip->mode & SB_MODE_PLAYBACK) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		return -EAGAIN;
+	}
+	runtime->hw = snd_sb16_playback;
+
+	/* skip if 16 bit DMA was reserved for capture */
+	if (chip->force_mode16 & SB_MODE_CAPTURE_16)
+		goto __skip_16bit;
+
+	if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) {
+		chip->mode |= SB_MODE_PLAYBACK_16;
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+		/* Vibra16X hack */
+		if (chip->dma16 <= 3) {
+			runtime->hw.buffer_bytes_max =
+			runtime->hw.period_bytes_max = 64 * 1024;
+		} else {
+			snd_sb16_csp_playback_open(chip, runtime);
+		}
+		goto __open_ok;
+	}
+
+      __skip_16bit:
+	if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) {
+		chip->mode |= SB_MODE_PLAYBACK_8;
+		/* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */
+		if (chip->dma16 < 0) {
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+			chip->mode |= SB_MODE_PLAYBACK_16;
+		} else {
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8;
+		}
+		runtime->hw.buffer_bytes_max =
+		runtime->hw.period_bytes_max = 64 * 1024;
+		goto __open_ok;
+	}
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return -EAGAIN;
+
+      __open_ok:
+	if (chip->hardware == SB_HW_ALS100)
+		runtime->hw.rate_max = 48000;
+	if (chip->mode & SB_RATE_LOCK)
+		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+	chip->playback_substream = substream;
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return 0;
+}
+
+int snd_sb16_playback_close(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+
+	snd_sb16_csp_playback_close(chip);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	chip->playback_substream = NULL;
+	chip->mode &= ~SB_MODE_PLAYBACK;
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return 0;
+}
+
+int snd_sb16_capture_open(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (chip->mode & SB_MODE_CAPTURE) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		return -EAGAIN;
+	}
+	runtime->hw = snd_sb16_capture;
+
+	/* skip if 16 bit DMA was reserved for playback */
+	if (chip->force_mode16 & SB_MODE_PLAYBACK_16)
+		goto __skip_16bit;
+
+	if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) {
+		chip->mode |= SB_MODE_CAPTURE_16;
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+		/* Vibra16X hack */
+		if (chip->dma16 <= 3) {
+			runtime->hw.buffer_bytes_max =
+			runtime->hw.period_bytes_max = 64 * 1024;
+		} else {
+			snd_sb16_csp_capture_open(chip, runtime);
+		}
+		goto __open_ok;
+	}
+
+      __skip_16bit:
+	if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) {
+		chip->mode |= SB_MODE_CAPTURE_8;
+		/* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */
+		if (chip->dma16 < 0) {
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+			chip->mode |= SB_MODE_CAPTURE_16;
+		} else {
+			runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8;
+		}
+		runtime->hw.buffer_bytes_max =
+		runtime->hw.period_bytes_max = 64 * 1024;
+		goto __open_ok;
+	}
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return -EAGAIN;
+
+      __open_ok:
+	if (chip->hardware == SB_HW_ALS100)
+		runtime->hw.rate_max = 48000;
+	if (chip->mode & SB_RATE_LOCK)
+		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+	chip->capture_substream = substream;
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return 0;
+}
+
+int snd_sb16_capture_close(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+
+	snd_sb16_csp_capture_close(chip);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	chip->capture_substream = NULL;
+	chip->mode &= ~SB_MODE_CAPTURE;
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return 0;
+}
+
+/*
+ *  DMA control interface
+ */
+
+static int snd_sb16_set_dma_mode(sb_t *chip, int what)
+{
+	if (chip->dma8 < 0 || chip->dma16 < 0) {
+		snd_assert(what == 0, return -EINVAL);
+		return 0;
+	}
+	if (what == 0) {
+		chip->force_mode16 = 0;
+	} else if (what == 1) {
+		chip->force_mode16 = SB_MODE_PLAYBACK_16;
+	} else if (what == 2) {
+		chip->force_mode16 = SB_MODE_CAPTURE_16;
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_sb16_get_dma_mode(sb_t *chip)
+{
+	if (chip->dma8 < 0 || chip->dma16 < 0)
+		return 0;
+	switch (chip->force_mode16) {
+	case SB_MODE_PLAYBACK_16:
+		return 1;
+	case SB_MODE_CAPTURE_16:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = {
+		"Auto", "Playback", "Capture"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char nval, oval;
+	int change;
+	
+	if ((nval = ucontrol->value.enumerated.item[0]) > 2)
+		return -EINVAL;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_sb16_get_dma_mode(chip);
+	change = nval != oval;
+	snd_sb16_set_dma_mode(chip, nval);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+snd_kcontrol_new_t snd_sb16_dma_control = {
+	iface: SNDRV_CTL_ELEM_IFACE_PCM,
+	name: "16-bit DMA Allocation",
+	info: snd_sb16_dma_control_info,
+	get: snd_sb16_dma_control_get,
+	put: snd_sb16_dma_control_put
+};
+
+/*
+ *  Initialization part
+ */
+ 
+int snd_sb16dsp_configure(sb_t * chip)
+{
+	unsigned long flags;
+	unsigned char irqreg = 0, dmareg = 0, mpureg;
+	unsigned char realirq, realdma, realmpureg;
+	/* note: mpu register should be present only on SB16 Vibra soundcards */
+
+	// printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16);
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	switch (chip->irq) {
+	case 2:
+	case 9:
+		irqreg |= SB_IRQSETUP_IRQ9;
+		break;
+	case 5:
+		irqreg |= SB_IRQSETUP_IRQ5;
+		break;
+	case 7:
+		irqreg |= SB_IRQSETUP_IRQ7;
+		break;
+	case 10:
+		irqreg |= SB_IRQSETUP_IRQ10;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (chip->dma8 >= 0) {
+		switch (chip->dma8) {
+		case 0:
+			dmareg |= SB_DMASETUP_DMA0;
+			break;
+		case 1:
+			dmareg |= SB_DMASETUP_DMA1;
+			break;
+		case 3:
+			dmareg |= SB_DMASETUP_DMA3;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	if (chip->dma16 >= 0) {
+		switch (chip->dma16) {
+		case 5:
+			dmareg |= SB_DMASETUP_DMA5;
+			break;
+		case 6:
+			dmareg |= SB_DMASETUP_DMA6;
+			break;
+		case 7:
+			dmareg |= SB_DMASETUP_DMA7;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	switch (chip->mpu_port) {
+	case 0x300:
+		mpureg |= 0x04;
+		break;
+	case 0x330:
+		mpureg |= 0x00;
+		break;
+	default:
+		mpureg |= 0x02;	/* disable MPU */
+	}
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+
+	snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
+	realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
+
+	snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
+	realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
+
+	snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
+	realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	if ((~realirq) & irqreg || (~realdma) & dmareg) {
+		snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port);
+		snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg);
+		snd_printk("SB16 [0x%lx]:    got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static snd_pcm_ops_t snd_sb16_playback_ops = {
+	open:			snd_sb16_playback_open,
+	close:			snd_sb16_playback_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_sb16_hw_params,
+	hw_free:		snd_sb16_hw_free,
+	prepare:		snd_sb16_playback_prepare,
+	trigger:		snd_sb16_playback_trigger,
+	pointer:		snd_sb16_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sb16_capture_ops = {
+	open:			snd_sb16_capture_open,
+	close:			snd_sb16_capture_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_sb16_hw_params,
+	hw_free:		snd_sb16_hw_free,
+	prepare:		snd_sb16_capture_prepare,
+	trigger:		snd_sb16_capture_trigger,
+	pointer:		snd_sb16_capture_pointer,
+};
+
+static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_card_t *card = chip->card;
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0)
+		return err;
+	sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
+	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+	pcm->private_data = chip;
+	pcm->private_free = snd_sb16dsp_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops);
+
+	snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip));
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_sb16dsp_pcm);
+EXPORT_SYMBOL(snd_sb16dsp_configure);
+EXPORT_SYMBOL(snd_sb16dsp_interrupt);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_sb16_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_sb16_exit(void)
+{
+}
+
+module_init(alsa_sb16_init)
+module_exit(alsa_sb16_exit)
diff -Nru linux/sound/isa/sb/sb8.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb8.c
--- linux/sound/isa/sb/sb8.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb8.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,256 @@
+/*
+ *  Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t sb_t
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Sound Blaster soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Sound Blaster soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Sound Blaster soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for SB8 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for SB8 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB8 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+struct snd_sb8 {
+	struct resource *fm_res;	/* used to block FM i/o region for legacy cards */
+};
+
+static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static void snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	sb_t *chip = snd_magic_cast(sb_t, dev_id, return);
+
+	if (chip->open & SB_OPEN_PCM) {
+		snd_sb8dsp_interrupt(chip);
+	} else {
+		snd_sb8dsp_midi_interrupt(chip);
+	}
+}
+
+static void snd_sb8_free(snd_card_t *card)
+{
+	struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data;
+
+	if (acard == NULL)
+		return;
+	if (acard->fm_res) {
+		release_resource(acard->fm_res);
+		kfree_nocheck(acard->fm_res);
+	}
+}
+
+static int __init snd_sb8_probe(int dev)
+{
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_sb8 *acard;
+	opl3_t *opl3;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_sb8));
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_sb8 *)card->private_data;
+	card->private_free = snd_sb8_free;
+
+	/* block the 0x388 port to avoid PnP conflicts */
+	acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+
+	if ((err = snd_sbdsp_create(card, snd_port[dev], snd_irq[dev],
+				    snd_sb8_interrupt,
+				    snd_dma8[dev],
+				    -1,
+				    SB_HW_AUTO,
+				    &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (chip->hardware >= SB_HW_16) {
+		snd_card_free(card);
+		if (chip->hardware == SB_HW_ALS100)
+			snd_printdd("ALS100 chip detected at 0x%lx, try snd-card-als100 module\n",
+				    snd_port[dev]);
+		else
+			snd_printdd("SB 16 chip detected at 0x%lx, try snd-card-sb16 module\n",
+				    snd_port[dev]);
+		return -ENODEV;
+	}
+
+	if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
+		if ((err = snd_opl3_create(card, chip->port + 8, 0,
+					   OPL3_HW_AUTO, 1,
+					   &opl3)) < 0) {
+			printk(KERN_ERR "sb8: no OPL device at 0x%lx\n", chip->port + 8);
+		}
+	} else {
+		if ((err = snd_opl3_create(card, chip->port, chip->port + 2,
+					   OPL3_HW_AUTO, 1,
+					   &opl3)) < 0) {
+			printk(KERN_ERR "sb8: no OPL device at 0x%lx-0x%lx\n",
+				   chip->port, chip->port + 2);
+		}
+	}
+	if (err >= 0) {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
+	strcpy(card->shortname, chip->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		chip->name,
+		chip->port,
+		snd_irq[dev], snd_dma8[dev]);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_sb8_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_card_sb8_legacy_auto_probe(unsigned long port)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+                        continue;
+                snd_port[dev] = port;
+                res = snd_sb8_probe(dev);
+                if (res < 0)
+                        snd_port[dev] = SNDRV_AUTO_PORT;
+                return res;
+        }
+        return -ENODEV;
+}
+
+static int __init alsa_card_sb8_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_sb8_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "Sound Blaster soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_sb8_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_sb8_cards[idx]);
+}
+
+module_init(alsa_card_sb8_init)
+module_exit(alsa_card_sb8_exit)
+
+#ifndef MODULE
+
+/* format is: snd-sb8=snd_enable,snd_index,snd_id,
+		      snd_port,snd_irq,snd_dma8 */
+
+static int __init alsa_card_sb8_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-sb8=", alsa_card_sb8_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/sb/sb8_main.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_main.c
--- linux/sound/isa/sb/sb8_main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_main.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,564 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Routines for control of 8-bit SoundBlaster cards and clones
+ *  Please note: I don't have access to old SB8 soundcards.
+ *
+ *
+ *   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
+ *
+ * --
+ *
+ * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
+ *   DSP can't respond to commands whilst in "high speed" mode. Caused 
+ *   glitching during playback. Fixed.
+ *
+ * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si>
+ *   Cleaned up and rewrote lowlevel routines.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones");
+MODULE_LICENSE("GPL");
+
+#define chip_t sb_t
+
+#define SB8_CLOCK	1000000
+#define SB8_DEN(v)	((SB8_CLOCK + (v) / 2) / (v))
+#define SB8_RATE(v)	(SB8_CLOCK / SB8_DEN(v))
+
+static ratnum_t clock = {
+	num: SB8_CLOCK,
+	den_min: 1,
+	den_max: 256,
+	den_step: 1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = {
+	nrats: 1,
+	rats: &clock,
+};
+
+static ratnum_t stereo_clocks[] = {
+	{
+		num: SB8_CLOCK,
+		den_min: SB8_DEN(22050),
+		den_max: SB8_DEN(22050),
+		den_step: 1,
+	},
+	{
+		num: SB8_CLOCK,
+		den_min: SB8_DEN(11025),
+		den_max: SB8_DEN(11025),
+		den_step: 1,
+	}
+};
+
+static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params,
+					       snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	if (c->min > 1) {
+	  	unsigned int num = 0, den = 0;
+		int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE),
+					  2, stereo_clocks, &num, &den);
+		if (err >= 0 && den) {
+			params->rate_num = num;
+			params->rate_den = den;
+		}
+		return err;
+	}
+	return 0;
+}
+
+static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params,
+					       snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) {
+		snd_interval_t t = { min: 1, max: 1 };
+		return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t);
+	}
+	return 0;
+}
+
+static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int mixreg, rate, size, count;
+
+	rate = runtime->rate;
+	switch (chip->hardware) {
+	case SB_HW_PRO:
+		if (runtime->channels > 1) {
+			snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
+			break;
+		}
+		/* fallthru */
+	case SB_HW_201:
+		if (rate > 23000) {
+			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
+			break;
+		}
+		/* fallthru */
+	case SB_HW_20:
+		chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
+		break;
+	case SB_HW_10:
+		chip->playback_format = SB_DSP_OUTPUT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
+	if (runtime->channels > 1) {
+		/* set playback stereo mode */
+		spin_lock(&chip->mixer_lock);
+		mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
+		snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
+		spin_unlock(&chip->mixer_lock);
+
+		/* Soundblaster hardware programming reference guide, 3-23 */
+		snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
+		runtime->dma_area[0] = 0x80;
+		snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE);
+		/* force interrupt */
+		chip->mode = SB_MODE_HALT;
+		snd_sbdsp_command(chip, SB_DSP_OUTPUT);
+		snd_sbdsp_command(chip, 0);
+		snd_sbdsp_command(chip, 0);
+	}
+	snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+	if (runtime->channels > 1) {
+		snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+		spin_lock(&chip->mixer_lock);
+		/* save output filter status and turn it off */
+		mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
+		snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
+		spin_unlock(&chip->mixer_lock);
+		/* just use force_mode16 for temporary storate... */
+		chip->force_mode16 = mixreg;
+	} else {
+		snd_sbdsp_command(chip, 256 - runtime->rate_den);
+	}
+	if (chip->playback_format != SB_DSP_OUTPUT) {
+		count--;
+		snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+		snd_sbdsp_command(chip, count & 0xff);
+		snd_sbdsp_command(chip, count >> 8);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_dma_program(chip->dma8, runtime->dma_addr,
+			size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	return 0;
+}
+
+static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream,
+				    int cmd)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int count;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_sbdsp_command(chip, chip->playback_format);
+		if (chip->playback_format == SB_DSP_OUTPUT) {
+			count = chip->p_period_size - 1;
+			snd_sbdsp_command(chip, count & 0xff);
+			snd_sbdsp_command(chip, count >> 8);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) {
+			snd_pcm_runtime_t *runtime = substream->runtime;
+			snd_sbdsp_reset(chip);
+			if (runtime->channels > 1) {
+				spin_lock(&chip->mixer_lock);
+				/* restore output filter and set hardware to mono mode */ 
+				snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
+				spin_unlock(&chip->mixer_lock);
+			}
+		} else {
+			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+		}
+		snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT;
+	return 0;
+}
+
+static int snd_sb8_hw_params(snd_pcm_substream_t * substream,
+			     snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sb8_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int mixreg, rate, size, count;
+
+	rate = runtime->rate;
+	switch (chip->hardware) {
+	case SB_HW_PRO:
+		if (runtime->channels > 1) {
+			snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
+			break;
+		}
+		chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO;
+		break;
+	case SB_HW_201:
+		if (rate > 13000) {
+			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
+			break;
+		}
+		/* fallthru */
+	case SB_HW_20:
+		chip->capture_format = SB_DSP_LO_INPUT_AUTO;
+		break;
+	case SB_HW_10:
+		chip->capture_format = SB_DSP_INPUT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
+	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);
+	snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+	if (runtime->channels > 1) {
+		snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+		spin_lock(&chip->mixer_lock);
+		/* save input filter status and turn it off */
+		mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
+		snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
+		spin_unlock(&chip->mixer_lock);
+		/* just use force_mode16 for temporary storate... */
+		chip->force_mode16 = mixreg;
+	} else {
+		snd_sbdsp_command(chip, 256 - runtime->rate_den);
+	}
+	if (chip->capture_format != SB_DSP_OUTPUT) {
+		count--;
+		snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+		snd_sbdsp_command(chip, count & 0xff);
+		snd_sbdsp_command(chip, count >> 8);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_dma_program(chip->dma8, runtime->dma_addr,
+			size, DMA_MODE_READ | DMA_AUTOINIT);
+	return 0;
+}
+
+static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream,
+				   int cmd)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int count;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_sbdsp_command(chip, chip->capture_format);
+		if (chip->capture_format == SB_DSP_INPUT) {
+			count = chip->c_period_size - 1;
+			snd_sbdsp_command(chip, count & 0xff);
+			snd_sbdsp_command(chip, count >> 8);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) {
+			snd_pcm_runtime_t *runtime = substream->runtime;
+			snd_sbdsp_reset(chip);
+			if (runtime->channels > 1) {
+				/* restore input filter status */
+				spin_lock(&chip->mixer_lock);
+				snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
+				spin_unlock(&chip->mixer_lock);
+				/* set hardware to mono mode */
+				snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
+			}
+		} else {
+			snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+		}
+		snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT;
+	return 0;
+}
+
+void snd_sb8dsp_interrupt(sb_t *chip)
+{
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+
+#if 0
+	snd_printk("sb8: interrupt\n");
+#endif
+	snd_sb_ack_8bit(chip);
+	switch (chip->mode) {
+	case SB_MODE_PLAYBACK_8:	/* ok.. playback is active */
+		substream = chip->playback_substream;
+		runtime = substream->runtime;
+		if (chip->playback_format == SB_DSP_OUTPUT)
+		    	snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
+		snd_pcm_period_elapsed(substream);
+		break;
+	case SB_MODE_CAPTURE_8:
+		substream = chip->capture_substream;
+		runtime = substream->runtime;
+		if (chip->capture_format == SB_DSP_INPUT)
+		    	snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
+		snd_pcm_period_elapsed(substream);
+		break;
+	}
+}
+
+static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (chip->mode != SB_MODE_PLAYBACK_8)
+		return 0;
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma8);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (chip->mode != SB_MODE_CAPTURE_8)
+		return 0;
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma8);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_sb8_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8,
+	rates:			(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
+	rate_min:		4000,
+	rate_max:		23000,
+	channels_min:		1,
+	channels_max:		1,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sb8_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8,
+	rates:			(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+				 SNDRV_PCM_RATE_11025),
+	rate_min:		4000,
+	rate_max:		13000,
+	channels_min:		1,
+	channels_max:		1,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *
+ */
+ 
+int snd_sb8_open(snd_pcm_substream_t *substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (chip->open) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		return -EAGAIN;
+	}
+	chip->open |= SB_OPEN_PCM;
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		chip->playback_substream = substream;
+		runtime->hw = snd_sb8_playback;
+	} else {
+		chip->capture_substream = substream;
+		runtime->hw = snd_sb8_capture;
+	}
+	switch (chip->hardware) {
+	case SB_HW_PRO:
+		runtime->hw.rate_max = 44100;
+		runtime->hw.channels_max = 2;
+		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				    snd_sb8_hw_constraint_rate_channels, 0,
+				    SNDRV_PCM_HW_PARAM_CHANNELS,
+				    SNDRV_PCM_HW_PARAM_RATE, -1);
+		snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				     snd_sb8_hw_constraint_channels_rate, 0,
+				     SNDRV_PCM_HW_PARAM_RATE, -1);
+		break;
+	case SB_HW_201:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			runtime->hw.rate_max = 44100;
+		} else {
+			runtime->hw.rate_max = 15000;
+		}
+	default:
+		break;
+	}
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clock);
+	return 0;	
+}
+
+int snd_sb8_close(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	chip->capture_substream = NULL;
+	spin_lock_irqsave(&chip->open_lock, flags);
+	chip->open &= ~SB_OPEN_PCM;
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+	return 0;
+}
+
+/*
+ *  Initialization part
+ */
+ 
+static snd_pcm_ops_t snd_sb8_playback_ops = {
+	open:			snd_sb8_open,
+	close:			snd_sb8_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_sb8_hw_params,
+	hw_free:		snd_sb8_hw_free,
+	prepare:		snd_sb8_playback_prepare,
+	trigger:		snd_sb8_playback_trigger,
+	pointer:		snd_sb8_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sb8_capture_ops = {
+	open:			snd_sb8_open,
+	close:			snd_sb8_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_sb8_hw_params,
+	hw_free:		snd_sb8_hw_free,
+	prepare:		snd_sb8_capture_prepare,
+	trigger:		snd_sb8_capture_trigger,
+	pointer:		snd_sb8_capture_pointer,
+};
+
+static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_card_t *card = chip->card;
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0)
+		return err;
+	sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
+	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	pcm->private_data = chip;
+	pcm->private_free = snd_sb8dsp_pcm_free;
+
+	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);
+
+	snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_sb8dsp_pcm);
+EXPORT_SYMBOL(snd_sb8dsp_interrupt);
+  /* sb8_midi.c */
+EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
+EXPORT_SYMBOL(snd_sb8dsp_midi);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_sb8_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_sb8_exit(void)
+{
+}
+
+module_init(alsa_sb8_init)
+module_exit(alsa_sb8_exit)
diff -Nru linux/sound/isa/sb/sb8_midi.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_midi.c
--- linux/sound/isa/sb/sb8_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,262 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of SoundBlaster cards - MIDI interface
+ *
+ *   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
+ *
+ * --
+ *
+ * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
+ *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 
+ *   working.
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+
+/*
+
+ */
+
+void snd_sb8dsp_midi_interrupt(sb_t * chip)
+{
+	snd_rawmidi_t *rmidi;
+	int max = 64;
+	char byte;
+
+	if (chip == NULL || (rmidi = chip->rmidi) == NULL) {
+		inb(SBP(chip, READ));	/* ack interrupt */
+		return;
+	}
+	while (max-- > 0) {
+		spin_lock(&chip->midi_input_lock);
+		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+			byte = inb(SBP(chip, READ));
+			spin_unlock(&chip->midi_input_lock);
+			snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
+		} else {
+			spin_unlock(&chip->midi_input_lock);
+		}
+	}
+}
+
+/*
+
+ */
+
+static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip;
+
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (chip->open) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		return -EAGAIN;
+	}
+	chip->open |= SB_OPEN_MIDI_INPUT;
+	chip->midi_substream_input = substream;
+	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		snd_sbdsp_reset(chip);		/* reset DSP */
+	} else {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+	}
+	return 0;
+}
+
+static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip;
+
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (chip->open) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		return -EAGAIN;
+	}
+	chip->open |= SB_OPEN_MIDI_OUTPUT;
+	chip->midi_substream_output = substream;
+	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		snd_sbdsp_reset(chip);		/* reset DSP */
+	} else {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+	}
+	return 0;
+}
+
+static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip;
+
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_TRIGGER);
+	chip->midi_substream_input = NULL;
+	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		snd_sbdsp_reset(chip);		/* reset DSP */
+	} else {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+	}
+	return 0;
+}
+
+static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip;
+
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	chip->open &= ~SB_OPEN_MIDI_OUTPUT;
+	chip->midi_substream_output = NULL;
+	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+		snd_sbdsp_reset(chip);		/* reset DSP */
+	} else {
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+	}
+	return 0;
+}
+
+static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	sb_t *chip;
+
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (up) {
+		if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) {
+			snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+			chip->open |= SB_OPEN_MIDI_TRIGGER;
+		}
+	} else {
+		if (chip->open & SB_OPEN_MIDI_TRIGGER) {
+			snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+			chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+		}
+	}
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+}
+
+static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip;
+	char byte;
+	int max = 32;
+
+	/* how big is Tx FIFO? */
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return);
+	while (max-- > 0) {
+		spin_lock_irqsave(&chip->open_lock, flags);
+		if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+			chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+			del_timer(&chip->midi_timer);
+			spin_unlock_irqrestore(&chip->open_lock, flags);
+			return;
+		}
+		snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
+		snd_sbdsp_command(chip, byte);
+		spin_unlock_irqrestore(&chip->open_lock, flags);
+	}
+}
+
+static void snd_sb8dsp_midi_output_timer(unsigned long data)
+{
+	snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data;
+	sb_t * chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->open_lock, flags);
+	chip->midi_timer.expires = 1 + jiffies;
+	add_timer(&chip->midi_timer);
+	spin_unlock_irqrestore(&chip->open_lock, flags);	
+	snd_sb8dsp_midi_output_write(substream);
+}
+
+static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	sb_t *chip;
+
+	chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return);
+	spin_lock_irqsave(&chip->open_lock, flags);
+	if (up) {
+		if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) {
+			chip->midi_timer.function = snd_sb8dsp_midi_output_timer;
+			chip->midi_timer.data = (unsigned long) substream;
+			chip->midi_timer.expires = 1 + jiffies;
+			add_timer(&chip->midi_timer);
+			chip->open |= SB_OPEN_MIDI_TRIGGER;
+		}
+	} else {
+		if (chip->open & SB_OPEN_MIDI_TRIGGER) {
+			chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+		}
+	}
+	spin_unlock_irqrestore(&chip->open_lock, flags);
+
+	if (up)
+		snd_sb8dsp_midi_output_write(substream);
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_sb8dsp_midi_output =
+{
+	open:           snd_sb8dsp_midi_output_open,
+	close:          snd_sb8dsp_midi_output_close,
+	trigger:	snd_sb8dsp_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_sb8dsp_midi_input =
+{
+	open:           snd_sb8dsp_midi_input_open,
+	close:          snd_sb8dsp_midi_input_close,
+	trigger:        snd_sb8dsp_midi_input_trigger,
+};
+
+int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, "SB8 MIDI");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = chip;
+	chip->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return 0;
+}
diff -Nru linux/sound/isa/sb/sb_common.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb_common.c
--- linux/sound/isa/sb/sb_common.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb_common.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,305 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Lowlevel routines for control of Sound Blaster cards
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/initval.h>
+
+#define chip_t sb_t
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+#define BUSY_LOOPS 100000
+
+#undef IO_DEBUG
+
+int snd_sbdsp_command(sb_t *chip, unsigned char val)
+{
+	int i;
+#ifdef IO_DEBUG
+	snd_printk("command 0x%x\n", val);
+#endif
+	for (i = BUSY_LOOPS; i; i--)
+		if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
+			outb(val, SBP(chip, COMMAND));
+			return 1;
+		}
+	snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val);
+	return 0;
+}
+
+int snd_sbdsp_get_byte(sb_t *chip)
+{
+	int val;
+	int i;
+	for (i = BUSY_LOOPS; i; i--) {
+		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+			val = inb(SBP(chip, READ));
+#ifdef IO_DEBUG
+			snd_printk("get_byte 0x%x\n", val);
+#endif
+			return val;
+		}
+	}
+	snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port);
+	return -ENODEV;
+}
+
+int snd_sbdsp_reset(sb_t *chip)
+{
+	int i;
+
+	outb(1, SBP(chip, RESET));
+	udelay(10);
+	outb(0, SBP(chip, RESET));
+	udelay(30);
+	for (i = BUSY_LOOPS; i; i--)
+		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+			if (inb(SBP(chip, READ)) == 0xaa)
+				return 0;
+			else
+				break;
+		}
+	snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port);
+	return -ENODEV;
+}
+
+int snd_sbdsp_version(sb_t * chip)
+{
+	unsigned int result = -ENODEV;
+
+	snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
+	result = (short) snd_sbdsp_get_byte(chip) << 8;
+	result |= (short) snd_sbdsp_get_byte(chip);
+	return result;
+}
+
+static int snd_sbdsp_probe(sb_t * chip)
+{
+	int version;
+	int major, minor;
+	char *str;
+	unsigned long flags;
+
+	/*
+	 *  initialization sequence
+	 */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (snd_sbdsp_reset(chip) < 0) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return -ENODEV;
+	}
+	version = snd_sbdsp_version(chip);
+	if (version < 0) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return -ENODEV;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	major = version >> 8;
+	minor = version & 0xff;
+	snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
+		    chip->port, major, minor);
+	
+	switch (chip->hardware) {
+	case SB_HW_AUTO:
+		switch (major) {
+		case 1:
+			chip->hardware = SB_HW_10;
+			str = "1.0";
+			break;
+		case 2:
+			if (minor) {
+				chip->hardware = SB_HW_201;
+				str = "2.01+";
+			} else {
+				chip->hardware = SB_HW_20;
+				str = "2.0";
+			}
+			break;
+		case 3:
+			chip->hardware = SB_HW_PRO;
+			str = "Pro";
+			break;
+		case 4:
+			chip->hardware = SB_HW_16;
+			str = "16";
+			break;
+		default:
+			snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n",
+				   chip->port, major, minor);
+			return -ENODEV;
+		}
+		break;
+	case SB_HW_ALS100:
+		str = "16 (ALS-100)";
+		break;
+	case SB_HW_ALS4000:
+		str = "16 (ALS-4000)";
+		break;
+	default:
+		return -ENODEV;
+	}
+	sprintf(chip->name, "Sound Blaster %s", str);
+	chip->version = (major << 8) | minor;
+	return 0;
+}
+
+static int snd_sbdsp_free(sb_t *chip)
+{
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->res_alt_port) {
+		release_resource(chip->res_alt_port);
+		kfree_nocheck(chip->res_alt_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+#ifdef CONFIG_ISA
+	if (chip->dma8 >= 0) {
+		disable_dma(chip->dma8);
+		free_dma(chip->dma8);
+	}
+	if (chip->dma16 >= 0) {
+		disable_dma(chip->dma16);
+		free_dma(chip->dma16);
+	}
+#endif
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_sbdsp_dev_free(snd_device_t *device)
+{
+	sb_t *chip = snd_magic_cast(sb_t, device->device_data, return -ENXIO);
+	return snd_sbdsp_free(chip);
+}
+
+int snd_sbdsp_create(snd_card_t *card,
+		     unsigned long port,
+		     int irq,
+		     void (*irq_handler)(int, void *, struct pt_regs *),
+		     int dma8,
+		     int dma16,
+		     unsigned short hardware,
+		     sb_t **r_chip)
+{
+	sb_t *chip;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:       snd_sbdsp_dev_free,
+	};
+
+	snd_assert(r_chip != NULL, return -EINVAL);
+	*r_chip = NULL;
+	chip = snd_magic_kcalloc(sb_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->open_lock);
+	spin_lock_init(&chip->midi_input_lock);
+	spin_lock_init(&chip->mixer_lock);
+	chip->irq = -1;
+	chip->dma8 = -1;
+	chip->dma16 = -1;
+	chip->port = port;
+	
+	if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ?
+			SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT,
+			"SoundBlaster", (void *) chip)) {
+		snd_sbdsp_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+
+	if (hardware == SB_HW_ALS4000)
+		goto __skip_allocation;
+	
+	if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
+		snd_sbdsp_free(chip);
+		return -EBUSY;
+	}
+
+#ifdef CONFIG_ISA
+	if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
+		snd_sbdsp_free(chip);
+		return -EBUSY;
+	}
+	chip->dma8 = dma8;
+	if (dma16 >= 0 && request_dma(dma16, "SoundBlaster - 16bit")) {
+		snd_sbdsp_free(chip);
+		return -EBUSY;
+	}
+	chip->dma16 = dma16;
+#endif
+
+      __skip_allocation:
+	chip->card = card;
+	chip->hardware = hardware;
+	if ((err = snd_sbdsp_probe(chip)) < 0) {
+		snd_sbdsp_free(chip);
+		return err;
+	}
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_sbdsp_free(chip);
+		return err;
+	}
+	*r_chip = chip;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_sbdsp_command);
+EXPORT_SYMBOL(snd_sbdsp_get_byte);
+EXPORT_SYMBOL(snd_sbdsp_reset);
+EXPORT_SYMBOL(snd_sbdsp_create);
+/* sb_mixer.c */
+EXPORT_SYMBOL(snd_sbmixer_write);
+EXPORT_SYMBOL(snd_sbmixer_read);
+EXPORT_SYMBOL(snd_sbmixer_new);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_sb_common_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_sb_common_exit(void)
+{
+}
+
+module_init(alsa_sb_common_init)
+module_exit(alsa_sb_common_exit)
diff -Nru linux/sound/isa/sb/sb_mixer.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb_mixer.c
--- linux/sound/isa/sb/sb_mixer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb_mixer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,497 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for Sound Blaster mixer control
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/control.h>
+
+#define chip_t sb_t
+
+#undef IO_DEBUG
+
+void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data)
+{
+	outb(reg, SBP(chip, MIXER_ADDR));
+	udelay(10);
+	outb(data, SBP(chip, MIXER_DATA));
+	udelay(10);
+#ifdef IO_DEBUG
+	snd_printk("mixer_write 0x%x 0x%x\n", reg, data);
+#endif
+}
+
+unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg)
+{
+	unsigned char result;
+
+	outb(reg, SBP(chip, MIXER_ADDR));
+	udelay(10);
+	result = inb(SBP(chip, MIXER_DATA));
+	udelay(10);
+#ifdef IO_DEBUG
+	snd_printk("mixer_read 0x%x 0x%x\n", reg, result);
+#endif
+	return result;
+}
+
+/*
+ * Single channel mixer element
+ */
+
+#define SB_SINGLE(xname, reg, shift, mask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
+  name: xname, \
+  info: snd_sbmixer_info_single, \
+  get: snd_sbmixer_get_single, put: snd_sbmixer_put_single, \
+  private_value: reg | (shift << 16) | (mask << 24) }
+
+static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sbmixer_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0xff;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}
+
+static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned char val, oval;
+
+	val = (ucontrol->value.integer.value[0] & mask) << shift;
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, reg);
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	if (change)
+		snd_sbmixer_write(sb, reg, val);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * Double channel mixer element
+ */
+
+#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
+  name: xname, \
+  info: snd_sbmixer_info_double, \
+  get: snd_sbmixer_get_double, put: snd_sbmixer_put_double, \
+  private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) }
+
+static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sbmixer_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char left, right;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
+	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = left;
+	ucontrol->value.integer.value[1] = right;
+	return 0;
+}
+
+static int snd_sbmixer_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned char left, right, oleft, oright;
+
+	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
+	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	if (left_reg == right_reg) {
+		oleft = snd_sbmixer_read(sb, left_reg);
+		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
+		change = left != oleft;
+		if (change)
+			snd_sbmixer_write(sb, left_reg, left);
+	} else {
+		oleft = snd_sbmixer_read(sb, left_reg);
+		oright = snd_sbmixer_read(sb, right_reg);
+		left = (oleft & ~(mask << left_shift)) | left;
+		right = (oright & ~(mask << right_shift)) | right;
+		change = left != oleft || right != oright;
+		if (change) {
+			snd_sbmixer_write(sb, left_reg, left);
+			snd_sbmixer_write(sb, right_reg, right);
+		}
+	}
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * SBPRO input multiplexer
+ */
+
+static int snd_sb8mixer_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = {
+		"Mic", "CD", "Line"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+
+static int snd_sb8mixer_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char oval;
+	
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	switch ((oval >> 0x01) & 0x03) {
+	case SB_DSP_MIXS_CD:
+		ucontrol->value.enumerated.item[0] = 1;
+		break;
+	case SB_DSP_MIXS_LINE:
+		ucontrol->value.enumerated.item[0] = 2;
+		break;
+	default:
+		ucontrol->value.enumerated.item[0] = 0;
+		break;
+	}
+	return 0;
+}
+
+static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval, oval;
+	
+	if (ucontrol->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	switch (ucontrol->value.enumerated.item[0]) {
+	case 1:
+		nval = SB_DSP_MIXS_CD;
+		break;
+	case 2:
+		nval = SB_DSP_MIXS_LINE;
+		break;
+	default:
+		nval = SB_DSP_MIXS_MIC;
+	}
+	nval <<= 1;
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
+	nval |= oval & ~0x06;
+	change = nval != oval;
+	if (change)
+		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * SB16 input switch
+ */
+
+#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
+  name: xname, \
+  info: snd_sb16mixer_info_input_sw, \
+  get: snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \
+  private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
+
+static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_sb16mixer_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+	unsigned char val1, val2;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	val1 = snd_sbmixer_read(sb, reg1);
+	val2 = snd_sbmixer_read(sb, reg2);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
+	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
+	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
+	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
+	return 0;
+}                                                                                                                   
+
+static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sb_t *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+	int change;
+	unsigned char val1, val2, oval1, oval2;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval1 = snd_sbmixer_read(sb, reg1);
+	oval2 = snd_sbmixer_read(sb, reg2);
+	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
+	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
+	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
+	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
+	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
+	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
+	change = val1 != oval1 || val2 != oval2;
+	if (change) {
+		snd_sbmixer_write(sb, reg1, val1);
+		snd_sbmixer_write(sb, reg2, val2);
+	}
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+#define SB20_CONTROLS (sizeof(snd_sb20_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sb20_controls[] = {
+SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
+SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
+SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
+SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
+};
+
+#define SB20_INIT_VALUES (sizeof(snd_sb20_init_values)/sizeof(unsigned char)/2)
+
+static unsigned char snd_sb20_init_values[][2] = {
+	{ SB_DSP20_MASTER_DEV, 0 },
+	{ SB_DSP20_FM_DEV, 0 },
+};
+
+#define SBPRO_CONTROLS (sizeof(snd_sbpro_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sbpro_controls[] = {
+SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
+SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
+SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
+SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
+SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
+SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
+SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_sb8mixer_info_mux,
+	get: snd_sb8mixer_get_mux,
+	put: snd_sb8mixer_put_mux,
+},
+SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
+SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
+};
+
+#define SBPRO_INIT_VALUES (sizeof(snd_sbpro_init_values)/sizeof(unsigned char)/2)
+
+static unsigned char snd_sbpro_init_values[][2] = {
+	{ SB_DSP_MASTER_DEV, 0 },
+	{ SB_DSP_PCM_DEV, 0 },
+	{ SB_DSP_FM_DEV, 0 },
+};
+
+#define SB16_CONTROLS (sizeof(snd_sb16_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sb16_controls[] = {
+SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
+SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
+SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
+SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15),
+SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
+SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
+SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
+SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
+SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
+SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
+SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
+SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
+SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
+SB_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1),
+SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
+SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
+SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
+SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
+SB_SINGLE("Auto Mic Gain", SB_DSP4_MIC_AGC, 0, 1)
+};
+
+#define SB16_INIT_VALUES (sizeof(snd_sb16_init_values)/sizeof(unsigned char)/2)
+
+static unsigned char snd_sb16_init_values[][2] = {
+	{ SB_DSP4_MASTER_DEV + 0, 0 },
+	{ SB_DSP4_MASTER_DEV + 1, 0 },
+	{ SB_DSP4_PCM_DEV + 0, 0 },
+	{ SB_DSP4_PCM_DEV + 1, 0 },
+	{ SB_DSP4_SYNTH_DEV + 0, 0 },
+	{ SB_DSP4_SYNTH_DEV + 1, 0 },
+	{ SB_DSP4_INPUT_LEFT, 0 },
+	{ SB_DSP4_INPUT_RIGHT, 0 },
+	{ SB_DSP4_OUTPUT_SW, 0 },
+	{ SB_DSP4_SPEAKER_DEV, 0 },
+};
+
+static int snd_sbmixer_init(sb_t *chip,
+			    snd_kcontrol_new_t *controls,
+			    int controls_count,
+			    unsigned char map[][2],
+			    int map_count,
+			    char *name)
+{
+	unsigned long flags;
+	snd_card_t *card = chip->card;
+	int idx, err;
+
+	/* mixer reset */
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	snd_sbmixer_write(chip, 0x00, 0x00);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+	/* mute and zero volume channels */
+	for (idx = 0; idx < map_count; idx++) {
+		spin_lock_irqsave(&chip->mixer_lock, flags);
+		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
+		spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	}
+
+	for (idx = 0; idx < controls_count; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&controls[idx], chip))) < 0)
+			return err;
+	}
+	snd_component_add(card, name);
+	strcpy(card->mixername, name);
+	return 0;
+}
+
+int snd_sbmixer_new(sb_t *chip)
+{
+	snd_card_t * card;
+	int err;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	switch (chip->hardware) {
+	case SB_HW_10:
+		return 0; /* no mixer chip on SB1.x */
+	case SB_HW_20:
+	case SB_HW_201:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_sb20_controls, SB20_CONTROLS,
+					    snd_sb20_init_values, SB20_INIT_VALUES,
+					    "CTL1335")) < 0)
+			return err;
+		break;
+	case SB_HW_PRO:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_sbpro_controls, SBPRO_CONTROLS,
+					    snd_sbpro_init_values, SBPRO_INIT_VALUES,
+					    "CTL1345")) < 0)
+			return err;
+		break;
+	case SB_HW_16:
+	case SB_HW_ALS100:
+	case SB_HW_ALS4000:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_sb16_controls, SB16_CONTROLS,
+					    snd_sb16_init_values, SB16_INIT_VALUES,
+					    "CTL1745")) < 0)
+			return err;
+		break;
+	default:
+		strcpy(card->mixername, "???");
+	}
+	return 0;
+}
diff -Nru linux/sound/isa/sb/sbawe.c linux-2.4.19-pre5-mjc/sound/isa/sb/sbawe.c
--- linux/sound/isa/sb/sbawe.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sb/sbawe.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,2 @@
+#define SNDRV_SBAWE
+#include "sb16.c"
diff -Nru linux/sound/isa/sgalaxy.c linux-2.4.19-pre5-mjc/sound/isa/sgalaxy.c
--- linux/sound/isa/sgalaxy.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/sgalaxy.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,352 @@
+/*
+ *  Driver for Aztech Sound Galaxy cards
+ *  Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
+ *
+ *  I don't have documentation for this card, I based this driver on the
+ *  driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/ad1848.h>
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>");
+MODULE_DESCRIPTION("Aztech Sound Galaxy");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Aztech Systems,Sound Galaxy}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240 */
+static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x530,0xe80,0xf40,0x604 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 7,9,10,11 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Sound Galaxy soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Sound Galaxy soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sbport, "Port # for Sound Galaxy SB driver.");
+MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220},{0x240}},dialog:list");
+MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wssport, "Port # for Sound Galaxy WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for Sound Galaxy driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{7},{9},{10},{11}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for Sound Galaxy driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC);
+
+#define SGALAXY_AUXC_LEFT 18
+#define SGALAXY_AUXC_RIGHT 19
+
+static snd_card_t *snd_sgalaxy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+/*
+
+ */
+
+#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
+
+/* from lowlevel/sb/sb.c - to avoid having to allocate a sb_t for the */
+/* short time we actually need it.. */
+
+static int snd_sgalaxy_sbdsp_reset(unsigned long port)
+{
+	int i;
+
+	outb(1, SBP1(port, RESET));
+	udelay(10);
+	outb(0, SBP1(port, RESET));
+	udelay(30);
+	for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
+	if (inb(SBP1(port, READ)) != 0xaa) {
+		snd_printd("sb_reset: failed at 0x%lx!!!\n", port);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char val)
+{
+	int i;
+       	
+	for (i = 10000; i; i--)
+		if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
+			outb(val, SBP1(port, COMMAND));
+			return 1;
+		}
+
+	return 0;
+}
+
+static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
+{
+	static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 
+				       0x10, 0x18, 0x20, -1, -1, -1, -1};
+	static int dma_bits[] = {1, 2, 0, 3};
+	int tmp, tmp1;
+
+	unsigned long flags;
+
+	if ((tmp = inb(port + 3)) == 0xff)
+	{
+		snd_printdd("I/O address dead (0x%lx)\n", port);
+		return 0;
+	}
+#if 0
+	snd_printdd("WSS signature = 0x%x\n", tmp);
+#endif
+
+        if ((tmp & 0x3f) != 0x04 &&
+            (tmp & 0x3f) != 0x0f &&
+            (tmp & 0x3f) != 0x00) {
+		snd_printdd("No WSS signature detected on port 0x%lx\n",
+			    port + 3);
+		return 0;
+	}
+
+#if 0
+	snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n");
+#endif
+
+	save_flags(flags);
+	cli();
+
+        /* initialize IRQ for WSS codec */
+        tmp = interrupt_bits[irq % 16];
+        if (tmp < 0) {
+		restore_flags(flags);
+                return -EINVAL;
+	}
+        outb(tmp | 0x40, port);
+        tmp1 = dma_bits[dma % 4];
+        outb(tmp | tmp1, port);
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int __init snd_sgalaxy_detect(int dev, int irq, int dma)
+{
+#if 0
+	snd_printdd("sgalaxy - switching to WSS mode\n");
+#endif
+
+	/* switch to WSS mode */
+	snd_sgalaxy_sbdsp_reset(snd_sbport[dev]);
+
+	snd_sgalaxy_sbdsp_command(snd_sbport[dev], 9);
+	snd_sgalaxy_sbdsp_command(snd_sbport[dev], 0);
+
+	udelay(400);
+	return snd_sgalaxy_setup_wss(snd_wssport[dev], irq, dma);
+}
+
+#define SGALAXY_CONTROLS 2
+
+static snd_kcontrol_new_t snd_sgalaxy_controls[2] = {
+AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
+AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
+};
+
+static int __init snd_sgalaxy_mixer(ad1848_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int idx, err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUX0 to LINE */
+	strcpy(id1.name, "Aux Playback Switch");
+	strcpy(id2.name, "Line Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Line Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign AUX1 to FM */
+	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+	strcpy(id2.name, "FM Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "FM Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* build AUX2 input */
+	for (idx = 0; idx < SGALAXY_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sgalaxy_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int __init snd_sgalaxy_probe(int dev)
+{
+	static int possible_irqs[] = {7, 9, 10, 11, -1};
+	static int possible_dmas[] = {1, 3, 0, -1};
+	int err, irq, dma1;
+	snd_card_t *card;
+	ad1848_t *chip;
+
+	if (snd_sbport[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify SB port\n");
+		return -EINVAL;
+	}
+	if (snd_wssport[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify WSS port\n");
+		return -EINVAL;
+	}
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+        if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_sgalaxy_detect(dev, irq, dma1)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ad1848_create(card, snd_wssport[dev] + 4,
+				     irq, dma1,
+				     AD1848_HW_DETECT, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) {
+		snd_printdd("sgalaxy - error creating new ad1848 PCM device\n");
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ad1848_mixer(chip)) < 0) {
+		snd_printdd("sgalaxy - error creating new ad1848 mixer\n");
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_sgalaxy_mixer(chip) < 0) {
+		snd_printdd("sgalaxy - the mixer rewrite failed\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "Sound Galaxy");
+	strcpy(card->shortname, "Sound Galaxy");
+	sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
+		snd_wssport[dev], irq, dma1);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_sgalaxy_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_sgalaxy_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_sgalaxy_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR "Sound Galaxy soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit alsa_card_sgalaxy_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_sgalaxy_cards[idx]);
+}
+
+module_init(alsa_card_sgalaxy_init)
+module_exit(alsa_card_sgalaxy_exit)
+
+#ifndef MODULE
+
+/* format is: snd-sgalaxy=snd_enable,snd_index,snd_id,
+			  snd_sbport,snd_wssport,
+			  snd_irq,snd_dma1 */
+
+static int __init alsa_card_sgalaxy_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_dma1[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-sgalaxy=", alsa_card_sgalaxy_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/wavefront/Makefile linux-2.4.19-pre5-mjc/sound/isa/wavefront/Makefile
--- linux/sound/isa/wavefront/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,30 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _wavefront.o
+
+#list-multi   := snd-wavefront-fx.o snd-wavefront-synth.o snd-wavefront.o
+list-multi   := snd-wavefront.o
+
+#export-objs  := wavefront_fx.o wavefront_synth.o
+
+#snd-wavefront-fx-objs := wavefront_fx.o
+#snd-wavefront-synth-objs := wavefront_synth.o wavefront_midi.o
+#snd-wavefront-objs := wavefront.o
+snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o
+
+include $(TOPDIR)/Rules.make
+
+snd-wavefront-fx.o: $(snd-wavefront-fx-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-fx-objs)
+
+snd-wavefront-synth.o: $(snd-wavefront-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-synth-objs)
+
+snd-wavefront.o: $(snd-wavefront-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-objs)
diff -Nru linux/sound/isa/wavefront/wavefront.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront.c
--- linux/sound/isa/wavefront/wavefront.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,797 @@
+/*
+ *  ALSA card-level driver for Turtle Beach Wavefront cards 
+ *                                              (Maui,Tropez,Tropez+)
+ *
+ *  Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>
+ *
+ *  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
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/opl3.h>
+#include <sound/snd_wavefront.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION("Turtle Beach Wavefront");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Turtle Beach,Maui/Tropez/Tropez+}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	    /* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	    /* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	    /* Enable this card */
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static long snd_cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
+static long snd_cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
+static int snd_cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */
+static long snd_ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
+static int snd_ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* 2,9,11,12,15 */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	    /* PnP setup */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	    /* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	    /* 0,1,3,5,6,7 */
+static int snd_use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; 
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_SYNTAX(snd_index, "Index value for WaveFront soundcard.");
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for WaveFront soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable WaveFront soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for WaveFront soundcards.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_cs4232_pcm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_4232_port, "Port # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_cs4232_pcm_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_cs4232_pcm_irq, "IRQ # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_pcm_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+MODULE_PARM(snd_cs4232_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_cs4232_mpu_port, "port # for CS4232 MPU-401 interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_cs4232_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_mpu_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_ics2115_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_ics2115_irq, "IRQ # for ICS2115.");
+MODULE_PARM_SYNTAX(snd_ics2115_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_ics2115_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_ics2115_port, "Port # for ICS2115.");
+MODULE_PARM_SYNTAX(snd_ics2115_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port #.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_use_cs4232_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)");
+MODULE_PARM_SYNTAX(snd_use_cs4232_midi, SNDRV_ENABLED ",allows use of CS4323 MPU-401 interface");
+
+static snd_card_t *snd_wavefront_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_wavefront_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_wavefront_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static struct isapnp_card_id snd_wavefront_pnpids[] __devinitdata = {
+	{
+		ISAPNP_CARD_ID('C','S','C',0x7532),	/* Tropez */
+		devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
+			ISAPNP_DEVICE_ID('C','S','C',0x0010),	/* CTRL */
+			ISAPNP_DEVICE_ID('P','n','P',0xb006),	/* MPU */
+			ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */
+	},
+	{
+		ISAPNP_CARD_ID('C','S','C',0x7632),	/* Tropez+ */
+		devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
+			ISAPNP_DEVICE_ID('C','S','C',0x0010),	/* CTRL */
+			ISAPNP_DEVICE_ID('P','n','P',0xb006),	/* MPU */
+			ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */
+	},
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_wavefront_pnpids);
+
+static int __init
+snd_wavefront_isapnp (int dev, snd_wavefront_card_t *acard)
+{
+	const struct isapnp_card_id *id = snd_wavefront_isapnp_id[dev];
+	struct isapnp_card *card = snd_wavefront_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+	int tmp;
+
+	/* Check for each logical device. */
+
+	/* CS4232 chip (aka "windows sound system") is logical device 0 */
+
+	acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->wss->active) {
+		acard->wss = NULL;
+		return -EBUSY;
+	}
+
+	/* there is a game port at logical device 1, but we ignore it completely */
+
+	/* the control interface is logical device 2, but we ignore it
+	   completely. in fact, nobody even seems to know what it
+	   does.
+	*/
+
+	/* Only configure the CS4232 MIDI interface if its been
+	   specifically requested. It is logical device 3.
+	*/
+
+	if (snd_use_cs4232_midi[dev]) {
+		acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+		if (acard->mpu->active) {
+			acard->wss = acard->synth = acard->mpu = NULL;
+			return -EBUSY;
+		}
+	}
+
+	/* The ICS2115 synth is logical device 4 */
+
+	acard->synth = isapnp_find_dev(card, id->devs[3].vendor, id->devs[3].function, NULL);
+	if (acard->synth->active) {
+		acard->wss = acard->synth = NULL;
+		return -EBUSY;
+	}
+
+	/* PCM/FM initialization */
+
+	pdev = acard->wss;
+
+	if ((tmp = pdev->prepare (pdev)) < 0) {
+		if (tmp == -EBUSY) {
+			snd_printk ("ISA PnP configuration appears to have "
+				    "been done. Restart the isapnp module.\n");
+			return 0;
+		}
+		snd_printk ("isapnp WSS preparation failed\n");
+		return -EAGAIN;
+	}
+
+	/* An interesting note from the Tropez+ FAQ:
+
+	   Q. [Ports] Why is the base address of the WSS I/O ports off by 4?
+
+	   A. WSS I/O requires a block of 8 I/O addresses ("ports"). Of these, the first
+	   4 are used to identify and configure the board. With the advent of PnP,
+	   these first 4 addresses have become obsolete, and software applications
+	   only use the last 4 addresses to control the codec chip. Therefore, the
+	   base address setting "skips past" the 4 unused addresses.
+
+	*/
+
+	if (snd_cs4232_pcm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_cs4232_pcm_port[dev], 4);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_cs4232_pcm_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk ("isapnp WSS activation failed\n");
+		return -EBUSY;
+	}
+
+	snd_cs4232_pcm_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_cs4232_pcm_irq[dev] = pdev->irq_resource[0].start;
+
+	/* Synth initialization */
+
+	pdev = acard->synth;
+	
+	if ((tmp = pdev->prepare(pdev))<0) {
+		if (tmp == -EBUSY) {
+			snd_printk ("ISA PnP configuration appears to have "
+				    "been done. Restart the isapnp module.\n");
+		}
+		acard->wss->deactivate(acard->wss);
+		snd_printk ("ICS2115 synth preparation failed\n");
+		return -EAGAIN;
+	}
+	if (snd_ics2115_port[dev] != SNDRV_AUTO_PORT) {
+		isapnp_resource_change(&pdev->resource[0], snd_ics2115_port[dev], 16);
+	}
+		
+	if (snd_ics2115_port[dev] != SNDRV_AUTO_IRQ) {
+		isapnp_resource_change(&pdev->irq_resource[0], snd_ics2115_irq[dev], 1);
+	}
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("isapnp activation for ICS2115 failed\n");
+		acard->wss->deactivate(acard->wss);
+		return -EBUSY;
+	}
+
+	snd_ics2115_port[dev] = pdev->resource[0].start;
+	snd_ics2115_irq[dev] = pdev->irq_resource[0].start;
+
+	/* CS4232 MPU initialization. Configure this only if
+	   explicitly requested, since its physically inaccessible and
+	   consumes another IRQ.
+	*/
+
+	if (snd_use_cs4232_midi[dev]) {
+
+		pdev = acard->mpu;
+
+		if (pdev->prepare(pdev)<0) {
+			acard->wss->deactivate(acard->wss);
+			if (acard->synth)
+				acard->synth->deactivate(acard->synth);
+			snd_printk ("CS4232 MPU preparation failed\n");
+			return -EAGAIN;
+		}
+
+		if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT)
+			isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_port[dev], 2);
+		if (snd_cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+			isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_irq[dev], 1);
+
+		if (pdev->activate(pdev)<0) {
+			snd_printk("isapnp CS4232 MPU activation failed\n");
+			snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT;
+		} else {
+			snd_cs4232_mpu_port[dev] = pdev->resource[0].start;
+			snd_cs4232_mpu_irq[dev] = pdev->irq_resource[0].start;
+		}
+
+		snd_printk ("CS4232 MPU: port=0x%lx, irq=%i\n", 
+			    snd_cs4232_mpu_port[dev], 
+			    snd_cs4232_mpu_irq[dev]);
+	}
+
+	snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n", 
+		    snd_cs4232_pcm_port[dev], 
+		    snd_fm_port[dev],
+		    snd_dma1[dev], 
+		    snd_dma2[dev], 
+		    snd_cs4232_pcm_irq[dev],
+		    snd_ics2115_port[dev], 
+		    snd_ics2115_irq[dev]);
+	
+	return 0;
+}
+
+static void 
+snd_wavefront_deactivate (snd_wavefront_card_t *acard)
+{
+	snd_printk ("deactivating PnP devices\n");
+	if (acard->wss) {
+		acard->wss->deactivate(acard->wss);
+		acard->wss = NULL;
+	}
+	if (acard->ctrl) {
+		acard->ctrl->deactivate(acard->ctrl);
+		acard->ctrl = NULL;
+	}
+	if (acard->mpu) {
+		acard->mpu->deactivate(acard->mpu);
+		acard->mpu = NULL;
+	}
+	if (acard->synth) {
+		acard->synth->deactivate(acard->synth);
+		acard->synth = NULL;
+	}
+}
+
+#endif /* __ISAPNP__ */
+
+static void snd_wavefront_ics2115_interrupt(int irq, 
+					    void *dev_id, 
+					    struct pt_regs *regs)
+{
+	snd_wavefront_card_t *acard;
+
+	acard = (snd_wavefront_card_t *) dev_id;
+
+	if (acard == NULL) 
+		return;
+
+	if (acard->wavefront.interrupts_are_midi) {
+		snd_wavefront_midi_interrupt (acard);
+	} else {
+		snd_wavefront_internal_interrupt (acard);
+	}
+}
+
+snd_hwdep_t * __init
+snd_wavefront_new_synth (snd_card_t *card,
+			 int hw_dev,
+			 snd_wavefront_card_t *acard)
+{
+	snd_hwdep_t *wavefront_synth;
+
+	if (snd_wavefront_detect (acard) < 0) {
+		return NULL;
+	}
+
+	if (snd_wavefront_start (&acard->wavefront) < 0) {
+		return NULL;
+	}
+
+	if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0)
+		return NULL;
+	strcpy (wavefront_synth->name, 
+		"WaveFront (ICS2115) wavetable synthesizer");
+	wavefront_synth->ops.open = snd_wavefront_synth_open;
+	wavefront_synth->ops.release = snd_wavefront_synth_release;
+	wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl;
+
+	return wavefront_synth;
+}
+
+snd_hwdep_t * __init
+snd_wavefront_new_fx (snd_card_t *card,
+		      int hw_dev,
+		      snd_wavefront_card_t *acard,
+		      unsigned long port)
+
+{
+	snd_hwdep_t *fx_processor;
+
+	if (snd_wavefront_fx_start (&acard->wavefront)) {
+		snd_printk ("cannot initialize YSS225 FX processor");
+		return NULL;
+	}
+
+	if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0)
+		return NULL;
+	sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port);
+	fx_processor->ops.open = snd_wavefront_fx_open;
+	fx_processor->ops.release = snd_wavefront_fx_release;
+	fx_processor->ops.ioctl = snd_wavefront_fx_ioctl;
+	
+	return fx_processor;
+}
+
+static snd_wavefront_mpu_id internal_id = internal_mpu;
+static snd_wavefront_mpu_id external_id = external_mpu;
+
+snd_rawmidi_t * __init
+snd_wavefront_new_midi (snd_card_t *card,
+			int midi_dev,
+			snd_wavefront_card_t *acard,
+			unsigned long port,
+			snd_wavefront_mpu_id mpu)
+
+{
+	snd_rawmidi_t *rmidi;
+	static int first = 1;
+
+	if (first) {
+		first = 0;
+		acard->wavefront.midi.base = port;
+		if (snd_wavefront_midi_start (acard)) {
+			snd_printk ("cannot initialize MIDI interface\n");
+			return NULL;
+		}
+	}
+
+	if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0)
+		return NULL;
+
+	if (mpu == internal_mpu) {
+		strcpy(rmidi->name, "WaveFront MIDI (Internal)");
+		rmidi->private_data = &internal_id;
+	} else {
+		strcpy(rmidi->name, "WaveFront MIDI (External)");
+		rmidi->private_data = &external_id;
+	}
+
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input);
+
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	return rmidi;
+}
+
+static void
+snd_wavefront_free(snd_card_t *card)
+{
+	snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data;
+	
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_wavefront_deactivate(acard);
+#endif
+		if (acard->wavefront.res_base != NULL) {
+			release_resource(acard->wavefront.res_base);
+			kfree_nocheck(acard->wavefront.res_base);
+		}
+		if (acard->wavefront.irq > 0)
+			free_irq(acard->wavefront.irq, (void *)acard);
+	}
+}
+
+static int __init
+snd_wavefront_probe (int dev)
+{
+	snd_card_t *card;
+	snd_wavefront_card_t *acard;
+	cs4231_t *chip;
+	snd_hwdep_t *wavefront_synth;
+	snd_rawmidi_t *ics2115_internal_rmidi = NULL;
+	snd_rawmidi_t *ics2115_external_rmidi = NULL;
+	snd_hwdep_t *fx_processor;
+	int hw_dev = 0, midi_dev = 0, err;
+
+	if (snd_cs4232_mpu_port[dev] < 0)
+		snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_fm_port[dev] < 0)
+		snd_fm_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_ics2115_port[dev] < 0)
+		snd_ics2115_port[dev] = SNDRV_AUTO_PORT;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify CS4232 port\n");
+			return -EINVAL;
+		}
+		if (snd_ics2115_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify ICS2115 port\n");
+			return -ENODEV;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new (snd_index[dev], 
+			     snd_id[dev],
+			     THIS_MODULE,
+			     sizeof(snd_wavefront_card_t));
+
+	if (card == NULL) {
+		return -ENOMEM;
+	}
+	acard = (snd_wavefront_card_t *)card->private_data;
+	acard->wavefront.irq = -1;
+	init_waitqueue_head(&acard->wavefront.interrupt_sleeper);
+	spin_lock_init(&acard->wavefront.midi.open);
+	spin_lock_init(&acard->wavefront.midi.virtual);
+	card->private_free = snd_wavefront_free;
+
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && snd_wavefront_isapnp (dev, acard) < 0) {
+		if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk ("isapnp detection failed\n");
+			snd_card_free (card);
+			return -ENODEV;
+		}
+	}
+#endif /* __ISAPNP__ */
+
+	/* --------- PCM --------------- */
+
+	if ((err = snd_cs4231_create (card,
+				      snd_cs4232_pcm_port[dev],
+				      -1,
+				      snd_cs4232_pcm_irq[dev],
+				      snd_dma1[dev],
+				      snd_dma2[dev],
+				      CS4231_HW_DETECT, 0, &chip)) < 0) {
+		snd_card_free(card);
+		snd_printk ("can't allocate CS4231 device\n");
+		return err;
+	}
+
+	if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	/* ---------- OPL3 synth --------- */
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT) {
+		opl3_t *opl3;
+
+	        if ((err = snd_opl3_create(card,
+					   snd_fm_port[dev],
+					   snd_fm_port[dev] + 2,
+					   OPL3_HW_OPL3_CS,
+					   0, &opl3)) < 0) {
+			snd_printk ("can't allocate or detect OPL3 synth\n");
+			snd_card_free(card);
+			return err;
+		}
+
+		if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		hw_dev++;
+	}
+
+	/* ------- ICS2115 Wavetable synth ------- */
+
+	if ((acard->wavefront.res_base = request_region(snd_ics2115_port[dev], 16, "ICS2115")) == NULL) {
+		snd_printk("unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", snd_ics2115_port[dev], snd_ics2115_port[dev] + 16 - 1);
+		snd_card_free(card);
+		return -EBUSY;
+	}
+	if (request_irq(snd_ics2115_irq[dev], snd_wavefront_ics2115_interrupt, SA_INTERRUPT, "ICS2115", (void *)acard)) {
+		snd_printk("unable to use ICS2115 IRQ %d\n", snd_ics2115_irq[dev]);
+		snd_card_free(card);
+		return -EBUSY;
+	}
+	
+	acard->wavefront.irq = snd_ics2115_irq[dev];
+	acard->wavefront.base = snd_ics2115_port[dev];
+
+	if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) {
+		snd_printk ("can't create WaveFront synth device\n");
+		snd_card_free(card);
+		return -ENOMEM;
+	}
+
+	strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer");
+	wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115;
+	hw_dev++;
+
+	/* --------- Mixer ------------ */
+
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_printk ("can't allocate mixer device\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	/* -------- CS4232 MPU-401 interface -------- */
+
+	if (snd_cs4232_mpu_port[dev] > 0 && snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
+		if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
+					       snd_cs4232_mpu_port[dev], 0,
+					       snd_cs4232_mpu_irq[dev],
+					       SA_INTERRUPT,
+					       NULL)) < 0) {
+			snd_printk ("can't allocate CS4232 MPU-401 device\n");
+			snd_card_free(card);
+			return err;
+		}
+		midi_dev++;
+	}
+
+	/* ------ ICS2115 internal MIDI ------------ */
+
+	if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) {
+		ics2115_internal_rmidi = 
+			snd_wavefront_new_midi (card, 
+						midi_dev,
+						acard,
+						snd_ics2115_port[dev],
+						internal_mpu);
+		if (ics2115_internal_rmidi == NULL) {
+			snd_printk ("can't setup ICS2115 internal MIDI device\n");
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+		midi_dev++;
+	}
+
+	/* ------ ICS2115 external MIDI ------------ */
+
+	if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) {
+		ics2115_external_rmidi = 
+			snd_wavefront_new_midi (card, 
+						midi_dev,
+						acard,
+						snd_ics2115_port[dev],
+						external_mpu);
+		if (ics2115_external_rmidi == NULL) {
+			snd_printk ("can't setup ICS2115 external MIDI device\n");
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+		midi_dev++;
+	}
+
+	/* FX processor for Tropez+ */
+
+	if (acard->wavefront.has_fx) {
+		fx_processor = snd_wavefront_new_fx (card,
+						     hw_dev,
+						     acard,
+						     snd_ics2115_port[dev]);
+		if (fx_processor == NULL) {
+			snd_printk ("can't setup FX device\n");
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+
+		hw_dev++;
+
+		strcpy(card->driver, "Tropez+");
+		strcpy(card->shortname, "Turtle Beach Tropez+");
+	} else {
+		/* Need a way to distinguish between Maui and Tropez */
+		strcpy(card->driver, "WaveFront");
+		strcpy(card->shortname, "Turtle Beach WaveFront");
+	}
+
+	/* ----- Register the card --------- */
+
+	/* Not safe to include "Turtle Beach" in longname, due to 
+	   length restrictions
+	*/
+
+	sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d",
+		card->driver,
+		chip->port,
+		snd_cs4232_pcm_irq[dev],
+		snd_dma1[dev]);
+
+	if (snd_dma2[dev] >= 0 && snd_dma2[dev] < 8)
+		sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]);
+
+	if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
+		sprintf (card->longname + strlen (card->longname), 
+			 " MPU-401 0x%lx irq %d",
+			 snd_cs4232_mpu_port[dev],
+			 snd_cs4232_mpu_irq[dev]);
+	}
+
+	sprintf (card->longname + strlen (card->longname), 
+		 " SYNTH 0x%lx irq %d",
+		 snd_ics2115_port[dev],
+		 snd_ics2115_irq[dev]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_wavefront_cards[dev] = card;
+	return 0;
+}	
+
+#ifdef __ISAPNP__
+
+static int __init snd_wavefront_isapnp_detect(struct isapnp_card *card,
+                                              const struct isapnp_card_id *id)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev] || !snd_isapnp[dev])
+                        continue;
+                snd_wavefront_isapnp_cards[dev] = card;
+                snd_wavefront_isapnp_id[dev] = id;
+                res = snd_wavefront_probe(dev);
+                if (res < 0)
+                        return res;
+                dev++;
+                return 0;
+        }
+
+        return -ENODEV;
+}
+
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_wavefront_init(void)
+{
+	int cards = 0;
+	int dev;
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_wavefront_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_wavefront_pnpids, snd_wavefront_isapnp_detect);
+#endif
+	if (!cards) {
+#ifdef MODULE
+		printk (KERN_ERR "No WaveFront cards found or devices busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_wavefront_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_wavefront_cards[idx]);
+}
+
+module_init(alsa_card_wavefront_init)
+module_exit(alsa_card_wavefront_exit)
+
+#ifndef MODULE
+
+/* format is: snd-wavefront=snd_enable,snd_index,snd_id,snd_isapnp,
+			    snd_cs4232_pcm_port,snd_cs4232_pcm_irq,
+			    snd_cs4232_mpu_port,snd_cs4232_mpu_irq,
+			    snd_ics2115_port,snd_ics2115_irq,
+			    snd_fm_port,
+			    snd_dma1,snd_dma2,
+			    snd_use_cs4232_midi */
+
+static int __init alsa_card_wavefront_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_isapnp[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_cs4232_pcm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_cs4232_pcm_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_cs4232_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_cs4232_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_ics2115_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ics2115_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_use_cs4232_midi[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-wavefront=", alsa_card_wavefront_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/wavefront/wavefront_fx.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_fx.c
--- linux/sound/isa/wavefront/wavefront_fx.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_fx.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1052 @@
+/*
+ *  Copyright (c) 1998-2002 by Paul Davis <pbd@op.net>
+ *
+ *  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
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/snd_wavefront.h>
+#include <sound/yss225.h>
+#include <sound/initval.h>
+
+#if 0
+MODULE_AUTHOR("Paul Davis <pbd@op.net>");
+MODULE_DESCRIPTION("ALSA driver for Turtle Beach Tropez+ YSS225 FX Processor");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+#endif
+
+/* Control bits for the Load Control Register
+ */
+
+#define FX_LSB_TRANSFER 0x01    /* transfer after DSP LSB byte written */
+#define FX_MSB_TRANSFER 0x02    /* transfer after DSP MSB byte written */
+#define FX_AUTO_INCR    0x04    /* auto-increment DSP address after transfer */
+
+static inline void
+dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+static int
+wavefront_fx_idle (snd_wavefront_t *dev) 
+    
+{
+	int i;
+	unsigned int x = 0x80;
+    
+	for (i = 0; i < 1000; i++) {
+		x = inb (dev->fx_status);
+		if ((x & 0x80) == 0) {
+			break;
+		}
+	}
+    
+	if (x & 0x80) {
+		snd_printk ("FX device never idle.\n");
+		return 0;
+	}
+    
+	return (1);
+}
+
+static void
+wavefront_fx_mute (snd_wavefront_t *dev, int onoff)
+    
+{
+	if (!wavefront_fx_idle(dev)) {
+		return;
+	}
+    
+	outb (onoff ? 0x02 : 0x00, dev->fx_op);
+}
+
+static int
+wavefront_fx_memset (snd_wavefront_t *dev, 
+		     int page,
+		     int addr, 
+		     int cnt, 
+		     unsigned short *data)
+{
+	if (page < 0 || page > 7) {
+		snd_printk ("FX memset: "
+			"page must be >= 0 and <= 7\n");
+		return -(EINVAL);
+	}
+
+	if (addr < 0 || addr > 0x7f) {
+		snd_printk ("FX memset: "
+			"addr must be >= 0 and <= 7f\n");
+		return -(EINVAL);
+	}
+
+	if (cnt == 1) {
+
+		outb (FX_LSB_TRANSFER, dev->fx_lcr);
+		outb (page, dev->fx_dsp_page);
+		outb (addr, dev->fx_dsp_addr);
+		outb ((data[0] >> 8), dev->fx_dsp_msb);
+		outb ((data[0] & 0xff), dev->fx_dsp_lsb);
+
+		snd_printk ("FX: addr %d:%x set to 0x%x\n",
+			page, addr, data[0]);
+	
+	} else {
+		int i;
+
+		outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+		outb (page, dev->fx_dsp_page);
+		outb (addr, dev->fx_dsp_addr);
+
+		for (i = 0; i < cnt; i++) {
+			outb ((data[i] >> 8), dev->fx_dsp_msb);
+			outb ((data[i] & 0xff), dev->fx_dsp_lsb);
+			if (!wavefront_fx_idle (dev)) {
+				break;
+			}
+		}
+
+		if (i != cnt) {
+			snd_printk ("FX memset "
+				    "(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
+				    page, addr, (unsigned long) data, cnt);
+			return -(EIO);
+		}
+	}
+
+	return 0;
+}
+
+int 
+snd_wavefront_fx_detect (snd_wavefront_t *dev)
+
+{
+	/* This is a crude check, but its the best one I have for now.
+	   Certainly on the Maui and the Tropez, wavefront_fx_idle() will
+	   report "never idle", which suggests that this test should
+	   work OK.
+	*/
+
+	if (inb (dev->fx_status) & 0x80) {
+		snd_printk ("Hmm, probably a Maui or Tropez.\n");
+		return -1;
+	}
+
+	return 0;
+}	
+
+int 
+snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file)
+
+{
+	MOD_INC_USE_COUNT;
+	if (!try_inc_mod_count(hw->card->module)) {
+		MOD_DEC_USE_COUNT;
+		return -EFAULT;
+	}
+	file->private_data = hw;
+	return 0;
+}
+
+int 
+snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file)
+
+{
+	dec_mod_count(hw->card->module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int
+snd_wavefront_fx_ioctl (snd_hwdep_t *sdev, struct file *file,
+			unsigned int cmd, unsigned long arg)
+
+{
+	snd_card_t *card;
+	snd_wavefront_card_t *acard;
+	snd_wavefront_t *dev;
+	wavefront_fx_info r;
+	unsigned short page_data[256];
+	unsigned short *pd;
+
+	snd_assert(sdev->card != NULL, return -ENODEV);
+	
+	card = sdev->card;
+
+	snd_assert(card->private_data != NULL, return -ENODEV);
+
+	acard = card->private_data;
+	dev = &acard->wavefront;
+
+	if (copy_from_user (&r, (unsigned char *) arg, sizeof (wavefront_fx_info)))
+		return -EFAULT;
+
+	switch (r.request) {
+	case WFFX_MUTE:
+		wavefront_fx_mute (dev, r.data[0]);
+		return -EIO;
+
+	case WFFX_MEMSET:
+		if (r.data[2] <= 0) {
+			snd_printk ("cannot write "
+				"<= 0 bytes to FX\n");
+			return -EIO;
+		} else if (r.data[2] == 1) {
+			pd = (unsigned short *) &r.data[3];
+		} else {
+			if (r.data[2] > sizeof (page_data)) {
+				snd_printk ("cannot write "
+					    "> 255 bytes to FX\n");
+				return -EIO;
+			}
+			if (copy_from_user (page_data,
+					    (unsigned char *) r.data[3],
+					    r.data[2]))
+				return -EFAULT;
+			pd = page_data;
+		}
+
+		wavefront_fx_memset (dev,
+			     r.data[0], /* page */
+			     r.data[1], /* addr */
+			     r.data[2], /* cnt */
+			     pd);
+		break;
+
+	default:
+		snd_printk ("FX: ioctl %d not yet supported\n",
+			    r.request);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+/* YSS225 initialization.
+
+   This code was developed using DOSEMU. The Turtle Beach SETUPSND
+   utility was run with I/O tracing in DOSEMU enabled, and a reconstruction
+   of the port I/O done, using the Yamaha faxback document as a guide
+   to add more logic to the code. Its really pretty wierd.
+
+   There was an alternative approach of just dumping the whole I/O
+   sequence as a series of port/value pairs and a simple loop
+   that output it. However, I hope that eventually I'll get more
+   control over what this code does, and so I tried to stick with
+   a somewhat "algorithmic" approach.
+*/
+
+
+int
+snd_wavefront_fx_start (snd_wavefront_t *dev)
+
+{
+	unsigned int i, j;
+
+	/* Set all bits for all channels on the MOD unit to zero */
+	/* XXX But why do this twice ? */
+
+	for (j = 0; j < 2; j++) {
+		for (i = 0x10; i <= 0xff; i++) {
+	    
+			if (!wavefront_fx_idle (dev)) {
+				return (-1);
+			}
+	    
+			outb (i, dev->fx_mod_addr);
+			outb (0x0, dev->fx_mod_data);
+		}
+	}
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x02, dev->fx_op);                        /* mute on */
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x44, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x42, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x43, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x7c, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x7e, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x46, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x49, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x47, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x4a, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+
+	/* either because of stupidity by TB's programmers, or because it
+	   actually does something, rezero the MOD page.
+	*/
+	for (i = 0x10; i <= 0xff; i++) {
+	
+		if (!wavefront_fx_idle (dev)) {
+			return (-1);
+		}
+	
+		outb (i, dev->fx_mod_addr);
+		outb (0x0, dev->fx_mod_data);
+	}
+	/* load page zero */
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x00, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_zero); i += 2) {
+		outb (page_zero[i], dev->fx_dsp_msb);
+		outb (page_zero[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Now load page one */
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x01, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_one); i += 2) {
+		outb (page_one[i], dev->fx_dsp_msb);
+		outb (page_one[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x02, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_two); i++) {
+		outb (page_two[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x03, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_three); i++) {
+		outb (page_three[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x04, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_four); i++) {
+		outb (page_four[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Load memory area (page six) */
+    
+	outb (FX_LSB_TRANSFER, dev->fx_lcr); 
+	outb (0x06, dev->fx_dsp_page); 
+
+	for (i = 0; i < sizeof (page_six); i += 3) {
+		outb (page_six[i], dev->fx_dsp_addr);
+		outb (page_six[i+1], dev->fx_dsp_msb);
+		outb (page_six[i+2], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_seven); i += 2) {
+		outb (page_seven[i], dev->fx_dsp_msb);
+		outb (page_seven[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Now setup the MOD area. We do this algorithmically in order to
+	   save a little data space. It could be done in the same fashion
+	   as the "pages".
+	*/
+
+	for (i = 0x00; i <= 0x0f; i++) {
+		outb (0x01, dev->fx_mod_addr);
+		outb (i, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+		outb (0x02, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0xb0; i <= 0xbf; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x20, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0xf0; i <= 0xff; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x20, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0x10; i <= 0x1d; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0xff, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x1e, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x1f; i <= 0x2d; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0xff, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x2e, dev->fx_mod_addr);
+	outb (0x00, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x2f; i <= 0x3e; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x3f, dev->fx_mod_addr);
+	outb (0x20, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x40; i <= 0x4d; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x4e, dev->fx_mod_addr);
+	outb (0x0e, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x4f, dev->fx_mod_addr);
+	outb (0x0e, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+
+	for (i = 0x50; i <= 0x6b; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x6c, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	outb (0x6d, dev->fx_mod_addr);
+	outb (0x00, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	outb (0x6e, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	outb (0x6f, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x70; i <= 0x7f; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0xc0, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	for (i = 0x80; i <= 0xaf; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0xc0; i <= 0xdd; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0xde, dev->fx_mod_addr);
+	outb (0x10, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0xdf, dev->fx_mod_addr);
+	outb (0x10, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0xe0; i <= 0xef; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0x00; i <= 0x0f; i++) {
+		outb (0x01, dev->fx_mod_addr);
+		outb (i, dev->fx_mod_data);
+		outb (0x02, dev->fx_mod_addr);
+		outb (0x01, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x02, dev->fx_op); /* mute on */
+
+	/* Now set the coefficients and so forth for the programs above */
+
+	for (i = 0; i < sizeof (coefficients); i += 4) {
+		outb (coefficients[i], dev->fx_dsp_page);
+		outb (coefficients[i+1], dev->fx_dsp_addr);
+		outb (coefficients[i+2], dev->fx_dsp_msb);
+		outb (coefficients[i+3], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Some settings (?) that are too small to bundle into loops */
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x1e, dev->fx_mod_addr);
+	outb (0x14, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0xde, dev->fx_mod_addr);
+	outb (0x20, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0xdf, dev->fx_mod_addr);
+	outb (0x20, dev->fx_mod_data);
+    
+	/* some more coefficients */
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x06, dev->fx_dsp_page);
+	outb (0x78, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x40, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x03, dev->fx_dsp_addr);
+	outb (0x0f, dev->fx_dsp_msb);
+	outb (0xff, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x0b, dev->fx_dsp_addr);
+	outb (0x0f, dev->fx_dsp_msb);
+	outb (0xff, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x02, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x0a, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x46, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x49, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+    
+	/* Now, for some strange reason, lets reload every page
+	   and all the coefficients over again. I have *NO* idea
+	   why this is done. I do know that no sound is produced
+	   is this phase is omitted.
+	*/
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x00, dev->fx_dsp_page);  
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_zero_v2); i += 2) {
+		outb (page_zero_v2[i], dev->fx_dsp_msb);
+		outb (page_zero_v2[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x01, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_one_v2); i += 2) {
+		outb (page_one_v2[i], dev->fx_dsp_msb);
+		outb (page_one_v2[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	if (!wavefront_fx_idle (dev)) return (-1);
+	if (!wavefront_fx_idle (dev)) return (-1);
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x02, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_two_v2); i++) {
+		outb (page_two_v2[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x03, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_three_v2); i++) {
+		outb (page_three_v2[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x04, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_four_v2); i++) {
+		outb (page_four_v2[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x06, dev->fx_dsp_page);
+
+	/* Page six v.2 is algorithmic */
+    
+	for (i = 0x10; i <= 0x3e; i += 2) {
+		outb (i, dev->fx_dsp_addr);
+		outb (0x00, dev->fx_dsp_msb);
+		outb (0x00, dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_seven_v2); i += 2) {
+		outb (page_seven_v2[i], dev->fx_dsp_msb);
+		outb (page_seven_v2[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0x00; i < sizeof(mod_v2); i += 2) {
+		outb (mod_v2[i], dev->fx_mod_addr);
+		outb (mod_v2[i+1], dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0; i < sizeof (coefficients2); i += 4) {
+		outb (coefficients2[i], dev->fx_dsp_page);
+		outb (coefficients2[i+1], dev->fx_dsp_addr);
+		outb (coefficients2[i+2], dev->fx_dsp_msb);
+		outb (coefficients2[i+3], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0; i < sizeof (coefficients3); i += 2) {
+		int x;
+
+		outb (0x07, dev->fx_dsp_page);
+		x = (i % 4) ? 0x4e : 0x4c;
+		outb (x, dev->fx_dsp_addr);
+		outb (coefficients3[i], dev->fx_dsp_msb);
+		outb (coefficients3[i+1], dev->fx_dsp_lsb);
+	}
+
+	outb (0x00, dev->fx_op); /* mute off */
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	return (0);
+}
+
+/* wierd stuff, derived from port I/O tracing with dosemu */
+
+static unsigned char page_zero[] __initdata = {
+0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
+0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
+0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19,
+0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01,
+0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00,
+0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02,
+0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
+0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00,
+0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02,
+0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40,
+0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02,
+0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00,
+0x1d, 0x02, 0xdf
+};    
+
+static unsigned char page_one[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00,
+0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01,
+0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00,
+0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7,
+0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0,
+0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00,
+0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0,
+0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03,
+0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00,
+0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02,
+0x60, 0x00, 0x1b
+};
+
+static unsigned char page_two[] __initdata = {
+0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4,
+0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07,
+0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46,
+0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46,
+0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07,
+0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05,
+0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05,
+0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44
+};
+
+static unsigned char page_three[] __initdata = {
+0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
+0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
+0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40,
+0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00,
+0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40
+};
+
+static unsigned char page_four[] __initdata = {
+0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
+0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00,
+0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60,
+0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00,
+0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22,
+0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01
+};
+
+static unsigned char page_six[] __initdata = {
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e,
+0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00,
+0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00,
+0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24,
+0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00,
+0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00,
+0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a,
+0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00,
+0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d,
+0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50,
+0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00,
+0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17,
+0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66,
+0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c,
+0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00,
+0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c,
+0x80, 0x00, 0x7e, 0x80, 0x80
+};
+
+static unsigned char page_seven[] __initdata = {
+0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f,
+0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff,
+0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f,
+0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38,
+0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06,
+0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b,
+0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06,
+0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55,
+0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14,
+0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93,
+0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x00
+};
+
+static unsigned char page_zero_v2[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char page_one_v2[] __initdata = {
+0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char page_two_v2[] __initdata = {
+0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+static unsigned char page_three_v2[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+static unsigned char page_four_v2[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char page_seven_v2[] __initdata = {
+0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char mod_v2[] __initdata = {
+0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02,
+0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05,
+0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0,
+0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20,
+0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3,
+0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff,
+0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16,
+0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff,
+0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31,
+0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
+0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44,
+0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00,
+0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57,
+0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00,
+0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72,
+0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0,
+0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85,
+0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00,
+0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0,
+0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00,
+0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00,
+0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6,
+0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00,
+0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02,
+0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03,
+0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01,
+0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01
+};
+static unsigned char coefficients[] __initdata = {
+0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03,
+0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49,
+0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01,
+0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00,
+0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00,
+0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47,
+0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07,
+0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00,
+0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01,
+0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43,
+0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07,
+0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00,
+0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02,
+0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44,
+0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07,
+0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40,
+0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a,
+0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56,
+0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07,
+0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda,
+0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05,
+0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79,
+0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07,
+0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52,
+0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03,
+0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a,
+0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06,
+0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3,
+0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20,
+0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c,
+0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06,
+0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48,
+0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02,
+0xba
+};
+static unsigned char coefficients2[] __initdata = {
+0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f,
+0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d,
+0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07,
+0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00,
+0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00
+};
+static unsigned char coefficients3[] __initdata = { 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00,
+0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc,
+0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01,
+0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99,
+0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02,
+0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f,
+0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03,
+0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c,
+0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03,
+0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51,
+0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04,
+0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e,
+0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05,
+0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14,
+0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06,
+0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1,
+0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07,
+0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7,
+0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08,
+0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3,
+0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09,
+0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99,
+0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a,
+0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66,
+0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a,
+0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c,
+0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b,
+0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28,
+0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c,
+0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e,
+0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d,
+0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb,
+0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e,
+0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1,
+0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f,
+0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae,
+0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff
+};
+
+#if 0
+EXPORT_SYMBOL(snd_wavefront_fx_start);
+EXPORT_SYMBOL(snd_wavefront_fx_detect);
+EXPORT_SYMBOL(snd_wavefront_fx_ioctl);
+EXPORT_SYMBOL(snd_wavefront_fx_open);
+EXPORT_SYMBOL(snd_wavefront_fx_release);
+
+static int __init alsa_wavefront_fx_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_wavefront_fx_exit(void)
+{
+}
+
+module_init(alsa_wavefront_fx_init)
+module_exit(alsa_wavefront_fx_exit)
+#endif
diff -Nru linux/sound/isa/wavefront/wavefront_midi.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_midi.c
--- linux/sound/isa/wavefront/wavefront_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) by Paul Barton-Davis 1998-1999
+ *
+ * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this
+ * software for more info.  
+ */
+
+/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
+ *
+ * Note that there is also an MPU-401 emulation (actually, a UART-401
+ * emulation) on the CS4232 on the Tropez and Tropez Plus. This code
+ * has nothing to do with that interface at all.
+ *
+ * The interface is essentially just a UART-401, but is has the
+ * interesting property of supporting what Turtle Beach called
+ * "Virtual MIDI" mode. In this mode, there are effectively *two*
+ * MIDI buses accessible via the interface, one that is routed
+ * solely to/from the external WaveFront synthesizer and the other
+ * corresponding to the pin/socket connector used to link external
+ * MIDI devices to the board.
+ *
+ * This driver fully supports this mode, allowing two distinct MIDI
+ * busses to be used completely independently, giving 32 channels of
+ * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI
+ * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,
+ * where `n' is the card number. Note that the device numbers may be
+ * something other than 0 and 1 if the CS4232 UART/MPU-401 interface
+ * is enabled.
+ *
+ * Switching between the two is accomplished externally by the driver
+ * using the two otherwise unused MIDI bytes. See the code for more details.
+ *
+ * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)
+ *
+ * The main reason to turn off Virtual MIDI mode is when you want to
+ * tightly couple the WaveFront synth with an external MIDI
+ * device. You won't be able to distinguish the source of any MIDI
+ * data except via SysEx ID, but thats probably OK, since for the most
+ * part, the WaveFront won't be sending any MIDI data at all.
+ *  
+ * The main reason to turn on Virtual MIDI Mode is to provide two
+ * completely independent 16-channel MIDI buses, one to the
+ * WaveFront and one to any external MIDI devices. Given the 32
+ * voice nature of the WaveFront, its pretty easy to find a use
+ * for all 16 channels driving just that synth.
+ *  
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/snd_wavefront.h>
+
+static inline int 
+wf_mpu_status (snd_wavefront_midi_t *midi)
+
+{
+	return inb (midi->mpu_status_port);
+}
+
+static inline int 
+input_avail (snd_wavefront_midi_t *midi)
+
+{
+	return !(wf_mpu_status(midi) & INPUT_AVAIL);
+}
+
+static inline int
+output_ready (snd_wavefront_midi_t *midi)
+
+{
+	return !(wf_mpu_status(midi) & OUTPUT_READY);
+}
+
+static inline int 
+read_data (snd_wavefront_midi_t *midi)
+
+{
+	return inb (midi->mpu_data_port);
+}
+
+static inline void 
+write_data (snd_wavefront_midi_t *midi, unsigned char byte)
+
+{
+	outb (byte, midi->mpu_data_port);
+}
+
+static snd_wavefront_midi_t *
+get_wavefront_midi (snd_rawmidi_substream_t *substream)
+
+{
+	snd_card_t *card;
+	snd_wavefront_card_t *acard;
+
+	if (substream == NULL || substream->rmidi == NULL) 
+	        return NULL;
+
+	card = substream->rmidi->card;
+
+	if (card == NULL) 
+	        return NULL;
+
+	if (card->private_data == NULL) 
+ 	        return NULL;
+
+	acard = card->private_data;
+
+	return &acard->wavefront.midi;
+}
+
+static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
+{
+	snd_wavefront_midi_t *midi = &card->wavefront.midi;
+	snd_wavefront_mpu_id  mpu;
+	unsigned long flags;
+	unsigned char midi_byte;
+	int max = 256, mask = 1;
+	int timeout;
+
+	/* Its not OK to try to change the status of "virtuality" of
+	   the MIDI interface while we're outputting stuff.  See
+	   snd_wavefront_midi_{enable,disable}_virtual () for the
+	   other half of this.  
+
+	   The first loop attempts to flush any data from the
+	   current output device, and then the second 
+	   emits the switch byte (if necessary), and starts
+	   outputting data for the output device currently in use.
+	*/
+
+	if (midi->substream_output[midi->output_mpu] == NULL) {
+		goto __second;
+	}
+
+	while (max > 0) {
+
+		/* XXX fix me - no hard timing loops allowed! */
+
+		for (timeout = 30000; timeout > 0; timeout--) {
+			if (output_ready (midi))
+				break;
+		}
+	
+		spin_lock_irqsave (&midi->virtual, flags);
+		if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			goto __second;
+		}
+		if (output_ready (midi)) {
+			if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
+				if (!midi->isvirtual ||
+					(midi_byte != WF_INTERNAL_SWITCH &&
+					 midi_byte != WF_EXTERNAL_SWITCH))
+					write_data(midi, midi_byte);
+				max--;
+			} else {
+				if (midi->istimer) {
+					if (--midi->istimer <= 0)
+						del_timer(&midi->timer);
+				}
+				midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				goto __second;
+			}
+		} else {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			return;
+		}
+		spin_unlock_irqrestore (&midi->virtual, flags);
+	}
+
+      __second:
+
+	if (midi->substream_output[!midi->output_mpu] == NULL) {
+		return;
+	}
+
+	while (max > 0) {
+
+		/* XXX fix me - no hard timing loops allowed! */
+
+		for (timeout = 30000; timeout > 0; timeout--) {
+			if (output_ready (midi))
+				break;
+		}
+	
+		spin_lock_irqsave (&midi->virtual, flags);
+		if (!midi->isvirtual)
+			mask = 0;
+		mpu = midi->output_mpu ^ mask;
+		mask = 0;	/* don't invert the value from now */
+		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			return;
+		}
+		if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
+			goto __timer;
+		if (output_ready (midi)) {
+			if (mpu != midi->output_mpu) {
+				write_data(midi, mpu == internal_mpu ?
+							WF_INTERNAL_SWITCH :
+							WF_EXTERNAL_SWITCH);
+				midi->output_mpu = mpu;
+			} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {
+				if (!midi->isvirtual ||
+					(midi_byte != WF_INTERNAL_SWITCH &&
+					 midi_byte != WF_EXTERNAL_SWITCH))
+					write_data(midi, midi_byte);
+				max--;
+			} else {
+			      __timer:
+				if (midi->istimer) {
+					if (--midi->istimer <= 0)
+						del_timer(&midi->timer);
+				}
+				midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				return;
+			}
+		} else {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			return;
+		}
+		spin_unlock_irqrestore (&midi->virtual, flags);
+	}
+}
+
+static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] |= MPU401_MODE_INPUT;
+	midi->substream_input[mpu] = substream;
+	spin_unlock_irqrestore (&midi->open, flags);
+
+	return 0;
+}
+
+static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] |= MPU401_MODE_OUTPUT;
+	midi->substream_output[mpu] = substream;
+	spin_unlock_irqrestore (&midi->open, flags);
+
+	return 0;
+}
+
+static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] &= ~MPU401_MODE_INPUT;
+	spin_unlock_irqrestore (&midi->open, flags);
+
+	return 0;
+}
+
+static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
+	spin_unlock_irqrestore (&midi->open, flags);
+	return 0;
+}
+
+static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	if (substream == NULL || substream->rmidi == NULL) 
+	        return;
+
+	if (substream->rmidi->private_data == NULL)
+	        return;
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL) {
+		return;
+	}
+
+	spin_lock_irqsave (&midi->virtual, flags);
+	if (up) {
+		midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
+	} else {
+		midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
+	}
+	spin_unlock_irqrestore (&midi->virtual, flags);
+}
+
+static void snd_wavefront_midi_output_timer(unsigned long data)
+{
+	snd_wavefront_card_t *card = (snd_wavefront_card_t *)data;
+	snd_wavefront_midi_t *midi = &card->wavefront.midi;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&midi->virtual, flags);
+	midi->timer.expires = 1 + jiffies;
+	add_timer(&midi->timer);
+	spin_unlock_irqrestore (&midi->virtual, flags);
+	snd_wavefront_midi_output_write(card);
+}
+
+static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	if (substream == NULL || substream->rmidi == NULL) 
+	        return;
+
+	if (substream->rmidi->private_data == NULL)
+	        return;
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL) {
+		return;
+	}
+
+	spin_lock_irqsave (&midi->virtual, flags);
+	if (up) {
+		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
+			if (!midi->istimer) {
+				midi->timer.function = snd_wavefront_midi_output_timer;
+				midi->timer.data = (unsigned long) substream->rmidi->card->private_data;
+				midi->timer.expires = 1 + jiffies;
+				add_timer(&midi->timer);
+			}
+			midi->istimer++;
+			midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
+		}
+	} else {
+		midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
+	}
+	spin_unlock_irqrestore (&midi->virtual, flags);
+
+	if (up)
+		snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
+}
+
+void
+snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
+
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	static snd_rawmidi_substream_t *substream = NULL;
+	static int mpu = external_mpu; 
+	int max = 128;
+	unsigned char byte;
+
+	midi = &card->wavefront.midi;
+
+	if (!input_avail (midi)) { /* not for us */
+		snd_wavefront_midi_output_write(card);
+		return;
+	}
+
+	while (--max) {
+		spin_lock_irqsave (&midi->virtual, flags);
+
+		if (input_avail (midi)) {
+			byte = read_data (midi);
+
+			if (midi->isvirtual) {				
+				if (byte == WF_EXTERNAL_SWITCH) {
+					substream = midi->substream_input[external_mpu];
+					mpu = external_mpu;
+				} else if (byte == WF_INTERNAL_SWITCH) { 
+					substream = midi->substream_output[internal_mpu];
+					mpu = internal_mpu;
+				} /* else just leave it as it is */
+			} else {
+				substream = midi->substream_input[internal_mpu];
+				mpu = internal_mpu;
+			}
+
+			if (substream == NULL) {
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				continue;
+			}
+
+			if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				snd_rawmidi_receive(substream, &byte, 1);
+			} else {
+				spin_unlock_irqrestore (&midi->virtual, flags);
+			}
+		} else {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			break;
+		}
+	} 
+
+	snd_wavefront_midi_output_write(card);
+}
+
+void
+snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)
+
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
+	card->wavefront.midi.isvirtual = 1;
+	card->wavefront.midi.output_mpu = internal_mpu;
+	card->wavefront.midi.input_mpu = internal_mpu;
+	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
+}
+
+void
+snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
+
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
+	// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
+	// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
+	card->wavefront.midi.isvirtual = 0;
+	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
+}
+
+int
+snd_wavefront_midi_start (snd_wavefront_card_t *card)
+
+{
+	int ok, i;
+	unsigned char rbuf[4], wbuf[4];
+	snd_wavefront_t *dev;
+	snd_wavefront_midi_t *midi;
+
+	dev = &card->wavefront;
+	midi = &dev->midi;
+
+	/* The ICS2115 MPU-401 interface doesn't do anything
+	   until its set into UART mode.
+	*/
+
+	/* XXX fix me - no hard timing loops allowed! */
+
+	for (i = 0; i < 30000 && !output_ready (midi); i++);
+
+	if (!output_ready (midi)) {
+		snd_printk ("MIDI interface not ready for command\n");
+		return -1;
+	}
+
+	/* Any interrupts received from now on
+	   are owned by the MIDI side of things.
+	*/
+
+	dev->interrupts_are_midi = 1;
+	
+	outb (UART_MODE_ON, midi->mpu_command_port);
+
+	for (ok = 0, i = 50000; i > 0 && !ok; i--) {
+		if (input_avail (midi)) {
+			if (read_data (midi) == MPU_ACK) {
+				ok = 1;
+				break;
+			}
+		}
+	}
+
+	if (!ok) {
+		snd_printk ("cannot set UART mode for MIDI interface");
+		dev->interrupts_are_midi = 0;
+		return -1;
+	}
+
+	/* Route external MIDI to WaveFront synth (by default) */
+    
+	if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
+		snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
+		/* XXX error ? */
+	}
+
+	/* Turn on Virtual MIDI, but first *always* turn it off,
+	   since otherwise consectutive reloads of the driver will
+	   never cause the hardware to generate the initial "internal" or 
+	   "external" source bytes in the MIDI data stream. This
+	   is pretty important, since the internal hardware generally will
+	   be used to generate none or very little MIDI output, and
+	   thus the only source of MIDI data is actually external. Without
+	   the switch bytes, the driver will think it all comes from
+	   the internal interface. Duh.
+	*/
+
+	if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { 
+		snd_printk ("virtual MIDI mode not disabled\n");
+		return 0; /* We're OK, but missing the external MIDI dev */
+	}
+
+	snd_wavefront_midi_enable_virtual (card);
+
+	if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
+		snd_printk ("cannot enable virtual MIDI mode.\n");
+		snd_wavefront_midi_disable_virtual (card);
+	} 
+	return 0;
+}
+
+snd_rawmidi_ops_t snd_wavefront_midi_output =
+{
+	open:		snd_wavefront_midi_output_open,
+	close:		snd_wavefront_midi_output_close,
+	trigger:	snd_wavefront_midi_output_trigger,
+};
+
+snd_rawmidi_ops_t snd_wavefront_midi_input =
+{
+	open:		snd_wavefront_midi_input_open,
+	close:		snd_wavefront_midi_input_close,
+	trigger:	snd_wavefront_midi_input_trigger,
+};
+
diff -Nru linux/sound/isa/wavefront/wavefront_synth.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_synth.c
--- linux/sound/isa/wavefront/wavefront_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_synth.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2244 @@
+/* Copyright (C) by Paul Barton-Davis 1998-1999
+ *
+ * Some portions of this file are taken from work that is
+ * copyright (C) by Hannu Savolainen 1993-1996
+ *
+ * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.  
+ */
+
+/*  
+ * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth
+ *                                             (Maui, Tropez, Tropez Plus)
+ *
+ * This driver supports the onboard wavetable synthesizer (an ICS2115),
+ * including patch, sample and program loading and unloading, conversion
+ * of GUS patches during loading, and full user-level access to all
+ * WaveFront commands. It tries to provide semi-intelligent patch and
+ * sample management as well.
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/snd_wavefront.h>
+#include <sound/initval.h>
+
+int wf_raw = 0; /* we normally check for "raw state" to firmware
+		   loading. if non-zero, then during driver loading, the
+		   state of the board is ignored, and we reset the
+		   board and load the firmware anyway.
+		*/
+		   
+int fx_raw = 1; /* if this is zero, we'll leave the FX processor in
+		   whatever state it is when the driver is loaded.
+		   The default is to download the microprogram and
+		   associated coefficients to set it up for "default"
+		   operation, whatever that means.
+		*/
+
+int debug_default = 0;  /* you can set this to control debugging
+			      during driver loading. it takes any combination
+			      of the WF_DEBUG_* flags defined in
+			      wavefront.h
+			   */
+
+/* XXX this needs to be made firmware and hardware version dependent */
+
+char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed
+					     version of the WaveFront OS
+					  */
+
+int wait_usecs = 150; /* This magic number seems to give pretty optimal
+			 throughput based on my limited experimentation.
+			 If you want to play around with it and find a better
+			 value, be my guest. Remember, the idea is to
+			 get a number that causes us to just busy wait
+			 for as many WaveFront commands as possible, without
+			 coming up with a number so large that we hog the
+			 whole CPU.
+
+			 Specifically, with this number, out of about 134,000
+			 status waits, only about 250 result in a sleep.
+		      */
+
+int sleep_interval = 100;   /* HZ/sleep_interval seconds per sleep */
+int sleep_tries = 50;       /* number of times we'll try to sleep */
+
+int reset_time = 2;        /* hundreths of a second we wait after a HW
+			      reset for the expected interrupt.
+			   */
+
+int ramcheck_time = 20;    /* time in seconds to wait while ROM code
+			      checks on-board RAM.
+			   */
+
+int osrun_time = 10;       /* time in seconds we wait for the OS to
+			      start running.
+			   */
+#if 0
+MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION("ALSA driver for Turtle Beach WaveFront ICS2215 Synth");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+#endif
+MODULE_PARM(wf_raw,"i");
+MODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS");
+MODULE_PARM(fx_raw,"i");
+MODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help");
+MODULE_PARM(debug_default,"i");
+MODULE_PARM_DESC(debug_default, "debug parameters for card initialization");
+MODULE_PARM(wait_usecs,"i");
+MODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs");
+MODULE_PARM(sleep_interval,"i");
+MODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply");
+MODULE_PARM(sleep_tries,"i");
+MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait");
+MODULE_PARM(ospath,"s");
+MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware");
+MODULE_PARM(reset_time,"i");
+MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect");
+MODULE_PARM(ramcheck_time,"i");
+MODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test");
+MODULE_PARM(osrun_time,"i");
+MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS");
+
+/* if WF_DEBUG not defined, no run-time debugging messages will
+   be available via the debug flag setting. Given the current
+   beta state of the driver, this will remain set until a future 
+   version.
+*/
+
+#define WF_DEBUG 1
+
+#ifdef WF_DEBUG
+
+#ifdef NEW_MACRO_VARARGS
+#define DPRINT(cond, ...) \
+       if ((dev->debug & (cond)) == (cond)) { \
+	     snd_printk (__VA_ARGS__); \
+       }
+#else
+#define DPRINT(cond, args...) \
+       if ((dev->debug & (cond)) == (cond)) { \
+	     snd_printk (##args); \
+       }
+#endif
+#else
+#define DPRINT(cond, args...)
+#endif /* WF_DEBUG */
+
+#define LOGNAME "WaveFront: "
+
+/* bitmasks for WaveFront status port value */
+
+#define STAT_RINTR_ENABLED	0x01
+#define STAT_CAN_READ		0x02
+#define STAT_INTR_READ		0x04
+#define STAT_WINTR_ENABLED	0x10
+#define STAT_CAN_WRITE		0x20
+#define STAT_INTR_WRITE		0x40
+
+static int wavefront_delete_sample (snd_wavefront_t *, int sampnum);
+static int wavefront_find_free_sample (snd_wavefront_t *);
+
+typedef struct {
+	int cmd;
+	char *action;
+	unsigned int read_cnt;
+	unsigned int write_cnt;
+	int need_ack;
+} wavefront_command;
+
+static struct {
+	int errno;
+	const char *errstr;
+} wavefront_errors[] = {
+	{ 0x01, "Bad sample number" },
+	{ 0x02, "Out of sample memory" },
+	{ 0x03, "Bad patch number" },
+	{ 0x04, "Error in number of voices" },
+	{ 0x06, "Sample load already in progress" },
+	{ 0x0B, "No sample load request pending" },
+	{ 0x0E, "Bad MIDI channel number" },
+	{ 0x10, "Download Record Error" },
+	{ 0x80, "Success" },
+	{ 0x0, 0x0 }
+};
+
+#define NEEDS_ACK 1
+
+static wavefront_command wavefront_commands[] = {
+	{ WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK },
+	{ WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0},
+	{ WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK },
+	{ WFC_GET_NVOICES, "get number of voices", 1, 0, 0 },
+	{ WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK },
+	{ WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 },
+	{ WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK },
+	{ WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK },
+	{ WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 },
+	{ WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK },
+	{ WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK },
+	{ WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK },
+	{ WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK },
+	{ WFC_MIDI_STATUS, "report midi status", 1, 0, 0 },
+	{ WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 },
+	{ WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 },
+	{ WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 },
+	{ WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 },
+	{ WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 },
+	{ WFC_DOWNLOAD_SAMPLE, "download sample",
+	  0, WF_SAMPLE_BYTES, NEEDS_ACK },
+	{ WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK},
+	{ WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header",
+	  0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK },
+	{ WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 },
+
+	/* This command requires a variable number of bytes to be written.
+	   There is a hack in snd_wavefront_cmd() to support this. The actual
+	   count is passed in as the read buffer ptr, cast appropriately.
+	   Ugh.
+	*/
+
+	{ WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK },
+
+	/* This one is a hack as well. We just read the first byte of the
+	   response, don't fetch an ACK, and leave the rest to the 
+	   calling function. Ugly, ugly, ugly.
+	*/
+
+	{ WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 },
+	{ WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias",
+	  0, WF_ALIAS_BYTES, NEEDS_ACK },
+	{ WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0},
+	{ WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK },
+	{ WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 },
+	{ WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" },
+	{ WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 },
+	{ WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK },
+	{ WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 },
+	{ WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK },
+	{ WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 },
+	{ WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9,
+	  NEEDS_ACK},
+	{ WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0},
+	{ WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel",
+	  0, 1, NEEDS_ACK },
+	{ WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK },
+	{ WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers",
+	  32, 0, 0 },
+	{ WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK },
+	{ 0x00 }
+};
+
+static inline void
+dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+static const char *
+wavefront_errorstr (int errnum)
+
+{
+	int i;
+
+	for (i = 0; wavefront_errors[i].errstr; i++) {
+		if (wavefront_errors[i].errno == errnum) {
+			return wavefront_errors[i].errstr;
+		}
+	}
+
+	return "Unknown WaveFront error";
+}
+
+static wavefront_command *
+wavefront_get_command (int cmd) 
+
+{
+	int i;
+
+	for (i = 0; wavefront_commands[i].cmd != 0; i++) {
+		if (cmd == wavefront_commands[i].cmd) {
+			return &wavefront_commands[i];
+		}
+	}
+
+	return (wavefront_command *) 0;
+}
+
+static inline int
+wavefront_status (snd_wavefront_t *dev) 
+
+{
+	return inb (dev->status_port);
+}
+
+static int
+wavefront_sleep (int limit)
+
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(limit);
+
+	return signal_pending(current);
+}
+
+static int
+wavefront_wait (snd_wavefront_t *dev, int mask)
+
+{
+	int             i;
+
+	/* Spin for a short period of time, because >99% of all
+	   requests to the WaveFront can be serviced inline like this.
+	*/
+
+	for (i = 0; i < wait_usecs; i += 5) {
+		if (wavefront_status (dev) & mask) {
+			return 1;
+		}
+		udelay(5);
+	}
+
+	for (i = 0; i < sleep_tries; i++) {
+
+		if (wavefront_status (dev) & mask) {
+			return 1;
+		}
+
+		if (wavefront_sleep (HZ/sleep_interval)) {
+			return (0);
+		}
+	}
+
+	return (0);
+}
+
+static int
+wavefront_read (snd_wavefront_t *dev)
+
+{
+	if (wavefront_wait (dev, STAT_CAN_READ))
+		return inb (dev->data_port);
+
+	DPRINT (WF_DEBUG_DATA, "read timeout.\n");
+
+	return -1;
+}
+
+static int
+wavefront_write (snd_wavefront_t *dev, unsigned char data)
+
+{
+	if (wavefront_wait (dev, STAT_CAN_WRITE)) {
+		outb (data, dev->data_port);
+		return 0;
+	}
+
+	DPRINT (WF_DEBUG_DATA, "write timeout.\n");
+
+	return -1;
+}
+
+int
+snd_wavefront_cmd (snd_wavefront_t *dev, 
+		   int cmd, unsigned char *rbuf, unsigned char *wbuf)
+
+{
+	int ack;
+	unsigned int i;
+	int c;
+	wavefront_command *wfcmd;
+
+	if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) {
+		snd_printk ("command 0x%x not supported.\n",
+			cmd);
+		return 1;
+	}
+
+	/* Hack to handle the one variable-size write command. See
+	   wavefront_send_multisample() for the other half of this
+	   gross and ugly strategy.
+	*/
+
+	if (cmd == WFC_DOWNLOAD_MULTISAMPLE) {
+		wfcmd->write_cnt = (unsigned long) rbuf;
+		rbuf = 0;
+	}
+
+	DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n",
+			       cmd, wfcmd->action, wfcmd->read_cnt,
+			       wfcmd->write_cnt, wfcmd->need_ack);
+    
+	if (wavefront_write (dev, cmd)) { 
+		DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request "
+						     "0x%x [%s].\n",
+						     cmd, wfcmd->action);
+		return 1;
+	} 
+
+	if (wfcmd->write_cnt > 0) {
+		DPRINT (WF_DEBUG_DATA, "writing %d bytes "
+					"for 0x%x\n",
+					wfcmd->write_cnt, cmd);
+
+		for (i = 0; i < wfcmd->write_cnt; i++) {
+			if (wavefront_write (dev, wbuf[i])) {
+				DPRINT (WF_DEBUG_IO, "bad write for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
+				return 1;
+			}
+
+			DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n",
+						i, wbuf[i]);
+		}
+	}
+
+	if (wfcmd->read_cnt > 0) {
+		DPRINT (WF_DEBUG_DATA, "reading %d ints "
+					"for 0x%x\n",
+					wfcmd->read_cnt, cmd);
+
+		for (i = 0; i < wfcmd->read_cnt; i++) {
+
+			if ((c = wavefront_read (dev)) == -1) {
+				DPRINT (WF_DEBUG_IO, "bad read for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
+				return 1;
+			}
+
+			/* Now handle errors. Lots of special cases here */
+	    
+			if (c == 0xff) { 
+				if ((c = wavefront_read (dev)) == -1) {
+					DPRINT (WF_DEBUG_IO, "bad read for "
+							      "error byte at "
+							      "read byte %d "
+							      "of 0x%x [%s].\n",
+							      i, cmd,
+							      wfcmd->action);
+					return 1;
+				}
+
+				/* Can you believe this madness ? */
+
+				if (c == 1 &&
+				    wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) {
+					rbuf[0] = WF_ST_EMPTY;
+					return (0);
+
+				} else if (c == 3 &&
+					   wfcmd->cmd == WFC_UPLOAD_PATCH) {
+
+					return 3;
+
+				} else if (c == 1 &&
+					   wfcmd->cmd == WFC_UPLOAD_PROGRAM) {
+
+					return 1;
+
+				} else {
+
+					DPRINT (WF_DEBUG_IO, "error %d (%s) "
+							      "during "
+							      "read for byte "
+							      "%d of 0x%x "
+							      "[%s].\n",
+							      c,
+							      wavefront_errorstr (c),
+							      i, cmd,
+							      wfcmd->action);
+					return 1;
+
+				}
+		
+		} else {
+				rbuf[i] = c;
+			}
+			
+			DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]);
+		}
+	}
+	
+	if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) {
+
+		DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd);
+
+		/* Some commands need an ACK, but return zero instead
+		   of the standard value.
+		*/
+	    
+		if ((ack = wavefront_read (dev)) == 0) {
+			ack = WF_ACK;
+		}
+	
+		if (ack != WF_ACK) {
+			if (ack == -1) {
+				DPRINT (WF_DEBUG_IO, "cannot read ack for "
+						      "0x%x [%s].\n",
+						      cmd, wfcmd->action);
+				return 1;
+		
+			} else {
+				int err = -1; /* something unknown */
+
+				if (ack == 0xff) { /* explicit error */
+		    
+					if ((err = wavefront_read (dev)) == -1) {
+						DPRINT (WF_DEBUG_DATA,
+							"cannot read err "
+							"for 0x%x [%s].\n",
+							cmd, wfcmd->action);
+					}
+				}
+				
+				DPRINT (WF_DEBUG_IO, "0x%x [%s] "
+					"failed (0x%x, 0x%x, %s)\n",
+					cmd, wfcmd->action, ack, err,
+					wavefront_errorstr (err));
+				
+				return -err;
+			}
+		}
+		
+		DPRINT (WF_DEBUG_DATA, "ack received "
+					"for 0x%x [%s]\n",
+					cmd, wfcmd->action);
+	} else {
+
+		DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need "
+				       "ACK (%d,%d,%d)\n",
+				       cmd, wfcmd->action, wfcmd->read_cnt,
+				       wfcmd->write_cnt, wfcmd->need_ack);
+	}
+
+	return 0;
+	
+}
+
+/***********************************************************************
+WaveFront data munging   
+
+Things here are wierd. All data written to the board cannot 
+have its most significant bit set. Any data item with values 
+potentially > 0x7F (127) must be split across multiple bytes.
+
+Sometimes, we need to munge numeric values that are represented on
+the x86 side as 8-32 bit values. Sometimes, we need to munge data
+that is represented on the x86 side as an array of bytes. The most
+efficient approach to handling both cases seems to be to use 2
+different functions for munging and 2 for de-munging. This avoids
+wierd casting and worrying about bit-level offsets.
+
+**********************************************************************/
+
+static unsigned char *
+munge_int32 (unsigned int src,
+	     unsigned char *dst,
+	     unsigned int dst_size)
+{
+	unsigned int i;
+
+	for (i = 0; i < dst_size; i++) {
+		*dst = src & 0x7F;  /* Mask high bit of LSB */
+		src = src >> 7;     /* Rotate Right 7 bits  */
+	                            /* Note: we leave the upper bits in place */ 
+
+		dst++;
+ 	};
+	return dst;
+};
+
+static int 
+demunge_int32 (unsigned char* src, int src_size)
+
+{
+	int i;
+ 	int outval = 0;
+	
+ 	for (i = src_size - 1; i >= 0; i--) {
+		outval=(outval<<7)+src[i];
+	}
+
+	return outval;
+};
+
+static 
+unsigned char *
+munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size)
+
+{
+	unsigned int i;
+	unsigned int last = dst_size / 2;
+
+	for (i = 0; i < last; i++) {
+		*dst++ = src[i] & 0x7f;
+		*dst++ = src[i] >> 7;
+	}
+	return dst;
+}
+
+static 
+unsigned char *
+demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes)
+
+{
+	int i;
+	unsigned char *end = src + src_bytes;
+    
+	end = src + src_bytes;
+
+	/* NOTE: src and dst *CAN* point to the same address */
+
+	for (i = 0; src != end; i++) {
+		dst[i] = *src++;
+		dst[i] |= (*src++)<<7;
+	}
+
+	return dst;
+}
+
+/***********************************************************************
+WaveFront: sample, patch and program management.
+***********************************************************************/
+
+static int
+wavefront_delete_sample (snd_wavefront_t *dev, int sample_num)
+
+{
+	unsigned char wbuf[2];
+	int x;
+
+	wbuf[0] = sample_num & 0x7f;
+	wbuf[1] = sample_num >> 7;
+
+	if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) {
+		dev->sample_status[sample_num] = WF_ST_EMPTY;
+	}
+
+	return x;
+}
+
+static int
+wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
+
+{
+	int i;
+	unsigned char rbuf[32], wbuf[32];
+	unsigned int    sc_real, sc_alias, sc_multi;
+
+	/* check sample status */
+    
+	if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) {
+		snd_printk ("cannot request sample count.\n");
+		return -1;
+	} 
+    
+	sc_real = sc_alias = sc_multi = dev->samples_used = 0;
+    
+	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+	
+		wbuf[0] = i & 0x7f;
+		wbuf[1] = i >> 7;
+
+		if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
+			snd_printk("cannot identify sample "
+				   "type of slot %d\n", i);
+			dev->sample_status[i] = WF_ST_EMPTY;
+			continue;
+		}
+
+		dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]);
+
+		if (assume_rom) {
+			dev->sample_status[i] |= WF_SLOT_ROM;
+		}
+
+		switch (rbuf[0] & WF_ST_MASK) {
+		case WF_ST_SAMPLE:
+			sc_real++;
+			break;
+		case WF_ST_MULTISAMPLE:
+			sc_multi++;
+			break;
+		case WF_ST_ALIAS:
+			sc_alias++;
+			break;
+		case WF_ST_EMPTY:
+			break;
+
+		default:
+			snd_printk ("unknown sample type for "
+				    "slot %d (0x%x)\n", 
+				    i, rbuf[0]);
+		}
+
+		if (rbuf[0] != WF_ST_EMPTY) {
+			dev->samples_used++;
+		} 
+	}
+
+	snd_printk ("%d samples used (%d real, %d aliases, %d multi), "
+		    "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi,
+		    WF_MAX_SAMPLE - dev->samples_used);
+
+
+	return (0);
+
+}
+
+static int
+wavefront_get_patch_status (snd_wavefront_t *dev)
+
+{
+	unsigned char patchbuf[WF_PATCH_BYTES];
+	unsigned char patchnum[2];
+	wavefront_patch *p;
+	int i, x, cnt, cnt2;
+
+	for (i = 0; i < WF_MAX_PATCH; i++) {
+		patchnum[0] = i & 0x7f;
+		patchnum[1] = i >> 7;
+
+		if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf,
+					patchnum)) == 0) {
+
+			dev->patch_status[i] |= WF_SLOT_FILLED;
+			p = (wavefront_patch *) patchbuf;
+			dev->sample_status
+				[p->sample_number|(p->sample_msb<<7)] |=
+				WF_SLOT_USED;
+	    
+		} else if (x == 3) { /* Bad patch number */
+			dev->patch_status[i] = 0;
+		} else {
+			snd_printk ("upload patch "
+				    "error 0x%x\n", x);
+			dev->patch_status[i] = 0;
+			return 1;
+		}
+	}
+
+	/* program status has already filled in slot_used bits */
+
+	for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) {
+		if (dev->patch_status[i] & WF_SLOT_FILLED) {
+			cnt++;
+		}
+		if (dev->patch_status[i] & WF_SLOT_USED) {
+			cnt2++;
+		}
+	
+	}
+	snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2);
+
+	return (0);
+}
+
+static int
+wavefront_get_program_status (snd_wavefront_t *dev)
+
+{
+	unsigned char progbuf[WF_PROGRAM_BYTES];
+	wavefront_program prog;
+	unsigned char prognum;
+	int i, x, l, cnt;
+
+	for (i = 0; i < WF_MAX_PROGRAM; i++) {
+		prognum = i;
+
+		if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf,
+					&prognum)) == 0) {
+
+			dev->prog_status[i] |= WF_SLOT_USED;
+
+			demunge_buf (progbuf, (unsigned char *) &prog,
+				     WF_PROGRAM_BYTES);
+
+			for (l = 0; l < WF_NUM_LAYERS; l++) {
+				if (prog.layer[l].mute) {
+					dev->patch_status
+						[prog.layer[l].patch_number] |=
+						WF_SLOT_USED;
+				}
+			}
+		} else if (x == 1) { /* Bad program number */
+			dev->prog_status[i] = 0;
+		} else {
+			snd_printk ("upload program "
+				    "error 0x%x\n", x);
+			dev->prog_status[i] = 0;
+		}
+	}
+
+	for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) {
+		if (dev->prog_status[i]) {
+			cnt++;
+		}
+	}
+
+	snd_printk ("%d programs slots in use\n", cnt);
+
+	return (0);
+}
+
+static int
+wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char buf[WF_PATCH_BYTES+2];
+	unsigned char *bptr;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
+				      header->number);
+
+	dev->patch_status[header->number] |= WF_SLOT_FILLED;
+
+	bptr = buf;
+	bptr = munge_int32 (header->number, buf, 2);
+	munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
+    
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, 0, buf)) {
+		snd_printk ("download patch failed\n");
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int
+wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char buf[WF_PROGRAM_BYTES+1];
+	int i;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
+		header->number);
+
+	dev->prog_status[header->number] = WF_SLOT_USED;
+
+	/* XXX need to zero existing SLOT_USED bit for program_status[i]
+	   where `i' is the program that's being (potentially) overwritten.
+	*/
+    
+	for (i = 0; i < WF_NUM_LAYERS; i++) {
+		if (header->hdr.pr.layer[i].mute) {
+			dev->patch_status[header->hdr.pr.layer[i].patch_number] |=
+				WF_SLOT_USED;
+
+			/* XXX need to mark SLOT_USED for sample used by
+			   patch_number, but this means we have to load it. Ick.
+			*/
+		}
+	}
+
+	buf[0] = header->number;
+	munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
+    
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, 0, buf)) {
+		snd_printk ("download patch failed\n");	
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int
+wavefront_freemem (snd_wavefront_t *dev)
+
+{
+	char rbuf[8];
+
+	if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, 0)) {
+		snd_printk ("can't get memory stats.\n");
+		return -1;
+	} else {
+		return demunge_int32 (rbuf, 4);
+	}
+}
+
+static int
+wavefront_send_sample (snd_wavefront_t *dev, 
+		       wavefront_patch_info *header,
+		       u16 *dataptr,
+		       int data_is_unsigned)
+
+{
+	/* samples are downloaded via a 16-bit wide i/o port
+	   (you could think of it as 2 adjacent 8-bit wide ports
+	   but its less efficient that way). therefore, all
+	   the blocksizes and so forth listed in the documentation,
+	   and used conventionally to refer to sample sizes,
+	   which are given in 8-bit units (bytes), need to be
+	   divided by 2.
+        */
+
+	u16 sample_short;
+	u32 length;
+	u16 *data_end = 0;
+	unsigned int i;
+	const int max_blksize = 4096/2;
+	unsigned int written;
+	unsigned int blocksize;
+	int dma_ack;
+	int blocknum;
+	unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES];
+	unsigned char *shptr;
+	int skip = 0;
+	int initial_skip = 0;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, "
+				      "type %d, %d bytes from 0x%lx\n",
+				      header->size ? "" : "header ", 
+				      header->number, header->subkey,
+				      header->size,
+				      (unsigned long) header->dataptr);
+
+	if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
+		int x;
+
+		if ((x = wavefront_find_free_sample (dev)) < 0) {
+			return -ENOMEM;
+		}
+		snd_printk ("unspecified sample => %d\n", x);
+		header->number = x;
+	}
+
+	if (header->size) {
+
+		/* XXX its a debatable point whether or not RDONLY semantics
+		   on the ROM samples should cover just the sample data or
+		   the sample header. For now, it only covers the sample data,
+		   so anyone is free at all times to rewrite sample headers.
+
+		   My reason for this is that we have the sample headers
+		   available in the WFB file for General MIDI, and so these
+		   can always be reset if needed. The sample data, however,
+		   cannot be recovered without a complete reset and firmware
+		   reload of the ICS2115, which is a very expensive operation.
+
+		   So, doing things this way allows us to honor the notion of
+		   "RESETSAMPLES" reasonably cheaply. Note however, that this
+		   is done purely at user level: there is no WFB parser in
+		   this driver, and so a complete reset (back to General MIDI,
+		   or theoretically some other configuration) is the
+		   responsibility of the user level library. 
+
+		   To try to do this in the kernel would be a little
+		   crazy: we'd need 158K of kernel space just to hold
+		   a copy of the patch/program/sample header data.
+		*/
+
+		if (dev->rom_samples_rdonly) {
+			if (dev->sample_status[header->number] & WF_SLOT_ROM) {
+				snd_printk ("sample slot %d "
+					    "write protected\n",
+					    header->number);
+				return -EACCES;
+			}
+		}
+
+		wavefront_delete_sample (dev, header->number);
+	}
+
+	if (header->size) {
+		dev->freemem = wavefront_freemem (dev);
+
+		if (dev->freemem < header->size) {
+			snd_printk ("insufficient memory to "
+				    "load %d byte sample.\n",
+				    header->size);
+			return -ENOMEM;
+		}
+	
+	}
+
+	skip = WF_GET_CHANNEL(&header->hdr.s);
+
+	if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
+		snd_printk ("channel selection only "
+			    "possible on 16-bit samples");
+		return -(EINVAL);
+	}
+
+	switch (skip) {
+	case 0:
+		initial_skip = 0;
+		skip = 1;
+		break;
+	case 1:
+		initial_skip = 0;
+		skip = 2;
+		break;
+	case 2:
+		initial_skip = 1;
+		skip = 2;
+		break;
+	case 3:
+		initial_skip = 2;
+		skip = 3;
+		break;
+	case 4:
+		initial_skip = 3;
+		skip = 4;
+		break;
+	case 5:
+		initial_skip = 4;
+		skip = 5;
+		break;
+	case 6:
+		initial_skip = 5;
+		skip = 6;
+		break;
+	}
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => "
+				      "initial skip = %d, skip = %d\n",
+				      WF_GET_CHANNEL (&header->hdr.s),
+				      initial_skip, skip);
+    
+	/* Be safe, and zero the "Unused" bits ... */
+
+	WF_SET_CHANNEL(&header->hdr.s, 0);
+
+	/* adjust size for 16 bit samples by dividing by two.  We always
+	   send 16 bits per write, even for 8 bit samples, so the length
+	   is always half the size of the sample data in bytes.
+	*/
+
+	length = header->size / 2;
+
+	/* the data we're sent has not been munged, and in fact, the
+	   header we have to send isn't just a munged copy either.
+	   so, build the sample header right here.
+	*/
+
+	shptr = &sample_hdr[0];
+
+	shptr = munge_int32 (header->number, shptr, 2);
+
+	if (header->size) {
+		shptr = munge_int32 (length, shptr, 4);
+	}
+
+	/* Yes, a 4 byte result doesn't contain all of the offset bits,
+	   but the offset only uses 24 bits.
+	*/
+
+	shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleStartOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((u32 *) &header->hdr.s.loopStartOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((u32 *) &header->hdr.s.loopEndOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleEndOffset),
+			     shptr, 4);
+	
+	/* This one is truly wierd. What kind of wierdo decided that in
+	   a system dominated by 16 and 32 bit integers, they would use
+	   a just 12 bits ?
+	*/
+	
+	shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3);
+	
+	/* Why is this nybblified, when the MSB is *always* zero ? 
+	   Anyway, we can't take address of bitfield, so make a
+	   good-faith guess at where it starts.
+	*/
+	
+	shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1),
+			     shptr, 2);
+
+	if (snd_wavefront_cmd (dev, 
+			   header->size ?
+			   WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
+			   0, sample_hdr)) {
+		snd_printk ("sample %sdownload refused.\n",
+			    header->size ? "" : "header ");
+		return -(EIO);
+	}
+
+	if (header->size == 0) {
+		goto sent; /* Sorry. Just had to have one somewhere */
+	}
+    
+	data_end = dataptr + length;
+
+	/* Do any initial skip over an unused channel's data */
+
+	dataptr += initial_skip;
+    
+	for (written = 0, blocknum = 0;
+	     written < length; written += max_blksize, blocknum++) {
+	
+		if ((length - written) > max_blksize) {
+			blocksize = max_blksize;
+		} else {
+			/* round to nearest 16-byte value */
+			blocksize = ((length-written+7)&~0x7);
+		}
+
+		if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, 0, 0)) {
+			snd_printk ("download block "
+				    "request refused.\n");
+			return -(EIO);
+		}
+
+		for (i = 0; i < blocksize; i++) {
+
+			if (dataptr < data_end) {
+		
+				__get_user (sample_short, dataptr);
+				dataptr += skip;
+		
+				if (data_is_unsigned) { /* GUS ? */
+
+					if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) {
+			
+						/* 8 bit sample
+						 resolution, sign
+						 extend both bytes.
+						*/
+			
+						((unsigned char*)
+						 &sample_short)[0] += 0x7f;
+						((unsigned char*)
+						 &sample_short)[1] += 0x7f;
+			
+					} else {
+			
+						/* 16 bit sample
+						 resolution, sign
+						 extend the MSB.
+						*/
+			
+						sample_short += 0x7fff;
+					}
+				}
+
+			} else {
+
+				/* In padding section of final block:
+
+				   Don't fetch unsupplied data from
+				   user space, just continue with
+				   whatever the final value was.
+				*/
+			}
+	    
+			if (i < blocksize - 1) {
+				outw (sample_short, dev->block_port);
+			} else {
+				outw (sample_short, dev->last_block_port);
+			}
+		}
+
+		/* Get "DMA page acknowledge", even though its really
+		   nothing to do with DMA at all.
+		*/
+	
+		if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) {
+			if (dma_ack == -1) {
+				snd_printk ("upload sample "
+					    "DMA ack timeout\n");
+				return -(EIO);
+			} else {
+				snd_printk ("upload sample "
+					    "DMA ack error 0x%x\n",
+					    dma_ack);
+				return -(EIO);
+			}
+		}
+	}
+
+	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE);
+
+	/* Note, label is here because sending the sample header shouldn't
+	   alter the sample_status info at all.
+	*/
+
+ sent:
+	return (0);
+}
+
+static int
+wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char alias_hdr[WF_ALIAS_BYTES];
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is "
+				      "alias for %d\n",
+				      header->number,
+				      header->hdr.a.OriginalSample);
+    
+	munge_int32 (header->number, &alias_hdr[0], 2);
+	munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
+	munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
+		     &alias_hdr[4], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset),
+		     &alias_hdr[8], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset),
+		     &alias_hdr[12], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset),
+		     &alias_hdr[16], 4);
+	munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3);
+	munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
+
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) {
+		snd_printk ("download alias failed.\n");
+		return -(EIO);
+	}
+
+	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
+
+	return (0);
+}
+
+static int
+wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
+{
+	int i;
+	int num_samples;
+	unsigned char msample_hdr[WF_MSAMPLE_BYTES];
+
+	munge_int32 (header->number, &msample_hdr[0], 2);
+
+	/* You'll recall at this point that the "number of samples" value
+	   in a wavefront_multisample struct is actually the log2 of the
+	   real number of samples.
+	*/
+
+	num_samples = (1<<(header->hdr.ms.NumberOfSamples&7));
+	msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n",
+				      header->number,
+				      header->hdr.ms.NumberOfSamples,
+				      num_samples);
+
+	for (i = 0; i < num_samples; i++) {
+		DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n",
+		       i, header->hdr.ms.SampleNumber[i]);
+		munge_int32 (header->hdr.ms.SampleNumber[i],
+		     &msample_hdr[3+(i*2)], 2);
+	}
+    
+	/* Need a hack here to pass in the number of bytes
+	   to be written to the synth. This is ugly, and perhaps
+	   one day, I'll fix it.
+	*/
+
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE, 
+			   (unsigned char *) (long) ((num_samples*2)+3),
+			   msample_hdr)) {
+		snd_printk ("download of multisample failed.\n");
+		return -(EIO);
+	}
+
+	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
+
+	return (0);
+}
+
+static int
+wavefront_fetch_multisample (snd_wavefront_t *dev, 
+			     wavefront_patch_info *header)
+{
+	int i;
+	unsigned char log_ns[1];
+	unsigned char number[2];
+	int num_samples;
+
+	munge_int32 (header->number, number, 2);
+    
+	if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
+		snd_printk ("upload multisample failed.\n");
+		return -(EIO);
+	}
+    
+	DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
+				header->number, log_ns[0]);
+
+	header->hdr.ms.NumberOfSamples = log_ns[0];
+
+	/* get the number of samples ... */
+
+	num_samples = (1 << log_ns[0]);
+    
+	for (i = 0; i < num_samples; i++) {
+		char d[2];
+		int val;
+	
+		if ((val = wavefront_read (dev)) == -1) {
+			snd_printk ("upload multisample failed "
+				    "during sample loop.\n");
+			return -(EIO);
+		}
+		d[0] = val;
+
+		if ((val = wavefront_read (dev)) == -1) {
+			snd_printk ("upload multisample failed "
+				    "during sample loop.\n");
+			return -(EIO);
+		}
+		d[1] = val;
+	
+		header->hdr.ms.SampleNumber[i] =
+			demunge_int32 ((unsigned char *) d, 2);
+	
+		DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n",
+					i, header->hdr.ms.SampleNumber[i]);
+	}
+
+	return (0);
+}
+
+
+static int
+wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char drumbuf[WF_DRUM_BYTES];
+	wavefront_drum *drum = &header->hdr.d;
+	int i;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI "
+		"note %d, patch = %d\n", 
+		header->number, drum->PatchNumber);
+
+	drumbuf[0] = header->number & 0x7f;
+
+	for (i = 0; i < 4; i++) {
+		munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2);
+	}
+
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) {
+		snd_printk ("download drum failed.\n");
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int 
+wavefront_find_free_sample (snd_wavefront_t *dev)
+
+{
+	int i;
+
+	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+		if (!(dev->sample_status[i] & WF_SLOT_FILLED)) {
+			return i;
+		}
+	}
+	snd_printk ("no free sample slots!\n");
+	return -1;
+}
+
+#if 0
+static int 
+wavefront_find_free_patch (snd_wavefront_t *dev)
+
+{
+	int i;
+
+	for (i = 0; i < WF_MAX_PATCH; i++) {
+		if (!(dev->patch_status[i] & WF_SLOT_FILLED)) {
+			return i;
+		}
+	}
+	snd_printk ("no free patch slots!\n");
+	return -1;
+}
+#endif
+
+static int
+wavefront_load_patch (snd_wavefront_t *dev, const char *addr)
+
+{
+	wavefront_patch_info header;
+	
+	if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) -
+			    sizeof(wavefront_any))) {
+		snd_printk ("bad address for load patch.\n");
+		return -(EFAULT);
+	}
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download "
+				      "Sample type: %d "
+				      "Sample number: %d "
+				      "Sample size: %d\n",
+				      header.subkey,
+				      header.number,
+				      header.size);
+
+	switch (header.subkey) {
+	case WF_ST_SAMPLE:  /* sample or sample_header, based on patch->size */
+
+		if (copy_from_user ((unsigned char *) &header.hdr.s,
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_sample)))
+			return -EFAULT;
+
+		return wavefront_send_sample (dev, &header, header.dataptr, 0);
+
+	case WF_ST_MULTISAMPLE:
+
+		if (copy_from_user ((unsigned char *) &header.hdr.s,
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_multisample)))
+			return -EFAULT;
+
+		return wavefront_send_multisample (dev, &header);
+
+
+	case WF_ST_ALIAS:
+
+		if (copy_from_user ((unsigned char *) &header.hdr.a,
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_alias)))
+			return -EFAULT;
+
+		return wavefront_send_alias (dev, &header);
+
+	case WF_ST_DRUM:
+		if (copy_from_user ((unsigned char *) &header.hdr.d, 
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_drum)))
+			return -EFAULT;
+
+		return wavefront_send_drum (dev, &header);
+
+	case WF_ST_PATCH:
+		if (copy_from_user ((unsigned char *) &header.hdr.p, 
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_patch)))
+			return -EFAULT;
+
+		return wavefront_send_patch (dev, &header);
+
+	case WF_ST_PROGRAM:
+		if (copy_from_user ((unsigned char *) &header.hdr.pr, 
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_program)))
+			return -EFAULT;
+
+		return wavefront_send_program (dev, &header);
+
+	default:
+		snd_printk ("unknown patch type %d.\n",
+			    header.subkey);
+		return -(EINVAL);
+	}
+
+	return 0;
+}
+
+/***********************************************************************
+WaveFront: hardware-dependent interface
+***********************************************************************/
+
+static void
+process_sample_hdr (u8 *buf)
+
+{
+	wavefront_sample s;
+	u8 *ptr;
+
+	ptr = buf;
+
+	/* The board doesn't send us an exact copy of a "wavefront_sample"
+	   in response to an Upload Sample Header command. Instead, we 
+	   have to convert the data format back into our data structure,
+	   just as in the Download Sample command, where we have to do
+	   something very similar in the reverse direction.
+	*/
+
+	*((u32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((u32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((u32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((u32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((u32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3;
+
+	s.SampleResolution = *ptr & 0x3;
+	s.Loop = *ptr & 0x8;
+	s.Bidirectional = *ptr & 0x10;
+	s.Reverse = *ptr & 0x40;
+
+	/* Now copy it back to where it came from */
+
+	memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample));
+}
+
+static int
+wavefront_synth_control (snd_wavefront_card_t *acard, 
+			 wavefront_control *wc)
+
+{
+	snd_wavefront_t *dev = &acard->wavefront;
+	unsigned char patchnumbuf[2];
+	int i;
+
+	DPRINT (WF_DEBUG_CMD, "synth control with "
+		"cmd 0x%x\n", wc->cmd);
+
+	/* Pre-handling of or for various commands */
+
+	switch (wc->cmd) {
+		
+	case WFC_DISABLE_INTERRUPTS:
+		snd_printk ("interrupts disabled.\n");
+		outb (0x80|0x20, dev->control_port);
+		dev->interrupts_are_midi = 1;
+		return 0;
+
+	case WFC_ENABLE_INTERRUPTS:
+		snd_printk ("interrupts enabled.\n");
+		outb (0x80|0x40|0x20, dev->control_port);
+		dev->interrupts_are_midi = 1;
+		return 0;
+
+	case WFC_INTERRUPT_STATUS:
+		wc->rbuf[0] = dev->interrupts_are_midi;
+		return 0;
+
+	case WFC_ROMSAMPLES_RDONLY:
+		dev->rom_samples_rdonly = wc->wbuf[0];
+		wc->status = 0;
+		return 0;
+
+	case WFC_IDENTIFY_SLOT_TYPE:
+		i = wc->wbuf[0] | (wc->wbuf[1] << 7);
+		if (i <0 || i >= WF_MAX_SAMPLE) {
+			snd_printk ("invalid slot ID %d\n",
+				i);
+			wc->status = EINVAL;
+			return -EINVAL;
+		}
+		wc->rbuf[0] = dev->sample_status[i];
+		wc->status = 0;
+		return 0;
+
+	case WFC_DEBUG_DRIVER:
+		dev->debug = wc->wbuf[0];
+		snd_printk ("debug = 0x%x\n", dev->debug);
+		return 0;
+
+	case WFC_UPLOAD_PATCH:
+		munge_int32 (*((u32 *) wc->wbuf), patchnumbuf, 2);
+		memcpy (wc->wbuf, patchnumbuf, 2);
+		break;
+
+	case WFC_UPLOAD_MULTISAMPLE:
+		/* multisamples have to be handled differently, and
+		   cannot be dealt with properly by snd_wavefront_cmd() alone.
+		*/
+		wc->status = wavefront_fetch_multisample
+			(dev, (wavefront_patch_info *) wc->rbuf);
+		return 0;
+
+	case WFC_UPLOAD_SAMPLE_ALIAS:
+		snd_printk ("support for sample alias upload "
+			"being considered.\n");
+		wc->status = EINVAL;
+		return -EINVAL;
+	}
+
+	wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf);
+
+	/* Post-handling of certain commands.
+
+	   In particular, if the command was an upload, demunge the data
+	   so that the user-level doesn't have to think about it.
+	*/
+
+	if (wc->status == 0) {
+		switch (wc->cmd) {
+			/* intercept any freemem requests so that we know
+			   we are always current with the user-level view
+			   of things.
+			*/
+
+		case WFC_REPORT_FREE_MEMORY:
+			dev->freemem = demunge_int32 (wc->rbuf, 4);
+			break;
+
+		case WFC_UPLOAD_PATCH:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES);
+			break;
+
+		case WFC_UPLOAD_PROGRAM:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES);
+			break;
+
+		case WFC_UPLOAD_EDRUM_PROGRAM:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1);
+			break;
+
+		case WFC_UPLOAD_SAMPLE_HEADER:
+			process_sample_hdr (wc->rbuf);
+			break;
+
+		case WFC_UPLOAD_SAMPLE_ALIAS:
+			snd_printk ("support for "
+				    "sample aliases still "
+				    "being considered.\n");
+			break;
+
+		case WFC_VMIDI_OFF:
+			snd_wavefront_midi_disable_virtual (acard);
+			break;
+
+		case WFC_VMIDI_ON:
+			snd_wavefront_midi_enable_virtual (acard);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int 
+snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file)
+
+{
+	MOD_INC_USE_COUNT;
+	if (!try_inc_mod_count(hw->card->module)) {
+		MOD_DEC_USE_COUNT;
+		return -EFAULT;
+	}
+	file->private_data = hw;
+	return 0;
+}
+
+int 
+snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file)
+
+{
+	dec_mod_count(hw->card->module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int
+snd_wavefront_synth_ioctl (snd_hwdep_t *hw, struct file *file,
+			   unsigned int cmd, unsigned long arg)
+
+{
+	snd_card_t *card;
+	snd_wavefront_t *dev;
+	snd_wavefront_card_t *acard;
+	wavefront_control wc;
+
+	card = (snd_card_t *) hw->card;
+
+	snd_assert(card != NULL, return -ENODEV);
+
+	snd_assert(card->private_data != NULL, return -ENODEV);
+
+	acard = card->private_data;
+	dev = &acard->wavefront;
+	
+	switch (cmd) {
+	case WFCTL_LOAD_SPP:
+		if (wavefront_load_patch (dev, (char *) arg) != 0) {
+			return -EIO;
+		}
+		break;
+
+	case WFCTL_WFCMD:
+		if (copy_from_user (&wc, (void *) arg, sizeof (wc)))
+			return -EFAULT;
+		if (wavefront_synth_control (acard, &wc) < 0) {
+			return -EIO;
+		}
+		if (copy_to_user ((void *) arg, &wc, sizeof (wc)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/***********************************************************************/
+/*  WaveFront: interface for card-level wavefront module               */
+/***********************************************************************/
+
+void
+snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
+{
+	snd_wavefront_t *dev = &card->wavefront;
+
+	/*
+	   Some comments on interrupts. I attempted a version of this
+	   driver that used interrupts throughout the code instead of
+	   doing busy and/or sleep-waiting. Alas, it appears that once
+	   the Motorola firmware is downloaded, the card *never*
+	   generates an RX interrupt. These are successfully generated
+	   during firmware loading, and after that wavefront_status()
+	   reports that an interrupt is pending on the card from time
+	   to time, but it never seems to be delivered to this
+	   driver. Note also that wavefront_status() continues to
+	   report that RX interrupts are enabled, suggesting that I
+	   didn't goof up and disable them by mistake.
+
+	   Thus, I stepped back to a prior version of
+	   wavefront_wait(), the only place where this really
+	   matters. Its sad, but I've looked through the code to check
+	   on things, and I really feel certain that the Motorola
+	   firmware prevents RX-ready interrupts.
+	*/
+
+	if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) {
+		return;
+	}
+
+	dev->irq_ok = 1;
+	dev->irq_cnt++;
+	wake_up_interruptible (&dev->interrupt_sleeper);
+}
+
+/* STATUS REGISTER 
+
+0 Host Rx Interrupt Enable (1=Enabled)
+1 Host Rx Register Full (1=Full)
+2 Host Rx Interrupt Pending (1=Interrupt)
+3 Unused
+4 Host Tx Interrupt (1=Enabled)
+5 Host Tx Register empty (1=Empty)
+6 Host Tx Interrupt Pending (1=Interrupt)
+7 Unused
+*/
+
+int
+snd_wavefront_interrupt_bits (int irq)
+
+{
+	int bits;
+
+	switch (irq) {
+	case 9:
+		bits = 0x00;
+		break;
+	case 5:
+		bits = 0x08;
+		break;
+	case 12:
+		bits = 0x10;
+		break;
+	case 15:
+		bits = 0x18;
+		break;
+	
+	default:
+		snd_printk ("invalid IRQ %d\n", irq);
+		bits = -1;
+	}
+
+	return bits;
+}
+
+static void
+wavefront_should_cause_interrupt (snd_wavefront_t *dev, 
+				  int val, int port, int timeout)
+
+{
+	unsigned long flags;
+
+	save_flags (flags);
+	cli();
+	dev->irq_ok = 0;
+	outb (val,port);
+	interruptible_sleep_on_timeout (&dev->interrupt_sleeper, timeout);
+	restore_flags (flags);
+}
+
+static int
+wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
+
+{
+	int bits;
+	int hwv[2];
+
+	/* IRQ already checked */
+
+	bits = snd_wavefront_interrupt_bits (dev->irq);
+
+	/* try reset of port */
+
+	outb (0x0, dev->control_port); 
+  
+	/* At this point, the board is in reset, and the H/W initialization
+	   register is accessed at the same address as the data port.
+     
+	   Bit 7 - Enable IRQ Driver	
+	   0 - Tri-state the Wave-Board drivers for the PC Bus IRQs
+	   1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus.
+     
+	   Bit 6 - MIDI Interface Select
+
+	   0 - Use the MIDI Input from the 26-pin WaveBlaster
+	   compatible header as the serial MIDI source
+	   1 - Use the MIDI Input from the 9-pin D connector as the
+	   serial MIDI source.
+     
+	   Bits 5:3 - IRQ Selection
+	   0 0 0 - IRQ 2/9
+	   0 0 1 - IRQ 5
+	   0 1 0 - IRQ 12
+	   0 1 1 - IRQ 15
+	   1 0 0 - Reserved
+	   1 0 1 - Reserved
+	   1 1 0 - Reserved
+	   1 1 1 - Reserved
+     
+	   Bits 2:1 - Reserved
+	   Bit 0 - Disable Boot ROM
+	   0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM
+	   1 - memory accesses to 03FC30-03FFFFH are directed to external 
+	   storage.
+     
+	*/
+
+	/* configure hardware: IRQ, enable interrupts, 
+	   plus external 9-pin MIDI interface selected
+	*/
+
+	outb (0x80 | 0x40 | bits, dev->data_port);	
+  
+	/* CONTROL REGISTER
+
+	   0 Host Rx Interrupt Enable (1=Enabled)      0x1
+	   1 Unused                                    0x2
+	   2 Unused                                    0x4
+	   3 Unused                                    0x8
+	   4 Host Tx Interrupt Enable                 0x10
+	   5 Mute (0=Mute; 1=Play)                    0x20
+	   6 Master Interrupt Enable (1=Enabled)      0x40
+	   7 Master Reset (0=Reset; 1=Run)            0x80
+
+	   Take us out of reset, mute output, master + TX + RX interrupts on.
+	   
+	   We'll get an interrupt presumably to tell us that the TX
+	   register is clear.
+	*/
+
+	wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1,
+					 dev->control_port,
+					 (reset_time*HZ)/100);
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+	 */
+
+	if (!dev->irq_ok) {
+		snd_printk ("intr not received after h/w un-reset.\n");
+		goto gone_bad;
+	} 
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+
+	   At this point, only "HW VERSION" or "DOWNLOAD OS" commands
+	   will work. So, issue one of them, and wait for TX
+	   interrupt. This can take a *long* time after a cold boot,
+	   while the ISC ROM does its RAM test. The SDK says up to 4
+	   seconds - with 12MB of RAM on a Tropez+, it takes a lot
+	   longer than that (~16secs). Note that the card understands
+	   the difference between a warm and a cold boot, so
+	   subsequent ISC2115 reboots (say, caused by module
+	   reloading) will get through this much faster.
+
+	   XXX Interesting question: why is no RX interrupt received first ?
+	*/
+
+	wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, 
+					 dev->data_port, ramcheck_time*HZ);
+
+	if (!dev->irq_ok) {
+		snd_printk ("post-RAM-check interrupt not received.\n");
+		goto gone_bad;
+	} 
+
+	if (!wavefront_wait (dev, STAT_CAN_READ)) {
+		snd_printk ("no response to HW version cmd.\n");
+		goto gone_bad;
+	}
+	
+	if ((hwv[0] = wavefront_read (dev)) == -1) {
+		snd_printk ("board not responding correctly.\n");
+		goto gone_bad;
+	}
+
+	if (hwv[0] == 0xFF) { /* NAK */
+
+		/* Board's RAM test failed. Try to read error code,
+		   and tell us about it either way.
+		*/
+		
+		if ((hwv[0] = wavefront_read (dev)) == -1) {
+			snd_printk ("on-board RAM test failed "
+				    "(bad error code).\n");
+		} else {
+			snd_printk ("on-board RAM test failed "
+				    "(error code: 0x%x).\n",
+				hwv[0]);
+		}
+		goto gone_bad;
+	}
+
+	/* We're OK, just get the next byte of the HW version response */
+
+	if ((hwv[1] = wavefront_read (dev)) == -1) {
+		snd_printk ("incorrect h/w response.\n");
+		goto gone_bad;
+	}
+
+	snd_printk ("hardware version %d.%d\n",
+		    hwv[0], hwv[1]);
+
+	return 0;
+
+
+     gone_bad:
+	return (1);
+}
+
+#define __KERNEL_SYSCALLS__
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <asm/uaccess.h>
+
+static int errno;
+
+static int
+wavefront_download_firmware (snd_wavefront_t *dev, char *path)
+
+{
+	unsigned char section[WF_SECTION_MAX];
+	char section_length; /* yes, just a char; max value is WF_SECTION_MAX */
+	int section_cnt_downloaded = 0;
+	int fd;
+	int c;
+	int i;
+	mm_segment_t fs;
+
+	/* This tries to be a bit cleverer than the stuff Alan Cox did for
+	   the generic sound firmware, in that it actually knows
+	   something about the structure of the Motorola firmware. In
+	   particular, it uses a version that has been stripped of the
+	   20K of useless header information, and had section lengths
+	   added, making it possible to load the entire OS without any
+	   [kv]malloc() activity, since the longest entity we ever read is
+	   42 bytes (well, WF_SECTION_MAX) long.
+	*/
+
+	fs = get_fs();
+	set_fs (get_ds());
+
+	if ((fd = open (path, 0, 0)) < 0) {
+		snd_printk ("Unable to load \"%s\".\n",
+			path);
+		return 1;
+	}
+
+	while (1) {
+		int x;
+
+		if ((x = read (fd, &section_length, sizeof (section_length))) !=
+		    sizeof (section_length)) {
+			snd_printk ("firmware read error.\n");
+			goto failure;
+		}
+
+		if (section_length == 0) {
+			break;
+		}
+
+		if (read (fd, section, section_length) != section_length) {
+			snd_printk ("firmware section "
+				"read error.\n");
+			goto failure;
+		}
+
+		/* Send command */
+	
+		if (wavefront_write (dev, WFC_DOWNLOAD_OS)) {
+			goto failure;
+		}
+	
+		for (i = 0; i < section_length; i++) {
+			if (wavefront_write (dev, section[i])) {
+				goto failure;
+			}
+		}
+	
+		/* get ACK */
+	
+		if (wavefront_wait (dev, STAT_CAN_READ)) {
+
+			if ((c = inb (dev->data_port)) != WF_ACK) {
+
+				snd_printk ("download "
+					    "of section #%d not "
+					    "acknowledged, ack = 0x%x\n",
+					    section_cnt_downloaded + 1, c);
+				goto failure;
+		
+			}
+
+		} else {
+			snd_printk ("time out for firmware ACK.\n");
+			goto failure;
+		}
+
+	}
+
+	close (fd);
+	set_fs (fs);
+	return 0;
+
+ failure:
+	close (fd);
+	set_fs (fs);
+	snd_printk ("firmware download failed!!!\n");
+	return 1;
+}
+
+
+static int
+wavefront_do_reset (snd_wavefront_t *dev)
+
+{
+	char voices[1];
+
+	if (wavefront_reset_to_cleanliness (dev)) {
+		snd_printk ("hw reset failed.\n");
+		goto gone_bad;
+	}
+
+	if (dev->israw) {
+		if (wavefront_download_firmware (dev, ospath)) {
+			goto gone_bad;
+		}
+
+		dev->israw = 0;
+
+		/* Wait for the OS to get running. The protocol for
+		   this is non-obvious, and was determined by
+		   using port-IO tracing in DOSemu and some
+		   experimentation here.
+		   
+		   Rather than using timed waits, use interrupts creatively.
+		*/
+
+		wavefront_should_cause_interrupt (dev, WFC_NOOP,
+						  dev->data_port,
+						  (osrun_time*HZ));
+
+		if (!dev->irq_ok) {
+			snd_printk ("no post-OS interrupt.\n");
+			goto gone_bad;
+		}
+		
+		/* Now, do it again ! */
+		
+		wavefront_should_cause_interrupt (dev, WFC_NOOP,
+						  dev->data_port, (10*HZ));
+		
+		if (!dev->irq_ok) {
+			snd_printk ("no post-OS interrupt(2).\n");
+			goto gone_bad;
+		}
+
+		/* OK, no (RX/TX) interrupts any more, but leave mute
+		   in effect. 
+		*/
+		
+		outb (0x80|0x40, dev->control_port); 
+	}
+
+	/* SETUPSND.EXE asks for sample memory config here, but since i
+	   have no idea how to interpret the result, we'll forget
+	   about it.
+	*/
+	
+	if ((dev->freemem = wavefront_freemem (dev)) < 0) {
+		goto gone_bad;
+	}
+		
+	snd_printk ("available DRAM %dk\n", dev->freemem / 1024);
+
+	if (wavefront_write (dev, 0xf0) ||
+	    wavefront_write (dev, 1) ||
+	    (wavefront_read (dev) < 0)) {
+		dev->debug = 0;
+		snd_printk ("MPU emulation mode not set.\n");
+		goto gone_bad;
+	}
+
+	voices[0] = 32;
+
+	if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, 0, voices)) {
+		snd_printk ("cannot set number of voices to 32.\n");
+		goto gone_bad;
+	}
+
+
+	return 0;
+
+ gone_bad:
+	/* reset that sucker so that it doesn't bother us. */
+
+	outb (0x0, dev->control_port);
+	dev->interrupts_are_midi = 0;
+	return 1;
+}
+
+int 
+snd_wavefront_start (snd_wavefront_t *dev)
+
+{
+	int samples_are_from_rom;
+
+	/* IMPORTANT: assumes that snd_wavefront_detect() and/or
+	   wavefront_reset_to_cleanliness() has already been called 
+	*/
+
+	if (dev->israw) {
+		samples_are_from_rom = 1;
+	} else {
+		/* XXX is this always true ? */
+		samples_are_from_rom = 0;
+	}
+
+	if (dev->israw || fx_raw) {
+		if (wavefront_do_reset (dev)) {
+			return -1;
+		}
+	}
+	/* Check for FX device, present only on Tropez+ */
+
+	dev->has_fx = (snd_wavefront_fx_detect (dev) == 0);
+
+	if (dev->has_fx && fx_raw) {
+		snd_wavefront_fx_start (dev);
+	}
+
+	wavefront_get_sample_status (dev, samples_are_from_rom);
+	wavefront_get_program_status (dev);
+	wavefront_get_patch_status (dev);
+
+	/* Start normal operation: unreset, master interrupt enabled, no mute
+	*/
+
+	outb (0x80|0x40|0x20, dev->control_port); 
+
+	return (0);
+}
+
+int
+snd_wavefront_detect (snd_wavefront_card_t *card)
+
+{
+	unsigned char   rbuf[4], wbuf[4];
+	snd_wavefront_t *dev = &card->wavefront;
+	
+	/* returns zero if a WaveFront card is successfully detected.
+	   negative otherwise.
+	*/
+
+	dev->israw = 0;
+	dev->has_fx = 0;
+	dev->debug = debug_default;
+	dev->interrupts_are_midi = 0;
+	dev->irq_cnt = 0;
+	dev->rom_samples_rdonly = 1;
+
+	if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
+
+		dev->fw_version[0] = rbuf[0];
+		dev->fw_version[1] = rbuf[1];
+
+		snd_printk ("firmware %d.%d already loaded.\n",
+			    rbuf[0], rbuf[1]);
+
+		/* check that a command actually works */
+      
+		if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION,
+				       rbuf, wbuf) == 0) {
+			dev->hw_version[0] = rbuf[0];
+			dev->hw_version[1] = rbuf[1];
+		} else {
+			snd_printk ("not raw, but no "
+				    "hardware version!\n");
+			return -1;
+		}
+
+		if (!wf_raw) {
+			return 0;
+		} else {
+			snd_printk ("reloading firmware as you requested.\n");
+			dev->israw = 1;
+		}
+
+	} else {
+
+		dev->israw = 1;
+		snd_printk ("no response to firmware probe, assume raw.\n");
+
+	}
+
+	return 0;
+}
+
+#if 0
+EXPORT_SYMBOL(snd_wavefront_synth_ioctl);
+EXPORT_SYMBOL(snd_wavefront_synth_open);
+EXPORT_SYMBOL(snd_wavefront_synth_release);
+EXPORT_SYMBOL(snd_wavefront_internal_interrupt);
+EXPORT_SYMBOL(snd_wavefront_interrupt_bits);
+EXPORT_SYMBOL(snd_wavefront_start);
+EXPORT_SYMBOL(snd_wavefront_detect);
+EXPORT_SYMBOL(snd_wavefront_cmd);
+  /* wavefront_midi.c */
+EXPORT_SYMBOL(snd_wavefront_midi_interrupt);
+EXPORT_SYMBOL(snd_wavefront_midi_enable_virtual);
+EXPORT_SYMBOL(snd_wavefront_midi_disable_virtual);
+EXPORT_SYMBOL(snd_wavefront_midi_start);
+EXPORT_SYMBOL(snd_wavefront_midi_input);
+EXPORT_SYMBOL(snd_wavefront_midi_output);
+
+static int __init alsa_wavefront_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_wavefront_exit(void)
+{
+}
+
+module_init(alsa_wavefront_init)
+module_exit(alsa_wavefront_exit)
+#endif
diff -Nru linux/sound/last.c linux-2.4.19-pre5-mjc/sound/last.c
--- linux/sound/last.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/last.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,41 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/core.h>
+
+static int __init alsa_sound_last_init(void)
+{
+	int idx, ok = 0;
+	
+	printk(KERN_INFO "ALSA device list:\n");
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		if (snd_cards[idx] != NULL) {
+			printk(KERN_INFO "  #%i: %s\n", idx, snd_cards[idx]->longname);
+			ok++;
+		}
+	if (ok == 0)
+		printk(KERN_INFO "  No soundcards found.\n");
+	return 0;
+}
+
+__initcall(alsa_sound_last_init);
diff -Nru linux/sound/oss/724hwmcode.h linux-2.4.19-pre5-mjc/sound/oss/724hwmcode.h
--- linux/sound/oss/724hwmcode.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/724hwmcode.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1575 @@
+//=============================================================================
+// Copyright (c) 1997-1999	Yamaha Corporation.	All Rights Reserved.
+//
+//	Title:
+//		hwmcode.c
+//	Desc:
+//		micro-code for CTRL & DSP
+//=============================================================================
+#ifndef _HWMCODE_
+#define _HWMCODE_
+
+static unsigned long int	DspInst[] __initdata = {
+	0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+	0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+	0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+	0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static unsigned long int	CntrlInst[] __initdata = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x001A82, 0x032D0D, 0x000810, 0x10043A,
+	0x02D38D, 0x000810, 0x18043A, 0x00010D,
+	0x020015, 0x0000FD, 0x000020, 0x038860,
+	0x039060, 0x038060, 0x038040, 0x038040,
+	0x038040, 0x018040, 0x000A7D, 0x038040,
+	0x038040, 0x018040, 0x200402, 0x000882,
+	0x08001A, 0x000904, 0x015986, 0x000007,
+	0x260007, 0x000007, 0x000007, 0x018A06,
+	0x000007, 0x030C8D, 0x000810, 0x18043A,
+	0x260007, 0x00087D, 0x018042, 0x00160A,
+	0x04A206, 0x000007, 0x00218D, 0x000810,
+	0x08043A, 0x21C206, 0x000007, 0x0007FD,
+	0x018042, 0x08000A, 0x000904, 0x029386,
+	0x000195, 0x090D04, 0x000007, 0x000820,
+	0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
+	0x032206, 0x018040, 0x000A7D, 0x038042,
+	0x13804A, 0x18000A, 0x001820, 0x059060,
+	0x058860, 0x018040, 0x0000FD, 0x018042,
+	0x70000A, 0x000115, 0x071144, 0x032386,
+	0x030000, 0x007020, 0x034A06, 0x018040,
+	0x00348D, 0x000810, 0x08043A, 0x21EA06,
+	0x000007, 0x02D38D, 0x000810, 0x18043A,
+	0x018206, 0x000007, 0x240007, 0x000F8D,
+	0x000810, 0x00163A, 0x002402, 0x005C02,
+	0x0028FD, 0x000020, 0x018040, 0x08000D,
+	0x000815, 0x510984, 0x000007, 0x00004D,
+	0x000E5D, 0x000E02, 0x00418D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00008D,
+	0x000924, 0x000F02, 0x00458D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x018386, 0x000007, 0x01AA06, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x218086, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x055A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x034986, 0x000007, 0x002104, 0x034986,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x06C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00688D, 0x000810, 0x08043A, 0x288A06,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x060206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x215886, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x212086, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x07DA86, 0x00057D, 0x002820,
+	0x03B060, 0x07F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x07FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x008D8D, 0x000810,
+	0x08043A, 0x288A06, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x095186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x007FBD, 0x383DC4,
+	0x000007, 0x001A7D, 0x001375, 0x018042,
+	0x09004A, 0x10000A, 0x0B8D04, 0x139504,
+	0x000007, 0x000820, 0x019060, 0x001104,
+	0x212086, 0x010040, 0x0017FD, 0x018042,
+	0x08000A, 0x000904, 0x212286, 0x000007,
+	0x00197D, 0x038042, 0x09804A, 0x10000A,
+	0x000924, 0x001664, 0x0011FD, 0x038042,
+	0x2B804A, 0x19804A, 0x00008D, 0x218944,
+	0x000007, 0x002244, 0x0AE186, 0x000007,
+	0x001A64, 0x002A24, 0x00197D, 0x080102,
+	0x100122, 0x000820, 0x039060, 0x018040,
+	0x003DFD, 0x00008D, 0x000820, 0x018040,
+	0x001375, 0x001A7D, 0x010042, 0x09804A,
+	0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
+	0x309144, 0x000007, 0x00060D, 0x000A15,
+	0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
+	0x000464, 0x01B3E4, 0x0232E4, 0x000464,
+	0x000464, 0x000464, 0x000464, 0x00040D,
+	0x08B1C4, 0x000007, 0x000820, 0x000BF5,
+	0x030040, 0x00197D, 0x038042, 0x09804A,
+	0x000A24, 0x08000A, 0x080E64, 0x000007,
+	0x100122, 0x000820, 0x031060, 0x010040,
+	0x0064AC, 0x00027D, 0x000020, 0x018040,
+	0x00107D, 0x018042, 0x0011FD, 0x3B804A,
+	0x09804A, 0x20000A, 0x000095, 0x1A1144,
+	0x00A144, 0x0D2086, 0x00040D, 0x00B984,
+	0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
+	0x09804A, 0x28000A, 0x000095, 0x010924,
+	0x002A64, 0x0D1186, 0x000007, 0x002904,
+	0x0D2286, 0x000007, 0x0D2A06, 0x080002,
+	0x00008D, 0x00387D, 0x000820, 0x018040,
+	0x00127D, 0x018042, 0x10000A, 0x003904,
+	0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
+	0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
+	0x000015, 0x00082D, 0x02C78D, 0x000820,
+	0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
+	0x0E7186, 0x400025, 0x00008D, 0x110944,
+	0x000007, 0x00018D, 0x109504, 0x000007,
+	0x009164, 0x000424, 0x000424, 0x000424,
+	0x100102, 0x280002, 0x02C68D, 0x000820,
+	0x0EC206, 0x00018D, 0x00042D, 0x00008D,
+	0x109504, 0x000007, 0x00020D, 0x109184,
+	0x000007, 0x02C70D, 0x000820, 0x00008D,
+	0x0038FD, 0x018040, 0x003BFD, 0x001020,
+	0x03A860, 0x000815, 0x313184, 0x212184,
+	0x000007, 0x03B060, 0x03A060, 0x018040,
+	0x0022FD, 0x000095, 0x010924, 0x000424,
+	0x000424, 0x001264, 0x100102, 0x000820,
+	0x039060, 0x018040, 0x001924, 0x00FB8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x000424,
+	0x000424, 0x00117D, 0x018042, 0x08000A,
+	0x000A24, 0x280502, 0x280C02, 0x09800D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x0022FD, 0x018042, 0x08000A, 0x000095,
+	0x280DC4, 0x011924, 0x00197D, 0x018042,
+	0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
+	0x113144, 0x0A8D04, 0x000007, 0x080A44,
+	0x129504, 0x000007, 0x0023FD, 0x001020,
+	0x038040, 0x101244, 0x000007, 0x000820,
+	0x039060, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x10FA86, 0x000007,
+	0x003BFD, 0x000100, 0x000A10, 0x0B807A,
+	0x13804A, 0x090984, 0x000007, 0x000095,
+	0x013D04, 0x118086, 0x10000A, 0x100002,
+	0x090984, 0x000007, 0x038042, 0x11804A,
+	0x090D04, 0x000007, 0x10000A, 0x090D84,
+	0x000007, 0x00257D, 0x000820, 0x018040,
+	0x00010D, 0x000810, 0x28143A, 0x00127D,
+	0x018042, 0x20000A, 0x00197D, 0x018042,
+	0x00117D, 0x31804A, 0x10000A, 0x003124,
+	0x01280D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x300102, 0x003124, 0x000424, 0x000424,
+	0x001224, 0x280502, 0x001A4C, 0x130186,
+	0x700002, 0x00002D, 0x030000, 0x00387D,
+	0x018042, 0x10000A, 0x132A06, 0x002124,
+	0x0000AD, 0x100002, 0x00010D, 0x000924,
+	0x006B24, 0x01368D, 0x00397D, 0x000820,
+	0x058040, 0x038042, 0x09844A, 0x000606,
+	0x08040A, 0x003264, 0x00008D, 0x000A24,
+	0x001020, 0x00227D, 0x018040, 0x013C0D,
+	0x000810, 0x08043A, 0x29D206, 0x000007,
+	0x002820, 0x00207D, 0x018040, 0x00117D,
+	0x038042, 0x13804A, 0x33800A, 0x00387D,
+	0x018042, 0x08000A, 0x000904, 0x163A86,
+	0x000007, 0x00008D, 0x030964, 0x01478D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x380102,
+	0x000424, 0x000424, 0x001224, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x14A286,
+	0x000007, 0x280502, 0x001A4C, 0x163986,
+	0x000007, 0x032164, 0x00632C, 0x003DFD,
+	0x018042, 0x08000A, 0x000095, 0x090904,
+	0x000007, 0x000820, 0x001A4C, 0x156186,
+	0x018040, 0x030000, 0x157A06, 0x002124,
+	0x00010D, 0x000924, 0x006B24, 0x015B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x003A64,
+	0x000095, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x01628D, 0x000810, 0x08043A, 0x29D206,
+	0x000007, 0x14D206, 0x000007, 0x007020,
+	0x08010A, 0x10012A, 0x0020FD, 0x038860,
+	0x039060, 0x018040, 0x00227D, 0x018042,
+	0x003DFD, 0x08000A, 0x31844A, 0x000904,
+	0x16D886, 0x18008B, 0x00008D, 0x189904,
+	0x00312C, 0x17AA06, 0x000007, 0x00324C,
+	0x173386, 0x000007, 0x001904, 0x173086,
+	0x000007, 0x000095, 0x199144, 0x00222C,
+	0x003124, 0x00636C, 0x000E3D, 0x001375,
+	0x000BFD, 0x010042, 0x09804A, 0x10000A,
+	0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
+	0x000007, 0x00008D, 0x189904, 0x00226C,
+	0x00322C, 0x30050A, 0x301DAB, 0x002083,
+	0x0018FD, 0x018042, 0x08000A, 0x018924,
+	0x300502, 0x001083, 0x001875, 0x010042,
+	0x10000A, 0x00008D, 0x010924, 0x001375,
+	0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
+	0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
+	0x305C8B, 0x006083, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x187A86, 0x000007,
+	0x001E2D, 0x0005FD, 0x018042, 0x08000A,
+	0x028924, 0x280502, 0x00060D, 0x000810,
+	0x280C3A, 0x00008D, 0x000810, 0x28143A,
+	0x0A808D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001275, 0x030042, 0x21004A,
+	0x00008D, 0x1A0944, 0x000007, 0x01980D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0001F5, 0x030042, 0x0D004A, 0x10000A,
+	0x089144, 0x000007, 0x000820, 0x010040,
+	0x0025F5, 0x0A3144, 0x000007, 0x000820,
+	0x032860, 0x030040, 0x00217D, 0x038042,
+	0x0B804A, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00008D, 0x000124, 0x00012C,
+	0x000E64, 0x001A64, 0x00636C, 0x08010A,
+	0x10012A, 0x000820, 0x031060, 0x030040,
+	0x0020FD, 0x018042, 0x08000A, 0x00227D,
+	0x018042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00197D, 0x018042, 0x08000A,
+	0x0022FD, 0x038042, 0x10000A, 0x000820,
+	0x031060, 0x030040, 0x090D04, 0x000007,
+	0x000820, 0x030040, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x038042, 0x13804A, 0x19804A, 0x110D04,
+	0x198D04, 0x000007, 0x08000A, 0x001020,
+	0x031860, 0x030860, 0x030040, 0x00008D,
+	0x0B0944, 0x000007, 0x000820, 0x010040,
+	0x0005F5, 0x030042, 0x08000A, 0x000820,
+	0x010040, 0x0000F5, 0x010042, 0x08000A,
+	0x000904, 0x1C6086, 0x001E75, 0x030042,
+	0x01044A, 0x000C0A, 0x1C7206, 0x000007,
+	0x000402, 0x000C02, 0x00177D, 0x001AF5,
+	0x018042, 0x03144A, 0x031C4A, 0x03244A,
+	0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
+	0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
+	0x030042, 0x0B004A, 0x1B804A, 0x13804A,
+	0x20000A, 0x089144, 0x19A144, 0x0389E4,
+	0x0399EC, 0x005502, 0x005D0A, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x006502, 0x006D0A, 0x030042, 0x0B004A,
+	0x19004A, 0x2B804A, 0x13804A, 0x21804A,
+	0x30000A, 0x089144, 0x19A144, 0x2AB144,
+	0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
+	0x03A9E4, 0x000702, 0x00107D, 0x000415,
+	0x018042, 0x08000A, 0x0109E4, 0x000F02,
+	0x002AF5, 0x0019FD, 0x010042, 0x09804A,
+	0x10000A, 0x000934, 0x001674, 0x0029F5,
+	0x010042, 0x10000A, 0x00917C, 0x002075,
+	0x010042, 0x08000A, 0x000904, 0x1ED286,
+	0x0026F5, 0x0027F5, 0x030042, 0x09004A,
+	0x10000A, 0x000A3C, 0x00167C, 0x001A75,
+	0x000BFD, 0x010042, 0x51804A, 0x48000A,
+	0x160007, 0x001075, 0x010042, 0x282C0A,
+	0x281D12, 0x282512, 0x001F32, 0x1E0007,
+	0x0E0007, 0x001975, 0x010042, 0x002DF5,
+	0x0D004A, 0x10000A, 0x009144, 0x1FB286,
+	0x010042, 0x28340A, 0x000E5D, 0x00008D,
+	0x000375, 0x000820, 0x010040, 0x05D2F4,
+	0x54D104, 0x00735C, 0x205386, 0x000007,
+	0x0C0007, 0x080007, 0x0A0007, 0x02040D,
+	0x000810, 0x08043A, 0x332206, 0x000007,
+	0x205A06, 0x000007, 0x080007, 0x002275,
+	0x010042, 0x20000A, 0x002104, 0x212086,
+	0x001E2D, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x209286, 0x000007, 0x002010,
+	0x30043A, 0x00057D, 0x0180C3, 0x08000A,
+	0x028924, 0x280502, 0x280C02, 0x0A810D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x0004FD, 0x018042, 0x70000A, 0x030000,
+	0x007020, 0x06FA06, 0x018040, 0x02180D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x218A86, 0x000007, 0x01F206, 0x000007,
+	0x000875, 0x0009FD, 0x00010D, 0x220A06,
+	0x000295, 0x000B75, 0x00097D, 0x00000D,
+	0x000515, 0x010042, 0x18000A, 0x001904,
+	0x287886, 0x0006F5, 0x001020, 0x010040,
+	0x0004F5, 0x000820, 0x010040, 0x000775,
+	0x010042, 0x09804A, 0x10000A, 0x001124,
+	0x000904, 0x22BA86, 0x000815, 0x080102,
+	0x101204, 0x22DA06, 0x000575, 0x081204,
+	0x000007, 0x100102, 0x000575, 0x000425,
+	0x021124, 0x100102, 0x000820, 0x031060,
+	0x010040, 0x001924, 0x287886, 0x00008D,
+	0x000464, 0x009D04, 0x278886, 0x180102,
+	0x000575, 0x010042, 0x28040A, 0x00018D,
+	0x000924, 0x280D02, 0x00000D, 0x000924,
+	0x281502, 0x10000D, 0x000820, 0x0002F5,
+	0x010040, 0x200007, 0x001175, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x23C286,
+	0x000007, 0x000100, 0x080B20, 0x130B60,
+	0x1B0B60, 0x030A60, 0x010040, 0x050042,
+	0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
+	0x0006F5, 0x010042, 0x28140A, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x24CA86, 0x004015, 0x000095, 0x010D04,
+	0x24B886, 0x100022, 0x10002A, 0x24E206,
+	0x000007, 0x333104, 0x2AA904, 0x000007,
+	0x032124, 0x280502, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x00292C, 0x00636C,
+	0x25F386, 0x000007, 0x02B164, 0x000464,
+	0x000464, 0x00008D, 0x000A64, 0x280D02,
+	0x10008D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x00008D, 0x38B904, 0x000007,
+	0x03296C, 0x30010A, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x25BA86, 0x000007,
+	0x02312C, 0x28050A, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x267A86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x26C086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x26CA86, 0x000007, 0x003124, 0x300502,
+	0x003924, 0x300583, 0x000883, 0x0005F5,
+	0x010042, 0x28040A, 0x00008D, 0x008124,
+	0x280D02, 0x00008D, 0x008124, 0x281502,
+	0x10018D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001025, 0x000575, 0x030042,
+	0x09004A, 0x10000A, 0x0A0904, 0x121104,
+	0x000007, 0x001020, 0x050860, 0x050040,
+	0x0006FD, 0x018042, 0x09004A, 0x10000A,
+	0x0000A5, 0x0A0904, 0x121104, 0x000007,
+	0x000820, 0x019060, 0x010040, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x284286,
+	0x000007, 0x230A06, 0x000007, 0x000606,
+	0x000007, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x289286, 0x000007, 0x000100,
+	0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
+	0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
+	0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
+	0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
+	0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
+	0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
+	0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
+	0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
+	0x000606, 0x018040, 0x00008D, 0x000A64,
+	0x280D02, 0x000A24, 0x00027D, 0x018042,
+	0x10000A, 0x001224, 0x0003FD, 0x018042,
+	0x08000A, 0x000904, 0x2A8286, 0x000007,
+	0x00018D, 0x000A24, 0x000464, 0x000464,
+	0x080102, 0x000924, 0x000424, 0x000424,
+	0x100102, 0x02000D, 0x009144, 0x2AD986,
+	0x000007, 0x0001FD, 0x018042, 0x08000A,
+	0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x00027D, 0x001020, 0x000606, 0x018040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x2B2A86, 0x000007, 0x00037D, 0x018042,
+	0x08000A, 0x000904, 0x2B5A86, 0x000007,
+	0x000075, 0x002E7D, 0x010042, 0x0B804A,
+	0x000020, 0x000904, 0x000686, 0x010040,
+	0x31844A, 0x30048B, 0x000883, 0x00008D,
+	0x000810, 0x28143A, 0x00008D, 0x000810,
+	0x280C3A, 0x000675, 0x010042, 0x08000A,
+	0x003815, 0x010924, 0x280502, 0x0B000D,
+	0x000820, 0x0002F5, 0x010040, 0x000606,
+	0x220007, 0x000464, 0x000464, 0x000606,
+	0x000007, 0x000134, 0x007F8D, 0x00093C,
+	0x281D12, 0x282512, 0x001F32, 0x0E0007,
+	0x00010D, 0x00037D, 0x000820, 0x018040,
+	0x05D2F4, 0x000007, 0x080007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2D0286,
+	0x000007, 0x000606, 0x000007, 0x000007,
+	0x000012, 0x100007, 0x320007, 0x600007,
+	0x100080, 0x48001A, 0x004904, 0x2D6186,
+	0x000007, 0x001210, 0x58003A, 0x000145,
+	0x5C5D04, 0x000007, 0x000080, 0x48001A,
+	0x004904, 0x2DB186, 0x000007, 0x001210,
+	0x50003A, 0x005904, 0x2E0886, 0x000045,
+	0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
+	0x004224, 0x500102, 0x200502, 0x000082,
+	0x40001A, 0x004104, 0x2E3986, 0x000007,
+	0x003865, 0x40001A, 0x004020, 0x00104D,
+	0x04C184, 0x301B86, 0x000040, 0x040007,
+	0x000165, 0x000145, 0x004020, 0x000040,
+	0x000765, 0x080080, 0x40001A, 0x004104,
+	0x2EC986, 0x000007, 0x001210, 0x40003A,
+	0x004104, 0x2F2286, 0x00004D, 0x0000CD,
+	0x004810, 0x20043A, 0x000882, 0x40001A,
+	0x004104, 0x2F3186, 0x000007, 0x004820,
+	0x005904, 0x300886, 0x000040, 0x0007E5,
+	0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
+	0x4216E0, 0x021260, 0x000040, 0x000032,
+	0x400075, 0x00007D, 0x07D574, 0x200512,
+	0x000082, 0x40001A, 0x004104, 0x2FE186,
+	0x000007, 0x037206, 0x640007, 0x060007,
+	0x0000E5, 0x000020, 0x000040, 0x000A65,
+	0x000020, 0x020040, 0x020040, 0x000040,
+	0x000165, 0x000042, 0x70000A, 0x007104,
+	0x30A286, 0x000007, 0x018206, 0x640007,
+	0x050000, 0x007020, 0x000040, 0x037206,
+	0x640007, 0x000007, 0x00306D, 0x028860,
+	0x029060, 0x08000A, 0x028860, 0x008040,
+	0x100012, 0x00100D, 0x009184, 0x314186,
+	0x000E0D, 0x009184, 0x325186, 0x000007,
+	0x300007, 0x001020, 0x003B6D, 0x008040,
+	0x000080, 0x08001A, 0x000904, 0x316186,
+	0x000007, 0x001220, 0x000DED, 0x008040,
+	0x008042, 0x10000A, 0x40000D, 0x109544,
+	0x000007, 0x001020, 0x000DED, 0x008040,
+	0x008042, 0x20040A, 0x000082, 0x08001A,
+	0x000904, 0x31F186, 0x000007, 0x003B6D,
+	0x008042, 0x08000A, 0x000E15, 0x010984,
+	0x329B86, 0x600007, 0x08001A, 0x000C15,
+	0x010984, 0x328386, 0x000020, 0x1A0007,
+	0x0002ED, 0x008040, 0x620007, 0x00306D,
+	0x028042, 0x0A804A, 0x000820, 0x0A804A,
+	0x000606, 0x10804A, 0x000007, 0x282512,
+	0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
+	0x000786, 0x000007, 0x0C0007, 0x0A0007,
+	0x1C0007, 0x003465, 0x020040, 0x004820,
+	0x025060, 0x40000A, 0x024060, 0x000040,
+	0x454944, 0x000007, 0x004020, 0x003AE5,
+	0x000040, 0x0028E5, 0x000042, 0x48000A,
+	0x004904, 0x386886, 0x002C65, 0x000042,
+	0x40000A, 0x0000D5, 0x454104, 0x000007,
+	0x000655, 0x054504, 0x34F286, 0x0001D5,
+	0x054504, 0x34F086, 0x002B65, 0x000042,
+	0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
+	0x000007, 0x454504, 0x000007, 0x0000CD,
+	0x444944, 0x000007, 0x454504, 0x000007,
+	0x00014D, 0x554944, 0x000007, 0x045144,
+	0x34E986, 0x002C65, 0x000042, 0x48000A,
+	0x4CD104, 0x000007, 0x04C144, 0x34F386,
+	0x000007, 0x160007, 0x002CE5, 0x040042,
+	0x40000A, 0x004020, 0x000040, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x356086,
+	0x000007, 0x002402, 0x36A206, 0x005C02,
+	0x0025E5, 0x000042, 0x40000A, 0x004274,
+	0x002AE5, 0x000042, 0x40000A, 0x004274,
+	0x500112, 0x0029E5, 0x000042, 0x40000A,
+	0x004234, 0x454104, 0x000007, 0x004020,
+	0x000040, 0x003EE5, 0x000020, 0x000040,
+	0x002DE5, 0x400152, 0x50000A, 0x045144,
+	0x364A86, 0x0000C5, 0x003EE5, 0x004020,
+	0x000040, 0x002BE5, 0x000042, 0x40000A,
+	0x404254, 0x000007, 0x002AE5, 0x004020,
+	0x000040, 0x500132, 0x040134, 0x005674,
+	0x0029E5, 0x020042, 0x42000A, 0x000042,
+	0x50000A, 0x05417C, 0x0028E5, 0x000042,
+	0x48000A, 0x0000C5, 0x4CC144, 0x371086,
+	0x0026E5, 0x0027E5, 0x020042, 0x40004A,
+	0x50000A, 0x00423C, 0x00567C, 0x0028E5,
+	0x004820, 0x000040, 0x281D12, 0x282512,
+	0x001F72, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x37AA86, 0x0E0007, 0x160007,
+	0x1E0007, 0x003EE5, 0x000042, 0x40000A,
+	0x004104, 0x37E886, 0x002D65, 0x000042,
+	0x28340A, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
+	0x54D104, 0x00735C, 0x385186, 0x000007,
+	0x000606, 0x080007, 0x0C0007, 0x080007,
+	0x0A0007, 0x0001E5, 0x020045, 0x004020,
+	0x000060, 0x000365, 0x000040, 0x002E65,
+	0x001A20, 0x0A1A60, 0x000040, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x000606, 0x50004A, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+// --------------------------------------------
+//  DS-1E Controller InstructionRAM Code
+//	1999/06/21
+//	Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09?@creat
+// 04/12  stop nise fix
+// 06/21?@WorkingOff timming
+static unsigned long int	CntrlInst1E[] __initdata = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x00800D, 0x000810, 0x20043A, 0x001A82,
+	0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+	0x000810, 0x18043A, 0x00010D, 0x020015,
+	0x0000FD, 0x000020, 0x038860, 0x039060,
+	0x038060, 0x038040, 0x038040, 0x038040,
+	0x018040, 0x000A7D, 0x038040, 0x038040,
+	0x018040, 0x200402, 0x000882, 0x08001A,
+	0x000904, 0x017186, 0x000007, 0x260007,
+	0x400007, 0x000007, 0x03258D, 0x000810,
+	0x18043A, 0x260007, 0x284402, 0x00087D,
+	0x018042, 0x00160A, 0x05A206, 0x000007,
+	0x440007, 0x00230D, 0x000810, 0x08043A,
+	0x22FA06, 0x000007, 0x0007FD, 0x018042,
+	0x08000A, 0x000904, 0x02AB86, 0x000195,
+	0x090D04, 0x000007, 0x000820, 0x0000F5,
+	0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+	0x018040, 0x000A7D, 0x038042, 0x13804A,
+	0x18000A, 0x001820, 0x059060, 0x058860,
+	0x018040, 0x0000FD, 0x018042, 0x70000A,
+	0x000115, 0x071144, 0x033B86, 0x030000,
+	0x007020, 0x036206, 0x018040, 0x00360D,
+	0x000810, 0x08043A, 0x232206, 0x000007,
+	0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+	0x000007, 0x240007, 0x000F8D, 0x000810,
+	0x00163A, 0x002402, 0x005C02, 0x0028FD,
+	0x000020, 0x018040, 0x08000D, 0x000815,
+	0x510984, 0x000007, 0x00004D, 0x000E5D,
+	0x000E02, 0x00430D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x00008D, 0x000924,
+	0x000F02, 0x00470D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x480480, 0x001210,
+	0x28043A, 0x00778D, 0x000810, 0x280C3A,
+	0x00068D, 0x000810, 0x28143A, 0x284402,
+	0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+	0x000820, 0x0002FD, 0x018040, 0x260007,
+	0x200007, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x051286, 0x000007, 0x240007,
+	0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x019B86, 0x000007, 0x01B206, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x22B886, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x065A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x036186, 0x000007, 0x002104, 0x036186,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x07C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00788D, 0x000810, 0x08043A, 0x2A1206,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x070206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x229086, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x225886, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x08DA86, 0x00057D, 0x002820,
+	0x03B060, 0x08F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x08FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x009D8D, 0x000810,
+	0x08043A, 0x2A1206, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x0A5186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x00107D, 0x018042,
+	0x08000A, 0x000215, 0x010984, 0x3A8186,
+	0x000007, 0x007FBD, 0x383DC4, 0x000007,
+	0x001A7D, 0x001375, 0x018042, 0x09004A,
+	0x10000A, 0x0B8D04, 0x139504, 0x000007,
+	0x000820, 0x019060, 0x001104, 0x225886,
+	0x010040, 0x0017FD, 0x018042, 0x08000A,
+	0x000904, 0x225A86, 0x000007, 0x00197D,
+	0x038042, 0x09804A, 0x10000A, 0x000924,
+	0x001664, 0x0011FD, 0x038042, 0x2B804A,
+	0x19804A, 0x00008D, 0x218944, 0x000007,
+	0x002244, 0x0C1986, 0x000007, 0x001A64,
+	0x002A24, 0x00197D, 0x080102, 0x100122,
+	0x000820, 0x039060, 0x018040, 0x003DFD,
+	0x00008D, 0x000820, 0x018040, 0x001375,
+	0x001A7D, 0x010042, 0x09804A, 0x10000A,
+	0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+	0x000007, 0x00060D, 0x000A15, 0x000C1D,
+	0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+	0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+	0x000464, 0x000464, 0x00040D, 0x08B1C4,
+	0x000007, 0x000820, 0x000BF5, 0x030040,
+	0x00197D, 0x038042, 0x09804A, 0x000A24,
+	0x08000A, 0x080E64, 0x000007, 0x100122,
+	0x000820, 0x031060, 0x010040, 0x0064AC,
+	0x00027D, 0x000020, 0x018040, 0x00107D,
+	0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+	0x20000A, 0x000095, 0x1A1144, 0x00A144,
+	0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+	0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+	0x28000A, 0x000095, 0x010924, 0x002A64,
+	0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+	0x000007, 0x0E6206, 0x080002, 0x00008D,
+	0x00387D, 0x000820, 0x018040, 0x00127D,
+	0x018042, 0x10000A, 0x003904, 0x0F0986,
+	0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+	0x000025, 0x0FB206, 0x00002D, 0x000015,
+	0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+	0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+	0x400025, 0x00008D, 0x110944, 0x000007,
+	0x00018D, 0x109504, 0x000007, 0x009164,
+	0x000424, 0x000424, 0x000424, 0x100102,
+	0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+	0x00018D, 0x00042D, 0x00008D, 0x109504,
+	0x000007, 0x00020D, 0x109184, 0x000007,
+	0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+	0x018040, 0x003BFD, 0x001020, 0x03A860,
+	0x000815, 0x313184, 0x212184, 0x000007,
+	0x03B060, 0x03A060, 0x018040, 0x0022FD,
+	0x000095, 0x010924, 0x000424, 0x000424,
+	0x001264, 0x100102, 0x000820, 0x039060,
+	0x018040, 0x001924, 0x010F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x000424, 0x000424,
+	0x00117D, 0x018042, 0x08000A, 0x000A24,
+	0x280502, 0x280C02, 0x09800D, 0x000820,
+	0x0002FD, 0x018040, 0x200007, 0x0022FD,
+	0x018042, 0x08000A, 0x000095, 0x280DC4,
+	0x011924, 0x00197D, 0x018042, 0x0011FD,
+	0x09804A, 0x10000A, 0x0000B5, 0x113144,
+	0x0A8D04, 0x000007, 0x080A44, 0x129504,
+	0x000007, 0x0023FD, 0x001020, 0x038040,
+	0x101244, 0x000007, 0x000820, 0x039060,
+	0x018040, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x123286, 0x000007, 0x003BFD,
+	0x000100, 0x000A10, 0x0B807A, 0x13804A,
+	0x090984, 0x000007, 0x000095, 0x013D04,
+	0x12B886, 0x10000A, 0x100002, 0x090984,
+	0x000007, 0x038042, 0x11804A, 0x090D04,
+	0x000007, 0x10000A, 0x090D84, 0x000007,
+	0x00257D, 0x000820, 0x018040, 0x00010D,
+	0x000810, 0x28143A, 0x00127D, 0x018042,
+	0x20000A, 0x00197D, 0x018042, 0x00117D,
+	0x31804A, 0x10000A, 0x003124, 0x013B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x300102,
+	0x003124, 0x000424, 0x000424, 0x001224,
+	0x280502, 0x001A4C, 0x143986, 0x700002,
+	0x00002D, 0x030000, 0x00387D, 0x018042,
+	0x10000A, 0x146206, 0x002124, 0x0000AD,
+	0x100002, 0x00010D, 0x000924, 0x006B24,
+	0x014A0D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x003264, 0x00008D, 0x000A24, 0x001020,
+	0x00227D, 0x018040, 0x014F8D, 0x000810,
+	0x08043A, 0x2B5A06, 0x000007, 0x002820,
+	0x00207D, 0x018040, 0x00117D, 0x038042,
+	0x13804A, 0x33800A, 0x00387D, 0x018042,
+	0x08000A, 0x000904, 0x177286, 0x000007,
+	0x00008D, 0x030964, 0x015B0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x380102, 0x000424,
+	0x000424, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x280502, 0x001A4C, 0x177186, 0x000007,
+	0x032164, 0x00632C, 0x003DFD, 0x018042,
+	0x08000A, 0x000095, 0x090904, 0x000007,
+	0x000820, 0x001A4C, 0x169986, 0x018040,
+	0x030000, 0x16B206, 0x002124, 0x00010D,
+	0x000924, 0x006B24, 0x016F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x003A64, 0x000095,
+	0x001224, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x171286, 0x000007, 0x01760D,
+	0x000810, 0x08043A, 0x2B5A06, 0x000007,
+	0x160A06, 0x000007, 0x007020, 0x08010A,
+	0x10012A, 0x0020FD, 0x038860, 0x039060,
+	0x018040, 0x00227D, 0x018042, 0x003DFD,
+	0x08000A, 0x31844A, 0x000904, 0x181086,
+	0x18008B, 0x00008D, 0x189904, 0x00312C,
+	0x18E206, 0x000007, 0x00324C, 0x186B86,
+	0x000007, 0x001904, 0x186886, 0x000007,
+	0x000095, 0x199144, 0x00222C, 0x003124,
+	0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+	0x010042, 0x09804A, 0x10000A, 0x038AEC,
+	0x0393EC, 0x00224C, 0x18E186, 0x000007,
+	0x00008D, 0x189904, 0x00226C, 0x00322C,
+	0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+	0x018042, 0x08000A, 0x018924, 0x300502,
+	0x001083, 0x001875, 0x010042, 0x10000A,
+	0x00008D, 0x010924, 0x001375, 0x330542,
+	0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+	0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+	0x006083, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x19B286, 0x000007, 0x001E2D,
+	0x0005FD, 0x018042, 0x08000A, 0x028924,
+	0x280502, 0x00060D, 0x000810, 0x280C3A,
+	0x00008D, 0x000810, 0x28143A, 0x0A808D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x001275, 0x030042, 0x21004A, 0x00008D,
+	0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+	0x030042, 0x0D004A, 0x10000A, 0x089144,
+	0x000007, 0x000820, 0x010040, 0x0025F5,
+	0x0A3144, 0x000007, 0x000820, 0x032860,
+	0x030040, 0x00217D, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00008D, 0x000124, 0x00012C, 0x000E64,
+	0x001A64, 0x00636C, 0x08010A, 0x10012A,
+	0x000820, 0x031060, 0x030040, 0x0020FD,
+	0x018042, 0x08000A, 0x00227D, 0x018042,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00197D, 0x018042, 0x08000A, 0x0022FD,
+	0x038042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x090D04, 0x000007, 0x000820,
+	0x030040, 0x038042, 0x0B804A, 0x10000A,
+	0x000820, 0x031060, 0x030040, 0x038042,
+	0x13804A, 0x19804A, 0x110D04, 0x198D04,
+	0x000007, 0x08000A, 0x001020, 0x031860,
+	0x030860, 0x030040, 0x00008D, 0x0B0944,
+	0x000007, 0x000820, 0x010040, 0x0005F5,
+	0x030042, 0x08000A, 0x000820, 0x010040,
+	0x0000F5, 0x010042, 0x08000A, 0x000904,
+	0x1D9886, 0x001E75, 0x030042, 0x01044A,
+	0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+	0x000C02, 0x00177D, 0x001AF5, 0x018042,
+	0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+	0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+	0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x005502, 0x005D0A, 0x030042, 0x0B004A,
+	0x1B804A, 0x13804A, 0x20000A, 0x089144,
+	0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+	0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+	0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+	0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+	0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+	0x000702, 0x00107D, 0x000415, 0x018042,
+	0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+	0x0019FD, 0x010042, 0x09804A, 0x10000A,
+	0x000934, 0x001674, 0x0029F5, 0x010042,
+	0x10000A, 0x00917C, 0x002075, 0x010042,
+	0x08000A, 0x000904, 0x200A86, 0x0026F5,
+	0x0027F5, 0x030042, 0x09004A, 0x10000A,
+	0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+	0x010042, 0x51804A, 0x48000A, 0x160007,
+	0x001075, 0x010042, 0x282C0A, 0x281D12,
+	0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+	0x001975, 0x010042, 0x002DF5, 0x0D004A,
+	0x10000A, 0x009144, 0x20EA86, 0x010042,
+	0x28340A, 0x000E5D, 0x00008D, 0x000375,
+	0x000820, 0x010040, 0x05D2F4, 0x54D104,
+	0x00735C, 0x218B86, 0x000007, 0x0C0007,
+	0x080007, 0x0A0007, 0x02178D, 0x000810,
+	0x08043A, 0x34B206, 0x000007, 0x219206,
+	0x000007, 0x080007, 0x002275, 0x010042,
+	0x20000A, 0x002104, 0x225886, 0x001E2D,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x21CA86, 0x000007, 0x002010, 0x30043A,
+	0x00057D, 0x0180C3, 0x08000A, 0x028924,
+	0x280502, 0x280C02, 0x0A810D, 0x000820,
+	0x0002F5, 0x010040, 0x220007, 0x0004FD,
+	0x018042, 0x70000A, 0x030000, 0x007020,
+	0x07FA06, 0x018040, 0x022B8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x22C286,
+	0x000007, 0x020206, 0x000007, 0x000875,
+	0x0009FD, 0x00010D, 0x234206, 0x000295,
+	0x000B75, 0x00097D, 0x00000D, 0x000515,
+	0x010042, 0x18000A, 0x001904, 0x2A0086,
+	0x0006F5, 0x001020, 0x010040, 0x0004F5,
+	0x000820, 0x010040, 0x000775, 0x010042,
+	0x09804A, 0x10000A, 0x001124, 0x000904,
+	0x23F286, 0x000815, 0x080102, 0x101204,
+	0x241206, 0x000575, 0x081204, 0x000007,
+	0x100102, 0x000575, 0x000425, 0x021124,
+	0x100102, 0x000820, 0x031060, 0x010040,
+	0x001924, 0x2A0086, 0x00008D, 0x000464,
+	0x009D04, 0x291086, 0x180102, 0x000575,
+	0x010042, 0x28040A, 0x00018D, 0x000924,
+	0x280D02, 0x00000D, 0x000924, 0x281502,
+	0x10000D, 0x000820, 0x0002F5, 0x010040,
+	0x200007, 0x001175, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x24FA86, 0x000007,
+	0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+	0x030A60, 0x010040, 0x050042, 0x3D004A,
+	0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+	0x010042, 0x28140A, 0x0004F5, 0x010042,
+	0x08000A, 0x000315, 0x010D04, 0x260286,
+	0x004015, 0x000095, 0x010D04, 0x25F086,
+	0x100022, 0x10002A, 0x261A06, 0x000007,
+	0x333104, 0x2AA904, 0x000007, 0x032124,
+	0x280502, 0x284402, 0x001124, 0x400102,
+	0x000424, 0x000424, 0x003224, 0x00292C,
+	0x00636C, 0x277386, 0x000007, 0x02B164,
+	0x000464, 0x000464, 0x00008D, 0x000A64,
+	0x280D02, 0x10008D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x00008D, 0x38B904,
+	0x000007, 0x03296C, 0x30010A, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x270286,
+	0x000007, 0x00212C, 0x28050A, 0x00316C,
+	0x00046C, 0x00046C, 0x28450A, 0x001124,
+	0x006B64, 0x100102, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x004124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x27FA86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x284086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x284A86, 0x000007, 0x284402, 0x003124,
+	0x300502, 0x003924, 0x300583, 0x000883,
+	0x0005F5, 0x010042, 0x28040A, 0x00008D,
+	0x008124, 0x280D02, 0x00008D, 0x008124,
+	0x281502, 0x10018D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001025, 0x000575,
+	0x030042, 0x09004A, 0x10000A, 0x0A0904,
+	0x121104, 0x000007, 0x001020, 0x050860,
+	0x050040, 0x0006FD, 0x018042, 0x09004A,
+	0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+	0x000007, 0x000820, 0x019060, 0x010040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x29CA86, 0x000007, 0x244206, 0x000007,
+	0x000606, 0x000007, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x2A1A86, 0x000007,
+	0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+	0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+	0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+	0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+	0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+	0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+	0x038A60, 0x000606, 0x018040, 0x00008D,
+	0x000A64, 0x280D02, 0x000A24, 0x00027D,
+	0x018042, 0x10000A, 0x001224, 0x0003FD,
+	0x018042, 0x08000A, 0x000904, 0x2C0A86,
+	0x000007, 0x00018D, 0x000A24, 0x000464,
+	0x000464, 0x080102, 0x000924, 0x000424,
+	0x000424, 0x100102, 0x02000D, 0x009144,
+	0x2C6186, 0x000007, 0x0001FD, 0x018042,
+	0x08000A, 0x000A44, 0x2C4386, 0x018042,
+	0x0A000D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00027D, 0x001020, 0x000606,
+	0x018040, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x2CB286, 0x000007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2CE286,
+	0x000007, 0x000075, 0x002E7D, 0x010042,
+	0x0B804A, 0x000020, 0x000904, 0x000686,
+	0x010040, 0x31844A, 0x30048B, 0x000883,
+	0x00008D, 0x000810, 0x28143A, 0x00008D,
+	0x000810, 0x280C3A, 0x000675, 0x010042,
+	0x08000A, 0x003815, 0x010924, 0x280502,
+	0x0B000D, 0x000820, 0x0002F5, 0x010040,
+	0x000606, 0x220007, 0x000464, 0x000464,
+	0x000606, 0x000007, 0x000134, 0x007F8D,
+	0x00093C, 0x281D12, 0x282512, 0x001F32,
+	0x0E0007, 0x00010D, 0x00037D, 0x000820,
+	0x018040, 0x05D2F4, 0x000007, 0x080007,
+	0x00037D, 0x018042, 0x08000A, 0x000904,
+	0x2E8A86, 0x000007, 0x000606, 0x000007,
+	0x000007, 0x000012, 0x100007, 0x320007,
+	0x600007, 0x460007, 0x100080, 0x48001A,
+	0x004904, 0x2EF186, 0x000007, 0x001210,
+	0x58003A, 0x000145, 0x5C5D04, 0x000007,
+	0x000080, 0x48001A, 0x004904, 0x2F4186,
+	0x000007, 0x001210, 0x50003A, 0x005904,
+	0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+	0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+	0x200502, 0x000082, 0x40001A, 0x004104,
+	0x2FC986, 0x000007, 0x003865, 0x40001A,
+	0x004020, 0x00104D, 0x04C184, 0x31AB86,
+	0x000040, 0x040007, 0x000165, 0x000145,
+	0x004020, 0x000040, 0x000765, 0x080080,
+	0x40001A, 0x004104, 0x305986, 0x000007,
+	0x001210, 0x40003A, 0x004104, 0x30B286,
+	0x00004D, 0x0000CD, 0x004810, 0x20043A,
+	0x000882, 0x40001A, 0x004104, 0x30C186,
+	0x000007, 0x004820, 0x005904, 0x319886,
+	0x000040, 0x0007E5, 0x200480, 0x2816A0,
+	0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+	0x000040, 0x000032, 0x400075, 0x00007D,
+	0x07D574, 0x200512, 0x000082, 0x40001A,
+	0x004104, 0x317186, 0x000007, 0x038A06,
+	0x640007, 0x0000E5, 0x000020, 0x000040,
+	0x000A65, 0x000020, 0x020040, 0x020040,
+	0x000040, 0x000165, 0x000042, 0x70000A,
+	0x007104, 0x323286, 0x000007, 0x060007,
+	0x019A06, 0x640007, 0x050000, 0x007020,
+	0x000040, 0x038A06, 0x640007, 0x000007,
+	0x00306D, 0x028860, 0x029060, 0x08000A,
+	0x028860, 0x008040, 0x100012, 0x00100D,
+	0x009184, 0x32D186, 0x000E0D, 0x009184,
+	0x33E186, 0x000007, 0x300007, 0x001020,
+	0x003B6D, 0x008040, 0x000080, 0x08001A,
+	0x000904, 0x32F186, 0x000007, 0x001220,
+	0x000DED, 0x008040, 0x008042, 0x10000A,
+	0x40000D, 0x109544, 0x000007, 0x001020,
+	0x000DED, 0x008040, 0x008042, 0x20040A,
+	0x000082, 0x08001A, 0x000904, 0x338186,
+	0x000007, 0x003B6D, 0x008042, 0x08000A,
+	0x000E15, 0x010984, 0x342B86, 0x600007,
+	0x08001A, 0x000C15, 0x010984, 0x341386,
+	0x000020, 0x1A0007, 0x0002ED, 0x008040,
+	0x620007, 0x00306D, 0x028042, 0x0A804A,
+	0x000820, 0x0A804A, 0x000606, 0x10804A,
+	0x000007, 0x282512, 0x001F32, 0x05D2F4,
+	0x54D104, 0x00735C, 0x000786, 0x000007,
+	0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+	0x020040, 0x004820, 0x025060, 0x40000A,
+	0x024060, 0x000040, 0x454944, 0x000007,
+	0x004020, 0x003AE5, 0x000040, 0x0028E5,
+	0x000042, 0x48000A, 0x004904, 0x39F886,
+	0x002C65, 0x000042, 0x40000A, 0x0000D5,
+	0x454104, 0x000007, 0x000655, 0x054504,
+	0x368286, 0x0001D5, 0x054504, 0x368086,
+	0x002B65, 0x000042, 0x003AE5, 0x50004A,
+	0x40000A, 0x45C3D4, 0x000007, 0x454504,
+	0x000007, 0x0000CD, 0x444944, 0x000007,
+	0x454504, 0x000007, 0x00014D, 0x554944,
+	0x000007, 0x045144, 0x367986, 0x002C65,
+	0x000042, 0x48000A, 0x4CD104, 0x000007,
+	0x04C144, 0x368386, 0x000007, 0x160007,
+	0x002CE5, 0x040042, 0x40000A, 0x004020,
+	0x000040, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x36F086, 0x000007, 0x002402,
+	0x383206, 0x005C02, 0x0025E5, 0x000042,
+	0x40000A, 0x004274, 0x002AE5, 0x000042,
+	0x40000A, 0x004274, 0x500112, 0x0029E5,
+	0x000042, 0x40000A, 0x004234, 0x454104,
+	0x000007, 0x004020, 0x000040, 0x003EE5,
+	0x000020, 0x000040, 0x002DE5, 0x400152,
+	0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+	0x003EE5, 0x004020, 0x000040, 0x002BE5,
+	0x000042, 0x40000A, 0x404254, 0x000007,
+	0x002AE5, 0x004020, 0x000040, 0x500132,
+	0x040134, 0x005674, 0x0029E5, 0x020042,
+	0x42000A, 0x000042, 0x50000A, 0x05417C,
+	0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+	0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+	0x020042, 0x40004A, 0x50000A, 0x00423C,
+	0x00567C, 0x0028E5, 0x004820, 0x000040,
+	0x281D12, 0x282512, 0x001F72, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x393A86,
+	0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+	0x000042, 0x40000A, 0x004104, 0x397886,
+	0x002D65, 0x000042, 0x28340A, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+	0x39E186, 0x000007, 0x000606, 0x080007,
+	0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+	0x020045, 0x004020, 0x000060, 0x000365,
+	0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+	0x000040, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x000606, 0x50004A,
+	0x0017FD, 0x018042, 0x08000A, 0x000904,
+	0x225A86, 0x000007, 0x00107D, 0x018042,
+	0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+	0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+	0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+	0x018042, 0x0010FD, 0x09804A, 0x38000A,
+	0x000095, 0x010924, 0x003A64, 0x3B8186,
+	0x000007, 0x003904, 0x3B9286, 0x000007,
+	0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+	0x00387D, 0x018040, 0x700002, 0x00117D,
+	0x018042, 0x00197D, 0x29804A, 0x30000A,
+	0x380002, 0x003124, 0x000424, 0x000424,
+	0x002A24, 0x280502, 0x00068D, 0x000810,
+	0x28143A, 0x00750D, 0x00B124, 0x002264,
+	0x3D0386, 0x284402, 0x000810, 0x280C3A,
+	0x0B800D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00758D, 0x00B124, 0x100102,
+	0x012144, 0x3E4986, 0x001810, 0x10003A,
+	0x00387D, 0x018042, 0x08000A, 0x000904,
+	0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+	0x00008D, 0x023164, 0x000A64, 0x280D02,
+	0x0B808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00387D, 0x018042, 0x08000A,
+	0x000904, 0x3E3286, 0x030000, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x3D8286,
+	0x000007, 0x002810, 0x28043A, 0x00750D,
+	0x030924, 0x002264, 0x280D02, 0x02316C,
+	0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x00008D, 0x000A24,
+	0x3E4A06, 0x100102, 0x001810, 0x10003A,
+	0x0000BD, 0x003810, 0x30043A, 0x00187D,
+	0x018042, 0x0018FD, 0x09804A, 0x20000A,
+	0x0000AD, 0x028924, 0x07212C, 0x001010,
+	0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+	0x002083, 0x00137D, 0x038042, 0x33844A,
+	0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+	0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+	0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+	0x020924, 0x00068D, 0x00A96C, 0x00009D,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+	0x284402, 0x001810, 0x28143A, 0x0C008D,
+	0x000820, 0x0002FD, 0x018040, 0x220007,
+	0x003904, 0x225886, 0x001E0D, 0x00057D,
+	0x018042, 0x20000A, 0x020924, 0x0000A5,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x402A86, 0x000007, 0x280502, 0x280C02,
+	0x002010, 0x28143A, 0x0C010D, 0x000820,
+	0x0002FD, 0x018040, 0x225A06, 0x220007,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+#endif	//_HWMCODE_
+
+
diff -Nru linux/sound/oss/CHANGELOG linux-2.4.19-pre5-mjc/sound/oss/CHANGELOG
--- linux/sound/oss/CHANGELOG	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/CHANGELOG	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,369 @@
+Note these changes relate to Hannu's code and don't include the changes
+made outside of this for modularising the sound
+
+Changelog for version 3.8o
+--------------------------
+
+Since 3.8h
+- Included support for OPL3-SA1 and SoftOSS
+
+Since 3.8
+- Fixed SNDCTL_DSP_GETOSPACE
+- Compatibility fixes for Linux 2.1.47
+
+Since 3.8-beta21
+- Fixed all known bugs (I think).
+
+Since 3.8-beta8
+- Lot of fixes to audio playback code in dmabuf.c
+
+Since 3.8-beta6
+- Fixed the famous Quake delay bug.
+
+Since 3.8-beta5
+- Fixed many bugs in audio playback.
+
+Since 3.8-beta4
+- Just minor changes.
+
+Since 3.8-beta1
+- Major rewrite of audio playback handling.
+- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
+
+Since 3.7-beta#
+- Passing of ioctl() parameters between soundcard.c and other modules has been
+changed so that arg always points to kernel space.
+- Some bugfixes.
+
+Since 3.7-beta5
+- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
+stream of received 0x00 bytes when the MIDI receiver is enabled.
+
+Since 3.5
+- Changes almost everywhere.
+- Support for OPTi 82C924-based sound cards.
+
+Since 3.5.4-beta8
+- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
+  with GUS.
+- Limited minimum fragment size with some audio devices (GUS=512 and
+  SB=32). These devices require more time to "recover" from processing
+  of each fragment. 
+
+Since 3.5.4-beta6/7
+- There seems to be problems in the OPTi 82C930 so cards based on this
+  chip don't necessarily work yet. There are problems in detecting the 
+  MIDI interface. Also mixer volumes may be seriously wrong on some systems.
+  You can safely use this driver version with C930 if it looks to work.
+  However please don't complain if you have problems with it. C930 support
+  should be fixed in future releases.
+- Got initialization of GUS PnP to work. With this version GUS PnP should
+  work in GUS compatible mode after initialization using isapnptools.
+- Fixed a bug in handling of full duplex cards in write only mode. This has
+  been causing "audio device opening" errors with RealAudio player.
+
+Since 3.5.4.beta5
+- Changes to OPTi 82C930 driver.
+- Major changes to the Soundscape driver. The driver requires now just one
+  DMA channel. The extra audio/dsp device (the "Not functional" one) used
+  for code download in the earlier versions has been eliminated. There is now
+  just one /dev/dsp# device which is used both for code download and audio.
+
+Since 3.5.4.beta4
+- Minor changes.
+
+Since 3.5.4-beta2
+- Fixed silent playback with ESS 688/1688.
+- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one
+  is required for 8 and 16 bit modes).
+- Added the "lowlevel" subdirectory for additional low level drivers that
+  are not part of USS core. See lowlevel/README for more info.
+- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
+  miroPCM sound cards. See lowlevel/aci.readme for more info.
+- Support for Aztech Washington chipset (AZT2316 ASIC).
+
+Since 3.5.4-beta1
+- Reduced clicking with AD1848.
+- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback
+  is sometimes just white noise (occurs randomly).
+
+Since 3.5.2
+- Major changes to the SB/Jazz16/ESS driver (most parts rewritten).
+  The most noticeable new feature is support for multiple SB cards at the same
+  time.
+- Renamed sb16_midi.c to uart401.c. Also modified it to work also with
+  other MPU401 UART compatible cards than SB16/ESS/Jazz.
+- Some changes which reduce clicking in audio playback.
+- Copying policy is now GPL.
+
+Since 3.5.1
+- TB Maui initialization support
+Since 3.5
+- Improved handling of playback underrun situations.
+
+Since 3.5-beta10
+- Bug fixing
+
+Since 3.5-beta9
+- Fixed for compatibility with Linux 1.3.70 and later.
+- Changed boot time passing of 16 bit DMA channel number to SB driver.
+
+Since 3.5-beta8
+- Minor changes
+
+Since 3.5-beta7
+- enhancements to configure program (by Jeff Tranter):
+  - prompts are in same format as 1.3.x Linux kernel config program
+  - on-line help for each question
+  - fixed some compile warnings detected by gcc/g++ -Wall
+  - minor grammatical changes to prompts
+
+Since 3.5-beta6
+- Fixed bugs in mmap() support.
+- Minor changes to Maui driver.
+
+Since 3.5-beta5
+- Fixed crash after recording with ESS688. It's generally a good
+  idea to stop inbound DMA transfers before freeing the memory
+  buffer. 
+- Fixed handling of AD1845 codec (for example Shuttle Sound System).
+- Few other fixes.
+
+Since 3.5-beta4
+- Fixed bug in handling of uninitialized instruments with GUS.
+
+Since 3.5-beta3
+- Few changes which decrease popping at end/beginning of audio playback.
+
+Since 3.5-beta2
+- Removed MAD16+CS4231 hack made in previous version since it didn't
+  help.
+- Fixed the above bug in proper way and in proper place. Many thanks
+  to James Hightower.
+
+Since 3.5-beta1
+- Bug fixes.
+- Full duplex audio with MAD16+CS4231 may work now. The driver configures
+  SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels.
+  The side effect is that all 8 bit DMA channels (0,1,3) are populated in 
+  duplex mode.
+
+Since 3.5-alpha9
+- Bug fixes (mostly in Jazz16 and ESS1688/688 supports).
+- Temporarily disabled recording with ESS1688/688 since it causes crash.
+- Changed audio buffer partitioning algorithm so that it selects
+  smaller fragment size than earlier. This improves real time capabilities
+  of the driver and makes recording to disk to work better. Unfortunately
+  this change breaks some programs which assume that fragments cannot be
+  shorter than 4096 bytes.
+
+Since 3.5-alpha8
+- Bug fixes
+
+Since 3.5-alpha7
+- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable
+  using command "cd /linux/drivers/sound;make script" and then
+  just run kernel's make config normally.
+- Minor fixes to the SB support. Hopefully the driver works with
+  all SB models now.
+- Added support for ESS ES1688 "AudioDrive" based cards.
+
+Since 3.5-alpha6
+- SB Pro and SB16 supports are no longer separately selectable options.
+  Enabling SB enables them too.
+- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified
+configure to handle this. 
+- Removed initialization messages from the
+modularized version. They can be enabled by using init_trace=1 in
+the insmod command line (insmod sound init_trace=1).
+- More AIX stuff.
+- Added support for synchronizing dsp/audio devices with /dev/sequencer.
+- mmap() support for dsp/audio devices.
+
+Since 3.5-alpha5
+- AIX port.
+- Changed some xxx_PATCH macros in soundcard.h to work with
+  big endian machines.
+
+Since 3.5-alpha4
+- Removed the 'setfx' stuff from the version distributed with kernel
+  sources. Running 'setfx' is required again.
+
+Since 3.5-alpha3
+- Moved stuff from the 'setfx' program to the AudioTrix Pro driver.
+
+Since 3.5-alpha2
+- Modifications to makefile and configure.c. Unnecessary sources
+  are no longer compiled. Newly created local.h is also copied to
+  /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
+  new local.h which is compatible with current version of the driver.
+- Some fixes to the SB16 support.
+- Fixed random protection fault in gus_wave.c
+
+Since 3.5-alpha1
+- Modified to work with Linux-1.3.33 and later
+- Some minor changes
+
+Since 3.0.2
+- Support for CS4232 based PnP cards (AcerMagic S23 etc).
+- Full duplex support for some CS4231, CS4232 and AD1845 based cards
+(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards
+having a codec mentioned above).
+- Almost fully rewritten loadable modules support.
+- Fixed some bugs.
+- Huge amount of testing (more testing is still required).
+- mmap() support (works with some cards). Requires much more testing.
+- Sample/patch/program loading for TB Maui/Tropez. No initialization
+since TB doesn't allow me to release that code.
+- Using CS4231 compatible codecs as timer for /dev/music.
+
+Since 3.0.1
+- Added allocation of I/O ports, DMA channels and interrupts
+to the initialization code. This may break modules support since
+the driver may not free some resources on unload. Should be fixed soon.
+
+Since 3.0
+- Some important bug fixes. 
+- select() for /dev/dsp and /dev/audio (Linux only).
+(To use select() with read, you have to call read() to start
+the recording. Calling write() kills recording immediately so
+use select() carefully when you are writing a half duplex app.
+Full duplex mode is not implemented yet.) Select works also with
+/dev/sequencer and /dev/music. Maybe with /dev/midi## too.
+
+Since 3.0-beta2
+- Minor fixes.
+- Added Readme.cards
+
+Since 3.0-beta1
+- Minor fixes to the modules support.
+- Eliminated call to sb_free_irq() in ad1848.c
+- Rewritten MAD16&Mozart support (not tested with MAD16 Pro).
+- Fix to DMA initialization of PSS cards.
+- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.)
+- Fixed some bugs in the PSS driver which caused I/O errors with
+  the MSS mode (/dev/dsp).
+
+Since 3.0-950506
+- Recording with GUS MAX fixed. It works when the driver is configured
+  to use two DMA channels with GUS MAX (16 bit ones recommended).
+
+Since 3.0-94xxxx
+- Too many changes
+
+Since 3.0-940818
+- Fixes for Linux 1.1.4x.
+- Disables Disney Sound System with SG NX Pro 16 (less noise).
+
+Since 2.90-2
+- Fixes to soundcard.h
+- Non blocking mode to /dev/sequencer
+- Experimental detection code for Ensoniq Soundscape.
+
+Since 2.90
+- Minor and major bug fixes
+
+Since pre-3.0-940712
+- GUS MAX support
+- Partially working MSS/WSS support (could work with some cards).
+- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
+  (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
+  GUS MAX, but it doesn't work yet.
+Since pre-3.0-940426
+- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
+This codec chip is used in various sound cards. This version is developed
+for the 16 bit daughtercard of GUS. It should work with other cards also
+if the following requirements are met:
+	- The I/O, IRQ and DMA settings are jumper selectable or
+	the card is initialized by booting DOS before booting Linux (etc.).
+	- You add the IO, IRQ and DMA settings manually to the local.h.
+	  (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
+	the base address bust be the base address of the codec chip not the
+	card itself. For the GUS16 these are the same but most MSS compatible
+	cards have the codec located at card_base+4.
+- Some minor changes
+
+Since 2.5 (******* MAJOR REWRITE ***********)
+
+This version is based on v2.3. I have tried to maintain two versions
+together so that this one should have the same features than v2.5.
+Something may still be missing. If you notice such things, please let me
+know.
+
+The Readme.v30 contains more details.
+
+- /dev/midi## devices.
+- /dev/sequencer2
+
+Since 2.5-beta2
+- Some fine tuning to the GUS v3.7 mixer code.
+- Fixed speed limits for the plain SB (1.0 to 2.0).
+
+Since 2.5-beta
+- Fixed OPL-3 detection with SB. Caused problems with PAS16.
+- GUS v3.7 mixer support.
+
+Since 2.4
+- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
+- Fixed truncated sound on /dev/dsp when the device is closed.
+- Linear volume mode for GUS
+- Pitch bends larger than +/- 2 octaves.
+- MIDI recording for SB and SB Pro. (Untested).
+- Some other fixes.
+- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
+- Implemented better detection for OPL-3. This should be useful if you
+  have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
+- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
+
+Since 2.3b
+- Fixed bug which made it impossible to make long recordings to disk.
+  Recording was not restarted after a buffer overflow situation.
+- Limited mixer support for GUS.
+- Numerous improvements to the GUS driver by Andrew Robinson. Including
+  some click removal etc.
+
+Since 2.3
+- Fixed some minor bugs in the SB16 driver.
+
+Since 2.2b
+- Full SB16 DSP support. 8/16 bit, mono/stereo
+- The SCO and FreeBSD versions should be in sync now. There are some
+  problems with SB16 and GUS in the FreeBSD versions.
+  The DMA buffer allocation of the SCO version has been polished but
+  there could still be some problems. At least it hogs memory.
+  The DMA channel
+  configuration method used in the SCO/System is a hack.
+- Support for the MPU emulation of the SB16.
+- Some big arrays are now allocated boot time. This makes the BSS segment
+  smaller which makes it possible to use the full driver with
+  NetBSD. These arrays are not allocated if no suitable sound card is available.
+- Fixed a bug in the compute_and_set_volume in gus_wave.c
+- Fixed the too fast mono playback problem of SB Pro and PAS16.
+
+Since 2.2
+- Stereo recording for SB Pro. Somehow it was missing and nobody
+  had noticed it earlier.
+- Minor polishing.
+- Interpreting of boot time arguments (sound=) for Linux.
+- Breakup of sb_dsp.c. Parts of the code has been moved to
+  sb_mixer.c and sb_midi.c
+
+Since 2.1
+- Preliminary support for SB16. 
+  - The SB16 mixer is supported in its native mode.
+  - Digitized voice capability up to 44.1 kHz/8 bit/mono
+    (16 bit and stereo support coming in the next release).
+- Fixed some bugs in the digitized voice driver for PAS16.
+- Proper initialization of the SB emulation of latest PAS16 models.
+
+- Significantly improved /dev/dsp and /dev/audio support.
+  - Now supports half duplex mode. It's now possible to record and
+    playback without closing and reopening the device.
+  - It's possible to use smaller buffers than earlier. There is a new
+    ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
+    This call instructs the driver to use smaller buffers. The default
+    buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
+    immediately after opening the device.
+
+Since 2.0
+Just cosmetic changes. 
diff -Nru linux/sound/oss/COPYING linux-2.4.19-pre5-mjc/sound/oss/COPYING
--- linux/sound/oss/COPYING	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/COPYING	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff -Nru linux/sound/oss/Config.help linux-2.4.19-pre5-mjc/sound/oss/Config.help
--- linux/sound/oss/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/Config.help	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,723 @@
+CONFIG_INPUT_GAMEPORT
+  Gameport support is for the standard 15-pin PC gameport.  If you
+  have a joystick, gamepad, gameport card, a soundcard with a gameport
+  or anything else that uses the gameport, say Y or M here and also to
+  at least one of the hardware specific drivers.
+  Please read the file <file:Documentation/input/joystick.txt> which
+  contains more information and the location of the joystick package
+  that you'll need if you use the gameport with a joystick.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called gameport.o.  If you want to compile it as
+  a module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_SOUND_ALSA_OSS
+  OSS is the Open Sound System suite of sound card drivers.  They make
+  sound programming easier since they provide a common API.  Say Y or
+  M here (the module will be called sound.o) if you haven't found a
+  driver for your sound card above, then pick your driver from the
+  list below.
+
+CONFIG_SOUND_ALSA_DMAP
+  Linux can often have problems allocating DMA buffers for ISA sound
+  cards on machines with more than 16MB of RAM. This is because ISA
+  DMA buffers must exist below the 16MB boundary and it is quite
+  possible that a large enough free block in this region cannot be
+  found after the machine has been running for a while. If you say Y
+  here the DMA buffers (64Kb) will be allocated at boot time and kept
+  until the shutdown. This option is only useful if you said Y to
+  "OSS sound modules", above. If you said M to "OSS sound modules"
+  then you can get the persistent DMA buffer functionality by passing
+  the command-line argument "dmabuf=1" to the sound.o module.
+
+  Say Y unless you have 16MB or more RAM or a PCI sound card.
+
+CONFIG_SOUND_ALSA_SGALAXY
+  This module initializes the older non Plug and Play sound galaxy
+  cards from Aztech. It supports the Waverider Pro 32 - 3D and the
+  Galaxy Washington 16.
+
+  If you compile the driver into the kernel, you have to add
+  "sgalaxy=<io>,<irq>,<dma>,<dma2>,<sgbase>" to the kernel command
+  line.
+
+CONFIG_SOUND_ALSA_AD1816
+  Say M here if you have a sound card based on the Analog Devices
+  AD1816(A) chip.
+
+  If you compile the driver into the kernel, you have to add
+  "ad1816=<io>,<irq>,<dma>,<dma2>" to the kernel command line.
+
+CONFIG_SOUND_ALSA_OPL3SA1
+  Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is
+  usually built into motherboards. Read
+  <file:Documentation/sound/OPL3-SA> for details.
+
+  If you compile the driver into the kernel, you have to add
+  "opl3sa=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the kernel
+  command line.
+
+CONFIG_SOUND_ALSA_PAS
+  Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
+  16 or Logitech SoundMan 16 sound card. Answer N if you have some
+  other card made by Media Vision or Logitech since those are not
+  PAS16 compatible. Please read <file:Documentation/sound/PAS16>.
+  It is not necessary to add Sound Blaster support separately; it
+  is included in PAS support.
+
+  If you compile the driver into the kernel, you have to add
+  "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2>
+  to the kernel command line.
+
+CONFIG_PAS_JOYSTICK
+  Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick
+  port.
+
+CONFIG_SOUND_ALSA_SB
+  Answer Y if you have an original Sound Blaster card made by Creative
+  Labs or a 100% hardware compatible clone (like the Thunderboard or
+  SM Games). For an unknown card you may answer Y if the card claims
+  to be Sound Blaster-compatible.
+
+  Please read the file <file:Documentation/sound/Soundblaster>.
+
+  You should also say Y here for cards based on the Avance Logic
+  ALS-007 and ALS-1X0 chips (read <file:Documentation/sound/ALS>) and
+  for cards based on ESS chips (read
+  <file:Documentation/sound/ESS1868> and
+  <file:Documentation/sound/ESS>). If you have an SB AWE 32 or SB AWE
+  64, say Y here and also to "AWE32 synth" below and read
+  <file:Documentation/sound/INSTALL.awe>. If you have an IBM Mwave
+  card, say Y here and read <file:Documentation/sound/mwave>.
+
+  If you compile the driver into the kernel and don't want to use
+  isapnp, you have to add "sb=<io>,<irq>,<dma>,<dma2>" to the kernel
+  command line.
+
+  You can say M here to compile this driver as a module; the module is
+  called sb.o.
+
+CONFIG_SOUND_ALSA_GUS
+  Say Y here for any type of Gravis Ultrasound card, including the GUS
+  or GUS MAX.  See also <file:Documentation/sound/ultrasound> for more
+  information on configuring this card with modules.
+
+  If you compile the driver into the kernel, you have to add
+  "gus=<io>,<irq>,<dma>,<dma2>" to the kernel command line.
+
+CONFIG_SOUND_ALSA_MPU401
+  Be careful with this question.  The MPU401 interface is supported by
+  all sound cards.  However, some natively supported cards have their
+  own driver for MPU401.  Enabling this MPU401 option with these cards
+  will cause a conflict.  Also, enabling MPU401 on a system that
+  doesn't really have a MPU401 could cause some trouble.  If your card
+  was in the list of supported cards, look at the card specific
+  instructions in the <file:Documentation/sound/README.OSS> file.  It
+  is safe to answer Y if you have a true MPU401 MIDI interface card.
+
+  If you compile the driver into the kernel, you have to add
+  "mpu401=<io>,<irq>" to the kernel command line.
+
+CONFIG_SOUND_ALSA_UART6850
+  This option enables support for MIDI interfaces based on the 6850
+  UART chip. This interface is rarely found on sound cards. It's safe
+  to answer N to this question.
+
+  If you compile the driver into the kernel, you have to add
+  "uart6850=<io>,<irq>" to the kernel command line.
+
+CONFIG_SOUND_ALSA_PSS
+  Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
+  ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
+  ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on
+  how to compile it into the kernel or as a module see the file
+  <file:Documentation/sound/PSS>.
+
+  If you compile the driver into the kernel, you have to add
+  "pss=<io>,<mssio>,<mssirq>,<mssdma>,<mpuio>,<mpuirq>" to the kernel
+  command line.
+
+CONFIG_PSS_MIXER
+  Answer Y for Beethoven ADSP-16. You may try to say Y also for other
+  cards if they have master volume, bass, treble, and you can't
+  control it under Linux. If you answer N for Beethoven ADSP-16, you
+  can't control master volume, bass, treble and synth volume.
+
+  If you said M to "PSS support" above, you may enable or disable this
+  PSS mixer with the module parameter pss_mixer. For more information
+  see the file <file:Documentation/sound/PSS>.
+
+CONFIG_PSS_HAVE_BOOT
+  If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y
+  to include this file. Without this file the synth device (OPL) may
+  not work.
+
+CONFIG_PSS_BOOT_FILE
+  Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file,
+  starting from /.
+
+CONFIG_SOUND_ALSA_MSS
+  Again think carefully before answering Y to this question.  It's
+  safe to answer Y if you have the original Windows Sound System card
+  made by Microsoft or Aztech SG 16 Pro (or NX16 Pro).  Also you may
+  say Y in case your card is NOT among these:
+
+     ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16,
+     Ensoniq SoundScape (and compatibles made by Reveal and Spea),
+     Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max,
+     Gravis Ultrasound with 16 bit option, Logitech Sound Man 16,
+     Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi
+     82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft
+     Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid
+     SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro
+     Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface,
+     Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound
+     Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M
+     notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM
+     synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface.
+
+  For cards having native support in VoxWare, consult the card
+  specific instructions in <file:Documentation/sound/README.OSS>.
+  Some drivers have their own MSS support and saying Y to this option
+  will cause a conflict.
+
+  If you compile the driver into the kernel, you have to add
+  "ad1848=<io>,<irq>,<dma>,<dma2>[,<type>]" to the kernel command
+  line.
+
+CONFIG_SOUND_ALSA_VWSND
+  Say Y or M if you have an SGI Visual Workstation and you want to be
+  able to use its on-board audio.  Read
+  <file:Documentation/sound/vwsnd> for more info on this driver's
+  capabilities.
+
+CONFIG_SOUND_ALSA_SSCAPE
+  Answer Y if you have a sound card based on the Ensoniq SoundScape
+  chipset. Such cards are being manufactured at least by Ensoniq, Spea
+  and Reveal (Reveal makes also other cards).
+
+  If you compile the driver into the kernel, you have to add
+  "sscape=<io>,<irq>,<dma>,<mpuio>,<mpuirq>" to the kernel command
+  line.
+
+CONFIG_SOUND_ALSA_TRIX
+  Answer Y if you have the AudioTriX Pro sound card manufactured
+  by MediaTrix.
+
+CONFIG_TRIX_HAVE_BOOT
+  The MediaTrix AudioTrix Pro has an on-board microcontroller which
+  needs to be initialized by downloading the code from the file
+  TRXPRO.HEX in the DOS driver directory. If you don't have the
+  TRXPRO.HEX file handy you may skip this step. However, the SB and
+  MPU-401 modes of AudioTrix Pro will not work without this file!
+
+CONFIG_TRIX_BOOT_FILE
+  Enter the full pathname of your TRXPRO.HEX file, starting from /.
+
+CONFIG_SOUND_ALSA_MAD16
+  Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi
+  82C928 or 82C929 or 82C931) audio interface chip. These chips are
+  quite common so it's possible that many no-name cards have one of
+  them. In addition the MAD16 chip is used in some cards made by known
+  manufacturers such as Turtle Beach (Tropez), Reveal (some models)
+  and Diamond (latest ones). Note however that the Tropez sound cards
+  have their own driver; if you have one of those, say N here and Y or
+  M to "Full support for Turtle Beach WaveFront", below.
+
+  If you compile the driver into the kernel, you have to add
+  "mad16=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the
+  kernel command line.
+
+  See also <file:Documentation/sound/Opti> and
+  <file:Documentation/sound/MAD16> for more information on setting
+  these cards up as modules.
+
+CONFIG_SOUND_ALSA_WAVEFRONT
+  Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card
+  and read the files <file:Documentation/sound/Wavefront> and
+  <file:Documentation/sound/Tropez+>.
+
+CONFIG_MAD16_OLDCARD
+  Answer Y (or M) if you have an older card based on the C928 or
+  Mozart chipset and you want to have MIDI support. If you enable this
+  option you also need to enable support for Sound Blaster.
+
+CONFIG_SOUND_ALSA_CS4232
+  Say Y here if you have a card based on the Crystal CS4232 chip set,
+  which uses its own Plug and Play protocol.
+
+  If you compile the driver into the kernel, you have to add
+  "cs4232=<io>,<irq>,<dma>,<dma2>,<mpuio>,<mpuirq>" to the kernel
+  command line.
+
+  See <file:Documentation/sound/CS4232> for more information on
+  configuring this card.
+
+CONFIG_SOUND_ALSA_OPL3SA2
+  Say Y or M if you have a card based on one of these Yamaha sound
+  chipsets or the "SAx", which is actually a SA3. Read
+  <file:Documentation/sound/OPL3-SA2> for more information on
+  configuring these cards.
+
+  If you compile the driver into the kernel and do not also
+  configure in the optional ISA PnP support, you will have to add
+  "opl3sa2=<io>,<irq>,<dma>,<dma2>,<mssio>,<mpuio>" to the kernel
+  command line.
+
+CONFIG_SOUND_ALSA_MAUI
+  Say Y here if you have a Turtle Beach Wave Front, Maui, or Tropez
+  sound card.
+
+  If you compile the driver into the kernel, you have to add
+  "maui=<io>,<irq>" to the kernel command line.
+
+CONFIG_MAUI_HAVE_BOOT
+  Turtle Beach Maui and Tropez sound cards have a microcontroller
+  which needs to be initialized prior to use. OSWF.MOT is a file
+  distributed with the card's DOS/Windows drivers. Answer Y if you
+  have this file.
+
+CONFIG_MAUI_BOOT_FILE
+  Enter the full pathname of your OSWF.MOT file, starting from /.
+
+CONFIG_SOUND_ALSA_MSNDCLAS
+  Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
+  Monterey (not for the Pinnacle or Fiji).
+
+  See <file:Documentation/sound/MultiSound> for important information
+  about this driver.  Note that it has been discontinued, but the
+  Voyetra Turtle Beach knowledge base entry for it is still available
+  at <http://www.voyetra-turtle-beach.com/site/kb_ftp/790.asp>.
+
+CONFIG_MSNDCLAS_IO
+  I/O port address for the MultiSound Classic and related cards.
+
+CONFIG_MSNDCLAS_IRQ
+  Interrupt Request line for the MultiSound Classic and related cards.
+
+CONFIG_MSNDCLAS_MEM
+  Memory-mapped I/O base address for the MultiSound Classic and
+  related cards.
+
+CONFIG_MSNDCLAS_INIT_FILE
+  The MultiSound cards have two firmware files which are required for
+  operation, and are not currently included. These files can be
+  obtained from Turtle Beach. See
+  <file:Documentation/sound/MultiSound> for information on how to
+  obtain this.
+
+CONFIG_MSNDCLAS_PERM_FILE
+  The MultiSound cards have two firmware files which are required for
+  operation, and are not currently included. These files can be
+  obtained from Turtle Beach. See
+  <file:Documentation/sound/MultiSound> for information on how to
+  obtain this.
+
+CONFIG_SOUND_ALSA_MSNDPIN
+  Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
+  See <file:Documentation/sound/MultiSound> for important information
+  about this driver. Note that it has been discontinued, but the
+  Voyetra Turtle Beach knowledge base entry for it is still available
+  at <http://www.voyetra-turtle-beach.com/site/kb_ftp/600.asp>.
+
+CONFIG_MSNDPIN_IDE_IO0
+  CD-ROM drive 0 memory-mapped I/O base address for the MultiSound
+  Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_IDE_IO1
+  CD-ROM drive 1 memory-mapped I/O base address for the MultiSound
+  Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_IDE_IRQ
+  Interrupt request number for the IDE CD-ROM interface on the
+  MultiSound Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_IO
+  Memory-mapped I/O base address for the primary synthesizer on
+  MultiSound Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_MPU_IO
+  Memory-mapped I/O base address for the Kurzweil daughterboard
+  synthesizer on MultiSound Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_MPU_IRQ
+  Iinterrupt request number for the Kurzweil daughterboard
+  synthesizer on MultiSound Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_IRQ
+  Interrupt request line for the primary synthesizer on MultiSound
+  Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_JOYSTICK_IO
+  Memory-mapped I/O base address for the joystick port on MultiSound
+  Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_MEM
+  Memory-mapped I/O base address for the primary synthesizer on
+  MultiSound Pinnacle and Fiji sound cards.
+
+CONFIG_MSNDPIN_INIT_FILE
+  The MultiSound cards have two firmware files which are required
+  for operation, and are not currently included. These files can be
+  obtained from Turtle Beach. See
+  <file:Documentation/sound/MultiSound> for information on how to
+  obtain this.
+
+CONFIG_MSNDPIN_PERM_FILE
+  The MultiSound cards have two firmware files which are required for
+  operation, and are not currently included. These files can be
+  obtained from Turtle Beach. See
+  <file:Documentation/sound/MultiSound> for information on how to
+  obtain this.
+
+CONFIG_MSNDPIN_DIGITAL
+  If you have the S/PDIF daughter board for the Pinnacle or Fiji,
+  answer Y here; otherwise, say N. If you have this, you will be able
+  to play and record from the S/PDIF port (digital signal). See
+  <file:Documentation/sound/MultiSound> for information on how to make
+  use of this capability.
+
+CONFIG_MSNDPIN_NONPNP
+  The Pinnacle and Fiji card resources can be configured either with
+  PnP, or through a configuration port. Say Y here if your card is NOT
+  in PnP mode. For the Pinnacle, configuration in non-PnP mode allows
+  use of the IDE and joystick peripherals on the card as well; these
+  do not show up when the card is in PnP mode. Specifying zero for any
+  resource of a device will disable the device. If you are running the
+  card in PnP mode, you must say N here and use isapnptools to
+  configure the card's resources.
+
+CONFIG_MSNDPIN_CFG
+  This is the port which the Pinnacle and Fiji uses to configure the
+  card's resources when not in PnP mode. If your card is in PnP mode,
+  then be sure to say N to the previous option, "MSND Pinnacle Non-PnP
+  Mode".
+
+CONFIG_MSND_FIFOSIZE
+  Configures the size of each audio buffer, in kilobytes, for
+  recording and playing in the MultiSound drivers (both the Classic
+  and Pinnacle). Larger values reduce the chance of data overruns at
+  the expense of overall latency. If unsure, use the default.
+
+CONFIG_SOUND_ALSA_YM3812
+  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
+  Answering Y is usually a safe and recommended choice, however some
+  cards may have software (TSR) FM emulation. Enabling FM support with
+  these cards may cause trouble (I don't currently know of any such
+  cards, however). Please read the file
+  <file:Documentation/sound/OPL3> if your card has an OPL3 chip.
+
+  If you compile the driver into the kernel, you have to add
+  "opl3=<io>" to the kernel command line.
+
+  If unsure, say Y.
+
+CONFIG_SOUND_ALSA_ACI_MIXER
+  ACI (Audio Command Interface) is a protocol used to communicate with
+  the microcontroller on some sound cards produced by miro and
+  Cardinal Technologies.  The main function of the ACI is to control
+  the mixer and to get a product identification.
+
+  This VoxWare ACI driver currently supports the ACI functions on the
+  miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI
+  also controls the radio tuner. This is supported in the video4linux
+  miropcm20 driver (say M or Y here and go back to "Multimedia
+  devices" -> "Radio Adapters").
+
+  This driver is also available as a module and will be called aci.o.
+
+CONFIG_SOUND_ALSA_AWE32_SYNTH
+  Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or
+  similar sound card. See <file:Documentation/sound/README.awe>,
+  <file:Documentation/sound/AWE32> and the Soundblaster-AWE
+  mini-HOWTO, available from <http://www.linuxdoc.org/docs.html#howto>
+  for more info.
+
+CONFIG_SOUND_ALSA_AEDSP16
+  Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
+  driver supports Audio Excel DSP 16 but not the III nor PnP versions
+  of this card.
+
+  The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or
+  a Microsoft Sound System card, so you should have said Y to either
+  "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
+  or "Microsoft Sound System support", above, and you need to answer
+  the "MSS emulation" and "SBPro emulation" questions below
+  accordingly. You should say Y to one and only one of these two
+  questions.
+
+  Read the <file:Documentation/sound/README.OSS> file and the head of
+  <file:drivers/sound/aedsp16.c> as well as
+  <file:Documentation/sound/AudioExcelDSP16> to get more information
+  about this driver and its configuration.
+
+CONFIG_AEDSP16_SBPRO
+  Answer Y if you want your audio card to emulate Sound Blaster Pro.
+  You should then say Y to "100% Sound Blaster compatibles
+  (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS
+  emulation)".
+
+  If you compile the driver into the kernel, you have to add
+  "aedsp16=<io>,<irq>,<dma>,<mssio>,<mpuio>,<mouirq>" to the kernel
+  command line.
+
+CONFIG_AEDSP16_MSS
+  Answer Y if you want your audio card to emulate Microsoft Sound
+  System. You should then say Y to "Microsoft Sound System support"
+  and say N to "Audio Excel DSP 16 (SBPro emulation)".
+
+CONFIG_SC6600
+  The SC6600 is the new version of DSP mounted on the Audio Excel DSP
+  16 cards. Find in the manual the FCC ID of your audio card and
+  answer Y if you have an SC6600 DSP.
+
+CONFIG_SC6600_JOY
+  Say Y here in order to use the joystick interface of the Audio Excel
+  DSP 16 card.
+
+CONFIG_SC6600_CDROMBASE
+  Base I/O port address for the CD-ROM interface of the Audio Excel
+  DSP 16 card.
+
+CONFIG_AEDSP16_MPU401
+  Answer Y if you want your audio card to emulate the MPU-401 midi
+  interface. You should then also say Y to "MPU-401 support".
+
+  Note that the I/O base for MPU-401 support of aedsp16 is the same
+  you have selected for "MPU-401 support". If you are using this
+  driver as a module you have to specify the MPU I/O base address with
+  the parameter 'mpu_base=0xNNN'.
+
+CONFIG_SOUND_ALSA_CMPCI
+  Say Y or M if you have a PCI sound card using the CMI8338
+  or the CMI8378 chipset.  Data on these chips are available at
+  <http://www.cmedia.com.tw/>.
+
+  A userspace utility to control some internal registers of these
+  chips is available at
+  <http://member.nifty.ne.jp/Breeze/softwares/unix/cmictl-e.html>.
+
+CONFIG_SOUND_ALSA_CMPCI_CM8738
+  Say Y or M if you have a PCI sound card using the CMI8338
+  or the CMI8378 chipset.  Data on this chip is available at
+  <http://www.cmedia.com.tw/doc8738.htm>.
+
+  A userspace utility to control some internal registers of these
+  chips is available at
+  <http://member.nifty.ne.jp/Breeze/softwares/unix/cmictl-e.html>.
+
+CONFIG_SOUND_ALSA_CMPCI_JOYSTICK
+  Say here in order to enable the joystick port on a sound crd using
+  the CMI8338 or the CMI8738 chipset.  Data on these chips are
+  available at <http://www.cmedia.com.tw/>.
+
+CONFIG_SOUND_ALSA_CMPCI_SPEAKERS
+  Specify the number of speaker channels you want the card to drive,
+  as an integer.
+
+CONFIG_SOUND_ALSA_CMPCI_SPDIFLOOP
+  Enable loopback from SPDIF in to SPDIF out.  For discussion, see
+  "The 8738 Audio SPDIF In/Out Technical Data" on the technical
+  support page at <http://www.cmedia.com.tw/>.
+
+  A userspace utility to control even more internal registers of these
+  chips is available at
+  <http://member.nifty.ne.jp/Breeze/softwares/unix/cmictl-e.html>.
+  This package will among other things help you enable SPDIF 
+  out/in/loop/monitor.
+
+CONFIG_SOUND_ALSA_EMU10K1
+  Say Y or M if you have a PCI sound card using the EMU10K1 chipset,
+  such as the Creative SBLive!, SB PCI512 or Emu-APS.
+
+  For more information on this driver and the degree of support for the
+  different card models please check <http://opensource.creative.com/>.
+
+  It is now possible to load dsp microcode patches into the EMU10K1
+  chip.  These patches are used to implement real time sound
+  processing effects which include for example: signal routing,
+  bass/treble control, AC3 passthrough, ...
+  Userspace tools to create new patches and load/unload them can be
+  found at <http://opensource.creative.com/dist.html>.
+
+CONFIG_MIDI_EMU10K1
+  Say Y if you want to be able to use the OSS /dev/sequencer
+  interface.  This code is still experimental.
+
+CONFIG_SOUND_ALSA_FUSION
+  This module drives the Crystal SoundFusion devices (CS4280/46xx
+  series) when wired as native sound drivers with AC97 codecs.  If
+  this driver does not work try the CS4232 driver.
+
+CONFIG_SOUND_ALSA_ES1370
+  Say Y or M if you have a PCI sound card utilizing the Ensoniq
+  ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find
+  out if your sound card uses an ES1370 without removing your
+  computer's cover, use lspci -n and look for the PCI ID
+  1274:5000. Since Ensoniq was bought by Creative Labs,
+  Sound Blaster 64/PCI models are either ES1370 or ES1371 based.
+  This driver differs slightly from OSS/Free, so PLEASE READ
+  <file:Documentation/sound/es1370>.
+
+CONFIG_SOUND_ALSA_ES1371
+  Say Y or M if you have a PCI sound card utilizing the Ensoniq
+  ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if
+  your sound card uses an ES1371 without removing your computer's
+  cover, use lspci -n and look for the PCI ID 1274:1371. Since
+  Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI
+  models are either ES1370 or ES1371 based. This driver differs
+  slightly from OSS/Free, so PLEASE READ
+  <file:Documentation/sound/es1371>.
+
+CONFIG_SOUND_ALSA_ESSSOLO1
+  Say Y or M if you have a PCI sound card utilizing the ESS Technology
+  Solo1 chip. To find out if your sound card uses a
+  Solo1 chip without removing your computer's cover, use
+  lspci -n and look for the PCI ID 125D:1969. This driver
+  differs slightly from OSS/Free, so PLEASE READ
+  <file:Documentation/sound/solo1>.
+
+CONFIG_SOUND_ALSA_SONICVIBES
+  Say Y or M if you have a PCI sound card utilizing the S3
+  SonicVibes chipset. To find out if your sound card uses a
+  SonicVibes chip without removing your computer's cover, use
+  lspci -n and look for the PCI ID 5333:CA00. This driver
+  differs slightly from OSS/Free, so PLEASE READ
+  <file:Documentation/sound/sonicvibes>.
+
+CONFIG_SOUND_ALSA_TRIDENT
+  Say Y or M if you have a PCI sound card utilizing the Trident
+  4DWave-DX/NX chipset or your mother board chipset has SiS 7018
+  or ALi 5451 built-in. The SiS 7018 PCI Audio Core is embedded
+  in SiS960 Super South Bridge and SiS540/630 Single Chipset.
+  The ALi 5451 PCI Audio Core is embedded in ALi M1535, M1535D,
+  M1535+ or M1535D+ South Bridge.
+
+  Use lspci -n to find out if your sound card or chipset uses
+  Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands
+  for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. PCI ID
+  10B9:5451 stands for ALi5451.
+
+  This driver supports S/PDIF in/out (record/playback) for ALi 5451
+  embedded in ALi M1535+ and M1535D+. Note that they aren't all
+  enabled by default; you can enable them by saying Y to "/proc file
+  system support" and "Sysctl support", and after the /proc file
+  system has been mounted, executing the command
+
+	command			what is enabled
+
+  echo 0>/proc/ALi5451	pcm out is also set to S/PDIF out. (Default).
+
+  echo 1>/proc/ALi5451	use S/PDIF out to output pcm data.
+
+  echo 2>/proc/ALi5451	use S/PDIF out to output non-pcm data.
+                        (AC3...).
+
+  echo 3>/proc/ALi5451	record from Ac97 in(MIC, Line in...).
+                        (Default).
+
+  echo 4>/proc/ALi5451	no matter Ac97 settings, record from S/PDIF
+                        in.
+
+
+  This driver differs slightly from OSS/Free, so PLEASE READ the
+  comments at the top of <file:drivers/sound/trident.c>.
+
+CONFIG_SOUND_WAVEARTIST
+  Say Y here to include support for the Rockwell WaveArtist sound
+  system.  This driver is mainly for the NetWinder.
+
+CONFIG_SOUND_ALSA_VIA82CXXX
+  Say Y here to include support for the audio codec found on VIA
+  82Cxxx-based chips. Typically these are built into a motherboard.
+
+  DO NOT select Sound Blaster or Adlib with this driver, unless
+  you have a Sound Blaster or Adlib card in addition to your VIA
+  audio chip.
+
+CONFIG_MIDI_VIA82CXXX
+  Answer Y to use the MIDI interface of the Via686. You may need to
+  enable this in the BIOS before it will work. This is for connection
+  to external MIDI hardware, and is not required for software playback
+  of MIDI files.
+
+CONFIG_SOUND_ALSA_NM256
+  Say M here to include audio support for the NeoMagic 256AV/256ZX
+  chipsets. These are the audio chipsets found in the Sony
+  Z505S/SX/DX, some Sony F-series, and the Dell Latitude CPi and CPt
+  laptops. It includes support for an AC97-compatible mixer and an
+  apparently proprietary sound engine.
+
+  See <file:Documentation/sound/NM256> for further information.
+
+CONFIG_SOUND_ALSA_MAESTRO
+  Say Y or M if you have a sound system driven by ESS's Maestro line
+  of PCI sound chips.  These include the Maestro 1, Maestro 2, and
+  Maestro 2E.  See <file:Documentation/sound/Maestro> for more
+  details.
+
+CONFIG_SOUND_ALSA_MAESTRO3
+  Say Y or M if you have a sound system driven by ESS's Maestro 3
+  PCI sound chip.
+
+CONFIG_SOUND_ALSA_ADLIB
+  Includes ASB 64 4D. Information on programming AdLib cards is
+  available at <http://www.itsnet.com/home/ldragon/Specs/adlib.html>.
+
+CONFIG_SOUND_ALSA_CS4281
+  Picture and feature list at
+  <http://www.pcbroker.com/crystal4281.html>.
+
+CONFIG_SOUND_ALSA_GUS16
+  Support for Gravis Ulstrasound (GUS) cards (other than the GUS),
+  sampling at 16-bit width.
+
+CONFIG_SOUND_ALSA_GUSMAX
+  Support for Gravis Ulstrasound MAX.
+
+CONFIG_SOUND_ALSA_ICH
+  Support for integral audio in Intel's I/O Controller Hub (ICH)
+  chipset, as used on the 810/820/840 motherboards.
+
+CONFIG_SOUND_ALSA_TRACEINIT
+  Verbose soundcard initialization -- affects the format of autoprobe
+  and initialization messages at boot time.
+
+CONFIG_SOUND_ALSA_TVMIXER
+  Support for audio mixer facilities on the BT848 TV frame-grabber
+  card.
+
+CONFIG_SOUND_ALSA_VIDC
+  16-bit support for the VIDC onboard sound hardware found on Acorn
+  machines.
+
+CONFIG_SOUND_ALSA_VMIDI
+  Support for MIDI loopback on port 1 or 2.
+
+CONFIG_SOUND_ALSA_YMFPCI
+  Support for Yamaha cards including the YMF711, YMF715, YMF718,
+  YMF719, YMF724, Waveforce 192XG, and Waveforce 192 Digital.
+
+CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+  Support for YMF7xx PCI cards emulating an MP401.
+
+CONFIG_SOUND_ALSA_RME96XX
+  Say Y or M if you have a Hammerfall, Hammerfall light or Hammerfall
+  DSP card from RME.
+
+CONFIG_SOUND_ALSA_BT878
+  Audio DMA support for bt878 based grabber boards.  As you might have
+  already noticed, bt878 is listed with two functions in /proc/pci.
+  Function 0 does the video stuff (bt848 compatible), function 1 does
+  the same for audio data.  This is a driver for the audio part of
+  the chip.  If you say 'Y' here you get a oss-compatible dsp device
+  where you can record from.  If you want just watch TV you probably
+  don't need this driver as most TV cards handle sound with a short
+  cable from the TV card to your sound card's line-in.
+
+  This driver is available as a module called btaudio.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read <file:Documentation/modules.txt>.
+
diff -Nru linux/sound/oss/Config.in linux-2.4.19-pre5-mjc/sound/oss/Config.in
--- linux/sound/oss/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/Config.in	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,218 @@
+# drivers/sound/Config.in
+#
+# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# More hacking for modularisation.
+#
+
+# Do not probe for modem codec option. This is a workaround to prevent 
+# hard IrDA lockups on some laptops (Dell Inspiron). -jprante
+
+bool 'Do not probe for AC97 Modem Codec (prevent IrDA lockup)' CONFIG_SOUND_ALSA_NO_MODEM_PROBE
+
+# Prompt user for primary drivers.
+
+dep_tristate '  BT878 audio dma' CONFIG_SOUND_ALSA_BT878 $CONFIG_SOUND_ALSA
+dep_tristate '  C-Media PCI (CMI8338/8738)' CONFIG_SOUND_ALSA_CMPCI $CONFIG_SOUND_ALSA $CONFIG_PCI
+if [ "$CONFIG_SOUND_ALSA_CMPCI" = "y" -o "$CONFIG_SOUND_ALSA_CMPCI" = "m" ]; then
+    bool '    Enable legacy FM' CONFIG_SOUND_ALSA_CMPCI_FM
+    if [ "$CONFIG_SOUND_ALSA_CMPCI_FM" = "y" ]; then
+	define_hex CONFIG_SOUND_ALSA_CMPCI_FMIO 388
+        hex '    FM I/O 388, 3C8, 3E0, 3E8' CONFIG_SOUND_ALSA_CMPCI_FMIO 388
+    fi
+    bool '    Enable legacy MPU-401' CONFIG_SOUND_ALSA_CMPCI_MIDI
+    if [ "$CONFIG_SOUND_ALSA_CMPCI_MIDI" = "y" ]; then
+        hex '    MPU-401 I/O 330, 320, 310, 300' CONFIG_SOUND_ALSA_CMPCI_MPUIO 330
+    fi
+    bool '    Enable joystick' CONFIG_SOUND_ALSA_CMPCI_JOYSTICK
+    bool '    Support CMI8738 based audio cards' CONFIG_SOUND_ALSA_CMPCI_CM8738
+    if [ "$CONFIG_SOUND_ALSA_CMPCI_CM8738" = "y" ]; then
+	bool '      Inverse S/PDIF in for CMI8738' CONFIG_SOUND_ALSA_CMPCI_SPDIFINVERSE
+	bool '      Enable S/PDIF loop for CMI8738' CONFIG_SOUND_ALSA_CMPCI_SPDIFLOOP
+	int  '      Number of speakers 2, 4, 5, 6' CONFIG_SOUND_ALSA_CMPCI_SPEAKERS 2
+	if [ "$CONFIG_SOUND_ALSA_CMPCI_SPEAKERS" != "2" ]; then
+	    bool '        Use Line-in as Read-out' CONFIG_SOUND_ALSA_CMPCI_LINE_REAR
+	    bool '        Use Line-in as Bass' CONFIG_SOUND_ALSA_CMPCI_LINE_BASS
+	fi
+    fi
+fi
+dep_tristate '  Creative SBLive! (EMU10K1)' CONFIG_SOUND_ALSA_EMU10K1 $CONFIG_SOUND_ALSA $CONFIG_PCI
+dep_mbool    '    Creative SBLive! MIDI' CONFIG_MIDI_EMU10K1 $CONFIG_SOUND_ALSA_EMU10K1 $CONFIG_EXPERIMENTAL
+dep_tristate '  Crystal SoundFusion (CS4280/461x)' CONFIG_SOUND_ALSA_FUSION $CONFIG_SOUND_ALSA
+dep_tristate '  Crystal Sound CS4281' CONFIG_SOUND_ALSA_CS4281 $CONFIG_SOUND_ALSA
+dep_tristate '  Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ALSA_ES1370 $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_SOUND_ALSA_GAMEPORT
+dep_tristate '  Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ALSA_ES1371 $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_SOUND_ALSA_GAMEPORT
+dep_tristate '  ESS Technology Solo1' CONFIG_SOUND_ALSA_ESSSOLO1 $CONFIG_SOUND_ALSA $CONFIG_SOUND_ALSA_GAMEPORT
+dep_tristate '  ESS Maestro, Maestro2, Maestro2E driver' CONFIG_SOUND_ALSA_MAESTRO $CONFIG_SOUND_ALSA
+dep_tristate '  ESS Maestro3/Allegro driver (EXPERIMENTAL)' CONFIG_SOUND_ALSA_MAESTRO3 $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_EXPERIMENTAL
+dep_tristate '  Intel ICH (i8xx) audio support' CONFIG_SOUND_ALSA_ICH $CONFIG_PCI
+dep_tristate '  RME Hammerfall (RME96XX) support' CONFIG_SOUND_ALSA_RME96XX $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_EXPERIMENTAL
+dep_tristate '  S3 SonicVibes' CONFIG_SOUND_ALSA_SONICVIBES $CONFIG_SOUND_ALSA $CONFIG_SOUND_ALSA_GAMEPORT
+if [ "$CONFIG_VISWS" = "y" ]; then
+    dep_tristate '  SGI Visual Workstation Sound' CONFIG_SOUND_ALSA_VWSND $CONFIG_SOUND_ALSA
+fi
+if [ "$CONFIG_DDB5477" = "y" ]; then
+    dep_tristate '  NEC Vrc5477 AC97 sound' CONFIG_SOUND_ALSA_VRC5477 $CONFIG_SOUND_ALSA
+fi
+dep_tristate '  Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core' CONFIG_SOUND_ALSA_TRIDENT $CONFIG_SOUND_ALSA
+
+dep_tristate '  Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_ALSA_MSNDCLAS $CONFIG_SOUND_ALSA
+if [ "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" -o "$CONFIG_SOUND_ALSA_MSNDCLAS" = "m" ]; then
+   if [ "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" ]; then
+     comment '  Compiled-in MSND Classic support requires firmware during compilation.'
+     define_bool CONFIG_MSNDCLAS_HAVE_BOOT y
+   else
+     define_bool CONFIG_MSNDCLAS_HAVE_BOOT n
+   fi
+   string 'Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin"
+   string 'Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin"
+fi
+if [ "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" ]; then
+   int '  MSND Classic IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDCLAS_IRQ 5
+   hex '  MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDCLAS_MEM D0000
+   hex '  MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDCLAS_IO 290
+fi
+
+dep_tristate '  Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_ALSA_MSNDPIN $CONFIG_SOUND_ALSA
+if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" -o "$CONFIG_SOUND_ALSA_MSNDPIN" = "m" ]; then
+   if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" ]; then
+     comment 'Compiled-in MSND Pinnacle support requires firmware during compilation.'
+     define_bool CONFIG_MSNDPIN_HAVE_BOOT y
+   else
+     define_bool CONFIG_MSNDPIN_HAVE_BOOT n
+   fi
+   string '  Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin"
+   string '  Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin"
+fi
+if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" ]; then
+   int '    MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDPIN_IRQ 5
+   hex '    MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDPIN_MEM D0000
+   hex 'MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDPIN_IO 290
+   bool '  MSND Pinnacle has S/PDIF I/O' CONFIG_MSNDPIN_DIGITAL
+   bool '  MSND Pinnacle non-PnP Mode' CONFIG_MSNDPIN_NONPNP
+   if [ "$CONFIG_MSNDPIN_NONPNP" = "y" ]; then
+      comment 'MSND Pinnacle DSP section will be configured to above parameters.'
+      hex 'MSND Pinnacle config port 250,260,270' CONFIG_MSNDPIN_CFG 250
+      comment 'Pinnacle-specific Device Configuration (0 disables)'
+      hex 'MSND Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0
+      int 'MSND Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0
+      hex 'MSND Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0
+      hex 'MSND Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0
+      int 'MSND Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0
+      hex 'MSND Pinnacle joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0
+   fi
+fi
+if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" -o "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" ]; then
+   int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128
+fi
+
+dep_tristate '  VIA 82C686 Audio Codec' CONFIG_SOUND_ALSA_VIA82CXXX $CONFIG_PCI
+dep_mbool    '  VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_ALSA_VIA82CXXX
+
+dep_tristate '  OSS sound modules' CONFIG_SOUND_ALSA_OSS $CONFIG_SOUND_ALSA
+
+if [ "$CONFIG_SOUND_ALSA_OSS" = "y" -o "$CONFIG_SOUND_ALSA_OSS" = "m" ]; then
+   bool '      Verbose initialisation' CONFIG_SOUND_ALSA_TRACEINIT
+   bool '      Persistent DMA buffers' CONFIG_SOUND_ALSA_DMAP
+
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      dep_tristate '    AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_ALSA_AD1816 $CONFIG_SOUND_ALSA_OSS
+   fi
+   dep_tristate '    Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_ALSA_SGALAXY $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Adlib Cards' CONFIG_SOUND_ALSA_ADLIB $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)' CONFIG_SOUND_ALSA_ACI_MIXER $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Crystal CS4232 based (PnP) cards' CONFIG_SOUND_ALSA_CS4232 $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Ensoniq SoundScape support' CONFIG_SOUND_ALSA_SSCAPE $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Gravis Ultrasound support' CONFIG_SOUND_ALSA_GUS $CONFIG_SOUND_ALSA_OSS
+   if [ "$CONFIG_SOUND_ALSA_GUS" != "n" ]; then
+      bool '      16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_SOUND_ALSA_GUS16 
+      bool '      GUS MAX support' CONFIG_SOUND_ALSA_GUSMAX
+   fi
+   dep_tristate '    Loopback MIDI device support' CONFIG_SOUND_ALSA_VMIDI $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    MediaTrix AudioTrix Pro support' CONFIG_SOUND_ALSA_TRIX $CONFIG_SOUND_ALSA_OSS
+   if [ "$CONFIG_SOUND_ALSA_TRIX" = "y" ]; then
+      bool '      Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT
+      if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then
+  	string '  Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE /etc/sound/trxpro.hex
+      fi
+   fi
+  
+   dep_tristate '    Microsoft Sound System support' CONFIG_SOUND_ALSA_MSS $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    MPU-401 support (NOT for SB16)' CONFIG_SOUND_ALSA_MPU401 $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    NM256AV/NM256ZX audio support' CONFIG_SOUND_ALSA_NM256 $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_ALSA_MAD16 $CONFIG_SOUND_ALSA_OSS
+   if [ "$CONFIG_SOUND_ALSA_MAD16" = "y" -o "$CONFIG_SOUND_ALSA_MAD16" = "m" ]; then
+     bool '      Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD
+   fi
+   dep_tristate '    ProAudioSpectrum 16 support' CONFIG_SOUND_ALSA_PAS $CONFIG_SOUND_ALSA_OSS
+   dep_bool '      Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK $CONFIG_SOUND_ALSA_PAS
+
+   dep_tristate '    PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_ALSA_PSS $CONFIG_SOUND_ALSA_OSS
+   if [ "$CONFIG_SOUND_ALSA_PSS" = "y" -o "$CONFIG_SOUND_ALSA_PSS" = "m" ]; then
+      bool '      Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER
+      bool '      Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT
+      if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then
+         string '        Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE /etc/sound/dsp001.ld
+      fi
+   fi
+
+   dep_tristate '    100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_ALSA_SB $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    AWE32 synth' CONFIG_SOUND_ALSA_AWE32_SYNTH $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_ALSA_WAVEFRONT $CONFIG_SOUND_ALSA_OSS m
+   dep_tristate '    Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_ALSA_MAUI $CONFIG_SOUND_ALSA_OSS
+   if [ "$CONFIG_SOUND_ALSA_MAUI" = "y" ]; then
+      bool '      Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT
+      if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then
+  	 string '  Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE /etc/sound/oswf.mot
+      fi
+   fi
+
+   dep_tristate '    Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_ALSA_YM3812 $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_ALSA_OPL3SA1 $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Yamaha OPL3-SA2 and SA3 based PnP cards' CONFIG_SOUND_ALSA_OPL3SA2 $CONFIG_SOUND_ALSA_OSS
+   dep_tristate '    Yamaha YMF7xx PCI audio (native mode)' CONFIG_SOUND_ALSA_YMFPCI $CONFIG_SOUND_ALSA_OSS $CONFIG_PCI
+   dep_mbool '      Yamaha PCI legacy ports support' CONFIG_SOUND_ALSA_YMFPCI_LEGACY $CONFIG_SOUND_ALSA_YMFPCI
+   dep_tristate '    6850 UART support' CONFIG_SOUND_ALSA_UART6850 $CONFIG_SOUND_ALSA_OSS
+  
+   dep_tristate '    Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_SOUND_ALSA_AEDSP16 $CONFIG_SOUND_ALSA_OSS
+   if [ "$CONFIG_SOUND_ALSA_AEDSP16" = "y" -o "$CONFIG_SOUND_ALSA_AEDSP16" = "m" ]; then
+      bool '      SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         bool '        Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int '        SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4
+         hex '        SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+      if [ "$CONFIG_SOUND_ALSA_SB" = "y" -o "$CONFIG_SOUND_ALSA_SB" = "m" ]; then
+         if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then
+            bool '      Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         fi
+      fi
+      if [ "$CONFIG_SOUND_ALSA_MSS" = "y" -o "$CONFIG_SOUND_ALSA_MSS" = "m" ]; then
+         if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+            bool '      Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         fi
+      fi
+      if [ "$CONFIG_SOUND_ALSA_MPU401" = "y" -o "$CONFIG_SOUND_ALSA_MPU401" = "m" ]; then
+         bool '      Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+      fi
+   fi
+	 
+   if [ "$CONFIG_ARM" = "y" ]; then
+      if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then
+         dep_tristate '    VIDC 16-bit sound' CONFIG_SOUND_ALSA_VIDC $CONFIG_SOUND_ALSA_OSS
+      fi
+      dep_tristate '    Netwinder WaveArtist' CONFIG_SOUND_ALSA_WAVEARTIST $CONFIG_SOUND_ALSA_OSS $CONFIG_ARCH_NETWINDER
+   fi
+
+fi
+
+dep_tristate '  TV card (bt848) mixer support' CONFIG_SOUND_ALSA_TVMIXER $CONFIG_SOUND_ALSA $CONFIG_I2C
+
+# A cross directory dependence. The sound modules will need gameport.o compiled in,
+# but it resides in the drivers/char/joystick directory. This define_tristate takes
+# care of that. --Vojtech
+
+if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then
+  if [ "$CONFIG_SOUND_ALSA_ESSSOLO1" = "y" -o "$CONFIG_SOUND_ALSA_ES1370" = "y" -o "$CONFIG_SOUND_ALSA_ES1371" = "y" -o "$CONFIG_SOUND_ALSA_SONICVIBES" = "y" ]; then
+    define_tristate CONFIG_INPUT_GAMEPORT y
+  fi
+fi
diff -Nru linux/sound/oss/Hwmcode.h linux-2.4.19-pre5-mjc/sound/oss/Hwmcode.h
--- linux/sound/oss/Hwmcode.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/Hwmcode.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,804 @@
+//=============================================================================
+// Copyright (c) 1997	Yamaha Corporation.	All Rights Reserved.
+//
+//	Title:
+//		hwmcode.c
+//	Desc:
+//		micro-code for CTRL & DSP
+//	HISTORY:
+//		April 03, 1997:		1st try by M. Mukojima
+//=============================================================================
+#define	YDSXG_DSPLENGTH				0x0080
+#define	YDSXG_CTRLLENGTH			0x3000
+
+
+static unsigned long int	gdwDSPCode[YDSXG_DSPLENGTH >> 2] = {
+	0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+	0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+	0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+	0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+
+// --------------------------------------------
+//  DS-1E Controller InstructionRAM Code
+//	1999/06/21
+//	Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09?@creat
+// 04/12  stop nise fix
+// 06/21?@WorkingOff timming
+static unsigned long	gdwCtrl1eCode[YDSXG_CTRLLENGTH >> 2] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x00800D, 0x000810, 0x20043A, 0x001A82,
+	0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+	0x000810, 0x18043A, 0x00010D, 0x020015,
+	0x0000FD, 0x000020, 0x038860, 0x039060,
+	0x038060, 0x038040, 0x038040, 0x038040,
+	0x018040, 0x000A7D, 0x038040, 0x038040,
+	0x018040, 0x200402, 0x000882, 0x08001A,
+	0x000904, 0x017186, 0x000007, 0x260007,
+	0x400007, 0x000007, 0x03258D, 0x000810,
+	0x18043A, 0x260007, 0x284402, 0x00087D,
+	0x018042, 0x00160A, 0x05A206, 0x000007,
+	0x440007, 0x00230D, 0x000810, 0x08043A,
+	0x22FA06, 0x000007, 0x0007FD, 0x018042,
+	0x08000A, 0x000904, 0x02AB86, 0x000195,
+	0x090D04, 0x000007, 0x000820, 0x0000F5,
+	0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+	0x018040, 0x000A7D, 0x038042, 0x13804A,
+	0x18000A, 0x001820, 0x059060, 0x058860,
+	0x018040, 0x0000FD, 0x018042, 0x70000A,
+	0x000115, 0x071144, 0x033B86, 0x030000,
+	0x007020, 0x036206, 0x018040, 0x00360D,
+	0x000810, 0x08043A, 0x232206, 0x000007,
+	0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+	0x000007, 0x240007, 0x000F8D, 0x000810,
+	0x00163A, 0x002402, 0x005C02, 0x0028FD,
+	0x000020, 0x018040, 0x08000D, 0x000815,
+	0x510984, 0x000007, 0x00004D, 0x000E5D,
+	0x000E02, 0x00430D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x00008D, 0x000924,
+	0x000F02, 0x00470D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x480480, 0x001210,
+	0x28043A, 0x00778D, 0x000810, 0x280C3A,
+	0x00068D, 0x000810, 0x28143A, 0x284402,
+	0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+	0x000820, 0x0002FD, 0x018040, 0x260007,
+	0x200007, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x051286, 0x000007, 0x240007,
+	0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x019B86, 0x000007, 0x01B206, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x22B886, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x065A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x036186, 0x000007, 0x002104, 0x036186,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x07C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00788D, 0x000810, 0x08043A, 0x2A1206,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x070206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x229086, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x225886, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x08DA86, 0x00057D, 0x002820,
+	0x03B060, 0x08F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x08FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x009D8D, 0x000810,
+	0x08043A, 0x2A1206, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x0A5186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x00107D, 0x018042,
+	0x08000A, 0x000215, 0x010984, 0x3A8186,
+	0x000007, 0x007FBD, 0x383DC4, 0x000007,
+	0x001A7D, 0x001375, 0x018042, 0x09004A,
+	0x10000A, 0x0B8D04, 0x139504, 0x000007,
+	0x000820, 0x019060, 0x001104, 0x225886,
+	0x010040, 0x0017FD, 0x018042, 0x08000A,
+	0x000904, 0x225A86, 0x000007, 0x00197D,
+	0x038042, 0x09804A, 0x10000A, 0x000924,
+	0x001664, 0x0011FD, 0x038042, 0x2B804A,
+	0x19804A, 0x00008D, 0x218944, 0x000007,
+	0x002244, 0x0C1986, 0x000007, 0x001A64,
+	0x002A24, 0x00197D, 0x080102, 0x100122,
+	0x000820, 0x039060, 0x018040, 0x003DFD,
+	0x00008D, 0x000820, 0x018040, 0x001375,
+	0x001A7D, 0x010042, 0x09804A, 0x10000A,
+	0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+	0x000007, 0x00060D, 0x000A15, 0x000C1D,
+	0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+	0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+	0x000464, 0x000464, 0x00040D, 0x08B1C4,
+	0x000007, 0x000820, 0x000BF5, 0x030040,
+	0x00197D, 0x038042, 0x09804A, 0x000A24,
+	0x08000A, 0x080E64, 0x000007, 0x100122,
+	0x000820, 0x031060, 0x010040, 0x0064AC,
+	0x00027D, 0x000020, 0x018040, 0x00107D,
+	0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+	0x20000A, 0x000095, 0x1A1144, 0x00A144,
+	0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+	0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+	0x28000A, 0x000095, 0x010924, 0x002A64,
+	0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+	0x000007, 0x0E6206, 0x080002, 0x00008D,
+	0x00387D, 0x000820, 0x018040, 0x00127D,
+	0x018042, 0x10000A, 0x003904, 0x0F0986,
+	0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+	0x000025, 0x0FB206, 0x00002D, 0x000015,
+	0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+	0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+	0x400025, 0x00008D, 0x110944, 0x000007,
+	0x00018D, 0x109504, 0x000007, 0x009164,
+	0x000424, 0x000424, 0x000424, 0x100102,
+	0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+	0x00018D, 0x00042D, 0x00008D, 0x109504,
+	0x000007, 0x00020D, 0x109184, 0x000007,
+	0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+	0x018040, 0x003BFD, 0x001020, 0x03A860,
+	0x000815, 0x313184, 0x212184, 0x000007,
+	0x03B060, 0x03A060, 0x018040, 0x0022FD,
+	0x000095, 0x010924, 0x000424, 0x000424,
+	0x001264, 0x100102, 0x000820, 0x039060,
+	0x018040, 0x001924, 0x010F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x000424, 0x000424,
+	0x00117D, 0x018042, 0x08000A, 0x000A24,
+	0x280502, 0x280C02, 0x09800D, 0x000820,
+	0x0002FD, 0x018040, 0x200007, 0x0022FD,
+	0x018042, 0x08000A, 0x000095, 0x280DC4,
+	0x011924, 0x00197D, 0x018042, 0x0011FD,
+	0x09804A, 0x10000A, 0x0000B5, 0x113144,
+	0x0A8D04, 0x000007, 0x080A44, 0x129504,
+	0x000007, 0x0023FD, 0x001020, 0x038040,
+	0x101244, 0x000007, 0x000820, 0x039060,
+	0x018040, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x123286, 0x000007, 0x003BFD,
+	0x000100, 0x000A10, 0x0B807A, 0x13804A,
+	0x090984, 0x000007, 0x000095, 0x013D04,
+	0x12B886, 0x10000A, 0x100002, 0x090984,
+	0x000007, 0x038042, 0x11804A, 0x090D04,
+	0x000007, 0x10000A, 0x090D84, 0x000007,
+	0x00257D, 0x000820, 0x018040, 0x00010D,
+	0x000810, 0x28143A, 0x00127D, 0x018042,
+	0x20000A, 0x00197D, 0x018042, 0x00117D,
+	0x31804A, 0x10000A, 0x003124, 0x013B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x300102,
+	0x003124, 0x000424, 0x000424, 0x001224,
+	0x280502, 0x001A4C, 0x143986, 0x700002,
+	0x00002D, 0x030000, 0x00387D, 0x018042,
+	0x10000A, 0x146206, 0x002124, 0x0000AD,
+	0x100002, 0x00010D, 0x000924, 0x006B24,
+	0x014A0D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x003264, 0x00008D, 0x000A24, 0x001020,
+	0x00227D, 0x018040, 0x014F8D, 0x000810,
+	0x08043A, 0x2B5A06, 0x000007, 0x002820,
+	0x00207D, 0x018040, 0x00117D, 0x038042,
+	0x13804A, 0x33800A, 0x00387D, 0x018042,
+	0x08000A, 0x000904, 0x177286, 0x000007,
+	0x00008D, 0x030964, 0x015B0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x380102, 0x000424,
+	0x000424, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x280502, 0x001A4C, 0x177186, 0x000007,
+	0x032164, 0x00632C, 0x003DFD, 0x018042,
+	0x08000A, 0x000095, 0x090904, 0x000007,
+	0x000820, 0x001A4C, 0x169986, 0x018040,
+	0x030000, 0x16B206, 0x002124, 0x00010D,
+	0x000924, 0x006B24, 0x016F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x003A64, 0x000095,
+	0x001224, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x171286, 0x000007, 0x01760D,
+	0x000810, 0x08043A, 0x2B5A06, 0x000007,
+	0x160A06, 0x000007, 0x007020, 0x08010A,
+	0x10012A, 0x0020FD, 0x038860, 0x039060,
+	0x018040, 0x00227D, 0x018042, 0x003DFD,
+	0x08000A, 0x31844A, 0x000904, 0x181086,
+	0x18008B, 0x00008D, 0x189904, 0x00312C,
+	0x18E206, 0x000007, 0x00324C, 0x186B86,
+	0x000007, 0x001904, 0x186886, 0x000007,
+	0x000095, 0x199144, 0x00222C, 0x003124,
+	0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+	0x010042, 0x09804A, 0x10000A, 0x038AEC,
+	0x0393EC, 0x00224C, 0x18E186, 0x000007,
+	0x00008D, 0x189904, 0x00226C, 0x00322C,
+	0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+	0x018042, 0x08000A, 0x018924, 0x300502,
+	0x001083, 0x001875, 0x010042, 0x10000A,
+	0x00008D, 0x010924, 0x001375, 0x330542,
+	0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+	0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+	0x006083, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x19B286, 0x000007, 0x001E2D,
+	0x0005FD, 0x018042, 0x08000A, 0x028924,
+	0x280502, 0x00060D, 0x000810, 0x280C3A,
+	0x00008D, 0x000810, 0x28143A, 0x0A808D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x001275, 0x030042, 0x21004A, 0x00008D,
+	0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+	0x030042, 0x0D004A, 0x10000A, 0x089144,
+	0x000007, 0x000820, 0x010040, 0x0025F5,
+	0x0A3144, 0x000007, 0x000820, 0x032860,
+	0x030040, 0x00217D, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00008D, 0x000124, 0x00012C, 0x000E64,
+	0x001A64, 0x00636C, 0x08010A, 0x10012A,
+	0x000820, 0x031060, 0x030040, 0x0020FD,
+	0x018042, 0x08000A, 0x00227D, 0x018042,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00197D, 0x018042, 0x08000A, 0x0022FD,
+	0x038042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x090D04, 0x000007, 0x000820,
+	0x030040, 0x038042, 0x0B804A, 0x10000A,
+	0x000820, 0x031060, 0x030040, 0x038042,
+	0x13804A, 0x19804A, 0x110D04, 0x198D04,
+	0x000007, 0x08000A, 0x001020, 0x031860,
+	0x030860, 0x030040, 0x00008D, 0x0B0944,
+	0x000007, 0x000820, 0x010040, 0x0005F5,
+	0x030042, 0x08000A, 0x000820, 0x010040,
+	0x0000F5, 0x010042, 0x08000A, 0x000904,
+	0x1D9886, 0x001E75, 0x030042, 0x01044A,
+	0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+	0x000C02, 0x00177D, 0x001AF5, 0x018042,
+	0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+	0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+	0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x005502, 0x005D0A, 0x030042, 0x0B004A,
+	0x1B804A, 0x13804A, 0x20000A, 0x089144,
+	0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+	0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+	0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+	0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+	0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+	0x000702, 0x00107D, 0x000415, 0x018042,
+	0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+	0x0019FD, 0x010042, 0x09804A, 0x10000A,
+	0x000934, 0x001674, 0x0029F5, 0x010042,
+	0x10000A, 0x00917C, 0x002075, 0x010042,
+	0x08000A, 0x000904, 0x200A86, 0x0026F5,
+	0x0027F5, 0x030042, 0x09004A, 0x10000A,
+	0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+	0x010042, 0x51804A, 0x48000A, 0x160007,
+	0x001075, 0x010042, 0x282C0A, 0x281D12,
+	0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+	0x001975, 0x010042, 0x002DF5, 0x0D004A,
+	0x10000A, 0x009144, 0x20EA86, 0x010042,
+	0x28340A, 0x000E5D, 0x00008D, 0x000375,
+	0x000820, 0x010040, 0x05D2F4, 0x54D104,
+	0x00735C, 0x218B86, 0x000007, 0x0C0007,
+	0x080007, 0x0A0007, 0x02178D, 0x000810,
+	0x08043A, 0x34B206, 0x000007, 0x219206,
+	0x000007, 0x080007, 0x002275, 0x010042,
+	0x20000A, 0x002104, 0x225886, 0x001E2D,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x21CA86, 0x000007, 0x002010, 0x30043A,
+	0x00057D, 0x0180C3, 0x08000A, 0x028924,
+	0x280502, 0x280C02, 0x0A810D, 0x000820,
+	0x0002F5, 0x010040, 0x220007, 0x0004FD,
+	0x018042, 0x70000A, 0x030000, 0x007020,
+	0x07FA06, 0x018040, 0x022B8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x22C286,
+	0x000007, 0x020206, 0x000007, 0x000875,
+	0x0009FD, 0x00010D, 0x234206, 0x000295,
+	0x000B75, 0x00097D, 0x00000D, 0x000515,
+	0x010042, 0x18000A, 0x001904, 0x2A0086,
+	0x0006F5, 0x001020, 0x010040, 0x0004F5,
+	0x000820, 0x010040, 0x000775, 0x010042,
+	0x09804A, 0x10000A, 0x001124, 0x000904,
+	0x23F286, 0x000815, 0x080102, 0x101204,
+	0x241206, 0x000575, 0x081204, 0x000007,
+	0x100102, 0x000575, 0x000425, 0x021124,
+	0x100102, 0x000820, 0x031060, 0x010040,
+	0x001924, 0x2A0086, 0x00008D, 0x000464,
+	0x009D04, 0x291086, 0x180102, 0x000575,
+	0x010042, 0x28040A, 0x00018D, 0x000924,
+	0x280D02, 0x00000D, 0x000924, 0x281502,
+	0x10000D, 0x000820, 0x0002F5, 0x010040,
+	0x200007, 0x001175, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x24FA86, 0x000007,
+	0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+	0x030A60, 0x010040, 0x050042, 0x3D004A,
+	0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+	0x010042, 0x28140A, 0x0004F5, 0x010042,
+	0x08000A, 0x000315, 0x010D04, 0x260286,
+	0x004015, 0x000095, 0x010D04, 0x25F086,
+	0x100022, 0x10002A, 0x261A06, 0x000007,
+	0x333104, 0x2AA904, 0x000007, 0x032124,
+	0x280502, 0x284402, 0x001124, 0x400102,
+	0x000424, 0x000424, 0x003224, 0x00292C,
+	0x00636C, 0x277386, 0x000007, 0x02B164,
+	0x000464, 0x000464, 0x00008D, 0x000A64,
+	0x280D02, 0x10008D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x00008D, 0x38B904,
+	0x000007, 0x03296C, 0x30010A, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x270286,
+	0x000007, 0x00212C, 0x28050A, 0x00316C,
+	0x00046C, 0x00046C, 0x28450A, 0x001124,
+	0x006B64, 0x100102, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x004124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x27FA86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x284086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x284A86, 0x000007, 0x284402, 0x003124,
+	0x300502, 0x003924, 0x300583, 0x000883,
+	0x0005F5, 0x010042, 0x28040A, 0x00008D,
+	0x008124, 0x280D02, 0x00008D, 0x008124,
+	0x281502, 0x10018D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001025, 0x000575,
+	0x030042, 0x09004A, 0x10000A, 0x0A0904,
+	0x121104, 0x000007, 0x001020, 0x050860,
+	0x050040, 0x0006FD, 0x018042, 0x09004A,
+	0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+	0x000007, 0x000820, 0x019060, 0x010040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x29CA86, 0x000007, 0x244206, 0x000007,
+	0x000606, 0x000007, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x2A1A86, 0x000007,
+	0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+	0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+	0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+	0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+	0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+	0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+	0x038A60, 0x000606, 0x018040, 0x00008D,
+	0x000A64, 0x280D02, 0x000A24, 0x00027D,
+	0x018042, 0x10000A, 0x001224, 0x0003FD,
+	0x018042, 0x08000A, 0x000904, 0x2C0A86,
+	0x000007, 0x00018D, 0x000A24, 0x000464,
+	0x000464, 0x080102, 0x000924, 0x000424,
+	0x000424, 0x100102, 0x02000D, 0x009144,
+	0x2C6186, 0x000007, 0x0001FD, 0x018042,
+	0x08000A, 0x000A44, 0x2C4386, 0x018042,
+	0x0A000D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00027D, 0x001020, 0x000606,
+	0x018040, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x2CB286, 0x000007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2CE286,
+	0x000007, 0x000075, 0x002E7D, 0x010042,
+	0x0B804A, 0x000020, 0x000904, 0x000686,
+	0x010040, 0x31844A, 0x30048B, 0x000883,
+	0x00008D, 0x000810, 0x28143A, 0x00008D,
+	0x000810, 0x280C3A, 0x000675, 0x010042,
+	0x08000A, 0x003815, 0x010924, 0x280502,
+	0x0B000D, 0x000820, 0x0002F5, 0x010040,
+	0x000606, 0x220007, 0x000464, 0x000464,
+	0x000606, 0x000007, 0x000134, 0x007F8D,
+	0x00093C, 0x281D12, 0x282512, 0x001F32,
+	0x0E0007, 0x00010D, 0x00037D, 0x000820,
+	0x018040, 0x05D2F4, 0x000007, 0x080007,
+	0x00037D, 0x018042, 0x08000A, 0x000904,
+	0x2E8A86, 0x000007, 0x000606, 0x000007,
+	0x000007, 0x000012, 0x100007, 0x320007,
+	0x600007, 0x460007, 0x100080, 0x48001A,
+	0x004904, 0x2EF186, 0x000007, 0x001210,
+	0x58003A, 0x000145, 0x5C5D04, 0x000007,
+	0x000080, 0x48001A, 0x004904, 0x2F4186,
+	0x000007, 0x001210, 0x50003A, 0x005904,
+	0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+	0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+	0x200502, 0x000082, 0x40001A, 0x004104,
+	0x2FC986, 0x000007, 0x003865, 0x40001A,
+	0x004020, 0x00104D, 0x04C184, 0x31AB86,
+	0x000040, 0x040007, 0x000165, 0x000145,
+	0x004020, 0x000040, 0x000765, 0x080080,
+	0x40001A, 0x004104, 0x305986, 0x000007,
+	0x001210, 0x40003A, 0x004104, 0x30B286,
+	0x00004D, 0x0000CD, 0x004810, 0x20043A,
+	0x000882, 0x40001A, 0x004104, 0x30C186,
+	0x000007, 0x004820, 0x005904, 0x319886,
+	0x000040, 0x0007E5, 0x200480, 0x2816A0,
+	0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+	0x000040, 0x000032, 0x400075, 0x00007D,
+	0x07D574, 0x200512, 0x000082, 0x40001A,
+	0x004104, 0x317186, 0x000007, 0x038A06,
+	0x640007, 0x0000E5, 0x000020, 0x000040,
+	0x000A65, 0x000020, 0x020040, 0x020040,
+	0x000040, 0x000165, 0x000042, 0x70000A,
+	0x007104, 0x323286, 0x000007, 0x060007,
+	0x019A06, 0x640007, 0x050000, 0x007020,
+	0x000040, 0x038A06, 0x640007, 0x000007,
+	0x00306D, 0x028860, 0x029060, 0x08000A,
+	0x028860, 0x008040, 0x100012, 0x00100D,
+	0x009184, 0x32D186, 0x000E0D, 0x009184,
+	0x33E186, 0x000007, 0x300007, 0x001020,
+	0x003B6D, 0x008040, 0x000080, 0x08001A,
+	0x000904, 0x32F186, 0x000007, 0x001220,
+	0x000DED, 0x008040, 0x008042, 0x10000A,
+	0x40000D, 0x109544, 0x000007, 0x001020,
+	0x000DED, 0x008040, 0x008042, 0x20040A,
+	0x000082, 0x08001A, 0x000904, 0x338186,
+	0x000007, 0x003B6D, 0x008042, 0x08000A,
+	0x000E15, 0x010984, 0x342B86, 0x600007,
+	0x08001A, 0x000C15, 0x010984, 0x341386,
+	0x000020, 0x1A0007, 0x0002ED, 0x008040,
+	0x620007, 0x00306D, 0x028042, 0x0A804A,
+	0x000820, 0x0A804A, 0x000606, 0x10804A,
+	0x000007, 0x282512, 0x001F32, 0x05D2F4,
+	0x54D104, 0x00735C, 0x000786, 0x000007,
+	0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+	0x020040, 0x004820, 0x025060, 0x40000A,
+	0x024060, 0x000040, 0x454944, 0x000007,
+	0x004020, 0x003AE5, 0x000040, 0x0028E5,
+	0x000042, 0x48000A, 0x004904, 0x39F886,
+	0x002C65, 0x000042, 0x40000A, 0x0000D5,
+	0x454104, 0x000007, 0x000655, 0x054504,
+	0x368286, 0x0001D5, 0x054504, 0x368086,
+	0x002B65, 0x000042, 0x003AE5, 0x50004A,
+	0x40000A, 0x45C3D4, 0x000007, 0x454504,
+	0x000007, 0x0000CD, 0x444944, 0x000007,
+	0x454504, 0x000007, 0x00014D, 0x554944,
+	0x000007, 0x045144, 0x367986, 0x002C65,
+	0x000042, 0x48000A, 0x4CD104, 0x000007,
+	0x04C144, 0x368386, 0x000007, 0x160007,
+	0x002CE5, 0x040042, 0x40000A, 0x004020,
+	0x000040, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x36F086, 0x000007, 0x002402,
+	0x383206, 0x005C02, 0x0025E5, 0x000042,
+	0x40000A, 0x004274, 0x002AE5, 0x000042,
+	0x40000A, 0x004274, 0x500112, 0x0029E5,
+	0x000042, 0x40000A, 0x004234, 0x454104,
+	0x000007, 0x004020, 0x000040, 0x003EE5,
+	0x000020, 0x000040, 0x002DE5, 0x400152,
+	0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+	0x003EE5, 0x004020, 0x000040, 0x002BE5,
+	0x000042, 0x40000A, 0x404254, 0x000007,
+	0x002AE5, 0x004020, 0x000040, 0x500132,
+	0x040134, 0x005674, 0x0029E5, 0x020042,
+	0x42000A, 0x000042, 0x50000A, 0x05417C,
+	0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+	0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+	0x020042, 0x40004A, 0x50000A, 0x00423C,
+	0x00567C, 0x0028E5, 0x004820, 0x000040,
+	0x281D12, 0x282512, 0x001F72, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x393A86,
+	0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+	0x000042, 0x40000A, 0x004104, 0x397886,
+	0x002D65, 0x000042, 0x28340A, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+	0x39E186, 0x000007, 0x000606, 0x080007,
+	0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+	0x020045, 0x004020, 0x000060, 0x000365,
+	0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+	0x000040, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x000606, 0x50004A,
+	0x0017FD, 0x018042, 0x08000A, 0x000904,
+	0x225A86, 0x000007, 0x00107D, 0x018042,
+	0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+	0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+	0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+	0x018042, 0x0010FD, 0x09804A, 0x38000A,
+	0x000095, 0x010924, 0x003A64, 0x3B8186,
+	0x000007, 0x003904, 0x3B9286, 0x000007,
+	0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+	0x00387D, 0x018040, 0x700002, 0x00117D,
+	0x018042, 0x00197D, 0x29804A, 0x30000A,
+	0x380002, 0x003124, 0x000424, 0x000424,
+	0x002A24, 0x280502, 0x00068D, 0x000810,
+	0x28143A, 0x00750D, 0x00B124, 0x002264,
+	0x3D0386, 0x284402, 0x000810, 0x280C3A,
+	0x0B800D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00758D, 0x00B124, 0x100102,
+	0x012144, 0x3E4986, 0x001810, 0x10003A,
+	0x00387D, 0x018042, 0x08000A, 0x000904,
+	0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+	0x00008D, 0x023164, 0x000A64, 0x280D02,
+	0x0B808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00387D, 0x018042, 0x08000A,
+	0x000904, 0x3E3286, 0x030000, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x3D8286,
+	0x000007, 0x002810, 0x28043A, 0x00750D,
+	0x030924, 0x002264, 0x280D02, 0x02316C,
+	0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x00008D, 0x000A24,
+	0x3E4A06, 0x100102, 0x001810, 0x10003A,
+	0x0000BD, 0x003810, 0x30043A, 0x00187D,
+	0x018042, 0x0018FD, 0x09804A, 0x20000A,
+	0x0000AD, 0x028924, 0x07212C, 0x001010,
+	0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+	0x002083, 0x00137D, 0x038042, 0x33844A,
+	0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+	0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+	0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+	0x020924, 0x00068D, 0x00A96C, 0x00009D,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+	0x284402, 0x001810, 0x28143A, 0x0C008D,
+	0x000820, 0x0002FD, 0x018040, 0x220007,
+	0x003904, 0x225886, 0x001E0D, 0x00057D,
+	0x018042, 0x20000A, 0x020924, 0x0000A5,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x402A86, 0x000007, 0x280502, 0x280C02,
+	0x002010, 0x28143A, 0x0C010D, 0x000820,
+	0x0002FD, 0x018040, 0x225A06, 0x220007,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
diff -Nru linux/sound/oss/Makefile linux-2.4.19-pre5-mjc/sound/oss/Makefile
--- linux/sound/oss/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,288 @@
+# Makefile for the Linux sound card driver
+#
+# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# Rewritten to use lists instead of if-statements.
+
+
+# All of the (potential) objects that export symbols.
+# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
+
+export-objs	:=  ad1848.o audio_syms.o midi_syms.o mpu401.o \
+		    msnd.o opl3.o sb_common.o sequencer_syms.o \
+		    sound_syms.o uart401.o	\
+		    nm256_audio.o ac97.o ac97_codec.o aci.o
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_SOUND_ALSA_OSS)		+= sound.o
+obj-$(CONFIG_SOUND_ALSA_CS4232)	+= cs4232.o ad1848.o 
+
+# Please leave it as is, cause the link order is significant !
+
+obj-$(CONFIG_SOUND_ALSA_AEDSP16)	+= aedsp16.o
+obj-$(CONFIG_SOUND_ALSA_PSS)		+= pss.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_ALSA_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_ALSA_OPL3SA1)	+= opl3sa.o ad1848.o uart401.o
+obj-$(CONFIG_SOUND_ALSA_SSCAPE)	+= sscape.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_ALSA_MAD16)	+= mad16.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_ALSA_CS4232)	+= cs4232.o uart401.o
+obj-$(CONFIG_SOUND_ALSA_OPL3SA2)	+= opl3sa2.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_ALSA_MSS)		+= ad1848.o
+obj-$(CONFIG_SOUND_ALSA_PAS)		+= pas2.o sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_ALSA_SB)		+= sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_ALSA_WAVEFRONT)	+= wavefront.o
+obj-$(CONFIG_SOUND_ALSA_MAUI)	+= maui.o mpu401.o
+obj-$(CONFIG_SOUND_ALSA_MPU401)	+= mpu401.o
+obj-$(CONFIG_SOUND_ALSA_UART6850)	+= uart6850.o
+obj-$(CONFIG_SOUND_ALSA_GUS)		+= gus.o ad1848.o
+obj-$(CONFIG_SOUND_ALSA_ADLIB)	+= adlib_card.o opl3.o
+obj-$(CONFIG_SOUND_ALSA_YM3812)	+= opl3.o
+obj-$(CONFIG_SOUND_ALSA_VMIDI)	+= v_midi.o
+obj-$(CONFIG_SOUND_ALSA_VIDC)	+= vidc_mod.o
+obj-$(CONFIG_SOUND_ALSA_WAVEARTIST)	+= waveartist.o
+obj-$(CONFIG_SOUND_ALSA_SGALAXY)	+= sgalaxy.o ad1848.o
+obj-$(CONFIG_SOUND_ALSA_AD1816)	+= ad1816.o
+obj-$(CONFIG_SOUND_ALSA_ACI_MIXER)	+= aci.o
+obj-$(CONFIG_SOUND_ALSA_AWE32_SYNTH)	+= awe_wave.o
+
+obj-$(CONFIG_SOUND_ALSA_VIA82CXXX)	+= via82cxxx_audio.o ac97_codec.o
+ifeq ($(CONFIG_MIDI_VIA82CXXX),y)
+  obj-$(CONFIG_SOUND_ALSA_VIA82CXXX) += sound.o uart401.o
+endif
+obj-$(CONFIG_SOUND_ALSA_YMFPCI)	+= ymfpci.o ac97_codec.o
+ifeq ($(CONFIG_SOUND_ALSA_YMFPCI_LEGACY),y)
+  obj-$(CONFIG_SOUND_ALSA_YMFPCI)    += opl3.o uart401.o
+endif
+obj-$(CONFIG_SOUND_ALSA_MSNDCLAS)	+= msnd.o msnd_classic.o
+obj-$(CONFIG_SOUND_ALSA_MSNDPIN)	+= msnd.o msnd_pinnacle.o
+obj-$(CONFIG_SOUND_ALSA_VWSND)	+= vwsnd.o
+obj-$(CONFIG_SOUND_ALSA_NM256)	+= nm256_audio.o ac97.o
+obj-$(CONFIG_SOUND_ALSA_ICH)		+= i810_audio.o ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_SONICVIBES)	+= sonicvibes.o
+obj-$(CONFIG_SOUND_ALSA_CMPCI)	+= cmpci.o
+obj-$(CONFIG_SOUND_ALSA_ES1370)	+= es1370.o
+obj-$(CONFIG_SOUND_ALSA_ES1371)	+= es1371.o ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_VRC5477)	+= nec_vrc5477.o ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_ESSSOLO1)	+= esssolo1.o
+obj-$(CONFIG_SOUND_ALSA_FUSION)	+= cs46xx.o ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_MAESTRO)	+= maestro.o
+obj-$(CONFIG_SOUND_ALSA_MAESTRO3)	+= maestro3.o ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_TRIDENT)	+= trident.o ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_EMU10K1)	+= ac97_codec.o
+obj-$(CONFIG_SOUND_ALSA_RME96XX)     += rme96xx.o
+obj-$(CONFIG_SOUND_ALSA_BT878)	+= btaudio.o
+obj-$(CONFIG_SOUND_ALSA_EMU10K1)	+= ac97_codec.o
+
+ifeq ($(CONFIG_MIDI_EMU10K1),y)
+  obj-$(CONFIG_SOUND_ALSA_EMU10K1)	+= sound.o
+endif
+
+subdir-$(CONFIG_SOUND_ALSA_EMU10K1) += emu10k1
+subdir-$(CONFIG_SOUND_ALSA_CS4281) += cs4281
+
+ifeq ($(CONFIG_SOUND_ALSA_EMU10K1),y)
+  obj-y += emu10k1/emu10k1.o
+endif
+
+ifeq ($(CONFIG_SOUND_ALSA_CS4281),y)
+  obj-y += cs4281/cs4281.o
+endif
+
+subdir-$(CONFIG_DMASOUND) += dmasound
+
+ifeq ($(CONFIG_DMASOUND),y)
+  obj-y += dmasound/dmasound.o
+endif
+
+
+# Declare multi-part drivers.
+
+list-multi	:= sound.o gus.o pas2.o sb.o sb_lib.o vidc_mod.o \
+    wavefront.o
+
+sound-objs	:= 							\
+    dev_table.o soundcard.o sound_syms.o		\
+    audio.o audio_syms.o dmabuf.o					\
+    midi_syms.o midi_synth.o midibuf.o					\
+    sequencer.o sequencer_syms.o sound_timer.o sys_timer.o
+
+gus-objs	:= gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
+pas2-objs	:= pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
+sb-objs		:= sb_card.o
+sb_lib-objs	:= sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
+vidc_mod-objs	:= vidc.o vidc_fill.o
+wavefront-objs  := wavfront.o wf_midi.o yss225.o
+
+
+O_TARGET	:= sounddrivers.o
+
+include $(TOPDIR)/Rules.make
+
+
+
+# Link rules for multi-part drivers.
+
+sound.o: $(sound-objs)
+	$(LD) -r -o $@ $(sound-objs)
+
+gus.o: $(gus-objs)
+	$(LD) -r -o $@ $(gus-objs)
+
+pas2.o: $(pas2-objs)
+	$(LD) -r -o $@ $(pas2-objs)
+
+sb.o: $(sb-objs)
+	$(LD) -r -o $@ $(sb-objs)
+
+sb_lib.o: $(sb_lib-objs)
+	$(LD) -r -o $@ $(sb_lib-objs)
+
+vidc_mod.o: $(vidc_mod-objs)
+	$(LD) -r -o $@ $(vidc_mod-objs)
+
+wavefront.o: $(wavefront-objs)
+	$(LD) -r -o $@ $(wavefront-objs)
+
+# Firmware files that need translation
+#
+# The translated files are protected by a file that keeps track
+# of what name was used to build them.  If the name changes, they
+# will be forced to be remade.
+#
+# First make the utilities.
+
+bin2hex: bin2hex.c
+	$(HOSTCC) $(HOSTCFLAGS) -o bin2hex bin2hex.c
+
+hex2hex: hex2hex.c
+	$(HOSTCC) $(HOSTCFLAGS) -o hex2hex hex2hex.c
+
+
+
+
+# Turtle Beach Maui / Tropez
+
+maui.o: maui_boot.h
+
+ifeq ($(CONFIG_MAUI_HAVE_BOOT),y)
+    maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) bin2hex
+	./bin2hex -i maui_os < $(CONFIG_MAUI_BOOT_FILE) > $@
+else
+    maui_boot.h:
+	(							\
+	    echo 'static unsigned char * maui_os = NULL;';	\
+	    echo 'static int maui_osLen = 0;';			\
+	) > $@
+endif
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_HAVE_BOOT) $$(CONFIG_MAUI_BOOT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+
+
+# Turtle Beach MultiSound
+
+ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
+    msnd_classic.o: msndperm.c msndinit.c
+
+    msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) bin2hex
+	./bin2hex msndperm < $(CONFIG_MSNDCLAS_PERM_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_PERM_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_PERM_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+    msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) bin2hex
+	./bin2hex msndinit < $(CONFIG_MSNDCLAS_INIT_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_INIT_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_INIT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+endif
+
+ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
+    msnd_pinnacle.o: pndsperm.c pndspini.c
+
+    pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) bin2hex
+	./bin2hex pndsperm < $(CONFIG_MSNDPIN_PERM_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_PERM_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_PERM_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+    pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) bin2hex
+	./bin2hex pndspini < $(CONFIG_MSNDPIN_INIT_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_INIT_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_INIT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+endif
+
+
+
+# PSS (ECHO-ADI2111)
+
+pss.o: pss_boot.h
+
+ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
+    pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex
+	./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@
+else
+    pss_boot.h:
+	(							\
+	    echo 'static unsigned char * pss_synth = NULL;';	\
+	    echo 'static int pss_synthLen = 0;';		\
+	) > $@
+endif
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_HAVE_BOOT) $$(CONFIG_PSS_BOOT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+
+
+# MediaTrix AudioTrix Pro
+
+trix.o: trix_boot.h
+
+ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
+    trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex
+	./hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@
+else
+    trix_boot.h:
+	(							\
+	    echo 'static unsigned char * trix_boot = NULL;';	\
+	    echo 'static int trix_boot_len = 0;';		\
+	) > $@
+endif
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_HAVE_BOOT) $$(CONFIG_TRIX_BOOT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+
+
+# Find boot files whose source file names have changed and force rebuild.
+
+FILES_BOOT_UP_TO_DATE :=
+
+FILES_BOOT_EXIST := $(wildcard .*.boot)
+ifneq ($(FILES_BOOT_EXIST),)
+include $(FILES_BOOT_EXIST)
+endif
+
+FILES_BOOT_CHANGED := $(strip \
+    $(filter-out $(FILES_BOOT_UP_TO_DATE), \
+	maui_boot.h pss_boot.h trix_boot.h))
+
+ifneq ($(FILES_BOOT_CHANGED),)
+$(FILES_BOOT_CHANGED): dummy
+endif
diff -Nru linux/sound/oss/README.FIRST linux-2.4.19-pre5-mjc/sound/oss/README.FIRST
--- linux/sound/oss/README.FIRST	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/README.FIRST	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,6 @@
+The modular sound driver patches were funded by Red Hat Software 
+(www.redhat.com). The sound driver here is thus a modified version of 
+Hannu's code. Please bear that in mind when considering the appropriate
+forums for bug reporting. 
+
+Alan Cox
diff -Nru linux/sound/oss/ac97.c linux-2.4.19-pre5-mjc/sound/oss/ac97.c
--- linux/sound/oss/ac97.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ac97.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,455 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include "ac97.h"
+
+/* Flag for mono controls. */
+#define MO 0
+/* And for stereo. */
+#define ST 1
+
+/* Whether or not the bits in the channel are inverted. */
+#define INV 1
+#define NINV 0
+
+static struct ac97_chn_desc {
+    int ac97_regnum;
+    int oss_channel;
+    int maxval;
+    int is_stereo;
+    int oss_mask;
+    int recordNum;
+    u16 regmask;
+    int is_inverted;
+} mixerRegs[] = {
+    { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME,   0x3f, ST, SOUND_MASK_VOLUME,   5, 0x0000, INV  },
+    { AC97_MASTER_VOL_MONO,   SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV  },
+    { AC97_MASTER_TONE,       SOUND_MIXER_TREBLE,   0x0f, MO, SOUND_MASK_TREBLE,  -1, 0x00ff, INV  },
+    { AC97_MASTER_TONE,       SOUND_MIXER_BASS,     0x0f, MO, SOUND_MASK_BASS,    -1, 0xff00, INV  },
+    { AC97_PCBEEP_VOL,        SOUND_MIXER_SPEAKER,  0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV  },
+    { AC97_PHONE_VOL,         SOUND_MIXER_PHONEIN,  0x1f, MO, SOUND_MASK_PHONEIN,  7, 0x0000, INV  },
+    { AC97_MIC_VOL,           SOUND_MIXER_MIC,      0x1f, MO, SOUND_MASK_MIC,      0, 0x0000, INV  },
+    { AC97_LINEIN_VOL,        SOUND_MIXER_LINE,     0x1f, ST, SOUND_MASK_LINE,     4, 0x0000, INV  },
+    { AC97_CD_VOL,            SOUND_MIXER_CD,       0x1f, ST, SOUND_MASK_CD,       1, 0x0000, INV  },
+    { AC97_VIDEO_VOL,         SOUND_MIXER_VIDEO,    0x1f, ST, SOUND_MASK_VIDEO,    2, 0x0000, INV  },
+    { AC97_AUX_VOL,           SOUND_MIXER_LINE1,    0x1f, ST, SOUND_MASK_LINE1,	   3, 0x0000, INV  },
+    { AC97_PCMOUT_VOL,        SOUND_MIXER_PCM,      0x1f, ST, SOUND_MASK_PCM,     -1, 0x0000, INV  },
+    { AC97_RECORD_GAIN,       SOUND_MIXER_IGAIN,    0x0f, ST, SOUND_MASK_IGAIN,   -1, 0x0000, NINV },
+    { -1,		      -1,		    0xff, 0,  0,                  -1, 0x0000, 0    },
+};
+
+static struct ac97_chn_desc *
+ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel)
+{
+    int x;
+
+    for (x = 0; mixerRegs[x].oss_channel != -1; x++) {
+	if (mixerRegs[x].oss_channel == oss_channel)
+	    return mixerRegs + x;
+    }
+
+    return NULL;
+}
+
+static inline int
+ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn)
+{
+    return (dev->last_written_mixer_values[chn->ac97_regnum / 2]
+	    != AC97_REG_UNSUPPORTED);
+}
+
+int
+ac97_init (struct ac97_hwint *dev)
+{
+    int x;
+    int reg0;
+
+    /* Clear out the arrays of cached values. */
+    for (x = 0; x < AC97_REG_CNT; x++)
+	dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN;
+
+    for (x = 0; x < SOUND_MIXER_NRDEVICES; x++)
+	dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN;
+
+    /* Clear the device masks.  */
+    dev->mixer_devmask = 0;
+    dev->mixer_stereomask = 0;
+    dev->mixer_recmask = 0;
+
+    /* ??? Do a "standard reset" via register 0? */
+
+    /* Hardware-dependent reset.  */
+    if (dev->reset_device (dev))
+	return -1;
+
+    /* Check the mixer device capabilities.  */
+    reg0 = dev->read_reg (dev, AC97_RESET);
+
+    if (reg0 < 0)
+	return -1;
+
+    /* Check for support for treble/bass controls.  */
+    if (! (reg0 & 4)) {
+	dev->last_written_mixer_values[AC97_MASTER_TONE / 2] 
+	    = AC97_REG_UNSUPPORTED;
+    }
+
+    /* ??? There may be other tests here? */
+
+    /* Fill in the device masks.  */
+    for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
+	if (ac97_is_valid_channel (dev, mixerRegs + x)) {
+	    dev->mixer_devmask |= mixerRegs[x].oss_mask;
+
+	    if (mixerRegs[x].is_stereo)
+		dev->mixer_stereomask |= mixerRegs[x].oss_mask;
+
+	    if (mixerRegs[x].recordNum != -1)
+		dev->mixer_recmask |= mixerRegs[x].oss_mask;
+	}
+    }
+
+    return 0;
+}
+
+/* Reset the mixer to the currently saved settings.  */
+int
+ac97_reset (struct ac97_hwint *dev)
+{
+    int x;
+
+    if (dev->reset_device (dev))
+	return -1;
+
+    /* Now set the registers back to their last-written values. */
+    for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
+	int regnum = mixerRegs[x].ac97_regnum;
+	int value = dev->last_written_mixer_values [regnum / 2];
+	if (value >= 0)
+	    ac97_put_register (dev, regnum, value);
+    }
+    return 0;
+}
+
+/* Return the contents of register REG; use the cache if the value in it
+   is valid.  Returns a negative error code on failure. */
+int
+ac97_get_register (struct ac97_hwint *dev, u8 reg) 
+{
+    if (reg > 127 || (reg & 1))
+	return -EINVAL;
+
+    /* See if it's in the cache, or if it's just plain invalid.  */
+    switch (dev->last_written_mixer_values[reg / 2]) {
+    case AC97_REG_UNSUPPORTED:
+	return -EINVAL;
+	break;
+    case AC97_REGVAL_UNKNOWN:
+	dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg);
+	break;
+    default:
+	break;
+    }
+    return dev->last_written_mixer_values[reg / 2];
+}
+
+/* Write VALUE to AC97 register REG, and cache its value in the last-written
+   cache.  Returns a negative error code on failure, or 0 on success. */
+int
+ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value)
+{
+    if (reg > 127 || (reg & 1))
+	return -EINVAL;
+
+    if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED)
+	return -EINVAL;
+    else {
+	int res = dev->write_reg (dev, reg, value);
+	if (res >= 0) {
+	    dev->last_written_mixer_values[reg / 2] = value;
+	    return 0;
+	}
+	else
+	    return res;
+    }
+}
+
+/* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100.  If
+   IS_STEREO is set, VALUE is a stereo value; the left channel value
+   is in the lower 8 bits, and the right channel value is in the upper
+   8 bits.
+
+   A negative error code is returned on failure, or the unsigned
+   scaled value on success.  */
+
+static int
+ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv)
+{
+    /* Muted?  */
+    if (value & AC97_MUTE)
+	return 0;
+
+    if (is_stereo)
+	return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8)
+	| (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
+    else {
+	int i;
+	
+	/* Inverted. */
+	if (inv)
+	    value = maxval - value;
+
+	i = (value * 100 + (maxval / 2)) / maxval;
+	if (i > 100)
+	     i = 100;
+	if (i < 0)
+	    i = 0;
+	return i;
+    }
+}
+
+static int
+ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv)
+{
+    if (is_stereo)
+	return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8)
+	| (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
+    else {
+	int i = ((value & 255) * maxval + 50) / 100;
+	if (inv)
+	    i = maxval - i;
+	if (i < 0)
+	    i = 0;
+	if (i > maxval)
+	    i = maxval;
+	return i;
+    }
+}
+
+int
+ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value)
+{
+    int scaled_value;
+    struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel);
+    int result;
+
+    if (channel == NULL)
+	return -ENODEV;
+    if (! ac97_is_valid_channel (dev, channel))
+	return -ENODEV;
+    scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval,
+					    channel->is_stereo, 
+					    channel->is_inverted);
+    if (scaled_value < 0)
+	return scaled_value;
+
+    if (channel->regmask != 0) {
+	int mv;
+
+	int oldval = ac97_get_register (dev, channel->ac97_regnum);
+	if (oldval < 0)
+	    return oldval;
+
+	for (mv = channel->regmask; ! (mv & 1); mv >>= 1)
+	    scaled_value <<= 1;
+
+	scaled_value &= channel->regmask;
+	scaled_value |= (oldval & ~channel->regmask);
+    }
+    result = ac97_put_register (dev, channel->ac97_regnum, scaled_value);
+    if (result == 0)
+	dev->last_written_OSS_values[oss_channel] = oss_value;
+    return result;
+}
+
+int
+ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel)
+{
+    struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel);
+    int regval;
+
+    if (channel == NULL)
+	return -ENODEV;
+
+    if (! ac97_is_valid_channel (dev, channel))
+	return -ENODEV;
+
+    regval = ac97_get_register (dev, channel->ac97_regnum);
+
+    if (regval < 0)
+	return regval;
+
+    if (channel->regmask != 0) {
+	int mv;
+
+	regval &= channel->regmask;
+
+	for (mv = channel->regmask; ! (mv & 1); mv >>= 1)
+	    regval >>= 1;
+    }
+    return ac97_scale_to_oss_val (regval, channel->maxval,
+				  channel->is_stereo, 
+				  channel->is_inverted);
+}
+
+int
+ac97_get_recmask (struct ac97_hwint *dev)
+{
+    int recReg = ac97_get_register (dev, AC97_RECORD_SELECT);
+
+    if (recReg < 0)
+	return recReg;
+    else {
+	int x;
+	for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) {
+	    if (mixerRegs[x].recordNum == (recReg & 7))
+		return mixerRegs[x].oss_mask;
+	}
+	return -ENODEV;
+    }
+}
+
+int
+ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask)
+{
+    int x;
+
+    if (oss_recmask == 0)
+	oss_recmask = SOUND_MIXER_MIC;
+
+    for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { 
+	if ((mixerRegs[x].recordNum >= 0)
+	     && (oss_recmask & mixerRegs[x].oss_mask))
+	    break;
+    }
+    if (mixerRegs[x].ac97_regnum < 0)
+	return -ENODEV;
+    else {
+	int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum;
+	int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval);
+	if (res == 0)
+	    return ac97_get_recmask (dev);
+	else
+	    return res;
+    }
+}
+
+/* Set the mixer DEV to the list of values in VALUE_LIST.  Return 0 on
+   success, or a negative error code.  */
+int
+ac97_set_values (struct ac97_hwint *dev, 
+		 struct ac97_mixer_value_list *value_list)
+{
+    int x;
+
+    for (x = 0; value_list[x].oss_channel != -1; x++) {
+	int chnum = value_list[x].oss_channel;
+	struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum);
+	if (chent != NULL) {
+	    u16 val;
+	    int res;
+
+	    if (chent->is_stereo)
+		val = (value_list[x].value.stereo.right << 8) 
+		      | value_list[x].value.stereo.left;
+	    else {
+		/* We do this so the returned value looks OK in the
+		   mixer app.  It's not necessary otherwise.  */
+		val = (value_list[x].value.mono << 8) 
+		      | value_list[x].value.mono;
+	    }
+	    res = ac97_set_mixer (dev, chnum, val);
+	    if (res < 0)
+		return res;
+	}
+	else
+	    return -ENODEV;
+    }
+    return 0;
+}
+
+int
+ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, caddr_t arg)
+{
+    int ret;
+
+    switch (cmd) {
+    case SOUND_MIXER_READ_RECSRC:
+	ret = ac97_get_recmask (dev);
+	break;
+
+    case SOUND_MIXER_WRITE_RECSRC:
+	{
+	    if (get_user (ret, (int *) arg))
+		ret = -EFAULT;
+	    else
+		ret = ac97_set_recmask (dev, ret);
+	}
+	break;
+
+    case SOUND_MIXER_READ_CAPS:
+	ret = SOUND_CAP_EXCL_INPUT;
+	break;
+
+    case SOUND_MIXER_READ_DEVMASK:
+	ret = dev->mixer_devmask;
+	break;
+
+    case SOUND_MIXER_READ_RECMASK:
+	ret = dev->mixer_recmask;
+	break;
+
+    case SOUND_MIXER_READ_STEREODEVS:
+	ret = dev->mixer_stereomask;
+	break;
+
+    default:
+	/* Read or write request. */
+	ret = -EINVAL;
+	if (_IOC_TYPE (cmd) == 'M') {
+	    int dir = _SIOC_DIR (cmd);
+	    int channel = _IOC_NR (cmd);
+
+	    if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) {
+		ret = 0;
+		if (dir & _SIOC_WRITE) {
+		    int val;
+		    if (get_user (val, (int *) arg) == 0)
+			ret = ac97_set_mixer (dev, channel, val);
+		    else
+			ret = -EFAULT;
+		}
+		if (ret >= 0 && (dir & _SIOC_READ)) {
+		    if (dev->last_written_OSS_values[channel]
+			== AC97_REGVAL_UNKNOWN)
+			dev->last_written_OSS_values[channel]
+			    = ac97_get_mixer_scaled (dev, channel);
+		    ret = dev->last_written_OSS_values[channel];
+		}
+	    }
+	}
+	break;
+    }
+
+    if (ret < 0)
+	return ret;
+    else
+	return put_user(ret, (int *) arg);
+}
+
+EXPORT_SYMBOL(ac97_init);
+EXPORT_SYMBOL(ac97_set_values);
+EXPORT_SYMBOL(ac97_set_mixer);
+EXPORT_SYMBOL(ac97_get_register);
+EXPORT_SYMBOL(ac97_put_register);
+EXPORT_SYMBOL(ac97_get_mixer_scaled);
+EXPORT_SYMBOL(ac97_mixer_ioctl);
+EXPORT_SYMBOL(ac97_reset);
+MODULE_LICENSE("GPL");
+
+
+/*
+ * Local variables:
+ *  c-basic-offset: 4
+ * End:
+ */
diff -Nru linux/sound/oss/ac97.h linux-2.4.19-pre5-mjc/sound/oss/ac97.h
--- linux/sound/oss/ac97.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ac97.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,220 @@
+/*
+ * ac97.h 
+ * 
+ * definitions for the AC97, Intel's Audio Codec 97 Spec
+ * also includes support for a generic AC97 interface
+ */
+
+#ifndef _AC97_H_
+#define _AC97_H_
+#include "sound_config.h"
+#include "sound_calls.h"
+
+#define  AC97_RESET              0x0000      //
+#define  AC97_MASTER_VOL_STEREO  0x0002      // Line Out
+#define  AC97_HEADPHONE_VOL      0x0004      // 
+#define  AC97_MASTER_VOL_MONO    0x0006      // TAD Output
+#define  AC97_MASTER_TONE        0x0008      //
+#define  AC97_PCBEEP_VOL         0x000a      // none
+#define  AC97_PHONE_VOL          0x000c      // TAD Input (mono)
+#define  AC97_MIC_VOL            0x000e      // MIC Input (mono)
+#define  AC97_LINEIN_VOL         0x0010      // Line Input (stereo)
+#define  AC97_CD_VOL             0x0012      // CD Input (stereo)
+#define  AC97_VIDEO_VOL          0x0014      // none
+#define  AC97_AUX_VOL            0x0016      // Aux Input (stereo)
+#define  AC97_PCMOUT_VOL         0x0018      // Wave Output (stereo)
+#define  AC97_RECORD_SELECT      0x001a      //
+#define  AC97_RECORD_GAIN        0x001c
+#define  AC97_RECORD_GAIN_MIC    0x001e
+#define  AC97_GENERAL_PURPOSE    0x0020
+#define  AC97_3D_CONTROL         0x0022
+#define  AC97_MODEM_RATE         0x0024
+#define  AC97_POWER_CONTROL      0x0026
+
+/* registers 0x0028 - 0x0058 are reserved */
+
+/* AC'97 2.0 */
+#define AC97_EXTENDED_ID	0x0028	/* Extended Audio ID */
+#define AC97_EXTENDED_STATUS	0x002A	/* Extended Audio Status */
+#define AC97_PCM_FRONT_DAC_RATE 0x002C  /* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE  0x002E  /* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE   0x0030  /* PCM LFE DAC Rate */
+#define AC97_PCM_LR_ADC_RATE	0x0032	/* PCM LR DAC Rate */
+#define AC97_PCM_MIC_ADC_RATE   0x0034  /* PCM MIC ADC Rate */
+#define AC97_CENTER_LFE_MASTER  0x0036  /* Center + LFE Master Volume */
+#define AC97_SURROUND_MASTER    0x0038  /* Surround (Rear) Master Volume */
+#define AC97_RESERVED_3A	0x003A	/* Reserved */
+/* range 0x3c-0x58 - MODEM */
+
+/* registers 0x005a - 0x007a are vendor reserved */
+
+#define  AC97_VENDOR_ID1         0x007c
+#define  AC97_VENDOR_ID2         0x007e
+
+/* volume control bit defines */
+
+#define AC97_MUTE                0x8000
+#define AC97_MICBOOST            0x0040
+#define AC97_LEFTVOL             0x3f00
+#define AC97_RIGHTVOL            0x003f
+
+/* record mux defines */
+
+#define AC97_RECMUX_MIC          0x0000
+#define AC97_RECMUX_CD           0x0101
+#define AC97_RECMUX_VIDEO        0x0202      /* not used */
+#define AC97_RECMUX_AUX          0x0303      
+#define AC97_RECMUX_LINE         0x0404      
+#define AC97_RECMUX_STEREO_MIX   0x0505
+#define AC97_RECMUX_MONO_MIX     0x0606
+#define AC97_RECMUX_PHONE        0x0707
+
+
+/* general purpose register bit defines */
+
+#define AC97_GP_LPBK             0x0080      /* Loopback mode */
+#define AC97_GP_MS               0x0100      /* Mic Select 0=Mic1, 1=Mic2 */
+#define AC97_GP_MIX              0x0200      /* Mono output select 0=Mix, 1=Mic */
+#define AC97_GP_RLBK             0x0400      /* Remote Loopback - Modem line codec */
+#define AC97_GP_LLBK             0x0800      /* Local Loopback - Modem Line codec */
+#define AC97_GP_LD               0x1000      /* Loudness 1=on */
+#define AC97_GP_3D               0x2000      /* 3D Enhancement 1=on */
+#define AC97_GP_ST               0x4000      /* Stereo Enhancement 1=on */
+#define AC97_GP_POP              0x8000      /* Pcm Out Path, 0=pre 3D, 1=post 3D */
+
+
+/* powerdown control and status bit defines */
+
+/* status */
+#define AC97_PWR_MDM             0x0010      /* Modem section ready */
+#define AC97_PWR_REF             0x0008      /* Vref nominal */
+#define AC97_PWR_ANL             0x0004      /* Analog section ready */
+#define AC97_PWR_DAC             0x0002      /* DAC section ready */
+#define AC97_PWR_ADC             0x0001      /* ADC section ready */
+
+/* control */
+#define AC97_PWR_PR0             0x0100      /* ADC and Mux powerdown */
+#define AC97_PWR_PR1             0x0200      /* DAC powerdown */
+#define AC97_PWR_PR2             0x0400      /* Output mixer powerdown (Vref on) */
+#define AC97_PWR_PR3             0x0800      /* Output mixer powerdown (Vref off) */
+#define AC97_PWR_PR4             0x1000      /* AC-link powerdown */
+#define AC97_PWR_PR5             0x2000      /* Internal Clk disable */
+#define AC97_PWR_PR6             0x4000      /* HP amp powerdown */
+#define AC97_PWR_PR7             0x8000      /* Modem off - if supported */
+
+/* useful power states */
+#define AC97_PWR_D0              0x0000      /* everything on */
+#define AC97_PWR_D1              AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4
+#define AC97_PWR_D2              AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
+#define AC97_PWR_D3              AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
+#define AC97_PWR_ANLOFF          AC97_PWR_PR2|AC97_PWR_PR3  /* analog section off */
+
+/* Total number of defined registers.  */
+#define AC97_REG_CNT 64
+
+/* Generic AC97 mixer interface. */
+
+/* Structure describing access to the hardware. */
+struct ac97_hwint
+{
+    /* Perform any hardware-specific reset and initialization.  Returns
+     0 on success, or a negative error code.  */
+    int (*reset_device) (struct ac97_hwint *dev);
+
+    /* Returns the contents of the specified register REG.  The caller
+       should check to see if the desired contents are available in
+       the cache first, if applicable. Returns a positive unsigned value
+       representing the contents of the register, or a negative error
+       code.  */
+    int (*read_reg) (struct ac97_hwint *dev, u8 reg);
+
+    /* Writes VALUE to register REG.  Returns 0 on success, or a
+       negative error code.  */
+    int (*write_reg) (struct ac97_hwint *dev, u8 reg, u16 value);
+
+    /* Hardware-specific information. */
+    void *driver_private;
+
+    /* Three OSS masks. */
+    int mixer_devmask;
+    int mixer_stereomask;
+    int mixer_recmask;
+
+    /* The mixer cache. The indices correspond to the AC97 hardware register
+       number / 2, since the register numbers are always an even number.
+
+       Unknown values are set to -1; unsupported registers contain a
+       -2.  */
+    int last_written_mixer_values[AC97_REG_CNT];
+
+    /* A cache of values written via OSS; we need these so we can return
+       the values originally written by the user.
+
+       Why the original user values?  Because the real-world hardware
+       has less precision, and some existing applications assume that
+       they will get back the exact value that they wrote (aumix).
+
+       A -1 value indicates that no value has been written to this mixer
+       channel via OSS.  */
+    int last_written_OSS_values[SOUND_MIXER_NRDEVICES];
+};
+
+/* Values stored in the register cache.  */
+#define AC97_REGVAL_UNKNOWN -1
+#define AC97_REG_UNSUPPORTED -2
+
+struct ac97_mixer_value_list
+{
+    /* Mixer channel to set.  List is terminated by a value of -1.  */
+    int oss_channel;
+    /* The scaled value to set it to; values generally range from 0-100. */
+    union {
+	struct {
+	    u8 left, right;
+	} stereo;
+	u8 mono;
+    } value;
+};
+
+/* Initialize the ac97 mixer by resetting it.  */
+extern int ac97_init (struct ac97_hwint *dev);
+
+/* Sets the mixer DEV to the values in VALUE_LIST.  Returns 0 on success,
+   or a negative error code.  */
+extern int ac97_set_values (struct ac97_hwint *dev,
+			    struct ac97_mixer_value_list *value_list);
+
+/* Sets one mixer channel OSS_CHANNEL to the scaled value OSS_VALUE.
+   Returns the resulting (rescaled) value, or a negative value
+   representing an error code.
+
+   Stereo channels have two values in OSS_VALUE (the left value is in the
+   lower 8 bits, the right value is in the upper 8 bits). */
+extern int ac97_set_mixer (struct ac97_hwint *dev, int oss_channel,
+			   u16 oss_value);
+
+/* Return the contents of the specified AC97 register REG; it uses the
+   last-written value if it is available.  */
+extern int ac97_get_register (struct ac97_hwint *dev, u8 reg);
+
+/* Writes the specified VALUE to the AC97 register REG in the mixer.
+   Takes care of setting the last-written cache as well.  */
+extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value);
+
+/* Returns the last OSS value written to the OSS_CHANNEL mixer channel.  */
+extern int ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel);
+
+/* Default ioctl. */
+extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd,
+			     caddr_t arg);
+
+/* Do a complete reset on the AC97 mixer, restoring all mixer registers to
+   the current values.  Normally used after an APM resume event.  */
+extern int ac97_reset (struct ac97_hwint *dev);
+#endif
+
+/*
+ * Local variables:
+ *  c-basic-offset: 4
+ * End:
+ */
diff -Nru linux/sound/oss/ac97_codec.c linux-2.4.19-pre5-mjc/sound/oss/ac97_codec.c
--- linux/sound/oss/ac97_codec.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ac97_codec.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1058 @@
+
+/*
+ * ac97_codec.c: Generic AC97 mixer/modem module
+ *
+ * Derived from ac97 mixer in maestro and trident driver.
+ *
+ * Copyright 2000 Silicon Integrated System Corporation
+ *
+ *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ **************************************************************************
+ *
+ * The Intel Audio Codec '97 specification is available at the Intel
+ * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/
+ *
+ * The specification itself is currently available at:
+ * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf
+ *
+ **************************************************************************
+ *
+ * History
+ * v0.4a Mar 12 2002 Jrg Prante <joerg@infolinux.de>
+ *  minor fix: do not probe for modem codec
+ * v0.4 Mar 15 2000 Ollie Lho
+ *	dual codecs support verified with 4 channels output
+ * v0.3 Feb 22 2000 Ollie Lho
+ *	bug fix for record mask setting
+ * v0.2 Feb 10 2000 Ollie Lho
+ *	add ac97_read_proc for /proc/driver/{vendor}/ac97
+ * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw> 
+ *	Isolated from trident.c to support multiple ac97 codec
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/ac97_codec.h>
+#include <asm/uaccess.h>
+
+static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
+static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, 
+			     unsigned int left, unsigned int right);
+static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
+static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
+static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
+
+static int ac97_init_mixer(struct ac97_codec *codec);
+
+static int wolfson_init(struct ac97_codec * codec);
+static int tritech_init(struct ac97_codec * codec);
+static int tritech_maestro_init(struct ac97_codec * codec);
+static int sigmatel_9708_init(struct ac97_codec *codec);
+static int sigmatel_9721_init(struct ac97_codec *codec);
+static int sigmatel_9744_init(struct ac97_codec *codec);
+static int eapd_control(struct ac97_codec *codec, int);
+static int crystal_digital_control(struct ac97_codec *codec, int mode);
+
+
+/*
+ *	AC97 operations.
+ *
+ *	If you are adding a codec then you should be able to use
+ *		eapd_ops - any codec that supports EAPD amp control (most)
+ *		null_ops - any ancient codec that supports nothing
+ *
+ *	The three functions are
+ *		init - used for non AC97 standard initialisation
+ *		amplifier - used to do amplifier control (1=on 0=off)
+ *		digital - switch to digital modes (0 = analog)
+ *
+ *	Not all codecs support all features, not all drivers use all the
+ *	operations yet
+ */
+ 
+static struct ac97_ops null_ops = { NULL, NULL, NULL };
+static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
+static struct ac97_ops wolfson_ops = { wolfson_init, NULL, NULL };
+static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
+static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
+static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
+static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
+static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
+static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
+
+/* sorted by vendor/device id */
+static const struct {
+	u32 id;
+	char *name;
+	struct ac97_ops *ops;
+} ac97_codec_ids[] = {
+	{0x41445303, "Analog Devices AD1819",	&null_ops},
+	{0x41445340, "Analog Devices AD1881",	&null_ops},
+	{0x41445348, "Analog Devices AD1881A",	&null_ops},
+	{0x41445360, "Analog Devices AD1885",	&default_ops},
+	{0x41445460, "Analog Devices AD1885",	&default_ops},
+	{0x414B4D00, "Asahi Kasei AK4540",	&null_ops},
+	{0x414B4D01, "Asahi Kasei AK4542",	&null_ops},
+	{0x414B4D02, "Asahi Kasei AK4543",	&null_ops},
+	{0x414C4710, "ALC200/200P",		&null_ops},
+	{0x43525900, "Cirrus Logic CS4297",	&default_ops},
+	{0x43525903, "Cirrus Logic CS4297",	&default_ops},
+	{0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
+	{0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
+	{0x43525923, "Cirrus Logic CS4298",	&null_ops},
+	{0x4352592B, "Cirrus Logic CS4294",	&null_ops},
+	{0x4352592D, "Cirrus Logic CS4294",	&null_ops},
+	{0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
+	{0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
+	{0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
+	{0x45838308, "ESS Allegro ES1988",	&null_ops},
+	{0x49434511, "ICE1232",			&null_ops}, /* I hope --jk */
+	{0x4e534331, "National Semiconductor LM4549", &null_ops},
+	{0x53494c22, "Silicon Laboratory Si3036", &null_ops},
+	{0x53494c23, "Silicon Laboratory Si3038", &null_ops},
+	{0x545200FF, "TriTech TR?????",		&tritech_m_ops},
+	{0x54524102, "TriTech TR28022",		&null_ops},
+	{0x54524103, "TriTech TR28023",		&null_ops},
+	{0x54524106, "TriTech TR28026",		&null_ops},
+	{0x54524108, "TriTech TR28028",		&tritech_ops},
+	{0x54524123, "TriTech TR A5",		&null_ops},
+	{0x574D4C00, "Wolfson WM9704",		&wolfson_ops},
+	{0x574D4C03, "Wolfson WM9703/9704",	&wolfson_ops},
+	{0x574D4C04, "Wolfson WM9704 (quad)",	&wolfson_ops},
+	{0x83847600, "SigmaTel STAC????",	&null_ops},
+	{0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
+	{0x83847605, "SigmaTel STAC9704",	&null_ops},
+	{0x83847608, "SigmaTel STAC9708",	&sigmatel_9708_ops},
+	{0x83847609, "SigmaTel STAC9721/23",	&sigmatel_9721_ops},
+	{0x83847644, "SigmaTel STAC9744/45",	&sigmatel_9744_ops},
+	{0x83847656, "SigmaTel STAC9756/57",	&sigmatel_9744_ops},
+	{0x83847684, "SigmaTel STAC9783/84?",	&null_ops},
+	{0x57454301, "Winbond 83971D",		&null_ops},
+};
+
+static const char *ac97_stereo_enhancements[] =
+{
+	/*   0 */ "No 3D Stereo Enhancement",
+	/*   1 */ "Analog Devices Phat Stereo",
+	/*   2 */ "Creative Stereo Enhancement",
+	/*   3 */ "National Semi 3D Stereo Enhancement",
+	/*   4 */ "YAMAHA Ymersion",
+	/*   5 */ "BBE 3D Stereo Enhancement",
+	/*   6 */ "Crystal Semi 3D Stereo Enhancement",
+	/*   7 */ "Qsound QXpander",
+	/*   8 */ "Spatializer 3D Stereo Enhancement",
+	/*   9 */ "SRS 3D Stereo Enhancement",
+	/*  10 */ "Platform Tech 3D Stereo Enhancement",
+	/*  11 */ "AKM 3D Audio",
+	/*  12 */ "Aureal Stereo Enhancement",
+	/*  13 */ "Aztech 3D Enhancement",
+	/*  14 */ "Binaura 3D Audio Enhancement",
+	/*  15 */ "ESS Technology Stereo Enhancement",
+	/*  16 */ "Harman International VMAx",
+	/*  17 */ "Nvidea 3D Stereo Enhancement",
+	/*  18 */ "Philips Incredible Sound",
+	/*  19 */ "Texas Instruments 3D Stereo Enhancement",
+	/*  20 */ "VLSI Technology 3D Stereo Enhancement",
+	/*  21 */ "TriTech 3D Stereo Enhancement",
+	/*  22 */ "Realtek 3D Stereo Enhancement",
+	/*  23 */ "Samsung 3D Stereo Enhancement",
+	/*  24 */ "Wolfson Microelectronics 3D Enhancement",
+	/*  25 */ "Delta Integration 3D Enhancement",
+	/*  26 */ "SigmaTel 3D Enhancement",
+	/*  27 */ "Winbond 3D Stereo Enhancement",
+	/*  28 */ "Rockwell 3D Stereo Enhancement",
+	/*  29 */ "Reserved 29",
+	/*  30 */ "Reserved 30",
+	/*  31 */ "Reserved 31"
+};
+
+/* this table has default mixer values for all OSS mixers. */
+static struct mixer_defaults {
+	int mixer;
+	unsigned int value;
+} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
+	/* all values 0 -> 100 in bytes */
+	{SOUND_MIXER_VOLUME,	0x4343},
+	{SOUND_MIXER_BASS,	0x4343},
+	{SOUND_MIXER_TREBLE,	0x4343},
+	{SOUND_MIXER_PCM,	0x4343},
+	{SOUND_MIXER_SPEAKER,	0x4343},
+	{SOUND_MIXER_LINE,	0x4343},
+	{SOUND_MIXER_MIC,	0x0000},
+	{SOUND_MIXER_CD,	0x4343},
+	{SOUND_MIXER_ALTPCM,	0x4343},
+	{SOUND_MIXER_IGAIN,	0x4343},
+	{SOUND_MIXER_LINE1,	0x4343},
+	{SOUND_MIXER_PHONEIN,	0x4343},
+	{SOUND_MIXER_PHONEOUT,	0x4343},
+	{SOUND_MIXER_VIDEO,	0x4343},
+	{-1,0}
+};
+
+/* table to scale scale from OSS mixer value to AC97 mixer register value */	
+static struct ac97_mixer_hw {
+	unsigned char offset;
+	int scale;
+} ac97_hw[SOUND_MIXER_NRDEVICES]= {
+	[SOUND_MIXER_VOLUME]	=	{AC97_MASTER_VOL_STEREO,64},
+	[SOUND_MIXER_BASS]	=	{AC97_MASTER_TONE,	16},
+	[SOUND_MIXER_TREBLE]	=	{AC97_MASTER_TONE,	16},
+	[SOUND_MIXER_PCM]	=	{AC97_PCMOUT_VOL,	32},
+	[SOUND_MIXER_SPEAKER]	=	{AC97_PCBEEP_VOL,	16},
+	[SOUND_MIXER_LINE]	=	{AC97_LINEIN_VOL,	32},
+	[SOUND_MIXER_MIC]	=	{AC97_MIC_VOL,		32},
+	[SOUND_MIXER_CD]	=	{AC97_CD_VOL,		32},
+	[SOUND_MIXER_ALTPCM]	=	{AC97_HEADPHONE_VOL,	64},
+	[SOUND_MIXER_IGAIN]	=	{AC97_RECORD_GAIN,	16},
+	[SOUND_MIXER_LINE1]	=	{AC97_AUX_VOL,		32},
+	[SOUND_MIXER_PHONEIN]	= 	{AC97_PHONE_VOL,	32},
+	[SOUND_MIXER_PHONEOUT]	= 	{AC97_MASTER_VOL_MONO,	64},
+	[SOUND_MIXER_VIDEO]	=	{AC97_VIDEO_VOL,	32},
+};
+
+/* the following tables allow us to go from OSS <-> ac97 quickly. */
+enum ac97_recsettings {
+	AC97_REC_MIC=0,
+	AC97_REC_CD,
+	AC97_REC_VIDEO,
+	AC97_REC_AUX,
+	AC97_REC_LINE,
+	AC97_REC_STEREO, /* combination of all enabled outputs..  */
+	AC97_REC_MONO,	      /*.. or the mono equivalent */
+	AC97_REC_PHONE
+};
+
+static const unsigned int ac97_rm2oss[] = {
+	[AC97_REC_MIC] 	 = SOUND_MIXER_MIC,
+	[AC97_REC_CD] 	 = SOUND_MIXER_CD,
+	[AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
+	[AC97_REC_AUX] 	 = SOUND_MIXER_LINE1,
+	[AC97_REC_LINE]  = SOUND_MIXER_LINE,
+	[AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
+	[AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
+};
+
+/* indexed by bit position */
+static const unsigned int ac97_oss_rm[] = {
+	[SOUND_MIXER_MIC] 	= AC97_REC_MIC,
+	[SOUND_MIXER_CD] 	= AC97_REC_CD,
+	[SOUND_MIXER_VIDEO] 	= AC97_REC_VIDEO,
+	[SOUND_MIXER_LINE1] 	= AC97_REC_AUX,
+	[SOUND_MIXER_LINE] 	= AC97_REC_LINE,
+	[SOUND_MIXER_IGAIN]	= AC97_REC_STEREO,
+	[SOUND_MIXER_PHONEIN] 	= AC97_REC_PHONE
+};
+
+/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
+   about that given mixer, and should be holding a spinlock for the card */
+static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) 
+{
+	u16 val;
+	int ret = 0;
+	int scale;
+	struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
+
+	val = codec->codec_read(codec , mh->offset);
+
+	if (val & AC97_MUTE) {
+		ret = 0;
+	} else if (AC97_STEREO_MASK & (1 << oss_channel)) {
+		/* nice stereo mixers .. */
+		int left,right;
+
+		left = (val >> 8)  & 0x7f;
+		right = val  & 0x7f;
+
+		if (oss_channel == SOUND_MIXER_IGAIN) {
+			right = (right * 100) / mh->scale;
+			left = (left * 100) / mh->scale;
+		} else {
+			/* these may have 5 or 6 bit resolution */
+			if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
+				scale = (1 << codec->bit_resolution);
+			else
+				scale = mh->scale;
+
+			right = 100 - ((right * 100) / scale);
+			left = 100 - ((left * 100) / scale);
+		}
+		ret = left | (right << 8);
+	} else if (oss_channel == SOUND_MIXER_SPEAKER) {
+		ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
+	} else if (oss_channel == SOUND_MIXER_PHONEIN) {
+		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+	} else if (oss_channel == SOUND_MIXER_PHONEOUT) {
+		scale = (1 << codec->bit_resolution);
+		ret = 100 - (((val & 0x1f) * 100) / scale);
+	} else if (oss_channel == SOUND_MIXER_MIC) {
+		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+		/*  the low bit is optional in the tone sliders and masking
+		    it lets us avoid the 0xf 'bypass'.. */
+	} else if (oss_channel == SOUND_MIXER_BASS) {
+		ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
+	} else if (oss_channel == SOUND_MIXER_TREBLE) {
+		ret = 100 - (((val & 0xe) * 100) / mh->scale);
+	}
+
+#ifdef DEBUG
+	printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
+	       "0x%04x -> 0x%04x\n",
+	       oss_channel, codec->id ? "Secondary" : "Primary",
+	       mh->offset, val, ret);
+#endif
+
+	return ret;
+}
+
+/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
+   make sure all is well in arg land, call with spinlock held */
+static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
+		      unsigned int left, unsigned int right)
+{
+	u16 val = 0;
+	int scale;
+	struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
+
+#ifdef DEBUG
+	printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
+	       "left vol:%2d, right vol:%2d:",
+	       oss_channel, codec->id ? "Secondary" : "Primary",
+	       mh->offset, left, right);
+#endif
+
+	if (AC97_STEREO_MASK & (1 << oss_channel)) {
+		/* stereo mixers */
+		if (left == 0 && right == 0) {
+			val = AC97_MUTE;
+		} else {
+			if (oss_channel == SOUND_MIXER_IGAIN) {
+				right = (right * mh->scale) / 100;
+				left = (left * mh->scale) / 100;
+				if (right >= mh->scale)
+					right = mh->scale-1;
+				if (left >= mh->scale)
+					left = mh->scale-1;
+			} else {
+				/* these may have 5 or 6 bit resolution */
+				if (oss_channel == SOUND_MIXER_VOLUME ||
+				    oss_channel == SOUND_MIXER_ALTPCM)
+					scale = (1 << codec->bit_resolution);
+				else
+					scale = mh->scale;
+
+				right = ((100 - right) * scale) / 100;
+				left = ((100 - left) * scale) / 100;
+				if (right >= scale)
+					right = scale-1;
+				if (left >= scale)
+					left = scale-1;
+			}
+			val = (left << 8) | right;
+		}
+	} else if (oss_channel == SOUND_MIXER_BASS) {
+		val = codec->codec_read(codec , mh->offset) & ~0x0f00;
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val |= (left << 8) & 0x0e00;
+	} else if (oss_channel == SOUND_MIXER_TREBLE) {
+		val = codec->codec_read(codec , mh->offset) & ~0x000f;
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val |= left & 0x000e;
+	} else if(left == 0) {
+		val = AC97_MUTE;
+	} else if (oss_channel == SOUND_MIXER_SPEAKER) {
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val = left << 1;
+	} else if (oss_channel == SOUND_MIXER_PHONEIN) {
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val = left;
+	} else if (oss_channel == SOUND_MIXER_PHONEOUT) {
+		scale = (1 << codec->bit_resolution);
+		left = ((100 - left) * scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val = left;
+	} else if (oss_channel == SOUND_MIXER_MIC) {
+		val = codec->codec_read(codec , mh->offset) & ~0x801f;
+		left = ((100 - left) * mh->scale) / 100;
+		if (left >= mh->scale)
+			left = mh->scale-1;
+		val |= left;
+		/*  the low bit is optional in the tone sliders and masking
+		    it lets us avoid the 0xf 'bypass'.. */
+	}
+#ifdef DEBUG
+	printk(" 0x%04x", val);
+#endif
+
+	codec->codec_write(codec, mh->offset, val);
+
+#ifdef DEBUG
+	val = codec->codec_read(codec, mh->offset);
+	printk(" -> 0x%04x\n", val);
+#endif
+}
+
+/* a thin wrapper for write_mixer */
+static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) 
+{
+	unsigned int left,right;
+
+	/* cleanse input a little */
+	right = ((val >> 8)  & 0xff) ;
+	left = (val  & 0xff) ;
+
+	if (right > 100) right = 100;
+	if (left > 100) left = 100;
+
+	codec->mixer_state[oss_mixer] = (right << 8) | left;
+	codec->write_mixer(codec, oss_mixer, left, right);
+}
+
+/* read or write the recmask, the ac97 can really have left and right recording
+   inputs independantly set, but OSS doesn't seem to want us to express that to
+   the user. the caller guarantees that we have a supported bit set, and they
+   must be holding the card's spinlock */
+static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) 
+{
+	unsigned int val;
+
+	if (rw) {
+		/* read it from the card */
+		val = codec->codec_read(codec, AC97_RECORD_SELECT);
+#ifdef DEBUG
+		printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
+#endif
+		return (1 << ac97_rm2oss[val & 0x07]);
+	}
+
+	/* else, write the first set in the mask as the
+	   output */	
+	/* clear out current set value first (AC97 supports only 1 input!) */
+	val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
+	if (mask != val)
+	    mask &= ~val;
+       
+	val = ffs(mask); 
+	val = ac97_oss_rm[val-1];
+	val |= val << 8;  /* set both channels */
+
+#ifdef DEBUG
+	printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
+#endif
+
+	codec->codec_write(codec, AC97_RECORD_SELECT, val);
+
+	return 0;
+};
+
+static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
+{
+	int i, val = 0;
+
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, codec->name, sizeof(info.id));
+		strncpy(info.name, codec->name, sizeof(info.name));
+		info.modify_counter = codec->modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, codec->name, sizeof(info.id));
+		strncpy(info.name, codec->name, sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+		return -EINVAL;
+
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	if (_SIOC_DIR(cmd) == _SIOC_READ) {
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC: /* give them the current record source */
+			if (!codec->recmask_io) {
+				val = 0;
+			} else {
+				val = codec->recmask_io(codec, 1, 0);
+			}
+			break;
+
+		case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
+			val = codec->supported_mixers;
+			break;
+
+		case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			val = codec->record_sources;
+			break;
+
+		case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			val = codec->stereo_mixers;
+			break;
+
+		case SOUND_MIXER_CAPS:
+			val = SOUND_CAP_EXCL_INPUT;
+			break;
+
+		default: /* read a specific mixer */
+			i = _IOC_NR(cmd);
+
+			if (!supported_mixer(codec, i)) 
+				return -EINVAL;
+
+			/* do we ever want to touch the hardware? */
+		        /* val = codec->read_mixer(codec, i); */
+			val = codec->mixer_state[i];
+ 			break;
+		}
+		return put_user(val, (int *)arg);
+	}
+
+	if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
+		codec->modcnt++;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			if (!codec->recmask_io) return -EINVAL;
+			if (!val) return 0;
+			if (!(val &= codec->record_sources)) return -EINVAL;
+
+			codec->recmask_io(codec, 0, val);
+
+			return 0;
+		default: /* write a specific mixer */
+			i = _IOC_NR(cmd);
+
+			if (!supported_mixer(codec, i)) 
+				return -EINVAL;
+
+			ac97_set_mixer(codec, i, val);
+
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+/* entry point for /proc/driver/controller_vendor/ac97/%d */
+int ac97_read_proc (char *page, char **start, off_t off,
+		    int count, int *eof, void *data)
+{
+	int len = 0, cap, extid, val, id1, id2;
+	struct ac97_codec *codec;
+	int is_ac97_20 = 0;
+
+	if ((codec = data) == NULL)
+		return -ENODEV;
+
+	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+	len += sprintf (page+len, "Vendor name      : %s\n", codec->name);
+	len += sprintf (page+len, "Vendor id        : %04X %04X\n", id1, id2);
+
+	extid = codec->codec_read(codec, AC97_EXTENDED_ID);
+	extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13));
+	len += sprintf (page+len, "AC97 Version     : %s\n",
+			extid ? "2.0 or later" : "1.0");
+	if (extid) is_ac97_20 = 1;
+
+	cap = codec->codec_read(codec, AC97_RESET);
+	len += sprintf (page+len, "Capabilities     :%s%s%s%s%s%s\n",
+			cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "",
+			cap & 0x0002 ? " -reserved1-" : "",
+			cap & 0x0004 ? " -bass & treble-" : "",
+			cap & 0x0008 ? " -simulated stereo-" : "",
+			cap & 0x0010 ? " -headphone out-" : "",
+			cap & 0x0020 ? " -loudness-" : "");
+	val = cap & 0x00c0;
+	len += sprintf (page+len, "DAC resolutions  :%s%s%s\n",
+			" -16-bit-",
+			val & 0x0040 ? " -18-bit-" : "",
+			val & 0x0080 ? " -20-bit-" : "");
+	val = cap & 0x0300;
+	len += sprintf (page+len, "ADC resolutions  :%s%s%s\n",
+			" -16-bit-",
+			val & 0x0100 ? " -18-bit-" : "",
+			val & 0x0200 ? " -20-bit-" : "");
+	len += sprintf (page+len, "3D enhancement   : %s\n",
+			ac97_stereo_enhancements[(cap >> 10) & 0x1f]);
+
+	val = codec->codec_read(codec, AC97_GENERAL_PURPOSE);
+	len += sprintf (page+len, "POP path         : %s 3D\n"
+			"Sim. stereo      : %s\n"
+			"3D enhancement   : %s\n"
+			"Loudness         : %s\n"
+			"Mono output      : %s\n"
+			"MIC select       : %s\n"
+			"ADC/DAC loopback : %s\n",
+			val & 0x8000 ? "post" : "pre",
+			val & 0x4000 ? "on" : "off",
+			val & 0x2000 ? "on" : "off",
+			val & 0x1000 ? "on" : "off",
+			val & 0x0200 ? "MIC" : "MIX",
+			val & 0x0100 ? "MIC2" : "MIC1",
+			val & 0x0080 ? "on" : "off");
+
+	extid = codec->codec_read(codec, AC97_EXTENDED_ID);
+	cap = extid;
+	len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n",
+			cap & 0x0001 ? " -var rate PCM audio-" : "",
+			cap & 0x0002 ? " -2x PCM audio out-" : "",
+			cap & 0x0008 ? " -var rate MIC in-" : "",
+			cap & 0x0040 ? " -PCM center DAC-" : "",
+			cap & 0x0080 ? " -PCM surround DAC-" : "",
+			cap & 0x0100 ? " -PCM LFE DAC-" : "",
+			cap & 0x0200 ? " -slot/DAC mappings-" : "");
+	if (is_ac97_20) {
+		len += sprintf (page+len, "Front DAC rate   : %d\n",
+				codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE));
+	}
+
+	return len;
+}
+
+/**
+ *	ac97_probe_codec - Initialize and setup AC97-compatible codec
+ *	@codec: (in/out) Kernel info for a single AC97 codec
+ *
+ *	Reset the AC97 codec, then initialize the mixer and
+ *	the rest of the @codec structure.
+ *
+ *	The codec_read and codec_write fields of @codec are
+ *	required to be setup and working when this function
+ *	is called.  All other fields are set by this function.
+ *
+ *	codec_wait field of @codec can optionally be provided
+ *	when calling this function.  If codec_wait is not %NULL,
+ *	this function will call codec_wait any time it is
+ *	necessary to wait for the audio chip to reach the
+ *	codec-ready state.  If codec_wait is %NULL, then
+ *	the default behavior is to call schedule_timeout.
+ *	Currently codec_wait is used to wait for AC97 codec
+ *	reset to complete. 
+ *
+ *	Returns 1 (true) on success, or 0 (false) on failure.
+ */
+ 
+int ac97_probe_codec(struct ac97_codec *codec)
+{
+	u16 id1, id2;
+	u16 audio, modem;
+	int i;
+
+	/* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should 
+	 * be read zero.
+	 *
+	 * FIXME: is the following comment outdated?  -jgarzik 
+	 * Probing of AC97 in this way is not reliable, it is not even SAFE !!
+	 */
+	codec->codec_write(codec, AC97_RESET, 0L);
+
+	/* also according to spec, we wait for codec-ready state */	
+	if (codec->codec_wait)
+		codec->codec_wait(codec);
+	else
+		udelay(10);
+
+	if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
+		printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
+		       codec->id ? "Secondary" : "Primary");
+		return 0;
+	}
+
+	/* probe for Modem Codec */
+#if CONFIG_SOUND_ALSA_NO_MODEM_PROBE
+	/* Do not probe for Modem Codec. 
+	   This prevents IrDA lockups on some laptops (Dell Inspiron)
+       when sound is initialized after IrDA init. -jprante */
+	modem = 0; 
+#else
+	codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
+	modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID);
+#endif
+
+	codec->name = NULL;
+	codec->codec_ops = &null_ops;
+
+	id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
+	id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
+	for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
+		if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
+			codec->type = ac97_codec_ids[i].id;
+			codec->name = ac97_codec_ids[i].name;
+			codec->codec_ops = ac97_codec_ids[i].ops;
+			break;
+		}
+	}
+	if (codec->name == NULL)
+		codec->name = "Unknown";
+	printk(KERN_INFO "ac97_codec: AC97 %s codec, id: 0x%04x:"
+	       "0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""),
+	       id1, id2, codec->name);
+
+	return ac97_init_mixer(codec);
+}
+
+static int ac97_init_mixer(struct ac97_codec *codec)
+{
+	u16 cap;
+	int i;
+
+	cap = codec->codec_read(codec, AC97_RESET);
+
+	/* mixer masks */
+	codec->supported_mixers = AC97_SUPPORTED_MASK;
+	codec->stereo_mixers = AC97_STEREO_MASK;
+	codec->record_sources = AC97_RECORD_MASK;
+	if (!(cap & 0x04))
+		codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
+	if (!(cap & 0x10))
+		codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
+
+	/* detect bit resolution */
+	codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
+	if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x1f1f)
+		codec->bit_resolution = 5;
+	else
+		codec->bit_resolution = 6;
+
+	/* generic OSS to AC97 wrapper */
+	codec->read_mixer = ac97_read_mixer;
+	codec->write_mixer = ac97_write_mixer;
+	codec->recmask_io = ac97_recmask_io;
+	codec->mixer_ioctl = ac97_mixer_ioctl;
+
+	/* codec specific initialization for 4-6 channel output or secondary codec stuff */
+	if (codec->codec_ops->init != NULL) {
+		codec->codec_ops->init(codec);
+	}
+
+	/* initialize mixer channel volumes */
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+		struct mixer_defaults *md = &mixer_defaults[i];
+		if (md->mixer == -1) 
+			break;
+		if (!supported_mixer(codec, md->mixer)) 
+			continue;
+		ac97_set_mixer(codec, md->mixer, md->value);
+	}
+
+	return 1;
+}
+
+#define AC97_SIGMATEL_ANALOG    0x6c	/* Analog Special */
+#define AC97_SIGMATEL_DAC2INVERT 0x6e
+#define AC97_SIGMATEL_BIAS1     0x70
+#define AC97_SIGMATEL_BIAS2     0x72
+#define AC97_SIGMATEL_MULTICHN  0x74	/* Multi-Channel programming */
+#define AC97_SIGMATEL_CIC1      0x76
+#define AC97_SIGMATEL_CIC2      0x78
+
+
+static int sigmatel_9708_init(struct ac97_codec * codec)
+{
+	u16 codec72, codec6c;
+
+	codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
+	codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
+
+	if ((codec72==0) && (codec6c==0)) {
+		codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+		codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
+		codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
+		codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
+	} else if ((codec72==0x8000) && (codec6c==0)) {
+		codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+		codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
+		codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
+	} else if ((codec72==0x8000) && (codec6c==0x0080)) {
+		/* nothing */
+	}
+	codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+
+static int sigmatel_9721_init(struct ac97_codec * codec)
+{
+	/* Only set up secondary codec */
+	if (codec->id == 0)
+		return 0;
+
+	codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
+
+	/* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
+	   sloc 3,4 = 0x01, slot 7,8 = 0x00, */
+	codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
+
+	/* we don't have the crystal when we are on an AMR card, so use
+	   BIT_CLK as our clock source. Write the magic word ABBA and read
+	   back to enable register 0x78 */
+	codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+	codec->codec_read(codec, AC97_SIGMATEL_CIC1);
+
+	/* sync all the clocks*/
+	codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
+
+	return 0;
+}
+
+
+static int sigmatel_9744_init(struct ac97_codec * codec)
+{
+	// patch for SigmaTel
+	codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
+	codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
+	codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
+	codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
+	codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+
+static int wolfson_init(struct ac97_codec * codec)
+{
+	codec->codec_write(codec, 0x72, 0x0808);
+	codec->codec_write(codec, 0x74, 0x0808);
+
+	// patch for DVD noise
+	codec->codec_write(codec, 0x5a, 0x0200);
+
+	// init vol as PCM vol
+	codec->codec_write(codec, 0x70,
+		codec->codec_read(codec, AC97_PCMOUT_VOL));
+
+	codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
+	return 0;
+}
+
+
+static int tritech_init(struct ac97_codec * codec)
+{
+	codec->codec_write(codec, 0x26, 0x0300);
+	codec->codec_write(codec, 0x26, 0x0000);
+	codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
+	codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
+	return 0;
+}
+
+
+/* copied from drivers/sound/maestro.c */
+static int tritech_maestro_init(struct ac97_codec * codec)
+{
+	/* no idea what this does */
+	codec->codec_write(codec, 0x2A, 0x0001);
+	codec->codec_write(codec, 0x2C, 0x0000);
+	codec->codec_write(codec, 0x2C, 0XFFFF);
+	return 0;
+}
+
+
+/*
+ *	This is basically standard AC97. It should work as a default for
+ *	almost all modern codecs. Note that some cards wire EAPD *backwards*
+ *	That side of it is up to the card driver not us to cope with.
+ *
+ */
+
+static int eapd_control(struct ac97_codec * codec, int on)
+{
+	if(on)
+		codec->codec_write(codec, AC97_POWER_CONTROL,
+			codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
+	else
+		codec->codec_write(codec, AC97_POWER_CONTROL,
+			codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
+	return 0;
+}
+
+/*
+ *	Crystal digital audio control (CS4299
+ */
+ 
+static int crystal_digital_control(struct ac97_codec *codec, int mode)
+{
+	u16 cv;
+
+	switch(mode)
+	{
+		case 0: cv = 0x0; break;	/* SPEN off */
+		case 1: cv = 0x8004; break;	/* 48KHz digital */
+		case 2: cv = 0x8104; break;	/* 44.1KHz digital */
+		default:
+			return -1;		/* Not supported yet(eg AC3) */
+	}
+	codec->codec_write(codec, 0x68, cv);
+	return 0;
+}
+
+/* copied from drivers/sound/maestro.c */
+#if 0  /* there has been 1 person on the planet with a pt101 that we
+        know of.  If they care, they can put this back in :) */
+static int pt101_init(struct ac97_codec * codec)
+{
+	printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
+	/* who knows.. */
+	codec->codec_write(codec, 0x2A, 0x0001);
+	codec->codec_write(codec, 0x2C, 0x0000);
+	codec->codec_write(codec, 0x2C, 0xFFFF);
+	codec->codec_write(codec, 0x10, 0x9F1F);
+	codec->codec_write(codec, 0x12, 0x0808);
+	codec->codec_write(codec, 0x14, 0x9F1F);
+	codec->codec_write(codec, 0x16, 0x9F1F);
+	codec->codec_write(codec, 0x18, 0x0404);
+	codec->codec_write(codec, 0x1A, 0x0000);
+	codec->codec_write(codec, 0x1C, 0x0000);
+	codec->codec_write(codec, 0x02, 0x0404);
+	codec->codec_write(codec, 0x04, 0x0808);
+	codec->codec_write(codec, 0x0C, 0x801F);
+	codec->codec_write(codec, 0x0E, 0x801F);
+	return 0;
+}
+#endif
+	
+
+EXPORT_SYMBOL(ac97_read_proc);
+EXPORT_SYMBOL(ac97_probe_codec);
+
+/*
+ *	AC97 library support routines
+ */	
+ 
+/**
+ *	ac97_set_dac_rate	-	set codec rate adaption
+ *	@codec: ac97 code
+ *	@rate: rate in hertz
+ *
+ *	Set the DAC rate. Assumes the codec supports VRA. The caller is
+ *	expected to have checked this little detail.
+ */
+ 
+unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate)
+{
+	unsigned int new_rate = rate;
+	u32 dacp;
+	u32 mast_vol, phone_vol, mono_vol, pcm_vol;
+	u32 mute_vol = 0x8000;	/* The mute volume? */
+
+	if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE))
+	{
+		/* Mute several registers */
+		mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO);
+		mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO);
+		phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL);
+		pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL);
+		codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol);
+		codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol);
+		codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol);
+		codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol);
+		
+		/* Power down the DAC */
+		dacp=codec->codec_read(codec, AC97_POWER_CONTROL);
+		codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200);
+		/* Load the rate and read the effective rate */
+		codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate);
+		new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE);
+		/* Power it back up */
+		codec->codec_write(codec, AC97_POWER_CONTROL, dacp);
+
+		/* Restore volumes */
+		codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol);
+		codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol);
+		codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol);
+		codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol);
+	}
+	return new_rate;
+}
+
+EXPORT_SYMBOL(ac97_set_dac_rate);
+
+/**
+ *	ac97_set_adc_rate	-	set codec rate adaption
+ *	@codec: ac97 code
+ *	@rate: rate in hertz
+ *
+ *	Set the ADC rate. Assumes the codec supports VRA. The caller is
+ *	expected to have checked this little detail.
+ */
+
+unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate)
+{
+	unsigned int new_rate = rate;
+	u32 dacp;
+
+	if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE))
+	{
+		/* Power down the ADC */
+		dacp=codec->codec_read(codec, AC97_POWER_CONTROL);
+		codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100);
+		/* Load the rate and read the effective rate */
+		codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate);
+		new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE);
+		/* Power it back up */
+		codec->codec_write(codec, AC97_POWER_CONTROL, dacp);
+	}
+	return new_rate;
+}
+
+EXPORT_SYMBOL(ac97_set_adc_rate);
+
+int ac97_save_state(struct ac97_codec *codec)
+{
+	return 0;	
+}
+
+EXPORT_SYMBOL(ac97_save_state);
+
+int ac97_restore_state(struct ac97_codec *codec)
+{
+	int i;
+	unsigned int left, right, val;
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+		if (!supported_mixer(codec, i)) 
+			continue;
+
+		val = codec->mixer_state[i];
+		right = val >> 8;
+		left = val  & 0xff;
+		codec->write_mixer(codec, i, left, right);
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(ac97_restore_state);
+
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/aci.c linux-2.4.19-pre5-mjc/sound/oss/aci.c
--- linux/sound/oss/aci.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/aci.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,711 @@
+/*
+ * Audio Command Interface (ACI) driver (sound/aci.c)
+ *
+ * ACI is a protocol used to communicate with the microcontroller on
+ * some sound cards produced by miro, e.g. the miroSOUND PCM12 and
+ * PCM20. The ACI has been developed for miro by Norberto Pellicci
+ * <pellicci@home.com>. Special thanks to both him and miro for
+ * providing the ACI specification.
+ *
+ * The main function of the ACI is to control the mixer and to get a
+ * product identification. On the PCM20, ACI also controls the radio
+ * tuner on this card, this is supported in the Video for Linux 
+ * miropcm20 driver.
+ * -
+ * This is a fullfeatured implementation. Unsupported features
+ * are bugs... (:
+ *
+ * It is not longer necessary to load the mad16 module first. The
+ * user is currently responsible to set the mad16 mixer correctly.
+ *
+ * To toggle the solo mode for full duplex operation just use the OSS
+ * record switch for the pcm ('wave') controller.           Robert
+ * -
+ *
+ * Revision history:
+ *
+ *   1995-11-10  Markus Kuhn <mskuhn@cip.informatik.uni-erlangen.de>
+ *        First version written.
+ *   1995-12-31  Markus Kuhn
+ *        Second revision, general code cleanup.
+ *   1996-05-16	 Hannu Savolainen
+ *	  Integrated with other parts of the driver.
+ *   1996-05-28  Markus Kuhn
+ *        Initialize CS4231A mixer, make ACI first mixer,
+ *        use new private mixer API for solo mode.
+ *   1998-08-18  Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ *	  Small modification to export ACI functions and 
+ *	  complete modularisation.
+ *   2000-06-20  Robert Siemer <Robert.Siemer@gmx.de>
+ *        Don't initialize the CS4231A mixer anymore, so the code is
+ *        working again, and other small changes to fit in todays
+ *        kernels.
+ *   2000-08-26  Robert Siemer
+ *        Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (:
+ *        ioctl bugfix, and integration of solo-mode into OSS-API,
+ *        added (OSS-limited) equalizer support, return value bugfix,
+ *        changed param aci_reset to reset, new params: ide, wss.
+ *   2001-04-20  Robert Siemer
+ *        even more cleanups...
+ *   2001-10-08  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *   	  Get rid of check_region, .bss optimizations, use set_current_state
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h> 
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "sound_config.h"
+
+int aci_port;	/* as determined by bit 4 in the OPTi 929 MC4 register */
+int aci_idcode[2];	/* manufacturer and product ID */
+int aci_version;	/* ACI firmware version	*/
+
+EXPORT_SYMBOL(aci_port);
+EXPORT_SYMBOL(aci_idcode);
+EXPORT_SYMBOL(aci_version);
+
+#include "aci.h"
+
+
+static int aci_solo;	/* status bit of the card that can't be		*
+			 * checked with ACI versions prior to 0xb0	*/
+static int aci_amp;   /* status bit for power-amp/line-out level
+			   but I have no docs about what is what... */
+static int aci_micpreamp=3; /* microphone preamp-level that can't be    *
+			 * checked with ACI versions prior to 0xb0	*/
+
+static int mixer_device;
+static struct semaphore aci_sem;
+
+#ifdef MODULE
+static int reset;
+MODULE_PARM(reset,"i");
+MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer.");
+#else
+static int reset = 1;
+#endif
+
+static int ide=-1;
+MODULE_PARM(ide,"i");
+MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested"
+		 " default: do nothing");
+static int wss=-1;
+MODULE_PARM(wss,"i");
+MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested"
+		 " default: do nothing; for PCM1-pro only");
+
+#if DEBUG
+static void print_bits(unsigned char c)
+{
+	int j;
+	printk(KERN_DEBUG "aci: ");
+
+	for (j=7; j>=0; j--) {
+		printk("%d", (c >> j) & 0x1);
+	}
+
+	printk("\n");
+}
+#endif
+
+/*
+ * This busy wait code normally requires less than 15 loops and
+ * practically always less than 100 loops on my i486/DX2 66 MHz.
+ *
+ * Warning: Waiting on the general status flag after reseting the MUTE
+ * function can take a VERY long time, because the PCM12 does some kind
+ * of fade-in effect. For this reason, access to the MUTE function has
+ * not been implemented at all.
+ *
+ * - The OSS interface has no mute option. It takes about 3 seconds to
+ * fade-in on my PCM20. busy_wait() handles it great now...     Robert
+ */
+
+static int busy_wait(void)
+{
+	#define MINTIME 500
+	long timeout;
+	unsigned char byte;
+
+	for (timeout = 1; timeout <= MINTIME+30; timeout++) {
+		if (((byte=inb(BUSY_REGISTER)) & 1) == 0) {
+			if (timeout >= MINTIME)
+				printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME);
+			return byte;
+		}
+		if (timeout >= MINTIME) {
+			long out=10*HZ;
+			switch (timeout-MINTIME) {
+			case 0 ... 9:
+				out /= 10;
+			case 10 ... 19:
+				out /= 10;
+			case 20 ... 30:
+				out /= 10;
+			default:
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule_timeout(out);
+				break;
+			}
+		}
+	}
+	printk(KERN_WARNING "aci: busy_wait() time out.\n");
+	return -EBUSY;
+}
+
+/* The four ACI command types are fucked up. [-:
+ * implied is: 1w      - special case for INIT
+ * write   is: 2w1r
+ * read    is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER,
+ *                                        1 VERSION, 2 IDCODE)
+ *  the command is only in the first write, rest is protocol overhead
+ *
+ * indexed is technically a write and used for STATUS
+ * and the special case for TUNE is: 3w1r
+ * 
+ * Here the new general sheme: TUNE --> aci_rw_cmd(x,  y,  z)
+ *                indexed and write --> aci_rw_cmd(x,  y, -1)
+ *           implied and read (x=1) --> aci_rw_cmd(x, -1, -1)
+ *
+ * Read (x>=2) is not implemented (only used during initialization).
+ * Use aci_idcode[2] and aci_version...                    Robert
+ */
+
+/* Some notes for error detection: theoretically it is possible.
+ * But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal 
+ * case and doesn't seem to be designed for that...        Robert
+ */
+
+static inline int aci_rawwrite(unsigned char byte)
+{
+	if (busy_wait() >= 0) {
+#if DEBUG
+		printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte);
+#endif
+		outb(byte, COMMAND_REGISTER);
+		return 0;
+	} else
+		return -EBUSY;
+}
+
+static inline int aci_rawread(void)
+{
+	unsigned char byte;
+
+	if (busy_wait() >= 0) {
+		byte=inb(STATUS_REGISTER);
+#if DEBUG
+		printk(KERN_DEBUG "%d = aci_rawread()\n", byte);
+#endif
+		return byte;
+	} else
+		return -EBUSY;
+}
+
+
+int aci_rw_cmd(int write1, int write2, int write3)
+{
+	int write[] = {write1, write2, write3};
+	int read = -EINTR, i;
+
+	if (down_interruptible(&aci_sem))
+		goto out;
+
+	for (i=0; i<3; i++) {
+		if (write[i]< 0 || write[i] > 255)
+			break;
+		else {
+			read = aci_rawwrite(write[i]);
+			if (read < 0)
+				goto out_up;
+		}
+		
+	}
+	
+	read = aci_rawread();
+out_up:	up(&aci_sem);
+out:	return read;
+}
+
+EXPORT_SYMBOL(aci_rw_cmd);
+
+static int setvolume(caddr_t arg, 
+		     unsigned char left_index, unsigned char right_index)
+{
+	int vol, ret, uservol, buf;
+
+	__get_user(uservol, (int *)arg);
+
+	/* left channel */
+	vol = uservol & 0xff;
+	if (vol > 100)
+		vol = 100;
+	vol = SCALE(100, 0x20, vol);
+	if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0)
+		return buf;
+	ret = SCALE(0x20, 100, vol);
+
+
+	/* right channel */
+	vol = (uservol >> 8) & 0xff;
+	if (vol > 100)
+		vol = 100;
+	vol = SCALE(100, 0x20, vol);
+	if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0)
+		return buf;
+	ret |= SCALE(0x20, 100, vol) << 8;
+ 
+	__put_user(ret, (int *)arg);
+
+	return 0;
+}
+
+static int getvolume(caddr_t arg,
+		     unsigned char left_index, unsigned char right_index)
+{
+	int vol;
+	int buf;
+
+	/* left channel */
+	if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0)
+		return buf;
+	vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0);
+	
+	/* right channel */
+	if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0)
+		return buf;
+	vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
+
+	__put_user(vol, (int *)arg);
+
+	return 0;
+}
+
+
+/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB
+ * write:  0xff..down.to..0x80==0x00..up.to..0x7f
+ */
+
+static inline unsigned int eq_oss2aci(unsigned int vol)
+{
+	int boost=0;
+	unsigned int ret;
+
+	if (vol > 100)
+		vol = 100;
+	if (vol > 50) {
+		vol -= 51;
+		boost=1;
+	}
+	if (boost)
+		ret=SCALE(49, 0x7e, vol)+1;
+	else
+		ret=0xff - SCALE(50, 0x7f, vol);
+	return ret;
+}
+
+static inline unsigned int eq_aci2oss(unsigned int vol)
+{
+	if (vol < 0x80)
+		return SCALE(0x7f, 50, vol) + 50;
+	else
+		return SCALE(0x7f, 50, 0xff-vol);
+}
+
+
+static int setequalizer(caddr_t arg, 
+			unsigned char left_index, unsigned char right_index)
+{
+	int buf;
+	unsigned int vol;
+
+	__get_user(vol, (int *)arg);
+
+	/* left channel */
+	if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0)
+		return buf;
+
+	/* right channel */
+	if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0)
+		return buf;
+
+	/* the ACI equalizer is more precise */
+	return 0;
+}
+
+static int getequalizer(caddr_t arg,
+			unsigned char left_index, unsigned char right_index)
+{
+	int buf;
+	unsigned int vol;
+
+	/* left channel */
+	if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0)
+		return buf;
+	vol = eq_aci2oss(buf);
+	
+	/* right channel */
+	if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0)
+		return buf;
+	vol |= eq_aci2oss(buf) << 8;
+
+	__put_user(vol, (int *)arg);
+
+	return 0;
+}
+
+static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+	int vol, buf;
+
+	switch (cmd) {
+	case SOUND_MIXER_WRITE_VOLUME:
+		return setvolume(arg, 0x01, 0x00);
+	case SOUND_MIXER_WRITE_CD:
+		return setvolume(arg, 0x3c, 0x34);
+	case SOUND_MIXER_WRITE_MIC:
+		return setvolume(arg, 0x38, 0x30);
+	case SOUND_MIXER_WRITE_LINE:
+		return setvolume(arg, 0x39, 0x31);
+	case SOUND_MIXER_WRITE_SYNTH:
+		return setvolume(arg, 0x3b, 0x33);
+	case SOUND_MIXER_WRITE_PCM:
+		return setvolume(arg, 0x3a, 0x32);
+	case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */
+	case SOUND_MIXER_WRITE_LINE1:  /* AUX1 or radio */
+		return setvolume(arg, 0x3d, 0x35);
+	case SOUND_MIXER_WRITE_LINE2:  /* AUX2 */
+		return setvolume(arg, 0x3e, 0x36);
+	case SOUND_MIXER_WRITE_BASS:   /* set band one and two */
+		if (aci_idcode[1]=='C') {
+			if ((buf=setequalizer(arg, 0x48, 0x40)) || 
+			    (buf=setequalizer(arg, 0x49, 0x41)));
+			return buf;
+		}
+		break;
+	case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */
+		if (aci_idcode[1]=='C') {
+			if ((buf=setequalizer(arg, 0x4d, 0x45)) || 
+			    (buf=setequalizer(arg, 0x4e, 0x46)));
+			return buf;
+		}
+		break;
+	case SOUND_MIXER_WRITE_IGAIN:  /* MIC pre-amp */
+		if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
+			__get_user(vol, (int *)arg);
+			vol = vol & 0xff;
+			if (vol > 100)
+				vol = 100;
+			vol = SCALE(100, 3, vol);
+			if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0)
+				return buf;
+			aci_micpreamp = vol;
+			vol = SCALE(3, 100, vol);
+			vol |= (vol << 8);
+			__put_user(vol, (int *)arg);
+			return 0;
+		}
+		break;
+	case SOUND_MIXER_WRITE_OGAIN:  /* Power-amp/line-out level */
+		if (aci_idcode[1]=='A' || aci_idcode[1]=='B') {
+			__get_user(buf, (int *)arg);
+			buf = buf & 0xff;
+			if (buf > 50)
+				vol = 1;
+			else
+				vol = 0;
+			if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0)
+				return buf;
+			aci_amp = vol;
+			if (aci_amp)
+				buf = (100 || 100<<8);
+			else
+				buf = 0;
+			__put_user(buf, (int *)arg);
+			return 0;
+		}
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		/* handle solo mode control */
+		__get_user(buf, (int *)arg);
+		/* unset solo when RECSRC for PCM is requested */
+		if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
+			vol = !(buf & SOUND_MASK_PCM);
+			if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0)
+				return buf;
+			aci_solo = vol;
+		}
+		buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE|
+		       SOUND_MASK_SYNTH| SOUND_MASK_LINE2);
+		if (aci_idcode[1] == 'C') /* PCM20 radio */
+			buf |= SOUND_MASK_RADIO;
+		else
+			buf |= SOUND_MASK_LINE1;
+		if (!aci_solo)
+			buf |= SOUND_MASK_PCM;
+		__put_user(buf, (int *)arg);
+		return 0;
+	case SOUND_MIXER_READ_DEVMASK:
+		buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD    |
+		       SOUND_MASK_MIC    | SOUND_MASK_LINE  |
+		       SOUND_MASK_SYNTH  | SOUND_MASK_PCM   |
+		       SOUND_MASK_LINE2);
+		switch (aci_idcode[1]) {
+		case 'C': /* PCM20 radio */
+			buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN |
+				SOUND_MASK_BASS  | SOUND_MASK_TREBLE);
+			break;
+		case 'B': /* PCM12 */
+			buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN |
+				SOUND_MASK_OGAIN);
+			break;
+		case 'A': /* PCM1-pro */
+			buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN);
+			break;
+		default:
+			buf |= SOUND_MASK_LINE1;
+		}
+		__put_user(buf, (int *)arg);
+		return 0;
+	case SOUND_MIXER_READ_STEREODEVS:
+		buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD    |
+		       SOUND_MASK_MIC    | SOUND_MASK_LINE  |
+		       SOUND_MASK_SYNTH  | SOUND_MASK_PCM   |
+		       SOUND_MASK_LINE2);
+		switch (aci_idcode[1]) {
+		case 'C': /* PCM20 radio */
+			buf |= (SOUND_MASK_RADIO |
+				SOUND_MASK_BASS  | SOUND_MASK_TREBLE);
+			break;
+		default:
+			buf |= SOUND_MASK_LINE1;
+		}
+		__put_user(buf, (int *)arg);
+		return 0;
+	case SOUND_MIXER_READ_RECMASK:
+		buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE|
+		       SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM);
+		if (aci_idcode[1] == 'C') /* PCM20 radio */
+			buf |= SOUND_MASK_RADIO;
+		else
+			buf |= SOUND_MASK_LINE1;
+
+		__put_user(buf, (int *)arg);
+		return 0;
+	case SOUND_MIXER_READ_RECSRC:
+		buf = (SOUND_MASK_CD    | SOUND_MASK_MIC   | SOUND_MASK_LINE  |
+		       SOUND_MASK_SYNTH | SOUND_MASK_LINE2);
+		/* do we need aci_solo or can I get it from the ACI? */
+		switch (aci_idcode[1]) {
+		case 'B': /* PCM12 */
+		case 'C': /* PCM20 radio */
+			if (aci_version >= 0xb0) {
+				if ((vol=aci_rw_cmd(ACI_STATUS,
+						    ACI_S_GENERAL, -1))<0)
+					return vol;
+				if (vol & 0x20)
+					buf |= SOUND_MASK_PCM;
+			}
+			else
+				if (!aci_solo)
+					buf |= SOUND_MASK_PCM;
+			break;
+		default:
+			buf |= SOUND_MASK_PCM;
+		}
+		if (aci_idcode[1] == 'C') /* PCM20 radio */
+			buf |= SOUND_MASK_RADIO;
+		else
+			buf |= SOUND_MASK_LINE1;
+
+		__put_user(buf, (int *)arg);
+		return 0;
+	case SOUND_MIXER_READ_CAPS:
+		__put_user(0, (int *)arg);
+		return 0;
+	case SOUND_MIXER_READ_VOLUME:
+		return getvolume(arg, 0x04, 0x03);
+	case SOUND_MIXER_READ_CD:
+		return getvolume(arg, 0x0a, 0x09);
+	case SOUND_MIXER_READ_MIC:
+		return getvolume(arg, 0x06, 0x05);
+	case SOUND_MIXER_READ_LINE:
+		return getvolume(arg, 0x08, 0x07);
+	case SOUND_MIXER_READ_SYNTH:
+		return getvolume(arg, 0x0c, 0x0b);
+	case SOUND_MIXER_READ_PCM:
+		return getvolume(arg, 0x0e, 0x0d);
+	case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */
+	case SOUND_MIXER_READ_LINE1:  /* AUX1 */
+		return getvolume(arg, 0x11, 0x10);
+	case SOUND_MIXER_READ_LINE2:  /* AUX2 */
+		return getvolume(arg, 0x13, 0x12);
+	case SOUND_MIXER_READ_BASS:   /* get band one */
+		if (aci_idcode[1]=='C') {
+			return getequalizer(arg, 0x23, 0x22);
+		}
+		break;
+	case SOUND_MIXER_READ_TREBLE: /* get band seven */
+		if (aci_idcode[1]=='C') {
+			return getequalizer(arg, 0x2f, 0x2e);
+		}
+		break;
+	case SOUND_MIXER_READ_IGAIN:  /* MIC pre-amp */
+		if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
+			/* aci_micpreamp or ACI? */
+			if (aci_version >= 0xb0) {
+				if ((buf=aci_indexed_cmd(ACI_STATUS,
+							 ACI_S_READ_IGAIN))<0)
+					return buf;
+			}
+			else
+				buf=aci_micpreamp;
+			vol = SCALE(3, 100, buf <= 3 ? buf : 3);
+			vol |= vol << 8;
+			__put_user(vol, (int *)arg);
+			return 0;
+		}
+		break;
+	case SOUND_MIXER_READ_OGAIN:
+		if (aci_amp)
+			buf = (100 || 100<<8);
+		else
+			buf = 0;
+		__put_user(buf, (int *)arg);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static struct mixer_operations aci_mixer_operations =
+{
+	owner: THIS_MODULE,
+	id:    "ACI",
+	ioctl: aci_mixer_ioctl
+};
+
+/*
+ * There is also an internal mixer in the codec (CS4231A or AD1845),
+ * that deserves no purpose in an ACI based system which uses an
+ * external ACI controlled stereo mixer. Make sure that this codec
+ * mixer has the AUX1 input selected as the recording source, that the
+ * input gain is set near maximum and that the other channels going
+ * from the inputs to the codec output are muted.
+ */
+
+static int __init attach_aci(void)
+{
+	char *boardname;
+	int i, rc = -EBUSY;
+
+	init_MUTEX(&aci_sem);
+
+	outb(0xE3, 0xf8f); /* Write MAD16 password */
+	aci_port = (inb(0xf90) & 0x10) ?
+		0x344: 0x354; /* Get aci_port from MC4_PORT */
+
+	if (!request_region(aci_port, 3, "sound mixer (ACI)")) {
+		printk(KERN_NOTICE
+		       "aci: I/O area 0x%03x-0x%03x already used.\n",
+		       aci_port, aci_port+2);
+		goto out;
+	}
+
+	/* force ACI into a known state */
+	rc = -EFAULT;
+	for (i=0; i<3; i++)
+		if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0)
+			goto out_release_region;
+
+	/* official this is one aci read call: */
+	rc = -EFAULT;
+	if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 ||
+	    (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) {
+		printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n",
+		       aci_port);
+		goto out_release_region;
+	}
+
+	if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) {
+		printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n",
+		       aci_port);
+		goto out_release_region;
+	}
+
+	if (aci_idcode[0] == 'm') {
+		/* It looks like a miro sound card. */
+		switch (aci_idcode[1]) {
+		case 'A':
+			boardname = "PCM1 pro / early PCM12";
+			break;
+		case 'B':
+			boardname = "PCM12";
+			break;
+		case 'C':
+			boardname = "PCM20 radio";
+			break;
+		default:
+			boardname = "unknown miro";
+		}
+	} else {
+		printk(KERN_WARNING "aci: Warning: unsupported card! - "
+		       "no hardware, no specs...\n");
+		boardname = "unknown Cardinal Technologies";
+	}
+
+	printk(KERN_INFO "<ACI 0x%02x, id %02x/%02x \"%c/%c\", (%s)> at 0x%03x\n",
+	       aci_version,
+	       aci_idcode[0], aci_idcode[1],
+	       aci_idcode[0], aci_idcode[1],
+	       boardname, aci_port);
+
+	rc = -EBUSY;
+	if (reset) {
+		/* first write()s after reset fail with my PCM20 */
+		if (aci_rw_cmd(ACI_INIT, -1, -1)<0 ||
+		    aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 ||
+		    aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0)
+			goto out_release_region;
+	}
+
+	/* the PCM20 is muted after reset (and reboot) */
+	if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0)
+		goto out_release_region;
+
+	if (ide>=0)
+		if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0)
+			goto out_release_region;
+	
+	if (wss>=0 && aci_idcode[1]=='A')
+		if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0)
+			goto out_release_region;
+
+	mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname,
+					   &aci_mixer_operations,
+					   sizeof(aci_mixer_operations), NULL);
+	rc = 0;
+	if (mixer_device < 0) {
+		printk(KERN_ERR "aci: Failed to install mixer.\n");
+		rc = mixer_device;
+		goto out_release_region;
+	} /* else Maybe initialize the CS4231A mixer here... */
+out:	return rc;
+out_release_region:
+	release_region(aci_port, 3);
+	goto out;
+}
+
+static void __exit unload_aci(void)
+{
+	sound_unload_mixerdev(mixer_device);
+	release_region(aci_port, 3);
+}
+
+module_init(attach_aci);
+module_exit(unload_aci);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/aci.h linux-2.4.19-pre5-mjc/sound/oss/aci.h
--- linux/sound/oss/aci.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/aci.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,58 @@
+#ifndef _ACI_H_
+#define _ACI_H_
+
+extern int aci_port;
+extern int aci_idcode[2];	/* manufacturer and product ID */
+extern int aci_version;		/* ACI firmware version	*/
+extern int aci_rw_cmd(int write1, int write2, int write3);
+
+#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1)
+#define aci_write_cmd(a, b)   aci_rw_cmd(a, b, -1)
+#define aci_read_cmd(a)       aci_rw_cmd(a,-1, -1)
+
+#define COMMAND_REGISTER    (aci_port)      /* write register */
+#define STATUS_REGISTER     (aci_port + 1)  /* read register */
+#define BUSY_REGISTER       (aci_port + 2)  /* also used for rds */
+
+#define RDS_REGISTER        BUSY_REGISTER
+
+#define ACI_SET_MUTE          0x0d
+#define ACI_SET_POWERAMP      0x0f
+#define ACI_SET_TUNERMUTE     0xa3
+#define ACI_SET_TUNERMONO     0xa4
+#define ACI_SET_IDE           0xd0
+#define ACI_SET_WSS           0xd1
+#define ACI_SET_SOLOMODE      0xd2
+#define ACI_WRITE_IGAIN       0x03
+#define ACI_WRITE_TUNE        0xa7
+#define ACI_READ_TUNERSTEREO  0xa8
+#define ACI_READ_TUNERSTATION 0xa9
+#define ACI_READ_VERSION      0xf1
+#define ACI_READ_IDCODE       0xf2
+#define ACI_INIT              0xff
+#define ACI_STATUS            0xf0
+#define     ACI_S_GENERAL     0x00
+#define     ACI_S_READ_IGAIN  0x21
+#define ACI_ERROR_OP          0xdf
+
+/*
+ * The following macro SCALE can be used to scale one integer volume
+ * value into another one using only integer arithmetic. If the input
+ * value x is in the range 0 <= x <= xmax, then the result will be in
+ * the range 0 <= SCALE(xmax,ymax,x) <= ymax.
+ *
+ * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the
+ * following nice properties:
+ *
+ * - SCALE(xmax,ymax,xmax) = ymax
+ * - SCALE(xmax,ymax,0) = 0
+ * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x)
+ *
+ * In addition, the rounding error is minimal and nicely distributed.
+ * The proofs are left as an exercise to the reader.
+ */
+
+#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax))
+
+
+#endif  /* _ACI_H_ */
diff -Nru linux/sound/oss/ad1816.c linux-2.4.19-pre5-mjc/sound/oss/ad1816.c
--- linux/sound/oss/ad1816.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ad1816.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1478 @@
+/*
+ *
+ * AD1816 lowlevel sound driver for Linux 2.2.0 and above
+ *
+ * Copyright (C) 1998 by Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de>
+ *
+ * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996
+ *
+ *
+ * version: 1.3.1
+ * status: experimental
+ * date: 1999/4/18
+ *
+ * Changes:
+ *	Oleg Drokin: Some cleanup of load/unload functions.	1998/11/24
+ *	
+ *	Thorsten Knabe: attach and unload rewritten, 
+ *	some argument checks added				1998/11/30
+ *
+ *	Thorsten Knabe: Buggy isa bridge workaround added	1999/01/16
+ *	
+ *	David Moews/Thorsten Knabe: Introduced options 
+ *	parameter. Added slightly modified patch from 
+ *	David Moews to disable dsp audio sources by setting 
+ *	bit 0 of options parameter. This seems to be
+ *	required by some Aztech/Newcom SC-16 cards.		1999/04/18
+ *
+ *	Christoph Hellwig: Adapted to module_init/module_exit.	2000/03/03
+ *
+ *	Christoph Hellwig: Added isapnp support			2000/03/15
+ *
+ *	Arnaldo Carvalho de Melo: get rid of check_region	2001/10/07
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include <linux/stddef.h>
+
+#include "sound_config.h"
+
+#define DEBUGNOISE(x)
+#define DEBUGINFO(x)
+#define DEBUGLOG(x)
+#define DEBUGWARN(x)
+
+#define CHECK_FOR_POWER { int timeout=100; \
+  while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\
+          timeout--; \
+  } \
+  if (timeout==0) {\
+          printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \
+  } \
+}
+
+/* structure to hold device specific information */
+typedef struct
+{
+        int            base;          /* set in attach */
+	int            irq;
+	int            dma_playback;
+        int            dma_capture;
+  
+        int            speed;         /* open */
+	int            channels;
+	int            audio_format;
+	unsigned char  format_bits;
+        int            audio_mode; 
+	int            opened;
+  
+        int            recmask;        /* setup */
+	int            supported_devices;
+	int            supported_rec_devices;
+	unsigned short levels[SOUND_MIXER_NRDEVICES];
+        int            dev_no;   /* this is the # in audio_devs and NOT 
+				    in ad1816_info */
+	int            irq_ok;
+	int            *osp;
+  
+} ad1816_info;
+
+static int nr_ad1816_devs;
+static int ad1816_clockfreq = 33000;
+static int options;
+
+/* for backward mapping of irq to sound device */
+
+static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1,
+				    -1, -1, -1, -1, -1, -1, -1, -1, -1};
+
+
+/* supported audio formats */
+static int  ad_format_mask =
+AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW;
+
+/* array of device info structures */
+static ad1816_info dev_info[MAX_AUDIO_DEV];
+
+
+/* ------------------------------------------------------------------- */
+
+/* functions for easier access to inderect registers */
+
+static int ad_read (ad1816_info * devc, int reg)
+{
+	unsigned long   flags;
+	int result;
+	
+	CHECK_FOR_POWER;
+
+	save_flags (flags); /* make register access atomic */
+	cli ();
+	outb ((unsigned char) (reg & 0x3f), devc->base+0);
+	result = inb(devc->base+2);
+	result+= inb(devc->base+3)<<8;
+	restore_flags (flags);
+	
+	return (result);
+}
+
+
+static void ad_write (ad1816_info * devc, int reg, int data)
+{
+	unsigned long flags;
+	
+	CHECK_FOR_POWER;
+	
+	save_flags (flags); /* make register access atomic */
+	cli ();
+	outb ((unsigned char) (reg & 0xff), devc->base+0);
+	outb ((unsigned char) (data & 0xff),devc->base+2);
+	outb ((unsigned char) ((data>>8)&0xff),devc->base+3);
+	restore_flags (flags);
+
+}
+
+/* ------------------------------------------------------------------- */
+
+/* function interface required by struct audio_driver */
+
+static void ad1816_halt_input (int dev)
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	unsigned char buffer;
+	
+	DEBUGINFO (printk("ad1816: halt_input called\n"));
+	
+	save_flags (flags); 
+	cli ();
+	
+	if(!isa_dma_bridge_buggy) {
+	        disable_dma(audio_devs[dev]->dmap_in->dma);
+	}
+	
+	buffer=inb(devc->base+9);
+	if (buffer & 0x01) {
+		/* disable capture */
+		outb(buffer & ~0x01,devc->base+9); 
+	}
+
+	if(!isa_dma_bridge_buggy) {
+	        enable_dma(audio_devs[dev]->dmap_in->dma);
+	}
+
+	/* Clear interrupt status */
+	outb (~0x40, devc->base+1);	
+	
+	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+	restore_flags (flags);
+}
+
+static void ad1816_halt_output (int dev)
+{
+	unsigned long  flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	unsigned char buffer;
+
+	DEBUGINFO (printk("ad1816: halt_output called!\n"));
+
+	save_flags (flags); 
+	cli ();
+	/* Mute pcm output */
+	ad_write(devc, 4, ad_read(devc,4)|0x8080);
+
+	if(!isa_dma_bridge_buggy) {
+	        disable_dma(audio_devs[dev]->dmap_out->dma);
+	}
+
+	buffer=inb(devc->base+8);
+	if (buffer & 0x01) {
+		/* disable capture */
+		outb(buffer & ~0x01,devc->base+8); 
+	}
+
+	if(!isa_dma_bridge_buggy) {
+	        enable_dma(audio_devs[dev]->dmap_out->dma);
+	}
+
+	/* Clear interrupt status */
+	outb ((unsigned char)~0x80, devc->base+1);	
+
+	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+	restore_flags (flags);
+}
+
+static void ad1816_output_block (int dev, unsigned long buf, 
+				 int count, int intrflag)
+{
+	unsigned long flags;
+	unsigned long cnt;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	DEBUGINFO(printk("ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag));
+  
+	cnt = count/4 - 1;
+  
+	save_flags (flags);
+	cli ();
+	
+	/* set transfer count */
+	ad_write (devc, 8, cnt & 0xffff); 
+	
+	devc->audio_mode |= PCM_ENABLE_OUTPUT; 
+	restore_flags (flags);
+}
+
+
+static void ad1816_start_input (int dev, unsigned long buf, int count,
+				int intrflag)
+{
+	unsigned long flags;
+	unsigned long  cnt;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	DEBUGINFO(printk("ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag));
+
+	cnt = count/4 - 1;
+
+	save_flags (flags); /* make register access atomic */
+	cli ();
+
+	/* set transfer count */
+	ad_write (devc, 10, cnt & 0xffff); 
+
+	devc->audio_mode |= PCM_ENABLE_INPUT;
+	restore_flags (flags);
+}
+
+static int ad1816_prepare_for_input (int dev, int bsize, int bcount)
+{
+	unsigned long flags;
+	unsigned int freq;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	unsigned char fmt_bits;
+	
+	DEBUGINFO (printk ("ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount));
+
+	save_flags (flags); 
+	cli ();
+	
+	fmt_bits= (devc->format_bits&0x7)<<3;
+	
+	/* set mono/stereo mode */
+	if (devc->channels > 1) {
+		fmt_bits |=0x4;
+	}
+
+	/* set Mono/Stereo in playback/capture register */
+	outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); 
+	outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9);
+  
+	/* If compiled into kernel, AD1816_CLOCK is defined, so use it */
+#ifdef AD1816_CLOCK 
+	ad1816_clockfreq=AD1816_CLOCK;
+#endif
+
+	/* capture/playback frequency correction for soundcards 
+	   with clock chips != 33MHz (allowed range 5 - 100 kHz) */
+
+	if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) {
+		ad1816_clockfreq=33000;
+	}
+	
+	freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; 
+
+	/* write playback/capture speeds */
+	ad_write (devc, 2, freq & 0xffff);	
+	ad_write (devc, 3, freq & 0xffff);	
+
+	restore_flags (flags);
+
+	ad1816_halt_input(dev);
+	return 0;
+}
+
+static int ad1816_prepare_for_output (int dev, int bsize, int bcount)
+{
+	unsigned long flags;
+	unsigned int freq;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	unsigned char fmt_bits;
+
+	DEBUGINFO (printk ("ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount));
+
+	save_flags (flags); /* make register access atomic */
+	cli ();
+
+	fmt_bits= (devc->format_bits&0x7)<<3;
+	/* set mono/stereo mode */
+	if (devc->channels > 1) {
+		fmt_bits |=0x4;
+	}
+
+	/* write format bits to playback/capture registers */
+	outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); 
+	outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9);
+  
+#ifdef AD1816_CLOCK 
+	ad1816_clockfreq=AD1816_CLOCK;
+#endif
+
+	/* capture/playback frequency correction for soundcards 
+	   with clock chips != 33MHz (allowed range 5 - 100 kHz)*/
+
+	if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) {
+		ad1816_clockfreq=33000;
+	}
+  
+	freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; 
+	
+	/* write playback/capture speeds */
+	ad_write (devc, 2, freq & 0xffff);
+	ad_write (devc, 3, freq & 0xffff);
+
+	restore_flags (flags);
+	
+	ad1816_halt_output(dev);
+	return 0;
+
+}
+
+static void ad1816_trigger (int dev, int state) 
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+	DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base));
+
+	/* mode may have changed */
+
+	save_flags (flags); /* make register access atomic */
+	cli ();
+
+	/* mask out modes not specified on open call */
+	state &= devc->audio_mode; 
+				
+	/* setup soundchip to new io-mode */
+	if (state & PCM_ENABLE_INPUT) {
+		/* enable capture */
+		outb(inb(devc->base+9)|0x01, devc->base+9);
+	} else {
+		/* disable capture */
+		outb(inb(devc->base+9)&~0x01, devc->base+9);
+	}
+
+	if (state & PCM_ENABLE_OUTPUT) {
+		/* enable playback */
+		outb(inb(devc->base+8)|0x01, devc->base+8);
+		/* unmute pcm output */
+		ad_write(devc, 4, ad_read(devc,4)&~0x8080);
+	} else {
+		/* mute pcm output */
+		ad_write(devc, 4, ad_read(devc,4)|0x8080);
+		/* disable capture */
+		outb(inb(devc->base+8)&~0x01, devc->base+8);
+	}
+	restore_flags (flags);
+}
+
+
+/* halt input & output */
+static void ad1816_halt (int dev)
+{
+	ad1816_halt_input(dev);
+	ad1816_halt_output(dev);
+}
+
+static void ad1816_reset (int dev)
+{
+	ad1816_halt (dev);
+}
+
+/* set playback speed */
+static int ad1816_set_speed (int dev, int arg)
+{
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	if (arg == 0) {
+		return devc->speed;
+	}
+	/* range checking */
+	if (arg < 4000) {
+		arg = 4000;
+	}
+	if (arg > 55000) {
+		arg = 55000;
+	}
+
+	devc->speed = arg;
+	return devc->speed;
+
+}
+
+static unsigned int ad1816_set_bits (int dev, unsigned int arg)
+{
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	static struct format_tbl {
+		int             format;
+		unsigned char   bits;
+	} format2bits[] = {
+		{ 0, 0 },
+		{ AFMT_MU_LAW, 1 },
+		{ AFMT_A_LAW, 3 },
+		{ AFMT_IMA_ADPCM, 0 },
+		{ AFMT_U8, 0 },
+		{ AFMT_S16_LE, 2 },
+		{ AFMT_S16_BE, 6 },
+		{ AFMT_S8, 0 },
+		{ AFMT_U16_LE, 0 },
+		{ AFMT_U16_BE, 0 }
+  	};
+
+	int  i, n = sizeof (format2bits) / sizeof (struct format_tbl);
+
+	/* return current format */
+	if (arg == 0)
+		return devc->audio_format;
+	
+	devc->audio_format = arg;
+
+	/* search matching format bits */
+	for (i = 0; i < n; i++)
+		if (format2bits[i].format == arg) {
+			devc->format_bits = format2bits[i].bits;
+			devc->audio_format = arg;
+			return arg;
+		}
+
+	/* Still hanging here. Something must be terribly wrong */
+	devc->format_bits = 0;
+	return devc->audio_format = AFMT_U8;
+}
+
+static short ad1816_set_channels (int dev, short arg)
+{
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+	if (arg != 1 && arg != 2)
+		return devc->channels;
+
+	devc->channels = arg;
+	return arg;
+}
+
+/* open device */
+static int ad1816_open (int dev, int mode) 
+{
+	ad1816_info    *devc = NULL;
+	unsigned long   flags;
+
+	/* is device number valid ? */
+	if (dev < 0 || dev >= num_audiodevs)
+		return -(ENXIO);
+
+	/* get device info of this dev */
+	devc = (ad1816_info *) audio_devs[dev]->devc; 
+
+	/* make check if device already open atomic */
+	save_flags (flags); 
+	cli ();
+
+	if (devc->opened) {
+		restore_flags (flags);
+		return -(EBUSY);
+	}
+
+	/* mark device as open */
+	devc->opened = 1; 
+
+	devc->audio_mode = 0;
+	devc->speed = 8000;
+	devc->audio_format=AFMT_U8;
+	devc->channels=1;
+
+	ad1816_reset(devc->dev_no); /* halt all pending output */
+	restore_flags (flags);
+	return 0;
+}
+
+static void ad1816_close (int dev) /* close device */
+{
+	unsigned long flags;
+	ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc;
+
+	save_flags (flags); 
+	cli ();
+
+	/* halt all pending output */
+	ad1816_reset(devc->dev_no); 
+	
+	devc->opened = 0;
+	devc->audio_mode = 0;
+	devc->speed = 8000;
+	devc->audio_format=AFMT_U8;
+	devc->format_bits = 0;
+
+
+	restore_flags (flags);
+}
+
+
+/* ------------------------------------------------------------------- */
+
+/* Audio driver structure */
+
+static struct audio_driver ad1816_audio_driver =
+{
+	owner:		THIS_MODULE,
+	open:		ad1816_open,
+	close:		ad1816_close,
+	output_block:	ad1816_output_block,
+	start_input:	ad1816_start_input,
+	prepare_for_input:	ad1816_prepare_for_input,
+	prepare_for_output:	ad1816_prepare_for_output,
+	halt_io:		ad1816_halt,
+	halt_input:	ad1816_halt_input,
+	halt_output:	ad1816_halt_output,
+	trigger:	ad1816_trigger,
+	set_speed:	ad1816_set_speed,
+	set_bits:	ad1816_set_bits,
+	set_channels:	ad1816_set_channels,
+};
+
+
+/* ------------------------------------------------------------------- */
+
+/* Interrupt handler */
+
+
+static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
+{
+	unsigned char	status;
+	ad1816_info	*devc;
+	int		dev;
+	unsigned long	flags;
+
+	
+	if (irq < 0 || irq > 15) {
+	        printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq);
+		return;
+	}
+
+	dev = irq2dev[irq];
+	
+	if (dev < 0 || dev >= num_audiodevs) {
+	        printk(KERN_WARNING "ad1816: IRQ2AD1816-mapping failed for "
+				    "irq %d device %d\n", irq,dev);
+		return;	        
+	}
+
+	devc = (ad1816_info *) audio_devs[dev]->devc;
+	
+	save_flags(flags);
+	cli();
+
+	/* read interrupt register */
+	status = inb (devc->base+1); 
+	/* Clear all interrupt  */
+	outb (~status, devc->base+1);	
+
+	DEBUGNOISE (printk("ad1816: Got interrupt subclass %d\n",status));
+	
+	devc->irq_ok=1;
+
+	if (status == 0)
+		DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n"));
+
+	if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64))
+		DMAbuf_inputintr (dev);
+
+	if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128))
+		DMAbuf_outputintr (dev, 1);
+
+	restore_flags(flags);
+}
+
+/* ------------------------------------------------------------------- */
+
+/* Mixer stuff */
+
+struct mixer_def {
+	unsigned int regno: 7;
+	unsigned int polarity:1;	/* 0=normal, 1=reversed */
+	unsigned int bitpos:4;
+	unsigned int nbits:4;
+};
+
+static char mix_cvt[101] = {
+	 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+	43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+	65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+	80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+	91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+	100
+};
+
+typedef struct mixer_def mixer_ent;
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual soundcards is not fixed. Some
+ * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
+ * The current version doesn't try to compensate this.
+ */
+
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r)	\
+  {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}}
+
+
+mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = {
+MIX_ENT(SOUND_MIXER_VOLUME,	14, 1, 8, 5,	14, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	 5, 1, 8, 6,	 5, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_PCM,	 4, 1, 8, 6,	 4, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_SPEAKER,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	18, 1, 8, 5,	18, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_MIC,	19, 1, 8, 5,	19, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_CD,	 	15, 1, 8, 5,	15, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_IMIX,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	20, 0, 8, 4,	20, 0, 0, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1, 	17, 1, 8, 5,	17, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE2,	16, 1, 8, 5,	16, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE3,      39, 0, 9, 4,    39, 1, 0, 5)
+};
+
+
+static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] =
+{
+	0x4343,		/* Master Volume */
+	0x3232,		/* Bass */
+	0x3232,		/* Treble */
+	0x0000,		/* FM */
+	0x4343,		/* PCM */
+	0x0000,		/* PC Speaker */
+	0x0000,		/* Ext Line */
+	0x0000,		/* Mic */
+	0x0000,		/* CD */
+	0x0000,		/* Recording monitor */
+	0x0000,		/* SB PCM */
+	0x0000,		/* Recording level */
+	0x0000,		/* Input gain */
+	0x0000,		/* Output gain */
+	0x0000,		/* Line1 */
+	0x0000,		/* Line2 */
+	0x0000		/* Line3 (usually line in)*/
+};
+
+#define LEFT_CHN	0
+#define RIGHT_CHN	1
+
+
+
+static int
+ad1816_set_recmask (ad1816_info * devc, int mask)
+{
+	unsigned char   recdev;
+	int             i, n;
+	
+	mask &= devc->supported_rec_devices;
+	
+	n = 0;
+	/* Count selected device bits */
+	for (i = 0; i < 32; i++)
+		if (mask & (1 << i))
+			n++;
+	
+	if (n == 0)
+		mask = SOUND_MASK_MIC;
+	else if (n != 1) { /* Too many devices selected */
+		/* Filter out active settings */
+		mask &= ~devc->recmask;	
+		
+		n = 0;
+		/* Count selected device bits */
+		for (i = 0; i < 32; i++) 
+			if (mask & (1 << i))
+				n++;
+		
+		if (n != 1)
+			mask = SOUND_MASK_MIC;
+	}
+	
+	switch (mask) {
+	case SOUND_MASK_MIC:
+		recdev = 5;
+		break;
+		
+	case SOUND_MASK_LINE:
+		recdev = 0;
+		break;
+		
+	case SOUND_MASK_CD:
+		recdev = 2;
+		break;
+		
+	case SOUND_MASK_LINE1:
+		recdev = 4;
+		break;
+		
+	case SOUND_MASK_LINE2:
+		recdev = 3;
+		break;
+		
+	case SOUND_MASK_VOLUME:
+		recdev = 1;
+		break;
+		
+	default:
+		mask = SOUND_MASK_MIC;
+		recdev = 5;
+	}
+	
+	recdev <<= 4;
+	ad_write (devc, 20, 
+		  (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8));
+
+	devc->recmask = mask;
+	return mask;
+}
+
+static void
+change_bits (int *regval, int dev, int chn, int newval)
+{
+	unsigned char   mask;
+	int             shift;
+  
+	/* Reverse polarity*/
+
+	if (mix_devices[dev][chn].polarity == 1) 
+		newval = 100 - newval;
+
+	mask = (1 << mix_devices[dev][chn].nbits) - 1;
+	shift = mix_devices[dev][chn].bitpos;
+	/* Scale it */
+	newval = (int) ((newval * mask) + 50) / 100;	
+	/* Clear bits */
+	*regval &= ~(mask << shift);	
+	/* Set new value */
+	*regval |= (newval & mask) << shift;	
+}
+
+static int
+ad1816_mixer_get (ad1816_info * devc, int dev)
+{
+	DEBUGINFO(printk("ad1816: mixer_get called!\n"));
+	
+	/* range check + supported mixer check */
+	if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
+	        return (-(EINVAL));
+	if (!((1 << dev) & devc->supported_devices))
+		return -(EINVAL);
+	
+	return devc->levels[dev];
+}
+
+static int
+ad1816_mixer_set (ad1816_info * devc, int dev, int value)
+{
+	int   left = value & 0x000000ff;
+	int   right = (value & 0x0000ff00) >> 8;
+	int   retvol;
+
+	int   regoffs;
+	int   val;
+	int   valmute;
+
+	DEBUGINFO(printk("ad1816: mixer_set called!\n"));
+	
+	if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
+		return -(EINVAL);
+
+	if (left > 100)
+		left = 100;
+	if (left < 0)
+		left = 0;
+	if (right > 100)
+		right = 100;
+	if (right < 0)
+		right = 0;
+	
+	/* Mono control */
+	if (mix_devices[dev][RIGHT_CHN].nbits == 0) 
+		right = left;
+	retvol = left | (right << 8);
+	
+	/* Scale it */
+	
+	left = mix_cvt[left];
+	right = mix_cvt[right];
+
+	/* reject all mixers that are not supported */
+	if (!(devc->supported_devices & (1 << dev)))
+		return -(EINVAL);
+	
+	/* sanity check */
+	if (mix_devices[dev][LEFT_CHN].nbits == 0)
+		return -(EINVAL);
+
+	/* keep precise volume internal */
+	devc->levels[dev] = retvol;
+
+	/* Set the left channel */
+	regoffs = mix_devices[dev][LEFT_CHN].regno;
+	val = ad_read (devc, regoffs);
+	change_bits (&val, dev, LEFT_CHN, left);
+
+	valmute=val;
+
+	/* Mute bit masking on some registers */
+	if ( regoffs==5 || regoffs==14 || regoffs==15 ||
+	     regoffs==16 || regoffs==17 || regoffs==18 || 
+	     regoffs==19 || regoffs==39) {
+		if (left==0)
+			valmute |= 0x8000;
+		else
+			valmute &= ~0x8000;
+	}
+	ad_write (devc, regoffs, valmute); /* mute */
+
+	/*
+	 * Set the right channel
+	 */
+ 
+	/* Was just a mono channel */
+	if (mix_devices[dev][RIGHT_CHN].nbits == 0)
+		return retvol;		
+
+	regoffs = mix_devices[dev][RIGHT_CHN].regno;
+	val = ad_read (devc, regoffs);
+	change_bits (&val, dev, RIGHT_CHN, right);
+
+	valmute=val;
+	if ( regoffs==5 || regoffs==14 || regoffs==15 ||
+	     regoffs==16 || regoffs==17 || regoffs==18 || 
+	     regoffs==19 || regoffs==39) {
+		if (right==0)
+			valmute |= 0x80;
+		else
+			valmute &= ~0x80;
+	}
+	ad_write (devc, regoffs, valmute); /* mute */
+	
+       	return retvol;
+}
+
+#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \
+			SOUND_MASK_SYNTH | \
+			SOUND_MASK_PCM | \
+			SOUND_MASK_LINE | \
+			SOUND_MASK_LINE1 | \
+			SOUND_MASK_LINE2 | \
+			SOUND_MASK_LINE3 | \
+			SOUND_MASK_MIC | \
+			SOUND_MASK_CD | \
+			SOUND_MASK_RECLEV  \
+			)
+#define REC_DEVICES ( SOUND_MASK_LINE2 |\
+		      SOUND_MASK_LINE |\
+		      SOUND_MASK_LINE1 |\
+		      SOUND_MASK_MIC |\
+		      SOUND_MASK_CD |\
+		      SOUND_MASK_VOLUME \
+		      )
+     
+static void
+ad1816_mixer_reset (ad1816_info * devc)
+{
+	int  i;
+
+	devc->supported_devices = MIXER_DEVICES;
+	
+	devc->supported_rec_devices = REC_DEVICES;
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		if (devc->supported_devices & (1 << i))
+			ad1816_mixer_set (devc, i, default_mixer_levels[i]);
+	ad1816_set_recmask (devc, SOUND_MASK_MIC);
+}
+
+static int
+ad1816_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+	ad1816_info    *devc = mixer_devs[dev]->devc;
+	int val;
+  
+	DEBUGINFO(printk("ad1816: mixer_ioctl called!\n"));
+  
+	/* Mixer ioctl */
+	if (((cmd >> 8) & 0xff) == 'M') { 
+		
+		/* set ioctl */
+		if (_SIOC_DIR (cmd) & _SIOC_WRITE) { 
+			switch (cmd & 0xff){
+			case SOUND_MIXER_RECSRC:
+				
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				val=ad1816_set_recmask (devc, val);
+				return put_user(val, (int *)arg);
+				break;
+				
+			default:
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0)
+				        return val;
+				else
+				        return put_user(val, (int *)arg);
+			}
+		} else { 
+			/* read ioctl */
+			switch (cmd & 0xff) {
+				
+			case SOUND_MIXER_RECSRC:
+				val=devc->recmask;
+				return put_user(val, (int *)arg);
+				break;
+				
+			case SOUND_MIXER_DEVMASK:
+				val=devc->supported_devices;
+				return put_user(val, (int *)arg);
+				break;
+
+			case SOUND_MIXER_STEREODEVS:
+				val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+				return put_user(val, (int *)arg);
+				break;
+				
+			case SOUND_MIXER_RECMASK:
+				val=devc->supported_rec_devices;
+				return put_user(val, (int *)arg);
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				val=SOUND_CAP_EXCL_INPUT;
+				return put_user(val, (int *)arg);
+				break;
+				
+			default:
+			        if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0)
+				        return val;
+				else
+				        return put_user(val, (int *)arg);
+			}
+		}
+	} else
+		/* not for mixer */
+		return -(EINVAL);
+}
+
+/* ------------------------------------------------------------------- */
+
+/* Mixer structure */
+
+static struct mixer_operations ad1816_mixer_operations = {
+	owner:	THIS_MODULE,
+	id:	"AD1816",
+	name:	"AD1816 Mixer",
+	ioctl:	ad1816_mixer_ioctl
+};
+
+
+/* ------------------------------------------------------------------- */
+
+/* stuff for card recognition, init and unloading */
+
+
+/* replace with probe routine */
+static int __init probe_ad1816 ( struct address_info *hw_config )
+{
+	ad1816_info    *devc = &dev_info[nr_ad1816_devs];
+	int io_base=hw_config->io_base;
+	int *osp=hw_config->osp;
+	int tmp;
+
+	printk(KERN_INFO "ad1816: AD1816 sounddriver "
+			 "Copyright (C) 1998 by Thorsten Knabe\n");
+	printk(KERN_INFO "ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, "
+			 "clockfreq=%d, options=%d isadmabug=%d\n",
+	       hw_config->io_base,
+	       hw_config->irq,
+	       hw_config->dma,
+	       hw_config->dma2,
+	       ad1816_clockfreq,
+	       options,
+	       isa_dma_bridge_buggy);
+
+	if (!request_region(io_base, 16, "AD1816 Sound")) {
+		printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n",
+				    io_base);
+		goto err;
+	}
+
+	DEBUGLOG(printk ("ad1816: detect(%x)\n", io_base));
+	
+	if (nr_ad1816_devs >= MAX_AUDIO_DEV) {
+		printk(KERN_WARNING "ad1816: detect error - step 0\n");
+		goto out_release_region;
+	}
+
+	devc->base = io_base;
+	devc->irq_ok = 0;
+	devc->irq = 0;
+	devc->opened = 0;
+	devc->osp = osp;
+
+	/* base+0: bit 1 must be set but not 255 */
+	tmp=inb(devc->base);
+	if ( (tmp&0x80)==0 || tmp==255 ) {
+		DEBUGLOG (printk ("ad1816: Chip is not an AD1816 or chip is not active (Test 0)\n"));
+		goto out_release_region;
+	}
+
+
+	/* writes to ireg 8 are copied to ireg 9 */
+	ad_write(devc,8,12345); 
+	if (ad_read(devc,9)!=12345) {
+		DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 1)\n"));
+		goto out_release_region;
+	}
+  
+	/* writes to ireg 8 are copied to ireg 9 */
+	ad_write(devc,8,54321); 
+	if (ad_read(devc,9)!=54321) {
+		DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n"));
+		goto out_release_region;
+	}
+
+
+	/* writes to ireg 10 are copied to ireg 11 */
+	ad_write(devc,10,54321); 
+	if (ad_read(devc,11)!=54321) {
+		DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 3)\n"));
+		goto out_release_region;
+	}
+
+	/* writes to ireg 10 are copied to ireg 11 */
+	ad_write(devc,10,12345); 
+	if (ad_read(devc,11)!=12345) {
+		DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n"));
+		goto out_release_region;
+	}
+
+	/* bit in base +1 cannot be set to 1 */
+	tmp=inb(devc->base+1);
+	outb(0xff,devc->base+1); 
+	if (inb(devc->base+1)!=tmp) {
+		DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 5)\n"));
+		goto out_release_region;
+	}
+
+  
+	DEBUGLOG (printk ("ad1816: detect() - Detected OK\n"));
+	DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45)));
+
+	/* detection was successful */
+	return 1; 
+out_release_region:
+	release_region(io_base, 16);
+	/* detection was NOT successful */
+err:	return 0;
+}
+
+
+/* allocate resources from the kernel. If any allocation fails, free
+   all allocated resources and exit attach.
+  
+ */
+
+static void __init attach_ad1816 (struct address_info *hw_config)
+{
+	int             my_dev;
+	char            dev_name[100];
+	ad1816_info    *devc = &dev_info[nr_ad1816_devs];
+
+	devc->base = hw_config->io_base;	
+
+	/* disable all interrupts */
+	ad_write(devc,1,0);     
+
+	/* Clear pending interrupts */
+	outb (0, devc->base+1);	
+
+	/* allocate irq */
+	if (hw_config->irq < 0 || hw_config->irq > 15)
+		goto out_release_region;
+	if (request_irq(hw_config->irq, ad1816_interrupt,0,
+			"SoundPort", hw_config->osp) < 0)	{
+	        printk(KERN_WARNING "ad1816: IRQ in use\n");
+		goto out_release_region;
+	}
+	devc->irq=hw_config->irq;
+
+	/* DMA stuff */
+	if (sound_alloc_dma (hw_config->dma, "Sound System")) {
+		printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n",
+				    hw_config->dma);
+		goto out_free_irq;
+	}
+	devc->dma_playback=hw_config->dma;
+	
+	if ( hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) {
+		if (sound_alloc_dma(hw_config->dma2,
+				    "Sound System (capture)")) {
+			printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n",
+					    hw_config->dma2);
+			goto out_free_dma;
+		}
+		devc->dma_capture=hw_config->dma2;
+		devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX;
+	} else {
+	        devc->dma_capture=-1;
+		devc->audio_mode=DMA_AUTOMODE;
+	}
+
+	sprintf (dev_name,"AD1816 audio driver");
+  
+	conf_printf2 (dev_name,
+		      devc->base, devc->irq, hw_config->dma, hw_config->dma2);
+
+	/* register device */
+	if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+					      dev_name,
+					      &ad1816_audio_driver,
+					      sizeof (struct audio_driver),
+					      devc->audio_mode,
+					      ad_format_mask,
+					      devc,
+					      hw_config->dma, 
+					      hw_config->dma2)) < 0) {
+		printk(KERN_WARNING "ad1816: Can't install sound driver\n");
+		goto out_free_dma_2;
+	}
+
+	/* fill rest of structure with reasonable default values */
+	irq2dev[hw_config->irq] = devc->dev_no = my_dev;
+	devc->opened = 0;
+	devc->irq_ok = 0;
+	devc->osp = hw_config->osp;  
+	nr_ad1816_devs++;
+
+	ad_write(devc,32,0x80f0); /* sound system mode */
+	if (options&1) {
+	        ad_write(devc,33,0); /* disable all audiosources for dsp */
+	} else {
+	        ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */
+	}
+	ad_write(devc,4,0x8080);  /* default values for volumes (muted)*/
+	ad_write(devc,5,0x8080);
+	ad_write(devc,6,0x8080);
+	ad_write(devc,7,0x8080);
+	ad_write(devc,15,0x8888);
+	ad_write(devc,16,0x8888);
+	ad_write(devc,17,0x8888);
+	ad_write(devc,18,0x8888);
+	ad_write(devc,19,0xc888); /* +20db mic active */
+	ad_write(devc,14,0x0000); /* Master volume unmuted */
+	ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */
+	ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */
+	outb(0x10,devc->base+8); /* set dma mode */
+	outb(0x10,devc->base+9);
+  
+	/* enable capture + playback interrupt */
+	ad_write(devc,1,0xc000); 
+	
+	/* set mixer defaults */
+	ad1816_mixer_reset (devc); 
+  
+	/* register mixer */
+	if ((audio_devs[my_dev]->mixer_dev=sound_install_mixer(
+				       MIXER_DRIVER_VERSION,
+				       dev_name,
+				       &ad1816_mixer_operations,
+				       sizeof (struct mixer_operations),
+				       devc)) >= 0) {
+		audio_devs[my_dev]->min_fragment = 0;
+	}
+out:	return;
+out_free_dma_2:
+	if (devc->dma_capture >= 0)
+	        sound_free_dma(hw_config->dma2);
+out_free_dma:
+	sound_free_dma(hw_config->dma);
+out_free_irq:
+	free_irq(hw_config->irq,hw_config->osp);
+out_release_region:
+	release_region(hw_config->io_base, 16);
+	goto out;
+}
+
+static void __exit unload_card(ad1816_info *devc)
+{
+	int  mixer, dev = 0;
+	
+	if (devc != NULL) {
+		DEBUGLOG (printk("ad1816: Unloading card at base=%x\n",devc->base));
+		
+		dev = devc->dev_no;
+		mixer = audio_devs[dev]->mixer_dev;
+
+		/* unreg mixer*/
+		if(mixer>=0) {
+			sound_unload_mixerdev(mixer);
+		}
+		sound_unload_audiodev(dev);
+		
+		/* free dma channels */
+		if (devc->dma_capture>=0) {
+			sound_free_dma(devc->dma_capture);
+		}
+
+		/* card wont get added if resources could not be allocated
+		   thus we need not ckeck if allocation was successful */
+		sound_free_dma (devc->dma_playback);
+		free_irq(devc->irq, devc->osp);
+		release_region (devc->base, 16);
+		
+		DEBUGLOG (printk("ad1816: Unloading card at base=%x was successful\n",devc->base));
+		
+	} else
+		printk(KERN_WARNING "ad1816: no device/card specified\n");
+}
+
+static struct address_info cfg;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma2 = -1;
+
+#ifdef __ISAPNP__
+struct pci_dev	*ad1816_dev  = NULL;
+
+static int activated	= 1;
+
+static int isapnp	= 1;
+static int isapnpjump	= 0;
+
+MODULE_PARM(isapnp, "i");
+MODULE_PARM(isapnpjump, "i");
+
+#else
+static int isapnp = 0;
+#endif
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma2,"i");
+MODULE_PARM(ad1816_clockfreq,"i");
+MODULE_PARM(options,"i");
+
+#ifdef __ISAPNP__
+
+static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev)
+{
+	int err;
+	
+	if(dev->active) {
+		activated = 0;
+		return(dev);
+	}
+
+	if((err = dev->activate(dev)) < 0) {
+		printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n",
+			devname, resname, err);
+		dev->deactivate(dev);
+		return(NULL);
+	}
+		
+	return(dev);
+}
+
+static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card,
+	struct address_info *hw_config)
+{
+	if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) {
+		ad1816_dev->prepare(ad1816_dev);
+		
+		if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) {
+			hw_config->io_base	= ad1816_dev->resource[2].start;
+			hw_config->irq		= ad1816_dev->irq_resource[0].start;
+			hw_config->dma		= ad1816_dev->dma_resource[0].start;
+			hw_config->dma2		= ad1816_dev->dma_resource[1].start;
+		}
+	}
+	
+	return(ad1816_dev);
+}
+
+static struct ad1816_data {
+	struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *);
+	char *name;
+} ad1816_pnp_data[] __initdata = {
+	{ &ad1816_init_generic, "Analog Devices 1815" },
+	{ &ad1816_init_generic, "Analog Devices 1816A" }
+};
+
+static struct {
+	unsigned short card_vendor, card_device;
+	unsigned short vendor;
+	unsigned short function;
+	struct ad1816_data *data;
+} isapnp_ad1816_list[] __initdata = {
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), 
+		&ad1816_pnp_data[0] },
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180),
+		&ad1816_pnp_data[1] },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list);
+
+static int __init ad1816_init_isapnp(struct address_info *hw_config,
+	struct pci_bus *bus, struct pci_dev *card, int slot)
+{
+	struct pci_dev *idev = NULL;
+	
+	/* You missed the init func? That's bad. */
+	if(isapnp_ad1816_list[slot].data->initfunc) {
+		char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].data->name;
+		
+		printk(KERN_INFO "ad1816: %s detected\n", busname);
+		
+		/* Initialize this baby. */
+		if((idev = isapnp_ad1816_list[slot].data->initfunc(bus, card, hw_config))) {
+			/* We got it. */
+
+			printk(KERN_NOTICE "ad1816: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
+				busname,
+				hw_config->io_base, hw_config->irq, hw_config->dma,
+				hw_config->dma2);
+			return 1;
+		} else
+			printk(KERN_INFO "ad1816: Failed to initialize %s\n", busname);
+	} else
+		printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n");
+	
+	return 0;
+}
+
+/*
+ * Actually this routine will detect and configure only the first card with successful
+ * initialization. isapnpjump could be used to jump to a specific entry.
+ * Please always add entries at the end of the array.
+ * Should this be fixed? - azummo
+ */
+
+int __init ad1816_probe_isapnp(struct address_info *hw_config)
+{
+	int i;
+	
+	/* Count entries in isapnp_ad1816_list */
+	for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++)
+		;
+	/* Check and adjust isapnpjump */
+	if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) {
+		printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1);
+		isapnpjump = 0;
+	}
+
+	 for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) {
+	 	struct pci_dev *card = NULL;
+		
+		while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor,
+		  isapnp_ad1816_list[i].function, card)))
+			if(ad1816_init_isapnp(hw_config, card->bus, card, i))
+				return 0;
+	}
+
+	return -ENODEV;
+}
+#endif
+
+static int __init init_ad1816(void)
+{
+
+#ifdef __ISAPNP__
+	if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) {
+		printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n");
+		isapnp = 0;
+	}
+#endif
+
+	if( isapnp == 0) {
+		cfg.io_base	= io;
+		cfg.irq		= irq;
+		cfg.dma		= dma;
+		cfg.dma2	= dma2;
+	}
+
+	if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) {
+		printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n");
+		return -EINVAL;
+	}
+
+	if (probe_ad1816(&cfg) == 0) {
+		return -ENODEV;
+	}
+
+	attach_ad1816(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_ad1816 (void)
+{
+	int          i;
+	ad1816_info  *devc = NULL;
+  
+	/* remove any soundcard */
+	for (i = 0;  i < nr_ad1816_devs; i++) {
+		devc = &dev_info[i];
+		unload_card(devc);
+	}     
+	nr_ad1816_devs=0;
+
+#ifdef __ISAPNP__
+	if(activated)
+		if(ad1816_dev)
+			ad1816_dev->deactivate(ad1816_dev);
+#endif
+}
+
+module_init(init_ad1816);
+module_exit(cleanup_ad1816);
+
+#ifndef MODULE
+static int __init setup_ad1816(char *str)
+{
+	/* io, irq, dma, dma2 */
+	int ints[5];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+
+	return 1;
+}
+
+__setup("ad1816=", setup_ad1816);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/ad1848.c linux-2.4.19-pre5-mjc/sound/oss/ad1848.c
--- linux/sound/oss/ad1848.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ad1848.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,3177 @@
+/*
+ * sound/ad1848.c
+ *
+ * The low level driver for the AD1848/CS4248 codec chip which
+ * is used for example in the MS Sound System.
+ *
+ * The CS4231 which is used in the GUS MAX and some other cards is
+ * upwards compatible with AD1848 and this driver is able to drive it.
+ *
+ * CS4231A and AD1845 are upward compatible with CS4231. However
+ * the new features of these chips are different.
+ *
+ * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU).
+ * CS4232A is an improved version of CS4232.
+ *
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer	: ioctl code reworked (vmalloc/vfree removed)
+ *			  general sleep/wakeup clean up.
+ * Alan Cox		: reformatted. Fixed SMP bugs. Moved to kernel alloc/free
+ *		          of irqs. Use dev_id.
+ * Christoph Hellwig	: adapted to module_init/module_exit
+ * Aki Laukkanen	: added power management support
+ * Arnaldo C. de Melo	: added missing restore_flags in ad1848_resume
+ * Miguel Freitas       : added ISA PnP support
+ * Alan Cox		: Added CS4236->4239 identification
+ * Daniel T. Cobra	: Alernate config/mixer for later chips
+ * Alan Cox		: Merged chip idents and config code
+ *
+ * TODO
+ *		APM save restore assist code on IBM thinkpad
+ *
+ * Status:
+ *		Tested. Believed fully functional.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/pm.h>
+#include <linux/isapnp.h>
+
+#define DEB(x)
+#define DEB1(x)
+#include "sound_config.h"
+
+#include "ad1848.h"
+#include "ad1848_mixer.h"
+
+typedef struct
+{
+	int             base;
+	int             irq;
+	int             dma1, dma2;
+	int             dual_dma;	/* 1, when two DMA channels allocated */
+	int 		subtype;
+	unsigned char   MCE_bit;
+	unsigned char   saved_regs[64];	/* Includes extended register space */
+	int             debug_flag;
+
+	int             audio_flags;
+	int             record_dev, playback_dev;
+
+	int             xfer_count;
+	int             audio_mode;
+	int             open_mode;
+	int             intr_active;
+	char           *chip_name, *name;
+	int             model;
+#define MD_1848		1
+#define MD_4231		2
+#define MD_4231A	3
+#define MD_1845		4
+#define MD_4232		5
+#define MD_C930		6
+#define MD_IWAVE	7
+#define MD_4235         8 /* Crystal Audio CS4235  */
+#define MD_1845_SSCAPE  9 /* Ensoniq Soundscape PNP*/
+#define MD_4236		10 /* 4236 and higher */
+#define MD_42xB		11 /* CS 42xB */
+#define MD_4239		12 /* CS4239 */
+
+	/* Mixer parameters */
+	int             recmask;
+	int             supported_devices, orig_devices;
+	int             supported_rec_devices, orig_rec_devices;
+	int            *levels;
+	short           mixer_reroute[32];
+	int             dev_no;
+	volatile unsigned long timer_ticks;
+	int             timer_running;
+	int             irq_ok;
+	mixer_ents     *mix_devices;
+	int             mixer_output_port;
+
+	/* Power management */
+	struct		pm_dev *pmdev;
+} ad1848_info;
+
+typedef struct ad1848_port_info
+{
+	int             open_mode;
+	int             speed;
+	unsigned char   speed_bits;
+	int             channels;
+	int             audio_format;
+	unsigned char   format_bits;
+}
+ad1848_port_info;
+
+static struct address_info cfg;
+static int nr_ad1848_devs;
+
+int deskpro_xl;
+int deskpro_m;
+int soundpro;
+
+static volatile signed char irq2dev[17] = {
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+#ifndef EXCLUDE_TIMERS
+static int timer_installed = -1;
+#endif
+
+static int loaded;
+
+static int ad_format_mask[13 /*devc->model */ ] =
+{
+	0,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,	/* AD1845 */
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE /* CS4235 */,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW	/* Ensoniq Soundscape*/,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+	AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
+};
+
+static ad1848_info adev_info[MAX_AUDIO_DEV];
+
+#define io_Index_Addr(d)	((d)->base)
+#define io_Indexed_Data(d)	((d)->base+1)
+#define io_Status(d)		((d)->base+2)
+#define io_Polled_IO(d)		((d)->base+3)
+
+static struct {
+     unsigned char flags;
+#define CAP_F_TIMER 0x01     
+} capabilities [10 /*devc->model */ ] = {
+     {0}
+    ,{0}           /* MD_1848  */
+    ,{CAP_F_TIMER} /* MD_4231  */
+    ,{CAP_F_TIMER} /* MD_4231A */
+    ,{CAP_F_TIMER} /* MD_1845  */
+    ,{CAP_F_TIMER} /* MD_4232  */
+    ,{0}           /* MD_C930  */
+    ,{CAP_F_TIMER} /* MD_IWAVE */
+    ,{0}           /* MD_4235  */
+    ,{CAP_F_TIMER} /* MD_1845_SSCAPE */
+};
+
+#ifdef __ISAPNP__
+static int isapnp	= 1;
+static int isapnpjump	= 0;
+static int reverse	= 0;
+
+static int audio_activated = 0;
+#else
+static int isapnp	= 0;
+#endif
+
+
+
+static int      ad1848_open(int dev, int mode);
+static void     ad1848_close(int dev);
+static void     ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);
+static void     ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);
+static int      ad1848_prepare_for_output(int dev, int bsize, int bcount);
+static int      ad1848_prepare_for_input(int dev, int bsize, int bcount);
+static void     ad1848_halt(int dev);
+static void     ad1848_halt_input(int dev);
+static void     ad1848_halt_output(int dev);
+static void     ad1848_trigger(int dev, int bits);
+static int	ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+#ifndef EXCLUDE_TIMERS
+static int ad1848_tmr_install(int dev);
+static void ad1848_tmr_reprogram(int dev);
+#endif
+
+static int ad_read(ad1848_info * devc, int reg)
+{
+	unsigned long flags;
+	int x;
+	int timeout = 900000;
+
+	while (timeout > 0 && inb(devc->base) == 0x80)	/*Are we initializing */
+		timeout--;
+
+	save_flags(flags);
+	cli();
+	
+	if(reg < 32)
+	{
+		outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+		x = inb(io_Indexed_Data(devc));
+	}
+	else
+	{
+		int xreg, xra;
+
+		xreg = (reg & 0xff) - 32;
+		xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
+		outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+		outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
+		x = inb(io_Indexed_Data(devc));
+	}
+	restore_flags(flags);
+
+	return x;
+}
+
+static void ad_write(ad1848_info * devc, int reg, int data)
+{
+	unsigned long flags;
+	int timeout = 900000;
+
+	while (timeout > 0 && inb(devc->base) == 0x80)	/* Are we initializing */
+		timeout--;
+
+	save_flags(flags);
+	cli();
+	
+	if(reg < 32)
+	{
+		outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+		outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));
+	}
+	else
+	{
+		int xreg, xra;
+		
+		xreg = (reg & 0xff) - 32;
+		xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
+		outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+		outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
+		outb((unsigned char) (data & 0xff), io_Indexed_Data(devc));
+	}
+	restore_flags(flags);
+}
+
+static void wait_for_calibration(ad1848_info * devc)
+{
+	int timeout = 0;
+
+	/*
+	 * Wait until the auto calibration process has finished.
+	 *
+	 * 1)       Wait until the chip becomes ready (reads don't return 0x80).
+	 * 2)       Wait until the ACI bit of I11 gets on and then off.
+	 */
+
+	timeout = 100000;
+	while (timeout > 0 && inb(devc->base) == 0x80)
+		timeout--;
+	if (inb(devc->base) & 0x80)
+		printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n");
+
+	timeout = 100;
+	while (timeout > 0 && !(ad_read(devc, 11) & 0x20))
+		timeout--;
+	if (!(ad_read(devc, 11) & 0x20))
+		return;
+
+	timeout = 80000;
+	while (timeout > 0 && (ad_read(devc, 11) & 0x20))
+		timeout--;
+	if (ad_read(devc, 11) & 0x20)
+		if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE))
+			printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n");
+}
+
+static void ad_mute(ad1848_info * devc)
+{
+	int i;
+	unsigned char prev;
+
+	/*
+	 * Save old register settings and mute output channels
+	 */
+	 
+	for (i = 6; i < 8; i++)
+	{
+		prev = devc->saved_regs[i] = ad_read(devc, i);
+	}
+
+}
+
+static void ad_unmute(ad1848_info * devc)
+{
+}
+
+static void ad_enter_MCE(ad1848_info * devc)
+{
+	unsigned long flags;
+	int timeout = 1000;
+	unsigned short prev;
+
+	while (timeout > 0 && inb(devc->base) == 0x80)	/*Are we initializing */
+		timeout--;
+
+	save_flags(flags);
+	cli();
+
+	devc->MCE_bit = 0x40;
+	prev = inb(io_Index_Addr(devc));
+	if (prev & 0x40)
+	{
+		restore_flags(flags);
+		return;
+	}
+	outb((devc->MCE_bit), io_Index_Addr(devc));
+	restore_flags(flags);
+}
+
+static void ad_leave_MCE(ad1848_info * devc)
+{
+	unsigned long flags;
+	unsigned char prev, acal;
+	int timeout = 1000;
+
+	while (timeout > 0 && inb(devc->base) == 0x80)	/*Are we initializing */
+		timeout--;
+
+	save_flags(flags);
+	cli();
+
+	acal = ad_read(devc, 9);
+
+	devc->MCE_bit = 0x00;
+	prev = inb(io_Index_Addr(devc));
+	outb((0x00), io_Index_Addr(devc));	/* Clear the MCE bit */
+
+	if ((prev & 0x40) == 0)	/* Not in MCE mode */
+	{
+		restore_flags(flags);
+		return;
+	}
+	outb((0x00), io_Index_Addr(devc));	/* Clear the MCE bit */
+	if (acal & 0x08)	/* Auto calibration is enabled */
+		wait_for_calibration(devc);
+	restore_flags(flags);
+}
+
+static int ad1848_set_recmask(ad1848_info * devc, int mask)
+{
+	unsigned char   recdev;
+	int             i, n;
+
+	mask &= devc->supported_rec_devices;
+
+	/* Rename the mixer bits if necessary */
+	for (i = 0; i < 32; i++)
+	{
+		if (devc->mixer_reroute[i] != i)
+		{
+			if (mask & (1 << i))
+			{
+				mask &= ~(1 << i);
+				mask |= (1 << devc->mixer_reroute[i]);
+			}
+		}
+	}
+	
+	n = 0;
+	for (i = 0; i < 32; i++)	/* Count selected device bits */
+		if (mask & (1 << i))
+			n++;
+
+	if (!soundpro) {
+		if (n == 0)
+			mask = SOUND_MASK_MIC;
+		else if (n != 1) {	/* Too many devices selected */
+			mask &= ~devc->recmask;	/* Filter out active settings */
+
+			n = 0;
+			for (i = 0; i < 32; i++)	/* Count selected device bits */
+				if (mask & (1 << i))
+					n++;
+
+			if (n != 1)
+				mask = SOUND_MASK_MIC;
+		}
+		switch (mask) {
+		case SOUND_MASK_MIC:
+			recdev = 2;
+			break;
+
+		case SOUND_MASK_LINE:
+		case SOUND_MASK_LINE3:
+			recdev = 0;
+			break;
+
+		case SOUND_MASK_CD:
+		case SOUND_MASK_LINE1:
+			recdev = 1;
+			break;
+
+		case SOUND_MASK_IMIX:
+			recdev = 3;
+			break;
+
+		default:
+			mask = SOUND_MASK_MIC;
+			recdev = 2;
+		}
+
+		recdev <<= 6;
+		ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
+		ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
+	} else { /* soundpro */
+		unsigned char val;
+		int set_rec_bit;
+		int j;
+
+		for (i = 0; i < 32; i++) {	/* For each bit */
+			if ((devc->supported_rec_devices & (1 << i)) == 0)
+				continue;	/* Device not supported */
+
+			for (j = LEFT_CHN; j <= RIGHT_CHN; j++) {
+				if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */
+					continue;
+
+				/*
+				 * This is tricky:
+				 * set_rec_bit becomes 1 if the corresponding bit in mask is set
+				 * then it gets flipped if the polarity is inverse
+				 */
+				set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol;
+
+				val = ad_read(devc, devc->mix_devices[i][j].recreg);
+				val &= ~(1 << devc->mix_devices[i][j].recpos);
+				val |= (set_rec_bit << devc->mix_devices[i][j].recpos);
+				ad_write(devc, devc->mix_devices[i][j].recreg, val);
+			}
+		}
+	}
+
+	/* Rename the mixer bits back if necessary */
+	for (i = 0; i < 32; i++)
+	{
+		if (devc->mixer_reroute[i] != i)
+		{
+			if (mask & (1 << devc->mixer_reroute[i]))
+			{
+				mask &= ~(1 << devc->mixer_reroute[i]);
+				mask |= (1 << i);
+			}
+		}
+	}
+	devc->recmask = mask;
+	return mask;
+}
+
+static void change_bits(ad1848_info * devc, unsigned char *regval,
+			unsigned char *muteval, int dev, int chn, int newval)
+{
+	unsigned char mask;
+	int shift;
+	int mute;
+	int mutemask;
+	int set_mute_bit;
+
+	set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol;
+
+	if (devc->mix_devices[dev][chn].polarity == 1)	/* Reverse */
+		newval = 100 - newval;
+
+	mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
+	shift = devc->mix_devices[dev][chn].bitpos;
+
+	if (devc->mix_devices[dev][chn].mutepos == 8)
+	{			/* if there is no mute bit */
+		mute = 0;	/* No mute bit; do nothing special */
+		mutemask = ~0;	/* No mute bit; do nothing special */
+	}
+	else
+	{
+		mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
+		mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
+	}
+
+	newval = (int) ((newval * mask) + 50) / 100;	/* Scale it */
+	*regval &= ~(mask << shift);			/* Clear bits */
+	*regval |= (newval & mask) << shift;		/* Set new value */
+
+	*muteval &= mutemask;
+	*muteval |= mute;
+}
+
+static int ad1848_mixer_get(ad1848_info * devc, int dev)
+{
+	if (!((1 << dev) & devc->supported_devices))
+		return -EINVAL;
+
+	dev = devc->mixer_reroute[dev];
+
+	return devc->levels[dev];
+}
+
+static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel)
+{
+	int regoffs, muteregoffs;
+	unsigned char val, muteval;
+
+	regoffs = devc->mix_devices[dev][channel].regno;
+	muteregoffs = devc->mix_devices[dev][channel].mutereg;
+	val = ad_read(devc, regoffs);
+
+	if (muteregoffs != regoffs) {
+		muteval = ad_read(devc, muteregoffs);
+		change_bits(devc, &val, &muteval, dev, channel, value);
+	}
+	else
+		change_bits(devc, &val, &val, dev, channel, value);
+
+	ad_write(devc, regoffs, val);
+	devc->saved_regs[regoffs] = val;
+	if (muteregoffs != regoffs) {
+		ad_write(devc, muteregoffs, muteval);
+		devc->saved_regs[muteregoffs] = muteval;
+	}
+}
+
+static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
+{
+	int left = value & 0x000000ff;
+	int right = (value & 0x0000ff00) >> 8;
+	int retvol;
+
+	if (dev > 31)
+		return -EINVAL;
+
+	if (!(devc->supported_devices & (1 << dev)))
+		return -EINVAL;
+
+	dev = devc->mixer_reroute[dev];
+
+	if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
+		return -EINVAL;
+
+	if (left > 100)
+		left = 100;
+	if (right > 100)
+		right = 100;
+
+	if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)	/* Mono control */
+		right = left;
+
+	retvol = left | (right << 8);
+
+	/* Scale volumes */
+	left = mix_cvt[left];
+	right = mix_cvt[right];
+
+	devc->levels[dev] = retvol;
+
+	/*
+	 * Set the left channel
+	 */
+	ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN);
+
+	/*
+	 * Set the right channel
+	 */
+	if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
+		goto out;
+	ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN);
+
+ out:
+	return retvol;
+}
+
+static void ad1848_mixer_reset(ad1848_info * devc)
+{
+	int i;
+	char name[32];
+
+	devc->mix_devices = &(ad1848_mix_devices[0]);
+
+	sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs);
+
+	for (i = 0; i < 32; i++)
+		devc->mixer_reroute[i] = i;
+
+	devc->supported_rec_devices = MODE1_REC_DEVICES;
+
+	switch (devc->model)
+	{
+		case MD_4231:
+		case MD_4231A:
+		case MD_1845:
+		case MD_1845_SSCAPE:
+			devc->supported_devices = MODE2_MIXER_DEVICES;
+			break;
+
+		case MD_C930:
+			devc->supported_devices = C930_MIXER_DEVICES;
+			devc->mix_devices = &(c930_mix_devices[0]);
+			break;
+
+		case MD_IWAVE:
+			devc->supported_devices = MODE3_MIXER_DEVICES;
+			devc->mix_devices = &(iwave_mix_devices[0]);
+			break;
+
+		case MD_42xB:
+		case MD_4239:
+			devc->mix_devices = &(cs42xb_mix_devices[0]);
+			devc->supported_devices = MODE3_MIXER_DEVICES;
+			break;
+		case MD_4232:
+		case MD_4236:
+			devc->supported_devices = MODE3_MIXER_DEVICES;
+			break;
+
+		case MD_1848:
+			if (soundpro) {
+				devc->supported_devices = SPRO_MIXER_DEVICES;
+				devc->supported_rec_devices = SPRO_REC_DEVICES;
+				devc->mix_devices = &(spro_mix_devices[0]);
+				break;
+			}
+
+		default:
+			devc->supported_devices = MODE1_MIXER_DEVICES;
+	}
+
+	devc->orig_devices = devc->supported_devices;
+	devc->orig_rec_devices = devc->supported_rec_devices;
+
+	devc->levels = load_mixer_volumes(name, default_mixer_levels, 1);
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+	{
+		if (devc->supported_devices & (1 << i))
+			ad1848_mixer_set(devc, i, devc->levels[i]);
+	}
+	
+	ad1848_set_recmask(devc, SOUND_MASK_MIC);
+	
+	devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
+
+	if (!soundpro) {
+		if (devc->mixer_output_port & AUDIO_SPEAKER)
+			ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
+		else
+			ad_write(devc, 26, ad_read(devc, 26) | 0x40);	/* Mute mono out */
+	} else {
+		/*
+		 * From the "wouldn't it be nice if the mixer API had (better)
+		 * support for custom stuff" category
+		 */
+		/* Enable surround mode and SB16 mixer */
+		ad_write(devc, 16, 0x60);
+	}
+}
+
+static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	ad1848_info *devc = mixer_devs[dev]->devc;
+	int val;
+
+	if (cmd == SOUND_MIXER_PRIVATE1) 
+	{
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (val != 0xffff) 
+		{
+			val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
+			devc->mixer_output_port = val;
+			val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT;	/* Always on */
+			devc->mixer_output_port = val;
+			if (val & AUDIO_SPEAKER)
+				ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
+			else
+				ad_write(devc, 26, ad_read(devc, 26) | 0x40);		/* Mute mono out */
+		}
+		val = devc->mixer_output_port;
+		return put_user(val, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2)
+	{
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		return(ad1848_control(AD1848_MIXER_REROUTE, val));
+	}
+	if (((cmd >> 8) & 0xff) == 'M') 
+	{
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		{
+			switch (cmd & 0xff) 
+			{
+				case SOUND_MIXER_RECSRC:
+					if (get_user(val, (int *)arg))
+						return -EFAULT;
+					val = ad1848_set_recmask(devc, val);
+					break;
+				
+				default:
+					if (get_user(val, (int *)arg))
+					return -EFAULT;
+					val = ad1848_mixer_set(devc, cmd & 0xff, val);
+					break;
+			} 
+			return put_user(val, (int *)arg);
+		}
+		else
+		{
+			switch (cmd & 0xff) 
+			{
+				/*
+				 * Return parameters
+				 */
+			    
+				case SOUND_MIXER_RECSRC:
+					val = devc->recmask;
+					break;
+				
+				case SOUND_MIXER_DEVMASK:
+					val = devc->supported_devices;
+					break;
+				
+				case SOUND_MIXER_STEREODEVS:
+					val = devc->supported_devices;
+					if (devc->model != MD_C930)
+						val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+					break;
+				
+				case SOUND_MIXER_RECMASK:
+					val = devc->supported_rec_devices;
+					break;
+
+				case SOUND_MIXER_CAPS:
+					val=SOUND_CAP_EXCL_INPUT;
+					break;
+
+				default:
+					val = ad1848_mixer_get(devc, cmd & 0xff);
+					break;
+			}
+			return put_user(val, (int *)arg);
+		}
+	}
+	else
+		return -EINVAL;
+}
+
+static int ad1848_set_speed(int dev, int arg)
+{
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	/*
+	 * The sampling speed is encoded in the least significant nibble of I8. The
+	 * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
+	 * three bits select the divisor (indirectly):
+	 *
+	 * The available speeds are in the following table. Keep the speeds in
+	 * the increasing order.
+	 */
+	typedef struct
+	{
+		int             speed;
+		unsigned char   bits;
+	}
+	speed_struct;
+
+	static speed_struct speed_table[] =
+	{
+		{5510, (0 << 1) | 1},
+		{5510, (0 << 1) | 1},
+		{6620, (7 << 1) | 1},
+		{8000, (0 << 1) | 0},
+		{9600, (7 << 1) | 0},
+		{11025, (1 << 1) | 1},
+		{16000, (1 << 1) | 0},
+		{18900, (2 << 1) | 1},
+		{22050, (3 << 1) | 1},
+		{27420, (2 << 1) | 0},
+		{32000, (3 << 1) | 0},
+		{33075, (6 << 1) | 1},
+		{37800, (4 << 1) | 1},
+		{44100, (5 << 1) | 1},
+		{48000, (6 << 1) | 0}
+	};
+
+	int i, n, selected = -1;
+
+	n = sizeof(speed_table) / sizeof(speed_struct);
+
+	if (arg <= 0)
+		return portc->speed;
+
+	if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)	/* AD1845 has different timer than others */
+	{
+		if (arg < 4000)
+			arg = 4000;
+		if (arg > 50000)
+			arg = 50000;
+
+		portc->speed = arg;
+		portc->speed_bits = speed_table[3].bits;
+		return portc->speed;
+	}
+	if (arg < speed_table[0].speed)
+		selected = 0;
+	if (arg > speed_table[n - 1].speed)
+		selected = n - 1;
+
+	for (i = 1 /*really */ ; selected == -1 && i < n; i++)
+	{
+		if (speed_table[i].speed == arg)
+			selected = i;
+		else if (speed_table[i].speed > arg)
+		{
+			int diff1, diff2;
+
+			diff1 = arg - speed_table[i - 1].speed;
+			diff2 = speed_table[i].speed - arg;
+
+			if (diff1 < diff2)
+				selected = i - 1;
+			else
+				selected = i;
+		}
+	}
+	if (selected == -1)
+	{
+		printk(KERN_WARNING "ad1848: Can't find speed???\n");
+		selected = 3;
+	}
+	portc->speed = speed_table[selected].speed;
+	portc->speed_bits = speed_table[selected].bits;
+	return portc->speed;
+}
+
+static short ad1848_set_channels(int dev, short arg)
+{
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	if (arg != 1 && arg != 2)
+		return portc->channels;
+
+	portc->channels = arg;
+	return arg;
+}
+
+static unsigned int ad1848_set_bits(int dev, unsigned int arg)
+{
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	static struct format_tbl
+	{
+		  int             format;
+		  unsigned char   bits;
+	}
+	format2bits[] =
+	{
+		{
+			0, 0
+		}
+		,
+		{
+			AFMT_MU_LAW, 1
+		}
+		,
+		{
+			AFMT_A_LAW, 3
+		}
+		,
+		{
+			AFMT_IMA_ADPCM, 5
+		}
+		,
+		{
+			AFMT_U8, 0
+		}
+		,
+		{
+			AFMT_S16_LE, 2
+		}
+		,
+		{
+			AFMT_S16_BE, 6
+		}
+		,
+		{
+			AFMT_S8, 0
+		}
+		,
+		{
+			AFMT_U16_LE, 0
+		}
+		,
+		{
+			AFMT_U16_BE, 0
+		}
+	};
+	int i, n = sizeof(format2bits) / sizeof(struct format_tbl);
+
+	if (arg == 0)
+		return portc->audio_format;
+
+	if (!(arg & ad_format_mask[devc->model]))
+		arg = AFMT_U8;
+
+	portc->audio_format = arg;
+
+	for (i = 0; i < n; i++)
+		if (format2bits[i].format == arg)
+		{
+			if ((portc->format_bits = format2bits[i].bits) == 0)
+				return portc->audio_format = AFMT_U8;		/* Was not supported */
+
+			return arg;
+		}
+	/* Still hanging here. Something must be terribly wrong */
+	portc->format_bits = 0;
+	return portc->audio_format = AFMT_U8;
+}
+
+static struct audio_driver ad1848_audio_driver =
+{
+	owner:		THIS_MODULE,
+	open:		ad1848_open,
+	close:		ad1848_close,
+	output_block:	ad1848_output_block,
+	start_input:	ad1848_start_input,
+	prepare_for_input:	ad1848_prepare_for_input,
+	prepare_for_output:	ad1848_prepare_for_output,
+	halt_io:	ad1848_halt,
+	halt_input:	ad1848_halt_input,
+	halt_output:	ad1848_halt_output,
+	trigger:	ad1848_trigger,
+	set_speed:	ad1848_set_speed,
+	set_bits:	ad1848_set_bits,
+	set_channels:	ad1848_set_channels
+};
+
+static struct mixer_operations ad1848_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"SOUNDPORT",
+	name:	"AD1848/CS4248/CS4231",
+	ioctl:	ad1848_mixer_ioctl
+};
+
+static int ad1848_open(int dev, int mode)
+{
+	ad1848_info    *devc = NULL;
+	ad1848_port_info *portc;
+	unsigned long   flags;
+
+	if (dev < 0 || dev >= num_audiodevs)
+		return -ENXIO;
+
+	devc = (ad1848_info *) audio_devs[dev]->devc;
+	portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	save_flags(flags);
+	cli();
+	if (portc->open_mode || (devc->open_mode & mode))
+	{
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	devc->dual_dma = 0;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+	{
+		devc->dual_dma = 1;
+	}
+	devc->intr_active = 0;
+	devc->audio_mode = 0;
+	devc->open_mode |= mode;
+	portc->open_mode = mode;
+	ad1848_trigger(dev, 0);
+
+	if (mode & OPEN_READ)
+		devc->record_dev = dev;
+	if (mode & OPEN_WRITE)
+		devc->playback_dev = dev;
+	restore_flags(flags);
+/*
+ * Mute output until the playback really starts. This decreases clicking (hope so).
+ */
+	ad_mute(devc);
+
+	return 0;
+}
+
+static void ad1848_close(int dev)
+{
+	unsigned long   flags;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	DEB(printk("ad1848_close(void)\n"));
+
+	save_flags(flags);
+	cli();
+
+	devc->intr_active = 0;
+	ad1848_halt(dev);
+
+	devc->audio_mode = 0;
+	devc->open_mode &= ~portc->open_mode;
+	portc->open_mode = 0;
+
+	ad_unmute(devc);
+	restore_flags(flags);
+}
+
+static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
+{
+	unsigned long   flags, cnt;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	cnt = count;
+
+	if (portc->audio_format == AFMT_IMA_ADPCM)
+	{
+		cnt /= 4;
+	}
+	else
+	{
+		if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+			cnt >>= 1;
+	}
+	if (portc->channels > 1)
+		cnt >>= 1;
+	cnt--;
+
+	if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
+	    intrflag &&
+	    cnt == devc->xfer_count)
+	{
+		devc->audio_mode |= PCM_ENABLE_OUTPUT;
+		devc->intr_active = 1;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
+	save_flags(flags);
+	cli();
+
+	ad_write(devc, 15, (unsigned char) (cnt & 0xff));
+	ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+
+	devc->xfer_count = cnt;
+	devc->audio_mode |= PCM_ENABLE_OUTPUT;
+	devc->intr_active = 1;
+	restore_flags(flags);
+}
+
+static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+	unsigned long   flags, cnt;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	cnt = count;
+	if (portc->audio_format == AFMT_IMA_ADPCM)
+	{
+		cnt /= 4;
+	}
+	else
+	{
+		if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+			cnt >>= 1;
+	}
+	if (portc->channels > 1)
+		cnt >>= 1;
+	cnt--;
+
+	if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
+		intrflag &&
+		cnt == devc->xfer_count)
+	{
+		devc->audio_mode |= PCM_ENABLE_INPUT;
+		devc->intr_active = 1;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
+	save_flags(flags);
+	cli();
+
+	if (devc->model == MD_1848)
+	{
+		  ad_write(devc, 15, (unsigned char) (cnt & 0xff));
+		  ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+	}
+	else
+	{
+		  ad_write(devc, 31, (unsigned char) (cnt & 0xff));
+		  ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
+	}
+
+	ad_unmute(devc);
+
+	devc->xfer_count = cnt;
+	devc->audio_mode |= PCM_ENABLE_INPUT;
+	devc->intr_active = 1;
+	restore_flags(flags);
+}
+
+static int ad1848_prepare_for_output(int dev, int bsize, int bcount)
+{
+	int             timeout;
+	unsigned char   fs, old_fs, tmp = 0;
+	unsigned long   flags;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	ad_mute(devc);
+
+	save_flags(flags);
+	cli();
+	fs = portc->speed_bits | (portc->format_bits << 5);
+
+	if (portc->channels > 1)
+		fs |= 0x10;
+
+	ad_enter_MCE(devc);	/* Enables changes to the format select reg */
+
+	if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */
+	{
+		fs &= 0xf0;	/* Mask off the rate select bits */
+
+		ad_write(devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+		ad_write(devc, 23, portc->speed & 0xff);	/* Speed LSB */
+	}
+	old_fs = ad_read(devc, 8);
+
+	if (devc->model == MD_4232 || devc->model >= MD_4236)
+	{
+		tmp = ad_read(devc, 16);
+		ad_write(devc, 16, tmp | 0x30);
+	}
+	if (devc->model == MD_IWAVE)
+		ad_write(devc, 17, 0xc2);	/* Disable variable frequency select */
+
+	ad_write(devc, 8, fs);
+
+	/*
+	 * Write to I8 starts resynchronization. Wait until it completes.
+	 */
+
+	timeout = 0;
+	while (timeout < 100 && inb(devc->base) != 0x80)
+		timeout++;
+	timeout = 0;
+	while (timeout < 10000 && inb(devc->base) == 0x80)
+		timeout++;
+
+	if (devc->model >= MD_4232)
+		ad_write(devc, 16, tmp & ~0x30);
+
+	ad_leave_MCE(devc);	/*
+				 * Starts the calibration process.
+				 */
+	restore_flags(flags);
+	devc->xfer_count = 0;
+
+#ifndef EXCLUDE_TIMERS
+	if (dev == timer_installed && devc->timer_running)
+		if ((fs & 0x01) != (old_fs & 0x01))
+		{
+			ad1848_tmr_reprogram(dev);
+		}
+#endif
+	ad1848_halt_output(dev);
+	return 0;
+}
+
+static int ad1848_prepare_for_input(int dev, int bsize, int bcount)
+{
+	int timeout;
+	unsigned char fs, old_fs, tmp = 0;
+	unsigned long flags;
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	if (devc->audio_mode)
+		return 0;
+
+	save_flags(flags);
+	cli();
+	fs = portc->speed_bits | (portc->format_bits << 5);
+
+	if (portc->channels > 1)
+		fs |= 0x10;
+
+	ad_enter_MCE(devc);	/* Enables changes to the format select reg */
+
+	if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE))	/* Use alternate speed select registers */
+	{
+		fs &= 0xf0;	/* Mask off the rate select bits */
+
+		ad_write(devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+		ad_write(devc, 23, portc->speed & 0xff);	/* Speed LSB */
+	}
+	if (devc->model == MD_4232)
+	{
+		tmp = ad_read(devc, 16);
+		ad_write(devc, 16, tmp | 0x30);
+	}
+	if (devc->model == MD_IWAVE)
+		ad_write(devc, 17, 0xc2);	/* Disable variable frequency select */
+
+	/*
+	 * If mode >= 2 (CS4231), set I28. It's the capture format register.
+	 */
+	
+	if (devc->model != MD_1848)
+	{
+		old_fs = ad_read(devc, 28);
+		ad_write(devc, 28, fs);
+
+		/*
+		 * Write to I28 starts resynchronization. Wait until it completes.
+		 */
+		
+		timeout = 0;
+		while (timeout < 100 && inb(devc->base) != 0x80)
+			timeout++;
+
+		timeout = 0;
+		while (timeout < 10000 && inb(devc->base) == 0x80)
+			timeout++;
+
+		if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
+		{
+			/*
+			 * CS4231 compatible devices don't have separate sampling rate selection
+			 * register for recording an playback. The I8 register is shared so we have to
+			 * set the speed encoding bits of it too.
+			 */
+			unsigned char   tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
+
+			ad_write(devc, 8, tmp);
+			/*
+			 * Write to I8 starts resynchronization. Wait until it completes.
+			 */
+			timeout = 0;
+			while (timeout < 100 && inb(devc->base) != 0x80)
+				timeout++;
+
+			timeout = 0;
+			while (timeout < 10000 && inb(devc->base) == 0x80)
+				timeout++;
+		}
+	}
+	else
+	{			/* For AD1848 set I8. */
+
+		old_fs = ad_read(devc, 8);
+		ad_write(devc, 8, fs);
+		/*
+		 * Write to I8 starts resynchronization. Wait until it completes.
+		 */
+		timeout = 0;
+		while (timeout < 100 && inb(devc->base) != 0x80)
+			timeout++;
+		timeout = 0;
+		while (timeout < 10000 && inb(devc->base) == 0x80)
+			timeout++;
+	}
+
+	if (devc->model == MD_4232)
+		ad_write(devc, 16, tmp & ~0x30);
+
+	ad_leave_MCE(devc);	/*
+				 * Starts the calibration process.
+				 */
+	restore_flags(flags);
+	devc->xfer_count = 0;
+
+#ifndef EXCLUDE_TIMERS
+	if (dev == timer_installed && devc->timer_running)
+	{
+		if ((fs & 0x01) != (old_fs & 0x01))
+		{
+			ad1848_tmr_reprogram(dev);
+		}
+	}
+#endif
+	ad1848_halt_input(dev);
+	return 0;
+}
+
+static void ad1848_halt(int dev)
+{
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+	unsigned char   bits = ad_read(devc, 9);
+
+	if (bits & 0x01 && (portc->open_mode & OPEN_WRITE))
+		ad1848_halt_output(dev);
+
+	if (bits & 0x02 && (portc->open_mode & OPEN_READ))
+		ad1848_halt_input(dev);
+	devc->audio_mode = 0;
+}
+
+static void ad1848_halt_input(int dev)
+{
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	unsigned long   flags;
+
+	if (!(ad_read(devc, 9) & 0x02))
+		return;		/* Capture not enabled */
+
+	save_flags(flags);
+	cli();
+
+	ad_mute(devc);
+
+	{
+		int             tmout;
+		
+		if(!isa_dma_bridge_buggy)
+		        disable_dma(audio_devs[dev]->dmap_in->dma);
+
+		for (tmout = 0; tmout < 100000; tmout++)
+			if (ad_read(devc, 11) & 0x10)
+				break;
+		ad_write(devc, 9, ad_read(devc, 9) & ~0x02);	/* Stop capture */
+
+		if(!isa_dma_bridge_buggy)
+		        enable_dma(audio_devs[dev]->dmap_in->dma);
+		devc->audio_mode &= ~PCM_ENABLE_INPUT;
+	}
+
+	outb(0, io_Status(devc));	/* Clear interrupt status */
+	outb(0, io_Status(devc));	/* Clear interrupt status */
+
+	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+
+	restore_flags(flags);
+}
+
+static void ad1848_halt_output(int dev)
+{
+	ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+	unsigned long flags;
+
+	if (!(ad_read(devc, 9) & 0x01))
+		return;		/* Playback not enabled */
+
+	save_flags(flags);
+	cli();
+
+	ad_mute(devc);
+	{
+		int             tmout;
+
+		if(!isa_dma_bridge_buggy)
+		        disable_dma(audio_devs[dev]->dmap_out->dma);
+
+		for (tmout = 0; tmout < 100000; tmout++)
+			if (ad_read(devc, 11) & 0x10)
+				break;
+		ad_write(devc, 9, ad_read(devc, 9) & ~0x01);	/* Stop playback */
+
+		if(!isa_dma_bridge_buggy)
+		       enable_dma(audio_devs[dev]->dmap_out->dma);
+
+		devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+	}
+
+	outb((0), io_Status(devc));	/* Clear interrupt status */
+	outb((0), io_Status(devc));	/* Clear interrupt status */
+
+	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+
+	restore_flags(flags);
+}
+
+static void ad1848_trigger(int dev, int state)
+{
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+	unsigned long   flags;
+	unsigned char   tmp, old;
+
+	save_flags(flags);
+	cli();
+	state &= devc->audio_mode;
+
+	tmp = old = ad_read(devc, 9);
+
+	if (portc->open_mode & OPEN_READ)
+	{
+		  if (state & PCM_ENABLE_INPUT)
+			  tmp |= 0x02;
+		  else
+			  tmp &= ~0x02;
+	}
+	if (portc->open_mode & OPEN_WRITE)
+	{
+		if (state & PCM_ENABLE_OUTPUT)
+			tmp |= 0x01;
+		else
+			tmp &= ~0x01;
+	}
+	/* ad_mute(devc); */
+	if (tmp != old)
+	{
+		  ad_write(devc, 9, tmp);
+		  ad_unmute(devc);
+	}
+	restore_flags(flags);
+}
+
+static void ad1848_init_hw(ad1848_info * devc)
+{
+	int i;
+	int *init_values;
+
+	/*
+	 * Initial values for the indirect registers of CS4248/AD1848.
+	 */
+	static int      init_values_a[] =
+	{
+		0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+		0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
+
+	/* Positions 16 to 31 just for CS4231/2 and ad1845 */
+		0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+	static int      init_values_b[] =
+	{
+		/* 
+		   Values for the newer chips
+		   Some of the register initialization values were changed. In
+		   order to get rid of the click that preceded PCM playback,
+		   calibration was disabled on the 10th byte. On that same byte,
+		   dual DMA was enabled; on the 11th byte, ADC dithering was
+		   enabled, since that is theoretically desirable; on the 13th
+		   byte, Mode 3 was selected, to enable access to extended
+		   registers.
+		 */
+		0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+		0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00,
+ 		0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
+ 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+	/*
+	 *	Select initialisation data
+	 */
+	 
+	init_values = init_values_a;
+	if(devc->model >= MD_4236)
+		init_values = init_values_b;
+
+	for (i = 0; i < 16; i++)
+		ad_write(devc, i, init_values[i]);
+
+
+	ad_mute(devc);		/* Initialize some variables */
+	ad_unmute(devc);	/* Leave it unmuted now */
+
+	if (devc->model > MD_1848)
+	{
+		if (devc->model == MD_1845_SSCAPE)
+			ad_write(devc, 12, ad_read(devc, 12) | 0x50);
+		else 
+			ad_write(devc, 12, ad_read(devc, 12) | 0x40);		/* Mode2 = enabled */
+
+		if (devc->model == MD_IWAVE)
+			ad_write(devc, 12, 0x6c);	/* Select codec mode 3 */
+
+		if (devc->model != MD_1845_SSCAPE)
+			for (i = 16; i < 32; i++)
+				ad_write(devc, i, init_values[i]);
+
+		if (devc->model == MD_IWAVE)
+			ad_write(devc, 16, 0x30);	/* Playback and capture counters enabled */
+	}
+	if (devc->model > MD_1848)
+	{
+		if (devc->audio_flags & DMA_DUPLEX)
+			ad_write(devc, 9, ad_read(devc, 9) & ~0x04);	/* Dual DMA mode */
+		else
+			ad_write(devc, 9, ad_read(devc, 9) | 0x04);	/* Single DMA mode */
+
+		if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
+			ad_write(devc, 27, ad_read(devc, 27) | 0x08);		/* Alternate freq select enabled */
+
+		if (devc->model == MD_IWAVE)
+		{		/* Some magic Interwave specific initialization */
+			ad_write(devc, 12, 0x6c);	/* Select codec mode 3 */
+			ad_write(devc, 16, 0x30);	/* Playback and capture counters enabled */
+			ad_write(devc, 17, 0xc2);	/* Alternate feature enable */
+		}
+	}
+	else
+	{
+		  devc->audio_flags &= ~DMA_DUPLEX;
+		  ad_write(devc, 9, ad_read(devc, 9) | 0x04);	/* Single DMA mode */
+		  if (soundpro)
+			  ad_write(devc, 12, ad_read(devc, 12) | 0x40);	/* Mode2 = enabled */
+	}
+
+	outb((0), io_Status(devc));	/* Clear pending interrupts */
+
+	/*
+	 * Toggle the MCE bit. It completes the initialization phase.
+	 */
+
+	ad_enter_MCE(devc);	/* In case the bit was off */
+	ad_leave_MCE(devc);
+
+	ad1848_mixer_reset(devc);
+}
+
+int ad1848_detect(int io_base, int *ad_flags, int *osp)
+{
+	unsigned char tmp;
+	ad1848_info *devc = &adev_info[nr_ad1848_devs];
+	unsigned char tmp1 = 0xff, tmp2 = 0xff;
+	int optiC930 = 0;	/* OPTi 82C930 flag */
+	int interwave = 0;
+	int ad1847_flag = 0;
+	int cs4248_flag = 0;
+	int sscape_flag = 0;
+
+	int i;
+
+	DDB(printk("ad1848_detect(%x)\n", io_base));
+
+	if (ad_flags)
+	{
+		if (*ad_flags == 0x12345678)
+		{
+			interwave = 1;
+			*ad_flags = 0;
+		}
+		
+		if (*ad_flags == 0x87654321)
+		{
+			sscape_flag = 1;
+			*ad_flags = 0;
+		}
+		
+		if (*ad_flags == 0x12345677)
+		{
+		    cs4248_flag = 1;
+		    *ad_flags = 0;
+		}
+	}
+	if (nr_ad1848_devs >= MAX_AUDIO_DEV)
+	{
+		printk(KERN_ERR "ad1848 - Too many audio devices\n");
+		return 0;
+	}
+	if (check_region(io_base, 4))
+	{
+		printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base);
+		return 0;
+	}
+	devc->base = io_base;
+	devc->irq_ok = 0;
+	devc->timer_running = 0;
+	devc->MCE_bit = 0x40;
+	devc->irq = 0;
+	devc->open_mode = 0;
+	devc->chip_name = devc->name = "AD1848";
+	devc->model = MD_1848;	/* AD1848 or CS4248 */
+	devc->levels = NULL;
+	devc->debug_flag = 0;
+
+	/*
+	 * Check that the I/O address is in use.
+	 *
+	 * The bit 0x80 of the base I/O port is known to be 0 after the
+	 * chip has performed its power on initialization. Just assume
+	 * this has happened before the OS is starting.
+	 *
+	 * If the I/O address is unused, it typically returns 0xff.
+	 */
+
+	if (inb(devc->base) == 0xff)
+	{
+		DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
+	}
+
+	/*
+	 * Wait for the device to stop initialization
+	 */
+	
+	DDB(printk("ad1848_detect() - step 0\n"));
+
+	for (i = 0; i < 10000000; i++)
+	{
+		unsigned char   x = inb(devc->base);
+
+		if (x == 0xff || !(x & 0x80))
+			break;
+	}
+
+	DDB(printk("ad1848_detect() - step A\n"));
+
+	if (inb(devc->base) == 0x80)	/* Not ready. Let's wait */
+		ad_leave_MCE(devc);
+
+	if ((inb(devc->base) & 0x80) != 0x00)	/* Not a AD1848 */
+	{
+		DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
+		return 0;
+	}
+	
+	/*
+	 * Test if it's possible to change contents of the indirect registers.
+	 * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
+	 * so try to avoid using it.
+	 */
+
+	DDB(printk("ad1848_detect() - step B\n"));
+	ad_write(devc, 0, 0xaa);
+	ad_write(devc, 1, 0x45);	/* 0x55 with bit 0x10 clear */
+
+	if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45)
+	{
+		if (tmp2 == 0x65)	/* AD1847 has couple of bits hardcoded to 1 */
+			ad1847_flag = 1;
+		else
+		{
+			DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
+			return 0;
+		}
+	}
+	DDB(printk("ad1848_detect() - step C\n"));
+	ad_write(devc, 0, 0x45);
+	ad_write(devc, 1, 0xaa);
+
+	if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa)
+	{
+		if (tmp2 == 0x8a)	/* AD1847 has few bits hardcoded to 1 */
+			ad1847_flag = 1;
+		else
+		{
+			DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
+			return 0;
+		}
+	}
+
+	/*
+	 * The indirect register I12 has some read only bits. Let's
+	 * try to change them.
+	 */
+
+	DDB(printk("ad1848_detect() - step D\n"));
+	tmp = ad_read(devc, 12);
+	ad_write(devc, 12, (~tmp) & 0x0f);
+
+	if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f))
+	{
+		DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
+		return 0;
+	}
+	
+	/*
+	 * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+	 *   0x01=RevB and 0x0A=RevC.
+	 */
+
+	/*
+	 * The original AD1848/CS4248 has just 15 indirect registers. This means
+	 * that I0 and I16 should return the same value (etc.).
+	 * However this doesn't work with CS4248. Actually it seems to be impossible
+	 * to detect if the chip is a CS4231 or CS4248.
+	 * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+	 * with CS4231.
+	 */
+
+	/*
+	 * OPTi 82C930 has mode2 control bit in another place. This test will fail
+	 * with it. Accept this situation as a possible indication of this chip.
+	 */
+
+	DDB(printk("ad1848_detect() - step F\n"));
+	ad_write(devc, 12, 0);	/* Mode2=disabled */
+
+	for (i = 0; i < 16; i++)
+	{
+		if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16)))
+		{
+			DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
+			if (!ad1847_flag)
+				optiC930 = 1;
+			break;
+		}
+	}
+
+	/*
+	 * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
+	 * The bit 0x80 is always 1 in CS4248 and CS4231.
+	 */
+
+	DDB(printk("ad1848_detect() - step G\n"));
+
+	if (ad_flags && *ad_flags == 400)
+		*ad_flags = 0;
+	else
+		ad_write(devc, 12, 0x40);	/* Set mode2, clear 0x80 */
+
+
+	if (ad_flags)
+		*ad_flags = 0;
+
+	tmp1 = ad_read(devc, 12);
+	if (tmp1 & 0x80)
+	{
+		if (ad_flags)
+			*ad_flags |= AD_F_CS4248;
+
+		devc->chip_name = "CS4248";	/* Our best knowledge just now */
+	}
+	if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
+	{
+		/*
+		 *      CS4231 detected - is it?
+		 *
+		 *      Verify that setting I0 doesn't change I16.
+		 */
+		
+		DDB(printk("ad1848_detect() - step H\n"));
+		ad_write(devc, 16, 0);	/* Set I16 to known value */
+
+		ad_write(devc, 0, 0x45);
+		if ((tmp1 = ad_read(devc, 16)) != 0x45)	/* No change -> CS4231? */
+		{
+			ad_write(devc, 0, 0xaa);
+			if ((tmp1 = ad_read(devc, 16)) == 0xaa)	/* Rotten bits? */
+			{
+				DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
+				return 0;
+			}
+			
+			/*
+			 * Verify that some bits of I25 are read only.
+			 */
+
+			DDB(printk("ad1848_detect() - step I\n"));
+			tmp1 = ad_read(devc, 25);	/* Original bits */
+			ad_write(devc, 25, ~tmp1);	/* Invert all bits */
+			if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
+			{
+				int id;
+
+				/*
+				 *      It's at least CS4231
+				 */
+
+				devc->chip_name = "CS4231";
+				devc->model = MD_4231;
+				
+				/*
+				 * It could be an AD1845 or CS4231A as well.
+				 * CS4231 and AD1845 report the same revision info in I25
+				 * while the CS4231A reports different.
+				 */
+
+				id = ad_read(devc, 25);
+				if ((id & 0xe7) == 0x80)	/* Device busy??? */
+					id = ad_read(devc, 25);
+				if ((id & 0xe7) == 0x80)	/* Device still busy??? */
+					id = ad_read(devc, 25);
+				DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
+
+                                if ((id & 0xe7) == 0x80) {
+					/* 
+					 * It must be a CS4231 or AD1845. The register I23 of
+					 * CS4231 is undefined and it appears to be read only.
+					 * AD1845 uses I23 for setting sample rate. Assume
+					 * the chip is AD1845 if I23 is changeable.
+					 */
+
+					unsigned char   tmp = ad_read(devc, 23);
+					ad_write(devc, 23, ~tmp);
+
+					if (interwave)
+					{
+						devc->model = MD_IWAVE;
+						devc->chip_name = "IWave";
+					}
+					else if (ad_read(devc, 23) != tmp)	/* AD1845 ? */
+					{
+						devc->chip_name = "AD1845";
+						devc->model = MD_1845;
+					}
+					else if (cs4248_flag)
+					{
+						if (ad_flags)
+							  *ad_flags |= AD_F_CS4248;
+						devc->chip_name = "CS4248";
+						devc->model = MD_1848;
+						ad_write(devc, 12, ad_read(devc, 12) & ~0x40);	/* Mode2 off */
+					}
+					ad_write(devc, 23, tmp);	/* Restore */
+				}
+				else
+				{
+					switch (id & 0x1f) {
+					case 3: /* CS4236/CS4235/CS42xB/CS4239 */
+						{
+							int xid;
+							ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */
+							ad_write(devc, 23, 0x9c); /* select extended register 25 */
+							xid = inb(io_Indexed_Data(devc));
+							ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */
+							switch (xid & 0x1f)
+							{
+								case 0x00:
+									devc->chip_name = "CS4237B(B)";
+									devc->model = MD_42xB;
+									break;
+								case 0x08:
+									/* Seems to be a 4238 ?? */
+									devc->chip_name = "CS4238";
+									devc->model = MD_42xB;
+									break;
+								case 0x09:
+									devc->chip_name = "CS4238B";
+									devc->model = MD_42xB;
+									break;
+								case 0x0b:
+									devc->chip_name = "CS4236B";
+									devc->model = MD_4236;
+									break;
+								case 0x10:
+									devc->chip_name = "CS4237B";
+									devc->model = MD_42xB;
+									break;
+								case 0x1d:
+									devc->chip_name = "CS4235";
+									devc->model = MD_4235;
+									break;
+								case 0x1e:
+									devc->chip_name = "CS4239";
+									devc->model = MD_4239;
+									break;
+								default:
+									printk("Chip ident is %X.\n", xid&0x1F);
+									devc->chip_name = "CS42xx";
+									devc->model = MD_4232;
+									break;
+							}
+						}
+						break;
+
+					case 2: /* CS4232/CS4232A */
+						devc->chip_name = "CS4232";
+						devc->model = MD_4232;
+						break;
+				
+					case 0:
+						if ((id & 0xe0) == 0xa0)
+						{
+							devc->chip_name = "CS4231A";
+							devc->model = MD_4231A;
+						}
+						else
+						{
+							devc->chip_name = "CS4321";
+							devc->model = MD_4231;
+						}
+						break;
+
+					default: /* maybe */
+						DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7));
+                                                if (optiC930)
+                                                {
+                                                        devc->chip_name = "82C930";
+                                                        devc->model = MD_C930;
+                                                }
+						else
+						{
+							devc->chip_name = "CS4231";
+							devc->model = MD_4231;
+						}
+					}
+				}
+			}
+			ad_write(devc, 25, tmp1);	/* Restore bits */
+
+			DDB(printk("ad1848_detect() - step K\n"));
+		}
+	} else if (tmp1 == 0x0a) {
+		/*
+		 * Is it perhaps a SoundPro CMI8330?
+		 * If so, then we should be able to change indirect registers
+		 * greater than I15 after activating MODE2, even though reading
+		 * back I12 does not show it.
+		 */
+
+		/*
+		 * Let's try comparing register values
+		 */
+		for (i = 0; i < 16; i++) {
+			if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) {
+				DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2));
+				soundpro = 1;
+				devc->chip_name = "SoundPro CMI 8330";
+				break;
+			}
+		}
+	}
+
+	DDB(printk("ad1848_detect() - step L\n"));
+	if (ad_flags)
+	{
+		  if (devc->model != MD_1848)
+			  *ad_flags |= AD_F_CS4231;
+	}
+	DDB(printk("ad1848_detect() - Detected OK\n"));
+
+	if (devc->model == MD_1848 && ad1847_flag)
+		devc->chip_name = "AD1847";
+
+
+	if (sscape_flag == 1)
+		devc->model = MD_1845_SSCAPE;
+
+	return 1;
+}
+
+int ad1848_init (char *name, int io_base, int irq, int dma_playback,
+		int dma_capture, int share_dma, int *osp, struct module *owner)
+{
+	/*
+	 * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+	 *   so that this driver doesn't need to allocate/deallocate it.
+	 *   The actually used IRQ is ABS(irq).
+	 */
+
+	int my_dev;
+	char dev_name[100];
+	int e;
+
+	ad1848_info  *devc = &adev_info[nr_ad1848_devs];
+
+	ad1848_port_info *portc = NULL;
+
+	devc->irq = (irq > 0) ? irq : 0;
+	devc->open_mode = 0;
+	devc->timer_ticks = 0;
+	devc->dma1 = dma_playback;
+	devc->dma2 = dma_capture;
+	devc->subtype = cfg.card_subtype;
+	devc->audio_flags = DMA_AUTOMODE;
+	devc->playback_dev = devc->record_dev = 0;
+	if (name != NULL)
+		devc->name = name;
+
+	if (name != NULL && name[0] != 0)
+		sprintf(dev_name,
+			"%s (%s)", name, devc->chip_name);
+	else
+		sprintf(dev_name,
+			"Generic audio codec (%s)", devc->chip_name);
+
+	request_region(devc->base, 4, devc->name);
+
+	conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture);
+
+	if (devc->model == MD_1848 || devc->model == MD_C930)
+		devc->audio_flags |= DMA_HARDSTOP;
+
+	if (devc->model > MD_1848)
+	{
+		if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
+			devc->audio_flags &= ~DMA_DUPLEX;
+		else
+			devc->audio_flags |= DMA_DUPLEX;
+	}
+
+	portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL);
+	if(portc==NULL)
+		return -1;
+
+	if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+					     dev_name,
+					     &ad1848_audio_driver,
+					     sizeof(struct audio_driver),
+					     devc->audio_flags,
+					     ad_format_mask[devc->model],
+					     devc,
+					     dma_playback,
+					     dma_capture)) < 0)
+	{
+		kfree(portc);
+		portc=NULL;
+		return -1;
+	}
+	
+	audio_devs[my_dev]->portc = portc;
+	audio_devs[my_dev]->mixer_dev = -1;
+	if (owner)
+		audio_devs[my_dev]->d->owner = owner;
+	memset((char *) portc, 0, sizeof(*portc));
+
+	nr_ad1848_devs++;
+
+	devc->pmdev = pm_register(PM_ISA_DEV, my_dev, ad1848_pm_callback);
+	if (devc->pmdev)
+		devc->pmdev->data = devc;
+
+	ad1848_init_hw(devc);
+
+	if (irq > 0)
+	{
+		devc->dev_no = my_dev;
+		if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0)
+		{
+			printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n");
+			/* Don't free it either then.. */
+			devc->irq = 0;
+		}
+		if (capabilities[devc->model].flags & CAP_F_TIMER)
+		{
+#ifndef CONFIG_SMP
+			int x;
+			unsigned char tmp = ad_read(devc, 16);
+#endif			
+
+			devc->timer_ticks = 0;
+
+			ad_write(devc, 21, 0x00);	/* Timer MSB */
+			ad_write(devc, 20, 0x10);	/* Timer LSB */
+#ifndef CONFIG_SMP
+			ad_write(devc, 16, tmp | 0x40);	/* Enable timer */
+			for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
+			ad_write(devc, 16, tmp & ~0x40);	/* Disable timer */
+
+			if (devc->timer_ticks == 0)
+				printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq);
+			else
+			{
+				DDB(printk("Interrupt test OK\n"));
+				devc->irq_ok = 1;
+			}
+#else
+			devc->irq_ok = 1;
+#endif			
+		}
+		else
+			devc->irq_ok = 1;	/* Couldn't test. assume it's OK */
+	} else if (irq < 0)
+		irq2dev[-irq] = devc->dev_no = my_dev;
+
+#ifndef EXCLUDE_TIMERS
+	if ((capabilities[devc->model].flags & CAP_F_TIMER) &&
+	    devc->irq_ok)
+		ad1848_tmr_install(my_dev);
+#endif
+
+	if (!share_dma)
+	{
+		if (sound_alloc_dma(dma_playback, devc->name))
+			printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback);
+
+		if (dma_capture != dma_playback)
+			if (sound_alloc_dma(dma_capture, devc->name))
+				printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture);
+	}
+
+	if ((e = sound_install_mixer(MIXER_DRIVER_VERSION,
+				     dev_name,
+				     &ad1848_mixer_operations,
+				     sizeof(struct mixer_operations),
+				     devc)) >= 0)
+	{
+		audio_devs[my_dev]->mixer_dev = e;
+		if (owner)
+			mixer_devs[e]->owner = owner;
+	}
+	return my_dev;
+}
+
+int ad1848_control(int cmd, int arg)
+{
+	ad1848_info *devc;
+
+	if (nr_ad1848_devs < 1)
+		return -ENODEV;
+
+	devc = &adev_info[nr_ad1848_devs - 1];
+
+	switch (cmd)
+	{
+		case AD1848_SET_XTAL:	/* Change clock frequency of AD1845 (only ) */
+			if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE)
+				return -EINVAL;
+			ad_enter_MCE(devc);
+			ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
+			ad_leave_MCE(devc);
+			break;
+
+		case AD1848_MIXER_REROUTE:
+		{
+			int o = (arg >> 8) & 0xff;
+			int n = arg & 0xff;
+
+			if (o < 0 || o >= SOUND_MIXER_NRDEVICES)
+				return -EINVAL;
+
+			if (!(devc->supported_devices & (1 << o)) &&
+			    !(devc->supported_rec_devices & (1 << o)))
+				return -EINVAL;
+
+			if (n == SOUND_MIXER_NONE)
+			{	/* Just hide this control */
+				ad1848_mixer_set(devc, o, 0);	/* Shut up it */
+				devc->supported_devices &= ~(1 << o);
+				devc->supported_rec_devices &= ~(1 << o);
+				break;
+			}
+
+			/* Make the mixer control identified by o to appear as n */
+			if (n < 0 || n >= SOUND_MIXER_NRDEVICES)
+				return -EINVAL;
+
+			devc->mixer_reroute[n] = o;	/* Rename the control */
+			if (devc->supported_devices & (1 << o))
+				devc->supported_devices |= (1 << n);
+			if (devc->supported_rec_devices & (1 << o))
+				devc->supported_rec_devices |= (1 << n);
+
+			devc->supported_devices &= ~(1 << o);
+			devc->supported_rec_devices &= ~(1 << o);
+		}
+		break;
+	}
+	return 0;
+}
+
+void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
+{
+	int i, mixer, dev = 0;
+	ad1848_info *devc = NULL;
+
+	for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
+	{
+		if (adev_info[i].base == io_base)
+		{
+			devc = &adev_info[i];
+			dev = devc->dev_no;
+		}
+	}
+		
+	if (devc != NULL)
+	{
+		if(audio_devs[dev]->portc!=NULL)
+			kfree(audio_devs[dev]->portc);
+		release_region(devc->base, 4);
+
+		if (!share_dma)
+		{
+			if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */
+				free_irq(devc->irq, (void *)devc->dev_no);
+
+			sound_free_dma(dma_playback);
+
+			if (dma_playback != dma_capture)
+				sound_free_dma(dma_capture);
+
+		}
+		mixer = audio_devs[devc->dev_no]->mixer_dev;
+		if(mixer>=0)
+			sound_unload_mixerdev(mixer);
+
+		if (devc->pmdev)
+			pm_unregister(devc->pmdev);
+
+		nr_ad1848_devs--;
+		for ( ; i < nr_ad1848_devs ; i++)
+			adev_info[i] = adev_info[i+1];
+	}
+	else
+		printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
+}
+
+void adintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	unsigned char status;
+	ad1848_info *devc;
+	int dev;
+	int alt_stat = 0xff;
+	unsigned char c930_stat = 0;
+	int cnt = 0;
+
+	dev = (int)dev_id;
+	devc = (ad1848_info *) audio_devs[dev]->devc;
+
+interrupt_again:		/* Jump back here if int status doesn't reset */
+
+	status = inb(io_Status(devc));
+
+	if (status == 0x80)
+		printk(KERN_DEBUG "adintr: Why?\n");
+	if (devc->model == MD_1848)
+		outb((0), io_Status(devc));	/* Clear interrupt status */
+
+	if (status & 0x01)
+	{
+		if (devc->model == MD_C930)
+		{		/* 82C930 has interrupt status register in MAD16 register MC11 */
+			unsigned long   flags;
+
+			save_flags(flags);
+			cli();
+
+			/* 0xe0e is C930 address port
+			 * 0xe0f is C930 data port
+			 */
+			outb(11, 0xe0e);
+			c930_stat = inb(0xe0f);
+			outb((~c930_stat), 0xe0f);
+
+			restore_flags(flags);
+
+			alt_stat = (c930_stat << 2) & 0x30;
+		}
+		else if (devc->model != MD_1848)
+		{
+			alt_stat = ad_read(devc, 24);
+			ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat);	/* Selective ack */
+		}
+
+		if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20))
+		{
+			DMAbuf_inputintr(devc->record_dev);
+		}
+		if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) &&
+		      (alt_stat & 0x10))
+		{
+			DMAbuf_outputintr(devc->playback_dev, 1);
+		}
+		if (devc->model != MD_1848 && (alt_stat & 0x40))	/* Timer interrupt */
+		{
+			devc->timer_ticks++;
+#ifndef EXCLUDE_TIMERS
+			if (timer_installed == dev && devc->timer_running)
+				sound_timer_interrupt();
+#endif
+		}
+	}
+/*
+ * Sometimes playback or capture interrupts occur while a timer interrupt
+ * is being handled. The interrupt will not be retriggered if we don't
+ * handle it now. Check if an interrupt is still pending and restart
+ * the handler in this case.
+ */
+	if (inb(io_Status(devc)) & 0x01 && cnt++ < 4)
+	{
+		  goto interrupt_again;
+	}
+}
+
+/*
+ *	Experimental initialization sequence for the integrated sound system
+ *	of the Compaq Deskpro M.
+ */
+
+static int init_deskpro_m(struct address_info *hw_config)
+{
+	unsigned char   tmp;
+
+	if ((tmp = inb(0xc44)) == 0xff)
+	{
+		DDB(printk("init_deskpro_m: Dead port 0xc44\n"));
+		return 0;
+	}
+
+	outb(0x10, 0xc44);
+	outb(0x40, 0xc45);
+	outb(0x00, 0xc46);
+	outb(0xe8, 0xc47);
+	outb(0x14, 0xc44);
+	outb(0x40, 0xc45);
+	outb(0x00, 0xc46);
+	outb(0xe8, 0xc47);
+	outb(0x10, 0xc44);
+
+	return 1;
+}
+
+/*
+ *	Experimental initialization sequence for the integrated sound system
+ *	of Compaq Deskpro XL.
+ */
+
+static int init_deskpro(struct address_info *hw_config)
+{
+	unsigned char   tmp;
+
+	if ((tmp = inb(0xc44)) == 0xff)
+	{
+		DDB(printk("init_deskpro: Dead port 0xc44\n"));
+		return 0;
+	}
+	outb((tmp | 0x04), 0xc44);	/* Select bank 1 */
+	if (inb(0xc44) != 0x04)
+	{
+		DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
+		return 0;
+	}
+	/*
+	 * OK. It looks like a Deskpro so let's proceed.
+	 */
+
+	/*
+	 * I/O port 0xc44 Audio configuration register.
+	 *
+	 * bits 0xc0:   Audio revision bits
+	 *              0x00 = Compaq Business Audio
+	 *              0x40 = MS Sound System Compatible (reset default)
+	 *              0x80 = Reserved
+	 *              0xc0 = Reserved
+	 * bit 0x20:    No Wait State Enable
+	 *              0x00 = Disabled (reset default, DMA mode)
+	 *              0x20 = Enabled (programmed I/O mode)
+	 * bit 0x10:    MS Sound System Decode Enable
+	 *              0x00 = Decoding disabled (reset default)
+	 *              0x10 = Decoding enabled
+	 * bit 0x08:    FM Synthesis Decode Enable
+	 *              0x00 = Decoding Disabled (reset default)
+	 *              0x08 = Decoding enabled
+	 * bit 0x04     Bank select
+	 *              0x00 = Bank 0
+	 *              0x04 = Bank 1
+	 * bits 0x03    MSS Base address
+	 *              0x00 = 0x530 (reset default)
+	 *              0x01 = 0x604
+	 *              0x02 = 0xf40
+	 *              0x03 = 0xe80
+	 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc44 (before): ");
+	outb((tmp & ~0x04), 0xc44);
+	printk("%02x ", inb(0xc44));
+	outb((tmp | 0x04), 0xc44);
+	printk("%02x\n", inb(0xc44));
+#endif
+
+	/* Set bank 1 of the register */
+	tmp = 0x58;		/* MSS Mode, MSS&FM decode enabled */
+
+	switch (hw_config->io_base)
+	{
+		case 0x530:
+			tmp |= 0x00;
+			break;
+		case 0x604:
+			tmp |= 0x01;
+			break;
+		case 0xf40:
+			tmp |= 0x02;
+			break;
+		case 0xe80:
+			tmp |= 0x03;
+			break;
+		default:
+			DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
+			return 0;
+	}
+	outb((tmp & ~0x04), 0xc44);	/* Write to bank=0 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc44 (after): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc44));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc44));
+#endif
+
+	/*
+	 * I/O port 0xc45 FM Address Decode/MSS ID Register.
+	 *
+	 * bank=0, bits 0xfe:   FM synthesis Decode Compare bits 7:1 (default=0x88)
+	 * bank=0, bit 0x01:    SBIC Power Control Bit
+	 *                      0x00 = Powered up
+	 *                      0x01 = Powered down
+	 * bank=1, bits 0xfc:   MSS ID (default=0x40)
+	 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc45 (before): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc45));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc45));
+#endif
+
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	outb((0x88), 0xc45);	/* FM base 7:0 = 0x88 */
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	outb((0x10), 0xc45);	/* MSS ID = 0x10 (MSS port returns 0x04) */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc45 (after): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc45));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc45));
+#endif
+
+
+	/*
+	 * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register.
+	 *
+	 * bank=0, bits 0xff:   FM synthesis Decode Compare bits 15:8 (default=0x03)
+	 * bank=1, bits 0xff:   Audio addressing ASIC id
+	 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc46 (before): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc46));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc46));
+#endif
+
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	outb((0x03), 0xc46);	/* FM base 15:8 = 0x03 */
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	outb((0x11), 0xc46);	/* ASIC ID = 0x11 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc46 (after): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc46));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc46));
+#endif
+
+	/*
+	 * I/O port 0xc47 FM Address Decode Register.
+	 *
+	 * bank=0, bits 0xff:   Decode enable selection for various FM address bits
+	 * bank=1, bits 0xff:   Reserved
+	 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc47 (before): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc47));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc47));
+#endif
+
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	outb((0x7c), 0xc47);	/* FM decode enable bits = 0x7c */
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	outb((0x00), 0xc47);	/* Reserved bank1 = 0x00 */
+
+#ifdef DEBUGXL
+	/* Debug printing */
+	printk("Port 0xc47 (after): ");
+	outb((tmp & ~0x04), 0xc44);	/* Select bank=0 */
+	printk("%02x ", inb(0xc47));
+	outb((tmp | 0x04), 0xc44);	/* Select bank=1 */
+	printk("%02x\n", inb(0xc47));
+#endif
+
+	/*
+	 * I/O port 0xc6f = Audio Disable Function Register
+	 */
+
+#ifdef DEBUGXL
+	printk("Port 0xc6f (before) = %02x\n", inb(0xc6f));
+#endif
+
+	outb((0x80), 0xc6f);
+
+#ifdef DEBUGXL
+	printk("Port 0xc6f (after) = %02x\n", inb(0xc6f));
+#endif
+
+	return 1;
+}
+
+int probe_ms_sound(struct address_info *hw_config)
+{
+	unsigned char   tmp;
+
+	DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
+
+	if (check_region(hw_config->io_base, 8))
+	{
+		printk(KERN_ERR "MSS: I/O port conflict\n");
+		return 0;
+	}
+	if (hw_config->card_subtype == 1)	/* Has no IRQ/DMA registers */
+	{
+		/* check_opl3(0x388, hw_config); */
+		return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
+	}
+
+	if (deskpro_xl && hw_config->card_subtype == 2)	/* Compaq Deskpro XL */
+	{
+		if (!init_deskpro(hw_config))
+			return 0;
+	}
+
+	if (deskpro_m)	/* Compaq Deskpro M */
+	{
+		if (!init_deskpro_m(hw_config))
+			return 0;
+	}
+
+	/*
+	   * Check if the IO port returns valid signature. The original MS Sound
+	   * system returns 0x04 while some cards (AudioTrix Pro for example)
+	   * return 0x00 or 0x0f.
+	 */
+
+	if ((tmp = inb(hw_config->io_base + 3)) == 0xff)	/* Bus float */
+	{
+		  int             ret;
+
+		  DDB(printk("I/O address is inactive (%x)\n", tmp));
+		  if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
+			  return 0;
+		  return 1;
+	}
+	DDB(printk("MSS signature = %x\n", tmp & 0x3f));
+	if ((tmp & 0x3f) != 0x04 &&
+	    (tmp & 0x3f) != 0x0f &&
+	    (tmp & 0x3f) != 0x00)
+	{
+		int ret;
+
+		MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
+		DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
+		if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
+			return 0;
+
+		hw_config->card_subtype = 1;
+		return 1;
+	}
+	if ((hw_config->irq != 5)  &&
+	    (hw_config->irq != 7)  &&
+	    (hw_config->irq != 9)  &&
+	    (hw_config->irq != 10) &&
+	    (hw_config->irq != 11) &&
+	    (hw_config->irq != 12))
+	{
+		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+	{
+		  printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma);
+		  return 0;
+	}
+	/*
+	 * Check that DMA0 is not in use with a 8 bit board.
+	 */
+
+	if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+	{
+		printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n");
+		return 0;
+	}
+	if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+	{
+		printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+		return 0;
+	}
+	return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
+}
+
+void attach_ms_sound(struct address_info *hw_config, struct module *owner)
+{
+	static signed char interrupt_bits[12] =
+	{
+		-1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20
+	};
+	signed char     bits;
+	char            dma2_bit = 0;
+
+	static char     dma_bits[4] =
+	{
+		1, 2, 0, 3
+	};
+
+	int config_port = hw_config->io_base + 0;
+	int version_port = hw_config->io_base + 3;
+	int dma = hw_config->dma;
+	int dma2 = hw_config->dma2;
+
+	if (hw_config->card_subtype == 1)	/* Has no IRQ/DMA registers */
+	{
+		hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4,
+						    hw_config->irq,
+						    hw_config->dma,
+						    hw_config->dma2, 0, 
+						    hw_config->osp,
+						    owner);
+		request_region(hw_config->io_base, 4, "WSS config");
+		return;
+	}
+	/*
+	 * Set the IRQ and DMA addresses.
+	 */
+
+	bits = interrupt_bits[hw_config->irq];
+	if (bits == -1)
+	{
+		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+		return;
+	}
+	outb((bits | 0x40), config_port);
+	if ((inb(version_port) & 0x40) == 0)
+		printk(KERN_ERR "[MSS: IRQ Conflict?]\n");
+
+/*
+ * Handle the capture DMA channel
+ */
+
+	if (dma2 != -1 && dma2 != dma)
+	{
+		if (!((dma == 0 && dma2 == 1) ||
+			(dma == 1 && dma2 == 0) ||
+			(dma == 3 && dma2 == 0)))
+		{	/* Unsupported combination. Try to swap channels */
+			int tmp = dma;
+
+			dma = dma2;
+			dma2 = tmp;
+		}
+		if ((dma == 0 && dma2 == 1) ||
+			(dma == 1 && dma2 == 0) ||
+			(dma == 3 && dma2 == 0))
+		{
+			dma2_bit = 0x04;	/* Enable capture DMA */
+		}
+		else
+		{
+			printk(KERN_WARNING "MSS: Invalid capture DMA\n");
+			dma2 = dma;
+		}
+	}
+	else
+	{
+		dma2 = dma;
+	}
+
+	hw_config->dma = dma;
+	hw_config->dma2 = dma2;
+
+	outb((bits | dma_bits[dma] | dma2_bit), config_port);	/* Write IRQ+DMA setup */
+
+	hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4,
+					  hw_config->irq,
+					  dma, dma2, 0,
+					  hw_config->osp,
+					  THIS_MODULE);
+	request_region(hw_config->io_base, 4, "WSS config");
+}
+
+void unload_ms_sound(struct address_info *hw_config)
+{
+	ad1848_unload(hw_config->io_base + 4,
+		      hw_config->irq,
+		      hw_config->dma,
+		      hw_config->dma2, 0);
+	sound_unload_audiodev(hw_config->slots[0]);
+	release_region(hw_config->io_base, 4);
+}
+
+#ifndef EXCLUDE_TIMERS
+
+/*
+ * Timer stuff (for /dev/music).
+ */
+
+static unsigned int current_interval = 0;
+
+static unsigned int ad1848_tmr_start(int dev, unsigned int usecs)
+{
+	unsigned long   flags;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+	unsigned long   xtal_nsecs;	/* nanoseconds per xtal oscillator tick */
+	unsigned long   divider;
+
+	save_flags(flags);
+	cli();
+
+	/*
+	 * Length of the timer interval (in nanoseconds) depends on the
+	 * selected crystal oscillator. Check this from bit 0x01 of I8.
+	 *
+	 * AD1845 has just one oscillator which has cycle time of 10.050 us
+	 * (when a 24.576 MHz xtal oscillator is used).
+	 *
+	 * Convert requested interval to nanoseconds before computing
+	 * the timer divider.
+	 */
+
+	if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
+		xtal_nsecs = 10050;
+	else if (ad_read(devc, 8) & 0x01)
+		xtal_nsecs = 9920;
+	else
+		xtal_nsecs = 9969;
+
+	divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs;
+
+	if (divider < 100)	/* Don't allow shorter intervals than about 1ms */
+		divider = 100;
+
+	if (divider > 65535)	/* Overflow check */
+		divider = 65535;
+
+	ad_write(devc, 21, (divider >> 8) & 0xff);	/* Set upper bits */
+	ad_write(devc, 20, divider & 0xff);	/* Set lower bits */
+	ad_write(devc, 16, ad_read(devc, 16) | 0x40);	/* Start the timer */
+	devc->timer_running = 1;
+	restore_flags(flags);
+
+	return current_interval = (divider * xtal_nsecs + 500) / 1000;
+}
+
+static void ad1848_tmr_reprogram(int dev)
+{
+	/*
+	 *    Audio driver has changed sampling rate so that a different xtal
+	 *      oscillator was selected. We have to reprogram the timer rate.
+	 */
+
+	ad1848_tmr_start(dev, current_interval);
+	sound_timer_syncinterval(current_interval);
+}
+
+static void ad1848_tmr_disable(int dev)
+{
+	unsigned long   flags;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+	save_flags(flags);
+	cli();
+	ad_write(devc, 16, ad_read(devc, 16) & ~0x40);
+	devc->timer_running = 0;
+	restore_flags(flags);
+}
+
+static void ad1848_tmr_restart(int dev)
+{
+	unsigned long   flags;
+	ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+	if (current_interval == 0)
+		return;
+
+	save_flags(flags);
+	cli();
+	ad_write(devc, 16, ad_read(devc, 16) | 0x40);
+	devc->timer_running = 1;
+	restore_flags(flags);
+}
+
+static struct sound_lowlev_timer ad1848_tmr =
+{
+	0,
+	2,
+	ad1848_tmr_start,
+	ad1848_tmr_disable,
+	ad1848_tmr_restart
+};
+
+static int ad1848_tmr_install(int dev)
+{
+	if (timer_installed != -1)
+		return 0;	/* Don't install another timer */
+
+	timer_installed = ad1848_tmr.dev = dev;
+	sound_timer_init(&ad1848_tmr, audio_devs[dev]->name);
+
+	return 1;
+}
+#endif /* EXCLUDE_TIMERS */
+
+static int ad1848_suspend(ad1848_info *devc)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	ad_mute(devc);
+	
+	restore_flags(flags);
+	return 0;
+}
+
+static int ad1848_resume(ad1848_info *devc)
+{
+	unsigned long flags;
+	int mixer_levels[32], i;
+
+	save_flags(flags);
+	cli();
+	
+	/* Thinkpad is a bit more of PITA than normal. The BIOS tends to
+	   restore it in a different config to the one we use.  Need to
+	   fix this somehow */
+
+	/* store old mixer levels */
+	memcpy(mixer_levels, devc->levels, sizeof (mixer_levels));  
+	ad1848_init_hw(devc);
+
+	/* restore mixer levels */
+	for (i = 0; i < 32; i++)
+		ad1848_mixer_set(devc, devc->dev_no, mixer_levels[i]);
+
+	if (!devc->subtype) {
+		static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 };
+		static char dma_bits[4] = { 1, 2, 0, 3 };
+
+		signed char bits;
+		char dma2_bit = 0;
+
+		int config_port = devc->base + 0;
+
+		bits = interrupt_bits[devc->irq];
+		if (bits == -1) {
+			printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq);
+			restore_flags(flags);
+			return -1;
+		}
+
+		outb((bits | 0x40), config_port); 
+
+		if (devc->dma2 != -1 && devc->dma2 != devc->dma1)
+			if ( (devc->dma1 == 0 && devc->dma2 == 1) ||
+			     (devc->dma1 == 1 && devc->dma2 == 0) ||
+			     (devc->dma1 == 3 && devc->dma2 == 0))
+				dma2_bit = 0x04;
+
+		outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port);
+	}
+
+	restore_flags(flags);
+      	return 0;
+}
+
+static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) 
+{
+	ad1848_info *devc = dev->data;
+	if (devc) {
+		DEB(printk("ad1848: pm event received: 0x%x\n", rqst));
+
+		switch (rqst) {
+		case PM_SUSPEND:
+			ad1848_suspend(devc);
+			break;
+		case PM_RESUME:
+			ad1848_resume(devc);
+			break;
+		}
+	}
+	return 0;
+}
+
+
+EXPORT_SYMBOL(ad1848_detect);
+EXPORT_SYMBOL(ad1848_init);
+EXPORT_SYMBOL(ad1848_unload);
+EXPORT_SYMBOL(ad1848_control);
+EXPORT_SYMBOL(adintr);
+EXPORT_SYMBOL(probe_ms_sound);
+EXPORT_SYMBOL(attach_ms_sound);
+EXPORT_SYMBOL(unload_ms_sound);
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma2 = -1;
+static int __initdata type = 0;
+
+MODULE_PARM(io, "i");                   /* I/O for a raw AD1848 card */
+MODULE_PARM(irq, "i");                  /* IRQ to use */
+MODULE_PARM(dma, "i");                  /* First DMA channel */
+MODULE_PARM(dma2, "i");                 /* Second DMA channel */
+MODULE_PARM(type, "i");                 /* Card type */
+MODULE_PARM(deskpro_xl, "i");           /* Special magic for Deskpro XL boxen */
+MODULE_PARM(deskpro_m, "i");            /* Special magic for Deskpro M box */
+MODULE_PARM(soundpro, "i");             /* More special magic for SoundPro chips */
+
+#ifdef __ISAPNP__
+MODULE_PARM(isapnp,	"i");
+MODULE_PARM(isapnpjump,	"i");
+MODULE_PARM(reverse,	"i");
+MODULE_PARM_DESC(isapnp,	"When set to 0, Plug & Play support will be disabled");
+MODULE_PARM_DESC(isapnpjump,	"Jumps to a specific slot in the driver's PnP table. Use the source, Luke.");
+MODULE_PARM_DESC(reverse,	"When set to 1, will reverse ISAPnP search order");
+
+struct pci_dev	*ad1848_dev  = NULL;
+
+/* Please add new entries at the end of the table */
+static struct {
+	char *name;
+	unsigned short	card_vendor, card_device,
+			vendor, function;
+	short mss_io, irq, dma, dma2;   /* index into isapnp table */
+        int type;
+} ad1848_isapnp_list[] __initdata = {
+	{"CMI 8330 SoundPRO",
+		ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
+		0, 0, 0,-1, 0},
+        {"CS4232 based card",
+                ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000),
+		0, 0, 0, 1, 0},
+        {"CS4232 based card",
+                ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100),
+		0, 0, 0, 1, 0},
+        {"OPL3-SA2 WSS mode",
+        	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021),
+                1, 0, 0, 1, 1},
+	{"Advanced Gravis InterWave Audio",
+		ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
+		ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000),
+		0, 0, 0, 1, 0},
+	{0}
+};
+
+static struct isapnp_device_id id_table[] __devinitdata = {
+	{	ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
+        {       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 },
+        {       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 },
+        {       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 },
+	{	ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
+		ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev)
+{
+	int err;
+
+	/* Device already active? Let's use it */
+	if(dev->active)
+		return(dev);
+
+	if((err = dev->activate(dev)) < 0) {
+		printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err);
+
+		dev->deactivate(dev);
+
+		return(NULL);
+	}
+	return(dev);
+}
+
+static struct pci_dev *ad1848_init_generic(struct pci_bus *bus, struct address_info *hw_config, int slot)
+{
+
+	/* Configure Audio device */
+	if((ad1848_dev = isapnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL)))
+	{
+		int ret;
+		ret = ad1848_dev->prepare(ad1848_dev);
+		/* If device is active, assume configured with /proc/isapnp
+		 * and use anyway. Some other way to check this? */
+		if(ret && ret != -EBUSY) {
+			printk(KERN_ERR "ad1848: ISAPnP found device that could not be autoconfigured.\n");
+			return(NULL);
+		}
+		if(ret == -EBUSY)
+			audio_activated = 1;
+
+		if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev)))
+		{
+			hw_config->io_base 	= ad1848_dev->resource[ad1848_isapnp_list[slot].mss_io].start;
+			hw_config->irq 		= ad1848_dev->irq_resource[ad1848_isapnp_list[slot].irq].start;
+			hw_config->dma 		= ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma].start;
+			if(ad1848_isapnp_list[slot].dma2 != -1)
+				hw_config->dma2 = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma2].start;
+			else
+				hw_config->dma2 = -1;
+                        hw_config->card_subtype = ad1848_isapnp_list[slot].type;
+		} else
+			return(NULL);
+	} else
+		return(NULL);
+
+	return(ad1848_dev);
+}
+
+static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pci_bus *bus, int slot)
+{
+	char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name;
+
+	printk(KERN_INFO "ad1848: %s detected\n", busname);
+
+	/* Initialize this baby. */
+
+	if(ad1848_init_generic(bus, hw_config, slot)) {
+		/* We got it. */
+
+		printk(KERN_NOTICE "ad1848: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
+		       busname,
+		       hw_config->io_base, hw_config->irq, hw_config->dma,
+		       hw_config->dma2);
+		return 1;
+	}
+	else
+		printk(KERN_INFO "ad1848: Failed to initialize %s\n", busname);
+
+	return 0;
+}
+
+static int __init ad1848_isapnp_probe(struct address_info *hw_config)
+{
+	static int first = 1;
+	int i;
+
+	/* Count entries in sb_isapnp_list */
+	for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++);
+	i--;
+
+	/* Check and adjust isapnpjump */
+	if( isapnpjump < 0 || isapnpjump > i) {
+		isapnpjump = reverse ? i : 0;
+		printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump);
+	}
+
+	if(!first || !reverse)
+		i = isapnpjump;
+	first = 0;
+	while(ad1848_isapnp_list[i].card_vendor != 0) {
+		static struct pci_bus *bus = NULL;
+
+		while ((bus = isapnp_find_card(
+				ad1848_isapnp_list[i].card_vendor,
+				ad1848_isapnp_list[i].card_device,
+				bus))) {
+
+			if(ad1848_isapnp_init(hw_config, bus, i)) {
+				isapnpjump = i; /* start next search from here */
+				return 0;
+			}
+		}
+		i += reverse ? -1 : 1;
+	}
+
+	return -ENODEV;
+}
+#endif
+
+
+static int __init init_ad1848(void)
+{
+	printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+#ifdef __ISAPNP__
+	if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) {
+		printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n");
+		isapnp = 0;
+	}
+#endif
+
+	if(io != -1) {
+	        if( isapnp == 0 )
+	        {
+			if(irq == -1 || dma == -1) {
+				printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n");
+				return -EINVAL;
+			}
+
+			cfg.irq = irq;
+			cfg.io_base = io;
+			cfg.dma = dma;
+			cfg.dma2 = dma2;
+			cfg.card_subtype = type;
+	        }
+
+		if(!probe_ms_sound(&cfg))
+			return -ENODEV;
+		attach_ms_sound(&cfg, THIS_MODULE);
+		loaded = 1;
+	}
+	return 0;
+}
+
+static void __exit cleanup_ad1848(void)
+{
+	if(loaded)
+		unload_ms_sound(&cfg);
+
+#ifdef __ISAPNP__
+	if(audio_activated)
+		if(ad1848_dev)
+			ad1848_dev->deactivate(ad1848_dev);
+#endif
+}
+
+module_init(init_ad1848);
+module_exit(cleanup_ad1848);
+
+#ifndef MODULE
+static int __init setup_ad1848(char *str)
+{
+        /* io, irq, dma, dma2, type */
+	int ints[6];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	type	= ints[5];
+
+	return 1;
+}
+
+__setup("ad1848=", setup_ad1848);	
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/ad1848.h linux-2.4.19-pre5-mjc/sound/oss/ad1848.h
--- linux/sound/oss/ad1848.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ad1848.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,28 @@
+/*
+ * ad1848.c
+ *
+ * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
+ */
+
+#define AD_F_CS4231     0x0001  /* Returned if a CS4232 (or compatible) detected */
+#define AD_F_CS4248     0x0001  /* Returned if a CS4248 (or compatible) detected */
+
+#define         AD1848_SET_XTAL         1
+#define         AD1848_MIXER_REROUTE    2
+
+#define AD1848_REROUTE(oldctl, newctl) \
+                ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
+		
+
+int ad1848_init(char *name, int io_base, int irq, int dma_playback,
+	int dma_capture, int share_dma, int *osp, struct module *owner);
+void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
+
+int ad1848_detect (int io_base, int *flags, int *osp);
+int ad1848_control(int cmd, int arg);
+
+void adintr(int irq, void *dev_id, struct pt_regs * dummy);
+void attach_ms_sound(struct address_info * hw_config, struct module * owner);
+
+int probe_ms_sound(struct address_info *hw_config);
+void unload_ms_sound(struct address_info *hw_info);
diff -Nru linux/sound/oss/ad1848_mixer.h linux-2.4.19-pre5-mjc/sound/oss/ad1848_mixer.h
--- linux/sound/oss/ad1848_mixer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ad1848_mixer.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,253 @@
+/*
+ * sound/ad1848_mixer.h
+ *
+ * Definitions for the mixer of AD1848 and compatible codecs.
+ */
+
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+/*
+ * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
+ * Sound card manufacturers have connected actual inputs (CD, synth, line,
+ * etc) to these inputs in different order. Therefore it's difficult
+ * to assign mixer channels to these inputs correctly. The following
+ * contains two alternative mappings. The first one is for GUS MAX and
+ * the second is just a generic one (line1, line2 and line3).
+ * (Actually this is not a mapping but rather some kind of interleaving
+ * solution).
+ */
+#define MODE1_REC_DEVICES		(SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
+					 SOUND_MASK_LINE1 | SOUND_MASK_IMIX)
+
+#define SPRO_REC_DEVICES		(SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_LINE1)
+
+#define MODE1_MIXER_DEVICES		(SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
+					 SOUND_MASK_LINE2 | \
+					 SOUND_MASK_IGAIN | \
+					 SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#define MODE2_MIXER_DEVICES		(SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
+					 SOUND_MASK_MIC | \
+					 SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
+					 SOUND_MASK_IGAIN | \
+					 SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#define MODE3_MIXER_DEVICES		(MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME)
+
+/* OPTi 82C930 has no IMIX level control, but it can still be selected as an
+ * input
+ */
+#define C930_MIXER_DEVICES	(SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
+				 SOUND_MASK_MIC | SOUND_MASK_VOLUME | \
+				 SOUND_MASK_LINE3 | \
+				 SOUND_MASK_IGAIN | SOUND_MASK_PCM)
+
+#define SPRO_MIXER_DEVICES	(SOUND_MASK_VOLUME | SOUND_MASK_PCM | \
+				 SOUND_MASK_LINE | SOUND_MASK_SYNTH | \
+				 SOUND_MASK_CD | SOUND_MASK_MIC | \
+				 SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \
+				 SOUND_MASK_OGAIN)
+
+struct mixer_def {
+	unsigned int regno:6;		/* register number for volume */
+	unsigned int polarity:1;	/* volume polarity: 0=normal, 1=reversed */
+	unsigned int bitpos:3;		/* position of bits in register for volume */
+	unsigned int nbits:3;		/* number of bits in register for volume */
+	unsigned int mutereg:6;		/* register number for mute bit */
+	unsigned int mutepol:1;		/* mute polarity: 0=normal, 1=reversed */
+	unsigned int mutepos:4;		/* position of mute bit in register */
+	unsigned int recreg:6;		/* register number for recording bit */
+	unsigned int recpol:1;		/* recording polarity: 0=normal, 1=reversed */
+	unsigned int recpos:4;		/* position of recording bit in register */
+};
+
+static char mix_cvt[101] = {
+	 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+	43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
+	65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
+	80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
+	91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
+	100
+};
+
+typedef struct mixer_def mixer_ent;
+typedef mixer_ent mixer_ents[2];
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual sound cards is not fixed. Some
+ * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
+ * The current version doesn't try to compensate this.
+ */
+
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit)	\
+	[name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8},			\
+		  {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}}
+
+#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
+		    rec_reg_l, rec_pola_l, rec_pos_l,					 \
+		 reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r,	 \
+		    rec_reg_r, rec_pola_r, rec_pos_r)					 \
+	[name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l,	 \
+		    rec_reg_l, rec_pola_l, rec_pos_l},					 \
+		  {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r,	 \
+		    rec_reg_r, rec_pola_r, rec_pos_r}}
+
+static mixer_ents ad1848_mix_devices[32] = {
+	MIX_ENT(SOUND_MIXER_VOLUME,	27, 1, 0, 4,	29, 1, 0, 4,  8),
+	MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6,  7),
+	MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1,  8),
+	MIX_ENT(SOUND_MIXER_CD,		 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_IMIX,	13, 1, 2, 6,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+	MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE1,	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5,  7)
+};
+
+static mixer_ents iwave_mix_devices[32] = {
+	MIX_ENT(SOUND_MIXER_VOLUME,	25, 1, 0, 5,	27, 1, 0, 5,  8),
+	MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6,  7),
+	MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1,  8),
+	MIX_ENT(SOUND_MIXER_CD,		 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_IMIX,	16, 1, 0, 5,	17, 1, 0, 5,  8),
+	MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+	MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE1,	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5,  7)
+};
+
+static mixer_ents cs42xb_mix_devices[32] = {
+	/* Digital master volume actually has seven bits, but we only use
+	   six to avoid the discontinuity when the analog gain kicks in. */
+	MIX_ENT(SOUND_MIXER_VOLUME,	46, 1, 0, 6,	47, 1, 0, 6,  7),
+	MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6,  7),
+	MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_MIC,	34, 1, 0, 5,	35, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_CD,		 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+	/* For the IMIX entry, it was not possible to use the MIX_ENT macro
+	   because the mute bit is in different positions for the two
+	   channels and requires reverse polarity. */
+	[SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},
+		      {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},
+	MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+	MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE1,	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE3,	38, 1, 0, 6,	39, 1, 0, 6,  7)
+};
+
+/* OPTi 82C930 has somewhat different port addresses.
+ * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1
+ * VOLUME, SYNTH, LINE, CD are not enabled above.
+ * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
+ */
+static mixer_ents c930_mix_devices[32] = {
+	MIX_ENT(SOUND_MIXER_VOLUME,	22, 1, 1, 5,	23, 1, 1, 5,  7),
+	MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 1, 4,	 5, 1, 1, 4,  7),
+	MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 5,	 7, 1, 0, 5,  7),
+	MIX_ENT(SOUND_MIXER_SPEAKER,	22, 1, 1, 5,	23, 1, 1, 5,  7),
+	MIX_ENT(SOUND_MIXER_LINE,	18, 1, 1, 4,	19, 1, 1, 4,  7),
+	MIX_ENT(SOUND_MIXER_MIC,	20, 1, 1, 4,	21, 1, 1, 4,  7),
+	MIX_ENT(SOUND_MIXER_CD,		 2, 1, 1, 4,	 3, 1, 1, 4,  7),
+	MIX_ENT(SOUND_MIXER_IMIX,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+	MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+	MIX_ENT(SOUND_MIXER_LINE1,	 2, 1, 1, 4,	 3, 1, 1, 4,  7),
+	MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 1, 4,	 5, 1, 1, 4,  7),
+	MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 1, 4,	19, 1, 1, 4,  7)
+};
+
+static mixer_ents spro_mix_devices[32] = {
+	MIX_ENT (SOUND_MIXER_VOLUME,	19, 0, 4, 4,			 19, 0, 0, 4,  8),
+	MIX_ENT (SOUND_MIXER_BASS,	 0, 0, 0, 0,			  0, 0, 0, 0,  8),
+	MIX_ENT (SOUND_MIXER_TREBLE,	 0, 0, 0, 0,			  0, 0, 0, 0,  8),
+	MIX_ENT2(SOUND_MIXER_SYNTH,	 4, 1, 1, 4, 23, 0, 3,  0, 0, 8,
+		 			 5, 1, 1, 4, 23, 0, 3, 0, 0, 8),
+	MIX_ENT (SOUND_MIXER_PCM,	 6, 1, 1, 4,			  7, 1, 1, 4,  8),
+	MIX_ENT (SOUND_MIXER_SPEAKER,	18, 0, 3, 2,			  0, 0, 0, 0,  8),
+	MIX_ENT2(SOUND_MIXER_LINE,	20, 0, 4, 4, 17, 1, 4, 16, 0, 2,
+		 			20, 0, 0, 4, 17, 1, 3, 16, 0, 1),
+	MIX_ENT2(SOUND_MIXER_MIC,	18, 0, 0, 3, 17, 1, 0, 16, 0, 0,
+		 			 0, 0, 0, 0,  0, 0, 0,  0, 0, 0),
+	MIX_ENT2(SOUND_MIXER_CD,	21, 0, 4, 4, 17, 1, 2, 16, 0, 4,
+					21, 0, 0, 4, 17, 1, 1, 16, 0, 3),
+	MIX_ENT (SOUND_MIXER_IMIX,	 0, 0, 0, 0,			  0, 0, 0, 0,  8),
+	MIX_ENT (SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,			  0, 0, 0, 0,  8),
+	MIX_ENT (SOUND_MIXER_RECLEV,	 0, 0, 0, 0,			  0, 0, 0, 0,  8),
+	MIX_ENT (SOUND_MIXER_IGAIN,	 0, 0, 0, 0,			  0, 0, 0, 0,  8),
+	MIX_ENT (SOUND_MIXER_OGAIN,	17, 1, 6, 1,			  0, 0, 0, 0,  8),
+	/* This is external wavetable */
+	MIX_ENT2(SOUND_MIXER_LINE1,	22, 0, 4, 4, 23, 1, 1, 23, 0, 4,
+		 			22, 0, 0, 4, 23, 1, 0, 23, 0, 5),
+};
+
+static int default_mixer_levels[32] =
+{
+	0x3232,			/* Master Volume */
+	0x3232,			/* Bass */
+	0x3232,			/* Treble */
+	0x4b4b,			/* FM */
+	0x3232,			/* PCM */
+	0x1515,			/* PC Speaker */
+	0x2020,			/* Ext Line */
+	0x1010,			/* Mic */
+	0x4b4b,			/* CD */
+	0x0000,			/* Recording monitor */
+	0x4b4b,			/* Second PCM */
+	0x4b4b,			/* Recording level */
+	0x4b4b,			/* Input gain */
+	0x4b4b,			/* Output gain */
+	0x2020,			/* Line1 */
+	0x2020,			/* Line2 */
+	0x1515			/* Line3 (usually line in)*/
+};
+
+#define LEFT_CHN	0
+#define RIGHT_CHN	1
+
+/*
+ * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1)
+ */
+
+#ifndef AUDIO_SPEAKER
+#define	AUDIO_SPEAKER		0x01	/* Enable mono output */
+#define	AUDIO_HEADPHONE		0x02	/* Sparc only */
+#define	AUDIO_LINE_OUT		0x04	/* Sparc only */
+#endif
diff -Nru linux/sound/oss/adlib_card.c linux-2.4.19-pre5-mjc/sound/oss/adlib_card.c
--- linux/sound/oss/adlib_card.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/adlib_card.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,73 @@
+/*
+ * sound/adlib_card.c
+ *
+ * Detection routine for the AdLib card.
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "sound_config.h"
+
+#include "opl3.h"
+
+static void __init attach_adlib_card(struct address_info *hw_config)
+{
+	hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp, THIS_MODULE);
+}
+
+static int __init probe_adlib(struct address_info *hw_config)
+{
+	return opl3_detect(hw_config->io_base, hw_config->osp);
+}
+
+static struct address_info cfg;
+
+static int __initdata io = -1;
+
+MODULE_PARM(io, "i");
+
+static int __init init_adlib(void)
+{
+	cfg.io_base = io;
+
+	if (cfg.io_base == -1) {
+		printk(KERN_ERR "adlib: must specify I/O address.\n");
+		return -EINVAL;
+	}
+	if (probe_adlib(&cfg) == 0)
+		return -ENODEV;
+	attach_adlib_card(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_adlib(void)
+{
+	sound_unload_synthdev(cfg.slots[0]);
+	
+}
+
+module_init(init_adlib);
+module_exit(cleanup_adlib);
+
+#ifndef MODULE
+static int __init setup_adlib(char *str)
+{
+        /* io */
+	int ints[2];
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+
+	return 1;
+}
+__setup("adlib=", setup_adlib);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/aedsp16.c linux-2.4.19-pre5-mjc/sound/oss/aedsp16.c
--- linux/sound/oss/aedsp16.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/aedsp16.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1381 @@
+/*
+   drivers/sound/lowlevel/aedsp16.c
+
+   Audio Excel DSP 16 software configuration routines
+   Copyright (C) 1995,1996,1997,1998  Riccardo Facchetti (fizban@tin.it)
+
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+ * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
+ * headers needed by this source.
+ */
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include "sound_config.h"
+
+/*
+ * Sanity checks
+ */
+
+#if defined(CONFIG_SOUND_ALSA_AEDSP16_SBPRO) && defined(CONFIG_SOUND_ALSA_AEDSP16_MSS)
+#error You have to enable only one of the MSS and SBPRO emulations.
+#endif
+
+/*
+
+   READ THIS
+
+   This module started to configure the Audio Excel DSP 16 Sound Card.
+   Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
+
+   NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
+   audio card and want to see the kernel support for it, please contact me.
+
+   Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
+   compatible card.
+   It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
+   so before this module, the only way to configure the DSP under linux was
+   boot the MS-DOS loading the sound.sys device driver (this driver soft-
+   configure the sound board hardware by massaging someone of its registers),
+   and then ctrl-alt-del to boot linux with the DSP configured by the DOS
+   driver.
+
+   This module works configuring your Audio Excel DSP 16's irq, dma and
+   mpu-401-irq. The OSS Lite routines rely on the fact that if the
+   hardware is there, they can detect it. The problem with AEDSP16 is
+   that no hardware can be found by the probe routines if the sound card
+   is not configured properly. Sometimes the kernel probe routines can find
+   an SBPRO even when the card is not configured (this is the standard setup
+   of the card), but the SBPRO emulation don't work well if the card is not
+   properly initialized. For this reason
+
+   aedsp16_init_board()
+
+   routine is called before the OSS Lite probe routines try to detect the
+   hardware.
+
+   NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
+
+   NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
+   have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
+   have to be configured by software.
+
+   NOTE: The driver is merged with the new OSS Lite sound driver. It works
+   as a lowlevel driver.
+
+   The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
+   the OSS Lite sound driver can be configured for SBPRO and MSS cards
+   at the same time, but the aedsp16 can't be two cards!!
+   When we configure it, we have to choose the SBPRO or the MSS emulation
+   for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
+
+   NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
+   please let me know if it works.
+
+   The MPU-401 support can be compiled in together with one of the other
+   two operating modes.
+
+   NOTE: This is something like plug-and-play: we have only to plug
+   the AEDSP16 board in the socket, and then configure and compile
+   a kernel that uses the AEDSP16 software configuration capability.
+   No jumper setting is needed!
+
+   For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
+   you have just to make config the OSS Lite package, configuring
+   the AEDSP16 sound card, then activating the SBPro emulation mode
+   and at last configuring IRQ and DMA.
+   Compile the kernel and run it.
+
+   NOTE: This means for SC-6000 cards that you can choose irq and dma,
+   but not the I/O addresses. To change I/O addresses you have to set
+   them with jumpers. For SC-6600 cards you have no jumpers so you have
+   to set up your full card configuration in the make config.
+
+   You can change the irq/dma/mirq settings WITHOUT THE NEED to open
+   your computer and massage the jumpers (there are no irq/dma/mirq
+   jumpers to be configured anyway, only I/O BASE values have to be
+   configured with jumpers)
+
+   For some ununderstandable reason, the card default of irq 7, dma 1,
+   don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
+   HDD work, the kernel start to erupt out a lot of messages like:
+
+   'Sound: DMA timed out - IRQ/DRQ config error?'
+
+   For what I can say, I have NOT any conflict at irq 7 (under linux I'm
+   using the lp polling driver), and dma line 1 is unused as stated by
+   /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
+   I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
+   Anyway a setting of irq 10, dma 3 works really fine.
+
+   NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
+   the emulation mode, all the installed hardware and the hardware
+   configuration (irq and dma settings of all the hardware).
+
+   This init module should work with SBPRO+MSS, when one of the two is
+   the AEDSP16 emulation and the other the real card. (see [1])
+   For example:
+
+   AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
+   AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
+
+   MPU401 should work. (see [2])
+
+   [1]
+       ---
+       Date: Mon, 29 Jul 1997 08:35:40 +0100
+       From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+
+       [...]
+       Just to let you know got my Audio Excel (emulating a MSS) working
+       with my original SB16, thanks for the driver!
+       [...]
+       ---
+
+   [2] Not tested by me for lack of hardware.
+
+   TODO, WISHES AND TECH
+
+   - About I/O ports allocation -
+
+   Request the 2x0h region (port base) in any case if we are using this card.
+
+   NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
+   port base region (see code) does not mean necessarily that we are emulating
+   sbpro.  Even if this region is the sbpro I/O ports region, we use this
+   region to access the control registers of the card, and if emulating
+   sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
+   registers are not used, in no way, to emulate an sbpro: they are
+   used only for configuration purposes.
+
+   Started Fri Mar 17 16:13:18 MET 1995
+
+   v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
+   - Initial code.
+   v0.2 (ALPHA)
+   - Cleanups.
+   - Integrated with Linux voxware v 2.90-2 kernel sound driver.
+   - SoundBlaster Pro mode configuration.
+   - Microsoft Sound System mode configuration.
+   - MPU-401 mode configuration.
+   v0.3 (ALPHA)
+   - Cleanups.
+   - Rearranged the code to let aedsp16_init_board be more general.
+   - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
+   inclusion too. We rely on os.h
+   - Used the  to get a variable
+   len string (we are not sure about the len of Copyright string).
+   This works with any SB and compatible.
+   - Added the code to request_region at device init (should go in
+   the main body of voxware).
+   v0.4 (BETA)
+   - Better configure.c patch for aedsp16 configuration (better
+   logic of inclusion of AEDSP16 support)
+   - Modified the conditional compilation to better support more than
+   one sound card of the emulated type (read the NOTES above)
+   - Moved the sb init routine from the attach to the very first
+   probe in sb_card.c
+   - Rearrangements and cleanups
+   - Wiped out some unnecessary code and variables: this is kernel
+   code so it is better save some TEXT and DATA
+   - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
+   I/O ports in any case because they are used to access the DSP
+   configuration registers and we can not allow anyone to get them.
+   v0.5
+   - cleanups on comments
+   - prep for diffs against v3.0-proto-950402
+   v0.6
+   - removed the request_region()s when compiling the MODULE sound.o
+   because we are not allowed (by the actual voxware structure) to
+   release_region()
+   v0.7 (pre ALPHA, not distributed)
+   - started porting this module to kernel 1.3.84. Dummy probe/attach
+   routines.
+   v0.8 (ALPHA)
+   - attached all the init routines.
+   v0.9 (BETA)
+   - Integrated with linux-pre2.0.7
+   - Integrated with configuration scripts.
+   - Cleaned up and beautyfied the code.
+   v0.9.9 (BETA)
+   - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
+     Now only the code configured is compiled in, with some memory saving.
+   v0.9.10
+   - Integration into the sound/lowlevel/ section of the sound driver.
+   - Re-organized the code.
+   v0.9.11 (not distributed)
+   - Rewritten the init interface-routines to initialize the AEDSP16 in
+     one shot.
+   - More cosmetics.
+   - SC-6600 support.
+   - More soft/hard configuration.
+   v0.9.12
+   - Refined the v0.9.11 code with conditional compilation to distinguish
+     between SC-6000 and SC-6600 code.
+   v1.0.0
+   - Prep for merging with OSS Lite and Linux kernel 2.1.13
+   - Corrected a bug in request/check/release region calls (thanks to the
+     new kernel exception handling).
+   v1.1
+   - Revamped for integration with new modularized sound drivers: to enhance
+     the flexibility of modular version, I have removed all the conditional
+     compilation for SBPRO, MPU and MSS code. Now it is all managed with
+     the ae_config structure.
+   v1.2
+   - Module informations added.
+   - Removed aedsp16_delay_10msec(), now using mdelay(10)
+   - All data and funcs moved to .*.init section.
+   v1.3
+   Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/27
+   - got rid of check_region
+
+   Known Problems:
+   - Audio Excel DSP 16 III don't work with this driver.
+
+   Credits:
+   Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
+   lot in testing the 0.9.11 and 0.9.12 versions of this driver.
+
+ */
+
+
+#define VERSION "1.3"		/* Version of Audio Excel DSP 16 driver */
+
+#undef	AEDSP16_DEBUG 		/* Define this to 1 to enable debug code     */
+#undef	AEDSP16_DEBUG_MORE 	/* Define this to 1 to enable more debug     */
+#undef	AEDSP16_INFO 		/* Define this to 1 to enable info code      */
+
+#if defined(AEDSP16_DEBUG)
+# define DBG(x)  printk x
+# if defined(AEDSP16_DEBUG_MORE)
+#  define DBG1(x) printk x
+# else
+#  define DBG1(x)
+# endif
+#else
+# define DBG(x)
+# define DBG1(x)
+#endif
+
+/*
+ * Misc definitions
+ */
+#define TRUE	1
+#define FALSE	0
+
+/*
+ * Region Size for request/check/release region.
+ */
+#define IOBASE_REGION_SIZE	0x10
+
+/*
+ * Hardware related defaults
+ */
+#define DEF_AEDSP16_IOB 0x220   /* 0x220(default) 0x240                 */
+#define DEF_AEDSP16_IRQ 7	/* 5 7(default) 9 10 11                 */
+#define DEF_AEDSP16_MRQ 0	/* 5 7 9 10 0(default), 0 means disable */
+#define DEF_AEDSP16_DMA 1	/* 0 1(default) 3                       */
+
+/*
+ * Commands of AEDSP16's DSP (SBPRO+special).
+ * Some of them are COMMAND_xx, in the future they may change.
+ */
+#define WRITE_MDIRQ_CFG   0x50	/* Set M&I&DRQ mask (the real config)   */
+#define COMMAND_52        0x52	/*                                      */
+#define READ_HARD_CFG     0x58	/* Read Hardware Config (I/O base etc)  */
+#define COMMAND_5C        0x5c	/*                                      */
+#define COMMAND_60        0x60	/*                                      */
+#define COMMAND_66        0x66	/*                                      */
+#define COMMAND_6C        0x6c	/*                                      */
+#define COMMAND_6E        0x6e	/*                                      */
+#define COMMAND_88        0x88	/*                                      */
+#define DSP_INIT_MSS      0x8c	/* Enable Microsoft Sound System mode   */
+#define COMMAND_C5        0xc5	/*                                      */
+#define GET_DSP_VERSION   0xe1	/* Get DSP Version                      */
+#define GET_DSP_COPYRIGHT 0xe3	/* Get DSP Copyright                    */
+
+/*
+ * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
+ * to have the actual I/O port.
+ * Register permissions are:
+ * (wo) == Write Only
+ * (ro) == Read  Only
+ * (w-) == Write
+ * (r-) == Read
+ */
+#define DSP_RESET    0x06	/* offset of DSP RESET             (wo) */
+#define DSP_READ     0x0a	/* offset of DSP READ              (ro) */
+#define DSP_WRITE    0x0c	/* offset of DSP WRITE             (w-) */
+#define DSP_COMMAND  0x0c	/* offset of DSP COMMAND           (w-) */
+#define DSP_STATUS   0x0c	/* offset of DSP STATUS            (r-) */
+#define DSP_DATAVAIL 0x0e	/* offset of DSP DATA AVAILABLE    (ro) */
+
+
+#define RETRY           10	/* Various retry values on I/O opera-   */
+#define STATUSRETRY   1000	/* tions. Sometimes we have to          */
+#define HARDRETRY   500000	/* wait for previous cmd to complete    */
+
+/*
+ * Size of character arrays that store name and version of sound card
+ */
+#define CARDNAMELEN 15		/* Size of the card's name in chars     */
+#define CARDVERLEN  2		/* Size of the card's version in chars  */
+
+#if defined(CONFIG_SC6600)
+/*
+ * Bitmapped flags of hard configuration
+ */
+/*
+ * Decode macros (xl == low byte, xh = high byte)
+ */
+#define IOBASE(xl)		((xl & 0x01)?0x240:0x220)
+#define JOY(xl)  		(xl & 0x02)
+#define MPUADDR(xl)		( 			\
+				(xl & 0x0C)?0x330:	\
+				(xl & 0x08)?0x320:	\
+				(xl & 0x04)?0x310:	\
+						0x300)
+#define WSSADDR(xl)		((xl & 0x10)?0xE80:0x530)
+#define CDROM(xh)		(xh & 0x20)
+#define CDROMADDR(xh)		(((xh & 0x1F) << 4) + 0x200)
+/*
+ * Encode macros
+ */
+#define BLDIOBASE(xl, val) {		\
+	xl &= ~0x01; 			\
+	if (val == 0x240)		\
+		xl |= 0x01;		\
+	}
+#define BLDJOY(xl, val) {		\
+	xl &= ~0x02; 			\
+	if (val == 1)			\
+		xl |= 0x02;		\
+	}
+#define BLDMPUADDR(xl, val) {		\
+	xl &= ~0x0C;			\
+	switch (val) {			\
+		case 0x330:		\
+			xl |= 0x0C;	\
+			break;		\
+		case 0x320:		\
+			xl |= 0x08;	\
+			break;		\
+		case 0x310:		\
+			xl |= 0x04;	\
+			break;		\
+		case 0x300:		\
+			xl |= 0x00;	\
+			break;		\
+		default:		\
+			xl |= 0x00;	\
+			break;		\
+		}			\
+	}
+#define BLDWSSADDR(xl, val) {		\
+	xl &= ~0x10; 			\
+	if (val == 0xE80)		\
+		xl |= 0x10;		\
+	}
+#define BLDCDROM(xh, val) {		\
+	xh &= ~0x20; 			\
+	if (val == 1)			\
+		xh |= 0x20;		\
+	}
+#define BLDCDROMADDR(xh, val) {		\
+	int tmp = val;			\
+	tmp -= 0x200;			\
+	tmp >>= 4;			\
+	tmp &= 0x1F;			\
+	xh |= tmp;			\
+	xh &= 0x7F;			\
+	xh |= 0x40;			\
+	}
+#endif /* CONFIG_SC6600 */
+
+/*
+ * Bit mapped flags for calling aedsp16_init_board(), and saving the current
+ * emulation mode.
+ */
+#define INIT_NONE   (0   )
+#define INIT_SBPRO  (1<<0)
+#define INIT_MSS    (1<<1)
+#define INIT_MPU401 (1<<2)
+
+static int      soft_cfg __initdata = 0;	/* bitmapped config */
+static int      soft_cfg_mss __initdata = 0;	/* bitmapped mss config */
+static int      ver[CARDVERLEN] __initdata = {0, 0};	/* DSP Ver:
+						   hi->ver[0] lo->ver[1] */
+
+#if defined(CONFIG_SC6600)
+static int	hard_cfg[2]     /* lo<-hard_cfg[0] hi<-hard_cfg[1]      */
+                     __initdata = { 0, 0};
+#endif /* CONFIG_SC6600 */
+
+#if defined(CONFIG_SC6600)
+/* Decoded hard configuration */
+struct	d_hcfg {
+	int iobase;
+	int joystick;
+	int mpubase;
+	int wssbase;
+	int cdrom;
+	int cdrombase;
+};
+
+struct d_hcfg decoded_hcfg __initdata = {0, };
+
+#endif /* CONFIG_SC6600 */
+
+/* orVals contain the values to be or'ed       				*/
+struct orVals {
+	int	val;		/* irq|mirq|dma                         */
+	int	or;		/* soft_cfg |= TheStruct.or             */
+};
+
+/* aedsp16_info contain the audio card configuration                  */
+struct aedsp16_info {
+	int base_io;            /* base I/O address for accessing card  */
+	int irq;                /* irq value for DSP I/O                */
+	int mpu_irq;            /* irq for mpu401 interface I/O         */
+	int dma;                /* dma value for DSP I/O                */
+	int mss_base;           /* base I/O for Microsoft Sound System  */
+	int mpu_base;           /* base I/O for MPU-401 emulation       */
+	int init;               /* Initialization status of the card    */
+};
+
+/*
+ * Magic values that the DSP will eat when configuring irq/mirq/dma
+ */
+/* DSP IRQ conversion array             */
+static struct orVals orIRQ[] __initdata = {
+	{0x05, 0x28},
+	{0x07, 0x08},
+	{0x09, 0x10},
+	{0x0a, 0x18},
+	{0x0b, 0x20},
+	{0x00, 0x00}
+};
+
+/* MPU-401 IRQ conversion array         */
+static struct orVals orMIRQ[] __initdata = {
+	{0x05, 0x04},
+	{0x07, 0x44},
+	{0x09, 0x84},
+	{0x0a, 0xc4},
+	{0x00, 0x00}
+};
+
+/* DMA Channels conversion array        */
+static struct orVals orDMA[] __initdata = {
+	{0x00, 0x01},
+	{0x01, 0x02},
+	{0x03, 0x03},
+	{0x00, 0x00}
+};
+
+static struct aedsp16_info ae_config __initdata = {
+	DEF_AEDSP16_IOB,
+	DEF_AEDSP16_IRQ,
+	DEF_AEDSP16_MRQ,
+	DEF_AEDSP16_DMA,
+	-1,
+	-1,
+	INIT_NONE
+};
+
+/*
+ * Buffers to store audio card informations
+ */
+static char     DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
+static char     DSPVersion[CARDVERLEN + 1] __initdata = {0, };
+
+static int __init aedsp16_wait_data(int port)
+{
+	int             loop = STATUSRETRY;
+	unsigned char   ret = 0;
+
+	DBG1(("aedsp16_wait_data (0x%x): ", port));
+
+	do {
+		  ret = inb(port + DSP_DATAVAIL);
+	/*
+	 * Wait for data available (bit 7 of ret == 1)
+	 */
+	  } while (!(ret & 0x80) && loop--);
+
+	if (ret & 0x80) {
+		DBG1(("success.\n"));
+		return TRUE;
+	}
+
+	DBG1(("failure.\n"));
+	return FALSE;
+}
+
+static int __init aedsp16_read(int port)
+{
+	int inbyte;
+
+	DBG(("    Read DSP Byte (0x%x): ", port));
+
+	if (aedsp16_wait_data(port) == FALSE) {
+		DBG(("failure.\n"));
+		return -1;
+	}
+
+	inbyte = inb(port + DSP_READ);
+
+	DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
+
+	return inbyte;
+}
+
+static int __init aedsp16_test_dsp(int port)
+{
+	return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
+}
+
+static int __init aedsp16_dsp_reset(int port)
+{
+	/*
+	 * Reset DSP
+	 */
+
+	DBG(("Reset DSP:\n"));
+
+	outb(1, (port + DSP_RESET));
+	udelay(10);
+	outb(0, (port + DSP_RESET));
+	udelay(10);
+	udelay(10);
+	if (aedsp16_test_dsp(port) == TRUE) {
+		DBG(("success.\n"));
+		return TRUE;
+	} else
+		DBG(("failure.\n"));
+	return FALSE;
+}
+
+static int __init aedsp16_write(int port, int cmd)
+{
+	unsigned char   ret;
+	int             loop = HARDRETRY;
+
+	DBG(("    Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
+
+	do {
+		ret = inb(port + DSP_STATUS);
+		/*
+		 * DSP ready to receive data if bit 7 of ret == 0
+		 */
+		if (!(ret & 0x80)) {
+			outb(cmd, port + DSP_COMMAND);
+			DBG(("success.\n"));
+			return 0;
+		}
+	} while (loop--);
+
+	DBG(("timeout.\n"));
+	printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
+
+	return -1;
+}
+
+#if defined(CONFIG_SC6600)
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+void __init aedsp16_pinfo(void) {
+	DBG(("\n Base address:  %x\n", decoded_hcfg.iobase));
+	DBG((" Joystick    : %s present\n", decoded_hcfg.joystick?"":" not"));
+	DBG((" WSS addr    :  %x\n", decoded_hcfg.wssbase));
+	DBG((" MPU-401 addr:  %x\n", decoded_hcfg.mpubase));
+	DBG((" CDROM       : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
+	DBG((" CDROMADDR   :  %x\n\n", decoded_hcfg.cdrombase));
+}
+#endif
+
+void __init aedsp16_hard_decode(void) {
+
+	DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+/*
+ * Decode Cfg Bytes.
+ */
+	decoded_hcfg.iobase	= IOBASE(hard_cfg[0]);
+	decoded_hcfg.joystick	= JOY(hard_cfg[0]);
+	decoded_hcfg.wssbase	= WSSADDR(hard_cfg[0]);
+	decoded_hcfg.mpubase	= MPUADDR(hard_cfg[0]);
+	decoded_hcfg.cdrom	= CDROM(hard_cfg[1]);
+	decoded_hcfg.cdrombase	= CDROMADDR(hard_cfg[1]);
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+	printk(" Original sound card configuration:\n");
+	aedsp16_pinfo();
+#endif
+
+/*
+ * Now set up the real kernel configuration.
+ */
+	decoded_hcfg.iobase	= ae_config.base_io;
+	decoded_hcfg.wssbase	= ae_config.mss_base;
+	decoded_hcfg.mpubase	= ae_config.mpu_base;
+
+#if defined(CONFIG_SC6600_JOY)
+ 	decoded_hcfg.joystick	= CONFIG_SC6600_JOY; /* Enable */
+#endif
+#if defined(CONFIG_SC6600_CDROM)
+	decoded_hcfg.cdrom	= CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
+#endif
+#if defined(CONFIG_SC6600_CDROMBASE)
+	decoded_hcfg.cdrombase	= CONFIG_SC6600_CDROMBASE; /* 0 Disable */
+#endif
+
+#if defined(AEDSP16_DEBUG)
+	DBG((" New Values:\n"));
+	aedsp16_pinfo();
+#endif
+
+	DBG(("success.\n"));
+}
+
+void __init aedsp16_hard_encode(void) {
+
+	DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+	hard_cfg[0] = 0;
+	hard_cfg[1] = 0;
+
+	hard_cfg[0] |= 0x20;
+
+	BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
+	BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
+	BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
+	BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
+	BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
+	BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
+
+#if defined(AEDSP16_DEBUG)
+	aedsp16_pinfo();
+#endif
+
+	DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+	DBG(("success.\n"));
+
+}
+
+static int __init aedsp16_hard_write(int port) {
+
+	DBG(("aedsp16_hard_write:\n"));
+
+	if (aedsp16_write(port, COMMAND_6C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, COMMAND_5C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, hard_cfg[0])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, hard_cfg[1])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, COMMAND_C5)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int __init aedsp16_hard_read(int port) {
+
+	DBG(("aedsp16_hard_read:\n"));
+
+	if (aedsp16_write(port, READ_HARD_CFG)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+
+	if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+			READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+			READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_read(port) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+			READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int __init aedsp16_ext_cfg_write(int port) {
+
+	int extcfg, val;
+
+	if (aedsp16_write(port, COMMAND_66)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
+		return FALSE;
+	}
+
+	extcfg = 7;
+	if (decoded_hcfg.cdrom != 2)
+		extcfg = 0x0F;
+	if ((decoded_hcfg.cdrom == 4) ||
+	    (decoded_hcfg.cdrom == 3))
+		extcfg &= ~2;
+	if (decoded_hcfg.cdrombase == 0)
+		extcfg &= ~2;
+	if (decoded_hcfg.mpubase == 0)
+		extcfg &= ~1;
+
+	if (aedsp16_write(port, extcfg)) {
+		printk("[AEDSP16] Write extcfg: failed!\n");
+		return FALSE;
+	}
+	if (aedsp16_write(port, 0)) {
+		printk("[AEDSP16] Write extcfg: failed!\n");
+		return FALSE;
+	}
+	if (decoded_hcfg.cdrom == 3) {
+		if (aedsp16_write(port, COMMAND_52)) {
+			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+			return FALSE;
+		}
+		if ((val = aedsp16_read(port)) == -1) {
+			printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
+					, COMMAND_52);
+			return FALSE;
+		}
+		val &= 0x7F;
+		if (aedsp16_write(port, COMMAND_60)) {
+			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+			return FALSE;
+		}
+		if (aedsp16_write(port, val)) {
+			printk("[AEDSP16] Write val: failed!\n");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+#endif /* CONFIG_SC6600 */
+
+static int __init aedsp16_cfg_write(int port) {
+	if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+		return FALSE;
+	}
+	if (aedsp16_write(port, soft_cfg)) {
+		printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static int __init aedsp16_init_mss(int port)
+{
+	DBG(("aedsp16_init_mss:\n"));
+
+	mdelay(10);
+
+	if (aedsp16_write(port, DSP_INIT_MSS)) {
+		printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
+				DSP_INIT_MSS);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	
+	mdelay(10);
+
+	if (aedsp16_cfg_write(port) == FALSE)
+		return FALSE;
+
+	outb(soft_cfg_mss, ae_config.mss_base);
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int __init aedsp16_setup_board(int port) {
+	int	loop = RETRY;
+
+#if defined(CONFIG_SC6600)
+	int	val = 0;
+
+	if (aedsp16_hard_read(port) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_write(port, COMMAND_52)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+		return FALSE;
+	}
+
+	if ((val = aedsp16_read(port)) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+				COMMAND_52);
+		return FALSE;
+	}
+#endif
+
+	do {
+		if (aedsp16_write(port, COMMAND_88)) {
+			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
+			return FALSE;
+		}
+		mdelay(10);
+	} while ((aedsp16_wait_data(port) == FALSE) && loop--);
+
+	if (aedsp16_read(port) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+				COMMAND_88);
+		return FALSE;
+	}
+
+#if !defined(CONFIG_SC6600)
+	if (aedsp16_write(port, COMMAND_5C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+		return FALSE;
+	}
+#endif
+
+	if (aedsp16_cfg_write(port) == FALSE)
+		return FALSE;
+
+#if defined(CONFIG_SC6600)
+	if (aedsp16_write(port, COMMAND_60)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+		return FALSE;
+	}
+	if (aedsp16_write(port, val)) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", val);
+		return FALSE;
+	}
+	if (aedsp16_write(port, COMMAND_6E)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
+		return FALSE;
+	}
+	if (aedsp16_write(port, ver[0])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
+		return FALSE;
+	}
+	if (aedsp16_write(port, ver[1])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
+		return FALSE;
+	}
+
+	if (aedsp16_hard_write(port) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_write(port, COMMAND_5C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+		return FALSE;
+	}
+
+#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
+	if (aedsp16_cfg_write(port) == FALSE)
+		return FALSE;
+#endif
+
+#endif
+
+	return TRUE;
+}
+
+static int __init aedsp16_stdcfg(int port) {
+	if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+		return FALSE;
+	}
+	/*
+	 * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
+	 */
+	if (aedsp16_write(port, 0x0A)) {
+		printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static int __init aedsp16_dsp_version(int port)
+{
+	int             len = 0;
+	int             ret;
+
+	DBG(("Get DSP Version:\n"));
+
+	if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
+		DBG(("failed.\n"));
+		return FALSE;
+	}
+
+	do {
+		if ((ret = aedsp16_read(port)) == -1) {
+			DBG(("failed.\n"));
+			return FALSE;
+		}
+	/*
+	 * We already know how many int are stored (2), so we know when the
+	 * string is finished.
+	 */
+		ver[len++] = ret;
+	  } while (len < CARDVERLEN);
+	sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int __init aedsp16_dsp_copyright(int port)
+{
+	int             len = 0;
+	int             ret;
+
+	DBG(("Get DSP Copyright:\n"));
+
+	if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
+		DBG(("failed.\n"));
+		return FALSE;
+	}
+
+	do {
+		if ((ret = aedsp16_read(port)) == -1) {
+	/*
+	 * If no more data available, return to the caller, no error if len>0.
+	 * We have no other way to know when the string is finished.
+	 */
+			if (len)
+				break;
+			else {
+				DBG(("failed.\n"));
+				return FALSE;
+			}
+		}
+
+		DSPCopyright[len++] = ret;
+
+	  } while (len < CARDNAMELEN);
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static void __init aedsp16_init_tables(void)
+{
+	int i = 0;
+
+	memset(DSPCopyright, 0, CARDNAMELEN + 1);
+	memset(DSPVersion, 0, CARDVERLEN + 1);
+
+	for (i = 0; orIRQ[i].or; i++)
+		if (orIRQ[i].val == ae_config.irq) {
+			soft_cfg |= orIRQ[i].or;
+			soft_cfg_mss |= orIRQ[i].or;
+		}
+
+	for (i = 0; orMIRQ[i].or; i++)
+		if (orMIRQ[i].or == ae_config.mpu_irq)
+			soft_cfg |= orMIRQ[i].or;
+
+	for (i = 0; orDMA[i].or; i++)
+		if (orDMA[i].val == ae_config.dma) {
+			soft_cfg |= orDMA[i].or;
+			soft_cfg_mss |= orDMA[i].or;
+		}
+}
+
+static int __init aedsp16_init_board(void)
+{
+	aedsp16_init_tables();
+
+	if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
+		return FALSE;
+	}
+	if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
+		return FALSE;
+	}
+
+	/*
+	 * My AEDSP16 card return SC-6000 in DSPCopyright, so
+	 * if we have something different, we have to be warned.
+	 */
+	if (strcmp("SC-6000", DSPCopyright))
+		printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
+
+	if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+		return FALSE;
+	}
+
+#if defined(CONFIG_SC6600)
+	if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+		return FALSE;
+	}
+
+	aedsp16_hard_decode();
+
+	aedsp16_hard_encode();
+
+	if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
+		return FALSE;
+	}
+#endif /* CONFIG_SC6600 */
+
+	if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_setup_board: failed!\n");
+		return FALSE;
+	}
+
+	if (ae_config.mss_base != -1) {
+		if (ae_config.init & INIT_MSS) {
+			if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
+				printk("[AEDSP16] Can not initialize"
+				       "Microsoft Sound System mode.\n");
+				return FALSE;
+			}
+		}
+	}
+
+#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+
+	printk("Audio Excel DSP 16 init v%s (%s %s) [",
+		VERSION, DSPCopyright,
+		DSPVersion);
+
+	if (ae_config.mpu_base != -1) {
+		if (ae_config.init & INIT_MPU401) {
+			printk("MPU401");
+			if ((ae_config.init & INIT_MSS) ||
+			    (ae_config.init & INIT_SBPRO))
+				printk(" ");
+		}
+	}
+
+	if (ae_config.mss_base == -1) {
+		if (ae_config.init & INIT_SBPRO) {
+			printk("SBPro");
+			if (ae_config.init & INIT_MSS)
+				printk(" ");
+		}
+	}
+
+	if (ae_config.mss_base != -1)
+		if (ae_config.init & INIT_MSS)
+			printk("MSS");
+
+	printk("]\n");
+#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
+
+	mdelay(10);
+
+	return TRUE;
+}
+
+static int __init init_aedsp16_sb(void)
+{
+	DBG(("init_aedsp16_sb: "));
+
+/*
+ * If the card is already init'ed MSS, we can not init it to SBPRO too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+	if (ae_config.init & INIT_MSS)
+		return FALSE;
+	if (ae_config.init & INIT_SBPRO)
+		return FALSE;
+
+	ae_config.init |= INIT_SBPRO;
+
+	DBG(("done.\n"));
+
+	return TRUE;
+}
+
+static void __init uninit_aedsp16_sb(void)
+{
+	DBG(("uninit_aedsp16_sb: "));
+
+	ae_config.init &= ~INIT_SBPRO;
+
+	DBG(("done.\n"));
+}
+
+static int __init init_aedsp16_mss(void)
+{
+	DBG(("init_aedsp16_mss: "));
+
+/*
+ * If the card is already init'ed SBPRO, we can not init it to MSS too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+	if (ae_config.init & INIT_SBPRO)
+		return FALSE;
+	if (ae_config.init & INIT_MSS)
+		return FALSE;
+/*
+ * We must allocate the CONFIG_AEDSP16_BASE region too because these are the 
+ * I/O ports to access card's control registers.
+ */
+	if (!(ae_config.init & INIT_MPU401)) {
+		if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+				"aedsp16 (base)")) {
+			printk(
+			"AEDSP16 BASE I/O port region is already in use.\n");
+			return FALSE;
+		}
+	}
+
+	ae_config.init |= INIT_MSS;
+
+	DBG(("done.\n"));
+
+	return TRUE;
+}
+
+static void __init uninit_aedsp16_mss(void)
+{
+	DBG(("uninit_aedsp16_mss: "));
+
+	if ((!(ae_config.init & INIT_MPU401)) &&
+	   (ae_config.init & INIT_MSS)) {
+		release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+		DBG(("AEDSP16 base region released.\n"));
+	}
+
+	ae_config.init &= ~INIT_MSS;
+	DBG(("done.\n"));
+}
+
+static int __init init_aedsp16_mpu(void)
+{
+	DBG(("init_aedsp16_mpu: "));
+
+	if (ae_config.init & INIT_MPU401)
+		return FALSE;
+
+/*
+ * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O 
+ * ports to access card's control registers.
+ */
+	if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
+		if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+					"aedsp16 (base)")) {
+			printk(
+			"AEDSP16 BASE I/O port region is already in use.\n");
+			return FALSE;
+		}
+	}
+
+	ae_config.init |= INIT_MPU401;
+
+	DBG(("done.\n"));
+
+	return TRUE;
+}
+
+static void __init uninit_aedsp16_mpu(void)
+{
+	DBG(("uninit_aedsp16_mpu: "));
+
+	if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
+	   (ae_config.init & INIT_MPU401)) {
+		release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+		DBG(("AEDSP16 base region released.\n"));
+	}
+
+	ae_config.init &= ~INIT_MPU401;
+
+	DBG(("done.\n"));
+}
+
+int __init init_aedsp16(void)
+{
+	int initialized = FALSE;
+
+	DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
+	     ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
+
+	if (ae_config.mss_base == -1) {
+		if (init_aedsp16_sb() == FALSE) {
+			uninit_aedsp16_sb();
+		} else {
+			initialized = TRUE;
+		}
+	}
+
+	if (ae_config.mpu_base != -1) {
+		if (init_aedsp16_mpu() == FALSE) {
+			uninit_aedsp16_mpu();
+		} else {
+			initialized = TRUE;
+		}
+	}
+
+/*
+ * In the sequence of init routines, the MSS init MUST be the last!
+ * This because of the special register programming the MSS mode needs.
+ * A board reset would disable the MSS mode restoring the default SBPRO
+ * mode.
+ */
+	if (ae_config.mss_base != -1) {
+		if (init_aedsp16_mss() == FALSE) {
+			uninit_aedsp16_mss();
+		} else {
+			initialized = TRUE;
+		}
+	}
+
+	if (initialized)
+		initialized = aedsp16_init_board();
+	return initialized;
+}
+
+void __init uninit_aedsp16(void)
+{
+	if (ae_config.mss_base != -1)
+		uninit_aedsp16_mss();
+	else
+		uninit_aedsp16_sb();
+	if (ae_config.mpu_base != -1)
+		uninit_aedsp16_mpu();
+}
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata mpu_irq = -1;
+static int __initdata mss_base = -1;
+static int __initdata mpu_base = -1;
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "dma line (0 1 3)");
+MODULE_PARM(mpu_irq, "i");
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
+MODULE_PARM(mss_base, "i");
+MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
+MODULE_PARM(mpu_base, "i");
+MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
+MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
+MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
+MODULE_LICENSE("GPL");
+
+static int __init do_init_aedsp16(void) {
+	printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
+	if (io == -1 || dma == -1 || irq == -1) {
+		printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
+		return -EINVAL;
+	}
+
+	ae_config.base_io = io;
+	ae_config.irq = irq;
+	ae_config.dma = dma;
+
+	ae_config.mss_base = mss_base;
+	ae_config.mpu_base = mpu_base;
+	ae_config.mpu_irq = mpu_irq;
+
+	if (init_aedsp16() == FALSE) {
+		printk(KERN_ERR "aedsp16: initialization failed\n");
+		/*
+		 * XXX
+		 * What error should we return here ?
+		 */
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void __exit cleanup_aedsp16(void) {
+	uninit_aedsp16();
+}
+
+module_init(do_init_aedsp16);
+module_exit(cleanup_aedsp16);
+
+#ifndef MODULE
+static int __init setup_aedsp16(char *str)
+{
+	/* io, irq, dma, mss_io, mpu_io, mpu_irq */
+	int ints[7];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io	 = ints[1];
+	irq	 = ints[2];
+	dma	 = ints[3];
+	mss_base = ints[4];
+	mpu_base = ints[5];
+	mpu_irq	 = ints[6];
+	return 1;
+}
+
+__setup("aedsp16=", setup_aedsp16);
+#endif
diff -Nru linux/sound/oss/audio.c linux-2.4.19-pre5-mjc/sound/oss/audio.c
--- linux/sound/oss/audio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/audio.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,987 @@
+/*
+ * sound/audio.c
+ *
+ * Device file manager for /dev/audio
+ */
+
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Thomas Sailer   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
+ * Thomas Sailer   : use more logical O_NONBLOCK semantics
+ * Daniel Rodriksson: reworked the use of the device specific copy_user
+ *                    still generic
+ * Horst von Brand:  Add missing #include <linux/string.h>
+ * Chris Rankin    : Update the module-usage counter for the coprocessor,
+ *                   and decrement the counters again if we cannot open
+ *                   the audio device.
+ */
+
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/kmod.h>
+
+#include "sound_config.h"
+#include "ulaw.h"
+#include "coproc.h"
+
+#define NEUTRAL8	0x80
+#define NEUTRAL16	0x00
+
+
+int             dma_ioctl(int dev, unsigned int cmd, caddr_t arg);
+
+static int set_format(int dev, int fmt)
+{
+	if (fmt != AFMT_QUERY)
+	{
+		audio_devs[dev]->local_conversion = 0;
+
+		if (!(audio_devs[dev]->format_mask & fmt))	/* Not supported */
+		{
+			if (fmt == AFMT_MU_LAW)
+			{
+				fmt = AFMT_U8;
+				audio_devs[dev]->local_conversion = CNV_MU_LAW;
+			}
+			else
+				fmt = AFMT_U8;	/* This is always supported */
+		}
+		audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
+		audio_devs[dev]->local_format = fmt;
+	}
+	else
+		return audio_devs[dev]->local_format;
+
+	if (audio_devs[dev]->local_conversion)
+		return audio_devs[dev]->local_conversion;
+	else 
+		return audio_devs[dev]->local_format;
+}
+
+int audio_open(int dev, struct file *file)
+{
+	int ret;
+	int bits;
+	int dev_type = dev & 0x0f;
+	int mode = translate_mode(file);
+	const struct audio_driver *driver;
+	const struct coproc_operations *coprocessor;
+
+	dev = dev >> 4;
+
+	if (dev_type == SND_DEV_DSP16)
+		bits = 16;
+	else
+		bits = 8;
+
+	if (dev < 0 || dev >= num_audiodevs)
+		return -ENXIO;
+
+	driver = audio_devs[dev]->d;
+	if (driver->owner)
+		__MOD_INC_USE_COUNT(driver->owner);
+
+	if ((ret = DMAbuf_open(dev, mode)) < 0)
+		goto error_1;
+
+	if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
+		if (coprocessor->owner)
+			__MOD_INC_USE_COUNT(coprocessor->owner);
+
+		if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
+			printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
+			goto error_2;
+		}
+	}
+	
+	audio_devs[dev]->local_conversion = 0;
+
+	if (dev_type == SND_DEV_AUDIO)
+		set_format(dev, AFMT_MU_LAW);
+	else 
+		set_format(dev, bits);
+
+	audio_devs[dev]->audio_mode = AM_NONE;
+
+	return 0;
+
+	/*
+	 * Clean-up stack: this is what needs (un)doing if
+	 * we can't open the audio device ...
+	 */
+	error_2:
+	if (coprocessor->owner)
+		__MOD_DEC_USE_COUNT(coprocessor->owner);
+	DMAbuf_release(dev, mode);
+
+	error_1:
+	if (driver->owner)
+		__MOD_DEC_USE_COUNT(driver->owner);
+
+	return ret;
+}
+
+static void sync_output(int dev)
+{
+	int             p, i;
+	int             l;
+	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+	if (dmap->fragment_size <= 0)
+		return;
+	dmap->flags |= DMA_POST;
+
+	/* Align the write pointer with fragment boundaries */
+	
+	if ((l = dmap->user_counter % dmap->fragment_size) > 0)
+	{
+		int len;
+		unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
+
+		len = dmap->fragment_size - l;
+		memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
+		DMAbuf_move_wrpointer(dev, len);
+	}
+	
+	/*
+	 * Clean all unused buffer fragments.
+	 */
+
+	p = dmap->qtail;
+	dmap->flags |= DMA_POST;
+
+	for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
+	{
+		p = (p + 1) % dmap->nbufs;
+		if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
+			(dmap->raw_buf + dmap->buffsize))
+				printk(KERN_ERR "audio: Buffer error 2\n");
+
+		memset(dmap->raw_buf + p * dmap->fragment_size,
+			dmap->neutral_byte,
+			dmap->fragment_size);
+	}
+
+	dmap->flags |= DMA_DIRTY;
+}
+
+void audio_release(int dev, struct file *file)
+{
+	const struct coproc_operations *coprocessor;
+	int mode = translate_mode(file);
+
+	dev = dev >> 4;
+
+	/*
+	 * We do this in DMAbuf_release(). Why are we doing it
+	 * here? Why don't we test the file mode before setting
+	 * both flags? DMAbuf_release() does.
+	 * ...pester...pester...pester...
+	 */
+	audio_devs[dev]->dmap_out->closing = 1;
+	audio_devs[dev]->dmap_in->closing = 1;
+
+	/*
+	 * We need to make sure we allocated the dmap_out buffer
+	 * before we go mucking around with it in sync_output().
+	 */
+	if (mode & OPEN_WRITE)
+		sync_output(dev);
+
+	if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
+		coprocessor->close(coprocessor->devc, COPR_PCM);
+
+		if (coprocessor->owner)
+			__MOD_DEC_USE_COUNT(coprocessor->owner);
+	}
+	DMAbuf_release(dev, mode);
+
+	if (audio_devs[dev]->d->owner)
+		__MOD_DEC_USE_COUNT (audio_devs[dev]->d->owner);
+}
+
+static void translate_bytes(const unsigned char *table, unsigned char *buff, int n)
+{
+	unsigned long   i;
+
+	if (n <= 0)
+		return;
+
+	for (i = 0; i < n; ++i)
+		buff[i] = table[buff[i]];
+}
+
+int audio_write(int dev, struct file *file, const char *buf, int count)
+{
+	int c, p, l, buf_size, used, returned;
+	int err;
+	char *dma_buf;
+
+	dev = dev >> 4;
+
+	p = 0;
+	c = count;
+	
+	if(count < 0)
+		return -EINVAL;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+		return -EPERM;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+		audio_devs[dev]->audio_mode |= AM_WRITE;
+	else
+		audio_devs[dev]->audio_mode = AM_WRITE;
+
+	if (!count)		/* Flush output */
+	{
+		  sync_output(dev);
+		  return 0;
+	}
+	
+	while (c)
+	{
+		if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
+		{
+			    /* Handle nonblocking mode */
+			if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
+				return p? p : -EAGAIN;	/* No more space. Return # of accepted bytes */
+			return err;
+		}
+		l = c;
+
+		if (l > buf_size)
+			l = buf_size;
+
+		returned = l;
+		used = l;
+		if (!audio_devs[dev]->d->copy_user)
+		{
+			if ((dma_buf + l) >
+				(audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
+			{
+				printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
+				return -EDOM;
+			}
+			if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
+			{
+				printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
+				return -EDOM;
+			}
+			if(copy_from_user(dma_buf, &(buf)[p], l))
+				return -EFAULT;
+		} 
+		else audio_devs[dev]->d->copy_user (dev,
+						dma_buf, 0,
+						buf, p,
+						c, buf_size,
+						&used, &returned,
+						l);
+		l = returned;
+
+		if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
+		{
+			/*
+			 * This just allows interrupts while the conversion is running
+			 */
+			sti();
+			translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
+		}
+		c -= used;
+		p += used;
+		DMAbuf_move_wrpointer(dev, l);
+
+	}
+
+	return count;
+}
+
+int audio_read(int dev, struct file *file, char *buf, int count)
+{
+	int             c, p, l;
+	char           *dmabuf;
+	int             buf_no;
+
+	dev = dev >> 4;
+	p = 0;
+	c = count;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_READ))
+		return -EPERM;
+
+	if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+		sync_output(dev);
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+		audio_devs[dev]->audio_mode |= AM_READ;
+	else
+		audio_devs[dev]->audio_mode = AM_READ;
+
+	while(c)
+	{
+		if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
+		{
+			/*
+			 *	Nonblocking mode handling. Return current # of bytes
+			 */
+
+			if (p > 0) 		/* Avoid throwing away data */
+				return p;	/* Return it instead */
+
+			if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN)
+				return -EAGAIN;
+
+			return buf_no;
+		}
+		if (l > c)
+			l = c;
+
+		/*
+		 * Insert any local processing here.
+		 */
+
+		if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
+		{
+			/*
+			 * This just allows interrupts while the conversion is running
+			 */
+			sti();
+
+			translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
+		}
+		
+		{
+			char           *fixit = dmabuf;
+
+			if(copy_to_user(&(buf)[p], fixit, l))
+				return -EFAULT;
+		};
+
+		DMAbuf_rmchars(dev, buf_no, l);
+
+		p += l;
+		c -= l;
+	}
+
+	return count - c;
+}
+
+int audio_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg)
+{
+	int val, count;
+	unsigned long flags;
+	struct dma_buffparms *dmap;
+
+	dev = dev >> 4;
+
+	if (_IOC_TYPE(cmd) == 'C')	{
+		if (audio_devs[dev]->coproc)	/* Coprocessor ioctl */
+			return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
+		/* else
+		        printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
+		return -ENXIO;
+	}
+	else switch (cmd) 
+	{
+		case SNDCTL_DSP_SYNC:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return 0;
+			if (audio_devs[dev]->dmap_out->fragment_size == 0)
+				return 0;
+			sync_output(dev);
+			DMAbuf_sync(dev);
+			DMAbuf_reset(dev);
+			return 0;
+
+		case SNDCTL_DSP_POST:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return 0;
+			if (audio_devs[dev]->dmap_out->fragment_size == 0)
+				return 0;
+			audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
+			sync_output(dev);
+			dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0);
+			return 0;
+
+		case SNDCTL_DSP_RESET:
+			audio_devs[dev]->audio_mode = AM_NONE;
+			DMAbuf_reset(dev);
+			return 0;
+
+		case SNDCTL_DSP_GETFMTS:
+			val = audio_devs[dev]->format_mask | AFMT_MU_LAW;
+			break;
+	
+		case SNDCTL_DSP_SETFMT:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = set_format(dev, val);
+			break;
+
+		case SNDCTL_DSP_GETISPACE:
+			if (!(audio_devs[dev]->open_mode & OPEN_READ))
+				return 0;
+  			if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+  				return -EBUSY;
+			return dma_ioctl(dev, cmd, arg);
+
+		case SNDCTL_DSP_GETOSPACE:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EPERM;
+  			if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+  				return -EBUSY;
+			return dma_ioctl(dev, cmd, arg);
+		
+		case SNDCTL_DSP_NONBLOCK:
+			file->f_flags |= O_NONBLOCK;
+			return 0;
+
+		case SNDCTL_DSP_GETCAPS:
+				val = 1 | DSP_CAP_MMAP;	/* Revision level of this ioctl() */
+				if (audio_devs[dev]->flags & DMA_DUPLEX &&
+					audio_devs[dev]->open_mode == OPEN_READWRITE)
+					val |= DSP_CAP_DUPLEX;
+				if (audio_devs[dev]->coproc)
+					val |= DSP_CAP_COPROC;
+				if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
+					val |= DSP_CAP_BATCH;
+				if (audio_devs[dev]->d->trigger)	/* Supports SETTRIGGER */
+					val |= DSP_CAP_TRIGGER;
+				break;
+			
+		case SOUND_PCM_WRITE_RATE:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = audio_devs[dev]->d->set_speed(dev, val);
+			break;
+
+		case SOUND_PCM_READ_RATE:
+			val = audio_devs[dev]->d->set_speed(dev, 0);
+			break;
+			
+		case SNDCTL_DSP_STEREO:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val > 1 || val < 0)
+				return -EINVAL;
+			val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
+			break;
+
+		case SOUND_PCM_WRITE_CHANNELS:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = audio_devs[dev]->d->set_channels(dev, val);
+			break;
+
+		case SOUND_PCM_READ_CHANNELS:
+			val = audio_devs[dev]->d->set_channels(dev, 0);
+			break;
+		
+		case SOUND_PCM_READ_BITS:
+			val = audio_devs[dev]->d->set_bits(dev, 0);
+			break;
+
+		case SNDCTL_DSP_SETDUPLEX:
+			if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+				return -EPERM;
+			return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
+
+		case SNDCTL_DSP_PROFILE:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				audio_devs[dev]->dmap_out->applic_profile = val;
+			if (audio_devs[dev]->open_mode & OPEN_READ)
+				audio_devs[dev]->dmap_in->applic_profile = val;
+			return 0;
+		
+		case SNDCTL_DSP_GETODELAY:
+			dmap = audio_devs[dev]->dmap_out;
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+			if (!(dmap->flags & DMA_ALLOC_DONE))
+			{
+				val=0;
+				break;
+			}
+		
+			save_flags (flags);
+			cli();
+			/* Compute number of bytes that have been played */
+			count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
+			if (count < dmap->fragment_size && dmap->qhead != 0)
+				count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
+			count += dmap->byte_counter;
+		
+			/* Substract current count from the number of bytes written by app */
+			count = dmap->user_counter - count;
+			if (count < 0)
+				count = 0;
+			restore_flags (flags);
+			val = count;
+			break;
+		
+		default:
+			return dma_ioctl(dev, cmd, arg);
+	}
+	return put_user(val, (int *)arg);
+}
+
+void audio_init_devices(void)
+{
+	/*
+	 * NOTE! This routine could be called several times during boot.
+	 */
+}
+
+void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
+{
+	/*
+	 * This routine breaks the physical device buffers to logical ones.
+	 */
+
+	struct audio_operations *dsp_dev = audio_devs[dev];
+
+	unsigned i, n;
+	unsigned sr, nc, sz, bsz;
+
+	sr = dsp_dev->d->set_speed(dev, 0);
+	nc = dsp_dev->d->set_channels(dev, 0);
+	sz = dsp_dev->d->set_bits(dev, 0);
+
+	if (sz == 8)
+		dmap->neutral_byte = NEUTRAL8;
+	else
+		dmap->neutral_byte = NEUTRAL16;
+
+	if (sr < 1 || nc < 1 || sz < 1)
+	{
+/*		printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
+		sr = DSP_DEFAULT_SPEED;
+		nc = 1;
+		sz = 8;
+	}
+	
+	sz = sr * nc * sz;
+
+	sz /= 8;		/* #bits -> #bytes */
+	dmap->data_rate = sz;
+
+	if (!dmap->needs_reorg)
+		return;
+	dmap->needs_reorg = 0;
+
+	if (dmap->fragment_size == 0)
+	{	
+		/* Compute the fragment size using the default algorithm */
+
+		/*
+		 * Compute a buffer size for time not exceeding 1 second.
+		 * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+		 * of sound (using the current speed, sample size and #channels).
+		 */
+
+		bsz = dmap->buffsize;
+		while (bsz > sz)
+			bsz /= 2;
+
+		if (bsz == dmap->buffsize)
+			bsz /= 2;	/* Needs at least 2 buffers */
+
+		/*
+		 *    Split the computed fragment to smaller parts. After 3.5a9
+		 *      the default subdivision is 4 which should give better
+		 *      results when recording.
+		 */
+
+		if (dmap->subdivision == 0)	/* Not already set */
+		{
+			dmap->subdivision = 4;	/* Init to the default value */
+
+			if ((bsz / dmap->subdivision) > 4096)
+				dmap->subdivision *= 2;
+			if ((bsz / dmap->subdivision) < 4096)
+				dmap->subdivision = 1;
+		}
+		bsz /= dmap->subdivision;
+
+		if (bsz < 16)
+			bsz = 16;	/* Just a sanity check */
+
+		dmap->fragment_size = bsz;
+	}
+	else
+	{
+		/*
+		 * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
+		 * the buffer size computation has already been done.
+		 */
+		if (dmap->fragment_size > (dmap->buffsize / 2))
+			dmap->fragment_size = (dmap->buffsize / 2);
+		bsz = dmap->fragment_size;
+	}
+
+	if (audio_devs[dev]->min_fragment)
+		if (bsz < (1 << audio_devs[dev]->min_fragment))
+			bsz = 1 << audio_devs[dev]->min_fragment;
+	if (audio_devs[dev]->max_fragment)
+		if (bsz > (1 << audio_devs[dev]->max_fragment))
+			bsz = 1 << audio_devs[dev]->max_fragment;
+	bsz &= ~0x07;		/* Force size which is multiple of 8 bytes */
+#ifdef OS_DMA_ALIGN_CHECK
+	OS_DMA_ALIGN_CHECK(bsz);
+#endif
+
+	n = dmap->buffsize / bsz;
+	if (n > MAX_SUB_BUFFERS)
+		n = MAX_SUB_BUFFERS;
+	if (n > dmap->max_fragments)
+		n = dmap->max_fragments;
+
+	if (n < 2)
+	{
+		n = 2;
+		bsz /= 2;
+	}
+	dmap->nbufs = n;
+	dmap->bytes_in_use = n * bsz;
+	dmap->fragment_size = bsz;
+	dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
+			dmap->bytes_in_use;	/* Approximately one hour */
+
+	if (dmap->raw_buf)
+	{
+		memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use);
+	}
+	
+	for (i = 0; i < dmap->nbufs; i++)
+	{
+		dmap->counts[i] = 0;
+	}
+
+	dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
+}
+
+static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
+{
+	if (fact == 0) 
+	{
+		fact = dmap->subdivision;
+		if (fact == 0)
+			fact = 1;
+		return fact;
+	}
+	if (dmap->subdivision != 0 || dmap->fragment_size)	/* Too late to change */
+		return -EINVAL;
+
+	if (fact > MAX_REALTIME_FACTOR)
+		return -EINVAL;
+
+	if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+		return -EINVAL;
+
+	dmap->subdivision = fact;
+	return fact;
+}
+
+static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
+{
+	int bytes, count;
+
+	if (fact == 0)
+		return -EIO;
+
+	if (dmap->subdivision != 0 ||
+	    dmap->fragment_size)	/* Too late to change */
+		return -EINVAL;
+
+	bytes = fact & 0xffff;
+	count = (fact >> 16) & 0x7fff;
+
+	if (count == 0)
+		count = MAX_SUB_BUFFERS;
+	else if (count < MAX_SUB_BUFFERS)
+		count++;
+
+	if (bytes < 4 || bytes > 17)	/* <16 || > 512k */
+		return -EINVAL;
+
+	if (count < 2)
+		return -EINVAL;
+
+	if (audio_devs[dev]->min_fragment > 0)
+		if (bytes < audio_devs[dev]->min_fragment)
+			bytes = audio_devs[dev]->min_fragment;
+
+	if (audio_devs[dev]->max_fragment > 0)
+		if (bytes > audio_devs[dev]->max_fragment)
+			bytes = audio_devs[dev]->max_fragment;
+
+#ifdef OS_DMA_MINBITS
+	if (bytes < OS_DMA_MINBITS)
+		bytes = OS_DMA_MINBITS;
+#endif
+
+	dmap->fragment_size = (1 << bytes);
+	dmap->max_fragments = count;
+
+	if (dmap->fragment_size > dmap->buffsize)
+		dmap->fragment_size = dmap->buffsize;
+
+	if (dmap->fragment_size == dmap->buffsize &&
+	    audio_devs[dev]->flags & DMA_AUTOMODE)
+		dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
+
+	dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
+	return bytes | ((count - 1) << 16);
+}
+
+int dma_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
+	struct dma_buffparms *dmap;
+	audio_buf_info info;
+	count_info cinfo;
+	int fact, ret, changed, bits, count, err;
+	unsigned long flags;
+
+	switch (cmd) 
+	{
+		case SNDCTL_DSP_SUBDIVIDE:
+			ret = 0;
+			if (get_user(fact, (int *)arg))
+				return -EFAULT;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				ret = dma_subdivide(dev, dmap_out, fact);
+			if (ret < 0)
+				return ret;
+			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+				(audio_devs[dev]->flags & DMA_DUPLEX &&
+					audio_devs[dev]->open_mode & OPEN_READ))
+				ret = dma_subdivide(dev, dmap_in, fact);
+			if (ret < 0)
+				return ret;
+			break;
+
+		case SNDCTL_DSP_GETISPACE:
+		case SNDCTL_DSP_GETOSPACE:
+			dmap = dmap_out;
+			if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
+				return -EINVAL;
+			if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+			if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+				dmap = dmap_in;
+			if (dmap->mapping_flags & DMA_MAP_MAPPED)
+				return -EINVAL;
+			if (!(dmap->flags & DMA_ALLOC_DONE))
+				reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+			info.fragstotal = dmap->nbufs;
+			if (cmd == SNDCTL_DSP_GETISPACE)
+				info.fragments = dmap->qlen;
+			else 
+			{
+				if (!DMAbuf_space_in_queue(dev))
+					info.fragments = 0;
+				else
+				{
+					info.fragments = DMAbuf_space_in_queue(dev);
+					if (audio_devs[dev]->d->local_qlen) 
+					{
+						int tmp = audio_devs[dev]->d->local_qlen(dev);
+						if (tmp && info.fragments)
+							tmp--;	/*
+								 * This buffer has been counted twice
+								 */
+						info.fragments -= tmp;
+					}
+				}
+			}
+			if (info.fragments < 0)
+				info.fragments = 0;
+			else if (info.fragments > dmap->nbufs)
+				info.fragments = dmap->nbufs;
+
+			info.fragsize = dmap->fragment_size;
+			info.bytes = info.fragments * dmap->fragment_size;
+
+			if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+				info.bytes -= dmap->counts[dmap->qhead];
+			else 
+			{
+				info.fragments = info.bytes / dmap->fragment_size;
+				info.bytes -= dmap->user_counter % dmap->fragment_size;
+			}
+			if (copy_to_user(arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_DSP_SETTRIGGER:
+			if (get_user(bits, (int *)arg))
+				return -EFAULT;
+			bits &= audio_devs[dev]->open_mode;
+			if (audio_devs[dev]->d->trigger == NULL)
+				return -EINVAL;
+			if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
+				(bits & PCM_ENABLE_OUTPUT))
+				return -EINVAL;
+			save_flags(flags);
+			cli();
+			changed = audio_devs[dev]->enable_bits ^ bits;
+			if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) 
+			{
+				reorganize_buffers(dev, dmap_in, 1);
+				if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+					     dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
+					restore_flags(flags);
+					return -err;
+				}
+				dmap_in->dma_mode = DMODE_INPUT;
+				audio_devs[dev]->enable_bits = bits;
+				DMAbuf_activate_recording(dev, dmap_in);
+			}
+			if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+			    (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+			    audio_devs[dev]->go) 
+			{
+				if (!(dmap_out->flags & DMA_ALLOC_DONE))
+					reorganize_buffers(dev, dmap_out, 0);
+				dmap_out->dma_mode = DMODE_OUTPUT;
+				audio_devs[dev]->enable_bits = bits;
+				dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+				DMAbuf_launch_output(dev, dmap_out);
+			}
+			audio_devs[dev]->enable_bits = bits;
+#if 0
+			if (changed && audio_devs[dev]->d->trigger)
+				audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
+#endif				
+			restore_flags(flags);
+			/* Falls through... */
+
+		case SNDCTL_DSP_GETTRIGGER:
+			ret = audio_devs[dev]->enable_bits;
+			break;
+
+		case SNDCTL_DSP_SETSYNCRO:
+			if (!audio_devs[dev]->d->trigger)
+				return -EINVAL;
+			audio_devs[dev]->d->trigger(dev, 0);
+			audio_devs[dev]->go = 0;
+			return 0;
+
+		case SNDCTL_DSP_GETIPTR:
+			if (!(audio_devs[dev]->open_mode & OPEN_READ))
+				return -EINVAL;
+			save_flags(flags);
+			cli();
+			cinfo.bytes = dmap_in->byte_counter;
+			cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
+			if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
+				cinfo.bytes += dmap_in->bytes_in_use;	/* Pointer wrap not handled yet */
+			cinfo.blocks = dmap_in->qlen;
+			cinfo.bytes += cinfo.ptr;
+			if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
+				dmap_in->qlen = 0;	/* Reset interrupt counter */
+			restore_flags(flags);
+			if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_DSP_GETOPTR:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+
+			save_flags(flags);
+			cli();
+			cinfo.bytes = dmap_out->byte_counter;
+			cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
+			if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
+				cinfo.bytes += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+			cinfo.blocks = dmap_out->qlen;
+			cinfo.bytes += cinfo.ptr;
+			if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
+				dmap_out->qlen = 0;	/* Reset interrupt counter */
+			restore_flags(flags);
+			if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_DSP_GETODELAY:
+			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+				return -EINVAL;
+			if (!(dmap_out->flags & DMA_ALLOC_DONE))
+			{
+				ret=0;
+				break;
+			}
+			save_flags(flags);
+			cli();
+			/* Compute number of bytes that have been played */
+			count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
+			if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
+				count += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+			count += dmap_out->byte_counter;
+			/* Substract current count from the number of bytes written by app */
+			count = dmap_out->user_counter - count;
+			if (count < 0)
+				count = 0;
+			restore_flags (flags);
+			ret = count;
+			break;
+
+		case SNDCTL_DSP_POST:
+			if (audio_devs[dev]->dmap_out->qlen > 0)
+				if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+					DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
+			return 0;
+
+		case SNDCTL_DSP_GETBLKSIZE:
+			dmap = dmap_out;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
+			if (audio_devs[dev]->open_mode == OPEN_READ ||
+			    (audio_devs[dev]->flags & DMA_DUPLEX &&
+			     audio_devs[dev]->open_mode & OPEN_READ))
+				reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
+			if (audio_devs[dev]->open_mode == OPEN_READ)
+				dmap = dmap_in;
+			ret = dmap->fragment_size;
+			break;
+
+		case SNDCTL_DSP_SETFRAGMENT:
+			ret = 0;
+			if (get_user(fact, (int *)arg))
+				return -EFAULT;
+			if (audio_devs[dev]->open_mode & OPEN_WRITE)
+				ret = dma_set_fragment(dev, dmap_out, fact);
+			if (ret < 0)
+				return ret;
+			if (audio_devs[dev]->open_mode == OPEN_READ ||
+			    (audio_devs[dev]->flags & DMA_DUPLEX &&
+			     audio_devs[dev]->open_mode & OPEN_READ))
+				ret = dma_set_fragment(dev, dmap_in, fact);
+			if (ret < 0)
+				return ret;
+			if (!arg) /* don't know what this is good for, but preserve old semantics */
+				return 0;
+			break;
+
+		default:
+			if (!audio_devs[dev]->d->ioctl)
+				return -EINVAL;
+			return audio_devs[dev]->d->ioctl(dev, cmd, arg);
+	}
+	return put_user(ret, (int *)arg);
+}
diff -Nru linux/sound/oss/audio_syms.c linux-2.4.19-pre5-mjc/sound/oss/audio_syms.c
--- linux/sound/oss/audio_syms.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/audio_syms.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,21 @@
+/*
+ * Exported symbols for audio driver.
+ * __NO_VERSION__ because this is still part of sound.o.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+char audio_syms_symbol;
+
+#include "sound_config.h"
+#include "sound_calls.h"
+
+EXPORT_SYMBOL(DMAbuf_start_dma);
+EXPORT_SYMBOL(DMAbuf_open_dma);
+EXPORT_SYMBOL(DMAbuf_close_dma);
+EXPORT_SYMBOL(DMAbuf_inputintr);
+EXPORT_SYMBOL(DMAbuf_outputintr);
+EXPORT_SYMBOL(dma_ioctl);
+EXPORT_SYMBOL(audio_open);
+EXPORT_SYMBOL(audio_release);
diff -Nru linux/sound/oss/awe_hw.h linux-2.4.19-pre5-mjc/sound/oss/awe_hw.h
--- linux/sound/oss/awe_hw.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/awe_hw.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,99 @@
+/*
+ * sound/awe_hw.h
+ *
+ * Access routines and definitions for the low level driver for the 
+ * Creative AWE32/SB32/AWE64 wave table synth.
+ *   version 0.4.4; Jan. 4, 2000
+ *
+ * Copyright (C) 1996-2000 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_HW_H_DEF
+#define AWE_HW_H_DEF
+
+/*
+ * Emu-8000 control registers
+ * name(channel)	reg, port
+ */
+
+#define awe_cmd_idx(reg,ch)	(((reg)<< 5) | (ch))
+
+#define Data0    0		/* 0x620: doubleword r/w */
+#define Data1    1		/* 0xA20: doubleword r/w */
+#define Data2    2		/* 0xA22: word r/w */
+#define Data3    3		/* 0xE20: word r/w */
+#define Pointer  4		/* 0xE22 register pointer r/w */
+
+#define AWE_CPF(ch)	awe_cmd_idx(0,ch), Data0	/* DW: current pitch and fractional address */
+#define AWE_PTRX(ch)	awe_cmd_idx(1,ch), Data0	/* DW: pitch target and reverb send */
+#define AWE_CVCF(ch)	awe_cmd_idx(2,ch), Data0	/* DW: current volume and filter cutoff */
+#define AWE_VTFT(ch)	awe_cmd_idx(3,ch), Data0	/* DW: volume and filter cutoff targets */
+#define AWE_0080(ch)	awe_cmd_idx(4,ch), Data0	/* DW: ?? */
+#define AWE_00A0(ch)	awe_cmd_idx(5,ch), Data0	/* DW: ?? */
+#define AWE_PSST(ch)	awe_cmd_idx(6,ch), Data0	/* DW: pan send and loop start address */
+#define AWE_CSL(ch)	awe_cmd_idx(7,ch), Data0	/* DW: chorus send and loop end address */
+#define AWE_CCCA(ch)	awe_cmd_idx(0,ch), Data1	/* DW: Q, control bits, and current address */
+#define AWE_HWCF4	awe_cmd_idx(1,9),  Data1	/* DW: config dw 4 */
+#define AWE_HWCF5	awe_cmd_idx(1,10), Data1	/* DW: config dw 5 */
+#define AWE_HWCF6	awe_cmd_idx(1,13), Data1	/* DW: config dw 6 */
+#define AWE_HWCF7	awe_cmd_idx(1,14), Data1	/* DW: config dw 7? (not documented) */
+#define AWE_SMALR	awe_cmd_idx(1,20), Data1	/* DW: sound memory address for left read */
+#define AWE_SMARR	awe_cmd_idx(1,21), Data1	/* DW:    for right read */
+#define AWE_SMALW	awe_cmd_idx(1,22), Data1	/* DW: sound memory address for left write */
+#define AWE_SMARW	awe_cmd_idx(1,23), Data1	/* DW:    for right write */
+#define AWE_SMLD	awe_cmd_idx(1,26), Data1	/* W: sound memory left data */
+#define AWE_SMRD	awe_cmd_idx(1,26), Data2	/* W:    right data */
+#define AWE_WC		awe_cmd_idx(1,27), Data2	/* W: sample counter */
+#define AWE_WC_Cmd	awe_cmd_idx(1,27)
+#define AWE_WC_Port	Data2
+#define AWE_HWCF1	awe_cmd_idx(1,29), Data1	/* W: config w 1 */
+#define AWE_HWCF2	awe_cmd_idx(1,30), Data1	/* W: config w 2 */
+#define AWE_HWCF3	awe_cmd_idx(1,31), Data1	/* W: config w 3 */
+#define AWE_INIT1(ch)	awe_cmd_idx(2,ch), Data1	/* W: init array 1 */
+#define AWE_INIT2(ch)	awe_cmd_idx(2,ch), Data2	/* W: init array 2 */
+#define AWE_INIT3(ch)	awe_cmd_idx(3,ch), Data1	/* W: init array 3 */
+#define AWE_INIT4(ch)	awe_cmd_idx(3,ch), Data2	/* W: init array 4 */
+#define AWE_ENVVOL(ch)	awe_cmd_idx(4,ch), Data1	/* W: volume envelope delay */
+#define AWE_DCYSUSV(ch)	awe_cmd_idx(5,ch), Data1	/* W: volume envelope sustain and decay */
+#define AWE_ENVVAL(ch)	awe_cmd_idx(6,ch), Data1	/* W: modulation envelope delay */
+#define AWE_DCYSUS(ch)	awe_cmd_idx(7,ch), Data1	/* W: modulation envelope sustain and decay */
+#define AWE_ATKHLDV(ch)	awe_cmd_idx(4,ch), Data2	/* W: volume envelope attack and hold */
+#define AWE_LFO1VAL(ch)	awe_cmd_idx(5,ch), Data2	/* W: LFO#1 Delay */
+#define AWE_ATKHLD(ch)	awe_cmd_idx(6,ch), Data2	/* W: modulation envelope attack and hold */
+#define AWE_LFO2VAL(ch)	awe_cmd_idx(7,ch), Data2	/* W: LFO#2 Delay */
+#define AWE_IP(ch)	awe_cmd_idx(0,ch), Data3	/* W: initial pitch */
+#define AWE_IFATN(ch)	awe_cmd_idx(1,ch), Data3	/* W: initial filter cutoff and attenuation */
+#define AWE_PEFE(ch)	awe_cmd_idx(2,ch), Data3	/* W: pitch and filter envelope heights */
+#define AWE_FMMOD(ch)	awe_cmd_idx(3,ch), Data3	/* W: vibrato and filter modulation freq */
+#define AWE_TREMFRQ(ch)	awe_cmd_idx(4,ch), Data3	/* W: LFO#1 tremolo amount and freq */
+#define AWE_FM2FRQ2(ch)	awe_cmd_idx(5,ch), Data3	/* W: LFO#2 vibrato amount and freq */
+
+/* used during detection (returns ROM version?; not documented in ADIP) */
+#define AWE_U1		0xE0, Data3	  /* (R)(W) used in initialization */
+#define AWE_U2(ch)	0xC0+(ch), Data3  /* (W)(W) used in init envelope  */
+
+
+#define AWE_MAX_VOICES		32
+#define AWE_NORMAL_VOICES	30	/*30&31 are reserved for DRAM refresh*/
+
+#define AWE_MAX_CHANNELS	32	/* max midi channels (must >= voices) */
+#define AWE_MAX_LAYERS	AWE_MAX_VOICES	/* maximum number of multiple layers */
+
+#define AWE_DRAM_OFFSET		0x200000
+#define AWE_MAX_DRAM_SIZE	(28 * 1024)	/* 28 MB is max onboard memory */
+
+#endif
diff -Nru linux/sound/oss/awe_wave.c linux-2.4.19-pre5-mjc/sound/oss/awe_wave.c
--- linux/sound/oss/awe_wave.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/awe_wave.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,6159 @@
+/*
+ * sound/awe_wave.c
+ *
+ * The low level driver for the AWE32/SB32/AWE64 wave table synth.
+ *   version 0.4.4; Jan. 4, 2000
+ *
+ * Copyright (C) 1996-2000 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/awe_voice.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/isapnp.h>
+
+#include "sound_config.h"
+
+#include "awe_wave.h"
+#include "awe_hw.h"
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+#include "tuning.h"
+#include <linux/ultrasound.h>
+#endif
+
+/*
+ * debug message
+ */
+
+#ifdef AWE_DEBUG_ON
+#define DEBUG(LVL,XXX)	{if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }}
+#define ERRMSG(XXX)	{if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }}
+#define FATALERR(XXX)	XXX
+#else
+#define DEBUG(LVL,XXX) /**/
+#define ERRMSG(XXX)	XXX
+#define FATALERR(XXX)	XXX
+#endif
+
+/*
+ * bank and voice record
+ */
+
+typedef struct _sf_list sf_list;
+typedef struct _awe_voice_list awe_voice_list;
+typedef struct _awe_sample_list awe_sample_list;
+
+/* soundfont record */
+struct _sf_list {
+	unsigned short sf_id;	/* id number */
+	unsigned short type;	/* lock & shared flags */
+	int num_info;		/* current info table index */
+	int num_sample;		/* current sample table index */
+	int mem_ptr;		/* current word byte pointer */
+	awe_voice_list *infos, *last_infos;	/* instruments */
+	awe_sample_list *samples, *last_samples;	/* samples */
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	sf_list *shared;	/* shared list */
+	unsigned char name[AWE_PATCH_NAME_LEN];	/* sharing id */
+#endif
+	sf_list *next, *prev;
+};
+
+/* instrument list */
+struct _awe_voice_list {
+	awe_voice_info v;	/* instrument information */
+	sf_list *holder;	/* parent sf_list of this record */
+	unsigned char bank, instr;	/* preset number information */
+	char type, disabled;	/* type=normal/mapped, disabled=boolean */
+	awe_voice_list *next;	/* linked list with same sf_id */
+	awe_voice_list *next_instr;	/* instrument list */
+	awe_voice_list *next_bank;	/* hash table list */
+};
+
+/* voice list type */
+#define V_ST_NORMAL	0
+#define V_ST_MAPPED	1
+
+/* sample list */
+struct _awe_sample_list {
+	awe_sample_info v;	/* sample information */
+	sf_list *holder;	/* parent sf_list of this record */
+	awe_sample_list *next;	/* linked list with same sf_id */
+};
+
+/* sample and information table */
+static int current_sf_id = 0;	/* current number of fonts */
+static int locked_sf_id = 0;	/* locked position */
+static sf_list *sfhead = NULL, *sftail = NULL;	/* linked-lists */
+
+#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0)
+#define awe_free_info() (sftail ? sftail->num_info : 0)
+#define awe_free_sample() (sftail ? sftail->num_sample : 0)
+
+#define AWE_MAX_PRESETS		256
+#define AWE_DEFAULT_PRESET	0
+#define AWE_DEFAULT_BANK	0
+#define AWE_DEFAULT_DRUM	0
+#define AWE_DRUM_BANK		128
+
+#define MAX_LAYERS	AWE_MAX_VOICES
+
+/* preset table index */
+static awe_voice_list *preset_table[AWE_MAX_PRESETS];
+
+/*
+ * voice table
+ */
+
+/* effects table */
+typedef	struct FX_Rec { /* channel effects */
+	unsigned char flags[AWE_FX_END];
+	short val[AWE_FX_END];
+} FX_Rec;
+
+
+/* channel parameters */
+typedef struct _awe_chan_info {
+	int channel;		/* channel number */
+	int bank;		/* current tone bank */
+	int instr;		/* current program */
+	int bender;		/* midi pitchbend (-8192 - 8192) */
+	int bender_range;	/* midi bender range (x100) */
+	int panning;		/* panning (0-127) */
+	int main_vol;		/* channel volume (0-127) */
+	int expression_vol;	/* midi expression (0-127) */
+	int chan_press;		/* channel pressure */
+	int sustained;		/* sustain status in MIDI */
+	FX_Rec fx;		/* effects */
+	FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
+} awe_chan_info;
+
+/* voice parameters */
+typedef struct _voice_info {
+	int state;
+#define AWE_ST_OFF		(1<<0)	/* no sound */
+#define AWE_ST_ON		(1<<1)	/* playing */
+#define AWE_ST_STANDBY		(1<<2)	/* stand by for playing */
+#define AWE_ST_SUSTAINED	(1<<3)	/* sustained */
+#define AWE_ST_MARK		(1<<4)	/* marked for allocation */
+#define AWE_ST_DRAM		(1<<5)	/* DRAM read/write */
+#define AWE_ST_FM		(1<<6)	/* reserved for FM */
+#define AWE_ST_RELEASED		(1<<7)	/* released */
+
+	int ch;			/* midi channel */
+	int key;		/* internal key for search */
+	int layer;		/* layer number (for channel mode only) */
+	int time;		/* allocated time */
+	awe_chan_info	*cinfo;	/* channel info */
+
+	int note;		/* midi key (0-127) */
+	int velocity;		/* midi velocity (0-127) */
+	int sostenuto;		/* sostenuto on/off */
+	awe_voice_info *sample;	/* assigned voice */
+
+	/* EMU8000 parameters */
+	int apitch;		/* pitch parameter */
+	int avol;		/* volume parameter */
+	int apan;		/* panning parameter */
+	int acutoff;		/* cutoff parameter */
+	short aaux;		/* aux word */
+} voice_info;
+
+/* voice information */
+static voice_info voices[AWE_MAX_VOICES];
+
+#define IS_NO_SOUND(v)	(voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED))
+#define IS_NO_EFFECT(v)	(voices[v].state != AWE_ST_ON)
+#define IS_PLAYING(v)	(voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED))
+#define IS_EMPTY(v)	(voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM))
+
+
+/* MIDI channel effects information (for hw control) */
+static awe_chan_info channels[AWE_MAX_CHANNELS];
+
+
+/*
+ * global variables
+ */
+
+#ifndef AWE_DEFAULT_BASE_ADDR
+#define AWE_DEFAULT_BASE_ADDR	0	/* autodetect */
+#endif
+
+#ifndef AWE_DEFAULT_MEM_SIZE
+#define AWE_DEFAULT_MEM_SIZE	-1	/* autodetect */
+#endif
+
+int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */
+int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */
+#ifdef __ISAPNP__
+static int isapnp = -1;
+#else
+static int isapnp = 0;
+#endif
+
+MODULE_AUTHOR("Takashi Iwai <iwai@ww.uni-erlangen.de>");
+MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "base i/o port of Emu8000");
+MODULE_PARM(memsize, "i");
+MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes");
+MODULE_PARM(isapnp, "i");
+MODULE_PARM_DESC(isapnp, "use ISAPnP detection");
+EXPORT_NO_SYMBOLS;
+
+/* DRAM start offset */
+static int awe_mem_start = AWE_DRAM_OFFSET;
+
+/* maximum channels for playing */
+static int awe_max_voices = AWE_MAX_VOICES;
+
+static int patch_opened = 0;		/* sample already loaded? */
+
+static char atten_relative = FALSE;
+static short atten_offset = 0;
+
+static int awe_present = FALSE;		/* awe device present? */
+static int awe_busy = FALSE;		/* awe device opened? */
+
+static int my_dev = -1;			
+
+#define DEFAULT_DRUM_FLAGS	((1 << 9) | (1 << 25))
+#define IS_DRUM_CHANNEL(c)	(drum_flags & (1 << (c)))
+#define DRUM_CHANNEL_ON(c)	(drum_flags |= (1 << (c)))
+#define DRUM_CHANNEL_OFF(c)	(drum_flags &= ~(1 << (c)))
+static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+static int playing_mode = AWE_PLAY_INDIRECT;
+#define SINGLE_LAYER_MODE()	(playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT)
+#define MULTI_LAYER_MODE()	(playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2)
+
+static int current_alloc_time = 0;	/* voice allocation index for channel mode */
+
+static struct synth_info awe_info = {
+	"AWE32 Synth",		/* name */
+	0,			/* device */
+	SYNTH_TYPE_SAMPLE,	/* synth_type */
+	SAMPLE_TYPE_AWE32,	/* synth_subtype */
+	0,			/* perc_mode (obsolete) */
+	AWE_MAX_VOICES,		/* nr_voices */
+	0,			/* nr_drums (obsolete) */
+	400			/* instr_bank_size */
+};
+
+
+static struct voice_alloc_info *voice_alloc;	/* set at initialization */
+
+
+/*
+ * function prototypes
+ */
+
+static int awe_check_port(void);
+static void awe_request_region(void);
+static void awe_release_region(void);
+
+static void awe_reset_samples(void);
+/* emu8000 chip i/o access */
+static void setup_ports(int p1, int p2, int p3);
+static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data);
+static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data);
+static unsigned short awe_peek(unsigned short cmd, unsigned short port);
+static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port);
+static void awe_wait(unsigned short delay);
+
+/* initialize emu8000 chip */
+static int _attach_awe(void);
+static void _unload_awe(void);
+static void awe_initialize(void);
+
+/* set voice parameters */
+static void awe_init_ctrl_parms(int init_all);
+static void awe_init_voice_info(awe_voice_info *vp);
+static void awe_init_voice_parm(awe_voice_parm *pp);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static int freq_to_note(int freq);
+static int calc_rate_offset(int Hz);
+/*static int calc_parm_delay(int msec);*/
+static int calc_parm_hold(int msec);
+static int calc_parm_attack(int msec);
+static int calc_parm_decay(int msec);
+static int calc_parm_search(int msec, short *table);
+#endif /* gus compat */
+
+/* turn on/off note */
+static void awe_note_on(int voice);
+static void awe_note_off(int voice);
+static void awe_terminate(int voice);
+static void awe_exclusive_off(int voice);
+static void awe_note_off_all(int do_sustain);
+
+/* calculate voice parameters */
+typedef void (*fx_affect_func)(int voice, int forced);
+static void awe_set_pitch(int voice, int forced);
+static void awe_set_voice_pitch(int voice, int forced);
+static void awe_set_volume(int voice, int forced);
+static void awe_set_voice_vol(int voice, int forced);
+static void awe_set_pan(int voice, int forced);
+static void awe_fx_fmmod(int voice, int forced);
+static void awe_fx_tremfrq(int voice, int forced);
+static void awe_fx_fm2frq2(int voice, int forced);
+static void awe_fx_filterQ(int voice, int forced);
+static void awe_calc_pitch(int voice);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static void awe_calc_pitch_from_freq(int voice, int freq);
+#endif
+static void awe_calc_volume(int voice);
+static void awe_update_volume(void);
+static void awe_change_master_volume(short val);
+static void awe_voice_init(int voice, int init_all);
+static void awe_channel_init(int ch, int init_all);
+static void awe_fx_init(int ch);
+static void awe_send_effect(int voice, int layer, int type, int val);
+static void awe_modwheel_change(int voice, int value);
+
+/* sequencer interface */
+static int awe_open(int dev, int mode);
+static void awe_close(int dev);
+static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg);
+static int awe_kill_note(int dev, int voice, int note, int velocity);
+static int awe_start_note(int dev, int v, int note_num, int volume);
+static int awe_set_instr(int dev, int voice, int instr_no);
+static int awe_set_instr_2(int dev, int voice, int instr_no);
+static void awe_reset(int dev);
+static void awe_hw_control(int dev, unsigned char *event);
+static int awe_load_patch(int dev, int format, const char *addr,
+			  int offs, int count, int pmgr_flag);
+static void awe_aftertouch(int dev, int voice, int pressure);
+static void awe_controller(int dev, int voice, int ctrl_num, int value);
+static void awe_panning(int dev, int voice, int value);
+static void awe_volume_method(int dev, int mode);
+static void awe_bender(int dev, int voice, int value);
+static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc);
+static void awe_setup_voice(int dev, int voice, int chn);
+
+#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press)
+
+/* hardware controls */
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
+#endif
+static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
+static void awe_voice_change(int voice, fx_affect_func func);
+static void awe_sostenuto_on(int voice, int forced);
+static void awe_sustain_off(int voice, int forced);
+static void awe_terminate_and_init(int voice, int forced);
+
+/* voice search */
+static int awe_search_key(int bank, int preset, int note);
+static awe_voice_list *awe_search_instr(int bank, int preset, int note);
+static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist);
+static void awe_alloc_multi_voices(int ch, int note, int velocity, int key);
+static void awe_alloc_one_voice(int voice, int note, int velocity);
+static int awe_clear_voice(void);
+
+/* load / remove patches */
+static int awe_open_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_close_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_info(awe_patch_info *patch, const char *addr, int count);
+static int awe_remove_info(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_replace_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_map(awe_patch_info *patch, const char *addr, int count);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
+#endif
+/*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/
+static int awe_probe_data(awe_patch_info *patch, const char *addr, int count);
+static sf_list *check_patch_opened(int type, char *name);
+static int awe_write_wave_data(const char *addr, int offset, awe_sample_list *sp, int channels);
+static int awe_create_sf(int type, char *name);
+static void awe_free_sf(sf_list *sf);
+static void add_sf_info(sf_list *sf, awe_voice_list *rec);
+static void add_sf_sample(sf_list *sf, awe_sample_list *smp);
+static void purge_old_list(awe_voice_list *rec, awe_voice_list *next);
+static void add_info_list(awe_voice_list *rec);
+static void awe_remove_samples(int sf_id);
+static void rebuild_preset_list(void);
+static short awe_set_sample(awe_voice_list *rec);
+static awe_sample_list *search_sample_index(sf_list *sf, int sample);
+
+static int is_identical_holder(sf_list *sf1, sf_list *sf2);
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+static int is_identical_name(unsigned char *name, sf_list *p);
+static int is_shared_sf(unsigned char *name);
+static int info_duplicated(sf_list *sf, awe_voice_list *rec);
+#endif /* allow sharing */
+
+/* lowlevel functions */
+static void awe_init_audio(void);
+static void awe_init_dma(void);
+static void awe_init_array(void);
+static void awe_send_array(unsigned short *data);
+static void awe_tweak_voice(int voice);
+static void awe_tweak(void);
+static void awe_init_fm(void);
+static int awe_open_dram_for_write(int offset, int channels);
+static void awe_open_dram_for_check(void);
+static void awe_close_dram(void);
+/*static void awe_write_dram(unsigned short c);*/
+static int awe_detect_base(int addr);
+static int awe_detect(void);
+static void awe_check_dram(void);
+static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count);
+static void awe_set_chorus_mode(int mode);
+static void awe_update_chorus_mode(void);
+static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count);
+static void awe_set_reverb_mode(int mode);
+static void awe_update_reverb_mode(void);
+static void awe_equalizer(int bass, int treble);
+static void awe_update_equalizer(void);
+
+#ifdef CONFIG_AWE32_MIXER
+static void attach_mixer(void);
+static void unload_mixer(void);
+#endif
+
+#ifdef CONFIG_AWE32_MIDIEMU
+static void attach_midiemu(void);
+static void unload_midiemu(void);
+#endif
+
+#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b)
+
+/*
+ * control parameters
+ */
+
+
+#ifdef AWE_USE_NEW_VOLUME_CALC
+#define DEF_VOLUME_CALC	TRUE
+#else
+#define DEF_VOLUME_CALC	FALSE
+#endif /* new volume */
+
+#define DEF_ZERO_ATTEN		32	/* 12dB below */
+#define DEF_MOD_SENSE		18
+#define DEF_CHORUS_MODE		2
+#define DEF_REVERB_MODE		4
+#define DEF_BASS_LEVEL		5
+#define DEF_TREBLE_LEVEL	9
+
+static struct CtrlParmsDef {
+	int value;
+	int init_each_time;
+	void (*update)(void);
+} ctrl_parms[AWE_MD_END] = {
+	{0,0, NULL}, {0,0, NULL}, /* <-- not used */
+	{AWE_VERSION_NUMBER, FALSE, NULL},
+	{TRUE, FALSE, NULL}, /* exclusive */
+	{TRUE, FALSE, NULL}, /* realpan */
+	{AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */
+	{FALSE, TRUE, NULL}, /* keep effect */
+	{DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */
+	{FALSE, FALSE, NULL}, /* chn_prior */
+	{DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */
+	{AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */
+	{AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */
+	{AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */
+	{FALSE, FALSE, NULL}, /* toggle_drum_bank */
+	{DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */
+	{DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */
+	{DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */
+	{DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */
+	{DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */
+	{0, FALSE, NULL},	/* debug mode */
+	{FALSE, FALSE, NULL}, /* pan exchange */
+};
+
+static int ctrls[AWE_MD_END];
+
+
+/*
+ * synth operation table
+ */
+
+static struct synth_operations awe_operations =
+{
+	owner:		THIS_MODULE,
+	id:		"EMU8K",
+	info:		&awe_info,
+	midi_dev:	0,
+	synth_type:	SYNTH_TYPE_SAMPLE,
+	synth_subtype:	SAMPLE_TYPE_AWE32,
+	open:		awe_open,
+	close:		awe_close,
+	ioctl:		awe_ioctl,
+	kill_note:	awe_kill_note,
+	start_note:	awe_start_note,
+	set_instr:	awe_set_instr_2,
+	reset:		awe_reset,
+	hw_control:	awe_hw_control,
+	load_patch:	awe_load_patch,
+	aftertouch:	awe_aftertouch,
+	controller:	awe_controller,
+	panning:	awe_panning,
+	volume_method:	awe_volume_method,
+	bender:		awe_bender,
+	alloc_voice:	awe_alloc,
+	setup_voice:	awe_setup_voice
+};
+
+
+/*
+ * General attach / unload interface
+ */
+
+static int __init _attach_awe(void)
+{
+	if (awe_present) return 0; /* for OSS38.. called twice? */
+
+	/* check presence of AWE32 card */
+	if (! awe_detect()) {
+		printk(KERN_ERR "AWE32: not detected\n");
+		return 0;
+	}
+
+	/* check AWE32 ports are available */
+	if (awe_check_port()) {
+		printk(KERN_ERR "AWE32: I/O area already used.\n");
+		return 0;
+	}
+
+	/* set buffers to NULL */
+	sfhead = sftail = NULL;
+
+	my_dev = sound_alloc_synthdev();
+	if (my_dev == -1) {
+		printk(KERN_ERR "AWE32 Error: too many synthesizers\n");
+		return 0;
+	}
+
+	voice_alloc = &awe_operations.alloc;
+	voice_alloc->max_voice = awe_max_voices;
+	synth_devs[my_dev] = &awe_operations;
+
+#ifdef CONFIG_AWE32_MIXER
+	attach_mixer();
+#endif
+#ifdef CONFIG_AWE32_MIDIEMU
+	attach_midiemu();
+#endif
+
+	/* reserve I/O ports for awedrv */
+	awe_request_region();
+
+	/* clear all samples */
+	awe_reset_samples();
+
+	/* intialize AWE32 hardware */
+	awe_initialize();
+
+	sprintf(awe_info.name, "AWE32-%s (RAM%dk)",
+		AWEDRV_VERSION, memsize/1024);
+	printk(KERN_INFO "<SoundBlaster EMU8000 (RAM%dk)>\n", memsize/1024);
+
+	awe_present = TRUE;
+
+	return 1;
+}
+
+
+static void free_tables(void)
+{
+	if (sftail) {
+		sf_list *p, *prev;
+		for (p = sftail; p; p = prev) {
+			prev = p->prev;
+			awe_free_sf(p);
+		}
+	}
+	sfhead = sftail = NULL;
+}
+
+
+static void __exit _unload_awe(void)
+{
+	if (awe_present) {
+		awe_reset_samples();
+		awe_release_region();
+		free_tables();
+#ifdef CONFIG_AWE32_MIXER
+		unload_mixer();
+#endif
+#ifdef CONFIG_AWE32_MIDIEMU
+		unload_midiemu();
+#endif
+		sound_unload_synthdev(my_dev);
+		awe_present = FALSE;
+	}
+}
+
+
+/*
+ * clear sample tables 
+ */
+
+static void
+awe_reset_samples(void)
+{
+	/* free all bank tables */
+	memset(preset_table, 0, sizeof(preset_table));
+	free_tables();
+
+	current_sf_id = 0;
+	locked_sf_id = 0;
+	patch_opened = 0;
+}
+
+
+/*
+ * EMU register access
+ */
+
+/* select a given AWE32 pointer */
+static int awe_ports[5];
+static int port_setuped = FALSE;
+static int awe_cur_cmd = -1;
+#define awe_set_cmd(cmd) \
+if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; }
+
+/* store values to i/o port array */
+static void setup_ports(int port1, int port2, int port3)
+{
+	awe_ports[0] = port1;
+	if (port2 == 0)
+		port2 = port1 + 0x400;
+	awe_ports[1] = port2;
+	awe_ports[2] = port2 + 2;
+	if (port3 == 0)
+		port3 = port1 + 0x800;
+	awe_ports[3] = port3;
+	awe_ports[4] = port3 + 2;
+
+	port_setuped = TRUE;
+}
+
+/* write 16bit data */
+static void
+awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
+{
+	awe_set_cmd(cmd);
+	outw(data, awe_ports[port]);
+}
+
+/* write 32bit data */
+static void
+awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
+{
+	unsigned short addr = awe_ports[port];
+	awe_set_cmd(cmd);
+	outw(data, addr);		/* write lower 16 bits */
+	outw(data >> 16, addr + 2);	/* write higher 16 bits */
+}
+
+/* read 16bit data */
+static unsigned short
+awe_peek(unsigned short cmd, unsigned short port)
+{
+	unsigned short k;
+	awe_set_cmd(cmd);
+	k = inw(awe_ports[port]);
+	return k;
+}
+
+/* read 32bit data */
+static unsigned int
+awe_peek_dw(unsigned short cmd, unsigned short port)
+{
+	unsigned int k1, k2;
+	unsigned short addr = awe_ports[port];
+	awe_set_cmd(cmd);
+	k1 = inw(addr);
+	k2 = inw(addr + 2);
+	k1 |= k2 << 16;
+	return k1;
+}
+
+/* wait delay number of AWE32 44100Hz clocks */
+#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */
+static void
+awe_wait(unsigned short delay)
+{
+	unsigned short clock, target;
+	unsigned short port = awe_ports[AWE_WC_Port];
+	int counter;
+  
+	/* sample counter */
+	awe_set_cmd(AWE_WC_Cmd);
+	clock = (unsigned short)inw(port);
+	target = clock + delay;
+	counter = 0;
+	if (target < clock) {
+		for (; (unsigned short)inw(port) > target; counter++)
+			if (counter > 65536)
+				break;
+	}
+	for (; (unsigned short)inw(port) < target; counter++)
+		if (counter > 65536)
+			break;
+}
+#else
+
+static void awe_wait(unsigned short delay)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((HZ*(unsigned long)delay + 44099)/44100);
+}
+/*
+static void awe_wait(unsigned short delay)
+{
+	udelay(((unsigned long)delay * 1000000L + 44099) / 44100);
+}
+*/
+#endif /* wait by loop */
+
+/* write a word data */
+#define awe_write_dram(c)	awe_poke(AWE_SMLD, c)
+
+
+/*
+ * port check / request
+ *  0x620-623, 0xA20-A23, 0xE20-E23
+ */
+
+static int __init
+awe_check_port(void)
+{
+	if (! port_setuped) return 0;
+	return (check_region(awe_ports[0], 4) ||
+		check_region(awe_ports[1], 4) ||
+		check_region(awe_ports[3], 4));
+}
+
+static void __init
+awe_request_region(void)
+{
+	if (! port_setuped) return;
+	request_region(awe_ports[0], 4, "sound driver (AWE32)");
+	request_region(awe_ports[1], 4, "sound driver (AWE32)");
+	request_region(awe_ports[3], 4, "sound driver (AWE32)");
+}
+
+static void __exit
+awe_release_region(void)
+{
+	if (! port_setuped) return;
+	release_region(awe_ports[0], 4);
+	release_region(awe_ports[1], 4);
+	release_region(awe_ports[3], 4);
+}
+
+
+/*
+ * initialization of AWE driver
+ */
+
+static void
+awe_initialize(void)
+{
+	DEBUG(0,printk("AWE32: initializing..\n"));
+
+	/* initialize hardware configuration */
+	awe_poke(AWE_HWCF1, 0x0059);
+	awe_poke(AWE_HWCF2, 0x0020);
+
+	/* disable audio; this seems to reduce a clicking noise a bit.. */
+	awe_poke(AWE_HWCF3, 0);
+
+	/* initialize audio channels */
+	awe_init_audio();
+
+	/* initialize DMA */
+	awe_init_dma();
+
+	/* initialize init array */
+	awe_init_array();
+
+	/* check DRAM memory size */
+	awe_check_dram();
+
+	/* initialize the FM section of the AWE32 */
+	awe_init_fm();
+
+	/* set up voice envelopes */
+	awe_tweak();
+
+	/* enable audio */
+	awe_poke(AWE_HWCF3, 0x0004);
+
+	/* set default values */
+	awe_init_ctrl_parms(TRUE);
+
+	/* set equalizer */
+	awe_update_equalizer();
+
+	/* set reverb & chorus modes */
+	awe_update_reverb_mode();
+	awe_update_chorus_mode();
+}
+
+
+/*
+ * AWE32 voice parameters
+ */
+
+/* initialize voice_info record */
+static void
+awe_init_voice_info(awe_voice_info *vp)
+{
+	vp->sample = 0;
+	vp->rate_offset = 0;
+
+	vp->start = 0;
+	vp->end = 0;
+	vp->loopstart = 0;
+	vp->loopend = 0;
+	vp->mode = 0;
+	vp->root = 60;
+	vp->tune = 0;
+	vp->low = 0;
+	vp->high = 127;
+	vp->vellow = 0;
+	vp->velhigh = 127;
+
+	vp->fixkey = -1;
+	vp->fixvel = -1;
+	vp->fixpan = -1;
+	vp->pan = -1;
+
+	vp->exclusiveClass = 0;
+	vp->amplitude = 127;
+	vp->attenuation = 0;
+	vp->scaleTuning = 100;
+
+	awe_init_voice_parm(&vp->parm);
+}
+
+/* initialize voice_parm record:
+ * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
+ * Vibrato and Tremolo effects are zero.
+ * Cutoff is maximum.
+ * Chorus and Reverb effects are zero.
+ */
+static void
+awe_init_voice_parm(awe_voice_parm *pp)
+{
+	pp->moddelay = 0x8000;
+	pp->modatkhld = 0x7f7f;
+	pp->moddcysus = 0x7f7f;
+	pp->modrelease = 0x807f;
+	pp->modkeyhold = 0;
+	pp->modkeydecay = 0;
+
+	pp->voldelay = 0x8000;
+	pp->volatkhld = 0x7f7f;
+	pp->voldcysus = 0x7f7f;
+	pp->volrelease = 0x807f;
+	pp->volkeyhold = 0;
+	pp->volkeydecay = 0;
+
+	pp->lfo1delay = 0x8000;
+	pp->lfo2delay = 0x8000;
+	pp->pefe = 0;
+
+	pp->fmmod = 0;
+	pp->tremfrq = 0;
+	pp->fm2frq2 = 0;
+
+	pp->cutoff = 0xff;
+	pp->filterQ = 0;
+
+	pp->chorus = 0;
+	pp->reverb = 0;
+}	
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* convert frequency mHz to abstract cents (= midi key * 100) */
+static int
+freq_to_note(int mHz)
+{
+	/* abscents = log(mHz/8176) / log(2) * 1200 */
+	unsigned int max_val = (unsigned int)0xffffffff / 10000;
+	int i, times;
+	unsigned int base;
+	unsigned int freq;
+	int note, tune;
+
+	if (mHz == 0)
+		return 0;
+	if (mHz < 0)
+		return 12799; /* maximum */
+
+	freq = mHz;
+	note = 0;
+	for (base = 8176 * 2; freq >= base; base *= 2) {
+		note += 12;
+		if (note >= 128) /* over maximum */
+			return 12799;
+	}
+	base /= 2;
+
+	/* to avoid overflow... */
+	times = 10000;
+	while (freq > max_val) {
+		max_val *= 10;
+		times /= 10;
+		base /= 10;
+	}
+
+	freq = freq * times / base;
+	for (i = 0; i < 12; i++) {
+		if (freq < semitone_tuning[i+1])
+			break;
+		note++;
+	}
+
+	tune = 0;
+	freq = freq * 10000 / semitone_tuning[i];
+	for (i = 0; i < 100; i++) {
+		if (freq < cent_tuning[i+1])
+			break;
+		tune++;
+	}
+
+	return note * 100 + tune;
+}
+
+
+/* convert Hz to AWE32 rate offset:
+ * sample pitch offset for the specified sample rate
+ * rate=44100 is no offset, each 4096 is 1 octave (twice).
+ * eg, when rate is 22050, this offset becomes -4096.
+ */
+static int
+calc_rate_offset(int Hz)
+{
+	/* offset = log(Hz / 44100) / log(2) * 4096 */
+	int freq, base, i;
+
+	/* maybe smaller than max (44100Hz) */
+	if (Hz <= 0 || Hz >= 44100) return 0;
+
+	base = 0;
+	for (freq = Hz * 2; freq < 44100; freq *= 2)
+		base++;
+	base *= 1200;
+
+	freq = 44100 * 10000 / (freq/2);
+	for (i = 0; i < 12; i++) {
+		if (freq < semitone_tuning[i+1])
+			break;
+		base += 100;
+	}
+	freq = freq * 10000 / semitone_tuning[i];
+	for (i = 0; i < 100; i++) {
+		if (freq < cent_tuning[i+1])
+			break;
+		base++;
+	}
+	return -base * 4096 / 1200;
+}
+
+
+/*
+ * convert envelope time parameter to AWE32 raw parameter
+ */
+
+/* attack & decay/release time table (msec) */
+static short attack_time_tbl[128] = {
+32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
+707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
+361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
+180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
+90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
+45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
+22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
+};
+
+static short decay_time_tbl[128] = {
+32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
+2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
+1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
+691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
+345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
+172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
+86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
+43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
+};
+
+#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725);
+
+/* delay time = 0x8000 - msec/92 */
+static int
+calc_parm_hold(int msec)
+{
+	int val = (0x7f * 92 - msec) / 92;
+	if (val < 1) val = 1;
+	if (val > 127) val = 127;
+	return val;
+}
+
+/* attack time: search from time table */
+static int
+calc_parm_attack(int msec)
+{
+	return calc_parm_search(msec, attack_time_tbl);
+}
+
+/* decay/release time: search from time table */
+static int
+calc_parm_decay(int msec)
+{
+	return calc_parm_search(msec, decay_time_tbl);
+}
+
+/* search an index for specified time from given time table */
+static int
+calc_parm_search(int msec, short *table)
+{
+	int left = 1, right = 127, mid;
+	while (left < right) {
+		mid = (left + right) / 2;
+		if (msec < (int)table[mid])
+			left = mid + 1;
+		else
+			right = mid;
+	}
+	return left;
+}
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
+
+/*
+ * effects table
+ */
+
+/* set an effect value */
+#define FX_FLAG_OFF	0
+#define FX_FLAG_SET	1
+#define FX_FLAG_ADD	2
+
+#define FX_SET(rec,type,value) \
+	((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value))
+#define FX_ADD(rec,type,value) \
+	((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value))
+#define FX_UNSET(rec,type) \
+	((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0)
+
+/* check the effect value is set */
+#define FX_ON(rec,type)	((rec)->flags[type])
+
+#define PARM_BYTE	0
+#define PARM_WORD	1
+#define PARM_SIGN	2
+
+static struct PARM_DEFS {
+	int type;	/* byte or word */
+	int low, high;	/* value range */
+	fx_affect_func realtime;	/* realtime paramater change */
+} parm_defs[] = {
+	{PARM_WORD, 0, 0x8000, NULL},	/* env1 delay */
+	{PARM_BYTE, 1, 0x7f, NULL},	/* env1 attack */
+	{PARM_BYTE, 0, 0x7e, NULL},	/* env1 hold */
+	{PARM_BYTE, 1, 0x7f, NULL},	/* env1 decay */
+	{PARM_BYTE, 1, 0x7f, NULL},	/* env1 release */
+	{PARM_BYTE, 0, 0x7f, NULL},	/* env1 sustain */
+	{PARM_BYTE, 0, 0xff, NULL},	/* env1 pitch */
+	{PARM_BYTE, 0, 0xff, NULL},	/* env1 cutoff */
+
+	{PARM_WORD, 0, 0x8000, NULL},	/* env2 delay */
+	{PARM_BYTE, 1, 0x7f, NULL},	/* env2 attack */
+	{PARM_BYTE, 0, 0x7e, NULL},	/* env2 hold */
+	{PARM_BYTE, 1, 0x7f, NULL},	/* env2 decay */
+	{PARM_BYTE, 1, 0x7f, NULL},	/* env2 release */
+	{PARM_BYTE, 0, 0x7f, NULL},	/* env2 sustain */
+
+	{PARM_WORD, 0, 0x8000, NULL},	/* lfo1 delay */
+	{PARM_BYTE, 0, 0xff, awe_fx_tremfrq},	/* lfo1 freq */
+	{PARM_SIGN, -128, 127, awe_fx_tremfrq},	/* lfo1 volume */
+	{PARM_SIGN, -128, 127, awe_fx_fmmod},	/* lfo1 pitch */
+	{PARM_BYTE, 0, 0xff, awe_fx_fmmod},	/* lfo1 cutoff */
+
+	{PARM_WORD, 0, 0x8000, NULL},	/* lfo2 delay */
+	{PARM_BYTE, 0, 0xff, awe_fx_fm2frq2},	/* lfo2 freq */
+	{PARM_SIGN, -128, 127, awe_fx_fm2frq2},	/* lfo2 pitch */
+
+	{PARM_WORD, 0, 0xffff, awe_set_voice_pitch},	/* initial pitch */
+	{PARM_BYTE, 0, 0xff, NULL},	/* chorus */
+	{PARM_BYTE, 0, 0xff, NULL},	/* reverb */
+	{PARM_BYTE, 0, 0xff, awe_set_volume},	/* initial cutoff */
+	{PARM_BYTE, 0, 15, awe_fx_filterQ},	/* initial resonance */
+
+	{PARM_WORD, 0, 0xffff, NULL},	/* sample start */
+	{PARM_WORD, 0, 0xffff, NULL},	/* loop start */
+	{PARM_WORD, 0, 0xffff, NULL},	/* loop end */
+	{PARM_WORD, 0, 0xffff, NULL},	/* coarse sample start */
+	{PARM_WORD, 0, 0xffff, NULL},	/* coarse loop start */
+	{PARM_WORD, 0, 0xffff, NULL},	/* coarse loop end */
+	{PARM_BYTE, 0, 0xff, awe_set_volume},	/* initial attenuation */
+};
+
+
+static unsigned char
+FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value)
+{
+	int effect = 0;
+	int on = 0;
+	if (lay && (on = FX_ON(lay, type)) != 0)
+		effect = lay->val[type];
+	if (!on && (on = FX_ON(rec, type)) != 0)
+		effect = rec->val[type];
+	if (on == FX_FLAG_ADD) {
+		if (parm_defs[type].type == PARM_SIGN) {
+			if (value > 0x7f)
+				effect += (int)value - 0x100;
+			else
+				effect += (int)value;
+		} else {
+			effect += (int)value;
+		}
+	}
+	if (on) {
+		if (effect < parm_defs[type].low)
+			effect = parm_defs[type].low;
+		else if (effect > parm_defs[type].high)
+			effect = parm_defs[type].high;
+		return (unsigned char)effect;
+	}
+	return value;
+}
+
+/* get word effect value */
+static unsigned short
+FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value)
+{
+	int effect = 0;
+	int on = 0;
+	if (lay && (on = FX_ON(lay, type)) != 0)
+		effect = lay->val[type];
+	if (!on && (on = FX_ON(rec, type)) != 0)
+		effect = rec->val[type];
+	if (on == FX_FLAG_ADD)
+		effect += (int)value;
+	if (on) {
+		if (effect < parm_defs[type].low)
+			effect = parm_defs[type].low;
+		else if (effect > parm_defs[type].high)
+			effect = parm_defs[type].high;
+		return (unsigned short)effect;
+	}
+	return value;
+}
+
+/* get word (upper=type1/lower=type2) effect value */
+static unsigned short
+FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value)
+{
+	unsigned short tmp;
+	tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8));
+	tmp <<= 8;
+	tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff));
+	return tmp;
+}
+
+/* address offset */
+static int
+FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode)
+{
+	int addr = 0;
+	if (lay && FX_ON(lay, hi))
+		addr = (short)lay->val[hi];
+	else if (FX_ON(rec, hi))
+		addr = (short)rec->val[hi];
+	addr = addr << 15;
+	if (lay && FX_ON(lay, lo))
+		addr += (short)lay->val[lo];
+	else if (FX_ON(rec, lo))
+		addr += (short)rec->val[lo];
+	if (!(mode & AWE_SAMPLE_8BITS))
+		addr /= 2;
+	return addr;
+}
+
+
+/*
+ * turn on/off sample
+ */
+
+/* table for volume target calculation */
+static unsigned short voltarget[16] = { 
+   0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58,
+   0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90
+};
+
+static void
+awe_note_on(int voice)
+{
+	unsigned int temp;
+	int addr;
+	int vtarget, ftarget, ptarget, pitch;
+	awe_voice_info *vp;
+	awe_voice_parm_block *parm;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	/* A voice sample must assigned before calling */
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+
+	parm = (awe_voice_parm_block*)&vp->parm;
+
+	/* channel to be silent and idle */
+	awe_poke(AWE_DCYSUSV(voice), 0x0080);
+	awe_poke(AWE_VTFT(voice), 0x0000FFFF);
+	awe_poke(AWE_CVCF(voice), 0x0000FFFF);
+	awe_poke(AWE_PTRX(voice), 0);
+	awe_poke(AWE_CPF(voice), 0);
+
+	/* set pitch offset */
+	awe_set_pitch(voice, TRUE);
+
+	/* modulation & volume envelope */
+	if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) {
+		awe_poke(AWE_ENVVAL(voice), 0xBFFF);
+		pitch = (parm->env1pit<<4) + voices[voice].apitch;
+		if (pitch > 0xffff) pitch = 0xffff;
+		/* calculate filter target */
+		ftarget = parm->cutoff + parm->env1fc;
+		limitvalue(ftarget, 0, 255);
+		ftarget <<= 8;
+	} else {
+		awe_poke(AWE_ENVVAL(voice),
+			 FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay));
+		ftarget = parm->cutoff;
+		ftarget <<= 8;
+		pitch = voices[voice].apitch;
+	}
+
+	/* calcualte pitch target */
+	if (pitch != 0xffff) {
+		ptarget = 1 << (pitch >> 12);
+		if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710;
+		if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710;
+		if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710;
+		ptarget += (ptarget>>1);
+		if (ptarget > 0xffff) ptarget = 0xffff;
+
+	} else ptarget = 0xffff;
+	if (parm->modatk >= 0x80)
+		awe_poke(AWE_ATKHLD(voice),
+			 FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f);
+	else
+		awe_poke(AWE_ATKHLD(voice),
+			 FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
+				 vp->parm.modatkhld));
+	awe_poke(AWE_DCYSUS(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+			  vp->parm.moddcysus));
+
+	if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) {
+		awe_poke(AWE_ENVVOL(voice), 0xBFFF);
+		vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4);
+	} else {
+		awe_poke(AWE_ENVVOL(voice),
+			 FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+		vtarget = 0;
+	}
+	if (parm->volatk >= 0x80)
+		awe_poke(AWE_ATKHLDV(voice),
+			 FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f);
+	else
+		awe_poke(AWE_ATKHLDV(voice),
+			 FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
+			 vp->parm.volatkhld));
+	/* decay/sustain parameter for volume envelope must be set at last */
+
+	/* cutoff and volume */
+	awe_set_volume(voice, TRUE);
+
+	/* modulation envelope heights */
+	awe_poke(AWE_PEFE(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+			 vp->parm.pefe));
+
+	/* lfo1/2 delay */
+	awe_poke(AWE_LFO1VAL(voice),
+		 FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+	awe_poke(AWE_LFO2VAL(voice),
+		 FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+
+	/* lfo1 pitch & cutoff shift */
+	awe_fx_fmmod(voice, TRUE);
+	/* lfo1 volume & freq */
+	awe_fx_tremfrq(voice, TRUE);
+	/* lfo2 pitch & freq */
+	awe_fx_fm2frq2(voice, TRUE);
+	/* pan & loop start */
+	awe_set_pan(voice, TRUE);
+
+	/* chorus & loop end (chorus 8bit, MSB) */
+	addr = vp->loopend - 1;
+	addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END,
+			  AWE_FX_COARSE_LOOP_END, vp->mode);
+	temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus);
+	temp = (temp <<24) | (unsigned int)addr;
+	awe_poke_dw(AWE_CSL(voice), temp);
+	DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr));
+
+	/* Q & current address (Q 4bit value, MSB) */
+	addr = vp->start - 1;
+	addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START,
+			  AWE_FX_COARSE_SAMPLE_START, vp->mode);
+	temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ);
+	temp = (temp<<28) | (unsigned int)addr;
+	awe_poke_dw(AWE_CCCA(voice), temp);
+	DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr));
+
+	/* clear unknown registers */
+	awe_poke_dw(AWE_00A0(voice), 0);
+	awe_poke_dw(AWE_0080(voice), 0);
+
+	/* reset volume */
+	awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget);
+	awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget);
+
+	/* set reverb */
+	temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb);
+	temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux;
+	awe_poke_dw(AWE_PTRX(voice), temp);
+	awe_poke_dw(AWE_CPF(voice), ptarget << 16);
+	/* turn on envelope */
+	awe_poke(AWE_DCYSUSV(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+			  vp->parm.voldcysus));
+
+	voices[voice].state = AWE_ST_ON;
+
+	/* clear voice position for the next note on this channel */
+	if (SINGLE_LAYER_MODE()) {
+		FX_UNSET(fx, AWE_FX_SAMPLE_START);
+		FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START);
+	}
+}
+
+
+/* turn off the voice */
+static void
+awe_note_off(int voice)
+{
+	awe_voice_info *vp;
+	unsigned short tmp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if ((vp = voices[voice].sample) == NULL) {
+		voices[voice].state = AWE_ST_OFF;
+		return;
+	}
+
+	tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE,
+			       (unsigned char)vp->parm.modrelease);
+	awe_poke(AWE_DCYSUS(voice), tmp);
+	tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE,
+			       (unsigned char)vp->parm.volrelease);
+	awe_poke(AWE_DCYSUSV(voice), tmp);
+	voices[voice].state = AWE_ST_RELEASED;
+}
+
+/* force to terminate the voice (no releasing echo) */
+static void
+awe_terminate(int voice)
+{
+	awe_poke(AWE_DCYSUSV(voice), 0x807F);
+	awe_tweak_voice(voice);
+	voices[voice].state = AWE_ST_OFF;
+}
+
+/* turn off other voices with the same exclusive class (for drums) */
+static void
+awe_exclusive_off(int voice)
+{
+	int i, exclass;
+
+	if (voices[voice].sample == NULL)
+		return;
+	if ((exclass = voices[voice].sample->exclusiveClass) == 0)
+		return;	/* not exclusive */
+
+	/* turn off voices with the same class */
+	for (i = 0; i < awe_max_voices; i++) {
+		if (i != voice && IS_PLAYING(i) &&
+		    voices[i].sample && voices[i].ch == voices[voice].ch &&
+		    voices[i].sample->exclusiveClass == exclass) {
+			DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
+			awe_terminate(i);
+			awe_voice_init(i, TRUE);
+		}
+	}
+}
+
+
+/*
+ * change the parameters of an audible voice
+ */
+
+/* change pitch */
+static void
+awe_set_pitch(int voice, int forced)
+{
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	awe_poke(AWE_IP(voice), voices[voice].apitch);
+	DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+}
+
+/* calculate & change pitch */
+static void
+awe_set_voice_pitch(int voice, int forced)
+{
+	awe_calc_pitch(voice);
+	awe_set_pitch(voice, forced);
+}
+
+/* change volume & cutoff */
+static void
+awe_set_volume(int voice, int forced)
+{
+	awe_voice_info *vp;
+	unsigned short tmp2;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if (!IS_PLAYING(voice) && !forced) return;
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+
+	tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF,
+		       (unsigned char)voices[voice].acutoff);
+	tmp2 = (tmp2 << 8);
+	tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN,
+			(unsigned char)voices[voice].avol);
+	awe_poke(AWE_IFATN(voice), tmp2);
+}
+
+/* calculate & change volume */
+static void
+awe_set_voice_vol(int voice, int forced)
+{
+	if (IS_EMPTY(voice))
+		return;
+	awe_calc_volume(voice);
+	awe_set_volume(voice, forced);
+}
+
+
+/* change pan; this could make a click noise.. */
+static void
+awe_set_pan(int voice, int forced)
+{
+	unsigned int temp;
+	int addr;
+	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+
+	/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
+	if (vp->fixpan > 0)	/* 0-127 */
+		temp = 255 - (int)vp->fixpan * 2;
+	else {
+		int pos = 0;
+		if (vp->pan >= 0) /* 0-127 */
+			pos = (int)vp->pan * 2 - 128;
+		pos += voices[voice].cinfo->panning; /* -128 - 127 */
+		temp = 127 - pos;
+	}
+	limitvalue(temp, 0, 255);
+	if (ctrls[AWE_MD_PAN_EXCHANGE]) {
+		temp = 255 - temp;
+	}
+	if (forced || temp != voices[voice].apan) {
+		voices[voice].apan = temp;
+		if (temp == 0)
+			voices[voice].aaux = 0xff;
+		else
+			voices[voice].aaux = (-temp) & 0xff;
+		addr = vp->loopstart - 1;
+		addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START,
+				  AWE_FX_COARSE_LOOP_START, vp->mode);
+		temp = (temp<<24) | (unsigned int)addr;
+		awe_poke_dw(AWE_PSST(voice), temp);
+		DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr));
+	}
+}
+
+/* effects change during playing */
+static void
+awe_fx_fmmod(int voice, int forced)
+{
+	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+	awe_poke(AWE_FMMOD(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+			 vp->parm.fmmod));
+}
+
+/* set tremolo (lfo1) volume & frequency */
+static void
+awe_fx_tremfrq(int voice, int forced)
+{
+	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+	awe_poke(AWE_TREMFRQ(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+			 vp->parm.tremfrq));
+}
+
+/* set lfo2 pitch & frequency */
+static void
+awe_fx_fm2frq2(int voice, int forced)
+{
+	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+	awe_poke(AWE_FM2FRQ2(voice),
+		 FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+			 vp->parm.fm2frq2));
+}
+
+
+/* Q & current address (Q 4bit value, MSB) */
+static void
+awe_fx_filterQ(int voice, int forced)
+{
+	unsigned int addr;
+	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+		return;
+
+	addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff;
+	addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28);
+	awe_poke_dw(AWE_CCCA(voice), addr);
+}
+
+/*
+ * calculate pitch offset
+ *
+ * 0xE000 is no pitch offset at 44100Hz sample.
+ * Every 4096 is one octave.
+ */
+
+static void
+awe_calc_pitch(int voice)
+{
+	voice_info *vp = &voices[voice];
+	awe_voice_info *ap;
+	awe_chan_info *cp = voices[voice].cinfo;
+	int offset;
+
+	/* search voice information */
+	if ((ap = vp->sample) == NULL)
+			return;
+	if (ap->index == 0) {
+		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
+		if (awe_set_sample((awe_voice_list*)ap) == 0)
+			return;
+	}
+
+	/* calculate offset */
+	if (ap->fixkey >= 0) {
+		DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune));
+		offset = (ap->fixkey - ap->root) * 4096 / 12;
+	} else {
+		DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune));
+		offset = (vp->note - ap->root) * 4096 / 12;
+		DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset));
+	}
+	offset = (offset * ap->scaleTuning) / 100;
+	DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
+	offset += ap->tune * 4096 / 1200;
+	DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
+	if (cp->bender != 0) {
+		DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender));
+		/* (819200: 1 semitone) ==> (4096: 12 semitones) */
+		offset += cp->bender * cp->bender_range / 2400;
+	}
+
+	/* add initial pitch correction */
+	if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH))
+		offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH];
+	else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH))
+		offset += cp->fx.val[AWE_FX_INIT_PITCH];
+
+	/* 0xe000: root pitch */
+	vp->apitch = 0xe000 + ap->rate_offset + offset;
+	DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset));
+	if (vp->apitch > 0xffff)
+		vp->apitch = 0xffff;
+	if (vp->apitch < 0)
+		vp->apitch = 0;
+}
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* calculate MIDI key and semitone from the specified frequency */
+static void
+awe_calc_pitch_from_freq(int voice, int freq)
+{
+	voice_info *vp = &voices[voice];
+	awe_voice_info *ap;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	int offset;
+	int note;
+
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+	/* search voice information */
+	if ((ap = vp->sample) == NULL)
+		return;
+	if (ap->index == 0) {
+		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
+		if (awe_set_sample((awe_voice_list*)ap) == 0)
+			return;
+	}
+	note = freq_to_note(freq);
+	offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
+	offset = (offset * ap->scaleTuning) / 100;
+	if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH))
+		offset += fx_lay->val[AWE_FX_INIT_PITCH];
+	else if (FX_ON(fx, AWE_FX_INIT_PITCH))
+		offset += fx->val[AWE_FX_INIT_PITCH];
+	vp->apitch = 0xe000 + ap->rate_offset + offset;
+	if (vp->apitch > 0xffff)
+		vp->apitch = 0xffff;
+	if (vp->apitch < 0)
+		vp->apitch = 0;
+}
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
+
+/*
+ * calculate volume attenuation
+ *
+ * Voice volume is controlled by volume attenuation parameter.
+ * So volume becomes maximum when avol is 0 (no attenuation), and
+ * minimum when 255 (-96dB or silence).
+ */
+
+static int vol_table[128] = {
+	255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
+	47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
+	31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
+	22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
+	15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
+	10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
+	6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
+	2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
+};
+
+/* tables for volume->attenuation calculation */
+static unsigned char voltab1[128] = {
+   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+   0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
+   0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
+   0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
+   0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char voltab2[128] = {
+   0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
+   0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
+   0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
+   0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
+   0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char expressiontab[128] = {
+   0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
+   0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
+   0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
+   0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
+   0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
+   0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
+   0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
+   0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
+   0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
+   0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void
+awe_calc_volume(int voice)
+{
+	voice_info *vp = &voices[voice];
+	awe_voice_info *ap;
+	awe_chan_info *cp = voices[voice].cinfo;
+	int vol;
+
+	/* search voice information */
+	if ((ap = vp->sample) == NULL)
+		return;
+
+	ap = vp->sample;
+	if (ap->index == 0) {
+		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
+		if (awe_set_sample((awe_voice_list*)ap) == 0)
+			return;
+	}
+	
+	if (ctrls[AWE_MD_NEW_VOLUME_CALC]) {
+		int main_vol = cp->main_vol * ap->amplitude / 127;
+		limitvalue(vp->velocity, 0, 127);
+		limitvalue(main_vol, 0, 127);
+		limitvalue(cp->expression_vol, 0, 127);
+
+		vol = voltab1[main_vol] + voltab2[vp->velocity];
+		vol = (vol * 8) / 3;
+		vol += ap->attenuation;
+		if (cp->expression_vol < 127)
+			vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128;
+		vol += atten_offset;
+		if (atten_relative)
+			vol += ctrls[AWE_MD_ZERO_ATTEN];
+		limitvalue(vol, 0, 255);
+		vp->avol = vol;
+		
+	} else {
+		/* 0 - 127 */
+		vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
+		vol = vol * ap->amplitude / 127;
+
+		if (vol < 0) vol = 0;
+		if (vol > 127) vol = 127;
+
+		/* calc to attenuation */
+		vol = vol_table[vol];
+		vol += (int)ap->attenuation;
+		vol += atten_offset;
+		if (atten_relative)
+			vol += ctrls[AWE_MD_ZERO_ATTEN];
+		if (vol > 255) vol = 255;
+
+		vp->avol = vol;
+	}
+	if (cp->bank !=  AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) {
+		int atten;
+		if (vp->velocity < 70) atten = 70;
+		else atten = vp->velocity;
+		vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7;
+	} else {
+		vp->acutoff = ap->parm.cutoff;
+	}
+	DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol));
+}
+
+/* change master volume */
+static void
+awe_change_master_volume(short val)
+{
+	limitvalue(val, 0, 127);
+	atten_offset = vol_table[val];
+	atten_relative = TRUE;
+	awe_update_volume();
+}
+
+/* update volumes of all available channels */
+static void awe_update_volume(void)
+{
+	int i;
+	for (i = 0; i < awe_max_voices; i++)
+		awe_set_voice_vol(i, TRUE);
+}
+
+/* set sostenuto on */
+static void awe_sostenuto_on(int voice, int forced)
+{
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	voices[voice].sostenuto = 127;
+}
+
+
+/* drop sustain */
+static void awe_sustain_off(int voice, int forced)
+{
+	if (voices[voice].state == AWE_ST_SUSTAINED) {
+		awe_note_off(voice);
+		awe_fx_init(voices[voice].ch);
+		awe_voice_init(voice, FALSE);
+	}
+}
+
+
+/* terminate and initialize voice */
+static void awe_terminate_and_init(int voice, int forced)
+{
+	awe_terminate(voice);
+	awe_fx_init(voices[voice].ch);
+	awe_voice_init(voice, TRUE);
+}
+
+
+/*
+ * synth operation routines
+ */
+
+#define AWE_VOICE_KEY(v)	(0x8000 | (v))
+#define AWE_CHAN_KEY(c,n)	(((c) << 8) | ((n) + 1))
+#define KEY_CHAN_MATCH(key,c)	(((key) >> 8) == (c))
+
+/* initialize the voice */
+static void
+awe_voice_init(int voice, int init_all)
+{
+	voice_info *vp = &voices[voice];
+
+	/* reset voice search key */
+	if (playing_mode == AWE_PLAY_DIRECT)
+		vp->key = AWE_VOICE_KEY(voice);
+	else
+		vp->key = 0;
+
+	/* clear voice mapping */
+	voice_alloc->map[voice] = 0;
+
+	/* touch the timing flag */
+	vp->time = current_alloc_time;
+
+	/* initialize other parameters if necessary */
+	if (init_all) {
+		vp->note = -1;
+		vp->velocity = 0;
+		vp->sostenuto = 0;
+
+		vp->sample = NULL;
+		vp->cinfo = &channels[voice];
+		vp->ch = voice;
+		vp->state = AWE_ST_OFF;
+
+		/* emu8000 parameters */
+		vp->apitch = 0;
+		vp->avol = 255;
+		vp->apan = -1;
+	}
+}
+
+/* clear effects */
+static void awe_fx_init(int ch)
+{
+	if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) {
+		memset(&channels[ch].fx, 0, sizeof(channels[ch].fx));
+		memset(&channels[ch].fx_layer, 0, sizeof(&channels[ch].fx_layer));
+	}
+}
+
+/* initialize channel info */
+static void awe_channel_init(int ch, int init_all)
+{
+	awe_chan_info *cp = &channels[ch];
+	cp->channel = ch;
+	if (init_all) {
+		cp->panning = 0; /* zero center */
+		cp->bender_range = 200; /* sense * 100 */
+		cp->main_vol = 127;
+		if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) {
+			cp->instr = ctrls[AWE_MD_DEF_DRUM];
+			cp->bank = AWE_DRUM_BANK;
+		} else {
+			cp->instr = ctrls[AWE_MD_DEF_PRESET];
+			cp->bank = ctrls[AWE_MD_DEF_BANK];
+		}
+	}
+
+	cp->bender = 0; /* zero tune skew */
+	cp->expression_vol = 127;
+	cp->chan_press = 0;
+	cp->sustained = 0;
+
+	if (! ctrls[AWE_MD_KEEP_EFFECT]) {
+		memset(&cp->fx, 0, sizeof(cp->fx));
+		memset(&cp->fx_layer, 0, sizeof(cp->fx_layer));
+	}
+}
+
+
+/* change the voice parameters; voice = channel */
+static void awe_voice_change(int voice, fx_affect_func func)
+{
+	int i; 
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+		func(voice, FALSE);
+		break;
+	case AWE_PLAY_INDIRECT:
+		for (i = 0; i < awe_max_voices; i++)
+			if (voices[i].key == AWE_VOICE_KEY(voice))
+				func(i, FALSE);
+		break;
+	default:
+		for (i = 0; i < awe_max_voices; i++)
+			if (KEY_CHAN_MATCH(voices[i].key, voice))
+				func(i, FALSE);
+		break;
+	}
+}
+
+
+/*
+ * device open / close
+ */
+
+/* open device:
+ *   reset status of all voices, and clear sample position flag
+ */
+static int
+awe_open(int dev, int mode)
+{
+	if (awe_busy)
+		return -EBUSY;
+
+	awe_busy = TRUE;
+
+	/* set default mode */
+	awe_init_ctrl_parms(FALSE);
+	atten_relative = TRUE;
+	atten_offset = 0;
+	drum_flags = DEFAULT_DRUM_FLAGS;
+	playing_mode = AWE_PLAY_INDIRECT;
+
+	/* reset voices & channels */
+	awe_reset(dev);
+
+	patch_opened = 0;
+
+	return 0;
+}
+
+
+/* close device:
+ *   reset all voices again (terminate sounds)
+ */
+static void
+awe_close(int dev)
+{
+	awe_reset(dev);
+	awe_busy = FALSE;
+}
+
+
+/* set miscellaneous mode parameters
+ */
+static void
+awe_init_ctrl_parms(int init_all)
+{
+	int i;
+	for (i = 0; i < AWE_MD_END; i++) {
+		if (init_all || ctrl_parms[i].init_each_time)
+			ctrls[i] = ctrl_parms[i].value;
+	}
+}
+
+
+/* sequencer I/O control:
+ */
+static int
+awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	switch (cmd) {
+	case SNDCTL_SYNTH_INFO:
+		if (playing_mode == AWE_PLAY_DIRECT)
+			awe_info.nr_voices = awe_max_voices;
+		else
+			awe_info.nr_voices = AWE_MAX_CHANNELS;
+		memcpy((char*)arg, &awe_info, sizeof(awe_info));
+		return 0;
+		break;
+
+	case SNDCTL_SEQ_RESETSAMPLES:
+		awe_reset(dev);
+		awe_reset_samples();
+		return 0;
+		break;
+
+	case SNDCTL_SEQ_PERCMODE:
+		/* what's this? */
+		return 0;
+		break;
+
+	case SNDCTL_SYNTH_MEMAVL:
+		return memsize - awe_free_mem_ptr() * 2;
+
+	default:
+		printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd);
+		return -EINVAL;
+	}
+}
+
+
+static int voice_in_range(int voice)
+{
+	if (playing_mode == AWE_PLAY_DIRECT) {
+		if (voice < 0 || voice >= awe_max_voices)
+			return FALSE;
+	} else {
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static void release_voice(int voice, int do_sustain)
+{
+	if (IS_NO_SOUND(voice))
+		return;
+	if (do_sustain && (voices[voice].cinfo->sustained == 127 ||
+			    voices[voice].sostenuto == 127))
+		voices[voice].state = AWE_ST_SUSTAINED;
+	else {
+		awe_note_off(voice);
+		awe_fx_init(voices[voice].ch);
+		awe_voice_init(voice, FALSE);
+	}
+}
+
+/* release all notes */
+static void awe_note_off_all(int do_sustain)
+{
+	int i;
+	for (i = 0; i < awe_max_voices; i++)
+		release_voice(i, do_sustain);
+}
+
+/* kill a voice:
+ *   not terminate, just release the voice.
+ */
+static int
+awe_kill_note(int dev, int voice, int note, int velocity)
+{
+	int i, v2, key;
+
+	DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
+	if (! voice_in_range(voice))
+		return -EINVAL;
+
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+	case AWE_PLAY_INDIRECT:
+		key = AWE_VOICE_KEY(voice);
+		break;
+
+	case AWE_PLAY_MULTI2:
+		v2 = voice_alloc->map[voice] >> 8;
+		voice_alloc->map[voice] = 0;
+		voice = v2;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return -EINVAL;
+		/* continue to below */
+	default:
+		key = AWE_CHAN_KEY(voice, note);
+		break;
+	}
+
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key)
+			release_voice(i, TRUE);
+	}
+	return 0;
+}
+
+
+static void start_or_volume_change(int voice, int velocity)
+{
+	voices[voice].velocity = velocity;
+	awe_calc_volume(voice);
+	if (voices[voice].state == AWE_ST_STANDBY)
+		awe_note_on(voice);
+	else if (voices[voice].state == AWE_ST_ON)
+		awe_set_volume(voice, FALSE);
+}
+
+static void set_and_start_voice(int voice, int state)
+{
+	/* calculate pitch & volume parameters */
+	voices[voice].state = state;
+	awe_calc_pitch(voice);
+	awe_calc_volume(voice);
+	if (state == AWE_ST_ON)
+		awe_note_on(voice);
+}
+
+/* start a voice:
+ *   if note is 255, identical with aftertouch function.
+ *   Otherwise, start a voice with specified not and volume.
+ */
+static int
+awe_start_note(int dev, int voice, int note, int velocity)
+{
+	int i, key, state, volonly;
+
+	DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
+	if (! voice_in_range(voice))
+		return -EINVAL;
+	    
+	if (velocity == 0)
+		state = AWE_ST_STANDBY; /* stand by for playing */
+	else
+		state = AWE_ST_ON;	/* really play */
+	volonly = FALSE;
+
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+	case AWE_PLAY_INDIRECT:
+		key = AWE_VOICE_KEY(voice);
+		if (note == 255)
+			volonly = TRUE;
+		break;
+
+	case AWE_PLAY_MULTI2:
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return -EINVAL;
+		/* continue to below */
+	default:
+		if (note >= 128) { /* key volume mode */
+			note -= 128;
+			volonly = TRUE;
+		}
+		key = AWE_CHAN_KEY(voice, note);
+		break;
+	}
+
+	/* dynamic volume change */
+	if (volonly) {
+		for (i = 0; i < awe_max_voices; i++) {
+			if (voices[i].key == key)
+				start_or_volume_change(i, velocity);
+		}
+		return 0;
+	}
+
+	/* if the same note still playing, stop it */
+	if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) {
+		for (i = 0; i < awe_max_voices; i++)
+			if (voices[i].key == key) {
+				if (voices[i].state == AWE_ST_ON) {
+					awe_note_off(i);
+					awe_voice_init(i, FALSE);
+				} else if (voices[i].state == AWE_ST_STANDBY)
+					awe_voice_init(i, TRUE);
+			}
+	}
+
+	/* allocate voices */
+	if (playing_mode == AWE_PLAY_DIRECT)
+		awe_alloc_one_voice(voice, note, velocity);
+	else
+		awe_alloc_multi_voices(voice, note, velocity, key);
+
+	/* turn off other voices exlusively (for drums) */
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].key == key)
+			awe_exclusive_off(i);
+
+	/* set up pitch and volume parameters */
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key && voices[i].state == AWE_ST_OFF)
+			set_and_start_voice(i, state);
+	}
+
+	return 0;
+}
+
+
+/* calculate hash key */
+static int
+awe_search_key(int bank, int preset, int note)
+{
+	unsigned int key;
+
+#if 1 /* new hash table */
+	if (bank == AWE_DRUM_BANK)
+		key = preset + note + 128;
+	else
+		key = bank + preset;
+#else
+	key = preset;
+#endif
+	key %= AWE_MAX_PRESETS;
+
+	return (int)key;
+}
+
+
+/* search instrument from hash table */
+static awe_voice_list *
+awe_search_instr(int bank, int preset, int note)
+{
+	awe_voice_list *p;
+	int key, key2;
+
+	key = awe_search_key(bank, preset, note);
+	for (p = preset_table[key]; p; p = p->next_bank) {
+		if (p->instr == preset && p->bank == bank)
+			return p;
+	}
+	key2 = awe_search_key(bank, preset, 0); /* search default */
+	if (key == key2)
+		return NULL;
+	for (p = preset_table[key2]; p; p = p->next_bank) {
+		if (p->instr == preset && p->bank == bank)
+			return p;
+	}
+	return NULL;
+}
+
+
+/* assign the instrument to a voice */
+static int
+awe_set_instr_2(int dev, int voice, int instr_no)
+{
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return -EINVAL;
+	}
+	return awe_set_instr(dev, voice, instr_no);
+}
+
+/* assign the instrument to a channel; voice is the channel number */
+static int
+awe_set_instr(int dev, int voice, int instr_no)
+{
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return -EINVAL;
+
+	if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
+		return -EINVAL;
+
+	cinfo = &channels[voice];
+	cinfo->instr = instr_no;
+	DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no));
+
+	return 0;
+}
+
+
+/* reset all voices; terminate sounds and initialize parameters */
+static void
+awe_reset(int dev)
+{
+	int i;
+	current_alloc_time = 0;
+	/* don't turn off voice 31 and 32.  they are used also for FM voices */
+	for (i = 0; i < awe_max_voices; i++) {
+		awe_terminate(i);
+		awe_voice_init(i, TRUE);
+	}
+	for (i = 0; i < AWE_MAX_CHANNELS; i++)
+		awe_channel_init(i, TRUE);
+	for (i = 0; i < 16; i++) {
+		awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
+		awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
+	}
+	awe_init_fm();
+	awe_tweak();
+}
+
+
+/* hardware specific control:
+ *   GUS specific and AWE32 specific controls are available.
+ */
+static void
+awe_hw_control(int dev, unsigned char *event)
+{
+	int cmd = event[2];
+	if (cmd & _AWE_MODE_FLAG)
+		awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+	else
+		awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#endif
+}
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* GUS compatible controls */
+static void
+awe_hw_gus_control(int dev, int cmd, unsigned char *event)
+{
+	int voice, i, key;
+	unsigned short p1;
+	short p2;
+	int plong;
+
+	if (MULTI_LAYER_MODE())
+		return;
+	if (cmd == _GUS_NUMVOICES)
+		return;
+
+	voice = event[3];
+	if (! voice_in_range(voice))
+		return;
+
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+	plong = *(int*) &event[4];
+
+	switch (cmd) {
+	case _GUS_VOICESAMPLE:
+		awe_set_instr(dev, voice, p1);
+		return;
+
+	case _GUS_VOICEBALA:
+		/* 0 to 15 --> -128 to 127 */
+		awe_panning(dev, voice, ((int)p1 << 4) - 128);
+		return;
+
+	case _GUS_VOICEVOL:
+	case _GUS_VOICEVOL2:
+		/* not supported yet */
+		return;
+
+	case _GUS_RAMPRANGE:
+	case _GUS_RAMPRATE:
+	case _GUS_RAMPMODE:
+	case _GUS_RAMPON:
+	case _GUS_RAMPOFF:
+		/* volume ramping not supported */
+		return;
+
+	case _GUS_VOLUME_SCALE:
+		return;
+
+	case _GUS_VOICE_POS:
+		FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START,
+		       (short)(plong & 0x7fff));
+		FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START,
+		       (plong >> 15) & 0xffff);
+		return;
+	}
+
+	key = AWE_VOICE_KEY(voice);
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key) {
+			switch (cmd) {
+			case _GUS_VOICEON:
+				awe_note_on(i);
+				break;
+
+			case _GUS_VOICEOFF:
+				awe_terminate(i);
+				awe_fx_init(voices[i].ch);
+				awe_voice_init(i, TRUE);
+				break;
+
+			case _GUS_VOICEFADE:
+				awe_note_off(i);
+				awe_fx_init(voices[i].ch);
+				awe_voice_init(i, FALSE);
+				break;
+
+			case _GUS_VOICEFREQ:
+				awe_calc_pitch_from_freq(i, plong);
+				break;
+			}
+		}
+	}
+}
+
+#endif /* gus_compat */
+
+
+/* AWE32 specific controls */
+static void
+awe_hw_awe_control(int dev, int cmd, unsigned char *event)
+{
+	int voice;
+	unsigned short p1;
+	short p2;
+	int i;
+
+	voice = event[3];
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+
+	switch (cmd) {
+	case _AWE_DEBUG_MODE:
+		ctrls[AWE_MD_DEBUG_MODE] = p1;
+		printk(KERN_DEBUG "AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]);
+		break;
+	case _AWE_REVERB_MODE:
+		ctrls[AWE_MD_REVERB_MODE] = p1;
+		awe_update_reverb_mode();
+		break;
+
+	case _AWE_CHORUS_MODE:
+		ctrls[AWE_MD_CHORUS_MODE] = p1;
+		awe_update_chorus_mode();
+		break;
+		      
+	case _AWE_REMOVE_LAST_SAMPLES:
+		DEBUG(0,printk("AWE32: remove last samples\n"));
+		awe_reset(0);
+		if (locked_sf_id > 0)
+			awe_remove_samples(locked_sf_id);
+		break;
+
+	case _AWE_INITIALIZE_CHIP:
+		awe_initialize();
+		break;
+
+	case _AWE_SEND_EFFECT:
+		i = -1;
+		if (p1 >= 0x100) {
+			i = (p1 >> 8);
+			if (i < 0 || i >= MAX_LAYERS)
+				break;
+		}
+		awe_send_effect(voice, i, p1, p2);
+		break;
+
+	case _AWE_RESET_CHANNEL:
+		awe_channel_init(voice, !p1);
+		break;
+		
+	case _AWE_TERMINATE_ALL:
+		awe_reset(0);
+		break;
+
+	case _AWE_TERMINATE_CHANNEL:
+		awe_voice_change(voice, awe_terminate_and_init);
+		break;
+
+	case _AWE_RELEASE_ALL:
+		awe_note_off_all(FALSE);
+		break;
+	case _AWE_NOTEOFF_ALL:
+		awe_note_off_all(TRUE);
+		break;
+
+	case _AWE_INITIAL_VOLUME:
+		DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
+		atten_relative = (char)p2;
+		atten_offset = (short)p1;
+		awe_update_volume();
+		break;
+
+	case _AWE_CHN_PRESSURE:
+		channels[voice].chan_press = p1;
+		awe_modwheel_change(voice, p1);
+		break;
+
+	case _AWE_CHANNEL_MODE:
+		DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
+		playing_mode = p1;
+		awe_reset(0);
+		break;
+
+	case _AWE_DRUM_CHANNELS:
+		DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
+		drum_flags = *(unsigned int*)&event[4];
+		break;
+
+	case _AWE_MISC_MODE:
+		DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2));
+		if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) {
+			ctrls[p1] = p2;
+			if (ctrl_parms[p1].update)
+				ctrl_parms[p1].update();
+		}
+		break;
+
+	case _AWE_EQUALIZER:
+		ctrls[AWE_MD_BASS_LEVEL] = p1;
+		ctrls[AWE_MD_TREBLE_LEVEL] = p2;
+		awe_update_equalizer();
+		break;
+
+	default:
+		DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
+		break;
+	}
+}
+
+
+/* change effects */
+static void
+awe_send_effect(int voice, int layer, int type, int val)
+{
+	awe_chan_info *cinfo;
+	FX_Rec *fx;
+	int mode;
+
+	cinfo = &channels[voice];
+	if (layer >= 0 && layer < MAX_LAYERS)
+		fx = &cinfo->fx_layer[layer];
+	else
+		fx = &cinfo->fx;
+
+	if (type & 0x40)
+		mode = FX_FLAG_OFF;
+	else if (type & 0x80)
+		mode = FX_FLAG_ADD;
+	else
+		mode = FX_FLAG_SET;
+	type &= 0x3f;
+
+	if (type >= 0 && type < AWE_FX_END) {
+		DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val));
+		if (mode == FX_FLAG_SET)
+			FX_SET(fx, type, val);
+		else if (mode == FX_FLAG_ADD)
+			FX_ADD(fx, type, val);
+		else
+			FX_UNSET(fx, type);
+		if (mode != FX_FLAG_OFF && parm_defs[type].realtime) {
+			DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice));
+			awe_voice_change(voice, parm_defs[type].realtime);
+		}
+	}
+}
+
+
+/* change modulation wheel; voice is already mapped on multi2 mode */
+static void
+awe_modwheel_change(int voice, int value)
+{
+	int i;
+	awe_chan_info *cinfo;
+
+	cinfo = &channels[voice];
+	i = value * ctrls[AWE_MD_MOD_SENSE] / 1200;
+	FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i);
+	awe_voice_change(voice, awe_fx_fmmod);
+	FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i);
+	awe_voice_change(voice, awe_fx_fm2frq2);
+}
+
+
+/* voice pressure change */
+static void
+awe_aftertouch(int dev, int voice, int pressure)
+{
+	int note;
+
+	DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
+	if (! voice_in_range(voice))
+		return;
+
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+	case AWE_PLAY_INDIRECT:
+		awe_start_note(dev, voice, 255, pressure);
+		break;
+	case AWE_PLAY_MULTI2:
+		note = (voice_alloc->map[voice] & 0xff) - 1;
+		awe_key_pressure(dev, voice, note + 0x80, pressure);
+		break;
+	}
+}
+
+
+/* voice control change */
+static void
+awe_controller(int dev, int voice, int ctrl_num, int value)
+{
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	cinfo = &channels[voice];
+
+	switch (ctrl_num) {
+	case CTL_BANK_SELECT: /* MIDI control #0 */
+		DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
+		if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) &&
+		    !ctrls[AWE_MD_TOGGLE_DRUM_BANK])
+			break;
+		if (value < 0 || value > 255)
+			break;
+		cinfo->bank = value;
+		if (cinfo->bank == AWE_DRUM_BANK)
+			DRUM_CHANNEL_ON(cinfo->channel);
+		else
+			DRUM_CHANNEL_OFF(cinfo->channel);
+		awe_set_instr(dev, voice, cinfo->instr);
+		break;
+
+	case CTL_MODWHEEL: /* MIDI control #1 */
+		DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value));
+		awe_modwheel_change(voice, value);
+		break;
+
+	case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
+		DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
+		/* zero centered */
+		cinfo->bender = value;
+		awe_voice_change(voice, awe_set_voice_pitch);
+		break;
+
+	case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
+		DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
+		/* value = sense x 100 */
+		cinfo->bender_range = value;
+		/* no audible pitch change yet.. */
+		break;
+
+	case CTL_EXPRESSION: /* MIDI control #11 */
+		if (SINGLE_LAYER_MODE())
+			value /= 128;
+	case CTRL_EXPRESSION: /* SEQ1 V2 control */
+		DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
+		/* 0 - 127 */
+		cinfo->expression_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
+		break;
+
+	case CTL_PAN:	/* MIDI control #10 */
+		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
+		/* (0-127) -> signed 8bit */
+		cinfo->panning = value * 2 - 128;
+		if (ctrls[AWE_MD_REALTIME_PAN])
+			awe_voice_change(voice, awe_set_pan);
+		break;
+
+	case CTL_MAIN_VOLUME:	/* MIDI control #7 */
+		if (SINGLE_LAYER_MODE())
+			value = (value * 100) / 16383;
+	case CTRL_MAIN_VOLUME:	/* SEQ1 V2 control */
+		DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
+		/* 0 - 127 */
+		cinfo->main_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
+		break;
+
+	case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
+		DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
+		FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
+		break;
+
+	case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
+		DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
+		FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+		break;
+
+	case 120:  /* all sounds off */
+		awe_note_off_all(FALSE);
+		break;
+	case 123:  /* all notes off */
+		awe_note_off_all(TRUE);
+		break;
+
+	case CTL_SUSTAIN: /* MIDI control #64 */
+		cinfo->sustained = value;
+		if (value != 127)
+			awe_voice_change(voice, awe_sustain_off);
+		break;
+
+	case CTL_SOSTENUTO: /* MIDI control #66 */
+		if (value == 127)
+			awe_voice_change(voice, awe_sostenuto_on);
+		else
+			awe_voice_change(voice, awe_sustain_off);
+		break;
+
+	default:
+		DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
+			   voice, ctrl_num, value));
+		break;
+	}
+}
+
+
+/* voice pan change (value = -128 - 127) */
+static void
+awe_panning(int dev, int voice, int value)
+{
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	cinfo = &channels[voice];
+	cinfo->panning = value;
+	DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+	if (ctrls[AWE_MD_REALTIME_PAN])
+		awe_voice_change(voice, awe_set_pan);
+}
+
+
+/* volume mode change */
+static void
+awe_volume_method(int dev, int mode)
+{
+	/* not impremented */
+	DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
+}
+
+
+/* pitch wheel change: 0-16384 */
+static void
+awe_bender(int dev, int voice, int value)
+{
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	/* convert to zero centered value */
+	cinfo = &channels[voice];
+	cinfo->bender = value - 8192;
+	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+	awe_voice_change(voice, awe_set_voice_pitch);
+}
+
+
+/*
+ * load a sound patch:
+ *   three types of patches are accepted: AWE, GUS, and SYSEX.
+ */
+
+static int
+awe_load_patch(int dev, int format, const char *addr,
+	       int offs, int count, int pmgr_flag)
+{
+	awe_patch_info patch;
+	int rc = 0;
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+	if (format == GUS_PATCH) {
+		return awe_load_guspatch(addr, offs, count, pmgr_flag);
+	} else
+#endif
+	if (format == SYSEX_PATCH) {
+		/* no system exclusive message supported yet */
+		return 0;
+	} else if (format != AWE_PATCH) {
+		printk(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format);
+		return -EINVAL;
+	}
+	
+	if (count < AWE_PATCH_INFO_SIZE) {
+		printk(KERN_WARNING "AWE32 Error: Patch header too short\n");
+		return -EINVAL;
+	}
+	if (copy_from_user(((char*)&patch) + offs, addr + offs, 
+			   AWE_PATCH_INFO_SIZE - offs))
+		return -EFAULT;
+
+	count -= AWE_PATCH_INFO_SIZE;
+	if (count < patch.len) {
+		printk(KERN_WARNING "AWE32: sample: Patch record too short (%d<%d)\n",
+		       count, patch.len);
+		return -EINVAL;
+	}
+	
+	switch (patch.type) {
+	case AWE_LOAD_INFO:
+		rc = awe_load_info(&patch, addr, count);
+		break;
+	case AWE_LOAD_DATA:
+		rc = awe_load_data(&patch, addr, count);
+		break;
+	case AWE_OPEN_PATCH:
+		rc = awe_open_patch(&patch, addr, count);
+		break;
+	case AWE_CLOSE_PATCH:
+		rc = awe_close_patch(&patch, addr, count);
+		break;
+	case AWE_UNLOAD_PATCH:
+		rc = awe_unload_patch(&patch, addr, count);
+		break;
+	case AWE_REPLACE_DATA:
+		rc = awe_replace_data(&patch, addr, count);
+		break;
+	case AWE_MAP_PRESET:
+		rc = awe_load_map(&patch, addr, count);
+		break;
+	/* case AWE_PROBE_INFO:
+		rc = awe_probe_info(&patch, addr, count);
+		break;*/
+	case AWE_PROBE_DATA:
+		rc = awe_probe_data(&patch, addr, count);
+		break;
+	case AWE_REMOVE_INFO:
+		rc = awe_remove_info(&patch, addr, count);
+		break;
+	case AWE_LOAD_CHORUS_FX:
+		rc = awe_load_chorus_fx(&patch, addr, count);
+		break;
+	case AWE_LOAD_REVERB_FX:
+		rc = awe_load_reverb_fx(&patch, addr, count);
+		break;
+
+	default:
+		printk(KERN_WARNING "AWE32 Error: unknown patch format type %d\n",
+		       patch.type);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+
+/* create an sf list record */
+static int
+awe_create_sf(int type, char *name)
+{
+	sf_list *rec;
+
+	/* terminate sounds */
+	awe_reset(0);
+	rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL);
+	if (rec == NULL)
+		return 1; /* no memory */
+	rec->sf_id = current_sf_id + 1;
+	rec->type = type;
+	if (/*current_sf_id == 0 ||*/ (type & AWE_PAT_LOCKED) != 0)
+		locked_sf_id = current_sf_id + 1;
+	rec->num_info = awe_free_info();
+	rec->num_sample = awe_free_sample();
+	rec->mem_ptr = awe_free_mem_ptr();
+	rec->infos = rec->last_infos = NULL;
+	rec->samples = rec->last_samples = NULL;
+
+	/* add to linked-list */
+	rec->next = NULL;
+	rec->prev = sftail;
+	if (sftail)
+		sftail->next = rec;
+	else
+		sfhead = rec;
+	sftail = rec;
+	current_sf_id++;
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	rec->shared = NULL;
+	if (name)
+		memcpy(rec->name, name, AWE_PATCH_NAME_LEN);
+	else
+		strcpy(rec->name, "*TEMPORARY*");
+	if (current_sf_id > 1 && name && (type & AWE_PAT_SHARED) != 0) {
+		/* is the current font really a shared font? */
+		if (is_shared_sf(rec->name)) {
+			/* check if the shared font is already installed */
+			sf_list *p;
+			for (p = rec->prev; p; p = p->prev) {
+				if (is_identical_name(rec->name, p)) {
+					rec->shared = p;
+					break;
+				}
+			}
+		}
+	}
+#endif /* allow sharing */
+
+	return 0;
+}
+
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+
+/* check if the given name is a valid shared name */
+#define ASC_TO_KEY(c) ((c) - 'A' + 1)
+static int is_shared_sf(unsigned char *name)
+{
+	static unsigned char id_head[4] = {
+		ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'),
+		AWE_MAJOR_VERSION,
+	};
+	if (memcmp(name, id_head, 4) == 0)
+		return TRUE;
+	return FALSE;
+}
+
+/* check if the given name matches to the existing list */
+static int is_identical_name(unsigned char *name, sf_list *p) 
+{
+	char *id = p->name;
+	if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0)
+		return TRUE;
+	return FALSE;
+}
+
+/* check if the given voice info exists */
+static int info_duplicated(sf_list *sf, awe_voice_list *rec)
+{
+	/* search for all sharing lists */
+	for (; sf; sf = sf->shared) {
+		awe_voice_list *p;
+		for (p = sf->infos; p; p = p->next) {
+			if (p->type == V_ST_NORMAL &&
+			    p->bank == rec->bank &&
+			    p->instr == rec->instr &&
+			    p->v.low == rec->v.low &&
+			    p->v.high == rec->v.high &&
+			    p->v.sample == rec->v.sample)
+				return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+#endif /* AWE_ALLOW_SAMPLE_SHARING */
+
+
+/* free sf_list record */
+/* linked-list in this function is not cared */
+static void
+awe_free_sf(sf_list *sf)
+{
+	if (sf->infos) {
+		awe_voice_list *p, *next;
+		for (p = sf->infos; p; p = next) {
+			next = p->next;
+			kfree(p);
+		}
+	}
+	if (sf->samples) {
+		awe_sample_list *p, *next;
+		for (p = sf->samples; p; p = next) {
+			next = p->next;
+			kfree(p);
+		}
+	}
+	kfree(sf);
+}
+
+
+/* open patch; create sf list and set opened flag */
+static int
+awe_open_patch(awe_patch_info *patch, const char *addr, int count)
+{
+	awe_open_parm parm;
+	int shared;
+
+	if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm)))
+		return -EFAULT;
+	shared = FALSE;
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	if (sftail && (parm.type & AWE_PAT_SHARED) != 0) {
+		/* is the previous font the same font? */
+		if (is_identical_name(parm.name, sftail)) {
+			/* then append to the previous */
+			shared = TRUE;
+			awe_reset(0);
+			if (parm.type & AWE_PAT_LOCKED)
+				locked_sf_id = current_sf_id;
+		}
+	}
+#endif /* allow sharing */
+	if (! shared) {
+		if (awe_create_sf(parm.type, parm.name)) {
+			printk(KERN_ERR "AWE32: can't open: failed to alloc new list\n");
+			return -ENOMEM;
+		}
+	}
+	patch_opened = TRUE;
+	return current_sf_id;
+}
+
+/* check if the patch is already opened */
+static sf_list *
+check_patch_opened(int type, char *name)
+{
+	if (! patch_opened) {
+		if (awe_create_sf(type, name)) {
+			printk(KERN_ERR "AWE32: failed to alloc new list\n");
+			return NULL;
+		}
+		patch_opened = TRUE;
+		return sftail;
+	}
+	return sftail;
+}
+
+/* close the patch; if no voice is loaded, remove the patch */
+static int
+awe_close_patch(awe_patch_info *patch, const char *addr, int count)
+{
+	if (patch_opened && sftail) {
+		/* if no voice is loaded, release the current patch */
+		if (sftail->infos == NULL) {
+			awe_reset(0);
+			awe_remove_samples(current_sf_id - 1);
+		}
+	}
+	patch_opened = 0;
+	return 0;
+}
+
+
+/* remove the latest patch */
+static int
+awe_unload_patch(awe_patch_info *patch, const char *addr, int count)
+{
+	if (current_sf_id > 0 && current_sf_id > locked_sf_id) {
+		awe_reset(0);
+		awe_remove_samples(current_sf_id - 1);
+	}
+	return 0;
+}
+
+/* allocate voice info list records */
+static awe_voice_list *
+alloc_new_info(void)
+{
+	awe_voice_list *newlist;
+	
+	newlist = (awe_voice_list *)kmalloc(sizeof(*newlist), GFP_KERNEL);
+	if (newlist == NULL) {
+		printk(KERN_ERR "AWE32: can't alloc info table\n");
+		return NULL;
+	}
+	return newlist;
+}
+
+/* allocate sample info list records */
+static awe_sample_list *
+alloc_new_sample(void)
+{
+	awe_sample_list *newlist;
+	
+	newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL);
+	if (newlist == NULL) {
+		printk(KERN_ERR "AWE32: can't alloc sample table\n");
+		return NULL;
+	}
+	return newlist;
+}
+
+/* load voice map */
+static int
+awe_load_map(awe_patch_info *patch, const char *addr, int count)
+{
+	awe_voice_map map;
+	awe_voice_list *rec, *p;
+	sf_list *sf;
+
+	/* get the link info */
+	if (count < sizeof(map)) {
+		printk(KERN_WARNING "AWE32 Error: invalid patch info length\n");
+		return -EINVAL;
+	}
+	if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map)))
+		return -EFAULT;
+	
+	/* check if the identical mapping already exists */
+	p = awe_search_instr(map.map_bank, map.map_instr, map.map_key);
+	for (; p; p = p->next_instr) {
+		if (p->type == V_ST_MAPPED &&
+		    p->v.start == map.src_instr &&
+		    p->v.end == map.src_bank &&
+		    p->v.fixkey == map.src_key)
+			return 0; /* already present! */
+	}
+
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL)
+		return -ENOMEM;
+
+	if ((rec = alloc_new_info()) == NULL)
+		return -ENOMEM;
+
+	rec->bank = map.map_bank;
+	rec->instr = map.map_instr;
+	rec->type = V_ST_MAPPED;
+	rec->disabled = FALSE;
+	awe_init_voice_info(&rec->v);
+	if (map.map_key >= 0) {
+		rec->v.low = map.map_key;
+		rec->v.high = map.map_key;
+	}
+	rec->v.start = map.src_instr;
+	rec->v.end = map.src_bank;
+	rec->v.fixkey = map.src_key;
+	add_sf_info(sf, rec);
+	add_info_list(rec);
+
+	return 0;
+}
+
+#if 0
+/* probe preset in the current list -- nothing to be loaded */
+static int
+awe_probe_info(awe_patch_info *patch, const char *addr, int count)
+{
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	awe_voice_map map;
+	awe_voice_list *p;
+
+	if (! patch_opened)
+		return -EINVAL;
+
+	/* get the link info */
+	if (count < sizeof(map)) {
+		printk(KERN_WARNING "AWE32 Error: invalid patch info length\n");
+		return -EINVAL;
+	}
+	if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map)))
+		return -EFAULT;
+	
+	/* check if the identical mapping already exists */
+	if (sftail == NULL)
+		return -EINVAL;
+	p = awe_search_instr(map.src_bank, map.src_instr, map.src_key);
+	for (; p; p = p->next_instr) {
+		if (p->type == V_ST_NORMAL &&
+		    is_identical_holder(p->holder, sftail) &&
+		    p->v.low <= map.src_key &&
+		    p->v.high >= map.src_key)
+			return 0; /* already present! */
+	}
+#endif /* allow sharing */
+	return -EINVAL;
+}
+#endif
+
+/* probe sample in the current list -- nothing to be loaded */
+static int
+awe_probe_data(awe_patch_info *patch, const char *addr, int count)
+{
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	if (! patch_opened)
+		return -EINVAL;
+
+	/* search the specified sample by optarg */
+	if (search_sample_index(sftail, patch->optarg) != NULL)
+		return 0;
+#endif /* allow sharing */
+	return -EINVAL;
+}
+
+		
+/* remove the present instrument layers */
+static int
+remove_info(sf_list *sf, int bank, int instr)
+{
+	awe_voice_list *prev, *next, *p;
+	int removed = 0;
+
+	prev = NULL;
+	for (p = sf->infos; p; p = next) {
+		next = p->next;
+		if (p->type == V_ST_NORMAL &&
+		    p->bank == bank && p->instr == instr) {
+			/* remove this layer */
+			if (prev)
+				prev->next = next;
+			else
+				sf->infos = next;
+			if (p == sf->last_infos)
+				sf->last_infos = prev;
+			sf->num_info--;
+			removed++;
+			kfree(p);
+		} else
+			prev = p;
+	}
+	if (removed)
+		rebuild_preset_list();
+	return removed;
+}
+
+/* load voice information data */
+static int
+awe_load_info(awe_patch_info *patch, const char *addr, int count)
+{
+	int offset;
+	awe_voice_rec_hdr hdr;
+	int i;
+	int total_size;
+	sf_list *sf;
+	awe_voice_list *rec;
+
+	if (count < AWE_VOICE_REC_SIZE) {
+		printk(KERN_WARNING "AWE32 Error: invalid patch info length\n");
+		return -EINVAL;
+	}
+
+	offset = AWE_PATCH_INFO_SIZE;
+	if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE))
+		return -EFAULT;
+	offset += AWE_VOICE_REC_SIZE;
+
+	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+		printk(KERN_WARNING "AWE32 Error: Invalid voice number %d\n", hdr.nvoices);
+		return -EINVAL;
+	}
+	total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices;
+	if (count < total_size) {
+		printk(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
+		       count, hdr.nvoices);
+		return -EINVAL;
+	}
+
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL)
+		return -ENOMEM;
+
+	switch (hdr.write_mode) {
+	case AWE_WR_EXCLUSIVE:
+		/* exclusive mode - if the instrument already exists,
+		   return error */
+		for (rec = sf->infos; rec; rec = rec->next) {
+			if (rec->type == V_ST_NORMAL &&
+			    rec->bank == hdr.bank &&
+			    rec->instr == hdr.instr)
+				return -EINVAL;
+		}
+		break;
+	case AWE_WR_REPLACE:
+		/* replace mode - remove the instrument if it already exists */
+		remove_info(sf, hdr.bank, hdr.instr);
+		break;
+	}
+
+	/* append new layers */
+	for (i = 0; i < hdr.nvoices; i++) {
+		rec = alloc_new_info();
+		if (rec == NULL)
+			return -ENOMEM;
+
+		rec->bank = hdr.bank;
+		rec->instr = hdr.instr;
+		rec->type = V_ST_NORMAL;
+		rec->disabled = FALSE;
+
+		/* copy awe_voice_info parameters */
+		if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) {
+			kfree(rec);
+			return -EFAULT;
+		}
+		offset += AWE_VOICE_INFO_SIZE;
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+		if (sf && sf->shared) {
+			if (info_duplicated(sf, rec)) {
+				kfree(rec);
+				continue;
+			}
+		}
+#endif /* allow sharing */
+		if (rec->v.mode & AWE_MODE_INIT_PARM)
+			awe_init_voice_parm(&rec->v.parm);
+		add_sf_info(sf, rec);
+		awe_set_sample(rec);
+		add_info_list(rec);
+	}
+
+	return 0;
+}
+
+
+/* remove instrument layers */
+static int
+awe_remove_info(awe_patch_info *patch, const char *addr, int count)
+{
+	unsigned char bank, instr;
+	sf_list *sf;
+
+	if (! patch_opened || (sf = sftail) == NULL) {
+		printk(KERN_WARNING "AWE32: remove_info: patch not opened\n");
+		return -EINVAL;
+	}
+
+	bank = ((unsigned short)patch->optarg >> 8) & 0xff;
+	instr = (unsigned short)patch->optarg & 0xff;
+	if (! remove_info(sf, bank, instr))
+		return -EINVAL;
+	return 0;
+}
+
+
+/* load wave sample data */
+static int
+awe_load_data(awe_patch_info *patch, const char *addr, int count)
+{
+	int offset, size;
+	int rc;
+	awe_sample_info tmprec;
+	awe_sample_list *rec;
+	sf_list *sf;
+
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL)
+		return -ENOMEM;
+
+	size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
+	offset = AWE_PATCH_INFO_SIZE;
+	if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE))
+		return -EFAULT;
+	offset += AWE_SAMPLE_INFO_SIZE;
+	if (size != tmprec.size) {
+		printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n",
+		       tmprec.size, size);
+		return -EINVAL;
+	}
+
+	if (search_sample_index(sf, tmprec.sample) != NULL) {
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+		/* if shared sample, skip this data */
+		if (sf->type & AWE_PAT_SHARED)
+			return 0;
+#endif /* allow sharing */
+		DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample));
+		return -EINVAL;
+	}
+
+	if ((rec = alloc_new_sample()) == NULL)
+		return -ENOMEM;
+
+	memcpy(&rec->v, &tmprec, sizeof(tmprec));
+
+	if (rec->v.size > 0) {
+		if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) {
+			kfree(rec);
+			return rc;
+		}
+		sf->mem_ptr += rc;
+	}
+
+	add_sf_sample(sf, rec);
+	return 0;
+}
+
+
+/* replace wave sample data */
+static int
+awe_replace_data(awe_patch_info *patch, const char *addr, int count)
+{
+	int offset;
+	int size;
+	int rc;
+	int channels;
+	awe_sample_info cursmp;
+	int save_mem_ptr;
+	sf_list *sf;
+	awe_sample_list *rec;
+
+	if (! patch_opened || (sf = sftail) == NULL) {
+		printk(KERN_WARNING "AWE32: replace: patch not opened\n");
+		return -EINVAL;
+	}
+
+	size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
+	offset = AWE_PATCH_INFO_SIZE;
+	if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE))
+		return -EFAULT;
+	offset += AWE_SAMPLE_INFO_SIZE;
+	if (cursmp.size == 0 || size != cursmp.size) {
+		printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n",
+		       cursmp.size, size);
+		return -EINVAL;
+	}
+	channels = patch->optarg;
+	if (channels <= 0 || channels > AWE_NORMAL_VOICES) {
+		printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels);
+		return -EINVAL;
+	}
+
+	for (rec = sf->samples; rec; rec = rec->next) {
+		if (rec->v.sample == cursmp.sample)
+			break;
+	}
+	if (rec == NULL) {
+		printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n",
+		       cursmp.sample);
+		return -EINVAL;
+	}
+		
+	if (rec->v.size != cursmp.size) {
+		printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n",
+		       rec->v.size, cursmp.size);
+		return -EINVAL;
+	}
+
+	save_mem_ptr = awe_free_mem_ptr();
+	sftail->mem_ptr = rec->v.start - awe_mem_start;
+	memcpy(&rec->v, &cursmp, sizeof(cursmp));
+	rec->v.sf_id = current_sf_id;
+	if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0)
+		return rc;
+	sftail->mem_ptr = save_mem_ptr;
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+static const char *readbuf_addr;
+static int readbuf_offs;
+static int readbuf_flags;
+
+/* initialize read buffer */
+static int
+readbuf_init(const char *addr, int offset, awe_sample_info *sp)
+{
+	readbuf_addr = addr;
+	readbuf_offs = offset;
+	readbuf_flags = sp->mode_flags;
+	return 0;
+}
+
+/* read directly from user buffer */
+static unsigned short
+readbuf_word(int pos)
+{
+	unsigned short c;
+	/* read from user buffer */
+	if (readbuf_flags & AWE_SAMPLE_8BITS) {
+		unsigned char cc;
+		get_user(cc, (unsigned char*)(readbuf_addr + readbuf_offs + pos));
+		c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */
+	} else {
+		get_user(c, (unsigned short*)(readbuf_addr + readbuf_offs + pos * 2));
+	}
+	if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
+		c ^= 0x8000; /* unsigned -> signed */
+	return c;
+}
+
+#define readbuf_word_cache	readbuf_word
+#define readbuf_end()		/**/
+
+/*----------------------------------------------------------------*/
+
+#define BLANK_LOOP_START	8
+#define BLANK_LOOP_END		40
+#define BLANK_LOOP_SIZE		48
+
+/* loading onto memory - return the actual written size */
+static int 
+awe_write_wave_data(const char *addr, int offset, awe_sample_list *list, int channels)
+{
+	int i, truesize, dram_offset;
+	awe_sample_info *sp = &list->v;
+	int rc;
+
+	/* be sure loop points start < end */
+	if (sp->loopstart > sp->loopend) {
+		int tmp = sp->loopstart;
+		sp->loopstart = sp->loopend;
+		sp->loopend = tmp;
+	}
+
+	/* compute true data size to be loaded */
+	truesize = sp->size;
+	if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))
+		truesize += sp->loopend - sp->loopstart;
+	if (sp->mode_flags & AWE_SAMPLE_NO_BLANK)
+		truesize += BLANK_LOOP_SIZE;
+	if (awe_free_mem_ptr() + truesize >= memsize/2) {
+		DEBUG(-1,printk("AWE32 Error: Sample memory full\n"));
+		return -ENOSPC;
+	}
+
+	/* recalculate address offset */
+	sp->end -= sp->start;
+	sp->loopstart -= sp->start;
+	sp->loopend -= sp->start;
+
+	dram_offset = awe_free_mem_ptr() + awe_mem_start;
+	sp->start = dram_offset;
+	sp->end += dram_offset;
+	sp->loopstart += dram_offset;
+	sp->loopend += dram_offset;
+
+	/* set the total size (store onto obsolete checksum value) */
+	if (sp->size == 0)
+		sp->checksum = 0;
+	else
+		sp->checksum = truesize;
+
+	if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0)
+		return rc;
+
+	if (readbuf_init(addr, offset, sp) < 0)
+		return -ENOSPC;
+
+	for (i = 0; i < sp->size; i++) {
+		unsigned short c;
+		c = readbuf_word(i);
+		awe_write_dram(c);
+		if (i == sp->loopend &&
+		    (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
+			int looplen = sp->loopend - sp->loopstart;
+			/* copy reverse loop */
+			int k;
+			for (k = 1; k <= looplen; k++) {
+				c = readbuf_word_cache(i - k);
+				awe_write_dram(c);
+			}
+			if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
+				sp->end += looplen;
+			} else {
+				sp->start += looplen;
+				sp->end += looplen;
+			}
+		}
+	}
+	readbuf_end();
+
+	/* if no blank loop is attached in the sample, add it */
+	if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) {
+		for (i = 0; i < BLANK_LOOP_SIZE; i++)
+			awe_write_dram(0);
+		if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) {
+			sp->loopstart = sp->end + BLANK_LOOP_START;
+			sp->loopend = sp->end + BLANK_LOOP_END;
+		}
+	}
+
+	awe_close_dram();
+
+	/* initialize FM */
+	awe_init_fm();
+
+	return truesize;
+}
+
+
+/*----------------------------------------------------------------*/
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* calculate GUS envelope time:
+ * is this correct?  i have no idea..
+ */
+static int
+calc_gus_envelope_time(int rate, int start, int end)
+{
+	int r, p, t;
+	r = (3 - ((rate >> 6) & 3)) * 3;
+	p = rate & 0x3f;
+	t = end - start;
+	if (t < 0) t = -t;
+	if (13 > r)
+		t = t << (13 - r);
+	else
+		t = t >> (r - 13);
+	return (t * 10) / (p * 441);
+}
+
+#define calc_gus_sustain(val)  (0x7f - vol_table[(val)/2])
+#define calc_gus_attenuation(val)	vol_table[(val)/2]
+
+/* load GUS patch */
+static int
+awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
+{
+	struct patch_info patch;
+	awe_voice_info *rec;
+	awe_sample_info *smp;
+	awe_voice_list *vrec;
+	awe_sample_list *smprec;
+	int sizeof_patch;
+	int note, rc;
+	sf_list *sf;
+
+	sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */
+	if (size < sizeof_patch) {
+		printk(KERN_WARNING "AWE32 Error: Patch header too short\n");
+		return -EINVAL;
+	}
+	if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs))
+		return -EFAULT;
+	size -= sizeof_patch;
+	if (size < patch.len) {
+		printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n",
+		       size, patch.len);
+		return -EINVAL;
+	}
+	if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL)
+		return -ENOMEM;
+	if ((smprec = alloc_new_sample()) == NULL)
+		return -ENOMEM;
+	if ((vrec = alloc_new_info()) == NULL) {
+		kfree(smprec);
+		return -ENOMEM;
+	}
+
+	smp = &smprec->v;
+	smp->sample = sf->num_sample;
+	smp->start = 0;
+	smp->end = patch.len;
+	smp->loopstart = patch.loop_start;
+	smp->loopend = patch.loop_end;
+	smp->size = patch.len;
+
+	/* set up mode flags */
+	smp->mode_flags = 0;
+	if (!(patch.mode & WAVE_16_BITS))
+		smp->mode_flags |= AWE_SAMPLE_8BITS;
+	if (patch.mode & WAVE_UNSIGNED)
+		smp->mode_flags |= AWE_SAMPLE_UNSIGNED;
+	smp->mode_flags |= AWE_SAMPLE_NO_BLANK;
+	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
+		smp->mode_flags |= AWE_SAMPLE_SINGLESHOT;
+	if (patch.mode & WAVE_BIDIR_LOOP)
+		smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+	if (patch.mode & WAVE_LOOP_BACK)
+		smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
+
+	DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags));
+	if (patch.mode & WAVE_16_BITS) {
+		/* convert to word offsets */
+		smp->size /= 2;
+		smp->end /= 2;
+		smp->loopstart /= 2;
+		smp->loopend /= 2;
+	}
+	smp->checksum_flag = 0;
+	smp->checksum = 0;
+
+	if ((rc = awe_write_wave_data(addr, sizeof_patch, smprec, -1)) < 0)
+		return rc;
+	sf->mem_ptr += rc;
+	add_sf_sample(sf, smprec);
+
+	/* set up voice info */
+	rec = &vrec->v;
+	awe_init_voice_info(rec);
+	rec->sample = sf->num_info; /* the last sample */
+	rec->rate_offset = calc_rate_offset(patch.base_freq);
+	note = freq_to_note(patch.base_note);
+	rec->root = note / 100;
+	rec->tune = -(note % 100);
+	rec->low = freq_to_note(patch.low_note) / 100;
+	rec->high = freq_to_note(patch.high_note) / 100;
+	DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n",
+		       rec->rate_offset, note,
+		       rec->low, rec->high,
+	      patch.low_note, patch.high_note));
+	/* panning position; -128 - 127 => 0-127 */
+	rec->pan = (patch.panning + 128) / 2;
+
+	/* detuning is ignored */
+	/* 6points volume envelope */
+	if (patch.mode & WAVE_ENVELOPES) {
+		int attack, hold, decay, release;
+		attack = calc_gus_envelope_time
+			(patch.env_rate[0], 0, patch.env_offset[0]);
+		hold = calc_gus_envelope_time
+			(patch.env_rate[1], patch.env_offset[0],
+			 patch.env_offset[1]);
+		decay = calc_gus_envelope_time
+			(patch.env_rate[2], patch.env_offset[1],
+			 patch.env_offset[2]);
+		release = calc_gus_envelope_time
+			(patch.env_rate[3], patch.env_offset[1],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[4], patch.env_offset[3],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[5], patch.env_offset[4],
+			 patch.env_offset[5]);
+		rec->parm.volatkhld = (calc_parm_hold(hold) << 8) |
+			calc_parm_attack(attack);
+		rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+			calc_parm_decay(decay);
+		rec->parm.volrelease = 0x8000 | calc_parm_decay(release);
+		DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release));
+		rec->attenuation = calc_gus_attenuation(patch.env_offset[0]);
+	}
+
+	/* tremolo effect */
+	if (patch.mode & WAVE_TREMOLO) {
+		int rate = (patch.tremolo_rate * 1000 / 38) / 42;
+		rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+		DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n",
+			       patch.tremolo_rate, patch.tremolo_depth,
+			       rec->parm.tremfrq));
+	}
+	/* vibrato effect */
+	if (patch.mode & WAVE_VIBRATO) {
+		int rate = (patch.vibrato_rate * 1000 / 38) / 42;
+		rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+		DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n",
+			       patch.tremolo_rate, patch.tremolo_depth,
+			       rec->parm.tremfrq));
+	}
+	
+	/* scale_freq, scale_factor, volume, and fractions not implemented */
+
+	/* append to the tail of the list */
+	vrec->bank = ctrls[AWE_MD_GUS_BANK];
+	vrec->instr = patch.instr_no;
+	vrec->disabled = FALSE;
+	vrec->type = V_ST_NORMAL;
+
+	add_sf_info(sf, vrec);
+	add_info_list(vrec);
+
+	/* set the voice index */
+	awe_set_sample(vrec);
+
+	return 0;
+}
+
+#endif  /* AWE_HAS_GUS_COMPATIBILITY */
+
+/*
+ * sample and voice list handlers
+ */
+
+/* append this to the current sf list */
+static void add_sf_info(sf_list *sf, awe_voice_list *rec)
+{
+	if (sf == NULL)
+		return;
+	rec->holder = sf;
+	rec->v.sf_id = sf->sf_id;
+	if (sf->last_infos)
+		sf->last_infos->next = rec;
+	else
+		sf->infos = rec;
+	sf->last_infos = rec;
+	rec->next = NULL;
+	sf->num_info++;
+}
+
+/* prepend this sample to sf list */
+static void add_sf_sample(sf_list *sf, awe_sample_list *rec)
+{
+	if (sf == NULL)
+		return;
+	rec->holder = sf;
+	rec->v.sf_id = sf->sf_id;
+	if (sf->last_samples)
+		sf->last_samples->next = rec;
+	else
+		sf->samples = rec;
+	sf->last_samples = rec;
+	rec->next = NULL;
+	sf->num_sample++;
+}
+
+/* purge the old records which don't belong with the same file id */
+static void purge_old_list(awe_voice_list *rec, awe_voice_list *next)
+{
+	rec->next_instr = next;
+	if (rec->bank == AWE_DRUM_BANK) {
+		/* remove samples with the same note range */
+		awe_voice_list *cur, *prev = rec;
+		int low = rec->v.low;
+		int high = rec->v.high;
+		for (cur = next; cur; cur = cur->next_instr) {
+			if (cur->v.low == low &&
+			    cur->v.high == high &&
+			    ! is_identical_holder(cur->holder, rec->holder))
+				prev->next_instr = cur->next_instr;
+			else
+				prev = cur;
+		}
+	} else {
+		if (! is_identical_holder(next->holder, rec->holder))
+			/* remove all samples */
+			rec->next_instr = NULL;
+	}
+}
+
+/* prepend to top of the preset table */
+static void add_info_list(awe_voice_list *rec)
+{
+	awe_voice_list *prev, *cur;
+	int key;
+
+	if (rec->disabled)
+		return;
+
+	key = awe_search_key(rec->bank, rec->instr, rec->v.low);
+	prev = NULL;
+	for (cur = preset_table[key]; cur; cur = cur->next_bank) {
+		/* search the first record with the same bank number */
+		if (cur->instr == rec->instr && cur->bank == rec->bank) {
+			/* replace the list with the new record */
+			rec->next_bank = cur->next_bank;
+			if (prev)
+				prev->next_bank = rec;
+			else
+				preset_table[key] = rec;
+			purge_old_list(rec, cur);
+			return;
+		}
+		prev = cur;
+	}
+
+	/* this is the first bank record.. just add this */
+	rec->next_instr = NULL;
+	rec->next_bank = preset_table[key];
+	preset_table[key] = rec;
+}
+
+/* remove samples later than the specified sf_id */
+static void
+awe_remove_samples(int sf_id)
+{
+	sf_list *p, *prev;
+
+	if (sf_id <= 0) {
+		awe_reset_samples();
+		return;
+	}
+	/* already removed? */
+	if (current_sf_id <= sf_id)
+		return;
+
+	for (p = sftail; p; p = prev) {
+		if (p->sf_id <= sf_id)
+			break;
+		prev = p->prev;
+		awe_free_sf(p);
+	}
+	sftail = p;
+	if (sftail) {
+		sf_id = sftail->sf_id;
+		sftail->next = NULL;
+	} else {
+		sf_id = 0;
+		sfhead = NULL;
+	}
+	current_sf_id = sf_id;
+	if (locked_sf_id > sf_id)
+		locked_sf_id = sf_id;
+
+	rebuild_preset_list();
+}
+
+/* rebuild preset search list */
+static void rebuild_preset_list(void)
+{
+	sf_list *p;
+	awe_voice_list *rec;
+
+	memset(preset_table, 0, sizeof(preset_table));
+
+	for (p = sfhead; p; p = p->next) {
+		for (rec = p->infos; rec; rec = rec->next)
+			add_info_list(rec);
+	}
+}
+
+/* compare the given sf_id pair */
+static int is_identical_holder(sf_list *sf1, sf_list *sf2)
+{
+	if (sf1 == NULL || sf2 == NULL)
+		return FALSE;
+	if (sf1 == sf2)
+		return TRUE;
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	{
+		/* compare with the sharing id */
+		sf_list *p;
+		int counter = 0;
+		if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */
+			sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp;
+		}
+		for (p = sf1->shared; p; p = p->shared) {
+			if (counter++ > current_sf_id)
+				break; /* strange sharing loop.. quit */
+			if (p == sf2)
+				return TRUE;
+		}
+	}
+#endif /* allow sharing */
+	return FALSE;
+}
+
+/* search the sample index matching with the given sample id */
+static awe_sample_list *
+search_sample_index(sf_list *sf, int sample)
+{
+	awe_sample_list *p;
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+	int counter = 0;
+	while (sf) {
+		for (p = sf->samples; p; p = p->next) {
+			if (p->v.sample == sample)
+				return p;
+		}
+		sf = sf->shared;
+		if (counter++ > current_sf_id)
+			break; /* strange sharing loop.. quit */
+	}
+#else
+	if (sf) {
+		for (p = sf->samples; p; p = p->next) {
+			if (p->v.sample == sample)
+				return p;
+		}
+	}
+#endif
+	return NULL;
+}
+
+/* search the specified sample */
+/* non-zero = found */
+static short
+awe_set_sample(awe_voice_list *rec)
+{
+	awe_sample_list *smp;
+	awe_voice_info *vp = &rec->v;
+
+	vp->index = 0;
+	if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL)
+		return 0;
+
+	/* set the actual sample offsets */
+	vp->start += smp->v.start;
+	vp->end += smp->v.end;
+	vp->loopstart += smp->v.loopstart;
+	vp->loopend += smp->v.loopend;
+	/* copy mode flags */
+	vp->mode = smp->v.mode_flags;
+	/* set flag */
+	vp->index = 1;
+
+	return 1;
+}
+
+
+/*
+ * voice allocation
+ */
+
+/* look for all voices associated with the specified note & velocity */
+static int
+awe_search_multi_voices(awe_voice_list *rec, int note, int velocity,
+			awe_voice_info **vlist)
+{
+	int nvoices;
+
+	nvoices = 0;
+	for (; rec; rec = rec->next_instr) {
+		if (note >= rec->v.low &&
+		    note <= rec->v.high &&
+		    velocity >= rec->v.vellow &&
+		    velocity <= rec->v.velhigh) {
+			if (rec->type == V_ST_MAPPED) {
+				/* mapper */
+				vlist[0] = &rec->v;
+				return -1;
+			}
+			vlist[nvoices++] = &rec->v;
+			if (nvoices >= AWE_MAX_VOICES)
+				break;
+		}
+	}
+	return nvoices;	
+}
+
+/* store the voice list from the specified note and velocity.
+   if the preset is mapped, seek for the destination preset, and rewrite
+   the note number if necessary.
+   */
+static int
+really_alloc_voices(int bank, int instr, int *note, int velocity, awe_voice_info **vlist)
+{
+	int nvoices;
+	awe_voice_list *vrec;
+	int level = 0;
+
+	for (;;) {
+		vrec = awe_search_instr(bank, instr, *note);
+		nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+		if (nvoices == 0) {
+			if (bank == AWE_DRUM_BANK)
+				/* search default drumset */
+				vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note);
+			else
+				/* search default preset */
+				vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note);
+			nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+		}
+		if (nvoices == 0) {
+			if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0)
+				/* search default drumset */
+				vrec = awe_search_instr(bank, 0, *note);
+			else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0)
+				/* search default preset */
+				vrec = awe_search_instr(0, instr, *note);
+			nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+		}
+		if (nvoices < 0) { /* mapping */
+			int key = vlist[0]->fixkey;
+			instr = vlist[0]->start;
+			bank = vlist[0]->end;
+			if (level++ > 5) {
+				printk(KERN_ERR "AWE32: too deep mapping level\n");
+				return 0;
+			}
+			if (key >= 0)
+				*note = key;
+		} else
+			break;
+	}
+
+	return nvoices;
+}
+
+/* allocate voices corresponding note and velocity; supports multiple insts. */
+static void
+awe_alloc_multi_voices(int ch, int note, int velocity, int key)
+{
+	int i, v, nvoices, bank;
+	awe_voice_info *vlist[AWE_MAX_VOICES];
+
+	if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch))
+		bank = AWE_DRUM_BANK; /* always search drumset */
+	else
+		bank = channels[ch].bank;
+
+	/* check the possible voices; note may be changeable if mapped */
+	nvoices = really_alloc_voices(bank, channels[ch].instr,
+				      &note, velocity, vlist);
+
+	/* set the voices */
+	current_alloc_time++;
+	for (i = 0; i < nvoices; i++) {
+		v = awe_clear_voice();
+		voices[v].key = key;
+		voices[v].ch = ch;
+		voices[v].note = note;
+		voices[v].velocity = velocity;
+		voices[v].time = current_alloc_time;
+		voices[v].cinfo = &channels[ch];
+		voices[v].sample = vlist[i];
+		voices[v].state = AWE_ST_MARK;
+		voices[v].layer = nvoices - i - 1;  /* in reverse order */
+	}
+
+	/* clear the mark in allocated voices */
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state == AWE_ST_MARK)
+			voices[i].state = AWE_ST_OFF;
+			
+	}
+}
+
+
+/* search an empty voice.
+   if no empty voice is found, at least terminate a voice
+   */
+static int
+awe_clear_voice(void)
+{
+	enum {
+		OFF=0, RELEASED, SUSTAINED, PLAYING, END
+	};
+	struct voice_candidate_t {
+		int best;
+		int time;
+		int vtarget;
+	} candidate[END];
+	int i, type, vtarget;
+
+	vtarget = 0xffff;
+	for (type = OFF; type < END; type++) {
+		candidate[type].best = -1;
+		candidate[type].time = current_alloc_time + 1;
+		candidate[type].vtarget = vtarget;
+	}
+
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state & AWE_ST_OFF)
+			type = OFF;
+		else if (voices[i].state & AWE_ST_RELEASED)
+			type = RELEASED;
+		else if (voices[i].state & AWE_ST_SUSTAINED)
+			type = SUSTAINED;
+		else if (voices[i].state & ~AWE_ST_MARK)
+			type = PLAYING;
+		else
+			continue;
+#ifdef AWE_CHECK_VTARGET
+		/* get current volume */
+		vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff;
+#endif
+		if (candidate[type].best < 0 ||
+		    vtarget < candidate[type].vtarget ||
+		    (vtarget == candidate[type].vtarget &&
+		     voices[i].time < candidate[type].time)) {
+			candidate[type].best = i;
+			candidate[type].time = voices[i].time;
+			candidate[type].vtarget = vtarget;
+		}
+	}
+
+	for (type = OFF; type < END; type++) {
+		if ((i = candidate[type].best) >= 0) {
+			if (voices[i].state != AWE_ST_OFF)
+				awe_terminate(i);
+			awe_voice_init(i, TRUE);
+			return i;
+		}
+	}
+	return 0;
+}
+
+
+/* search sample for the specified note & velocity and set it on the voice;
+ * note that voice is the voice index (not channel index)
+ */
+static void
+awe_alloc_one_voice(int voice, int note, int velocity)
+{
+	int ch, nvoices, bank;
+	awe_voice_info *vlist[AWE_MAX_VOICES];
+
+	ch = voices[voice].ch;
+	if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice))
+		bank = AWE_DRUM_BANK; /* always search drumset */
+	else
+		bank = voices[voice].cinfo->bank;
+
+	nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr,
+				      &note, velocity, vlist);
+	if (nvoices > 0) {
+		voices[voice].time = ++current_alloc_time;
+		voices[voice].sample = vlist[0]; /* use the first one */
+		voices[voice].layer = 0;
+		voices[voice].note = note;
+		voices[voice].velocity = velocity;
+	}
+}
+
+
+/*
+ * sequencer2 functions
+ */
+
+/* search an empty voice; used by sequencer2 */
+static int
+awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+	playing_mode = AWE_PLAY_MULTI2;
+	awe_info.nr_voices = AWE_MAX_CHANNELS;
+	return awe_clear_voice();
+}
+
+
+/* set up voice; used by sequencer2 */
+static void
+awe_setup_voice(int dev, int voice, int chn)
+{
+	struct channel_info *info;
+	if (synth_devs[dev] == NULL ||
+	    (info = &synth_devs[dev]->chn_info[chn]) == NULL)
+		return;
+
+	if (voice < 0 || voice >= awe_max_voices)
+		return;
+
+	DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn));
+	channels[chn].expression_vol = info->controllers[CTL_EXPRESSION];
+	channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME];
+	channels[chn].panning =
+		info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */
+	channels[chn].bender = info->bender_value; /* zero center */
+	channels[chn].bank = info->controllers[CTL_BANK_SELECT];
+	channels[chn].sustained = info->controllers[CTL_SUSTAIN];
+	if (info->controllers[CTL_EXT_EFF_DEPTH]) {
+		FX_SET(&channels[chn].fx, AWE_FX_REVERB,
+		       info->controllers[CTL_EXT_EFF_DEPTH] * 2);
+	}
+	if (info->controllers[CTL_CHORUS_DEPTH]) {
+		FX_SET(&channels[chn].fx, AWE_FX_CHORUS,
+		       info->controllers[CTL_CHORUS_DEPTH] * 2);
+	}
+	awe_set_instr(dev, chn, info->pgm_num);
+}
+
+
+#ifdef CONFIG_AWE32_MIXER
+/*
+ * AWE32 mixer device control
+ */
+
+static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg);
+
+static int my_mixerdev = -1;
+
+static struct mixer_operations awe_mixer_operations = {
+	owner:	THIS_MODULE,
+	id:	"AWE",
+	name:	"AWE32 Equalizer",
+	ioctl:	awe_mixer_ioctl,
+};
+
+static void __init attach_mixer(void)
+{
+	if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) {
+		mixer_devs[my_mixerdev] = &awe_mixer_operations;
+	}
+}
+
+static void __exit unload_mixer(void)
+{
+	if (my_mixerdev >= 0)
+		sound_unload_mixerdev(my_mixerdev);
+}
+
+static int
+awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int i, level, value;
+
+	if (((cmd >> 8) & 0xff) != 'M')
+		return -EINVAL;
+
+	level = *(int*)arg;
+	level = ((level & 0xff) + (level >> 8)) / 2;
+	DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level));
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		switch (cmd & 0xff) {
+		case SOUND_MIXER_BASS:
+			value = level * 12 / 100;
+			if (value >= 12)
+				value = 11;
+			ctrls[AWE_MD_BASS_LEVEL] = value;
+			awe_update_equalizer();
+			break;
+		case SOUND_MIXER_TREBLE:
+			value = level * 12 / 100;
+			if (value >= 12)
+				value = 11;
+			ctrls[AWE_MD_TREBLE_LEVEL] = value;
+			awe_update_equalizer();
+			break;
+		case SOUND_MIXER_VOLUME:
+			level = level * 127 / 100;
+			if (level >= 128) level = 127;
+			atten_relative = FALSE;
+			atten_offset = vol_table[level];
+			awe_update_volume();
+			break;
+		}
+	}
+	switch (cmd & 0xff) {
+	case SOUND_MIXER_BASS:
+		level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24;
+		level = (level << 8) | level;
+		break;
+	case SOUND_MIXER_TREBLE:
+		level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24;
+		level = (level << 8) | level;
+		break;
+	case SOUND_MIXER_VOLUME:
+		value = atten_offset;
+		if (atten_relative)
+			value += ctrls[AWE_MD_ZERO_ATTEN];
+		for (i = 127; i > 0; i--) {
+			if (value <= vol_table[i])
+				break;
+		}
+		level = i * 100 / 127;
+		level = (level << 8) | level;
+		break;
+	case SOUND_MIXER_DEVMASK:
+		level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME;
+		break;
+	default:
+		level = 0;
+		break;
+	}
+	return *(int*)arg = level;
+}
+#endif /* CONFIG_AWE32_MIXER */
+
+
+/*
+ * initialization of Emu8000
+ */
+
+/* intiailize audio channels */
+static void
+awe_init_audio(void)
+{
+	int ch;
+
+	/* turn off envelope engines */
+	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
+		awe_poke(AWE_DCYSUSV(ch), 0x80);
+	}
+  
+	/* reset all other parameters to zero */
+	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
+		awe_poke(AWE_ENVVOL(ch), 0);
+		awe_poke(AWE_ENVVAL(ch), 0);
+		awe_poke(AWE_DCYSUS(ch), 0);
+		awe_poke(AWE_ATKHLDV(ch), 0);
+		awe_poke(AWE_LFO1VAL(ch), 0);
+		awe_poke(AWE_ATKHLD(ch), 0);
+		awe_poke(AWE_LFO2VAL(ch), 0);
+		awe_poke(AWE_IP(ch), 0);
+		awe_poke(AWE_IFATN(ch), 0);
+		awe_poke(AWE_PEFE(ch), 0);
+		awe_poke(AWE_FMMOD(ch), 0);
+		awe_poke(AWE_TREMFRQ(ch), 0);
+		awe_poke(AWE_FM2FRQ2(ch), 0);
+		awe_poke_dw(AWE_PTRX(ch), 0);
+		awe_poke_dw(AWE_VTFT(ch), 0);
+		awe_poke_dw(AWE_PSST(ch), 0);
+		awe_poke_dw(AWE_CSL(ch), 0);
+		awe_poke_dw(AWE_CCCA(ch), 0);
+	}
+
+	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
+		awe_poke_dw(AWE_CPF(ch), 0);
+		awe_poke_dw(AWE_CVCF(ch), 0);
+	}
+}
+
+
+/* initialize DMA address */
+static void
+awe_init_dma(void)
+{
+	awe_poke_dw(AWE_SMALR, 0);
+	awe_poke_dw(AWE_SMARR, 0);
+	awe_poke_dw(AWE_SMALW, 0);
+	awe_poke_dw(AWE_SMARW, 0);
+}
+
+
+/* initialization arrays; from ADIP */
+
+static unsigned short init1[128] = {
+	0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
+	0x13ff, 0x0430,  0x17ff, 0x0530, 0x1bff, 0x0630,  0x1fff, 0x0730,
+	0x23ff, 0x0830,  0x27ff, 0x0930, 0x2bff, 0x0a30,  0x2fff, 0x0b30,
+	0x33ff, 0x0c30,  0x37ff, 0x0d30, 0x3bff, 0x0e30,  0x3fff, 0x0f30,
+
+	0x43ff, 0x0030,  0x47ff, 0x0130, 0x4bff, 0x0230,  0x4fff, 0x0330,
+	0x53ff, 0x0430,  0x57ff, 0x0530, 0x5bff, 0x0630,  0x5fff, 0x0730,
+	0x63ff, 0x0830,  0x67ff, 0x0930, 0x6bff, 0x0a30,  0x6fff, 0x0b30,
+	0x73ff, 0x0c30,  0x77ff, 0x0d30, 0x7bff, 0x0e30,  0x7fff, 0x0f30,
+
+	0x83ff, 0x0030,  0x87ff, 0x0130, 0x8bff, 0x0230,  0x8fff, 0x0330,
+	0x93ff, 0x0430,  0x97ff, 0x0530, 0x9bff, 0x0630,  0x9fff, 0x0730,
+	0xa3ff, 0x0830,  0xa7ff, 0x0930, 0xabff, 0x0a30,  0xafff, 0x0b30,
+	0xb3ff, 0x0c30,  0xb7ff, 0x0d30, 0xbbff, 0x0e30,  0xbfff, 0x0f30,
+
+	0xc3ff, 0x0030,  0xc7ff, 0x0130, 0xcbff, 0x0230,  0xcfff, 0x0330,
+	0xd3ff, 0x0430,  0xd7ff, 0x0530, 0xdbff, 0x0630,  0xdfff, 0x0730,
+	0xe3ff, 0x0830,  0xe7ff, 0x0930, 0xebff, 0x0a30,  0xefff, 0x0b30,
+	0xf3ff, 0x0c30,  0xf7ff, 0x0d30, 0xfbff, 0x0e30,  0xffff, 0x0f30,
+};
+
+static unsigned short init2[128] = {
+	0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
+	0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
+	0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
+	0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
+
+	0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
+	0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
+	0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
+	0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
+
+	0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
+	0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
+	0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
+	0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
+	0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
+	0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
+	0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
+	0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
+};
+
+static unsigned short init3[128] = {
+	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
+	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
+	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
+
+	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
+	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
+	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
+	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
+
+	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
+	0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
+	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
+	0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
+
+	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
+	0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
+	0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
+	0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+static unsigned short init4[128] = {
+	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
+	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
+	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
+
+	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
+	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
+	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
+	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
+
+	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
+	0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
+	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
+	0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
+
+	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
+	0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
+	0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
+	0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+
+/* send initialization arrays to start up */
+static void
+awe_init_array(void)
+{
+	awe_send_array(init1);
+	awe_wait(1024);
+	awe_send_array(init2);
+	awe_send_array(init3);
+	awe_poke_dw(AWE_HWCF4, 0);
+	awe_poke_dw(AWE_HWCF5, 0x83);
+	awe_poke_dw(AWE_HWCF6, 0x8000);
+	awe_send_array(init4);
+}
+
+/* send an initialization array */
+static void
+awe_send_array(unsigned short *data)
+{
+	int i;
+	unsigned short *p;
+
+	p = data;
+	for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+		awe_poke(AWE_INIT1(i), *p);
+	for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+		awe_poke(AWE_INIT2(i), *p);
+	for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+		awe_poke(AWE_INIT3(i), *p);
+	for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+		awe_poke(AWE_INIT4(i), *p);
+}
+
+
+/*
+ * set up awe32 channels to some known state.
+ */
+
+/* set the envelope & LFO parameters to the default values; see ADIP */
+static void
+awe_tweak_voice(int i)
+{
+	/* set all mod/vol envelope shape to minimum */
+	awe_poke(AWE_ENVVOL(i), 0x8000);
+	awe_poke(AWE_ENVVAL(i), 0x8000);
+	awe_poke(AWE_DCYSUS(i), 0x7F7F);
+	awe_poke(AWE_ATKHLDV(i), 0x7F7F);
+	awe_poke(AWE_ATKHLD(i), 0x7F7F);
+	awe_poke(AWE_PEFE(i), 0);  /* mod envelope height to zero */
+	awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */
+	awe_poke(AWE_LFO2VAL(i), 0x8000);
+	awe_poke(AWE_IP(i), 0xE000);	/* no pitch shift */
+	awe_poke(AWE_IFATN(i), 0xFF00);	/* volume to minimum */
+	awe_poke(AWE_FMMOD(i), 0);
+	awe_poke(AWE_TREMFRQ(i), 0);
+	awe_poke(AWE_FM2FRQ2(i), 0);
+}
+
+static void
+awe_tweak(void)
+{
+	int i;
+	/* reset all channels */
+	for (i = 0; i < awe_max_voices; i++)
+		awe_tweak_voice(i);
+}
+
+
+/*
+ *  initializes the FM section of AWE32;
+ *   see Vince Vu's unofficial AWE32 programming guide
+ */
+
+static void
+awe_init_fm(void)
+{
+#ifndef AWE_ALWAYS_INIT_FM
+	/* if no extended memory is on board.. */
+	if (memsize <= 0)
+		return;
+#endif
+	DEBUG(3,printk("AWE32: initializing FM\n"));
+
+	/* Initialize the last two channels for DRAM refresh and producing
+	   the reverb and chorus effects for Yamaha OPL-3 synthesizer */
+
+	/* 31: FM left channel, 0xffffe0-0xffffe8 */
+	awe_poke(AWE_DCYSUSV(30), 0x80);
+	awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */
+	awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 |
+		    (DEF_FM_CHORUS_DEPTH << 24));
+	awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8));
+	awe_poke_dw(AWE_CPF(30), 0);
+	awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3);
+
+	/* 32: FM right channel, 0xfffff0-0xfffff8 */
+	awe_poke(AWE_DCYSUSV(31), 0x80);
+	awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */
+	awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
+		    (DEF_FM_CHORUS_DEPTH << 24));
+	awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
+	awe_poke_dw(AWE_CPF(31), 0x8000);
+	awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
+
+	/* skew volume & cutoff */
+	awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
+	awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
+
+	voices[30].state = AWE_ST_FM;
+	voices[31].state = AWE_ST_FM;
+
+	/* change maximum channels to 30 */
+	awe_max_voices = AWE_NORMAL_VOICES;
+	if (playing_mode == AWE_PLAY_DIRECT)
+		awe_info.nr_voices = awe_max_voices;
+	else
+		awe_info.nr_voices = AWE_MAX_CHANNELS;
+	voice_alloc->max_voice = awe_max_voices;
+}
+
+/*
+ *  AWE32 DRAM access routines
+ */
+
+/* open DRAM write accessing mode */
+static int
+awe_open_dram_for_write(int offset, int channels)
+{
+	int vidx[AWE_NORMAL_VOICES];
+	int i;
+
+	if (channels < 0 || channels >= AWE_NORMAL_VOICES) {
+		channels = AWE_NORMAL_VOICES;
+		for (i = 0; i < AWE_NORMAL_VOICES; i++)
+			vidx[i] = i;
+	} else {
+		for (i = 0; i < channels; i++) {
+			vidx[i] = awe_clear_voice();
+			voices[vidx[i]].state = AWE_ST_MARK;
+		}
+	}
+
+	/* use all channels for DMA transfer */
+	for (i = 0; i < channels; i++) {
+		if (vidx[i] < 0) continue;
+		awe_poke(AWE_DCYSUSV(vidx[i]), 0x80);
+		awe_poke_dw(AWE_VTFT(vidx[i]), 0);
+		awe_poke_dw(AWE_CVCF(vidx[i]), 0);
+		awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000);
+		awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000);
+		awe_poke_dw(AWE_PSST(vidx[i]), 0);
+		awe_poke_dw(AWE_CSL(vidx[i]), 0);
+		awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000);
+		voices[vidx[i]].state = AWE_ST_DRAM;
+	}
+	/* point channels 31 & 32 to ROM samples for DRAM refresh */
+	awe_poke_dw(AWE_VTFT(30), 0);
+	awe_poke_dw(AWE_PSST(30), 0x1d8);
+	awe_poke_dw(AWE_CSL(30), 0x1e0);
+	awe_poke_dw(AWE_CCCA(30), 0x1d8);
+	awe_poke_dw(AWE_VTFT(31), 0);
+	awe_poke_dw(AWE_PSST(31), 0x1d8);
+	awe_poke_dw(AWE_CSL(31), 0x1e0);
+	awe_poke_dw(AWE_CCCA(31), 0x1d8);
+	voices[30].state = AWE_ST_FM;
+	voices[31].state = AWE_ST_FM;
+
+	/* if full bit is on, not ready to write on */
+	if (awe_peek_dw(AWE_SMALW) & 0x80000000) {
+		for (i = 0; i < channels; i++) {
+			awe_poke_dw(AWE_CCCA(vidx[i]), 0);
+			voices[vidx[i]].state = AWE_ST_OFF;
+		}
+		printk("awe: not ready to write..\n");
+		return -EPERM;
+	}
+
+	/* set address to write */
+	awe_poke_dw(AWE_SMALW, offset);
+
+	return 0;
+}
+
+/* open DRAM for RAM size detection */
+static void
+awe_open_dram_for_check(void)
+{
+	int i;
+	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+		awe_poke(AWE_DCYSUSV(i), 0x80);
+		awe_poke_dw(AWE_VTFT(i), 0);
+		awe_poke_dw(AWE_CVCF(i), 0);
+		awe_poke_dw(AWE_PTRX(i), 0x40000000);
+		awe_poke_dw(AWE_CPF(i), 0x40000000);
+		awe_poke_dw(AWE_PSST(i), 0);
+		awe_poke_dw(AWE_CSL(i), 0);
+		if (i & 1) /* DMA write */
+			awe_poke_dw(AWE_CCCA(i), 0x06000000);
+		else	   /* DMA read */
+			awe_poke_dw(AWE_CCCA(i), 0x04000000);
+		voices[i].state = AWE_ST_DRAM;
+	}
+}
+
+
+/* close dram access */
+static void
+awe_close_dram(void)
+{
+	int i;
+	/* wait until FULL bit in SMAxW register be false */
+	for (i = 0; i < 10000; i++) {
+		if (!(awe_peek_dw(AWE_SMALW) & 0x80000000))
+			break;
+		awe_wait(10);
+	}
+
+	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+		if (voices[i].state == AWE_ST_DRAM) {
+			awe_poke_dw(AWE_CCCA(i), 0);
+			awe_poke(AWE_DCYSUSV(i), 0x807F);
+			voices[i].state = AWE_ST_OFF;
+		}
+	}
+}
+
+
+/*
+ * detect presence of AWE32 and check memory size
+ */
+
+/* detect emu8000 chip on the specified address; from VV's guide */
+
+static int __init
+awe_detect_base(int addr)
+{
+	setup_ports(addr, 0, 0);
+	if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
+		return 0;
+	if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
+		return 0;
+	if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
+		return 0;
+        DEBUG(0,printk("AWE32 found at %x\n", addr));
+	return 1;
+}
+	
+#ifdef __ISAPNP__
+static struct {
+	unsigned short card_vendor, card_device;
+	unsigned short vendor;
+	unsigned short function;
+	char *name;
+} isapnp_awe_list[] __initdata = {
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0021),
+		"AWE32 WaveTable" },
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0022),
+		"AWE64 WaveTable" },
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0023),
+		"AWE64 Gold WaveTable" },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_awe_list);
+
+static struct pci_dev *idev = NULL;
+
+static int __init awe_probe_isapnp(int *port)
+{
+	int i;
+
+	for (i = 0; isapnp_awe_list[i].vendor != 0; i++) {
+		while ((idev = isapnp_find_dev(NULL,
+		                               isapnp_awe_list[i].vendor,
+		                               isapnp_awe_list[i].function,
+		                               idev))) {
+			if (idev->prepare(idev) < 0)
+				continue;
+			if (idev->activate(idev) < 0 ||
+			    !idev->resource[0].start) {
+				idev->deactivate(idev);
+				idev->deactivate(idev);
+				continue;
+			}
+			*port = idev->resource[0].start;
+			break;
+		}
+		if (!idev)
+			continue;
+		printk(KERN_INFO "ISAPnP reports %s at i/o %#x\n",
+		       isapnp_awe_list[i].name, *port);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static void __exit awe_deactivate_isapnp(void)
+{
+#if 1
+	if (idev) {
+		idev->deactivate(idev);
+		idev = NULL;
+	}
+#endif
+}
+
+#endif
+
+static int __init
+awe_detect(void)
+{
+	int base;
+
+#ifdef __ISAPNP__
+	if (isapnp) {
+		if (awe_probe_isapnp(&io) < 0) {
+			printk(KERN_ERR "AWE32: No ISAPnP cards found\n");
+			if (isapnp != -1)
+			  return 0;
+		} else {
+			setup_ports(io, 0, 0);
+			return 1;
+		}
+	}
+#endif /* isapnp */
+
+	if (io) /* use default i/o port value */
+		setup_ports(io, 0, 0);
+	else { /* probe it */
+		for (base = 0x620; base <= 0x680; base += 0x20)
+			if (awe_detect_base(base))
+				return 1;
+		DEBUG(0,printk("AWE32 not found\n"));
+		return 0;
+	}
+
+	return 1;
+}
+
+
+/*
+ * check dram size on AWE board
+ */
+
+/* any three numbers you like */
+#define UNIQUE_ID1	0x1234
+#define UNIQUE_ID2	0x4321
+#define UNIQUE_ID3	0xABCD
+
+static void __init
+awe_check_dram(void)
+{
+	if (awe_present) /* already initialized */
+		return;
+
+	if (memsize >= 0) { /* given by config file or module option */
+		memsize *= 1024; /* convert to Kbytes */
+		return;
+	}
+
+	awe_open_dram_for_check();
+
+	memsize = 0;
+
+	/* set up unique two id numbers */
+	awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
+	awe_poke(AWE_SMLD, UNIQUE_ID1);
+	awe_poke(AWE_SMLD, UNIQUE_ID2);
+
+	while (memsize < AWE_MAX_DRAM_SIZE) {
+		awe_wait(5);
+		/* read a data on the DRAM start address */
+		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
+		awe_peek(AWE_SMLD); /* discard stale data  */
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID1)
+			break;
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
+			break;
+		memsize += 512;  /* increment 512kbytes */
+		/* Write a unique data on the test address;
+		 * if the address is out of range, the data is written on
+		 * 0x200000(=AWE_DRAM_OFFSET).  Then the two id words are
+		 * broken by this data.
+		 */
+		awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + memsize*512L);
+		awe_poke(AWE_SMLD, UNIQUE_ID3);
+		awe_wait(5);
+		/* read a data on the just written DRAM address */
+		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + memsize*512L);
+		awe_peek(AWE_SMLD); /* discard stale data  */
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID3)
+			break;
+	}
+	awe_close_dram();
+
+	DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", memsize));
+
+	/* convert to Kbytes */
+	memsize *= 1024;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/*
+ * chorus and reverb controls; from VV's guide
+ */
+
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static char chorus_defined[AWE_CHORUS_NUMBERS];
+static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = {
+	{0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+	{0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+	{0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+	{0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+	{0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+	{0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+	{0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */
+	{0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */
+};
+
+static int
+awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count)
+{
+	if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) {
+		printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg);
+		return -EINVAL;
+	}
+	if (count < sizeof(awe_chorus_fx_rec)) {
+		printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n");
+		return -EINVAL;
+	}
+	if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
+			   sizeof(awe_chorus_fx_rec)))
+		return -EFAULT;
+	chorus_defined[patch->optarg] = TRUE;
+	return 0;
+}
+
+static void
+awe_set_chorus_mode(int effect)
+{
+	if (effect < 0 || effect >= AWE_CHORUS_NUMBERS ||
+	    (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect]))
+		return;
+	awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback);
+	awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset);
+	awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth);
+	awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay);
+	awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq);
+	awe_poke_dw(AWE_HWCF6, 0x8000);
+	awe_poke_dw(AWE_HWCF7, 0x0000);
+}
+
+static void
+awe_update_chorus_mode(void)
+{
+	awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]);
+}
+
+/*----------------------------------------------------------------*/
+
+/* reverb mode settings; write the following 28 data of 16 bit length
+ *   on the corresponding ports in the reverb_cmds array
+ */
+static char reverb_defined[AWE_CHORUS_NUMBERS];
+static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = {
+{{  /* room 1 */
+	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* room 2 */
+	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* room 3 */
+	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+}},
+{{  /* hall 1 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+}},
+{{  /* hall 2 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* plate */
+	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* delay */
+	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+{{  /* panning delay */
+	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+};
+
+static struct ReverbCmdPair {
+	unsigned short cmd, port;
+} reverb_cmds[28] = {
+  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
+};
+
+static int
+awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count)
+{
+	if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) {
+		printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg);
+		return -EINVAL;
+	}
+	if (count < sizeof(awe_reverb_fx_rec)) {
+		printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n");
+		return -EINVAL;
+	}
+	if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
+			   sizeof(awe_reverb_fx_rec)))
+		return -EFAULT;
+	reverb_defined[patch->optarg] = TRUE;
+	return 0;
+}
+
+static void
+awe_set_reverb_mode(int effect)
+{
+	int i;
+	if (effect < 0 || effect >= AWE_REVERB_NUMBERS ||
+	    (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect]))
+		return;
+	for (i = 0; i < 28; i++)
+		awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
+			 reverb_parm[effect].parms[i]);
+}
+
+static void
+awe_update_reverb_mode(void)
+{
+	awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]);
+}
+
+/*
+ * treble/bass equalizer control
+ */
+
+static unsigned short bass_parm[12][3] = {
+	{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+	{0xD25B, 0xD35B, 0x0000}, /*  -8 */
+	{0xD24C, 0xD34C, 0x0000}, /*  -6 */
+	{0xD23D, 0xD33D, 0x0000}, /*  -4 */
+	{0xD21F, 0xD31F, 0x0000}, /*  -2 */
+	{0xC208, 0xC308, 0x0001}, /*   0 (HW default) */
+	{0xC219, 0xC319, 0x0001}, /*  +2 */
+	{0xC22A, 0xC32A, 0x0001}, /*  +4 */
+	{0xC24C, 0xC34C, 0x0001}, /*  +6 */
+	{0xC26E, 0xC36E, 0x0001}, /*  +8 */
+	{0xC248, 0xC348, 0x0002}, /* +10 */
+	{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+	{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+	{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+	{0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+	{0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+static void
+awe_equalizer(int bass, int treble)
+{
+	unsigned short w;
+
+	if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+		return;
+	awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]);
+	awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]);
+	awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]);
+	awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]);
+	awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]);
+	awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]);
+	awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]);
+	awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]);
+	awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]);
+	awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]);
+	w = bass_parm[bass][2] + treble_parm[treble][8];
+	awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262));
+	awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362));
+}
+
+static void awe_update_equalizer(void)
+{
+	awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]);
+}
+
+
+/*----------------------------------------------------------------*/
+
+#ifdef CONFIG_AWE32_MIDIEMU
+
+/*
+ * Emu8000 MIDI Emulation
+ */
+
+/*
+ * midi queue record
+ */
+
+/* queue type */
+enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, };
+
+#define MAX_MIDIBUF	64
+
+/* midi status */
+typedef struct MidiStatus {
+	int queue;	/* queue type */
+	int qlen;	/* queue length */
+	int read;	/* chars read */
+	int status;	/* current status */
+	int chan;	/* current channel */
+	unsigned char buf[MAX_MIDIBUF];
+} MidiStatus;
+
+/* MIDI mode type */
+enum { MODE_GM, MODE_GS, MODE_XG, };
+
+/* NRPN / CC -> Emu8000 parameter converter */
+typedef struct {
+	int control;
+	int awe_effect;
+	unsigned short (*convert)(int val);
+} ConvTable;
+
+
+/*
+ * prototypes
+ */
+
+static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int));
+static void awe_midi_close(int dev);
+static int awe_midi_ioctl(int dev, unsigned cmd, caddr_t arg);
+static int awe_midi_outputc(int dev, unsigned char midi_byte);
+
+static void init_midi_status(MidiStatus *st);
+static void clear_rpn(void);
+static void get_midi_char(MidiStatus *st, int c);
+/*static void queue_varlen(MidiStatus *st, int c);*/
+static void special_event(MidiStatus *st, int c);
+static void queue_read(MidiStatus *st, int c);
+static void midi_note_on(MidiStatus *st);
+static void midi_note_off(MidiStatus *st);
+static void midi_key_pressure(MidiStatus *st);
+static void midi_channel_pressure(MidiStatus *st);
+static void midi_pitch_wheel(MidiStatus *st);
+static void midi_program_change(MidiStatus *st);
+static void midi_control_change(MidiStatus *st);
+static void midi_select_bank(MidiStatus *st, int val);
+static void midi_nrpn_event(MidiStatus *st);
+static void midi_rpn_event(MidiStatus *st);
+static void midi_detune(int chan, int coarse, int fine);
+static void midi_system_exclusive(MidiStatus *st);
+static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val);
+static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val);
+static int xg_control_change(MidiStatus *st, int cmd, int val);
+
+#define numberof(ary)	(sizeof(ary)/sizeof(ary[0]))
+
+
+/*
+ * OSS Midi device record
+ */
+
+static struct midi_operations awe_midi_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"AWE Midi Emu", 0, 0, SNDCARD_SB},
+	in_info:	{0},
+	open:		awe_midi_open, /*open*/
+	close:		awe_midi_close, /*close*/
+	ioctl:		awe_midi_ioctl, /*ioctl*/
+	outputc:	awe_midi_outputc, /*outputc*/
+};
+
+static int my_mididev = -1;
+
+static void __init attach_midiemu(void)
+{
+	if ((my_mididev = sound_alloc_mididev()) < 0)
+		printk ("Sound: Too many midi devices detected\n");
+	else
+		midi_devs[my_mididev] = &awe_midi_operations;
+}
+
+static void __exit unload_midiemu(void)
+{
+	if (my_mididev >= 0)
+		sound_unload_mididev(my_mididev);
+}
+
+
+/*
+ * open/close midi device
+ */
+
+static int midi_opened = FALSE;
+
+static int midi_mode;
+static int coarsetune = 0, finetune = 0;
+
+static int xg_mapping = TRUE;
+static int xg_bankmode = 0;
+
+/* effect sensitivity */
+
+#define FX_CUTOFF	0
+#define FX_RESONANCE	1
+#define FX_ATTACK	2
+#define FX_RELEASE	3
+#define FX_VIBRATE	4
+#define FX_VIBDEPTH	5
+#define FX_VIBDELAY	6
+#define FX_NUMS		7
+
+#define DEF_FX_CUTOFF		170
+#define DEF_FX_RESONANCE	6
+#define DEF_FX_ATTACK		50
+#define DEF_FX_RELEASE		50
+#define DEF_FX_VIBRATE		30
+#define DEF_FX_VIBDEPTH		4
+#define DEF_FX_VIBDELAY		1500
+
+/* effect sense: */
+static int gs_sense[] = 
+{
+	DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+	DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+static int xg_sense[] = 
+{
+	DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+	DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+
+/* current status */
+static MidiStatus curst;
+
+
+static int
+awe_midi_open (int dev, int mode,
+	       void (*input)(int,unsigned char),
+	       void (*output)(int))
+{
+	if (midi_opened)
+		return -EBUSY;
+
+	midi_opened = TRUE;
+
+	midi_mode = MODE_GM;
+
+	curst.queue = Q_NONE;
+	curst.qlen = 0;
+	curst.read = 0;
+	curst.status = 0;
+	curst.chan = 0;
+	memset(curst.buf, 0, sizeof(curst.buf));
+
+	init_midi_status(&curst);
+
+	return 0;
+}
+
+static void
+awe_midi_close (int dev)
+{
+	midi_opened = FALSE;
+}
+
+
+static int
+awe_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+{
+	return -EPERM;
+}
+
+static int
+awe_midi_outputc (int dev, unsigned char midi_byte)
+{
+	if (! midi_opened)
+		return 1;
+
+	/* force to change playing mode */
+	playing_mode = AWE_PLAY_MULTI;
+
+	get_midi_char(&curst, midi_byte);
+	return 1;
+}
+
+
+/*
+ * initialize
+ */
+
+static void init_midi_status(MidiStatus *st)
+{
+	clear_rpn();
+	coarsetune = 0;
+	finetune = 0;
+}
+
+
+/*
+ * RPN & NRPN
+ */
+
+#define MAX_MIDI_CHANNELS	16
+
+/* RPN & NRPN */
+static unsigned char nrpn[MAX_MIDI_CHANNELS];  /* current event is NRPN? */
+static int msb_bit;  /* current event is msb for RPN/NRPN */
+/* RPN & NRPN indeces */
+static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS];
+/* RPN & NRPN values */
+static int rpn_val[MAX_MIDI_CHANNELS];
+
+static void clear_rpn(void)
+{
+	int i;
+	for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
+		nrpn[i] = 0;
+		rpn_msb[i] = 127;
+		rpn_lsb[i] = 127;
+		rpn_val[i] = 0;
+	}
+	msb_bit = 0;
+}
+
+
+/*
+ * process midi queue
+ */
+
+/* status event types */
+typedef void (*StatusEvent)(MidiStatus *st);
+static struct StatusEventList {
+	StatusEvent process;
+	int qlen;
+} status_event[8] = {
+	{midi_note_off, 2},
+	{midi_note_on, 2},
+	{midi_key_pressure, 2},
+	{midi_control_change, 2},
+	{midi_program_change, 1},
+	{midi_channel_pressure, 1},
+	{midi_pitch_wheel, 2},
+	{NULL, 0},
+};
+
+
+/* read a char from fifo and process it */
+static void get_midi_char(MidiStatus *st, int c)
+{
+	if (c == 0xfe) {
+		/* ignore active sense */
+		st->queue = Q_NONE;
+		return;
+	}
+
+	switch (st->queue) {
+	/* case Q_VARLEN: queue_varlen(st, c); break;*/
+	case Q_READ:
+	case Q_SYSEX:
+		queue_read(st, c);
+		break;
+	case Q_NONE:
+		st->read = 0;
+		if ((c & 0xf0) == 0xf0) {
+			special_event(st, c);
+		} else if (c & 0x80) { /* status change */
+			st->status = (c >> 4) & 0x07;
+			st->chan = c & 0x0f;
+			st->queue = Q_READ;
+			st->qlen = status_event[st->status].qlen;
+			if (st->qlen == 0)
+				st->queue = Q_NONE;
+		}
+		break;
+	}
+}
+
+/* 0xfx events */
+static void special_event(MidiStatus *st, int c)
+{
+	switch (c) {
+	case 0xf0: /* system exclusive */
+		st->queue = Q_SYSEX;
+		st->qlen = 0;
+		break;
+	case 0xf1: /* MTC quarter frame */
+	case 0xf3: /* song select */
+		st->queue = Q_READ;
+		st->qlen = 1;
+		break;
+	case 0xf2: /* song position */
+		st->queue = Q_READ;
+		st->qlen = 2;
+		break;
+	}
+}
+
+#if 0
+/* read variable length value */
+static void queue_varlen(MidiStatus *st, int c)
+{
+	st->qlen += (c & 0x7f);
+	if (c & 0x80) {
+		st->qlen <<= 7;
+		return;
+	}
+	if (st->qlen <= 0) {
+		st->qlen = 0;
+		st->queue = Q_NONE;
+	}
+	st->queue = Q_READ;
+	st->read = 0;
+}
+#endif
+
+
+/* read a char */
+static void queue_read(MidiStatus *st, int c)
+{
+	if (st->read < MAX_MIDIBUF) {
+		if (st->queue != Q_SYSEX)
+			c &= 0x7f;
+		st->buf[st->read] = (unsigned char)c;
+	}
+	st->read++;
+	if (st->queue == Q_SYSEX && c == 0xf7) {
+		midi_system_exclusive(st);
+		st->queue = Q_NONE;
+	} else if (st->queue == Q_READ && st->read >= st->qlen) {
+		if (status_event[st->status].process)
+			status_event[st->status].process(st);
+		st->queue = Q_NONE;
+	}
+}
+
+
+/*
+ * status events
+ */
+
+/* note on */
+static void midi_note_on(MidiStatus *st)
+{
+	DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1]));
+	if (st->buf[1] == 0)
+		midi_note_off(st);
+	else
+		awe_start_note(0, st->chan, st->buf[0], st->buf[1]);
+}
+
+/* note off */
+static void midi_note_off(MidiStatus *st)
+{
+	DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1]));
+	awe_kill_note(0, st->chan, st->buf[0], st->buf[1]);
+}
+
+/* key pressure change */
+static void midi_key_pressure(MidiStatus *st)
+{
+	awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]);
+}
+
+/* channel pressure change */
+static void midi_channel_pressure(MidiStatus *st)
+{
+	channels[st->chan].chan_press = st->buf[0];
+	awe_modwheel_change(st->chan, st->buf[0]);
+}
+
+/* pitch wheel change */
+static void midi_pitch_wheel(MidiStatus *st)
+{
+	int val = (int)st->buf[1] * 128 + st->buf[0];
+	awe_bender(0, st->chan, val);
+}
+
+/* program change */
+static void midi_program_change(MidiStatus *st)
+{
+	int preset;
+	preset = st->buf[0];
+	if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127)
+		preset = 0;
+	else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan))
+		preset += 64;
+
+	awe_set_instr(0, st->chan, preset);
+}
+
+#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val)
+#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val)
+#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0)
+
+/* midi control change */
+static void midi_control_change(MidiStatus *st)
+{
+	int cmd = st->buf[0];
+	int val = st->buf[1];
+
+	DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val));
+	if (midi_mode == MODE_XG) {
+		if (xg_control_change(st, cmd, val))
+			return;
+	}
+
+	/* controls #31 - #64 are LSB of #0 - #31 */
+	msb_bit = 1;
+	if (cmd >= 0x20 && cmd < 0x40) {
+		msb_bit = 0;
+		cmd -= 0x20;
+	}
+
+	switch (cmd) {
+	case CTL_SOFT_PEDAL:
+		if (val == 127)
+			add_effect(st->chan, AWE_FX_CUTOFF, -160);
+		else
+			unset_effect(st->chan, AWE_FX_CUTOFF);
+		break;
+
+	case CTL_BANK_SELECT:
+		midi_select_bank(st, val);
+		break;
+		
+	/* set RPN/NRPN parameter */
+	case CTL_REGIST_PARM_NUM_MSB:
+		nrpn[st->chan]=0; rpn_msb[st->chan]=val;
+		break;
+	case CTL_REGIST_PARM_NUM_LSB:
+		nrpn[st->chan]=0; rpn_lsb[st->chan]=val;
+		break;
+	case CTL_NONREG_PARM_NUM_MSB:
+		nrpn[st->chan]=1; rpn_msb[st->chan]=val;
+		break;
+	case CTL_NONREG_PARM_NUM_LSB:
+		nrpn[st->chan]=1; rpn_lsb[st->chan]=val;
+		break;
+
+	/* send RPN/NRPN entry */
+	case CTL_DATA_ENTRY:
+		if (msb_bit)
+			rpn_val[st->chan] = val * 128;
+		else
+			rpn_val[st->chan] |= val;
+		if (nrpn[st->chan])
+			midi_nrpn_event(st);
+		else
+			midi_rpn_event(st);
+		break;
+
+	/* increase/decrease data entry */
+	case CTL_DATA_INCREMENT:
+		rpn_val[st->chan]++;
+		midi_rpn_event(st);
+		break;
+	case CTL_DATA_DECREMENT:
+		rpn_val[st->chan]--;
+		midi_rpn_event(st);
+		break;
+
+	/* default */
+	default:
+		awe_controller(0, st->chan, cmd, val);
+		break;
+	}
+}
+
+/* tone bank change */
+static void midi_select_bank(MidiStatus *st, int val)
+{
+	if (midi_mode == MODE_XG && msb_bit) {
+		xg_bankmode = val;
+		/* XG MSB value; not normal bank selection */
+		switch (val) {
+		case 127: /* remap to drum channel */
+			awe_controller(0, st->chan, CTL_BANK_SELECT, 128);
+			break;
+		default: /* remap to normal channel */
+			awe_controller(0, st->chan, CTL_BANK_SELECT, val);
+			break;
+		}
+		return;
+	} else if (midi_mode == MODE_GS && !msb_bit)
+		/* ignore LSB bank in GS mode (used for mapping) */
+		return;
+
+	/* normal bank controls; accept both MSB and LSB */
+	if (! IS_DRUM_CHANNEL(st->chan)) {
+		if (midi_mode == MODE_XG) {
+			if (xg_bankmode) return;
+			if (val == 64 || val == 126)
+				val = 0;
+		} else if (midi_mode == MODE_GS && val == 127)
+			val = 0;
+		awe_controller(0, st->chan, CTL_BANK_SELECT, val);
+	}
+}
+
+
+/*
+ * RPN events
+ */
+
+static void midi_rpn_event(MidiStatus *st)
+{
+	int type;
+	type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan];
+	switch (type) {
+	case 0x0000: /* Pitch bend sensitivity */
+		/* MSB only / 1 semitone per 128 */
+		if (msb_bit) {
+			channels[st->chan].bender_range = 
+				rpn_val[st->chan] * 100 / 128;
+		}
+		break;
+					
+	case 0x0001: /* fine tuning: */
+		/* MSB/LSB, 8192=center, 100/8192 cent step */
+		finetune = rpn_val[st->chan] - 8192;
+		midi_detune(st->chan, coarsetune, finetune);
+		break;
+
+	case 0x0002: /* coarse tuning */
+		/* MSB only / 8192=center, 1 semitone per 128 */
+		if (msb_bit) {
+			coarsetune = rpn_val[st->chan] - 8192;
+			midi_detune(st->chan, coarsetune, finetune);
+		}
+		break;
+
+	case 0x7F7F: /* "lock-in" RPN */
+		break;
+	}
+}
+
+
+/* tuning:
+ *   coarse = -8192 to 8192 (100 cent per 128)
+ *   fine = -8192 to 8192 (max=100cent)
+ */
+static void midi_detune(int chan, int coarse, int fine)
+{
+	/* 4096 = 1200 cents in AWE parameter */
+	int val;
+	val = coarse * 4096 / (12 * 128);
+	val += fine / 24;
+	if (val)
+		send_effect(chan, AWE_FX_INIT_PITCH, val);
+	else
+		unset_effect(chan, AWE_FX_INIT_PITCH);
+}
+
+
+/*
+ * system exclusive message
+ * GM/GS/XG macros are accepted
+ */
+
+static void midi_system_exclusive(MidiStatus *st)
+{
+	/* GM on */
+	static unsigned char gm_on_macro[] = {
+		0x7e,0x7f,0x09,0x01,
+	};
+	/* XG on */
+	static unsigned char xg_on_macro[] = {
+		0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
+	};
+	/* GS prefix
+	 * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
+	 * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
+	 * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
+	 */
+	static unsigned char gs_pfx_macro[] = {
+		0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
+	};
+
+#if 0
+	/* SC88 system mode set
+	 * single module mode: XX=1
+	 * double module mode: XX=0
+	 */
+	static unsigned char gs_mode_macro[] = {
+		0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/
+	};
+	/* SC88 display macro: XX=01:bitmap, 00:text
+	 */
+	static unsigned char gs_disp_macro[] = {
+		0x41,0x10,0x45,0x12,0x10,/*XX,00*/
+	};
+#endif
+
+	/* GM on */
+	if (memcmp(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
+		if (midi_mode != MODE_GS && midi_mode != MODE_XG)
+			midi_mode = MODE_GM;
+		init_midi_status(st);
+	}
+
+	/* GS macros */
+	else if (memcmp(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
+		if (midi_mode != MODE_GS && midi_mode != MODE_XG)
+			midi_mode = MODE_GS;
+
+		if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) {
+			/* GS reset */
+			init_midi_status(st);
+		}
+
+		else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) {
+			/* drum pattern */
+			int p = st->buf[5] & 0x0f;
+			if (p == 0) p = 9;
+			else if (p < 10) p--;
+			if (st->buf[7] == 0)
+				DRUM_CHANNEL_OFF(p);
+			else
+				DRUM_CHANNEL_ON(p);
+
+		} else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) {
+			/* program */
+			int p = st->buf[5] & 0x0f;
+			if (p == 0) p = 9;
+			else if (p < 10) p--;
+			if (! IS_DRUM_CHANNEL(p))
+				awe_set_instr(0, p, st->buf[7]);
+
+		} else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) {
+			/* reverb mode */
+			awe_set_reverb_mode(st->buf[7]);
+
+		} else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) {
+			/* chorus mode */
+			awe_set_chorus_mode(st->buf[7]);
+
+		} else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) {
+			/* master volume */
+			awe_change_master_volume(st->buf[7]);
+
+		}
+	}
+
+	/* XG on */
+	else if (memcmp(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
+		midi_mode = MODE_XG;
+		xg_mapping = TRUE;
+		xg_bankmode = 0;
+	}
+}
+
+
+/*----------------------------------------------------------------*/
+
+/*
+ * convert NRPN/control values
+ */
+
+static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val)
+{
+	int i, cval;
+	for (i = 0; i < num_tables; i++) {
+		if (table[i].control == type) {
+			cval = table[i].convert(val);
+			send_effect(st->chan, table[i].awe_effect, cval);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val)
+{
+	int i, cval;
+	for (i = 0; i < num_tables; i++) {
+		if (table[i].control == type) {
+			cval = table[i].convert(val);
+			add_effect(st->chan, table[i].awe_effect|0x80, cval);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+
+/*
+ * AWE32 NRPN effects
+ */
+
+static unsigned short fx_delay(int val);
+static unsigned short fx_attack(int val);
+static unsigned short fx_hold(int val);
+static unsigned short fx_decay(int val);
+static unsigned short fx_the_value(int val);
+static unsigned short fx_twice_value(int val);
+static unsigned short fx_conv_pitch(int val);
+static unsigned short fx_conv_Q(int val);
+
+/* function for each NRPN */		/* [range]  units */
+#define fx_env1_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_env1_attack	fx_attack	/* [0,5940] 1msec */
+#define fx_env1_hold	fx_hold		/* [0,8191] 1msec */
+#define fx_env1_decay	fx_decay	/* [0,5940] 4msec */
+#define fx_env1_release	fx_decay	/* [0,5940] 4msec */
+#define fx_env1_sustain	fx_the_value	/* [0,127] 0.75dB */
+#define fx_env1_pitch	fx_the_value	/* [-127,127] 9.375cents */
+#define fx_env1_cutoff	fx_the_value	/* [-127,127] 56.25cents */
+
+#define fx_env2_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_env2_attack	fx_attack	/* [0,5940] 1msec */
+#define fx_env2_hold	fx_hold		/* [0,8191] 1msec */
+#define fx_env2_decay	fx_decay	/* [0,5940] 4msec */
+#define fx_env2_release	fx_decay	/* [0,5940] 4msec */
+#define fx_env2_sustain	fx_the_value	/* [0,127] 0.75dB */
+
+#define fx_lfo1_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_lfo1_freq	fx_twice_value	/* [0,127] 84mHz */
+#define fx_lfo1_volume	fx_twice_value	/* [0,127] 0.1875dB */
+#define fx_lfo1_pitch	fx_the_value	/* [-127,127] 9.375cents */
+#define fx_lfo1_cutoff	fx_twice_value	/* [-64,63] 56.25cents */
+
+#define fx_lfo2_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_lfo2_freq	fx_twice_value	/* [0,127] 84mHz */
+#define fx_lfo2_pitch	fx_the_value	/* [-127,127] 9.375cents */
+
+#define fx_init_pitch	fx_conv_pitch	/* [-8192,8192] cents */
+#define fx_chorus	fx_the_value	/* [0,255] -- */
+#define fx_reverb	fx_the_value	/* [0,255] -- */
+#define fx_cutoff	fx_twice_value	/* [0,127] 62Hz */
+#define fx_filterQ	fx_conv_Q	/* [0,127] -- */
+
+static unsigned short fx_delay(int val)
+{
+	return (unsigned short)calc_parm_delay(val);
+}
+
+static unsigned short fx_attack(int val)
+{
+	return (unsigned short)calc_parm_attack(val);
+}
+
+static unsigned short fx_hold(int val)
+{
+	return (unsigned short)calc_parm_hold(val);
+}
+
+static unsigned short fx_decay(int val)
+{
+	return (unsigned short)calc_parm_decay(val);
+}
+
+static unsigned short fx_the_value(int val)
+{
+	return (unsigned short)(val & 0xff);
+}
+
+static unsigned short fx_twice_value(int val)
+{
+	return (unsigned short)((val * 2) & 0xff);
+}
+
+static unsigned short fx_conv_pitch(int val)
+{
+	return (short)(val * 4096 / 1200);
+}
+
+static unsigned short fx_conv_Q(int val)
+{
+	return (unsigned short)((val / 8) & 0xff);
+}
+
+
+static ConvTable awe_effects[] =
+{
+	{ 0, AWE_FX_LFO1_DELAY,	fx_lfo1_delay},
+	{ 1, AWE_FX_LFO1_FREQ,	fx_lfo1_freq},
+	{ 2, AWE_FX_LFO2_DELAY,	fx_lfo2_delay},
+	{ 3, AWE_FX_LFO2_FREQ,	fx_lfo2_freq},
+
+	{ 4, AWE_FX_ENV1_DELAY,	fx_env1_delay},
+	{ 5, AWE_FX_ENV1_ATTACK,fx_env1_attack},
+	{ 6, AWE_FX_ENV1_HOLD,	fx_env1_hold},
+	{ 7, AWE_FX_ENV1_DECAY,	fx_env1_decay},
+	{ 8, AWE_FX_ENV1_SUSTAIN,	fx_env1_sustain},
+	{ 9, AWE_FX_ENV1_RELEASE,	fx_env1_release},
+
+	{10, AWE_FX_ENV2_DELAY,	fx_env2_delay},
+	{11, AWE_FX_ENV2_ATTACK,	fx_env2_attack},
+	{12, AWE_FX_ENV2_HOLD,	fx_env2_hold},
+	{13, AWE_FX_ENV2_DECAY,	fx_env2_decay},
+	{14, AWE_FX_ENV2_SUSTAIN,	fx_env2_sustain},
+	{15, AWE_FX_ENV2_RELEASE,	fx_env2_release},
+
+	{16, AWE_FX_INIT_PITCH,	fx_init_pitch},
+	{17, AWE_FX_LFO1_PITCH,	fx_lfo1_pitch},
+	{18, AWE_FX_LFO2_PITCH,	fx_lfo2_pitch},
+	{19, AWE_FX_ENV1_PITCH,	fx_env1_pitch},
+	{20, AWE_FX_LFO1_VOLUME,	fx_lfo1_volume},
+	{21, AWE_FX_CUTOFF,		fx_cutoff},
+	{22, AWE_FX_FILTERQ,	fx_filterQ},
+	{23, AWE_FX_LFO1_CUTOFF,	fx_lfo1_cutoff},
+	{24, AWE_FX_ENV1_CUTOFF,	fx_env1_cutoff},
+	{25, AWE_FX_CHORUS,		fx_chorus},
+	{26, AWE_FX_REVERB,		fx_reverb},
+};
+
+static int num_awe_effects = numberof(awe_effects);
+
+
+/*
+ * GS(SC88) NRPN effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static unsigned short gs_cutoff(int val)
+{
+	return (val - 64) * gs_sense[FX_CUTOFF] / 50;
+}
+
+/* resonance: 0 to 15(max) */
+static unsigned short gs_filterQ(int val)
+{
+	return (val - 64) * gs_sense[FX_RESONANCE] / 50;
+}
+
+/* attack: */
+static unsigned short gs_attack(int val)
+{
+	return -(val - 64) * gs_sense[FX_ATTACK] / 50;
+}
+
+/* decay: */
+static unsigned short gs_decay(int val)
+{
+	return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* release: */
+static unsigned short gs_release(int val)
+{
+	return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* vibrato freq: 0.042Hz step, max=255 */
+static unsigned short gs_vib_rate(int val)
+{
+	return (val - 64) * gs_sense[FX_VIBRATE] / 50;
+}
+
+/* vibrato depth: max=127, 1 octave */
+static unsigned short gs_vib_depth(int val)
+{
+	return (val - 64) * gs_sense[FX_VIBDEPTH] / 50;
+}
+
+/* vibrato delay: -0.725msec step */
+static unsigned short gs_vib_delay(int val)
+{
+	return -(val - 64) * gs_sense[FX_VIBDELAY] / 50;
+}
+
+static ConvTable gs_effects[] =
+{
+	{32, AWE_FX_CUTOFF,	gs_cutoff},
+	{33, AWE_FX_FILTERQ,	gs_filterQ},
+	{99, AWE_FX_ENV2_ATTACK, gs_attack},
+	{100, AWE_FX_ENV2_DECAY, gs_decay},
+	{102, AWE_FX_ENV2_RELEASE, gs_release},
+	{8, AWE_FX_LFO1_FREQ, gs_vib_rate},
+	{9, AWE_FX_LFO1_VOLUME, gs_vib_depth},
+	{10, AWE_FX_LFO1_DELAY, gs_vib_delay},
+};
+
+static int num_gs_effects = numberof(gs_effects);
+
+
+/*
+ * NRPN events: accept as AWE32/SC88 specific controls
+ */
+
+static void midi_nrpn_event(MidiStatus *st)
+{
+	if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) {
+		if (! msb_bit) /* both MSB/LSB necessary */
+			send_converted_effect(awe_effects, num_awe_effects,
+					      st, rpn_lsb[st->chan],
+					      rpn_val[st->chan] - 8192);
+	} else if (rpn_msb[st->chan] == 1) {
+		if (msb_bit) /* only MSB is valid */
+			add_converted_effect(gs_effects, num_gs_effects,
+					     st, rpn_lsb[st->chan],
+					     rpn_val[st->chan] / 128);
+	}
+}
+
+
+/*
+ * XG control effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static unsigned short xg_cutoff(int val)
+{
+	return (val - 64) * xg_sense[FX_CUTOFF] / 64;
+}
+
+/* resonance: 0(open) to 15(most nasal) */
+static unsigned short xg_filterQ(int val)
+{
+	return (val - 64) * xg_sense[FX_RESONANCE] / 64;
+}
+
+/* attack: */
+static unsigned short xg_attack(int val)
+{
+	return -(val - 64) * xg_sense[FX_ATTACK] / 64;
+}
+
+/* release: */
+static unsigned short xg_release(int val)
+{
+	return -(val - 64) * xg_sense[FX_RELEASE] / 64;
+}
+
+static ConvTable xg_effects[] =
+{
+	{71, AWE_FX_CUTOFF,	xg_cutoff},
+	{74, AWE_FX_FILTERQ,	xg_filterQ},
+	{72, AWE_FX_ENV2_RELEASE, xg_release},
+	{73, AWE_FX_ENV2_ATTACK, xg_attack},
+};
+
+static int num_xg_effects = numberof(xg_effects);
+
+static int xg_control_change(MidiStatus *st, int cmd, int val)
+{
+	return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val);
+}
+
+#endif /* CONFIG_AWE32_MIDIEMU */
+
+
+/*----------------------------------------------------------------*/
+
+/*
+ * device / lowlevel (module) interface
+ */
+
+int __init attach_awe(void)
+{
+	return _attach_awe() ? 0 : -ENODEV;
+}
+
+void __exit unload_awe(void)
+{
+	_unload_awe();
+#ifdef __ISAPNP__
+	if (isapnp)
+		awe_deactivate_isapnp();
+#endif /* isapnp */
+}
+
+
+module_init(attach_awe);
+module_exit(unload_awe);
+
+#ifndef MODULE
+static int __init setup_awe(char *str)
+{
+	/* io, memsize, isapnp */
+	int ints[4];
+
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io = ints[1];
+	memsize = ints[2];
+	isapnp = ints[3];
+
+	return 1;
+}
+
+__setup("awe=", setup_awe);
+#endif
diff -Nru linux/sound/oss/awe_wave.h linux-2.4.19-pre5-mjc/sound/oss/awe_wave.h
--- linux/sound/oss/awe_wave.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/awe_wave.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,77 @@
+/*
+ * sound/awe_config.h
+ *
+ * Configuration of AWE32/SB32/AWE64 wave table synth driver.
+ *   version 0.4.4; Jan. 4, 2000
+ *
+ * Copyright (C) 1996-1998 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * chorus & reverb effects send for FM chip: from 0 to 0xff
+ * larger numbers often cause weird sounds.
+ */
+
+#define DEF_FM_CHORUS_DEPTH	0x10
+#define DEF_FM_REVERB_DEPTH	0x10
+
+
+/*
+ * other compile conditions
+ */
+
+/* initialize FM passthrough even without extended RAM */
+#undef AWE_ALWAYS_INIT_FM
+
+/* debug on */
+#define AWE_DEBUG_ON
+
+/* GUS compatible mode */
+#define AWE_HAS_GUS_COMPATIBILITY
+
+/* add MIDI emulation by wavetable */
+#define CONFIG_AWE32_MIDIEMU
+
+/* add mixer control of emu8000 equalizer */
+#undef CONFIG_AWE32_MIXER
+
+/* use new volume calculation method as default */
+#define AWE_USE_NEW_VOLUME_CALC
+
+/* check current volume target for searching empty voices */
+#define AWE_CHECK_VTARGET
+
+/* allow sample sharing */
+#define AWE_ALLOW_SAMPLE_SHARING
+
+/*
+ * AWE32 card configuration:
+ * uncomment the following lines *ONLY* when auto detection doesn't
+ * work properly on your machine.
+ */
+
+/*#define AWE_DEFAULT_BASE_ADDR	0x620*/	/* base port address */
+/*#define AWE_DEFAULT_MEM_SIZE	512*/	/* kbytes */
+
+/*
+ * AWE driver version number
+ */
+#define AWE_MAJOR_VERSION	0
+#define AWE_MINOR_VERSION	4
+#define AWE_TINY_VERSION	4
+#define AWE_VERSION_NUMBER	((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION)
+#define AWEDRV_VERSION		"0.4.4"
diff -Nru linux/sound/oss/bin2hex.c linux-2.4.19-pre5-mjc/sound/oss/bin2hex.c
--- linux/sound/oss/bin2hex.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/bin2hex.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <string.h>
+
+int main( int argc, const char * argv [] )
+{
+    const char * varname;
+    int i = 0;
+    int c;
+    int id = 0;
+
+    if(argv[1] && strcmp(argv[1],"-i")==0)
+    {
+    	argv++;
+    	argc--;
+    	id=1;
+    }
+    	
+    if(argc==1)
+    {
+    	fprintf(stderr, "bin2hex: [-i] firmware\n");
+    	exit(1);
+    }
+    
+    varname = argv[1];
+    printf( "/* automatically generated by bin2hex */\n" );
+    printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
+
+    while ( ( c = getchar( ) ) != EOF )
+    {
+	if ( i != 0 && i % 10 == 0 )
+	    printf( "\n" );
+	printf( "0x%02lx,", c & 0xFFl );
+	i++;
+    }
+
+    printf( "};\nstatic int %sLen =  %d;\n", varname, i );
+    return 0;
+}
diff -Nru linux/sound/oss/btaudio.c linux-2.4.19-pre5-mjc/sound/oss/btaudio.c
--- linux/sound/oss/btaudio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/btaudio.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1072 @@
+/*
+    btaudio - bt878 audio dma driver for linux 2.4.x
+
+    (c) 2000 Gerd Knorr <kraxel@bytesex.org>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+
+/* mmio access */
+#define btwrite(dat,adr)    writel((dat), (bta->mmio+(adr)))
+#define btread(adr)         readl(bta->mmio+(adr))
+
+#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+/* registers (shifted because bta->mmio is long) */
+#define REG_INT_STAT      (0x100 >> 2)
+#define REG_INT_MASK      (0x104 >> 2)
+#define REG_GPIO_DMA_CTL  (0x10c >> 2)
+#define REG_PACKET_LEN    (0x110 >> 2)
+#define REG_RISC_STRT_ADD (0x114 >> 2)
+#define REG_RISC_COUNT    (0x120 >> 2)
+
+/* IRQ bits - REG_INT_(STAT|MASK) */
+#define IRQ_SCERR         (1 << 19)
+#define IRQ_OCERR         (1 << 18)
+#define IRQ_PABORT        (1 << 17)
+#define IRQ_RIPERR        (1 << 16)
+#define IRQ_PPERR         (1 << 15)
+#define IRQ_FDSR          (1 << 14)
+#define IRQ_FTRGT         (1 << 13)
+#define IRQ_FBUS          (1 << 12)
+#define IRQ_RISCI         (1 << 11)
+#define IRQ_OFLOW         (1 <<  3)
+
+#define IRQ_BTAUDIO       (IRQ_SCERR | IRQ_OCERR | IRQ_PABORT | IRQ_RIPERR |\
+			   IRQ_PPERR | IRQ_FDSR  | IRQ_FTRGT  | IRQ_FBUS   |\
+			   IRQ_RISCI)
+
+/* REG_GPIO_DMA_CTL bits */
+#define DMA_CTL_A_PWRDN   (1 << 26)
+#define DMA_CTL_DA_SBR    (1 << 14)
+#define DMA_CTL_DA_ES2    (1 << 13)
+#define DMA_CTL_ACAP_EN   (1 <<  4)
+#define DMA_CTL_RISC_EN   (1 <<  1)
+#define DMA_CTL_FIFO_EN   (1 <<  0)
+
+/* RISC instructions */
+#define RISC_WRITE        (0x01 << 28)
+#define RISC_JUMP         (0x07 << 28)
+#define RISC_SYNC         (0x08 << 28)
+
+/* RISC bits */
+#define RISC_WR_SOL       (1 << 27)
+#define RISC_WR_EOL       (1 << 26)
+#define RISC_IRQ          (1 << 24)
+#define RISC_SYNC_RESYNC  (1 << 15)
+#define RISC_SYNC_FM1     0x06
+#define RISC_SYNC_VRO     0x0c
+
+#define HWBASE_AD (448000)
+
+/* -------------------------------------------------------------- */
+
+struct btaudio {
+	/* linked list */
+	struct btaudio *next;
+
+	/* device info */
+	int            dsp_digital;
+	int            dsp_analog;
+	int            mixer_dev;
+	struct pci_dev *pci;
+	unsigned int   irq;
+	unsigned long  mem;
+	unsigned long  *mmio;
+
+	/* locking */
+	int            users;
+	struct semaphore lock;
+
+	/* risc instructions */
+	unsigned int   risc_size;
+	unsigned long  *risc_cpu;
+	dma_addr_t     risc_dma;
+
+	/* audio data */
+	unsigned int   buf_size;
+	unsigned char  *buf_cpu;
+	dma_addr_t     buf_dma;
+
+	/* buffer setup */
+	int line_bytes;
+	int line_count;
+	int block_bytes;
+	int block_count;
+
+	/* read fifo management */
+	int recording;
+	int dma_block;
+	int read_offset;
+	int read_count;
+	wait_queue_head_t readq;
+
+	/* settings */
+	int gain[3];
+	int source;
+	int bits;
+	int decimation;
+	int mixcount;
+	int sampleshift;
+	int channels;
+	int analog;
+};
+
+static struct btaudio *btaudios = NULL;
+static unsigned int dsp1 = -1;
+static unsigned int dsp2 = -1;
+static unsigned int mixer = -1;
+static unsigned int debug = 0;
+static unsigned int irq_debug = 0;
+static int digital = 1;
+static int analog = 1;
+static int rate = 32000;
+
+/* -------------------------------------------------------------- */
+
+#define BUF_DEFAULT 128*1024
+#define BUF_MIN         8192
+
+static int alloc_buffer(struct btaudio *bta)
+{
+	if (NULL == bta->buf_cpu) {
+		for (bta->buf_size = BUF_DEFAULT; bta->buf_size >= BUF_MIN;
+		     bta->buf_size = bta->buf_size >> 1) {
+			bta->buf_cpu = pci_alloc_consistent
+				(bta->pci, bta->buf_size, &bta->buf_dma);
+			if (NULL != bta->buf_cpu)
+				break;
+		}
+		if (NULL == bta->buf_cpu)
+			return -ENOMEM;
+		memset(bta->buf_cpu,0,bta->buf_size);
+	}
+	if (NULL == bta->risc_cpu) {
+		bta->risc_size = PAGE_SIZE;
+		bta->risc_cpu = pci_alloc_consistent
+			(bta->pci, bta->risc_size, &bta->risc_dma);
+		if (NULL == bta->risc_cpu)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static void free_buffer(struct btaudio *bta)
+{
+	if (NULL != bta->buf_cpu) {
+		pci_free_consistent(bta->pci, bta->buf_size,
+				    bta->buf_cpu, bta->buf_dma);
+		bta->buf_cpu = NULL;
+	}
+	if (NULL != bta->risc_cpu) {
+		pci_free_consistent(bta->pci, bta->risc_size,
+				    bta->risc_cpu, bta->risc_dma);
+		bta->risc_cpu = NULL;
+	}
+}
+
+static int make_risc(struct btaudio *bta)
+{
+	int rp, bp, line, block;
+	unsigned long risc;
+
+	bta->block_bytes = bta->buf_size >> 4;
+	bta->block_count = 1 << 4;
+	bta->line_bytes  = bta->block_bytes;
+	bta->line_count  = bta->block_count;
+	while (bta->line_bytes > 4095) {
+		bta->line_bytes >>= 1;
+		bta->line_count <<= 1;
+	}
+	if (bta->line_count > 255)
+		return -EINVAL;
+	if (debug)
+		printk(KERN_DEBUG
+		       "btaudio: bufsize=%d - bs=%d bc=%d - ls=%d, lc=%d\n",
+		       bta->buf_size,bta->block_bytes,bta->block_count,
+		       bta->line_bytes,bta->line_count);
+        rp = 0; bp = 0;
+	block = 0;
+	bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_FM1);
+	bta->risc_cpu[rp++] = cpu_to_le32(0);
+	for (line = 0; line < bta->line_count; line++) {
+		risc  = RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL;
+		risc |= bta->line_bytes;
+		if (0 == (bp & (bta->block_bytes-1))) {
+			risc |= RISC_IRQ;
+			risc |= (block  & 0x0f) << 16;
+			risc |= (~block & 0x0f) << 20;
+			block++;
+		}
+		bta->risc_cpu[rp++] = cpu_to_le32(risc);
+		bta->risc_cpu[rp++] = cpu_to_le32(bta->buf_dma + bp);
+		bp += bta->line_bytes;
+	}
+	bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_VRO);
+	bta->risc_cpu[rp++] = cpu_to_le32(0);
+	bta->risc_cpu[rp++] = cpu_to_le32(RISC_JUMP); 
+	bta->risc_cpu[rp++] = cpu_to_le32(bta->risc_dma);
+	return 0;
+}
+
+static int start_recording(struct btaudio *bta)
+{
+	int ret;
+
+	if (0 != (ret = alloc_buffer(bta)))
+		return ret;
+	if (0 != (ret = make_risc(bta)))
+		return ret;
+
+	btwrite(bta->risc_dma, REG_RISC_STRT_ADD);
+	btwrite((bta->line_count << 16) | bta->line_bytes,
+		REG_PACKET_LEN);
+	btwrite(IRQ_BTAUDIO, REG_INT_MASK);
+	if (bta->analog) {
+		btwrite(DMA_CTL_ACAP_EN |
+			DMA_CTL_RISC_EN |
+			DMA_CTL_FIFO_EN |
+			DMA_CTL_DA_ES2  |
+			((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) |
+			(bta->gain[bta->source] << 28) |
+			(bta->source            << 24) |
+			(bta->decimation        <<  8),
+			REG_GPIO_DMA_CTL);
+	} else {
+		btwrite(DMA_CTL_ACAP_EN |
+			DMA_CTL_RISC_EN |
+			DMA_CTL_FIFO_EN |
+			DMA_CTL_DA_ES2  |
+			DMA_CTL_A_PWRDN |
+			(1 << 6)   |
+			((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) |
+			(bta->gain[bta->source] << 28) |
+			(bta->source            << 24) |
+			(bta->decimation        <<  8),
+			REG_GPIO_DMA_CTL);
+	}
+	bta->dma_block = 0;
+	bta->read_offset = 0;
+	bta->read_count = 0;
+	bta->recording = 1;
+	if (debug)
+		printk(KERN_DEBUG "btaudio: recording started\n");
+	return 0;
+}
+
+static void stop_recording(struct btaudio *bta)
+{
+        btand(~15, REG_GPIO_DMA_CTL);
+	bta->recording = 0;
+	if (debug)
+		printk(KERN_DEBUG "btaudio: recording stopped\n");
+}
+
+
+/* -------------------------------------------------------------- */
+
+static int btaudio_mixer_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct btaudio *bta;
+
+	for (bta = btaudios; bta != NULL; bta = bta->next)
+		if (bta->mixer_dev == minor)
+			break;
+	if (NULL == bta)
+		return -ENODEV;
+
+	if (debug)
+		printk("btaudio: open mixer [%d]\n",minor);
+	file->private_data = bta;
+	return 0;
+}
+
+static int btaudio_mixer_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int btaudio_mixer_ioctl(struct inode *inode, struct file *file,
+			       unsigned int cmd, unsigned long arg)
+{
+	struct btaudio *bta = file->private_data;
+	int ret,val=0,i=0;
+
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		memset(&info,0,sizeof(info));
+                strncpy(info.id,"bt878",sizeof(info.id)-1);
+                strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1);
+                info.modify_counter = bta->mixcount;
+                if (copy_to_user((void *)arg, &info, sizeof(info)))
+                        return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		memset(&info,0,sizeof(info));
+                strncpy(info.id,"bt878",sizeof(info.id)-1);
+                strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1);
+                if (copy_to_user((void *)arg, &info, sizeof(info)))
+                        return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	/* read */
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+	switch (cmd) {
+	case MIXER_READ(SOUND_MIXER_CAPS):
+		ret = SOUND_CAP_EXCL_INPUT;
+		break;
+	case MIXER_READ(SOUND_MIXER_STEREODEVS):
+		ret = 0;
+		break;
+	case MIXER_READ(SOUND_MIXER_RECMASK):
+	case MIXER_READ(SOUND_MIXER_DEVMASK):
+		ret = SOUND_MASK_LINE1|SOUND_MASK_LINE2|SOUND_MASK_LINE3;
+		break;
+
+	case MIXER_WRITE(SOUND_MIXER_RECSRC):
+		if (val & SOUND_MASK_LINE1 && bta->source != 0)
+			bta->source = 0;
+		else if (val & SOUND_MASK_LINE2 && bta->source != 1)
+			bta->source = 1;
+		else if (val & SOUND_MASK_LINE3 && bta->source != 2)
+			bta->source = 2;
+		btaor((bta->gain[bta->source] << 28) |
+		      (bta->source            << 24),
+		      0x0cffffff, REG_GPIO_DMA_CTL);
+	case MIXER_READ(SOUND_MIXER_RECSRC):
+		switch (bta->source) {
+		case 0:  ret = SOUND_MASK_LINE1; break;
+		case 1:  ret = SOUND_MASK_LINE2; break;
+		case 2:  ret = SOUND_MASK_LINE3; break;
+		default: ret = 0;
+		}
+		break;
+
+	case MIXER_WRITE(SOUND_MIXER_LINE1):
+	case MIXER_WRITE(SOUND_MIXER_LINE2):
+	case MIXER_WRITE(SOUND_MIXER_LINE3):
+		if (MIXER_WRITE(SOUND_MIXER_LINE1) == cmd)
+			i = 0;
+		if (MIXER_WRITE(SOUND_MIXER_LINE2) == cmd)
+			i = 1;
+		if (MIXER_WRITE(SOUND_MIXER_LINE3) == cmd)
+			i = 2;
+		bta->gain[i] = (val & 0xff) * 15 / 100;
+		if (bta->gain[i] > 15) bta->gain[i] = 15;
+		if (bta->gain[i] <  0) bta->gain[i] =  0;
+		if (i == bta->source)
+			btaor((bta->gain[bta->source]<<28),
+			      0x0fffffff, REG_GPIO_DMA_CTL);
+		ret  = bta->gain[i] * 100 / 15;
+		ret |= ret << 8;
+		break;
+
+	case MIXER_READ(SOUND_MIXER_LINE1):
+	case MIXER_READ(SOUND_MIXER_LINE2):
+	case MIXER_READ(SOUND_MIXER_LINE3):
+		if (MIXER_READ(SOUND_MIXER_LINE1) == cmd)
+			i = 0;
+		if (MIXER_READ(SOUND_MIXER_LINE2) == cmd)
+			i = 1;
+		if (MIXER_READ(SOUND_MIXER_LINE3) == cmd)
+			i = 2;
+		ret  = bta->gain[i] * 100 / 15;
+		ret |= ret << 8;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (put_user(ret, (int *)arg))
+		return -EFAULT;
+	return 0;
+}
+
+static struct file_operations btaudio_mixer_fops = {
+	owner:   THIS_MODULE,
+	llseek:  no_llseek,
+	open:    btaudio_mixer_open,
+	release: btaudio_mixer_release,
+	ioctl:   btaudio_mixer_ioctl,
+};
+
+/* -------------------------------------------------------------- */
+
+static int btaudio_dsp_open(struct inode *inode, struct file *file,
+			    struct btaudio *bta, int analog)
+{
+	down(&bta->lock);
+	if (bta->users)
+		goto busy;
+	bta->users++;
+	file->private_data = bta;
+
+	bta->analog = analog;
+	bta->dma_block = 0;
+	bta->read_offset = 0;
+	bta->read_count = 0;
+	bta->sampleshift = 0;
+
+	up(&bta->lock);
+	return 0;
+
+ busy:
+	up(&bta->lock);
+	return -EBUSY;
+}
+
+static int btaudio_dsp_open_digital(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct btaudio *bta;
+
+	for (bta = btaudios; bta != NULL; bta = bta->next)
+		if (bta->dsp_digital == minor)
+			break;
+	if (NULL == bta)
+		return -ENODEV;
+	
+	if (debug)
+		printk("btaudio: open digital dsp [%d]\n",minor);
+	return btaudio_dsp_open(inode,file,bta,0);
+}
+
+static int btaudio_dsp_open_analog(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct btaudio *bta;
+
+	for (bta = btaudios; bta != NULL; bta = bta->next)
+		if (bta->dsp_analog == minor)
+			break;
+	if (NULL == bta)
+		return -ENODEV;
+
+	if (debug)
+		printk("btaudio: open analog dsp [%d]\n",minor);
+	return btaudio_dsp_open(inode,file,bta,1);
+}
+
+static int btaudio_dsp_release(struct inode *inode, struct file *file)
+{
+	struct btaudio *bta = file->private_data;
+
+	down(&bta->lock);
+	if (bta->recording)
+		stop_recording(bta);
+	bta->users--;
+	up(&bta->lock);
+	return 0;
+}
+
+static ssize_t btaudio_dsp_read(struct file *file, char *buffer,
+				size_t swcount, loff_t *ppos)
+{
+	struct btaudio *bta = file->private_data;
+	int hwcount = swcount << bta->sampleshift;
+	int nsrc, ndst, err, ret = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&bta->readq, &wait);
+	down(&bta->lock);
+	while (swcount > 0) {
+		if (0 == bta->read_count) {
+			if (!bta->recording) {
+				if (0 != (err = start_recording(bta))) {
+					if (0 == ret)
+						ret = err;
+					break;
+				}
+			}
+			if (file->f_flags & O_NONBLOCK) {
+				if (0 == ret)
+					ret = -EAGAIN;
+				break;
+			}
+			up(&bta->lock);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+			down(&bta->lock);
+			if(signal_pending(current)) {
+				if (0 == ret)
+					ret = -EINTR;
+				break;
+			}
+		}
+		nsrc = (bta->read_count < hwcount) ? bta->read_count : hwcount;
+		if (nsrc > bta->buf_size - bta->read_offset)
+			nsrc = bta->buf_size - bta->read_offset;
+		ndst = nsrc >> bta->sampleshift;
+		
+		if ((bta->analog  && 0 == bta->sampleshift) ||
+		    (!bta->analog && 2 == bta->channels)) {
+			/* just copy */
+			if (copy_to_user(buffer + ret, bta->buf_cpu + bta->read_offset, nsrc)) {
+				if (0 == ret)
+					ret = -EFAULT;
+				break;
+			}
+
+		} else if (!bta->analog) {
+			/* stereo => mono (digital audio) */
+			__s16 *src = (__s16*)(bta->buf_cpu + bta->read_offset);
+			__s16 *dst = (__s16*)(buffer + ret);
+			__s16 avg;
+			int n = ndst>>1;
+			if (0 != verify_area(VERIFY_WRITE,dst,ndst)) {
+				if (0 == ret)
+					ret = -EFAULT;
+				break;
+			}
+			for (; n; n--, dst++) {
+				avg  = (__s16)le16_to_cpu(*src) / 2; src++;
+				avg += (__s16)le16_to_cpu(*src) / 2; src++;
+				__put_user(cpu_to_le16(avg),(__u16*)(dst));
+			}
+
+		} else if (8 == bta->bits) {
+			/* copy + byte downsampling (audio A/D) */
+			__u8 *src = bta->buf_cpu + bta->read_offset;
+			__u8 *dst = buffer + ret;
+			int n = ndst;
+			if (0 != verify_area(VERIFY_WRITE,dst,ndst)) {
+				if (0 == ret)
+					ret = -EFAULT;
+				break;
+			}
+			for (; n; n--, src += (1 << bta->sampleshift), dst++)
+				__put_user(*src,(__u8*)(dst));
+
+		} else {
+			/* copy + word downsampling (audio A/D) */
+			__u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset);
+			__u16 *dst = (__u16*)(buffer + ret);
+			int n = ndst>>1;
+			if (0 != verify_area(VERIFY_WRITE,dst,ndst)) {
+				if (0 == ret)
+					ret = -EFAULT;
+				break;
+			}
+			for (; n; n--, src += (1 << bta->sampleshift), dst++)
+				__put_user(*src,(__u16*)(dst));
+		}
+
+		ret     += ndst;
+		swcount -= ndst;
+		hwcount -= nsrc;
+		bta->read_count  -= nsrc;
+		bta->read_offset += nsrc;
+		if (bta->read_offset == bta->buf_size)
+			bta->read_offset = 0;
+	}
+	up(&bta->lock);
+	remove_wait_queue(&bta->readq, &wait);
+	current->state = TASK_RUNNING;
+	return ret;
+}
+
+static ssize_t btaudio_dsp_write(struct file *file, const char *buffer,
+				 size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static int btaudio_dsp_ioctl(struct inode *inode, struct file *file,
+			     unsigned int cmd, unsigned long arg)
+{
+	struct btaudio *bta = file->private_data;
+	int s, i, ret, val = 0;
+	
+        switch (cmd) {
+        case OSS_GETVERSION:
+                return put_user(SOUND_VERSION, (int *)arg);
+        case SNDCTL_DSP_GETCAPS:
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int*)arg))
+			return -EFAULT;
+		if (bta->analog) {
+			for (s = 0; s < 16; s++)
+				if (val << s >= HWBASE_AD*4/15)
+					break;
+			for (i = 15; i >= 5; i--)
+				if (val << s <= HWBASE_AD*4/i)
+					break;
+			bta->sampleshift = s;
+			bta->decimation  = i;
+			if (debug)
+				printk(KERN_DEBUG "btaudio: rate: req=%d  "
+				       "dec=%d shift=%d hwrate=%d swrate=%d\n",
+				       val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s);
+		} else {
+			bta->sampleshift = (bta->channels == 2) ? 0 : 1;
+			bta->decimation  = 0;
+		}
+		if (bta->recording) {
+			down(&bta->lock);
+			stop_recording(bta);
+			start_recording(bta);
+			up(&bta->lock);
+		}
+		/* fall through */
+        case SOUND_PCM_READ_RATE:
+		if (bta->analog) {
+			return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, (int*)arg);
+		} else {
+			return put_user(rate, (int*)arg);
+		}
+
+        case SNDCTL_DSP_STEREO:
+		if (!bta->analog) {
+			if (get_user(val, (int*)arg))
+				return -EFAULT;
+			bta->channels    = (val > 0) ? 2 : 1;
+			bta->sampleshift = (bta->channels == 2) ? 0 : 1;
+			if (debug)
+				printk(KERN_INFO
+				       "btaudio: stereo=%d channels=%d\n",
+				       val,bta->channels);
+		} else {
+			if (val == 1)
+				return -EFAULT;
+			else {
+				bta->channels = 1;
+				if (debug)
+					printk(KERN_INFO
+					       "btaudio: stereo=0 channels=1\n");
+			}
+		}
+		return put_user((bta->channels)-1, (int *)arg);
+
+        case SNDCTL_DSP_CHANNELS:
+		if (!analog) {
+			if (get_user(val, (int*)arg))
+				return -EFAULT;
+			bta->channels    = (val > 1) ? 2 : 1;
+			bta->sampleshift = (bta->channels == 2) ? 0 : 1;
+			if (debug)
+				printk(KERN_DEBUG
+				       "btaudio: val=%d channels=%d\n",
+				       val,bta->channels);
+		}
+		/* fall through */
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user(bta->channels, (int *)arg);
+		
+        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+		if (analog)
+			return put_user(AFMT_S16_LE|AFMT_S8, (int*)arg);
+		else
+			return put_user(AFMT_S16_LE, (int*)arg);
+
+        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int*)arg))
+			return -EFAULT;
+                if (val != AFMT_QUERY) {
+			if (analog)
+				bta->bits = (val == AFMT_S8) ? 8 : 16;
+			else
+				bta->bits = 16;
+			if (bta->recording) {
+				down(&bta->lock);
+				stop_recording(bta);
+				start_recording(bta);
+				up(&bta->lock);
+			}
+		}
+		if (debug)
+			printk(KERN_DEBUG "btaudio: fmt: bits=%d\n",bta->bits);
+                return put_user((bta->bits==16) ? AFMT_S16_LE : AFMT_S8,
+				(int*)arg);
+		break;
+        case SOUND_PCM_READ_BITS:
+		return put_user(bta->bits, (int*)arg);
+
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_RESET:
+		if (bta->recording) {
+			down(&bta->lock);
+			stop_recording(bta);
+			up(&bta->lock);
+		}
+		return 0;
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (!bta->recording) {
+			if (0 != (ret = alloc_buffer(bta)))
+				return ret;
+			if (0 != (ret = make_risc(bta)))
+				return ret;
+		}
+		return put_user(bta->block_bytes>>bta->sampleshift,(int*)arg);
+
+        case SNDCTL_DSP_SYNC:
+		/* NOP */
+		return 0;
+	case SNDCTL_DSP_GETISPACE:
+	{
+		audio_buf_info info;
+		if (!bta->recording)
+			return -EINVAL;
+		info.fragsize = bta->block_bytes>>bta->sampleshift;
+		info.fragstotal = bta->block_count;
+		info.bytes = bta->read_count;
+		info.fragments = info.bytes / info.fragsize;
+		if (debug)
+			printk(KERN_DEBUG "btaudio: SNDCTL_DSP_GETISPACE "
+			       "returns %d/%d/%d/%d\n",
+			       info.fragsize, info.fragstotal,
+			       info.bytes, info.fragments);
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+#if 0 /* TODO */
+        case SNDCTL_DSP_GETTRIGGER:
+        case SNDCTL_DSP_SETTRIGGER:
+        case SNDCTL_DSP_SETFRAGMENT:
+#endif
+	default:
+		return -EINVAL;
+	}
+}
+
+static unsigned int btaudio_dsp_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct btaudio *bta = file->private_data;
+	unsigned int mask = 0;
+
+	poll_wait(file, &bta->readq, wait);
+
+	if (0 == bta->read_count)
+		mask |= (POLLIN | POLLRDNORM);
+
+	return mask;
+}
+
+static struct file_operations btaudio_digital_dsp_fops = {
+	owner:   THIS_MODULE,
+	llseek:  no_llseek,
+	open:    btaudio_dsp_open_digital,
+	release: btaudio_dsp_release,
+	read:    btaudio_dsp_read,
+	write:   btaudio_dsp_write,
+	ioctl:   btaudio_dsp_ioctl,
+	poll:    btaudio_dsp_poll,
+};
+
+static struct file_operations btaudio_analog_dsp_fops = {
+	owner:   THIS_MODULE,
+	llseek:  no_llseek,
+	open:    btaudio_dsp_open_analog,
+	release: btaudio_dsp_release,
+	read:    btaudio_dsp_read,
+	write:   btaudio_dsp_write,
+	ioctl:   btaudio_dsp_ioctl,
+	poll:    btaudio_dsp_poll,
+};
+
+/* -------------------------------------------------------------- */
+
+static char *irq_name[] = { "", "", "", "OFLOW", "", "", "", "", "", "", "",
+			    "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
+			    "RIPERR", "PABORT", "OCERR", "SCERR" };
+
+static void btaudio_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+	int count = 0;
+	u32 stat,astat;
+	struct btaudio *bta = dev_id;
+
+	for (;;) {
+		count++;
+		stat  = btread(REG_INT_STAT);
+		astat = stat & btread(REG_INT_MASK);
+		if (!astat)
+			return;
+		btwrite(astat,REG_INT_STAT);
+
+		if (irq_debug) {
+			int i;
+			printk(KERN_DEBUG "btaudio: irq loop=%d risc=%x, bits:",
+			       count, stat>>28);
+			for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
+				if (stat & (1 << i))
+					printk(" %s",irq_name[i]);
+				if (astat & (1 << i))
+					printk("*");
+			}
+			printk("\n");
+		}
+		if (stat & IRQ_RISCI) {
+			int blocks;
+			blocks = (stat >> 28) - bta->dma_block;
+			if (blocks < 0)
+				blocks += bta->block_count;
+			bta->dma_block = stat >> 28;
+			if (bta->read_count + 2*bta->block_bytes > bta->buf_size) {
+				stop_recording(bta);
+				printk(KERN_INFO "btaudio: buffer overrun\n");
+			}
+			if (blocks > 0) {
+				bta->read_count += blocks * bta->block_bytes;
+				wake_up_interruptible(&bta->readq);
+			}
+		}
+		if (count > 10) {
+			printk(KERN_WARNING
+			       "btaudio: Oops - irq mask cleared\n");
+			btwrite(0, REG_INT_MASK);
+		}
+	}
+	return;
+}
+
+/* -------------------------------------------------------------- */
+
+static int __devinit btaudio_probe(struct pci_dev *pci_dev,
+				   const struct pci_device_id *pci_id)
+{
+	struct btaudio *bta;
+	unsigned char revision,latency;
+	int rc = -EBUSY;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+	if (!request_mem_region(pci_resource_start(pci_dev,0),
+				pci_resource_len(pci_dev,0),
+				"btaudio")) {
+		return -EBUSY;
+	}
+
+	bta = kmalloc(sizeof(*bta),GFP_ATOMIC);
+	memset(bta,0,sizeof(*bta));
+
+	bta->pci  = pci_dev;
+	bta->irq  = pci_dev->irq;
+	bta->mem  = pci_resource_start(pci_dev,0);
+	bta->mmio = ioremap(pci_resource_start(pci_dev,0),
+			    pci_resource_len(pci_dev,0));
+
+	bta->source     = 1;
+	bta->bits       = 8;
+	bta->channels   = 1;
+	if (bta->analog) {
+		bta->decimation  = 15;
+	} else {
+		bta->decimation  = 0;
+		bta->sampleshift = 1;
+	}
+
+	init_MUTEX(&bta->lock);
+        init_waitqueue_head(&bta->readq);
+
+        pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
+        pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &latency);
+        printk(KERN_INFO "btaudio: Bt%x (rev %d) at %02x:%02x.%x, ",
+	       pci_dev->device,revision,pci_dev->bus->number,
+	       PCI_SLOT(pci_dev->devfn),PCI_FUNC(pci_dev->devfn));
+        printk("irq: %d, latency: %d, memory: 0x%lx\n",
+	       bta->irq, latency, bta->mem);
+
+	/* init hw */
+        btwrite(0, REG_GPIO_DMA_CTL);
+        btwrite(0, REG_INT_MASK);
+        btwrite(~0x0UL, REG_INT_STAT);
+	pci_set_master(pci_dev);
+
+	if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT,
+			       "btaudio",(void *)bta)) < 0) {
+		printk(KERN_WARNING
+		       "btaudio: can't request irq (rc=%d)\n",rc);
+		goto fail1;
+	}
+
+	/* register devices */
+	if (digital) {
+		rc = bta->dsp_digital =
+			register_sound_dsp(&btaudio_digital_dsp_fops,dsp1);
+		if (rc < 0) {
+			printk(KERN_WARNING
+			       "btaudio: can't register digital dsp (rc=%d)\n",rc);
+			goto fail2;
+		}
+	}
+	if (analog) {
+		rc = bta->dsp_analog =
+			register_sound_dsp(&btaudio_analog_dsp_fops,dsp2);
+		if (rc < 0) {
+			printk(KERN_WARNING
+			       "btaudio: can't register analog dsp (rc=%d)\n",rc);
+			goto fail3;
+		}
+		rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer);
+		if (rc < 0) {
+			printk(KERN_WARNING
+			       "btaudio: can't register mixer (rc=%d)\n",rc);
+			goto fail4;
+		}
+	}
+	if (debug)
+		printk(KERN_DEBUG "btaudio: minors: digital=%d, analog=%d, mixer=%d\n",
+		       bta->dsp_digital, bta->dsp_analog, bta->mixer_dev);
+
+	/* hook into linked list */
+	bta->next = btaudios;
+	btaudios = bta;
+
+	pci_set_drvdata(pci_dev,bta);
+        return 0;
+
+ fail4:
+	unregister_sound_dsp(bta->dsp_analog);
+ fail3:
+	if (digital)
+		unregister_sound_dsp(bta->dsp_digital);
+ fail2:
+        free_irq(bta->irq,bta);	
+ fail1:
+	release_mem_region(pci_resource_start(pci_dev,0),
+			   pci_resource_len(pci_dev,0));
+	kfree(bta);
+	return rc;
+}
+
+static void __devexit btaudio_remove(struct pci_dev *pci_dev)
+{
+	struct btaudio *bta = pci_get_drvdata(pci_dev);
+	struct btaudio *walk;
+
+	/* turn off all DMA / IRQs */
+        btand(~15, REG_GPIO_DMA_CTL);
+        btwrite(0, REG_INT_MASK);
+        btwrite(~0x0UL, REG_INT_STAT);
+
+	/* unregister devices */
+	if (digital) {
+		unregister_sound_dsp(bta->dsp_digital);
+	}
+	if (analog) {
+		unregister_sound_dsp(bta->dsp_analog);
+		unregister_sound_mixer(bta->mixer_dev);
+	}
+
+	/* free resources */
+	free_buffer(bta);
+        free_irq(bta->irq,bta);
+	release_mem_region(pci_resource_start(pci_dev,0),
+			   pci_resource_len(pci_dev,0));
+
+	/* remove from linked list */
+	if (bta == btaudios) {
+		btaudios = NULL;
+	} else {
+		for (walk = btaudios; walk->next != bta; walk = walk->next)
+			; /* if (NULL == walk->next) BUG(); */
+		walk->next = bta->next;
+	}
+
+	pci_set_drvdata(pci_dev, NULL);
+	kfree(bta);
+	return;
+}
+
+/* -------------------------------------------------------------- */
+
+static struct pci_device_id btaudio_pci_tbl[] __devinitdata = {
+        { PCI_VENDOR_ID_BROOKTREE, 0x0878,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+        { PCI_VENDOR_ID_BROOKTREE, 0x0879,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+        {0,}
+};
+
+static struct pci_driver btaudio_pci_driver = {
+        name:     "btaudio",
+        id_table: btaudio_pci_tbl,
+        probe:    btaudio_probe,
+        remove:   __devexit_p(btaudio_remove),
+};
+
+int btaudio_init_module(void)
+{
+	printk(KERN_INFO "btaudio: driver version 0.6 loaded [%s%s%s]\n",
+	       analog ? "analog" : "",
+	       analog && digital ? "+" : "",
+	       digital ? "digital" : "");
+	return pci_module_init(&btaudio_pci_driver);
+}
+
+void btaudio_cleanup_module(void)
+{
+	pci_unregister_driver(&btaudio_pci_driver);
+	return;
+}
+
+module_init(btaudio_init_module);
+module_exit(btaudio_cleanup_module);
+
+MODULE_PARM(dsp1,"i");
+MODULE_PARM(dsp2,"i");
+MODULE_PARM(mixer,"i");
+MODULE_PARM(debug,"i");
+MODULE_PARM(irq_debug,"i");
+MODULE_PARM(digital,"i");
+MODULE_PARM(analog,"i");
+MODULE_PARM(rate,"i");
+
+MODULE_DEVICE_TABLE(pci, btaudio_pci_tbl);
+MODULE_DESCRIPTION("bt878 audio dma driver");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -Nru linux/sound/oss/cmpci.c linux-2.4.19-pre5-mjc/sound/oss/cmpci.c
--- linux/sound/oss/cmpci.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cmpci.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3195 @@
+/*****************************************************************************/
+/*
+ *      cmpci.c  --  C-Media PCI audio driver.
+ *
+ *      Copyright (C) 1999  ChenLi Tien (cltien@cmedia.com.tw)
+ *      		    C-media support (support@cmedia.com.tw)
+ *
+ *      Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * 	For update, visit:
+ * 		http://members.home.net/puresoft/cmedia.html
+ * 		http://www.cmedia.com.tw
+ * 	
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to David C. Niemi, Jan Pfeifer
+ *
+ *
+ * Module command line parameters:
+ *   none so far
+ *
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  The card has both an FM and a Wavetable synth, but I have to figure
+ *  out first how to drive them...
+ *
+ *  Revision history
+ *    06.05.98   0.1   Initial release
+ *    10.05.98   0.2   Fixed many bugs, esp. ADC rate calculation
+ *                     First stab at a simple midi interface (no bells&whistles)
+ *    13.05.98   0.3   Fix stupid cut&paste error: set_adc_rate was called instead of
+ *                     set_dac_rate in the FMODE_WRITE case in cm_open
+ *                     Fix hwptr out of bounds (now mpg123 works)
+ *    14.05.98   0.4   Don't allow excessive interrupt rates
+ *    08.06.98   0.5   First release using Alan Cox' soundcore instead of miscdevice
+ *    03.08.98   0.6   Do not include modversions.h
+ *                     Now mixer behaviour can basically be selected between
+ *                     "OSS documented" and "OSS actual" behaviour
+ *    31.08.98   0.7   Fix realplayer problems - dac.count issues
+ *    10.12.98   0.8   Fix drain_dac trying to wait on not yet initialized DMA
+ *    16.12.98   0.9   Fix a few f_file & FMODE_ bugs
+ *    06.01.99   0.10  remove the silly SA_INTERRUPT flag.
+ *                     hopefully killed the egcs section type conflict
+ *    12.03.99   0.11  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                     reported by Johan Maes <joma@telindus.be>
+ *    22.03.99   0.12  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                     read/write cannot be executed
+ *    18.08.99   1.5   Only deallocate DMA buffer when unloading.
+ *    02.09.99   1.6   Enable SPDIF LOOP
+ *                     Change the mixer read back
+ *    21.09.99   2.33  Use RCS version as driver version.
+ *                     Add support for modem, S/PDIF loop and 4 channels.
+ *                     (8738 only)
+ *                     Fix bug cause x11amp cannot play.
+ *
+ *    Fixes:
+ *    Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *    18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it
+ *    		   was calling prog_dmabuf with s->lock held, call missing
+ *    		   unlock_kernel in cm_midi_release
+ *    08/10/2001 - use set_current_state in some more places
+ *
+ *	Carlos Eduardo Gorges <carlos@techlinux.com.br>
+ *	Fri May 25 2001 
+ *	- SMP support ( spin[un]lock* revision )
+ *	- speaker mixer support 
+ *	Mon Aug 13 2001
+ *	- optimizations and cleanups
+ *
+ */
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/wrapper.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/bitops.h>
+
+#include "dm.h"
+
+/* --------------------------------------------------------------------- */
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#undef DMABYTEIO
+/* --------------------------------------------------------------------- */
+
+#define CM_MAGIC  ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A)
+
+/* CM8338 registers definition ****************/
+
+#define CODEC_CMI_FUNCTRL0		(0x00)
+#define CODEC_CMI_FUNCTRL1		(0x04)
+#define CODEC_CMI_CHFORMAT		(0x08)
+#define CODEC_CMI_INT_HLDCLR		(0x0C)
+#define CODEC_CMI_INT_STATUS		(0x10)
+#define CODEC_CMI_LEGACY_CTRL		(0x14)
+#define CODEC_CMI_MISC_CTRL		(0x18)
+#define CODEC_CMI_TDMA_POS		(0x1C)
+#define CODEC_CMI_MIXER			(0x20)
+#define CODEC_SB16_DATA			(0x22)
+#define CODEC_SB16_ADDR			(0x23)
+#define CODEC_CMI_MIXER1		(0x24)
+#define CODEC_CMI_MIXER2		(0x25)
+#define CODEC_CMI_AUX_VOL		(0x26)
+#define CODEC_CMI_MISC			(0x27)
+#define CODEC_CMI_AC97			(0x28)
+
+#define CODEC_CMI_CH0_FRAME1		(0x80)
+#define CODEC_CMI_CH0_FRAME2		(0x84)
+#define CODEC_CMI_CH1_FRAME1		(0x88)
+#define CODEC_CMI_CH1_FRAME2		(0x8C)
+
+#define CODEC_CMI_EXT_REG		(0xF0)
+
+/*  Mixer registers for SB16 ******************/
+
+#define DSP_MIX_DATARESETIDX		((unsigned char)(0x00))
+
+#define DSP_MIX_MASTERVOLIDX_L		((unsigned char)(0x30))
+#define DSP_MIX_MASTERVOLIDX_R		((unsigned char)(0x31))
+#define DSP_MIX_VOICEVOLIDX_L		((unsigned char)(0x32))
+#define DSP_MIX_VOICEVOLIDX_R		((unsigned char)(0x33))
+#define DSP_MIX_FMVOLIDX_L		((unsigned char)(0x34))
+#define DSP_MIX_FMVOLIDX_R		((unsigned char)(0x35))
+#define DSP_MIX_CDVOLIDX_L		((unsigned char)(0x36))
+#define DSP_MIX_CDVOLIDX_R		((unsigned char)(0x37))
+#define DSP_MIX_LINEVOLIDX_L		((unsigned char)(0x38))
+#define DSP_MIX_LINEVOLIDX_R		((unsigned char)(0x39))
+
+#define DSP_MIX_MICVOLIDX		((unsigned char)(0x3A))
+#define DSP_MIX_SPKRVOLIDX		((unsigned char)(0x3B))
+
+#define DSP_MIX_OUTMIXIDX		((unsigned char)(0x3C))
+
+#define DSP_MIX_ADCMIXIDX_L		((unsigned char)(0x3D))
+#define DSP_MIX_ADCMIXIDX_R		((unsigned char)(0x3E))
+
+#define DSP_MIX_INGAINIDX_L		((unsigned char)(0x3F))
+#define DSP_MIX_INGAINIDX_R		((unsigned char)(0x40))
+#define DSP_MIX_OUTGAINIDX_L		((unsigned char)(0x41))
+#define DSP_MIX_OUTGAINIDX_R		((unsigned char)(0x42))
+
+#define DSP_MIX_AGCIDX			((unsigned char)(0x43))
+
+#define DSP_MIX_TREBLEIDX_L		((unsigned char)(0x44))
+#define DSP_MIX_TREBLEIDX_R		((unsigned char)(0x45))
+#define DSP_MIX_BASSIDX_L		((unsigned char)(0x46))
+#define DSP_MIX_BASSIDX_R		((unsigned char)(0x47))
+
+#define CM_CH0_RESET			0x04
+#define CM_CH1_RESET			0x08
+#define CM_EXTENT_CODEC			0x100
+#define CM_EXTENT_MIDI			0x2
+#define CM_EXTENT_SYNTH			0x4
+#define CM_INT_CH0			1
+#define CM_INT_CH1			2
+
+#define CM_CFMT_STEREO			0x01
+#define CM_CFMT_16BIT			0x02
+#define CM_CFMT_MASK			0x03
+#define CM_CFMT_DACSHIFT		2
+#define CM_CFMT_ADCSHIFT		0
+
+static const unsigned sample_shift[]	= { 0, 1, 1, 2 };
+
+#define CM_ENABLE_CH1      0x2
+#define CM_ENABLE_CH0      0x1
+
+/* MIDI buffer sizes **************************/
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 2
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define FMODE_DMFM 0x10
+
+#define SND_DEV_DSP16   5 
+
+#define NR_DEVICE 3		/* maximum number of devices */
+
+/*********************************************/
+
+struct cm_state {
+	unsigned int magic;		/* magic */
+	struct cm_state *next;		/* we keep cm cards in a linked list */
+
+	int dev_audio;			/* soundcore stuff */
+	int dev_mixer;
+	int dev_midi;
+	int dev_dmfm;
+
+	unsigned int iosb, iobase, iosynth,
+			 iomidi, iogame, irq;	/* hardware resources */
+	unsigned short deviceid;		/* pci_id */
+
+        struct {				/* mixer stuff */
+                unsigned int modcnt;
+		unsigned short vol[13];
+        } mix;
+
+	unsigned int rateadc, ratedac;		/* wave stuff */
+	unsigned char fmt, enable;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned rawphys;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error;		/* over/underrun */
+		wait_queue_head_t wait;
+		
+		unsigned fragsize;	/* redundant, but makes calculations easier */
+		unsigned dmasize;
+		unsigned fragsamples;
+		unsigned dmasamples;
+		
+		unsigned mapped:1;	/* OSS stuff */
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac, dma_adc;
+
+	struct {			/* midi stuff */
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		struct timer_list timer;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+	
+	int	chip_version;		
+	int	max_channels;
+	int	curr_channels;		
+	int	speakers;		/* number of speakers */
+	int	capability;		/* HW capability, various for chip versions */
+
+	int	status;			/* HW or SW state */
+	
+	int	spdif_counter;		/* spdif frame counter */
+};
+
+/* flags used for capability */
+#define	CAN_AC3_HW		0x00000001		/* 037 or later */
+#define	CAN_AC3_SW		0x00000002		/* 033 or later */
+#define	CAN_AC3			(CAN_AC3_HW | CAN_AC3_SW)
+#define CAN_DUAL_DAC		0x00000004		/* 033 or later */
+#define	CAN_MULTI_CH_HW		0x00000008		/* 039 or later */
+#define	CAN_MULTI_CH		(CAN_MULTI_CH_HW | CAN_DUAL_DAC)
+#define	CAN_LINE_AS_REAR	0x00000010		/* 033 or later */
+#define	CAN_LINE_AS_BASS	0x00000020		/* 039 or later */
+#define	CAN_MIC_AS_BASS		0x00000040		/* 039 or later */
+
+/* flags used for status */
+#define	DO_AC3_HW		0x00000001
+#define	DO_AC3_SW		0x00000002
+#define	DO_AC3			(DO_AC3_HW | DO_AC3_SW)
+#define	DO_DUAL_DAC		0x00000004
+#define	DO_MULTI_CH_HW		0x00000008
+#define	DO_MULTI_CH		(DO_MULTI_CH_HW | DO_DUAL_DAC)
+#define	DO_LINE_AS_REAR		0x00000010		/* 033 or later */
+#define	DO_LINE_AS_BASS		0x00000020		/* 039 or later */
+#define	DO_MIC_AS_BASS		0x00000040		/* 039 or later */
+#define	DO_SPDIF_OUT		0x00000100
+#define	DO_SPDIF_IN		0x00000200
+#define	DO_SPDIF_LOOP		0x00000400
+
+static struct cm_state *devs;
+static unsigned long wavetable_mem;
+
+/* --------------------------------------------------------------------- */
+
+static inline unsigned ld2(unsigned int x)
+{
+	unsigned exp=16,l=5,r=0;
+	static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000};
+
+	/* num: 2, 4, 16, 256, 65536 */
+	/* exp: 1, 2,  4,   8,    16 */
+	
+	while(l--) {
+		if( x >= num[l] ) {
+			if(num[l]>2) x >>= exp;
+			r+=exp;
+		}
+		exp>>=1;
+	}
+
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void maskb(unsigned int addr, unsigned int mask, unsigned int value)
+{
+	outb((inb(addr) & mask) | value, addr);
+}
+
+static void maskw(unsigned int addr, unsigned int mask, unsigned int value)
+{
+	outw((inw(addr) & mask) | value, addr);
+}
+
+static void maskl(unsigned int addr, unsigned int mask, unsigned int value)
+{
+	outl((inl(addr) & mask) | value, addr);
+}
+
+static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+	if (addr)
+	    outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1);
+	outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2);
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~1, 0);
+}
+
+static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+	outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1);
+	outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2);
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 1);
+}
+
+static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+	outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1);
+	outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2);
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 0);
+	if (s->status & DO_DUAL_DAC)
+		set_dmadac1(s, 0, count);
+}
+
+static void set_countadc(struct cm_state *s, unsigned count)
+{
+	outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2);
+}
+
+static void set_countdac(struct cm_state *s, unsigned count)
+{
+	outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2);
+	if (s->status & DO_DUAL_DAC)
+	    set_countadc(s, count);
+}
+
+static inline unsigned get_dmadac(struct cm_state *s)
+{
+	unsigned int curr_addr;
+
+	curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1;
+	curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
+	curr_addr = s->dma_dac.dmasize - curr_addr;
+
+	return curr_addr;
+}
+
+static inline unsigned get_dmaadc(struct cm_state *s)
+{
+	unsigned int curr_addr;
+
+	curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1;
+	curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK];
+	curr_addr = s->dma_adc.dmasize - curr_addr;
+
+	return curr_addr;
+}
+
+static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data)
+{
+	outb(idx, s->iobase + CODEC_SB16_ADDR);
+	udelay(10);
+	outb(data, s->iobase + CODEC_SB16_DATA);
+	udelay(10);
+}
+
+static unsigned char rdmixer(struct cm_state *s, unsigned char idx)
+{
+	unsigned char v;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&s->lock, flags);
+	outb(idx, s->iobase + CODEC_SB16_ADDR);
+	udelay(10);
+	v = inb(s->iobase + CODEC_SB16_DATA);
+	udelay(10);
+	spin_unlock_irqrestore(&s->lock, flags);
+	return v;
+}
+
+static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data)
+{
+	if (mask)
+	{
+		s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT);
+		udelay(10);
+	}
+	s->fmt = (s->fmt & mask) | data;
+	outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT);
+	udelay(10);
+}
+
+static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	set_fmt_unlocked(s,mask,data);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data)
+{
+	outb(idx, s->iobase + CODEC_SB16_ADDR);
+	udelay(10);
+	outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA);
+	udelay(10);
+}
+
+static struct {
+	unsigned	rate;
+	unsigned	lower;
+	unsigned	upper;
+	unsigned char	freq;
+} rate_lookup[] =
+{
+	{ 5512,		(0 + 5512) / 2,		(5512 + 8000) / 2,	0 },
+	{ 8000,		(5512 + 8000) / 2,	(8000 + 11025) / 2,	4 },
+	{ 11025,	(8000 + 11025) / 2,	(11025 + 16000) / 2,	1 },
+	{ 16000,	(11025 + 16000) / 2,	(16000 + 22050) / 2,	5 },
+	{ 22050,	(16000 + 22050) / 2,	(22050 + 32000) / 2,	2 },
+	{ 32000,	(22050 + 32000) / 2,	(32000 + 44100) / 2,	6 },
+	{ 44100,	(32000 + 44100) / 2,	(44100 + 48000) / 2,	3 },
+	{ 48000,	(44100 + 48000) / 2,	48000,			7 }
+};
+
+static void set_spdifout_unlocked(struct cm_state *s, unsigned rate)
+{
+	if (rate == 48000 || rate == 44100) {
+		// SPDIFI48K SPDF_ACc97
+		maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0);
+		// ENSPDOUT
+		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, 0x80);
+		// SPDF_1 SPD2DAC
+		maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x240);
+		// CDPLAY
+		if (s->chip_version >= 39)
+			maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1);
+		s->status |= DO_SPDIF_OUT;
+	} else {
+		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0x80, 0);
+		maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0x240, 0);
+		if (s->chip_version >= 39)
+			maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0);
+		s->status &= ~DO_SPDIF_OUT;
+	}
+}
+
+static void set_spdifout(struct cm_state *s, unsigned rate)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	set_spdifout_unlocked(s,rate);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* find parity for bit 4~30 */
+static unsigned parity(unsigned data)
+{
+	unsigned parity = 0;
+	int counter = 4;
+
+	data >>= 4;	// start from bit 4
+	while (counter <= 30) {
+		if (data & 1)
+			parity++;
+		data >>= 1;
+		counter++;
+	}
+	return parity & 1;
+}
+
+static void set_ac3_unlocked(struct cm_state *s, unsigned rate)
+{
+	/* enable AC3 */
+	if (rate == 48000 || rate == 44100) {
+		// mute DAC
+		maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x40);
+		// AC3EN for 037, 0x10
+		maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10);
+		// AC3EN for 039, 0x04
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x04);
+		if (s->capability & CAN_AC3_HW) {
+			// SPD24SEL for 037, 0x02
+			// SPD24SEL for 039, 0x20, but cannot be set
+			maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02);
+			s->status |= DO_AC3_HW;
+			if (s->chip_version >= 39)
+				maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0);
+		 } else {
+			// SPD32SEL for 037 & 039, 0x20
+			maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x20);
+			// set 176K sample rate to fix 033 HW bug
+			if (s->chip_version == 33) {
+				if (rate == 48000)
+					maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08);
+				else
+					maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0);
+			}
+			s->status |= DO_AC3_SW;
+		}
+	} else {
+		maskb(s->iobase + CODEC_CMI_MIXER1, ~0x40, 0);
+		maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0x32, 0);
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x24, 0);
+		maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0);
+		if (s->chip_version == 33)
+			maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0);
+		if (s->chip_version >= 39)
+			maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1);
+		s->status &= ~DO_AC3;
+	}
+	s->spdif_counter = 0;
+
+}
+
+static void set_ac3(struct cm_state *s, unsigned rate)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	set_spdifout_unlocked(s, rate);
+	set_ac3_unlocked(s,rate);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size)
+{
+	int   i = size / 2;
+	unsigned long data;
+	unsigned long *dst = (unsigned long *) dest;
+	unsigned short *src = (unsigned short *)source;
+
+	do {
+		data = (unsigned long) *src++;
+		data <<= 12;			// ok for 16-bit data
+		if (s->spdif_counter == 2 || s->spdif_counter == 3)
+			data |= 0x40000000;	// indicate AC-3 raw data
+		if (parity(data))
+			data |= 0x80000000;	// parity
+		if (s->spdif_counter == 0)
+			data |= 3;		// preamble 'M'
+		else if (s->spdif_counter & 1)
+			data |= 5;		// odd, 'W'
+		else
+			data |= 9;		// even, 'M'
+		*dst++ = data;
+		s->spdif_counter++;
+		if (s->spdif_counter == 384)
+			s->spdif_counter = 0;
+	} while (--i);
+}
+
+static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate)
+{
+	unsigned char freq = 4;
+	int	i;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 8000)
+		rate = 8000;
+	for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) {
+		if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) {
+			rate = rate_lookup[i].rate;
+			freq = rate_lookup[i].freq;
+			break;
+	    	}
+	}
+	s->rateadc = rate;
+	freq <<= 2;
+
+	maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq);
+}
+
+static void set_adc_rate(struct cm_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned char freq = 4;
+	int	i;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 8000)
+		rate = 8000;
+	for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) {
+		if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) {
+			rate = rate_lookup[i].rate;
+			freq = rate_lookup[i].freq;
+			break;
+	    	}
+	}
+	s->rateadc = rate;
+	freq <<= 2;
+
+	spin_lock_irqsave(&s->lock, flags);
+	maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void set_dac_rate(struct cm_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned char freq = 4;
+	int	i;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 8000)
+		rate = 8000;
+	for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) {
+		if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) {
+			rate = rate_lookup[i].rate;
+			freq = rate_lookup[i].freq;
+			break;
+	    	}
+	}
+	s->ratedac = rate;
+	freq <<= 5;
+
+	spin_lock_irqsave(&s->lock, flags);
+	maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq);
+
+
+	if (s->curr_channels <=  2)
+		set_spdifout_unlocked(s, rate);
+	if (s->status & DO_DUAL_DAC)
+		set_adc_rate_unlocked(s, rate);
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+static inline void reset_adc(struct cm_state *s)
+{
+	/* reset bus master */
+	outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	udelay(10);
+	outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+}
+
+static inline void reset_dac(struct cm_state *s)
+{
+	/* reset bus master */
+	outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	if (s->status & DO_DUAL_DAC)
+		reset_adc(s);
+}
+
+static inline void pause_adc(struct cm_state *s)
+{
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 4);
+}
+
+static inline void pause_dac(struct cm_state *s)
+{
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 8);
+	if (s->status & DO_DUAL_DAC)
+		pause_adc(s);
+}
+
+static inline void disable_adc(struct cm_state *s)
+{
+	/* disable channel */
+	s->enable &= ~CM_ENABLE_CH0;
+	outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	reset_adc(s);
+}
+
+static inline void disable_dac(struct cm_state *s)
+{
+	/* disable channel */
+	s->enable &= ~CM_ENABLE_CH1;
+	outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	reset_dac(s);
+	if (s->status & DO_DUAL_DAC)
+		disable_adc(s);
+}
+
+static inline void enable_adc(struct cm_state *s)
+{
+	if (!(s->enable & CM_ENABLE_CH0)) {
+		/* enable channel */
+		s->enable |= CM_ENABLE_CH0;
+		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	}
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0);
+}
+
+static inline void enable_dac_unlocked(struct cm_state *s)
+{
+	if (!(s->enable & CM_ENABLE_CH1)) {
+		/* enable channel */
+		s->enable |= CM_ENABLE_CH1;
+		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	}
+	maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0);
+
+	if (s->status & DO_DUAL_DAC)
+		enable_adc(s);
+}
+
+static inline void enable_dac(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	enable_dac_unlocked(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static inline void stop_adc_unlocked(struct cm_state *s)
+{
+	if (s->enable & CM_ENABLE_CH0) {
+		/* disable interrupt */
+		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0);
+		disable_adc(s);
+	}
+}
+
+static inline void stop_adc(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	stop_adc_unlocked(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+
+}
+
+static inline void stop_dac_unlocked(struct cm_state *s)
+{
+	if (s->enable & CM_ENABLE_CH1) {
+		/* disable interrupt */
+		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0);
+		disable_dac(s);
+	}
+	if (s->status & DO_DUAL_DAC)
+		stop_adc_unlocked(s);
+}
+
+static inline void stop_dac(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	stop_dac_unlocked(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_adc_unlocked(struct cm_state *s)
+{
+	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
+	    && s->dma_adc.ready) {
+		/* enable interrupt */
+		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1);
+		enable_adc(s);
+	}
+}
+
+static void start_adc(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	start_adc_unlocked(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac1_unlocked(struct cm_state *s)
+{
+	if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) {
+		/* enable interrupt */
+//		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1);
+ 		enable_dac_unlocked(s);
+	}
+}
+
+static void start_dac_unlocked(struct cm_state *s)
+{
+	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		/* enable interrupt */
+		maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2);
+		enable_dac_unlocked(s);
+	}
+		if (s->status & DO_DUAL_DAC)
+			start_dac1_unlocked(s);
+}
+
+static void start_dac(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	start_dac_unlocked(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static int prog_dmabuf(struct cm_state *s, unsigned rec);
+
+static int set_dac_channels(struct cm_state *s, int channels)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&s->lock, flags);
+
+	if ((channels > 2) && (channels <= s->max_channels)
+	 && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) {
+	    set_spdifout_unlocked(s, 0);
+	    if (s->capability & CAN_MULTI_CH_HW) {
+		// NXCHG
+		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80);
+		// CHB3D or CHB3D5C
+	       	maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, channels > 4 ? 0x80 : 0x20);
+		// CHB3D6C
+		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, channels == 6 ? 0x80 : 0);
+		// ENCENTER 
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x80, channels == 6 ? 0x80 : 0);
+		s->status |= DO_MULTI_CH_HW;
+	    } else if (s->capability & CAN_DUAL_DAC) {
+		unsigned char fmtm = ~0, fmts = 0;
+		ssize_t ret;
+
+		// ENDBDAC, turn on double DAC mode
+		// XCHGDAC, CH0 -> back, CH1->front
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0);
+		s->status |= DO_DUAL_DAC;
+		// prepare secondary buffer
+
+		spin_unlock_irqrestore(&s->lock, flags);
+		ret = prog_dmabuf(s, 1);
+		if (ret) return ret;
+		spin_lock_irqsave(&s->lock, flags);
+
+		// copy the hw state
+		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT);
+		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT);
+		// the HW only support 16-bit stereo
+		fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+		fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+		fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+		fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+		
+		set_fmt_unlocked(s, fmtm, fmts);
+		set_adc_rate_unlocked(s, s->ratedac);
+
+	    }
+
+	    if (s->speakers > 2)
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0);
+	    s->curr_channels = channels;
+	} else {
+	    if (s->status & DO_MULTI_CH_HW) {
+		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x80, 0);
+		maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, 0);
+		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, 0);
+	    } else if (s->status & DO_DUAL_DAC) {
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x80, 0);
+	    }
+	    // N4SPK3D, enable 4 speaker mode (analog duplicate)
+	    if (s->speakers > 2)
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, 0x04);
+	    s->status &= ~DO_MULTI_CH;
+	    s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1;
+	}
+
+	spin_unlock_irqrestore(&s->lock, flags);
+	return s->curr_channels;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+static void dealloc_dmabuf(struct dmabuf *db)
+{
+	struct page *pstart, *pend;
+	
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++)
+			mem_map_unreserve(pstart);
+		free_pages((unsigned long)db->rawbuf, db->buforder);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+/* Ch1 is used for playback, Ch0 is used for recording */
+
+static int prog_dmabuf(struct cm_state *s, unsigned rec)
+{
+	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+	unsigned rate = rec ? s->rateadc : s->ratedac;
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	struct page *pstart, *pend;
+	unsigned char fmt;
+	unsigned long flags;
+
+	fmt = s->fmt;
+	if (rec) {
+		stop_adc(s);
+		fmt >>= CM_CFMT_ADCSHIFT;
+	} else {
+		stop_dac(s);
+		fmt >>= CM_CFMT_DACSHIFT;
+	}
+
+	fmt &= CM_CFMT_MASK;
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))
+				break;
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		db->rawphys = virt_to_bus(db->rawbuf);
+		if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
+			printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
+			       (long) db->rawphys, PAGE_SIZE << db->buforder);
+		if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
+			printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
+			       (long) db->rawphys, PAGE_SIZE << db->buforder);
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++)
+			mem_map_reserve(pstart);
+	}
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+ 	/* to make fragsize >= 4096 */
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	db->dmasamples = db->dmasize >> sample_shift[fmt];
+	memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
+	spin_lock_irqsave(&s->lock, flags);
+	if (rec) {
+		if (s->status & DO_DUAL_DAC)
+		    set_dmadac1(s, db->rawphys, db->dmasize >> sample_shift[fmt]);
+		else
+		    set_dmaadc(s, db->rawphys, db->dmasize >> sample_shift[fmt]);
+		/* program sample counts */
+		set_countdac(s, db->fragsamples);
+	} else {
+		set_dmadac(s, db->rawphys, db->dmasize >> sample_shift[fmt]);
+		/* program sample counts */
+		set_countdac(s, db->fragsamples);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	db->ready = 1;
+	return 0;
+}
+
+static inline void clear_advance(struct cm_state *s)
+{
+	unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80;
+	unsigned char *buf = s->dma_dac.rawbuf;
+	unsigned char *buf1 = s->dma_adc.rawbuf;
+	unsigned bsize = s->dma_dac.dmasize;
+	unsigned bptr = s->dma_dac.swptr;
+	unsigned len = s->dma_dac.fragsize;
+
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(buf + bptr, c, x);
+		if (s->status & DO_DUAL_DAC)
+			memset(buf1 + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(buf + bptr, c, len);
+	if (s->status & DO_DUAL_DAC)
+		memset(buf1 + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void cm_update_ptr(struct cm_state *s)
+{
+	unsigned hwptr;
+	int diff;
+
+	/* update ADC pointer */
+	if (s->dma_adc.ready) {
+	    if (s->status & DO_DUAL_DAC) {
+		hwptr = get_dmaadc(s) % s->dma_adc.dmasize;
+		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		if (s->dma_adc.mapped) {
+			s->dma_adc.count += diff;
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+				wake_up(&s->dma_adc.wait);
+		} else {
+			s->dma_adc.count -= diff;
+			if (s->dma_adc.count <= 0) {
+				pause_adc(s);
+				s->dma_adc.error++;
+			} else if (s->dma_adc.count <= (signed)s->dma_adc.fragsize && !s->dma_adc.endcleared) {
+				clear_advance(s);
+				s->dma_adc.endcleared = 1;
+			}
+			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
+				wake_up(&s->dma_adc.wait);
+		}
+	    } else {
+		hwptr = get_dmaadc(s) % s->dma_adc.dmasize;
+		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+			wake_up(&s->dma_adc.wait);
+		if (!s->dma_adc.mapped) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				pause_adc(s);
+				s->dma_adc.error++;
+			}
+		}
+	    }
+	}
+	/* update DAC pointer */
+	if (s->dma_dac.ready) {
+		hwptr = get_dmadac(s) % s->dma_dac.dmasize;
+		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+		s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+				wake_up(&s->dma_dac.wait);
+		} else {
+			s->dma_dac.count -= diff;
+			if (s->dma_dac.count <= 0) {
+				pause_dac(s);
+				s->dma_dac.error++;
+			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+				clear_advance(s);
+				s->dma_dac.endcleared = 1;
+			}
+			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
+				wake_up(&s->dma_dac.wait);
+		}
+	}
+}
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+/* hold spinlock for the following! */
+static void cm_handle_midi(struct cm_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x80)) {
+		ch = inb(s->iomidi);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->iomidi);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+}
+#endif
+
+static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct cm_state *s = (struct cm_state *)dev_id;
+	unsigned int intsrc, intstat;
+	unsigned char mask = 0;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS);
+	if (!(intsrc & 0x80000000))
+		return;
+	spin_lock(&s->lock);
+	intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	/* acknowledge interrupt */
+	if (intsrc & CM_INT_CH0)
+		mask |= 1;
+	if (intsrc & CM_INT_CH1)
+		mask |= 2;
+	outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	cm_update_ptr(s);
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+	cm_handle_midi(s);
+#endif
+	spin_unlock(&s->lock);
+}
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+static void cm_midi_timer(unsigned long data)
+{
+	struct cm_state *s = (struct cm_state *)data;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&s->lock, flags);
+	cm_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->midi.timer.expires = jiffies+1;
+	add_timer(&s->midi.timer);
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI	/* support multiple chips */
+#define VALIDATE_STATE(s)
+#else
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != CM_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define MT_4          1
+#define MT_5MUTE      2
+#define MT_4MUTEMONO  3
+#define MT_6MUTE      4
+#define MT_5MUTEMONO  5
+
+static const struct {
+	unsigned left;
+	unsigned right;
+	unsigned type;
+	unsigned rec;
+	unsigned play;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_CD]     = { DSP_MIX_CDVOLIDX_L,     DSP_MIX_CDVOLIDX_R,     MT_5MUTE,     0x04, 0x02 },
+	[SOUND_MIXER_LINE]   = { DSP_MIX_LINEVOLIDX_L,   DSP_MIX_LINEVOLIDX_R,   MT_5MUTE,     0x10, 0x08 },
+	[SOUND_MIXER_MIC]    = { DSP_MIX_MICVOLIDX,      DSP_MIX_MICVOLIDX,      MT_5MUTEMONO, 0x01, 0x01 },
+	[SOUND_MIXER_SYNTH]  = { DSP_MIX_FMVOLIDX_L,  	 DSP_MIX_FMVOLIDX_R,     MT_5MUTE,     0x40, 0x00 },
+	[SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE,     0x00, 0x00 },
+	[SOUND_MIXER_PCM]    = { DSP_MIX_VOICEVOLIDX_L,  DSP_MIX_VOICEVOLIDX_R,  MT_5MUTE,     0x00, 0x00 },
+	[SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX,	 DSP_MIX_SPKRVOLIDX,	 MT_5MUTEMONO, 0x01, 0x01 }
+};
+
+static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = 
+{
+	[SOUND_MIXER_CD]     = 1,
+	[SOUND_MIXER_LINE]   = 2,
+	[SOUND_MIXER_MIC]    = 3,
+	[SOUND_MIXER_SYNTH]  = 4,
+	[SOUND_MIXER_VOLUME] = 5,
+	[SOUND_MIXER_PCM]    = 6,
+	[SOUND_MIXER_SPEAKER]= 7
+};
+
+static unsigned mixer_recmask(struct cm_state *s)
+{
+	int i, j, k;
+
+	j = rdmixer(s, DSP_MIX_ADCMIXIDX_L);
+	j &= 0x7f;
+	for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		if (j & mixtable[i].rec)
+			k |= 1 << i;
+	return k;
+}
+
+static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg)
+{
+	unsigned long flags;
+	int i, val, j;
+	unsigned char l, r, rl, rr;
+
+	VALIDATE_STATE(s);
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "cmpci", sizeof(info.id));
+		strncpy(info.name, "C-Media PCI", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "cmpci", sizeof(info.id));
+		strncpy(info.name, "C-Media cmpci", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_IOC_DIR(cmd) == _IOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_recmask(s), (int *)arg);
+			
+                case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_recmask(s), (int *)arg);//need fix
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].rec)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].play)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                 case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(0, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+                                return -EINVAL;
+			if (!volidx[i])
+				return -EINVAL;
+			return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+		}
+	}
+        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		i = generic_hweight32(val);
+		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (!mixtable[i].rec) {
+				val &= ~(1 << i);
+				continue;
+			}
+			j |= mixtable[i].rec;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		wrmixer(s, DSP_MIX_ADCMIXIDX_L, j);
+		wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1));
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+	case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (!mixtable[i].play) {
+				val &= ~(1 << i);
+				continue;
+			}
+			j |= mixtable[i].play;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, j);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+		default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+			return -EINVAL;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = val & 0xff;
+		r = (val >> 8) & 0xff;
+		if (l > 100)
+			l = 100;
+		if (r > 100)
+			r = 100;
+		spin_lock_irqsave(&s->lock, flags);
+		switch (mixtable[i].type) {
+		case MT_4:
+			if (l >= 10)
+				l -= 10;
+			if (r >= 10)
+				r -= 10;
+			frobindir(s, mixtable[i].left, 0xf0, l / 6);
+			frobindir(s, mixtable[i].right, 0xf0, l / 6);
+			break;
+
+		case MT_4MUTEMONO:
+			rl = (l < 4 ? 0 : (l - 5) / 3) & 31;
+			rr = (rl >> 2) & 7;
+			wrmixer(s, mixtable[i].left, rl<<3);
+			maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1);
+			break;
+			
+		case MT_5MUTEMONO:
+			r = l;
+			rl = l < 4 ? 0 : (l - 5) / 3;
+			rr = rl >> 2;
+ 			wrmixer(s, mixtable[i].left, rl<<3);
+			maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1);
+			break;
+				
+		case MT_5MUTE:
+			rl = l < 4 ? 0 : (l - 5) / 3;
+			rr = r < 4 ? 0 : (r - 5) / 3;
+ 			wrmixer(s, mixtable[i].left, rl<<3);
+			wrmixer(s, mixtable[i].right, rr<<3);
+			break;
+
+		case MT_6MUTE:
+			if (l < 6)
+				rl = 0x00;
+			else
+				rl = l * 2 / 3;
+			if (r < 6)
+				rr = 0x00;
+			else
+				rr = r * 2 / 3;
+			wrmixer(s, mixtable[i].left, rl);
+			wrmixer(s, mixtable[i].right, rr);
+			break;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		if (!volidx[i])
+			return -EINVAL;
+		s->mix.vol[volidx[i]-1] = val;
+		return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int cm_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct cm_state *s = devs;
+
+	while (s && s->dev_mixer != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	return 0;
+}
+
+static int cm_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	return 0;
+}
+
+static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations cm_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		cm_ioctl_mixdev,
+	open:		cm_open_mixdev,
+	release:	cm_release_mixdev,
+};
+
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct cm_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac.mapped || !s->dma_dac.ready)
+		return 0;
+        set_current_state(TASK_INTERRUPTIBLE);
+        add_wait_queue(&s->dma_dac.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac;
+		tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
+		if (!schedule_timeout(tmo + 1))
+			printk(KERN_DEBUG "cmpci: dma timed out??\n");
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
+				printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count,
+				       s->dma_adc.hwptr, s->dma_adc.swptr);
+				spin_lock_irqsave(&s->lock, flags);
+				stop_adc_unlocked(s);
+				set_dmaadc(s, s->dma_adc.rawphys, s->dma_adc.dmasamples);
+				/* program sample counts */
+				set_countadc(s, s->dma_adc.fragsamples);
+				s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc_unlocked(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	return ret;
+}
+
+static ssize_t cm_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	if (s->status & DO_DUAL_DAC) {
+		if (s->dma_adc.mapped)
+			return -ENXIO;
+		if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+			return ret;
+		if (!access_ok(VERIFY_READ, buffer, count))
+			return -EFAULT;
+	}
+	ret = 0;
+
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		if (s->status & DO_DUAL_DAC) {
+			s->dma_adc.swptr = s->dma_dac.swptr;
+			s->dma_adc.count = s->dma_dac.count;
+			s->dma_adc.endcleared = s->dma_dac.endcleared;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize-swptr;
+		if (s->status & DO_AC3_SW) {
+			if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize)
+				cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2;
+		} else {
+			if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+				cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if ((s->status & DO_DUAL_DAC) && (cnt > count / 2))
+		    cnt = count / 2;
+		if (cnt <= 0) {
+			start_dac(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
+				printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count,
+				       s->dma_dac.hwptr, s->dma_dac.swptr);
+				spin_lock_irqsave(&s->lock, flags);
+				stop_dac_unlocked(s);
+				set_dmadac(s, s->dma_dac.rawphys, s->dma_dac.dmasamples);
+				/* program sample counts */
+				set_countdac(s, s->dma_dac.fragsamples);
+				s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+				if (s->status & DO_DUAL_DAC)  {
+					set_dmadac1(s, s->dma_adc.rawphys, s->dma_adc.dmasamples);
+					s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+				}
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (s->status & DO_AC3_SW) {
+			// clip exceeded data, caught by 033 and 037
+			if (swptr + 2 * cnt > s->dma_dac.dmasize)
+				cnt = (s->dma_dac.dmasize - swptr) / 2;
+			trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt);
+			swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize;
+		} else if (s->status & DO_DUAL_DAC) {
+			int	i;
+			unsigned long *src, *dst0, *dst1;
+
+			src = (unsigned long *) buffer;
+			dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr);
+			dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr);
+			// copy left/right sample at one time
+			for (i = 0; i <= cnt / 4; i++) {
+				*dst0++ = *src++;
+				*dst1++ = *src++;
+			}
+			swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		} else {
+			if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
+				return ret ? ret : -EFAULT;
+			swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		if (s->status & DO_AC3_SW)
+			s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->status & DO_DUAL_DAC) {
+			count -= cnt;
+			buffer += cnt;
+			ret += cnt;
+		}
+		start_dac(s);
+	}
+	return ret;
+}
+
+static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac.wait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	cm_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int cm_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	struct dmabuf *db;
+	int ret = -EINVAL;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			goto out;
+		db = &s->dma_dac;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 1)) != 0)
+			goto out;
+		db = &s->dma_adc;
+	} else
+		goto out;
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		goto out;
+	ret = -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		goto out;
+	db->mapped = 1;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+	unsigned char fmtm, fmtd;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_BIND, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+			if (s->status & DO_DUAL_DAC)
+				s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+			 	spin_lock_irqsave(&s->lock, flags);
+				stop_adc_unlocked(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate_unlocked(s, val);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (s->status & DO_DUAL_DAC)
+					s->dma_adc.ready = 0;
+				set_dac_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+        case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		fmtd = 0;
+		fmtm = ~0;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			if (val)
+				fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+			else
+				fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			if (val)
+				fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+			else
+				fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT);
+			if (s->status & DO_DUAL_DAC) {
+				s->dma_adc.ready = 0;
+				if (val)
+					fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+			}
+		}
+		set_fmt(s, fmtm, fmtd);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT);
+				if (s->status & DO_DUAL_DAC) {
+					s->dma_adc.ready = 0;
+					if (val >= 2)
+						fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+					else
+						fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+				}
+			}
+			set_fmt(s, fmtm, fmtd);
+			if ((s->capability & CAN_MULTI_CH)
+			     && (file->f_mode & FMODE_WRITE)) {
+				val = set_dac_channels(s, val);
+				return put_user(val, (int *)arg);
+			}
+		}            
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) 
+					   : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8|AFMT_AC3, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val == AFMT_S16_LE || val == AFMT_AC3)
+					fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT);
+				if (val == AFMT_AC3) {
+					fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+					set_ac3(s, s->ratedac);
+				} else
+					set_ac3(s, 0);
+				if (s->status & DO_DUAL_DAC) {
+					s->dma_adc.ready = 0;
+					if (val == AFMT_S16_LE)
+						fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+					else
+						fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+				}
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		if (s->status & DO_AC3) return put_user(AFMT_AC3, (int *)arg);
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT)
+					   : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (s->status & DO_DUAL_DAC) {
+			if (file->f_mode & FMODE_WRITE &&
+			 (s->enable & CM_ENABLE_CH1) &&
+			 (s->enable & CM_ENABLE_CH0))
+				val |= PCM_ENABLE_OUTPUT;
+			return put_user(val, (int *)arg);
+		}
+		if (file->f_mode & FMODE_READ && s->enable & CM_ENABLE_CH0) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->enable & CM_ENABLE_CH1) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+					return ret;
+				if (s->status & DO_DUAL_DAC) {
+					if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+						return ret;
+				}
+				start_dac(s);
+			} else
+				stop_dac(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!(s->enable & CM_ENABLE_CH1) && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+		abinfo.fragsize = s->dma_dac.fragsize;
+                abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+                abinfo.fragstotal = s->dma_dac.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!(s->enable & CM_ENABLE_CH0) && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+                val = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+                cinfo.bytes = s->dma_dac.total_bytes;
+                cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+                cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize-1;
+		if (s->status & DO_DUAL_DAC) {
+			if (s->dma_adc.mapped)
+				s->dma_adc.count &= s->dma_adc.fragsize-1;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(s, 0)))
+				return val;
+			if (s->status & DO_DUAL_DAC) {
+				if ((val = prog_dmabuf(s, 1)))
+					return val;
+				return put_user(2 * s->dma_dac.fragsize, (int *)arg);
+			}
+			return put_user(s->dma_dac.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf(s, 1)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.ossfragshift = val & 0xffff;
+			s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac.ossfragshift < 4)
+				s->dma_dac.ossfragshift = 4;
+			if (s->dma_dac.ossfragshift > 15)
+				s->dma_dac.ossfragshift = 15;
+			if (s->dma_dac.ossmaxfrags < 4)
+				s->dma_dac.ossmaxfrags = 4;
+			if (s->status & DO_DUAL_DAC) {
+				s->dma_adc.ossfragshift = s->dma_dac.ossfragshift;
+				s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags;
+			}
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+			return -EINVAL;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.subdivision = val;
+			if (s->status & DO_DUAL_DAC)
+				s->dma_adc.subdivision = val;
+		}
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_READ_FILTER:
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+	case SNDCTL_DSP_GETCHANNELMASK:
+		return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, (int *)arg);
+		
+	case SNDCTL_DSP_BIND_CHANNEL:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val == DSP_BIND_QUERY) {
+			val = DSP_BIND_FRONT;
+			if (s->status & DO_SPDIF_OUT)
+				val |= DSP_BIND_SPDIF;
+			else {
+				if (s->curr_channels == 4)
+					val |= DSP_BIND_SURR;
+				if (s->curr_channels > 4)
+					val |= DSP_BIND_CENTER_LFE;
+			}
+		} else {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val & DSP_BIND_SPDIF) {
+					set_spdifout(s, s->ratedac);
+					set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1);
+					if (!(s->status & DO_SPDIF_OUT))
+						val &= ~DSP_BIND_SPDIF;
+				} else {
+					int channels;
+					int mask;
+
+					mask = val & (DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE);
+					switch (mask) {
+					    case DSP_BIND_FRONT:
+						channels = 2;
+						break;
+					    case DSP_BIND_FRONT|DSP_BIND_SURR:
+						channels = 4;
+						break;
+					    case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE:
+						channels = 6;
+						break;
+					    default:
+						channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1;
+						break;
+					}
+					set_dac_channels(s, channels);
+				}
+			}
+		}
+		return put_user(val, (int *)arg);
+
+	case SOUND_PCM_WRITE_FILTER:
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+        case SNDCTL_DSP_SETSYNCRO:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int cm_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct cm_state *s = devs;
+	unsigned char fmtm = ~0, fmts = 0;
+
+	while (s && ((s->dev_audio ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (file->f_mode & FMODE_READ) {
+		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+		set_dac_rate(s, 8000);
+		// clear previous multichannel, spdif, ac3 state
+		set_spdifout(s, 0);
+		if (s->deviceid == PCI_DEVICE_ID_CMEDIA_CM8738) {
+			set_ac3(s, 0);
+			set_dac_channels(s, 1);
+		}
+	}
+	set_fmt(s, fmtm, fmts);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int cm_release(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+
+		dealloc_dmabuf(&s->dma_dac);
+		if (s->status & DO_DUAL_DAC)
+			dealloc_dmabuf(&s->dma_adc);
+
+		if (s->status & DO_MULTI_CH)
+			set_dac_channels(s, 0);
+		if (s->status & DO_AC3)
+			set_ac3(s, 0);
+		if (s->status & DO_SPDIF_OUT)
+			set_spdifout(s, 0);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(&s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations cm_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		cm_read,
+	write:		cm_write,
+	poll:		cm_poll,
+	ioctl:		cm_ioctl,
+	mmap:		cm_mmap,
+	open:		cm_open,
+	release:	cm_release,
+};
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+/* --------------------------------------------------------------------- */
+
+static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	add_wait_queue(&s->midi.iwait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+			{
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			__set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current))
+			{
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+		{
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		break;
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.iwait, &wait);
+	return ret;
+}
+
+static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+	add_wait_queue(&s->midi.owait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0)
+			cm_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+			{
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			__set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+		{
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.owait, &wait);
+	return ret;
+}
+
+static unsigned int cm_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int cm_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct cm_state *s = devs;
+	unsigned long flags;
+
+	while (s && s->dev_midi != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		/* enable MPU-401 */
+		maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 4);
+		outb(0xff, s->iomidi+1); /* reset command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		outb(0x3f, s->iomidi+1); /* uart command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		init_timer(&s->midi.timer);
+		s->midi.timer.expires = jiffies+1;
+		s->midi.timer.data = (unsigned long)s;
+		s->midi.timer.function = cm_midi_timer;
+		add_timer(&s->midi.timer);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int cm_midi_release(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+
+	if (file->f_mode & FMODE_WRITE) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				set_current_state(TASK_RUNNING);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				printk(KERN_DEBUG "cmpci: midi timed out??\n");
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		set_current_state(TASK_RUNNING);
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		del_timer(&s->midi.timer);		
+		outb(0xff, s->iomidi+1); /* reset command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		/* disable MPU-401 */
+		maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~4, 0);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations cm_midi_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		cm_midi_read,
+	write:		cm_midi_write,
+	poll:		cm_midi_poll,
+	open:		cm_midi_open,
+	release:	cm_midi_release,
+};
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	static const unsigned char op_offset[18] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+	};
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	struct dm_fm_voice v;
+	struct dm_fm_note n;
+	struct dm_fm_params p;
+	unsigned int io;
+	unsigned int regb;
+
+	switch (cmd) {		
+	case FM_IOCTL_RESET:
+		for (regb = 0xb0; regb < 0xb9; regb++) {
+			outb(regb, s->iosynth);
+			outb(0, s->iosynth+1);
+			outb(regb, s->iosynth+2);
+			outb(0, s->iosynth+3);
+		}
+		return 0;
+
+	case FM_IOCTL_PLAY_NOTE:
+		if (copy_from_user(&n, (void *)arg, sizeof(n)))
+			return -EFAULT;
+		if (n.voice >= 18)
+			return -EINVAL;
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xa0 + regb, io);
+		outb(n.fnum & 0xff, io+1);
+		outb(0xb0 + regb, io);
+		outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+		return 0;
+
+	case FM_IOCTL_SET_VOICE:
+		if (copy_from_user(&v, (void *)arg, sizeof(v)))
+			return -EFAULT;
+		if (v.voice >= 18)
+			return -EINVAL;
+		regb = op_offset[v.voice];
+		io = s->iosynth + ((v.op & 1) << 1);
+		outb(0x20 + regb, io);
+		outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | 
+		     ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+		outb(0x40 + regb, io);
+		outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+		outb(0x60 + regb, io);
+		outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+		outb(0x80 + regb, io);
+		outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+		outb(0xe0 + regb, io);
+		outb(v.waveform & 0x7, io+1);
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xc0 + regb, io);
+		outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+		     (v.connection & 1), io+1);
+		return 0;
+		
+	case FM_IOCTL_SET_PARAMS:
+		if (copy_from_user(&p, (void *)arg, sizeof(p)))
+			return -EFAULT;
+		outb(0x08, s->iosynth);
+		outb((p.kbd_split & 1) << 6, s->iosynth+1);
+		outb(0xbd, s->iosynth);
+		outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+		     ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1);
+		return 0;
+
+	case FM_IOCTL_SET_OPL:
+		outb(4, s->iosynth+2);
+		outb(arg, s->iosynth+3);
+		return 0;
+
+	case FM_IOCTL_SET_MODE:
+		outb(5, s->iosynth+2);
+		outb(arg & 1, s->iosynth+3);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int cm_dmfm_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct cm_state *s = devs;
+
+	while (s && s->dev_dmfm != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DMFM) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	/* init the stuff */
+	outb(1, s->iosynth);
+	outb(0x20, s->iosynth+1); /* enable waveforms */
+	outb(4, s->iosynth+2);
+	outb(0, s->iosynth+3);  /* no 4op enabled */
+	outb(5, s->iosynth+2);
+	outb(1, s->iosynth+3);  /* enable OPL3 */
+	s->open_mode |= FMODE_DMFM;
+	up(&s->open_sem);
+	return 0;
+}
+
+static int cm_dmfm_release(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned int regb;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	down(&s->open_sem);
+	s->open_mode &= ~FMODE_DMFM;
+	for (regb = 0xb0; regb < 0xb9; regb++) {
+		outb(regb, s->iosynth);
+		outb(0, s->iosynth+1);
+		outb(regb, s->iosynth+2);
+		outb(0, s->iosynth+3);
+	}
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations cm_dmfm_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		cm_dmfm_ioctl,
+	open:		cm_dmfm_open,
+	release:	cm_dmfm_release,
+};
+#endif /* CONFIG_SOUND_ALSA_CMPCI_FM */
+
+
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_CD, 0x4f4f },
+	{ SOUND_MIXER_WRITE_LINE, 0x4f4f },
+	{ SOUND_MIXER_WRITE_MIC, 0x4f4f },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4f4f },
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4f4f },
+	{ SOUND_MIXER_WRITE_PCM, 0x4f4f }
+};
+
+/* check chip version and capability */
+static int query_chip(struct cm_state *s)
+{
+	int ChipVersion = -1;
+	unsigned char RegValue;
+
+	// check reg 0Ch, bit 24-31
+	RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3);
+	if (RegValue == 0) {
+	    // check reg 08h, bit 24-28
+	    RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3);
+	    RegValue &= 0x1f;
+	    if (RegValue == 0) {
+		ChipVersion = 33;
+		s->max_channels = 4;
+		s->capability |= CAN_AC3_SW;
+		s->capability |= CAN_DUAL_DAC;
+	    } else {
+		ChipVersion = 37;
+		s->max_channels = 4;
+		s->capability |= CAN_AC3_HW;
+		s->capability |= CAN_DUAL_DAC;
+	    }
+	} else {
+	    // check reg 0Ch, bit 26
+	    if (RegValue & (1 << (26-24))) {
+		ChipVersion = 39;
+	    	if (RegValue & (1 << (24-24)))
+		    s->max_channels  = 6;
+	    	else
+		    s->max_channels = 4;
+		s->capability |= CAN_AC3_HW;
+		s->capability |= CAN_DUAL_DAC;
+		s->capability |= CAN_MULTI_CH_HW;
+	    } else {
+		ChipVersion = 55; // 4 or 6 channels
+		s->max_channels  = 6;
+		s->capability |= CAN_AC3_HW;
+		s->capability |= CAN_DUAL_DAC;
+		s->capability |= CAN_MULTI_CH_HW;
+	    }
+	}
+	// still limited to number of speakers
+	if (s->max_channels > s->speakers)
+		s->max_channels = s->speakers;
+	return ChipVersion;
+}
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+static	int	mpuio = CONFIG_SOUND_ALSA_CMPCI_MPUIO;
+#else
+static	int	mpuio;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+static	int	fmio = CONFIG_SOUND_ALSA_CMPCI_FMIO;
+#else
+static	int	fmio;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_SPDIFINVERSE
+static	int	spdif_inverse = 1;
+#else
+static	int	spdif_inverse;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_SPDIFLOOP
+static	int	spdif_loop = 1;
+#else
+static	int	spdif_loop;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_SPEAKERS
+static	int	speakers = CONFIG_SOUND_ALSA_CMPCI_SPEAKERS;
+#else
+static	int	speakers = 2;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_LINE_REAR
+static	int	use_line_as_rear = 1;
+#else
+static	int	use_line_as_rear;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_LINE_BASS
+static	int	use_line_as_bass = 1;
+#else
+static	int	use_line_as_bass;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_JOYSTICK
+static	int	joystick = 1;
+#else
+static	int	joystick;
+#endif
+MODULE_PARM(mpuio, "i");
+MODULE_PARM(fmio, "i");
+MODULE_PARM(spdif_inverse, "i");
+MODULE_PARM(spdif_loop, "i");
+MODULE_PARM(speakers, "i");
+MODULE_PARM(use_line_as_rear, "i");
+MODULE_PARM(use_line_as_bass, "i");
+MODULE_PARM(joystick, "i");
+MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable");
+MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable");
+MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal");
+MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly");
+MODULE_PARM_DESC(speakers, "(2-6) Number of speakers you connect");
+MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out");
+MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center");
+MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver");
+
+static struct pci_device_id cmpci_pci_tbl[] = {
+	{ PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, 
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ 	{ PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, 
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, 
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, cmpci_pci_tbl);
+
+void initialize_chip(struct pci_dev *pcidev)
+{
+	struct cm_state *s;
+	mm_segment_t fs;
+	int i, val;
+#if defined(CONFIG_SOUND_ALSA_CMPCI_MIDI) || defined(CONFIG_SOUND_ALSA_CMPCI_FM)
+	unsigned char reg_mask = 0;
+#endif
+	struct {
+		unsigned short	deviceid;
+		char		*devicename;
+	} devicetable[] =
+	{
+		{ PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" },
+		{ PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" },
+		{ PCI_DEVICE_ID_CMEDIA_CM8738,  "CM8738" },
+		{ PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" },
+	};
+	char	*devicename = "unknown";
+	{
+		if (pci_enable_device(pcidev))
+			return;
+		if (pcidev->irq == 0)
+			return;
+		s = kmalloc(sizeof(*s), GFP_KERNEL);
+		if (!s) {
+			printk(KERN_WARNING "cmpci: out of memory\n");
+			return;
+		}
+		/* search device name */
+		for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) {
+			if (devicetable[i].deviceid == pcidev->device)
+			{
+				devicename = devicetable[i].devicename;
+				break;
+			}
+		}
+		memset(s, 0, sizeof(struct cm_state));
+		init_waitqueue_head(&s->dma_adc.wait);
+		init_waitqueue_head(&s->dma_dac.wait);
+		init_waitqueue_head(&s->open_wait);
+		init_waitqueue_head(&s->midi.iwait);
+		init_waitqueue_head(&s->midi.owait);
+		init_MUTEX(&s->open_sem);
+		spin_lock_init(&s->lock);
+		s->magic = CM_MAGIC;
+		s->iobase = pci_resource_start(pcidev, 0);
+		s->iosynth = fmio;
+		s->iomidi = mpuio;
+		s->status = 0;
+		/* range check */
+		if (speakers < 2)
+			speakers = 2;
+		else if (speakers > 6)
+			speakers = 6;
+		s->speakers = speakers;
+		if (s->iobase == 0)
+			return;
+		s->irq = pcidev->irq;
+
+		if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) {
+			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
+			goto err_region5;
+		}
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+		/* disable MPU-401 */
+		maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0);
+		if (s->iomidi) {
+		    if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) {
+			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
+			s->iomidi = 0;
+		    } else {
+		        /* set IO based at 0x330 */
+		        switch (s->iomidi) {
+		 	    case 0x330:
+				reg_mask = 0;
+				break;
+			    case 0x320:
+				reg_mask = 0x20;
+				break;
+			    case 0x310:
+				reg_mask = 0x40;
+				break;
+			    case 0x300:
+				reg_mask = 0x60;
+				break;
+			    default:
+				s->iomidi = 0;
+				break;
+		        }
+		        outb((inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60) | reg_mask, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
+			/* enable MPU-401 */
+			if (s->iomidi) {
+			    maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04);
+			}
+		    }
+		}
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+		/* disable FM */
+		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0);
+		if (s->iosynth) {
+		    if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) {
+			printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
+			s->iosynth = 0;
+		    } else {
+		        /* set IO based at 0x388 */
+		        switch (s->iosynth) {
+		 	    case 0x388:
+				reg_mask = 0;
+				break;
+			    case 0x3C8:
+				reg_mask = 0x01;
+				break;
+			    case 0x3E0:
+				reg_mask = 0x02;
+				break;
+			    case 0x3E8:
+				reg_mask = 0x03;
+				break;
+			    default:
+				s->iosynth = 0;
+				break;
+		        }
+		        maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask);
+		        /* enable FM */
+			if (s->iosynth) {
+		            maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8);
+			}
+		    }
+		}
+#endif
+		/* enable joystick */
+		if (joystick)
+			maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02);
+		else
+			maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0);
+		/* initialize codec registers */
+		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
+		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
+		/* reset mixer */
+		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
+
+		/* request irq */
+		if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) {
+			printk(KERN_ERR "cmpci: irq %u in use\n", s->irq);
+			goto err_irq;
+		}
+		printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n",
+		       devicename, s->iobase, s->irq);
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
+			goto err_dev1;
+		if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
+			goto err_dev2;
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+		if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+			goto err_dev3;
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+		if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+			goto err_dev4;
+#endif
+		pci_set_master(pcidev);	/* enable bus mastering */
+		/* initialize the chips */
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		/* set mixer output */
+		frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f);
+		/* set mixer input */
+		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC;
+		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+			val = initvol[i].vol;
+			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+		}
+		/* use channel 0 for record, channel 1 for play */
+		maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1);
+		s->deviceid = pcidev->device;
+
+		if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) {
+
+			/* chip version and hw capability check */
+			s->chip_version = query_chip(s);
+			printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version);
+
+			/* seet SPDIF-in inverse before enable SPDIF loop */
+			if (spdif_inverse) {
+				/* turn on spdif-in inverse */
+				maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1);
+				printk(KERN_INFO "cmpci: Inverse SPDIF-in\n");
+			} else {
+				/* turn off spdif-ininverse */
+				maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0);
+			}
+
+			/* enable SPDIF loop */
+			if (spdif_loop) {
+				s->status |= DO_SPDIF_LOOP;
+				/* turn on spdif-in to spdif-out */
+				maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x80);
+				printk(KERN_INFO "cmpci: Enable SPDIF loop\n");
+			} else {
+				s->status &= ~DO_SPDIF_LOOP;
+				/* turn off spdif-in to spdif-out */
+				maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x80, 0);
+			}
+			if (use_line_as_rear) {
+				s->capability |= CAN_LINE_AS_REAR;
+				s->status |= DO_LINE_AS_REAR;
+				maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x20);
+			} else
+				maskb(s->iobase + CODEC_CMI_MIXER1, ~0x20, 0);
+			if (s->chip_version >= 39) {
+				if (use_line_as_bass) {
+					s->capability |= CAN_LINE_AS_BASS;
+					s->status |= DO_LINE_AS_BASS;
+					maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, 0x60);
+				} else
+					maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x60, 0);
+			}
+		} else {
+			/* 8338 will fall here */
+			s->max_channels = 2;
+		}
+		/* queue it for later freeing */
+		s->next = devs;
+		devs = s;
+		return;
+
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+		unregister_sound_special(s->dev_dmfm);
+	err_dev4:
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+		unregister_sound_midi(s->dev_midi);
+	err_dev3:
+#endif
+		unregister_sound_mixer(s->dev_mixer);
+	err_dev2:
+		unregister_sound_dsp(s->dev_audio);
+	err_dev1:
+		printk(KERN_ERR "cmpci: cannot register misc device\n");
+		free_irq(s->irq, s);
+	err_irq:
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+		if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH);
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+		if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI);
+#endif
+		release_region(s->iobase, CM_EXTENT_CODEC);
+	err_region5:
+		kfree(s);
+	}
+	if (!devs) {
+		if (wavetable_mem)
+			free_pages(wavetable_mem, 20-PAGE_SHIFT);
+		return;
+	}
+	return;
+}
+
+static int __init init_cmpci(void)
+{
+	struct pci_dev *pcidev = NULL;
+	int index = 0;
+
+#ifdef CONFIG_PCI
+	if (!pci_present())   /* No PCI bus in this machine! */
+#endif
+		return -ENODEV;
+	printk(KERN_INFO "cmpci: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n");
+
+	while (index < NR_DEVICE && (
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { 
+		initialize_chip(pcidev);
+		index++;
+	}
+	while (index < NR_DEVICE && (
+ 	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)))) {
+		initialize_chip(pcidev);
+		index++;
+	}
+	while (index < NR_DEVICE && (
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)))) {
+		initialize_chip(pcidev);
+		index++;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw");
+MODULE_DESCRIPTION("CM8x38 Audio Driver");
+MODULE_LICENSE("GPL");
+
+
+static void __exit cleanup_cmpci(void)
+{
+	struct cm_state *s;
+
+	while ((s = devs)) {
+		devs = devs->next;
+		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
+		synchronize_irq();
+		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */
+		free_irq(s->irq, s);
+
+		/* reset mixer */
+		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
+
+		release_region(s->iobase, CM_EXTENT_CODEC);
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+		if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI);
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+		if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH);
+#endif
+		unregister_sound_dsp(s->dev_audio);
+		unregister_sound_mixer(s->dev_mixer);
+#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI
+		unregister_sound_midi(s->dev_midi);
+#endif
+#ifdef CONFIG_SOUND_ALSA_CMPCI_FM
+		unregister_sound_special(s->dev_dmfm);
+#endif
+		kfree(s);
+	}
+	if (wavetable_mem)
+		free_pages(wavetable_mem, 20-PAGE_SHIFT);
+	printk(KERN_INFO "cmpci: unloading\n");
+}
+
+module_init(init_cmpci);
+module_exit(cleanup_cmpci);
diff -Nru linux/sound/oss/coproc.h linux-2.4.19-pre5-mjc/sound/oss/coproc.h
--- linux/sound/oss/coproc.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/coproc.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,12 @@
+/*
+ * Definitions for various on board processors on the sound cards. For
+ * example DSP processors.
+ */
+
+/*
+ * Coprocessor access types 
+ */
+#define COPR_CUSTOM		0x0001	/* Custom applications */
+#define COPR_MIDI		0x0002	/* MIDI (MPU-401) emulation */
+#define COPR_PCM		0x0004	/* Digitized voice applications */
+#define COPR_SYNTH		0x0008	/* Music synthesis */
diff -Nru linux/sound/oss/cs4232.c linux-2.4.19-pre5-mjc/sound/oss/cs4232.c
--- linux/sound/oss/cs4232.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4232.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ *	cs4232.c
+ *
+ * The low level driver for Crystal CS4232 based cards. The CS4232 is
+ * a PnP compatible chip which contains a CS4231A codec, SB emulation,
+ * a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM 
+ * interfaces. This is just a temporary driver until full PnP support
+ * gets implemented. Just the WSS codec, FM synth and the MIDI ports are
+ * supported. Other interfaces are left uninitialized.
+ *
+ * ifdef ...WAVEFRONT...
+ * 
+ *   Support is provided for initializing the WaveFront synth
+ *   interface as well, which is logical device #4. Note that if
+ *   you have a Tropez+ card, you probably don't need to setup
+ *   the CS4232-supported MIDI interface, since it corresponds to
+ *   the internal 26-pin header that's hard to access. Using this
+ *   requires an additional IRQ, a resource none too plentiful in
+ *   this environment. Just don't set module parameters mpuio and
+ *   mpuirq, and the MIDI port will be left uninitialized. You can
+ *   still use the ICS2115 hosted MIDI interface which corresponds
+ *   to the 9-pin D connector on the back of the card.
+ *
+ * endif  ...WAVEFRONT...
+ *
+ * Supported chips are:
+ *      CS4232
+ *      CS4236
+ *      CS4236B
+ *
+ * Note: You will need a PnP config setup to initialise some CS4232 boards
+ * anyway.
+ *
+ * Changes
+ *	Alan Cox		Modularisation, Basic cleanups.
+ *      Paul Barton-Davis	Separated MPU configuration, added
+ *                                       Tropez+ (WaveFront) support
+ *	Christoph Hellwig	Adapted to module_init/module_exit,
+ * 					simple cleanups
+ * 	Arnaldo C. de Melo	got rid of attach_uart401
+ *	Bartlomiej Zolnierkiewicz
+ *				Added some __init/__initdata/__exit
+ *	Marcus Meissner		Added ISA PnP support.
+ */
+
+#include <linux/config.h>
+#include <linux/isapnp.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "sound_config.h"
+
+#include "cs4232.h"
+#include "ad1848.h"
+#include "mpu401.h"
+
+#define KEY_PORT	0x279	/* Same as LPT1 status port */
+#define CSN_NUM		0x99	/* Just a random number */
+
+static void CS_OUT(unsigned char a)
+{
+	outb(a, KEY_PORT);
+}
+
+#define CS_OUT2(a, b)		{CS_OUT(a);CS_OUT(b);}
+#define CS_OUT3(a, b, c)	{CS_OUT(a);CS_OUT(b);CS_OUT(c);}
+
+static int mpu_base = 0, mpu_irq = 0;
+static int synth_base = 0, synth_irq = 0;
+static int mpu_detected = 0;
+
+int __init probe_cs4232_mpu(struct address_info *hw_config)
+{
+	/*
+	 *	Just write down the config values.
+	 */
+
+	mpu_base = hw_config->io_base;
+	mpu_irq = hw_config->irq;
+
+	return 1;
+}
+
+static unsigned char crystal_key[] __initdata =	/* A 32 byte magic key sequence */
+{
+	0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc,
+	0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2,
+	0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13,
+	0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a
+};
+
+static void sleep(unsigned howlong)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(howlong);
+}
+
+int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured)
+{
+	int i, n;
+	int base = hw_config->io_base, irq = hw_config->irq;
+	int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+
+	/*
+	 * Verify that the I/O port range is free.
+	 */
+
+	if (check_region(base, 4))
+	{
+		printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base);
+		return 0;
+	}
+	if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) {
+		return 1;	/* The card is already active */
+	}
+	if (isapnp_configured) {
+		printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n");
+		return 0;
+	}
+
+	/*
+	 * This version of the driver doesn't use the PnP method when configuring
+	 * the card but a simplified method defined by Crystal. This means that
+	 * just one CS4232 compatible device can exist on the system. Also this
+	 * method conflicts with possible PnP support in the OS. For this reason 
+	 * driver is just a temporary kludge.
+	 *
+	 * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;)
+	 */
+
+	/*
+	 * Repeat initialization few times since it doesn't always succeed in
+	 * first time.
+	 */
+
+	for (n = 0; n < 4; n++)
+	{	
+		/*
+		 *	Wake up the card by sending a 32 byte Crystal key to the key port.
+		 */
+		
+		for (i = 0; i < 32; i++)
+			CS_OUT(crystal_key[i]);
+
+		sleep(HZ / 10);
+
+		/*
+		 *	Now set the CSN (Card Select Number).
+		 */
+
+		CS_OUT2(0x06, CSN_NUM);
+
+		/*
+		 *	Then set some config bytes. First logical device 0 
+		 */
+
+		CS_OUT2(0x15, 0x00);	/* Select logical device 0 (WSS/SB/FM) */
+		CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff);	/* WSS base */
+
+		if (check_region(0x388, 4))	/* Not free */
+			CS_OUT3(0x48, 0x00, 0x00)	/* FM base off */
+		else
+			CS_OUT3(0x48, 0x03, 0x88);	/* FM base 0x388 */
+
+		CS_OUT3(0x42, 0x00, 0x00);	/* SB base off */
+		CS_OUT2(0x22, irq);		/* SB+WSS IRQ */
+		CS_OUT2(0x2a, dma1);		/* SB+WSS DMA */
+
+		if (dma2 != -1)
+			CS_OUT2(0x25, dma2)	/* WSS DMA2 */
+		else
+			CS_OUT2(0x25, 4);	/* No WSS DMA2 */
+
+		CS_OUT2(0x33, 0x01);	/* Activate logical dev 0 */
+
+		sleep(HZ / 10);
+
+		/*
+		 * Initialize logical device 3 (MPU)
+		 */
+
+		if (mpu_base != 0 && mpu_irq != 0)
+		{
+			CS_OUT2(0x15, 0x03);	/* Select logical device 3 (MPU) */
+			CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff);	/* MPU base */
+			CS_OUT2(0x22, mpu_irq);	/* MPU IRQ */
+			CS_OUT2(0x33, 0x01);	/* Activate logical dev 3 */
+		}
+
+		if(synth_base != 0)
+		{
+		    CS_OUT2 (0x15, 0x04);	        /* logical device 4 (WaveFront) */
+		    CS_OUT3 (0x47, (synth_base >> 8) & 0xff,
+			     synth_base & 0xff);	/* base */
+		    CS_OUT2 (0x22, synth_irq);     	/* IRQ */
+		    CS_OUT2 (0x33, 0x01);	        /* Activate logical dev 4 */
+		}
+
+		/*
+		 * Finally activate the chip
+		 */
+		
+		CS_OUT(0x79);
+
+		sleep(HZ / 5);
+
+		/*
+		 * Then try to detect the codec part of the chip
+		 */
+
+		if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp))
+			return 1;
+		
+		sleep(HZ);
+	}
+	return 0;
+}
+
+void __init attach_cs4232(struct address_info *hw_config)
+{
+	int base = hw_config->io_base,
+		irq = hw_config->irq,
+		dma1 = hw_config->dma,
+		dma2 = hw_config->dma2;
+
+	if (base == -1 || irq == -1 || dma1 == -1) {
+		printk(KERN_ERR "cs4232: dma, irq and io must be set.\n");
+		return;
+	}
+
+	if (dma2 == -1)
+		dma2 = dma1;
+
+	hw_config->slots[0] = ad1848_init("Crystal audio controller", base,
+					  irq,
+					  dma1,		/* Playback DMA */
+					  dma2,		/* Capture DMA */
+					  0,
+					  hw_config->osp,
+					  THIS_MODULE);
+
+	if (hw_config->slots[0] != -1 &&
+		audio_devs[hw_config->slots[0]]->mixer_dev!=-1)
+	{	
+		/* Assume the mixer map is as suggested in the CS4232 databook */
+		AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+		AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+		AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH);		/* FM synth */
+	}
+	if (mpu_base != 0 && mpu_irq != 0)
+	{
+		static struct address_info hw_config2 = {
+			0
+		};		/* Ensure it's initialized */
+
+		hw_config2.io_base = mpu_base;
+		hw_config2.irq = mpu_irq;
+		hw_config2.dma = -1;
+		hw_config2.dma2 = -1;
+		hw_config2.always_detect = 0;
+		hw_config2.name = NULL;
+		hw_config2.driver_use_1 = 0;
+		hw_config2.driver_use_2 = 0;
+		hw_config2.card_subtype = 0;
+
+		if (probe_uart401(&hw_config2, THIS_MODULE))
+		{
+			mpu_detected = 1;
+		}
+		else
+		{
+			mpu_base = mpu_irq = 0;
+		}
+		hw_config->slots[1] = hw_config2.slots[1];
+	}
+}
+
+static void __exit unload_cs4232(struct address_info *hw_config)
+{
+	int base = hw_config->io_base, irq = hw_config->irq;
+	int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+
+	if (dma2 == -1)
+		dma2 = dma1;
+
+	ad1848_unload(base,
+		      irq,
+		      dma1,	/* Playback DMA */
+		      dma2,	/* Capture DMA */
+		      0);
+
+	sound_unload_audiodev(hw_config->slots[0]);
+	if (mpu_base != 0 && mpu_irq != 0 && mpu_detected)
+	{
+		static struct address_info hw_config2 =
+		{
+			0
+		};		/* Ensure it's initialized */
+
+		hw_config2.io_base = mpu_base;
+		hw_config2.irq = mpu_irq;
+		hw_config2.dma = -1;
+		hw_config2.dma2 = -1;
+		hw_config2.always_detect = 0;
+		hw_config2.name = NULL;
+		hw_config2.driver_use_1 = 0;
+		hw_config2.driver_use_2 = 0;
+		hw_config2.card_subtype = 0;
+		hw_config2.slots[1] = hw_config->slots[1];
+
+		unload_uart401(&hw_config2);
+	}
+}
+
+static struct address_info cfg;
+static struct address_info cfg_mpu;
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma2	= -1;
+static int __initdata mpuio	= -1;
+static int __initdata mpuirq	= -1;
+static int __initdata synthio	= -1;
+static int __initdata synthirq	= -1;
+static int __initdata isapnp	= 1;
+
+MODULE_DESCRIPTION("CS4232 based soundcard driver"); 
+MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); 
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(io,"i");
+MODULE_PARM_DESC(io,"base I/O port for AD1848");
+MODULE_PARM(irq,"i");
+MODULE_PARM_DESC(irq,"IRQ for AD1848 chip");
+MODULE_PARM(dma,"i");
+MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip");
+MODULE_PARM(dma2,"i");
+MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip");
+MODULE_PARM(mpuio,"i");
+MODULE_PARM_DESC(mpuio,"MPU 401 base address");
+MODULE_PARM(mpuirq,"i");
+MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ");
+MODULE_PARM(synthio,"i");
+MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port");
+MODULE_PARM(synthirq,"i");
+MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ");
+MODULE_PARM(isapnp,"i");
+MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)");
+
+/*
+ *	Install a CS4232 based card. Need to have ad1848 and mpu401
+ *	loaded ready.
+ */
+
+/* All cs4232 based cards have the main ad1848 card either as CSC0000 or
+ * CSC0100. */
+struct isapnp_device_id isapnp_cs4232_list[] __initdata = {
+	{       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100),
+		0 },
+	{       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000),
+		0 },
+	/* Guillemot Turtlebeach something appears to be cs4232 compatible
+	 * (untested) */
+	{       ISAPNP_VENDOR('C','S','C'), ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('G','I','M'), ISAPNP_FUNCTION(0x0100),
+		0 },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_cs4232_list);
+
+int cs4232_isapnp_probe(struct pci_dev *dev, const struct isapnp_device_id *id)
+{
+	int ret;
+	struct address_info *isapnpcfg;
+
+	isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL);
+	if (!isapnpcfg) 
+		return -ENOMEM;
+	/*
+	 * If device is active, assume configured with /proc/isapnp
+	 * and use anyway. Any other way to check this?
+	 */
+	ret = dev->prepare(dev);
+	if(ret && ret != -EBUSY) {
+		printk(KERN_ERR "cs4232: ISA PnP found device that could not be autoconfigured.\n");
+		kfree(isapnpcfg);
+		return -ENODEV;
+	}
+	if(ret != -EBUSY) {
+		if(dev->activate(dev) < 0) {
+			printk(KERN_WARNING "cs4232: ISA PnP activate failed\n");
+			kfree(isapnpcfg);
+			return -ENODEV;
+		}
+	} /* else subfunction is already activated */
+
+	isapnpcfg->irq		= dev->irq_resource[0].start;
+	isapnpcfg->dma		= dev->dma_resource[0].start;
+	isapnpcfg->dma2		= dev->dma_resource[1].start;
+	isapnpcfg->io_base	= dev->resource[0].start;
+	if (probe_cs4232(isapnpcfg,TRUE) == 0) {
+		printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n");
+		return -ENODEV;
+	}
+	attach_cs4232(isapnpcfg);
+	pci_set_drvdata(dev,isapnpcfg);
+	return 0;
+}
+
+static int __init init_cs4232(void)
+{
+#ifdef CONFIG_SOUND_ALSA_WAVEFRONT_MODULE
+	if(synthio == -1)
+		printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n");
+	else {
+		synth_base = synthio;
+		synth_irq =  synthirq;	
+	}
+#else
+	if(synthio != -1)
+		printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n");
+#endif
+	cfg.irq = -1;
+
+	if (isapnp &&
+	    (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0)
+	)
+		return 0;
+
+	if(io==-1||irq==-1||dma==-1)
+	{
+		printk(KERN_ERR "cs4232: Must set io, irq and dma.\n");
+		return -ENODEV;
+	}
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma2;
+
+	cfg_mpu.io_base = -1;
+	cfg_mpu.irq = -1;
+
+	if (mpuio != -1 && mpuirq != -1) {
+		cfg_mpu.io_base = mpuio;
+		cfg_mpu.irq = mpuirq;
+		probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */
+	}
+
+	if (probe_cs4232(&cfg,FALSE) == 0)
+		return -ENODEV;
+	attach_cs4232(&cfg);
+	
+	return 0;
+}
+
+static int __exit cs4232_isapnp_remove(struct pci_dev *dev,
+			const struct isapnp_device_id *id)
+{
+	struct address_info *cfg = (struct address_info*)pci_get_drvdata(dev);
+	if (cfg)
+		unload_cs4232(cfg);
+	pci_set_drvdata(dev,NULL);
+	dev->deactivate(dev);
+	return 0;
+}
+
+static void __exit cleanup_cs4232(void)
+{
+	isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_remove);
+        if (cfg.irq != -1)
+		unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */
+}
+
+module_init(init_cs4232);
+module_exit(cleanup_cs4232);
+
+#ifndef MODULE
+static int __init setup_cs4232(char *str)
+{
+	/* io, irq, dma, dma2 mpuio, mpuirq*/
+	int ints[7];
+
+	/* If we have isapnp cards, no need for options */
+	if (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0)
+		return 1;
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	mpuio	= ints[5];
+	mpuirq	= ints[6];
+
+	return 1;
+}
+
+__setup("cs4232=", setup_cs4232);
+#endif
diff -Nru linux/sound/oss/cs4232.h linux-2.4.19-pre5-mjc/sound/oss/cs4232.h
--- linux/sound/oss/cs4232.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4232.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,11 @@
+/*
+ *	cs4232.h
+ *
+ * Copyright: Christoph Hellwig <chhellwig@gmx.net>
+ *
+ */
+
+int probe_cs4232 (struct address_info *hw_config,int useisapnp);
+void attach_cs4232 (struct address_info *hw_config);
+int probe_cs4232_mpu (struct address_info *hw_config);
+void attach_cs4232_mpu (struct address_info *hw_config);
diff -Nru linux/sound/oss/cs4281/Makefile linux-2.4.19-pre5-mjc/sound/oss/cs4281/Makefile
--- linux/sound/oss/cs4281/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,12 @@
+# Makefile for Cirrus Logic-Crystal CS4281 
+#
+
+O_TARGET := cs4281.o
+
+obj-y := cs4281m.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+clean:
+	rm -f core *.o *.a *.s
diff -Nru linux/sound/oss/cs4281/cs4281_hwdefs.h linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_hwdefs.h
--- linux/sound/oss/cs4281/cs4281_hwdefs.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_hwdefs.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1234 @@
+//****************************************************************************
+//
+// HWDEFS.H - Definitions of the registers and data structures used by the
+//            CS4281
+//
+// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp.
+//
+//****************************************************************************
+
+#ifndef _H_HWDEFS
+#define _H_HWDEFS
+
+//****************************************************************************
+//
+// The following define the offsets of the registers located in the PCI
+// configuration space of the CS4281 part.
+//
+//****************************************************************************
+#define PCICONFIG_DEVID_VENID                   0x00000000L
+#define PCICONFIG_STATUS_COMMAND                0x00000004L
+#define PCICONFIG_CLASS_REVISION                0x00000008L
+#define PCICONFIG_LATENCY_TIMER                 0x0000000CL
+#define PCICONFIG_BA0                           0x00000010L
+#define PCICONFIG_BA1                           0x00000014L
+#define PCICONFIG_SUBSYSID_SUBSYSVENID          0x0000002CL
+#define PCICONFIG_INTERRUPT                     0x0000003CL
+
+//****************************************************************************
+//
+// The following define the offsets of the registers accessed via base address
+// register zero on the CS4281 part.
+//
+//****************************************************************************
+#define BA0_HISR                                0x00000000L
+#define BA0_HICR                                0x00000008L
+#define BA0_HIMR                                0x0000000CL
+#define BA0_IIER                                0x00000010L
+#define BA0_HDSR0                               0x000000F0L
+#define BA0_HDSR1                               0x000000F4L
+#define BA0_HDSR2                               0x000000F8L
+#define BA0_HDSR3                               0x000000FCL
+#define BA0_DCA0                                0x00000110L
+#define BA0_DCC0                                0x00000114L
+#define BA0_DBA0                                0x00000118L
+#define BA0_DBC0                                0x0000011CL
+#define BA0_DCA1                                0x00000120L
+#define BA0_DCC1                                0x00000124L
+#define BA0_DBA1                                0x00000128L
+#define BA0_DBC1                                0x0000012CL
+#define BA0_DCA2                                0x00000130L
+#define BA0_DCC2                                0x00000134L
+#define BA0_DBA2                                0x00000138L
+#define BA0_DBC2                                0x0000013CL
+#define BA0_DCA3                                0x00000140L
+#define BA0_DCC3                                0x00000144L
+#define BA0_DBA3                                0x00000148L
+#define BA0_DBC3                                0x0000014CL
+#define BA0_DMR0                                0x00000150L
+#define BA0_DCR0                                0x00000154L
+#define BA0_DMR1                                0x00000158L
+#define BA0_DCR1                                0x0000015CL
+#define BA0_DMR2                                0x00000160L
+#define BA0_DCR2                                0x00000164L
+#define BA0_DMR3                                0x00000168L
+#define BA0_DCR3                                0x0000016CL
+#define BA0_DLMR                                0x00000170L
+#define BA0_DLSR                                0x00000174L
+#define BA0_FCR0                                0x00000180L
+#define BA0_FCR1                                0x00000184L
+#define BA0_FCR2                                0x00000188L
+#define BA0_FCR3                                0x0000018CL
+#define BA0_FPDR0                               0x00000190L
+#define BA0_FPDR1                               0x00000194L
+#define BA0_FPDR2                               0x00000198L
+#define BA0_FPDR3                               0x0000019CL
+#define BA0_FCHS                                0x0000020CL
+#define BA0_FSIC0                               0x00000210L
+#define BA0_FSIC1                               0x00000214L
+#define BA0_FSIC2                               0x00000218L
+#define BA0_FSIC3                               0x0000021CL
+#define BA0_PCICFG00                            0x00000300L
+#define BA0_PCICFG04                            0x00000304L
+#define BA0_PCICFG08                            0x00000308L
+#define BA0_PCICFG0C                            0x0000030CL
+#define BA0_PCICFG10                            0x00000310L
+#define BA0_PCICFG14                            0x00000314L
+#define BA0_PCICFG18                            0x00000318L
+#define BA0_PCICFG1C                            0x0000031CL
+#define BA0_PCICFG20                            0x00000320L
+#define BA0_PCICFG24                            0x00000324L
+#define BA0_PCICFG28                            0x00000328L
+#define BA0_PCICFG2C                            0x0000032CL
+#define BA0_PCICFG30                            0x00000330L
+#define BA0_PCICFG34                            0x00000334L
+#define BA0_PCICFG38                            0x00000338L
+#define BA0_PCICFG3C                            0x0000033CL
+#define BA0_PCICFG40                            0x00000340L
+#define BA0_PMCS                                0x00000344L
+#define BA0_CWPR                                0x000003E0L
+#define BA0_EPPMC                               0x000003E4L
+#define BA0_GPIOR                               0x000003E8L
+#define BA0_SPMC                                0x000003ECL
+#define BA0_CFLR                                0x000003F0L
+#define BA0_IISR                                0x000003F4L
+#define BA0_TMS                                 0x000003F8L
+#define BA0_SSVID                               0x000003FCL
+#define BA0_CLKCR1                              0x00000400L
+#define BA0_FRR                                 0x00000410L
+#define BA0_SLT12O                              0x0000041CL
+#define BA0_SERMC                               0x00000420L
+#define BA0_SERC1                               0x00000428L
+#define BA0_SERC2                               0x0000042CL
+#define BA0_SLT12M                              0x0000045CL
+#define BA0_ACCTL                               0x00000460L
+#define BA0_ACSTS                               0x00000464L
+#define BA0_ACOSV                               0x00000468L
+#define BA0_ACCAD                               0x0000046CL
+#define BA0_ACCDA                               0x00000470L
+#define BA0_ACISV                               0x00000474L
+#define BA0_ACSAD                               0x00000478L
+#define BA0_ACSDA                               0x0000047CL
+#define BA0_JSPT                                0x00000480L
+#define BA0_JSCTL                               0x00000484L
+#define BA0_MIDCR                               0x00000490L
+#define BA0_MIDCMD                              0x00000494L
+#define BA0_MIDSR                               0x00000494L
+#define BA0_MIDWP                               0x00000498L
+#define BA0_MIDRP                               0x0000049CL
+#define BA0_AODSD1                              0x000004A8L
+#define BA0_AODSD2                              0x000004ACL
+#define BA0_CFGI                                0x000004B0L
+#define BA0_SLT12M2                             0x000004DCL
+#define BA0_ACSTS2                              0x000004E4L
+#define BA0_ACISV2                              0x000004F4L
+#define BA0_ACSAD2                              0x000004F8L
+#define BA0_ACSDA2                              0x000004FCL
+#define BA0_IOTGP                               0x00000500L
+#define BA0_IOTSB                               0x00000504L
+#define BA0_IOTFM                               0x00000508L
+#define BA0_IOTDMA                              0x0000050CL
+#define BA0_IOTAC0                              0x00000500L
+#define BA0_IOTAC1                              0x00000504L
+#define BA0_IOTAC2                              0x00000508L
+#define BA0_IOTAC3                              0x0000050CL
+#define BA0_IOTPCP                              0x0000052CL
+#define BA0_IOTCC                               0x00000530L
+#define BA0_IOTCR                               0x0000058CL
+#define BA0_PCPRR                               0x00000600L
+#define BA0_PCPGR                               0x00000604L
+#define BA0_PCPCR                               0x00000608L
+#define BA0_PCPCIEN                             0x00000608L
+#define BA0_SBMAR                               0x00000700L
+#define BA0_SBMDR                               0x00000704L
+#define BA0_SBRR                                0x00000708L
+#define BA0_SBRDP                               0x0000070CL
+#define BA0_SBWDP                               0x00000710L
+#define BA0_SBWBS                               0x00000710L
+#define BA0_SBRBS                               0x00000714L
+#define BA0_FMSR                                0x00000730L
+#define BA0_B0AP                                0x00000730L
+#define BA0_FMDP                                0x00000734L
+#define BA0_B1AP                                0x00000738L
+#define BA0_B1DP                                0x0000073CL
+#define BA0_SSPM                                0x00000740L
+#define BA0_DACSR                               0x00000744L
+#define BA0_ADCSR                               0x00000748L
+#define BA0_SSCR                                0x0000074CL
+#define BA0_FMLVC                               0x00000754L
+#define BA0_FMRVC                               0x00000758L
+#define BA0_SRCSA                               0x0000075CL
+#define BA0_PPLVC                               0x00000760L
+#define BA0_PPRVC                               0x00000764L
+#define BA0_PASR                                0x00000768L
+#define BA0_CASR                                0x0000076CL
+
+//****************************************************************************
+//
+// The following define the offsets of the AC97 shadow registers, which appear
+// as a virtual extension to the base address register zero memory range.
+//
+//****************************************************************************
+#define AC97_REG_OFFSET_MASK                    0x0000007EL
+#define AC97_CODEC_NUMBER_MASK                  0x00003000L
+
+#define BA0_AC97_RESET                          0x00001000L
+#define BA0_AC97_MASTER_VOLUME                  0x00001002L
+#define BA0_AC97_HEADPHONE_VOLUME               0x00001004L
+#define BA0_AC97_MASTER_VOLUME_MONO             0x00001006L
+#define BA0_AC97_MASTER_TONE                    0x00001008L
+#define BA0_AC97_PC_BEEP_VOLUME                 0x0000100AL
+#define BA0_AC97_PHONE_VOLUME                   0x0000100CL
+#define BA0_AC97_MIC_VOLUME                     0x0000100EL
+#define BA0_AC97_LINE_IN_VOLUME                 0x00001010L
+#define BA0_AC97_CD_VOLUME                      0x00001012L
+#define BA0_AC97_VIDEO_VOLUME                   0x00001014L
+#define BA0_AC97_AUX_VOLUME                     0x00001016L
+#define BA0_AC97_PCM_OUT_VOLUME                 0x00001018L
+#define BA0_AC97_RECORD_SELECT                  0x0000101AL
+#define BA0_AC97_RECORD_GAIN                    0x0000101CL
+#define BA0_AC97_RECORD_GAIN_MIC                0x0000101EL
+#define BA0_AC97_GENERAL_PURPOSE                0x00001020L
+#define BA0_AC97_3D_CONTROL                     0x00001022L
+#define BA0_AC97_MODEM_RATE                     0x00001024L
+#define BA0_AC97_POWERDOWN                      0x00001026L
+#define BA0_AC97_EXT_AUDIO_ID                   0x00001028L
+#define BA0_AC97_EXT_AUDIO_POWER                0x0000102AL
+#define BA0_AC97_PCM_FRONT_DAC_RATE             0x0000102CL
+#define BA0_AC97_PCM_SURR_DAC_RATE              0x0000102EL
+#define BA0_AC97_PCM_LFE_DAC_RATE               0x00001030L
+#define BA0_AC97_PCM_LR_ADC_RATE                0x00001032L
+#define BA0_AC97_MIC_ADC_RATE                   0x00001034L
+#define BA0_AC97_6CH_VOL_C_LFE                  0x00001036L
+#define BA0_AC97_6CH_VOL_SURROUND               0x00001038L
+#define BA0_AC97_RESERVED_3A                    0x0000103AL
+#define BA0_AC97_EXT_MODEM_ID                   0x0000103CL
+#define BA0_AC97_EXT_MODEM_POWER                0x0000103EL
+#define BA0_AC97_LINE1_CODEC_RATE               0x00001040L
+#define BA0_AC97_LINE2_CODEC_RATE               0x00001042L
+#define BA0_AC97_HANDSET_CODEC_RATE             0x00001044L
+#define BA0_AC97_LINE1_CODEC_LEVEL              0x00001046L
+#define BA0_AC97_LINE2_CODEC_LEVEL              0x00001048L
+#define BA0_AC97_HANDSET_CODEC_LEVEL            0x0000104AL
+#define BA0_AC97_GPIO_PIN_CONFIG                0x0000104CL
+#define BA0_AC97_GPIO_PIN_TYPE                  0x0000104EL
+#define BA0_AC97_GPIO_PIN_STICKY                0x00001050L
+#define BA0_AC97_GPIO_PIN_WAKEUP                0x00001052L
+#define BA0_AC97_GPIO_PIN_STATUS                0x00001054L
+#define BA0_AC97_MISC_MODEM_AFE_STAT            0x00001056L
+#define BA0_AC97_RESERVED_58                    0x00001058L
+#define BA0_AC97_CRYSTAL_REV_N_FAB_ID           0x0000105AL
+#define BA0_AC97_TEST_AND_MISC_CTRL             0x0000105CL
+#define BA0_AC97_AC_MODE                        0x0000105EL
+#define BA0_AC97_MISC_CRYSTAL_CONTROL           0x00001060L
+#define BA0_AC97_LINE1_HYPRID_CTRL              0x00001062L
+#define BA0_AC97_VENDOR_RESERVED_64             0x00001064L
+#define BA0_AC97_VENDOR_RESERVED_66             0x00001066L
+#define BA0_AC97_SPDIF_CONTROL                  0x00001068L
+#define BA0_AC97_VENDOR_RESERVED_6A             0x0000106AL
+#define BA0_AC97_VENDOR_RESERVED_6C             0x0000106CL
+#define BA0_AC97_VENDOR_RESERVED_6E             0x0000106EL
+#define BA0_AC97_VENDOR_RESERVED_70             0x00001070L
+#define BA0_AC97_VENDOR_RESERVED_72             0x00001072L
+#define BA0_AC97_VENDOR_RESERVED_74             0x00001074L
+#define BA0_AC97_CAL_ADDRESS                    0x00001076L
+#define BA0_AC97_CAL_DATA                       0x00001078L
+#define BA0_AC97_VENDOR_RESERVED_7A             0x0000107AL
+#define BA0_AC97_VENDOR_ID1                     0x0000107CL
+#define BA0_AC97_VENDOR_ID2                     0x0000107EL
+
+//****************************************************************************
+//
+// The following define the offsets of the registers and memories accessed via
+// base address register one on the CS4281 part.
+//
+//****************************************************************************
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI device ID/vendor ID
+// register.
+//
+//****************************************************************************
+#define PDV_VENID_MASK                          0x0000FFFFL
+#define PDV_DEVID_MASK                          0xFFFF0000L
+#define PDV_VENID_SHIFT                         0L
+#define PDV_DEVID_SHIFT                         16L
+#define VENID_CIRRUS_LOGIC                      0x1013L
+#define DEVID_CS4281                            0x6005L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI status and command
+// register.
+//
+//****************************************************************************
+#define PSC_IO_SPACE_ENABLE                     0x00000001L
+#define PSC_MEMORY_SPACE_ENABLE                 0x00000002L
+#define PSC_BUS_MASTER_ENABLE                   0x00000004L
+#define PSC_SPECIAL_CYCLES                      0x00000008L
+#define PSC_MWI_ENABLE                          0x00000010L
+#define PSC_VGA_PALETTE_SNOOP                   0x00000020L
+#define PSC_PARITY_RESPONSE                     0x00000040L
+#define PSC_WAIT_CONTROL                        0x00000080L
+#define PSC_SERR_ENABLE                         0x00000100L
+#define PSC_FAST_B2B_ENABLE                     0x00000200L
+#define PSC_UDF_MASK                            0x007F0000L
+#define PSC_FAST_B2B_CAPABLE                    0x00800000L
+#define PSC_PARITY_ERROR_DETECTED               0x01000000L
+#define PSC_DEVSEL_TIMING_MASK                  0x06000000L
+#define PSC_TARGET_ABORT_SIGNALLED              0x08000000L
+#define PSC_RECEIVED_TARGET_ABORT               0x10000000L
+#define PSC_RECEIVED_MASTER_ABORT               0x20000000L
+#define PSC_SIGNALLED_SERR                      0x40000000L
+#define PSC_DETECTED_PARITY_ERROR               0x80000000L
+#define PSC_UDF_SHIFT                           16L
+#define PSC_DEVSEL_TIMING_SHIFT                 25L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI class/revision ID
+// register.
+//
+//****************************************************************************
+#define PCR_REVID_MASK                          0x000000FFL
+#define PCR_INTERFACE_MASK                      0x0000FF00L
+#define PCR_SUBCLASS_MASK                       0x00FF0000L
+#define PCR_CLASS_MASK                          0xFF000000L
+#define PCR_REVID_SHIFT                         0L
+#define PCR_INTERFACE_SHIFT                     8L
+#define PCR_SUBCLASS_SHIFT                      16L
+#define PCR_CLASS_SHIFT                         24L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI latency timer register.
+//
+//****************************************************************************
+#define PLT_CACHE_LINE_SIZE_MASK                0x000000FFL
+#define PLT_LATENCY_TIMER_MASK                  0x0000FF00L
+#define PLT_HEADER_TYPE_MASK                    0x00FF0000L
+#define PLT_BIST_MASK                           0xFF000000L
+#define PLT_CACHE_LINE_SIZE_SHIFT               0L
+#define PLT_LATENCY_TIMER_SHIFT                 8L
+#define PLT_HEADER_TYPE_SHIFT                   16L
+#define PLT_BIST_SHIFT                          24L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI base address registers.
+//
+//****************************************************************************
+#define PBAR_MEMORY_SPACE_INDICATOR             0x00000001L
+#define PBAR_LOCATION_TYPE_MASK                 0x00000006L
+#define PBAR_NOT_PREFETCHABLE                   0x00000008L
+#define PBAR_ADDRESS_MASK                       0xFFFFFFF0L
+#define PBAR_LOCATION_TYPE_SHIFT                1L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI subsystem ID/subsystem
+// vendor ID register.
+//
+//****************************************************************************
+#define PSS_SUBSYSTEM_VENDOR_ID_MASK            0x0000FFFFL
+#define PSS_SUBSYSTEM_ID_MASK                   0xFFFF0000L
+#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT           0L
+#define PSS_SUBSYSTEM_ID_SHIFT                  16L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PCI interrupt register.
+//
+//****************************************************************************
+#define PI_LINE_MASK                            0x000000FFL
+#define PI_PIN_MASK                             0x0000FF00L
+#define PI_MIN_GRANT_MASK                       0x00FF0000L
+#define PI_MAX_LATENCY_MASK                     0xFF000000L
+#define PI_LINE_SHIFT                           0L
+#define PI_PIN_SHIFT                            8L
+#define PI_MIN_GRANT_SHIFT                      16L
+#define PI_MAX_LATENCY_SHIFT                    24L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the host interrupt status
+// register.
+//
+//****************************************************************************
+#define HISR_HVOLMASK                            0x00000003L
+#define HISR_VDNI                                0x00000001L
+#define HISR_VUPI                                0x00000002L
+#define HISR_GP1I                                0x00000004L
+#define HISR_GP3I                                0x00000008L
+#define HISR_GPSI                                0x00000010L
+#define HISR_GPPI                                0x00000020L
+#define HISR_DMAI                                0x00040000L
+#define HISR_FIFOI                               0x00100000L
+#define HISR_HVOL                                0x00200000L
+#define HISR_MIDI                                0x00400000L
+#define HISR_SBINT                               0x00800000L
+#define HISR_INTENA                              0x80000000L
+#define HISR_DMA_MASK                            0x00000F00L
+#define HISR_FIFO_MASK                           0x0000F000L
+#define HISR_DMA_SHIFT                           8L
+#define HISR_FIFO_SHIFT                          12L
+#define HISR_FIFO0                               0x00001000L
+#define HISR_FIFO1                               0x00002000L
+#define HISR_FIFO2                               0x00004000L
+#define HISR_FIFO3                               0x00008000L
+#define HISR_DMA0                                0x00000100L
+#define HISR_DMA1                                0x00000200L
+#define HISR_DMA2                                0x00000400L
+#define HISR_DMA3                                0x00000800L
+#define HISR_RESERVED                            0x40000000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the host interrupt control
+// register.
+//
+//****************************************************************************
+#define HICR_IEV                                 0x00000001L
+#define HICR_CHGM                                0x00000002L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the DMA Mode Register n
+// (DMRn)
+//
+//****************************************************************************
+#define DMRn_TR_MASK                             0x0000000CL
+#define DMRn_TR_SHIFT                            2L
+#define DMRn_AUTO                                0x00000010L
+#define DMRn_TR_READ                             0x00000008L
+#define DMRn_TR_WRITE                            0x00000004L
+#define DMRn_TYPE_MASK                           0x000000C0L
+#define DMRn_TYPE_SHIFT                          6L
+#define DMRn_SIZE8                               0x00010000L
+#define DMRn_MONO                                0x00020000L
+#define DMRn_BEND                                0x00040000L
+#define DMRn_USIGN                               0x00080000L
+#define DMRn_SIZE20                              0x00100000L
+#define DMRn_SWAPC                               0x00400000L
+#define DMRn_CBC                                 0x01000000L
+#define DMRn_TBC                                 0x02000000L
+#define DMRn_POLL                                0x10000000L
+#define DMRn_DMA                                 0x20000000L
+#define DMRn_FSEL_MASK                           0xC0000000L
+#define DMRn_FSEL_SHIFT                          30L
+#define DMRn_FSEL0                               0x00000000L
+#define DMRn_FSEL1                               0x40000000L
+#define DMRn_FSEL2                               0x80000000L
+#define DMRn_FSEL3                               0xC0000000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the DMA Command Register n
+// (DCRn)
+//
+//****************************************************************************
+#define DCRn_HTCIE                               0x00020000L
+#define DCRn_TCIE                                0x00010000L
+#define DCRn_MSK                                 0x00000001L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the FIFO Control 
+// register n.(FCRn)
+//
+//****************************************************************************
+#define FCRn_OF_MASK                            0x0000007FL
+#define FCRn_OF_SHIFT                           0L
+#define FCRn_SZ_MASK                            0x00007F00L
+#define FCRn_SZ_SHIFT                           8L
+#define FCRn_LS_MASK                            0x001F0000L
+#define FCRn_LS_SHIFT                           16L
+#define FCRn_RS_MASK                            0x1F000000L
+#define FCRn_RS_SHIFT                           24L
+#define FCRn_FEN                                0x80000000L
+#define FCRn_PSH                                0x20000000L
+#define FCRn_DACZ                               0x40000000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the serial port Power Management
+// control register.(SPMC)
+//
+//****************************************************************************
+#define SPMC_RSTN                               0x00000001L
+#define SPMC_ASYN                               0x00000002L
+#define SPMC_WUP1                               0x00000004L
+#define SPMC_WUP2                               0x00000008L
+#define SPMC_ASDI2E                             0x00000100L
+#define SPMC_ESSPD                              0x00000200L
+#define SPMC_GISPEN                             0x00004000L
+#define SPMC_GIPPEN                             0x00008000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the Configuration Load register.
+// (CFLR)
+//
+//****************************************************************************
+#define CFLR_CLOCK_SOURCE_MASK                  0x00000003L
+#define CFLR_CLOCK_SOURCE_AC97                  0x00000001L
+
+#define CFLR_CB0_MASK                            0x000000FFL
+#define CFLR_CB1_MASK                            0x0000FF00L
+#define CFLR_CB2_MASK                            0x00FF0000L
+#define CFLR_CB3_MASK                            0xFF000000L
+#define CFLR_CB0_SHIFT                           0L
+#define CFLR_CB1_SHIFT                           8L
+#define CFLR_CB2_SHIFT                           16L
+#define CFLR_CB3_SHIFT                           24L
+
+#define IOTCR_DMA0                              0x00000000L
+#define IOTCR_DMA1                              0x00000400L
+#define IOTCR_DMA2                              0x00000800L
+#define IOTCR_DMA3                              0x00000C00L
+#define IOTCR_CCLS                              0x00000100L
+#define IOTCR_PCPCI                             0x00000200L
+#define IOTCR_DDMA                              0x00000300L
+
+#define SBWBS_WBB                               0x00000080L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the SRC Slot Assignment Register
+// (SRCSA)
+//
+//****************************************************************************
+#define SRCSA_PLSS_MASK                         0x0000001FL
+#define SRCSA_PLSS_SHIFT                        0L
+#define SRCSA_PRSS_MASK                         0x00001F00L
+#define SRCSA_PRSS_SHIFT                        8L
+#define SRCSA_CLSS_MASK                         0x001F0000L
+#define SRCSA_CLSS_SHIFT                        16L
+#define SRCSA_CRSS_MASK                         0x1F000000L
+#define SRCSA_CRSS_SHIFT                        24L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the Sound System Power Management
+// register.(SSPM)
+//
+//****************************************************************************
+#define SSPM_FPDN                               0x00000080L
+#define SSPM_MIXEN                              0x00000040L
+#define SSPM_CSRCEN                             0x00000020L
+#define SSPM_PSRCEN                             0x00000010L
+#define SSPM_JSEN                               0x00000008L
+#define SSPM_ACLEN                              0x00000004L
+#define SSPM_FMEN                               0x00000002L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the Sound System Control
+// Register. (SSCR)
+//
+//****************************************************************************
+#define SSCR_SB                                 0x00000004L
+#define SSCR_HVC                                0x00000008L
+#define SSCR_LPFIFO                             0x00000040L
+#define SSCR_LPSRC                              0x00000080L
+#define SSCR_XLPSRC                             0x00000100L
+#define SSCR_MVMD                               0x00010000L
+#define SSCR_MVAD                               0x00020000L
+#define SSCR_MVLD                               0x00040000L
+#define SSCR_MVCS                               0x00080000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the Clock Control Register 1. 
+// (CLKCR1)
+//
+//****************************************************************************
+#define CLKCR1_DLLSS_MASK                       0x0000000CL
+#define CLKCR1_DLLSS_SHIFT                      2L
+#define CLKCR1_DLLP                             0x00000010L
+#define CLKCR1_SWCE                             0x00000020L
+#define CLKCR1_DLLOS                            0x00000040L
+#define CLKCR1_CKRA                             0x00010000L
+#define CLKCR1_CKRN                             0x00020000L
+#define CLKCR1_DLLRDY                           0x01000000L
+#define CLKCR1_CLKON                            0x02000000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the Sound Blaster Read Buffer
+// Status.(SBRBS)
+//
+//****************************************************************************
+#define SBRBS_RD_MASK                           0x0000007FL
+#define SBRBS_RD_SHIFT                          0L
+#define SBRBS_RBF                               0x00000080L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the serial port master control
+// register.(SERMC)
+//
+//****************************************************************************
+#define SERMC_MSPE                              0x00000001L
+#define SERMC_PTC_MASK                          0x0000000EL
+#define SERMC_PTC_SHIFT                         1L
+#define SERMC_PTC_AC97                          0x00000002L
+#define SERMC_PLB                               0x00000010L
+#define SERMC_PXLB                              0x00000020L
+#define SERMC_LOFV                              0x00080000L
+#define SERMC_SLB                               0x00100000L
+#define SERMC_SXLB                              0x00200000L
+#define SERMC_ODSEN1                            0x01000000L
+#define SERMC_ODSEN2                            0x02000000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the General Purpose I/O Register. 
+// (GPIOR)
+//
+//****************************************************************************
+#define GPIOR_VDNS                              0x00000001L
+#define GPIOR_VUPS                              0x00000002L
+#define GPIOR_GP1S                              0x00000004L
+#define GPIOR_GP3S                              0x00000008L
+#define GPIOR_GPSS                              0x00000010L
+#define GPIOR_GPPS                              0x00000020L
+#define GPIOR_GP1D                              0x00000400L
+#define GPIOR_GP3D                              0x00000800L
+#define GPIOR_VDNLT                             0x00010000L
+#define GPIOR_VDNPO                             0x00020000L
+#define GPIOR_VDNST                             0x00040000L
+#define GPIOR_VDNW                              0x00080000L
+#define GPIOR_VUPLT                             0x00100000L
+#define GPIOR_VUPPO                             0x00200000L
+#define GPIOR_VUPST                             0x00400000L
+#define GPIOR_VUPW                              0x00800000L
+#define GPIOR_GP1OE                             0x01000000L
+#define GPIOR_GP1PT                             0x02000000L
+#define GPIOR_GP1ST                             0x04000000L
+#define GPIOR_GP1W                              0x08000000L
+#define GPIOR_GP3OE                             0x10000000L
+#define GPIOR_GP3PT                             0x20000000L
+#define GPIOR_GP3ST                             0x40000000L
+#define GPIOR_GP3W                              0x80000000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the clock control register 1.
+//
+//****************************************************************************
+#define CLKCR1_PLLSS_MASK                       0x0000000CL
+#define CLKCR1_PLLSS_SERIAL                     0x00000000L
+#define CLKCR1_PLLSS_CRYSTAL                    0x00000004L
+#define CLKCR1_PLLSS_PCI                        0x00000008L
+#define CLKCR1_PLLSS_RESERVED                   0x0000000CL
+#define CLKCR1_PLLP                             0x00000010L
+#define CLKCR1_SWCE                             0x00000020L
+#define CLKCR1_PLLOS                            0x00000040L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the feature reporting register.
+//
+//****************************************************************************
+#define FRR_FAB_MASK                            0x00000003L
+#define FRR_MASK_MASK                           0x0000001CL
+#define FRR_ID_MASK                             0x00003000L
+#define FRR_FAB_SHIFT                           0L
+#define FRR_MASK_SHIFT                          2L
+#define FRR_ID_SHIFT                            12L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the serial port 1 configuration
+// register.
+//
+//****************************************************************************
+#define SERC1_VALUE                             0x00000003L
+#define SERC1_SO1EN                             0x00000001L
+#define SERC1_SO1F_MASK                         0x0000000EL
+#define SERC1_SO1F_CS423X                       0x00000000L
+#define SERC1_SO1F_AC97                         0x00000002L
+#define SERC1_SO1F_DAC                          0x00000004L
+#define SERC1_SO1F_SPDIF                        0x00000006L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the serial port 2 configuration
+// register.
+//
+//****************************************************************************
+#define SERC2_VALUE                             0x00000003L
+#define SERC2_SI1EN                             0x00000001L
+#define SERC2_SI1F_MASK                         0x0000000EL
+#define SERC2_SI1F_CS423X                       0x00000000L
+#define SERC2_SI1F_AC97                         0x00000002L
+#define SERC2_SI1F_ADC                          0x00000004L
+#define SERC2_SI1F_SPDIF                        0x00000006L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 control register.
+//
+//****************************************************************************
+#define ACCTL_ESYN                              0x00000002L
+#define ACCTL_VFRM                              0x00000004L
+#define ACCTL_DCV                               0x00000008L
+#define ACCTL_CRW                               0x00000010L
+#define ACCTL_TC                                0x00000040L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 status register.
+//
+//****************************************************************************
+#define ACSTS_CRDY                              0x00000001L
+#define ACSTS_VSTS                              0x00000002L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 output slot valid
+// register.
+//
+//****************************************************************************
+#define ACOSV_SLV3                              0x00000001L
+#define ACOSV_SLV4                              0x00000002L
+#define ACOSV_SLV5                              0x00000004L
+#define ACOSV_SLV6                              0x00000008L
+#define ACOSV_SLV7                              0x00000010L
+#define ACOSV_SLV8                              0x00000020L
+#define ACOSV_SLV9                              0x00000040L
+#define ACOSV_SLV10                             0x00000080L
+#define ACOSV_SLV11                             0x00000100L
+#define ACOSV_SLV12                             0x00000200L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 command address
+// register.
+//
+//****************************************************************************
+#define ACCAD_CI_MASK                           0x0000007FL
+#define ACCAD_CI_SHIFT                          0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 command data register.
+//
+//****************************************************************************
+#define ACCDA_CD_MASK                           0x0000FFFFL
+#define ACCDA_CD_SHIFT                          0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 input slot valid
+// register.
+//
+//****************************************************************************
+#define ACISV_ISV3                              0x00000001L
+#define ACISV_ISV4                              0x00000002L
+#define ACISV_ISV5                              0x00000004L
+#define ACISV_ISV6                              0x00000008L
+#define ACISV_ISV7                              0x00000010L
+#define ACISV_ISV8                              0x00000020L
+#define ACISV_ISV9                              0x00000040L
+#define ACISV_ISV10                             0x00000080L
+#define ACISV_ISV11                             0x00000100L
+#define ACISV_ISV12                             0x00000200L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 status address
+// register.
+//
+//****************************************************************************
+#define ACSAD_SI_MASK                           0x0000007FL
+#define ACSAD_SI_SHIFT                          0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 status data register.
+//
+//****************************************************************************
+#define ACSDA_SD_MASK                           0x0000FFFFL
+#define ACSDA_SD_SHIFT                          0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the I/O trap address and control
+// registers (all 12).
+//
+//****************************************************************************
+#define IOTAC_SA_MASK                           0x0000FFFFL
+#define IOTAC_MSK_MASK                          0x000F0000L
+#define IOTAC_IODC_MASK                         0x06000000L
+#define IOTAC_IODC_16_BIT                       0x00000000L
+#define IOTAC_IODC_10_BIT                       0x02000000L
+#define IOTAC_IODC_12_BIT                       0x04000000L
+#define IOTAC_WSPI                              0x08000000L
+#define IOTAC_RSPI                              0x10000000L
+#define IOTAC_WSE                               0x20000000L
+#define IOTAC_WE                                0x40000000L
+#define IOTAC_RE                                0x80000000L
+#define IOTAC_SA_SHIFT                          0L
+#define IOTAC_MSK_SHIFT                         16L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PC/PCI master enable
+// register.
+//
+//****************************************************************************
+#define PCPCIEN_EN                              0x00000001L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the joystick poll/trigger
+// register.
+//
+//****************************************************************************
+#define JSPT_CAX                                0x00000001L
+#define JSPT_CAY                                0x00000002L
+#define JSPT_CBX                                0x00000004L
+#define JSPT_CBY                                0x00000008L
+#define JSPT_BA1                                0x00000010L
+#define JSPT_BA2                                0x00000020L
+#define JSPT_BB1                                0x00000040L
+#define JSPT_BB2                                0x00000080L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the joystick control register.
+// The TBF bit has been moved from MIDSR register to JSCTL register bit 8.
+//
+//****************************************************************************
+#define JSCTL_SP_MASK                           0x00000003L
+#define JSCTL_SP_SLOW                           0x00000000L
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001L
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002L
+#define JSCTL_SP_FAST                           0x00000003L
+#define JSCTL_ARE                               0x00000004L
+#define JSCTL_TBF                               0x00000100L
+
+
+//****************************************************************************
+//
+// The following defines are for the flags in the MIDI control register.
+//
+//****************************************************************************
+#define MIDCR_TXE                               0x00000001L
+#define MIDCR_RXE                               0x00000002L
+#define MIDCR_RIE                               0x00000004L
+#define MIDCR_TIE                               0x00000008L
+#define MIDCR_MLB                               0x00000010L
+#define MIDCR_MRST                              0x00000020L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the MIDI status register.
+//
+//****************************************************************************
+#define MIDSR_RBE                               0x00000080L
+#define MIDSR_RDA                               0x00008000L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the MIDI write port register.
+//
+//****************************************************************************
+#define MIDWP_MWD_MASK                          0x000000FFL
+#define MIDWP_MWD_SHIFT                         0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the MIDI read port register.
+//
+//****************************************************************************
+#define MIDRP_MRD_MASK                          0x000000FFL
+#define MIDRP_MRD_SHIFT                         0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the configuration interface
+// register.
+//
+//****************************************************************************
+#define CFGI_CLK                                0x00000001L
+#define CFGI_DOUT                               0x00000002L
+#define CFGI_DIN_EEN                            0x00000004L
+#define CFGI_EELD                               0x00000008L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the subsystem ID and vendor ID
+// register.
+//
+//****************************************************************************
+#define SSVID_VID_MASK                          0x0000FFFFL
+#define SSVID_SID_MASK                          0xFFFF0000L
+#define SSVID_VID_SHIFT                         0L
+#define SSVID_SID_SHIFT                         16L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the GPIO pin interface register.
+//
+//****************************************************************************
+#define GPIOR_VOLDN                             0x00000001L
+#define GPIOR_VOLUP                             0x00000002L
+#define GPIOR_SI2D                              0x00000004L
+#define GPIOR_SI2OE                             0x00000008L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 status register 2.
+//
+//****************************************************************************
+#define ACSTS2_CRDY                             0x00000001L
+#define ACSTS2_VSTS                             0x00000002L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 input slot valid
+// register 2.
+//
+//****************************************************************************
+#define ACISV2_ISV3                             0x00000001L
+#define ACISV2_ISV4                             0x00000002L
+#define ACISV2_ISV5                             0x00000004L
+#define ACISV2_ISV6                             0x00000008L
+#define ACISV2_ISV7                             0x00000010L
+#define ACISV2_ISV8                             0x00000020L
+#define ACISV2_ISV9                             0x00000040L
+#define ACISV2_ISV10                            0x00000080L
+#define ACISV2_ISV11                            0x00000100L
+#define ACISV2_ISV12                            0x00000200L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 status address
+// register 2.
+//
+//****************************************************************************
+#define ACSAD2_SI_MASK                          0x0000007FL
+#define ACSAD2_SI_SHIFT                         0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 status data register 2.
+//
+//****************************************************************************
+#define ACSDA2_SD_MASK                          0x0000FFFFL
+#define ACSDA2_SD_SHIFT                         0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the I/O trap control register.
+//
+//****************************************************************************
+#define IOTCR_ITD                               0x00000001L
+#define IOTCR_HRV                               0x00000002L
+#define IOTCR_SRV                               0x00000004L
+#define IOTCR_DTI                               0x00000008L
+#define IOTCR_DFI                               0x00000010L
+#define IOTCR_DDP                               0x00000020L
+#define IOTCR_JTE                               0x00000040L
+#define IOTCR_PPE                               0x00000080L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the I/O trap address and control
+// registers for Hardware Master Volume.  
+//
+//****************************************************************************
+#define IOTGP_SA_MASK                           0x0000FFFFL
+#define IOTGP_MSK_MASK                          0x000F0000L
+#define IOTGP_IODC_MASK                         0x06000000L
+#define IOTGP_IODC_16_BIT                       0x00000000L
+#define IOTGP_IODC_10_BIT                       0x02000000L
+#define IOTGP_IODC_12_BIT                       0x04000000L
+#define IOTGP_WSPI                              0x08000000L
+#define IOTGP_RSPI                              0x10000000L
+#define IOTGP_WSE                               0x20000000L
+#define IOTGP_WE                                0x40000000L
+#define IOTGP_RE                                0x80000000L
+#define IOTGP_SA_SHIFT                          0L
+#define IOTGP_MSK_SHIFT                         16L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the I/O trap address and control
+// registers for Sound Blaster
+//
+//****************************************************************************
+#define IOTSB_SA_MASK                           0x0000FFFFL
+#define IOTSB_MSK_MASK                          0x000F0000L
+#define IOTSB_IODC_MASK                         0x06000000L
+#define IOTSB_IODC_16_BIT                       0x00000000L
+#define IOTSB_IODC_10_BIT                       0x02000000L
+#define IOTSB_IODC_12_BIT                       0x04000000L
+#define IOTSB_WSPI                              0x08000000L
+#define IOTSB_RSPI                              0x10000000L
+#define IOTSB_WSE                               0x20000000L
+#define IOTSB_WE                                0x40000000L
+#define IOTSB_RE                                0x80000000L
+#define IOTSB_SA_SHIFT                          0L
+#define IOTSB_MSK_SHIFT                         16L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the I/O trap address and control
+// registers for FM.
+//
+//****************************************************************************
+#define IOTFM_SA_MASK                           0x0000FFFFL
+#define IOTFM_MSK_MASK                          0x000F0000L
+#define IOTFM_IODC_MASK                         0x06000000L
+#define IOTFM_IODC_16_BIT                       0x00000000L
+#define IOTFM_IODC_10_BIT                       0x02000000L
+#define IOTFM_IODC_12_BIT                       0x04000000L
+#define IOTFM_WSPI                              0x08000000L
+#define IOTFM_RSPI                              0x10000000L
+#define IOTFM_WSE                               0x20000000L
+#define IOTFM_WE                                0x40000000L
+#define IOTFM_RE                                0x80000000L
+#define IOTFM_SA_SHIFT                          0L
+#define IOTFM_MSK_SHIFT                         16L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PC/PCI request register.
+//
+//****************************************************************************
+#define PCPRR_RDC_MASK                         0x00000007L
+#define PCPRR_REQ                              0x00008000L
+#define PCPRR_RDC_SHIFT                        0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PC/PCI grant register.
+//
+//****************************************************************************
+#define PCPGR_GDC_MASK                         0x00000007L
+#define PCPGR_VL                               0x00008000L
+#define PCPGR_GDC_SHIFT                        0L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the PC/PCI Control Register.
+//
+//****************************************************************************
+#define PCPCR_EN                               0x00000001L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the debug index register.
+//
+//****************************************************************************
+#define DREG_REGID_MASK                         0x0000007FL
+#define DREG_DEBUG                              0x00000080L
+#define DREG_RGBK_MASK                          0x00000700L
+#define DREG_TRAP                               0x00000800L
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_TRAPX                              0x00001000L
+#endif
+#endif
+#define DREG_REGID_SHIFT                        0L
+#define DREG_RGBK_SHIFT                         8L
+#define DREG_RGBK_REGID_MASK                    0x0000077FL
+#define DREG_REGID_R0                           0x00000010L
+#define DREG_REGID_R1                           0x00000011L
+#define DREG_REGID_R2                           0x00000012L
+#define DREG_REGID_R3                           0x00000013L
+#define DREG_REGID_R4                           0x00000014L
+#define DREG_REGID_R5                           0x00000015L
+#define DREG_REGID_R6                           0x00000016L
+#define DREG_REGID_R7                           0x00000017L
+#define DREG_REGID_R8                           0x00000018L
+#define DREG_REGID_R9                           0x00000019L
+#define DREG_REGID_RA                           0x0000001AL
+#define DREG_REGID_RB                           0x0000001BL
+#define DREG_REGID_RC                           0x0000001CL
+#define DREG_REGID_RD                           0x0000001DL
+#define DREG_REGID_RE                           0x0000001EL
+#define DREG_REGID_RF                           0x0000001FL
+#define DREG_REGID_RA_BUS_LOW                   0x00000020L
+#define DREG_REGID_RA_BUS_HIGH                  0x00000038L
+#define DREG_REGID_YBUS_LOW                     0x00000050L
+#define DREG_REGID_YBUS_HIGH                    0x00000058L
+#define DREG_REGID_TRAP_0                       0x00000100L
+#define DREG_REGID_TRAP_1                       0x00000101L
+#define DREG_REGID_TRAP_2                       0x00000102L
+#define DREG_REGID_TRAP_3                       0x00000103L
+#define DREG_REGID_TRAP_4                       0x00000104L
+#define DREG_REGID_TRAP_5                       0x00000105L
+#define DREG_REGID_TRAP_6                       0x00000106L
+#define DREG_REGID_TRAP_7                       0x00000107L
+#define DREG_REGID_INDIRECT_ADDRESS             0x0000010EL
+#define DREG_REGID_TOP_OF_STACK                 0x0000010FL
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_8                       0x00000110L
+#define DREG_REGID_TRAP_9                       0x00000111L
+#define DREG_REGID_TRAP_10                      0x00000112L
+#define DREG_REGID_TRAP_11                      0x00000113L
+#define DREG_REGID_TRAP_12                      0x00000114L
+#define DREG_REGID_TRAP_13                      0x00000115L
+#define DREG_REGID_TRAP_14                      0x00000116L
+#define DREG_REGID_TRAP_15                      0x00000117L
+#define DREG_REGID_TRAP_16                      0x00000118L
+#define DREG_REGID_TRAP_17                      0x00000119L
+#define DREG_REGID_TRAP_18                      0x0000011AL
+#define DREG_REGID_TRAP_19                      0x0000011BL
+#define DREG_REGID_TRAP_20                      0x0000011CL
+#define DREG_REGID_TRAP_21                      0x0000011DL
+#define DREG_REGID_TRAP_22                      0x0000011EL
+#define DREG_REGID_TRAP_23                      0x0000011FL
+#endif
+#endif
+#define DREG_REGID_RSA0_LOW                     0x00000200L
+#define DREG_REGID_RSA0_HIGH                    0x00000201L
+#define DREG_REGID_RSA1_LOW                     0x00000202L
+#define DREG_REGID_RSA1_HIGH                    0x00000203L
+#define DREG_REGID_RSA2                         0x00000204L
+#define DREG_REGID_RSA3                         0x00000205L
+#define DREG_REGID_RSI0_LOW                     0x00000206L
+#define DREG_REGID_RSI0_HIGH                    0x00000207L
+#define DREG_REGID_RSI1                         0x00000208L
+#define DREG_REGID_RSI2                         0x00000209L
+#define DREG_REGID_SAGUSTATUS                   0x0000020AL
+#define DREG_REGID_RSCONFIG01_LOW               0x0000020BL
+#define DREG_REGID_RSCONFIG01_HIGH              0x0000020CL
+#define DREG_REGID_RSCONFIG23_LOW               0x0000020DL
+#define DREG_REGID_RSCONFIG23_HIGH              0x0000020EL
+#define DREG_REGID_RSDMA01E                     0x0000020FL
+#define DREG_REGID_RSDMA23E                     0x00000210L
+#define DREG_REGID_RSD0_LOW                     0x00000211L
+#define DREG_REGID_RSD0_HIGH                    0x00000212L
+#define DREG_REGID_RSD1_LOW                     0x00000213L
+#define DREG_REGID_RSD1_HIGH                    0x00000214L
+#define DREG_REGID_RSD2_LOW                     0x00000215L
+#define DREG_REGID_RSD2_HIGH                    0x00000216L
+#define DREG_REGID_RSD3_LOW                     0x00000217L
+#define DREG_REGID_RSD3_HIGH                    0x00000218L
+#define DREG_REGID_SRAR_HIGH                    0x0000021AL
+#define DREG_REGID_SRAR_LOW                     0x0000021BL
+#define DREG_REGID_DMA_STATE                    0x0000021CL
+#define DREG_REGID_CURRENT_DMA_STREAM           0x0000021DL
+#define DREG_REGID_NEXT_DMA_STREAM              0x0000021EL
+#define DREG_REGID_CPU_STATUS                   0x00000300L
+#define DREG_REGID_MAC_MODE                     0x00000301L
+#define DREG_REGID_STACK_AND_REPEAT             0x00000302L
+#define DREG_REGID_INDEX0                       0x00000304L
+#define DREG_REGID_INDEX1                       0x00000305L
+#define DREG_REGID_DMA_STATE_0_3                0x00000400L
+#define DREG_REGID_DMA_STATE_4_7                0x00000404L
+#define DREG_REGID_DMA_STATE_8_11               0x00000408L
+#define DREG_REGID_DMA_STATE_12_15              0x0000040CL
+#define DREG_REGID_DMA_STATE_16_19              0x00000410L
+#define DREG_REGID_DMA_STATE_20_23              0x00000414L
+#define DREG_REGID_DMA_STATE_24_27              0x00000418L
+#define DREG_REGID_DMA_STATE_28_31              0x0000041CL
+#define DREG_REGID_DMA_STATE_32_35              0x00000420L
+#define DREG_REGID_DMA_STATE_36_39              0x00000424L
+#define DREG_REGID_DMA_STATE_40_43              0x00000428L
+#define DREG_REGID_DMA_STATE_44_47              0x0000042CL
+#define DREG_REGID_DMA_STATE_48_51              0x00000430L
+#define DREG_REGID_DMA_STATE_52_55              0x00000434L
+#define DREG_REGID_DMA_STATE_56_59              0x00000438L
+#define DREG_REGID_DMA_STATE_60_63              0x0000043CL
+#define DREG_REGID_DMA_STATE_64_67              0x00000440L
+#define DREG_REGID_DMA_STATE_68_71              0x00000444L
+#define DREG_REGID_DMA_STATE_72_75              0x00000448L
+#define DREG_REGID_DMA_STATE_76_79              0x0000044CL
+#define DREG_REGID_DMA_STATE_80_83              0x00000450L
+#define DREG_REGID_DMA_STATE_84_87              0x00000454L
+#define DREG_REGID_DMA_STATE_88_91              0x00000458L
+#define DREG_REGID_DMA_STATE_92_95              0x0000045CL
+#define DREG_REGID_TRAP_SELECT                  0x00000500L
+#define DREG_REGID_TRAP_WRITE_0                 0x00000500L
+#define DREG_REGID_TRAP_WRITE_1                 0x00000501L
+#define DREG_REGID_TRAP_WRITE_2                 0x00000502L
+#define DREG_REGID_TRAP_WRITE_3                 0x00000503L
+#define DREG_REGID_TRAP_WRITE_4                 0x00000504L
+#define DREG_REGID_TRAP_WRITE_5                 0x00000505L
+#define DREG_REGID_TRAP_WRITE_6                 0x00000506L
+#define DREG_REGID_TRAP_WRITE_7                 0x00000507L
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_WRITE_8                 0x00000510L
+#define DREG_REGID_TRAP_WRITE_9                 0x00000511L
+#define DREG_REGID_TRAP_WRITE_10                0x00000512L
+#define DREG_REGID_TRAP_WRITE_11                0x00000513L
+#define DREG_REGID_TRAP_WRITE_12                0x00000514L
+#define DREG_REGID_TRAP_WRITE_13                0x00000515L
+#define DREG_REGID_TRAP_WRITE_14                0x00000516L
+#define DREG_REGID_TRAP_WRITE_15                0x00000517L
+#define DREG_REGID_TRAP_WRITE_16                0x00000518L
+#define DREG_REGID_TRAP_WRITE_17                0x00000519L
+#define DREG_REGID_TRAP_WRITE_18                0x0000051AL
+#define DREG_REGID_TRAP_WRITE_19                0x0000051BL
+#define DREG_REGID_TRAP_WRITE_20                0x0000051CL
+#define DREG_REGID_TRAP_WRITE_21                0x0000051DL
+#define DREG_REGID_TRAP_WRITE_22                0x0000051EL
+#define DREG_REGID_TRAP_WRITE_23                0x0000051FL
+#endif
+#endif
+#define DREG_REGID_MAC0_ACC0_LOW                0x00000600L
+#define DREG_REGID_MAC0_ACC1_LOW                0x00000601L
+#define DREG_REGID_MAC0_ACC2_LOW                0x00000602L
+#define DREG_REGID_MAC0_ACC3_LOW                0x00000603L
+#define DREG_REGID_MAC1_ACC0_LOW                0x00000604L
+#define DREG_REGID_MAC1_ACC1_LOW                0x00000605L
+#define DREG_REGID_MAC1_ACC2_LOW                0x00000606L
+#define DREG_REGID_MAC1_ACC3_LOW                0x00000607L
+#define DREG_REGID_MAC0_ACC0_MID                0x00000608L
+#define DREG_REGID_MAC0_ACC1_MID                0x00000609L
+#define DREG_REGID_MAC0_ACC2_MID                0x0000060AL
+#define DREG_REGID_MAC0_ACC3_MID                0x0000060BL
+#define DREG_REGID_MAC1_ACC0_MID                0x0000060CL
+#define DREG_REGID_MAC1_ACC1_MID                0x0000060DL
+#define DREG_REGID_MAC1_ACC2_MID                0x0000060EL
+#define DREG_REGID_MAC1_ACC3_MID                0x0000060FL
+#define DREG_REGID_MAC0_ACC0_HIGH               0x00000610L
+#define DREG_REGID_MAC0_ACC1_HIGH               0x00000611L
+#define DREG_REGID_MAC0_ACC2_HIGH               0x00000612L
+#define DREG_REGID_MAC0_ACC3_HIGH               0x00000613L
+#define DREG_REGID_MAC1_ACC0_HIGH               0x00000614L
+#define DREG_REGID_MAC1_ACC1_HIGH               0x00000615L
+#define DREG_REGID_MAC1_ACC2_HIGH               0x00000616L
+#define DREG_REGID_MAC1_ACC3_HIGH               0x00000617L
+#define DREG_REGID_RSHOUT_LOW                   0x00000620L
+#define DREG_REGID_RSHOUT_MID                   0x00000628L
+#define DREG_REGID_RSHOUT_HIGH                  0x00000630L
+
+//****************************************************************************
+//
+// The following defines are for the flags in the AC97 S/PDIF Control register.
+//
+//****************************************************************************
+#define SPDIF_CONTROL_SPDIF_EN                 0x00008000L
+#define SPDIF_CONTROL_VAL                      0x00004000L
+#define SPDIF_CONTROL_COPY                     0x00000004L
+#define SPDIF_CONTROL_CC0                      0x00000010L
+#define SPDIF_CONTROL_CC1                      0x00000020L
+#define SPDIF_CONTROL_CC2                      0x00000040L
+#define SPDIF_CONTROL_CC3                      0x00000080L
+#define SPDIF_CONTROL_CC4                      0x00000100L
+#define SPDIF_CONTROL_CC5                      0x00000200L
+#define SPDIF_CONTROL_CC6                      0x00000400L
+#define SPDIF_CONTROL_L                        0x00000800L
+
+#endif // _H_HWDEFS
diff -Nru linux/sound/oss/cs4281/cs4281_wrapper-24.c linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_wrapper-24.c
--- linux/sound/oss/cs4281/cs4281_wrapper-24.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_wrapper-24.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,42 @@
+/*******************************************************************************
+*
+*      "cs4281_wrapper.c" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 12/20/00 trw - new file. 
+*
+*******************************************************************************/
+
+#include <linux/spinlock.h>
+
+void cs4281_null(struct pci_dev *pcidev) { return; }
+#define cs4x_mem_map_reserve(page) mem_map_reserve(page)
+#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page)
+
+#define free_dmabuf(state, dmabuf) \
+	pci_free_consistent(state->pcidev, \
+			    PAGE_SIZE << (dmabuf)->buforder, \
+			    (dmabuf)->rawbuf, (dmabuf)->dmaaddr);
+#define free_dmabuf2(state, dmabuf) \
+	pci_free_consistent((state)->pcidev, \
+				    PAGE_SIZE << (state)->buforder_tmpbuff, \
+				    (state)->tmpbuff, (state)->dmaaddr_tmpbuff);
+#define cs4x_pgoff(vma) ((vma)->vm_pgoff)
+
diff -Nru linux/sound/oss/cs4281/cs4281m.c linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281m.c
--- linux/sound/oss/cs4281/cs4281m.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281m.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,4528 @@
+/*******************************************************************************
+*
+*      "cs4281.c" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- adapted from drivers by Thomas Sailer, 
+*            -- but don't bug him; Problems should go to:
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Module command line parameters:
+*   none
+*
+*  Supported devices:
+*  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+*  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+*  /dev/midi   simple MIDI UART interface, no ioctl
+*
+* Modification History
+* 08/20/00 trw - silence and no stopping DAC until release
+* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop.
+* 09/18/00 trw - added 16bit only record with conversion 
+* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous 
+*                capture/playback rates)
+* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin  
+*                libOSSm.so)
+* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal)
+* 11/03/00 trw - fixed interrupt loss/stutter, added debug.
+* 11/10/00 bkz - added __devinit to cs4281_hw_init()
+* 11/10/00 trw - fixed SMP and capture spinlock hang.
+* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm.
+* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix.
+* 12/08/00 trw - added PM support. 
+* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 
+*		 (RH/Dell base), 2.2.18, 2.2.12.  cleaned up code mods by ident.
+* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup.
+* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use 
+*		 defaultorder-100 as power of 2 for the buffer size. example:
+*		 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
+*
+*******************************************************************************/
+
+/* uncomment the following line to disable building PM support into the driver */
+//#define NOT_CS4281_PM 1 
+
+#include <linux/list.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+//#include "cs_dm.h"
+#include "cs4281_hwdefs.h"
+#include "cs4281pm.h"
+
+struct cs4281_state;
+EXPORT_NO_SYMBOLS;
+
+static void stop_dac(struct cs4281_state *s);
+static void stop_adc(struct cs4281_state *s);
+static void start_dac(struct cs4281_state *s);
+static void start_adc(struct cs4281_state *s);
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+// --------------------------------------------------------------------- 
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS          0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281
+#define PCI_DEVICE_ID_CRYSTAL_CS4281  0x6005
+#endif
+
+#define CS4281_MAGIC  ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS)
+#define	CS4281_CFLR_DEFAULT	0x00000001  /* CFLR must be in AC97 link mode */
+
+// buffer order determines the size of the dma buffer for the driver.
+// under Linux, a smaller buffer allows more responsiveness from many of the 
+// applications (e.g. games).  A larger buffer allows some of the apps (esound) 
+// to not underrun the dma buffer as easily.  As default, use 32k (order=3)
+// rather than 64k as some of the games work more responsively.
+// log base 2( buff sz = 32k).
+static unsigned long defaultorder = 3;
+MODULE_PARM(defaultorder, "i");
+
+//
+// Turn on/off debugging compilation by commenting out "#define CSDEBUG"
+//
+#define CSDEBUG 1
+#if CSDEBUG
+#define CSDEBUG_INTERFACE 1
+#else
+#undef CSDEBUG_INTERFACE
+#endif
+//
+// cs_debugmask areas
+//
+#define CS_INIT	 	0x00000001	// initialization and probe functions
+#define CS_ERROR 	0x00000002	// tmp debugging bit placeholder
+#define CS_INTERRUPT	0x00000004	// interrupt handler (separate from all other)
+#define CS_FUNCTION 	0x00000008	// enter/leave functions
+#define CS_WAVE_WRITE 	0x00000010	// write information for wave
+#define CS_WAVE_READ 	0x00000020	// read information for wave
+#define CS_MIDI_WRITE 	0x00000040	// write information for midi
+#define CS_MIDI_READ 	0x00000080	// read information for midi
+#define CS_MPU401_WRITE 0x00000100	// write information for mpu401
+#define CS_MPU401_READ 	0x00000200	// read information for mpu401
+#define CS_OPEN		0x00000400	// all open functions in the driver
+#define CS_RELEASE	0x00000800	// all release functions in the driver
+#define CS_PARMS	0x00001000	// functional and operational parameters
+#define CS_IOCTL	0x00002000	// ioctl (non-mixer)
+#define CS_PM		0x00004000	// power management 
+#define CS_TMP		0x10000000	// tmp debug mask bit
+
+#define CS_IOCTL_CMD_SUSPEND	0x1	// suspend
+#define CS_IOCTL_CMD_RESUME	0x2	// resume
+//
+// CSDEBUG is usual mode is set to 1, then use the
+// cs_debuglevel and cs_debugmask to turn on or off debugging.
+// Debug level of 1 has been defined to be kernel errors and info
+// that should be printed on any released driver.
+//
+#if CSDEBUG
+#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;}
+#else
+#define CS_DBGOUT(mask,level,x)
+#endif
+
+#if CSDEBUG
+static unsigned long cs_debuglevel = 1;	// levels range from 1-9
+static unsigned long cs_debugmask = CS_INIT | CS_ERROR;	// use CS_DBGOUT with various mask values
+MODULE_PARM(cs_debuglevel, "i");
+MODULE_PARM(cs_debugmask, "i");
+#endif
+#define CS_TRUE 	1
+#define CS_FALSE 	0
+
+// MIDI buffer sizes 
+#define MIDIINBUF  500
+#define MIDIOUTBUF 500
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define CS4281_MAJOR_VERSION 	1
+#define CS4281_MINOR_VERSION 	13
+#ifdef __ia64__
+#define CS4281_ARCH	     	64	//architecture key
+#else
+#define CS4281_ARCH	     	32	//architecture key
+#endif
+
+#define CS_TYPE_ADC 0
+#define CS_TYPE_DAC 1
+
+
+static const char invalid_magic[] =
+    KERN_CRIT "cs4281: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+        if (!(s) || (s)->magic != CS4281_MAGIC) { \
+                printk(invalid_magic);            \
+                return -ENXIO;                    \
+        }                                         \
+})
+
+//LIST_HEAD(cs4281_devs);
+struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs };
+
+struct cs4281_state; 
+
+#include "cs4281_wrapper-24.c"
+
+struct cs4281_state {
+	// magic 
+	unsigned int magic;
+
+	// we keep the cards in a linked list 
+	struct cs4281_state *next;
+
+	// pcidev is needed to turn off the DDMA controller at driver shutdown 
+	struct pci_dev *pcidev;
+	struct list_head list;
+
+	// soundcore stuff 
+	int dev_audio;
+	int dev_mixer;
+	int dev_midi;
+
+	// hardware resources 
+	unsigned int pBA0phys, pBA1phys;
+	char *pBA0, *pBA1;
+	unsigned int irq;
+
+	// mixer registers 
+	struct {
+		unsigned short vol[10];
+		unsigned int recsrc;
+		unsigned int modcnt;
+		unsigned short micpreamp;
+	} mix;
+
+	// wave stuff   
+	struct properties {
+		unsigned fmt;
+		unsigned fmt_original;	// original requested format
+		unsigned channels;
+		unsigned rate;
+		unsigned char clkdiv;
+	} prop_dac, prop_adc;
+	unsigned conversion:1;	// conversion from 16 to 8 bit in progress
+	void *tmpbuff;		// tmp buffer for sample conversions
+	unsigned ena;
+	spinlock_t lock;
+	struct semaphore open_sem;
+	struct semaphore open_sem_adc;
+	struct semaphore open_sem_dac;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+	wait_queue_head_t open_wait_adc;
+	wait_queue_head_t open_wait_dac;
+
+	dma_addr_t dmaaddr_tmpbuff;
+	unsigned buforder_tmpbuff;	// Log base 2 of 'rawbuf' size in bytes..
+	struct dmabuf {
+		void *rawbuf;	// Physical address of  
+		dma_addr_t dmaaddr;
+		unsigned buforder;	// Log base 2 of 'rawbuf' size in bytes..
+		unsigned numfrag;	// # of 'fragments' in the buffer.
+		unsigned fragshift;	// Log base 2 of fragment size.
+		unsigned hwptr, swptr;
+		unsigned total_bytes;	// # bytes process since open.
+		unsigned blocks;	// last returned blocks value GETOPTR
+		unsigned wakeup;	// interrupt occurred on block 
+		int count;
+		unsigned underrun;	// underrun flag
+		unsigned error;	// over/underrun 
+		wait_queue_head_t wait;
+		// redundant, but makes calculations easier 
+		unsigned fragsize;	// 2**fragshift..
+		unsigned dmasize;	// 2**buforder.
+		unsigned fragsamples;
+		// OSS stuff 
+		unsigned mapped:1;	// Buffer mapped in cs4281_mmap()?
+		unsigned ready:1;	// prog_dmabuf_dac()/adc() successful?
+		unsigned endcleared:1;
+		unsigned type:1;	// adc or dac buffer (CS_TYPE_XXX)
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac, dma_adc;
+
+	// midi stuff 
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		struct timer_list timer;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+
+	struct cs4281_pm pm;
+	struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES];
+};
+
+#include "cs4281pm-24.c"
+
+#if CSDEBUG
+
+// DEBUG ROUTINES
+
+#define SOUND_MIXER_CS_GETDBGLEVEL 	_SIOWR('M',120, int)
+#define SOUND_MIXER_CS_SETDBGLEVEL 	_SIOWR('M',121, int)
+#define SOUND_MIXER_CS_GETDBGMASK 	_SIOWR('M',122, int)
+#define SOUND_MIXER_CS_SETDBGMASK 	_SIOWR('M',123, int)
+
+#define SOUND_MIXER_CS_APM	 	_SIOWR('M',124, int)
+
+
+static void cs_printioctl(unsigned int x)
+{
+	unsigned int i;
+	unsigned char vidx;
+	// Index of mixtable1[] member is Device ID 
+	// and must be <= SOUND_MIXER_NRDEVICES.
+	// Value of array member is index into s->mix.vol[]
+	static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
+		[SOUND_MIXER_PCM] = 1,	// voice 
+		[SOUND_MIXER_LINE1] = 2,	// AUX
+		[SOUND_MIXER_CD] = 3,	// CD 
+		[SOUND_MIXER_LINE] = 4,	// Line 
+		[SOUND_MIXER_SYNTH] = 5,	// FM
+		[SOUND_MIXER_MIC] = 6,	// Mic 
+		[SOUND_MIXER_SPEAKER] = 7,	// Speaker 
+		[SOUND_MIXER_RECLEV] = 8,	// Recording level 
+		[SOUND_MIXER_VOLUME] = 9	// Master Volume 
+	};
+
+	switch (x) {
+	case SOUND_MIXER_CS_GETDBGMASK:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_GETDBGMASK:\n"));
+		break;
+	case SOUND_MIXER_CS_GETDBGLEVEL:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_GETDBGLEVEL:\n"));
+		break;
+	case SOUND_MIXER_CS_SETDBGMASK:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_SETDBGMASK:\n"));
+		break;
+	case SOUND_MIXER_CS_SETDBGLEVEL:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_MIXER_CS_SETDBGLEVEL:\n"));
+		break;
+	case OSS_GETVERSION:
+		CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n"));
+		break;
+	case SNDCTL_DSP_SYNC:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n"));
+		break;
+	case SNDCTL_DSP_SETDUPLEX:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n"));
+		break;
+	case SNDCTL_DSP_GETCAPS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n"));
+		break;
+	case SNDCTL_DSP_RESET:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n"));
+		break;
+	case SNDCTL_DSP_SPEED:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n"));
+		break;
+	case SNDCTL_DSP_STEREO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n"));
+		break;
+	case SNDCTL_DSP_CHANNELS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n"));
+		break;
+	case SNDCTL_DSP_GETFMTS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n"));
+		break;
+	case SNDCTL_DSP_SETFMT:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n"));
+		break;
+	case SNDCTL_DSP_POST:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n"));
+		break;
+	case SNDCTL_DSP_GETTRIGGER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n"));
+		break;
+	case SNDCTL_DSP_SETTRIGGER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n"));
+		break;
+	case SNDCTL_DSP_GETOSPACE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n"));
+		break;
+	case SNDCTL_DSP_GETISPACE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n"));
+		break;
+	case SNDCTL_DSP_NONBLOCK:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n"));
+		break;
+	case SNDCTL_DSP_GETODELAY:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n"));
+		break;
+	case SNDCTL_DSP_GETIPTR:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n"));
+		break;
+	case SNDCTL_DSP_GETOPTR:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n"));
+		break;
+	case SNDCTL_DSP_GETBLKSIZE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n"));
+		break;
+	case SNDCTL_DSP_SETFRAGMENT:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SNDCTL_DSP_SETFRAGMENT:\n"));
+		break;
+	case SNDCTL_DSP_SUBDIVIDE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n"));
+		break;
+	case SOUND_PCM_READ_RATE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n"));
+		break;
+	case SOUND_PCM_READ_CHANNELS:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_PCM_READ_CHANNELS:\n"));
+		break;
+	case SOUND_PCM_READ_BITS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n"));
+		break;
+	case SOUND_PCM_WRITE_FILTER:
+		CS_DBGOUT(CS_IOCTL, 4,
+			  printk("SOUND_PCM_WRITE_FILTER:\n"));
+		break;
+	case SNDCTL_DSP_SETSYNCRO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n"));
+		break;
+	case SOUND_PCM_READ_FILTER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE1:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE2:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE3:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE4:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n"));
+		break;
+	case SOUND_MIXER_PRIVATE5:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n"));
+		break;
+	case SOUND_MIXER_INFO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n"));
+		break;
+	case SOUND_OLD_MIXER_INFO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n"));
+		break;
+
+	default:
+		switch (_IOC_NR(x)) {
+		case SOUND_MIXER_VOLUME:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_VOLUME:\n"));
+			break;
+		case SOUND_MIXER_SPEAKER:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_SPEAKER:\n"));
+			break;
+		case SOUND_MIXER_RECLEV:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_RECLEV:\n"));
+			break;
+		case SOUND_MIXER_MIC:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_MIC:\n"));
+			break;
+		case SOUND_MIXER_SYNTH:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_SYNTH:\n"));
+			break;
+		case SOUND_MIXER_RECSRC:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_RECSRC:\n"));
+			break;
+		case SOUND_MIXER_DEVMASK:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_DEVMASK:\n"));
+			break;
+		case SOUND_MIXER_RECMASK:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_RECMASK:\n"));
+			break;
+		case SOUND_MIXER_STEREODEVS:
+			CS_DBGOUT(CS_IOCTL, 4,
+				  printk("SOUND_MIXER_STEREODEVS:\n"));
+			break;
+		case SOUND_MIXER_CAPS:
+			CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n"));
+			break;
+		default:
+			i = _IOC_NR(x);
+			if (i >= SOUND_MIXER_NRDEVICES
+			    || !(vidx = mixtable1[i])) {
+				CS_DBGOUT(CS_IOCTL, 4, printk
+					("UNKNOWN IOCTL: 0x%.8x NR=%d\n",
+						x, i));
+			} else {
+				CS_DBGOUT(CS_IOCTL, 4, printk
+					("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n",
+						x, i));
+			}
+			break;
+		}
+	}
+}
+#endif
+static int prog_dmabuf_adc(struct cs4281_state *s);
+static void prog_codec(struct cs4281_state *s, unsigned type);
+
+// --------------------------------------------------------------------- 
+//
+//              Hardware Interfaces For the CS4281
+//
+
+
+//******************************************************************************
+// "delayus()-- Delay for the specified # of microseconds.
+//******************************************************************************
+static void delayus(struct cs4281_state *s, u32 delay)
+{
+	u32 j;
+	if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) {
+		j = (delay * HZ) / 1000000;	/* calculate delay in jiffies  */
+		if (j < 1)
+			j = 1;	/* minimum one jiffy. */
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(j);
+	} else
+		udelay(delay);
+	return;
+}
+
+
+//******************************************************************************
+// "cs4281_read_ac97" -- Reads a word from the specified location in the
+//               CS4281's address space(based on the BA0 register).
+//
+// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register,
+//                                            0h for reads.
+// 3. Write ACCTL = Control Register = 460h for initiating the write
+// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+// 5. if DCV not cleared, break and return error
+// 6. Read ACSTS = Status Register = 464h, check VSTS bit
+//****************************************************************************
+static int cs4281_read_ac97(struct cs4281_state *card, u32 offset,
+			    u32 * value)
+{
+	u32 count, status;
+
+	// Make sure that there is not data sitting
+	// around from a previous uncompleted access.
+	// ACSDA = Status Data Register = 47Ch
+	status = readl(card->pBA0 + BA0_ACSDA);
+
+	// Setup the AC97 control registers on the CS4281 to send the
+	// appropriate command to the AC97 to perform the read.
+	// ACCAD = Command Address Register = 46Ch
+	// ACCDA = Command Data Register = 470h
+	// ACCTL = Control Register = 460h
+	// bit DCV - will clear when process completed
+	// bit CRW - Read command
+	// bit VFRM - valid frame enabled
+	// bit ESYN - ASYNC generation enabled
+
+	// Get the actual AC97 register from the offset
+	writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD);
+	writel(0, card->pBA0 + BA0_ACCDA);
+	writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN,
+	       card->pBA0 + BA0_ACCTL);
+
+	// Wait for the read to occur.
+	for (count = 0; count < 10; count++) {
+		// First, we want to wait for a short time.
+		udelay(25);
+
+		// Now, check to see if the read has completed.
+		// ACCTL = 460h, DCV should be reset by now and 460h = 17h
+		if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV))
+			break;
+	}
+
+	// Make sure the read completed.
+	if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)
+		return 1;
+
+	// Wait for the valid status bit to go active.
+	for (count = 0; count < 10; count++) {
+		// Read the AC97 status register.
+		// ACSTS = Status Register = 464h
+		status = readl(card->pBA0 + BA0_ACSTS);
+
+		// See if we have valid status.
+		// VSTS - Valid Status
+		if (status & ACSTS_VSTS)
+			break;
+		// Wait for a short while.
+		udelay(25);
+	}
+
+	// Make sure we got valid status.
+	if (!(status & ACSTS_VSTS))
+		return 1;
+
+	// Read the data returned from the AC97 register.
+	// ACSDA = Status Data Register = 474h
+	*value = readl(card->pBA0 + BA0_ACSDA);
+
+	// Success.
+	return (0);
+}
+
+
+//****************************************************************************
+//
+// "cs4281_write_ac97()"-- writes a word to the specified location in the
+// CS461x's address space (based on the part's base address zero register).
+//
+// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg.
+// 3. Write ACCTL = Control Register = 460h for initiating the write
+// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+// 5. if DCV not cleared, break and return error
+//
+//****************************************************************************
+static int cs4281_write_ac97(struct cs4281_state *card, u32 offset,
+			     u32 value)
+{
+	u32 count, status=0;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n"));
+
+	// Setup the AC97 control registers on the CS4281 to send the
+	// appropriate command to the AC97 to perform the read.
+	// ACCAD = Command Address Register = 46Ch
+	// ACCDA = Command Data Register = 470h
+	// ACCTL = Control Register = 460h
+	// set DCV - will clear when process completed
+	// reset CRW - Write command
+	// set VFRM - valid frame enabled
+	// set ESYN - ASYNC generation enabled
+	// set RSTN - ARST# inactive, AC97 codec not reset
+
+	// Get the actual AC97 register from the offset
+
+	writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD);
+	writel(value, card->pBA0 + BA0_ACCDA);
+	writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN,
+	       card->pBA0 + BA0_ACCTL);
+
+	// Wait for the write to occur.
+	for (count = 0; count < 100; count++) {
+		// First, we want to wait for a short time.
+		udelay(25);
+		// Now, check to see if the write has completed.
+		// ACCTL = 460h, DCV should be reset by now and 460h = 07h
+		status = readl(card->pBA0 + BA0_ACCTL);
+		if (!(status & ACCTL_DCV))
+			break;
+	}
+
+	// Make sure the write completed.
+	if (status & ACCTL_DCV) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
+	      		"cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n"));
+		return 1;
+	}
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n"));
+	// Success.
+	return 0;
+}
+
+
+//******************************************************************************
+// "Init4281()" -- Bring up the part.
+//******************************************************************************
+static __devinit int cs4281_hw_init(struct cs4281_state *card)
+{
+	u32 ac97_slotid;
+	u32 temp1, temp2;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n"));
+#ifndef NOT_CS4281_PM
+	if(!card)
+		return 1;
+#endif
+	temp2 = readl(card->pBA0 + BA0_CFLR);
+	CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO 
+		"cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2));
+	if(temp2 != CS4281_CFLR_DEFAULT)
+	{
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO 
+			"cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n",
+				temp2,CS4281_CFLR_DEFAULT));
+		writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR);
+		temp2 = readl(card->pBA0 + BA0_CFLR);
+		if(temp2 != CS4281_CFLR_DEFAULT)
+		{
+			CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO 
+				"cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n"));
+			return 1;
+		}
+	}
+
+	//***************************************7
+	//  Set up the Sound System Configuration
+	//***************************************
+
+	// Set the 'Configuration Write Protect' register
+	// to 4281h.  Allows vendor-defined configuration
+	// space between 0e4h and 0ffh to be written.
+
+	writel(0x4281, card->pBA0 + BA0_CWPR);	// (3e0h)
+
+	// (0), Blast the clock control register to zero so that the
+	// PLL starts out in a known state, and blast the master serial
+	// port control register to zero so that the serial ports also
+	// start out in a known state.
+
+	writel(0, card->pBA0 + BA0_CLKCR1);	// (400h)
+	writel(0, card->pBA0 + BA0_SERMC);	// (420h)
+
+
+	// (1), Make ESYN go to zero to turn off
+	// the Sync pulse on the AC97 link.
+
+	writel(0, card->pBA0 + BA0_ACCTL);
+	udelay(50);
+
+
+	// (2) Drive the ARST# pin low for a minimum of 1uS (as defined in
+	// the AC97 spec) and then drive it high.  This is done for non
+	// AC97 modes since there might be logic external to the CS461x
+	// that uses the ARST# line for a reset.
+
+	writel(0, card->pBA0 + BA0_SPMC);	// (3ech)
+	udelay(100);
+	writel(SPMC_RSTN, card->pBA0 + BA0_SPMC);
+	delayus(card,50000);		// Wait 50 ms for ABITCLK to become stable.
+
+	// (3) Turn on the Sound System Clocks.
+	writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1);	// (400h)
+	delayus(card,50000);		// Wait for the PLL to stabilize.
+	// Turn on clocking of the core (CLKCR1(400h) = 0x00000030)
+	writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1);
+
+	// (4) Power on everything for now..
+	writel(0x7E, card->pBA0 + BA0_SSPM);	// (740h)
+
+	// (5) Wait for clock stabilization.
+	for (temp1 = 0; temp1 < 1000; temp1++) {
+		udelay(1000);
+		if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)
+			break;
+	}
+	if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR 
+			"cs4281: DLLRDY failed!\n"));
+		return -EIO;
+	}
+	// (6) Enable ASYNC generation.
+	writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL);	// (460h)
+
+	// Now wait 'for a short while' to allow the  AC97
+	// part to start generating bit clock. (so we don't
+	// Try to start the PLL without an input clock.)
+	delayus(card,50000);
+
+	// Set the serial port timing configuration, so that the
+	// clock control circuit gets its clock from the right place.
+	writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC);	// (420h)=2.
+
+	// (7) Wait for the codec ready signal from the AC97 codec.
+
+	for (temp1 = 0; temp1 < 1000; temp1++) {
+		// Delay a mil to let things settle out and
+		// to prevent retrying the read too quickly.
+		udelay(1000);
+		if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)	// If ready,  (464h)
+			break;	//   exit the 'for' loop.
+	}
+	if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY))	// If never came ready,
+	{
+		CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR
+			 "cs4281: ACSTS never came ready!\n"));
+		return -EIO;	//   exit initialization.
+	}
+	// (8) Assert the 'valid frame' signal so we can
+	// begin sending commands to the AC97 codec.
+	writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL);	// (460h)
+
+	// (9), Wait until CODEC calibration is finished.
+	// Print an error message if it doesn't.
+	for (temp1 = 0; temp1 < 1000; temp1++) {
+		delayus(card,10000);
+		// Read the AC97 Powerdown Control/Status Register.
+		cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2);
+		if ((temp2 & 0x0000000F) == 0x0000000F)
+			break;
+	}
+	if ((temp2 & 0x0000000F) != 0x0000000F) {
+		CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR
+			"cs4281: Codec failed to calibrate.  Status = %.8x.\n",
+				temp2));
+		return -EIO;
+	}
+	// (10), Set the serial port timing configuration, so that the
+	// clock control circuit gets its clock from the right place.
+	writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC);	// (420h)=2.
+
+
+	// (11) Wait until we've sampled input slots 3 & 4 as valid, meaning
+	// that the codec is pumping ADC data across the AC link.
+	for (temp1 = 0; temp1 < 1000; temp1++) {
+		// Delay a mil to let things settle out and
+		// to prevent retrying the read too quickly.
+		delayus(card,1000);	//(test)
+
+		// Read the input slot valid register;  See
+		// if input slots 3 and 4 are valid yet.
+		if (
+		    (readl(card->pBA0 + BA0_ACISV) &
+		     (ACISV_ISV3 | ACISV_ISV4)) ==
+		    (ACISV_ISV3 | ACISV_ISV4)) break;	// Exit the 'for' if slots are valid.
+	}
+	// If we never got valid data, exit initialization.
+	if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4))
+	    != (ACISV_ISV3 | ACISV_ISV4)) {
+		CS_DBGOUT(CS_FUNCTION, 2,
+			  printk(KERN_ERR
+				 "cs4281: Never got valid data!\n"));
+		return -EIO;	// If no valid data, exit initialization.
+	}
+	// (12), Start digital data transfer of audio data to the codec.
+	writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV);	// (468h)
+
+
+	//**************************************
+	// Unmute the Master and Alternate
+	// (headphone) volumes.  Set to max.
+	//**************************************
+	cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0);
+	cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0);
+
+	//******************************************
+	// Power on the DAC(AddDACUser()from main())
+	//******************************************
+	cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1);
+	cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff);
+
+	// Wait until we sample a DAC ready state.
+	for (temp2 = 0; temp2 < 32; temp2++) {
+		// Let's wait a mil to let things settle.
+		delayus(card,1000);
+		// Read the current state of the power control reg.
+		cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1);
+		// If the DAC ready state bit is set, stop waiting.
+		if (temp1 & 0x2)
+			break;
+	}
+
+	//******************************************
+	// Power on the ADC(AddADCUser()from main())
+	//******************************************
+	cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1);
+	cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff);
+
+	// Wait until we sample ADC ready state.
+	for (temp2 = 0; temp2 < 32; temp2++) {
+		// Let's wait a mil to let things settle.
+		delayus(card,1000);
+		// Read the current state of the power control reg.
+		cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1);
+		// If the ADC ready state bit is set, stop waiting.
+		if (temp1 & 0x1)
+			break;
+	}
+	// Set up 4281 Register contents that
+	// don't change for boot duration.
+
+	// For playback, we map AC97 slot 3 and 4(Left
+	// & Right PCM playback) to DMA Channel 0.
+	// Set the fifo to be 15 bytes at offset zero.
+
+	ac97_slotid = 0x01000f00;	// FCR0.RS[4:0]=1(=>slot4, right PCM playback).
+	// FCR0.LS[4:0]=0(=>slot3, left PCM playback).
+	// FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0.
+	writel(ac97_slotid, card->pBA0 + BA0_FCR0);	// (180h)
+	writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0);	// Turn on FIFO Enable.
+
+	// For capture, we map AC97 slot 10 and 11(Left
+	// and Right PCM Record) to DMA Channel 1.
+	// Set the fifo to be 15 bytes at offset sixteen.
+	ac97_slotid = 0x0B0A0f10;	// FCR1.RS[4:0]=11(=>slot11, right PCM record).
+	// FCR1.LS[4:0]=10(=>slot10, left PCM record).
+	// FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16.
+	writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1);	// (184h)
+	writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1);	// Turn on FIFO Enable.
+
+	// Map the Playback SRC to the same AC97 slots(3 & 4--
+	// --Playback left & right)as DMA channel 0.
+	// Map the record SRC to the same AC97 slots(10 & 11--
+	// -- Record left & right) as DMA channel 1.
+
+	ac97_slotid = 0x0b0a0100;	// SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback).
+	// SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback).
+	// SCRSA.CRSS[4:0]=11(=>slot11, right PCM record)
+	// SCRSA.CLSS[4:0]=10(=>slot10, left PCM record).
+	writel(ac97_slotid, card->pBA0 + BA0_SRCSA);	// (75ch)
+
+	// Set 'Half Terminal Count Interrupt Enable' and 'Terminal
+	// Count Interrupt Enable' in DMA Control Registers 0 & 1.
+	// Set 'MSK' flag to 1 to keep the DMA engines paused.
+	temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK);	// (00030001h)
+	writel(temp1, card->pBA0 + BA0_DCR0);	// (154h
+	writel(temp1, card->pBA0 + BA0_DCR1);	// (15ch)
+
+	// Set 'Auto-Initialize Control' to 'enabled'; For playback,
+	// set 'Transfer Type Control'(TR[1:0]) to 'read transfer',
+	// for record, set Transfer Type Control to 'write transfer'.
+	// All other bits set to zero;  Some will be changed @ transfer start.
+	temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ);	// (20000018h)
+	writel(temp1, card->pBA0 + BA0_DMR0);	// (150h)
+	temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE);	// (20000014h)
+	writel(temp1, card->pBA0 + BA0_DMR1);	// (158h)
+
+	// Enable DMA interrupts generally, and
+	// DMA0 & DMA1 interrupts specifically.
+	temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff;
+	writel(temp1, card->pBA0 + BA0_HIMR);
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n"));
+	return 0;
+}
+
+#ifndef NOT_CS4281_PM
+static void printpm(struct cs4281_state *s)
+{
+	CS_DBGOUT(CS_PM, 9, printk("pm struct:\n"));
+	CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n",
+		(unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue));
+	CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n",
+		s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n",
+		s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n",
+		s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n",
+		s->pm.u32SSCR,s->pm.u32SRCSA));
+	CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n",
+		s->pm.u32DacASR,s->pm.u32AdcASR));
+	CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n",
+		s->pm.u32DacSR,s->pm.u32AdcSR));
+	CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n",
+		s->pm.u32MIDCR_Save));
+
+}
+static void printpipe(struct cs4281_pipeline *pl)
+{
+
+	CS_DBGOUT(CS_PM, 9, printk("pm struct:\n"));
+	CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n",
+		(unsigned)pl->flags,pl->number));
+	CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n",
+		pl->u32DBAnValue,pl->u32DBCnValue));
+	CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n",
+		pl->u32DMRnValue,pl->u32DCRnValue));
+	CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n",
+		pl->u32DBAnAddress,pl->u32DBCnAddress));
+	CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n",
+		pl->u32DCCnAddress,pl->u32DCCnAddress));
+	CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n",
+		pl->u32DMRnAddress,pl->u32DCRnAddress));
+	CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n",
+		pl->u32HDSRnAddress,pl->u32DBAn_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n",
+		pl->u32DBCn_Save,pl->u32DMRn_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n",
+		pl->u32DCRn_Save,pl->u32DCCn_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n",
+		pl->u32DCAn_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n",
+		pl->u32FCRn_Save,pl->u32FSICn_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n",
+		pl->u32FCRnValue,pl->u32FSICnValue));
+	CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n",
+		pl->u32FCRnAddress,pl->u32FSICnAddress));
+	CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n",
+		pl->u32FPDRnValue,pl->u32FPDRnAddress));
+}
+static void printpipelines(struct cs4281_state *s)
+{
+	int i;
+	for(i=0;i<CS4281_NUMBER_OF_PIPELINES;i++)
+	{
+		if(s->pl[i].flags & CS4281_PIPELINE_VALID)
+		{
+			printpipe(&s->pl[i]);
+		}
+	}
+}
+/****************************************************************************
+*
+*  Suspend - save the ac97 regs, mute the outputs and power down the part.  
+*
+****************************************************************************/
+void cs4281_ac97_suspend(struct cs4281_state *s)
+{
+	int Count,i;
+
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n"));
+/*
+* change the state, save the current hwptr, then stop the dac/adc
+*/
+	s->pm.flags &= ~CS4281_PM_IDLE;
+	s->pm.flags |= CS4281_PM_SUSPENDING;
+	s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0);
+	s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1);
+	stop_dac(s);
+	stop_adc(s);
+
+	for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE)
+			&& (i < CS4281_AC97_NUMBER_RESTORE_REGS); 
+		Count += 2, i++)
+	{
+		cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]);
+	}
+/*
+* Save the ac97 volume registers as well as the current powerdown state.
+* Now, mute the all the outputs (master, headphone, and mono), as well
+* as the PCM volume, in preparation for powering down the entire part.
+*/ 
+	cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume);
+	cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume);
+	cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono);
+	cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume);
+		
+	cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000);
+	cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000);
+	cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
+	cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000);
+
+	cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown);
+	cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose);
+
+/*
+* And power down everything on the AC97 codec.
+*/
+	cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00);
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n"));
+}
+
+/****************************************************************************
+*
+*  Resume - power up the part and restore its registers..  
+*
+****************************************************************************/
+void cs4281_ac97_resume(struct cs4281_state *s)
+{
+	int Count,i;
+
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n"));
+
+/* do not save the power state registers at this time
+    //
+    // If we saved away the power control registers, write them into the
+    // shadows so those saved values get restored instead of the current
+    // shadowed value.
+    //
+    if( bPowerStateSaved )
+    {
+        PokeShadow( 0x26, ulSaveReg0x26 );
+        bPowerStateSaved = FALSE;
+    }
+*/
+
+//
+// First, we restore the state of the general purpose register.  This
+// contains the mic select (mic1 or mic2) and if we restore this after
+// we restore the mic volume/boost state and mic2 was selected at
+// suspend time, we will end up with a brief period of time where mic1
+// is selected with the volume/boost settings for mic2, causing
+// acoustic feedback.  So we restore the general purpose register
+// first, thereby getting the correct mic selected before we restore
+// the mic volume/boost.
+//
+	cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose);
+
+//
+// Now, while the outputs are still muted, restore the state of power
+// on the AC97 part.
+//
+	cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown);
+
+/*
+* Restore just the first set of registers, from register number
+* 0x02 to the register number that ulHighestRegToRestore specifies.
+*/
+	for(	Count = 0x2, i=0; 
+		(Count <= CS4281_AC97_HIGHESTREGTORESTORE)
+			&& (i < CS4281_AC97_NUMBER_RESTORE_REGS); 
+		Count += 2, i++)
+	{
+		cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]);
+	}
+	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n"));
+}
+
+/* do not save the power state registers at this time
+****************************************************************************
+*
+*  SavePowerState - Save the power registers away. 
+*
+****************************************************************************
+void 
+HWAC97codec::SavePowerState(void)
+{
+    ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n");
+
+    ulSaveReg0x26 = PeekShadow(0x26);
+
+    //
+    // Note that we have saved registers that need to be restored during a
+    // resume instead of ulAC97Regs[].
+    //
+    bPowerStateSaved = TRUE;
+
+} // SavePowerState
+*/
+
+void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl)
+{
+ /*
+ * We need to save the contents of the BASIC FIFO Registers.
+ */
+	pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress);
+	pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress);
+}
+void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl)
+{
+ /*
+ * We need to restore the contents of the BASIC FIFO Registers.
+ */
+	writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress);
+	writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress);
+}
+void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl)
+{
+	//
+	// We need to save the contents of the BASIC DMA Registers.
+	//
+	pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress);
+	pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress);
+	pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress);
+	pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress);
+	pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress);
+	pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress);
+}
+void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl)
+{
+	//
+	// We need to save the contents of the BASIC DMA Registers.
+	//
+	writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress);
+	writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress);
+	writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress);
+	writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress);
+	writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress);
+	writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress);
+}
+
+int cs4281_suspend(struct cs4281_state *s)
+{
+	int i;
+	u32 u32CLKCR1;
+	struct cs4281_pm *pm = &s->pm;
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 9, 
+		printk("cs4281: cs4281_suspend()+ flags=%d\n",
+			(unsigned)s->pm.flags));
+/*
+* check the current state, only suspend if IDLE
+*/
+	if(!(s->pm.flags & CS4281_PM_IDLE))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 2, 
+			printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n"));
+		return 1;
+	}
+	s->pm.flags &= ~CS4281_PM_IDLE;
+	s->pm.flags |= CS4281_PM_SUSPENDING;
+
+//
+// Gershwin CLKRUN - Set CKRA
+//
+	u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1);
+
+	pm->u32CLKCR1_SAVE = u32CLKCR1;
+	if(!(u32CLKCR1 & 0x00010000 ) )
+		writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1);
+
+//
+// First, turn on the clocks (yikes) to the devices, so that they will
+// respond when we try to save their state.
+//
+	if(!(u32CLKCR1 & CLKCR1_SWCE))
+	{
+		writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1);
+	}
+    
+	//
+	// Save the power state
+	//
+	pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM);
+
+	//
+	// Disable interrupts.
+	//
+	writel(HICR_CHGM, s->pBA0 + BA0_HICR);
+
+	//
+	// Save the PCM Playback Left and Right Volume Control.
+	//
+	pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC);
+	pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC);
+
+	//
+	// Save the FM Synthesis Left and Right Volume Control.
+	//
+	pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC);
+	pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC);
+
+	//
+	// Save the GPIOR value.
+	//
+	pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR);
+
+	//
+	// Save the JSCTL value.
+	//
+	pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR);
+
+	//
+	// Save Sound System Control Register
+	//
+	pm->u32SSCR = readl(s->pBA0 + BA0_SSCR);
+
+	//
+	// Save SRC Slot Assinment register
+	//
+	pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA);
+
+	//
+	// Save sample rate
+	//
+	pm->u32DacASR = readl(s->pBA0 + BA0_PASR);
+	pm->u32AdcASR = readl(s->pBA0 + BA0_CASR);
+	pm->u32DacSR = readl(s->pBA0 + BA0_DACSR);
+	pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR);
+
+	//
+	// Loop through all of the PipeLines 
+	//
+	for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++)
+        {
+		if(s->pl[i].flags & CS4281_PIPELINE_VALID)
+		{
+		//
+		// Ask the DMAengines and FIFOs to Suspend.
+		//
+			cs4281_SuspendDMAengine(s,&s->pl[i]);
+			cs4281_SuspendFIFO(s,&s->pl[i]);
+		}
+	}
+	//
+	// We need to save the contents of the Midi Control Register.
+	//
+	pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR);
+/*
+* save off the AC97 part information
+*/
+	cs4281_ac97_suspend(s);
+    
+	//
+	// Turn off the serial ports.
+	//
+	writel(0, s->pBA0 + BA0_SERMC);
+
+	//
+	// Power off FM, Joystick, AC link, 
+	//
+	writel(0, s->pBA0 + BA0_SSPM);
+
+	//
+	// DLL off.
+	//
+	writel(0, s->pBA0 + BA0_CLKCR1);
+
+	//
+	// AC link off.
+	//
+	writel(0, s->pBA0 + BA0_SPMC);
+
+	//
+	// Put the chip into D3(hot) state.
+	//
+	// PokeBA0(BA0_PMCS, 0x00000003);
+
+	//
+	// Gershwin CLKRUN - Clear CKRA
+	//
+	u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1);
+	writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1);
+
+#ifdef CSDEBUG
+	printpm(s);
+	printpipelines(s);
+#endif
+
+	s->pm.flags &= ~CS4281_PM_SUSPENDING;
+	s->pm.flags |= CS4281_PM_SUSPENDED;
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 9, 
+		printk("cs4281: cs4281_suspend()- flags=%d\n",
+			(unsigned)s->pm.flags));
+	return 0;
+}
+
+int cs4281_resume(struct cs4281_state *s)
+{
+	int i;
+	unsigned temp1;
+	u32 u32CLKCR1;
+	struct cs4281_pm *pm = &s->pm;
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs4281: cs4281_resume()+ flags=%d\n",
+			(unsigned)s->pm.flags));
+	if(!(s->pm.flags & CS4281_PM_SUSPENDED))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 2, 
+			printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n"));
+		return 1;
+	}
+	s->pm.flags &= ~CS4281_PM_SUSPENDED;
+	s->pm.flags |= CS4281_PM_RESUMING;
+
+//
+// Gershwin CLKRUN - Set CKRA
+//
+	u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1);
+	writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1);
+
+	//
+	// set the power state.
+	//
+	//old PokeBA0(BA0_PMCS, 0);
+
+	//
+	// Program the clock circuit and serial ports.
+	//
+	temp1 = cs4281_hw_init(s);
+	if (temp1) {
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1,
+		    printk(KERN_ERR
+			"cs4281: resume cs4281_hw_init() error.\n"));
+		return -1;
+	}
+
+	//
+	// restore the Power state
+	//
+	writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM);
+
+	//
+	// Set post SRC mix setting (FM or ALT48K)
+	//
+	writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM);
+
+	//
+	// Loop through all of the PipeLines 
+	//
+	for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++)
+        {
+		if(s->pl[i].flags & CS4281_PIPELINE_VALID)
+		{
+		//
+		// Ask the DMAengines and FIFOs to Resume.
+		//
+			cs4281_ResumeDMAengine(s,&s->pl[i]);
+			cs4281_ResumeFIFO(s,&s->pl[i]);
+		}
+	}
+	//
+	// We need to restore the contents of the Midi Control Register.
+	//
+	writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR);
+
+	cs4281_ac97_resume(s);
+	//
+	// Restore the PCM Playback Left and Right Volume Control.
+	//
+	writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC);
+	writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC);
+
+	//
+	// Restore the FM Synthesis Left and Right Volume Control.
+	//
+	writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC);
+	writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC);
+
+	//
+	// Restore the JSCTL value.
+	//
+	writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL);
+
+	//
+	// Restore the GPIOR register value.
+	//
+	writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR);
+
+	//
+	// Restore Sound System Control Register
+	//
+	writel(pm->u32SSCR, s->pBA0 + BA0_SSCR);
+
+	//
+	// Restore SRC Slot Assignment register
+	//
+	writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA);
+
+	//
+	// Restore sample rate
+	//
+	writel(pm->u32DacASR, s->pBA0 + BA0_PASR);
+	writel(pm->u32AdcASR, s->pBA0 + BA0_CASR);
+	writel(pm->u32DacSR, s->pBA0 + BA0_DACSR);
+	writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR);
+
+	// 
+	// Restore CFL1/2 registers we saved to compensate for OEM bugs.
+	//
+	//	PokeBA0(BA0_CFLR, ulConfig);
+
+	//
+	// Gershwin CLKRUN - Clear CKRA
+	//
+	writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1);
+
+	//
+	// Enable interrupts on the part.
+	//
+	writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR);
+
+#ifdef CSDEBUG
+	printpm(s);
+	printpipelines(s);
+#endif
+/*
+* change the state, restore the current hwptrs, then stop the dac/adc
+*/
+	s->pm.flags |= CS4281_PM_IDLE;
+	s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED 
+			| CS4281_PM_RESUMING | CS4281_PM_RESUMED);
+
+	writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0);
+	writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1);
+	start_dac(s);
+	start_adc(s);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n",
+		(unsigned)s->pm.flags));
+	return 0;
+}
+
+#endif
+
+//******************************************************************************
+// "cs4281_play_rate()" --
+//******************************************************************************
+static void cs4281_play_rate(struct cs4281_state *card, u32 playrate)
+{
+	u32 DACSRvalue = 1;
+
+	// Based on the sample rate, program the DACSR register.
+	if (playrate == 8000)
+		DACSRvalue = 5;
+	if (playrate == 11025)
+		DACSRvalue = 4;
+	else if (playrate == 22050)
+		DACSRvalue = 2;
+	else if (playrate == 44100)
+		DACSRvalue = 1;
+	else if ((playrate <= 48000) && (playrate >= 6023))
+		DACSRvalue = 24576000 / (playrate * 16);
+	else if (playrate < 6023)
+		// Not allowed by open.
+		return;
+	else if (playrate > 48000)
+		// Not allowed by open.
+		return;
+	CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO
+		"cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n",
+			DACSRvalue, playrate));
+	//  Write the 'sample rate select code'
+	//  to the 'DAC Sample Rate' register.
+	writel(DACSRvalue, card->pBA0 + BA0_DACSR);	// (744h)
+}
+
+//******************************************************************************
+// "cs4281_record_rate()" -- Initialize the record sample rate converter.
+//******************************************************************************
+static void cs4281_record_rate(struct cs4281_state *card, u32 outrate)
+{
+	u32 ADCSRvalue = 1;
+
+	//
+	// Based on the sample rate, program the ADCSR register
+	//
+	if (outrate == 8000)
+		ADCSRvalue = 5;
+	if (outrate == 11025)
+		ADCSRvalue = 4;
+	else if (outrate == 22050)
+		ADCSRvalue = 2;
+	else if (outrate == 44100)
+		ADCSRvalue = 1;
+	else if ((outrate <= 48000) && (outrate >= 6023))
+		ADCSRvalue = 24576000 / (outrate * 16);
+	else if (outrate < 6023) {
+		// Not allowed by open.
+		return;
+	} else if (outrate > 48000) {
+		// Not allowed by open.
+		return;
+	}
+	CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO
+		"cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n",
+			ADCSRvalue, outrate));
+	//  Write the 'sample rate select code
+	//  to the 'ADC Sample Rate' register.
+	writel(ADCSRvalue, card->pBA0 + BA0_ADCSR);	// (748h)
+}
+
+
+
+static void stop_dac(struct cs4281_state *s)
+{
+	unsigned long flags;
+	unsigned temp1;
+
+	CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n"));
+	spin_lock_irqsave(&s->lock, flags);
+	s->ena &= ~FMODE_WRITE;
+	temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK;
+	writel(temp1, s->pBA0 + BA0_DCR0);
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void start_dac(struct cs4281_state *s)
+{
+	unsigned long flags;
+	unsigned temp1;
+
+	CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: start_dac()+\n"));
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped ||
+					(s->dma_dac.count > 0
+	    				&& s->dma_dac.ready))
+#ifndef NOT_CS4281_PM
+	&& (s->pm.flags & CS4281_PM_IDLE))
+#else
+)
+#endif
+ {
+		s->ena |= FMODE_WRITE;
+		temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK;	// Clear DMA0 channel mask.
+		writel(temp1, s->pBA0 + BA0_DCR0);	// Start DMA'ing.
+		writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR);	// Enable interrupts.              
+
+		writel(7, s->pBA0 + BA0_PPRVC);
+		writel(7, s->pBA0 + BA0_PPLVC);
+		CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO
+			"cs4281: start_dac(): writel 0x%x start dma\n", temp1));
+
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 3,
+		  printk(KERN_INFO "cs4281: start_dac()-\n"));
+}
+
+
+static void stop_adc(struct cs4281_state *s)
+{
+	unsigned long flags;
+	unsigned temp1;
+
+	CS_DBGOUT(CS_FUNCTION, 3,
+		  printk(KERN_INFO "cs4281: stop_adc()+\n"));
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ena &= ~FMODE_READ;
+
+	if (s->conversion == 1) {
+		s->conversion = 0;
+		s->prop_adc.fmt = s->prop_adc.fmt_original;
+	}
+	temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK;
+	writel(temp1, s->pBA0 + BA0_DCR1);
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 3,
+		  printk(KERN_INFO "cs4281: stop_adc()-\n"));
+}
+
+
+static void start_adc(struct cs4281_state *s)
+{
+	unsigned long flags;
+	unsigned temp1;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: start_adc()+\n"));
+
+	if (!(s->ena & FMODE_READ) &&
+	    (s->dma_adc.mapped || s->dma_adc.count <=
+	     (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize))
+	    && s->dma_adc.ready
+#ifndef NOT_CS4281_PM
+	&& (s->pm.flags & CS4281_PM_IDLE))
+#else
+) 
+#endif
+	{
+		if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) {
+			// 
+			// now only use 16 bit capture, due to truncation issue
+			// in the chip, noticable distortion occurs.
+			// allocate buffer and then convert from 16 bit to 
+			// 8 bit for the user buffer.
+			//
+			s->prop_adc.fmt_original = s->prop_adc.fmt;
+			if (s->prop_adc.fmt & AFMT_S8) {
+				s->prop_adc.fmt &= ~AFMT_S8;
+				s->prop_adc.fmt |= AFMT_S16_LE;
+			}
+			if (s->prop_adc.fmt & AFMT_U8) {
+				s->prop_adc.fmt &= ~AFMT_U8;
+				s->prop_adc.fmt |= AFMT_U16_LE;
+			}
+			//
+			// prog_dmabuf_adc performs a stop_adc() but that is
+			// ok since we really haven't started the DMA yet.
+			//
+			prog_codec(s, CS_TYPE_ADC);
+
+			if (prog_dmabuf_adc(s) != 0) {
+				CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					 "cs4281: start_adc(): error in prog_dmabuf_adc\n"));
+			}
+			s->conversion = 1;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		s->ena |= FMODE_READ;
+		temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK;	// Clear DMA1 channel mask bit.
+		writel(temp1, s->pBA0 + BA0_DCR1);	// Start recording
+		writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR);	// Enable interrupts.
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO
+			 "cs4281: start_adc(): writel 0x%x \n", temp1));
+	}
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: start_adc()-\n"));
+
+}
+
+
+// --------------------------------------------------------------------- 
+
+#define DMABUF_MINORDER 1	// ==> min buffer size = 8K.
+
+
+extern void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db)
+{
+	struct page *map, *mapend;
+
+	if (db->rawbuf) {
+		// Undo prog_dmabuf()'s marking the pages as reserved 
+		mapend =
+		    virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) -
+				 1);
+		for (map = virt_to_page(db->rawbuf); map <= mapend; map++)
+			cs4x_mem_map_unreserve(map);
+		free_dmabuf(s, db);
+	}
+	if (s->tmpbuff && (db->type == CS_TYPE_ADC)) {
+		// Undo prog_dmabuf()'s marking the pages as reserved 
+		mapend =
+		    virt_to_page(s->tmpbuff +
+				 (PAGE_SIZE << s->buforder_tmpbuff) - 1);
+		for (map = virt_to_page(s->tmpbuff); map <= mapend; map++)
+			cs4x_mem_map_unreserve(map);
+		free_dmabuf2(s, db);
+	}
+	s->tmpbuff = NULL;
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db)
+{
+	int order;
+	unsigned bytespersec, temp1;
+	unsigned bufs, sample_shift = 0;
+	struct page *map, *mapend;
+	unsigned long df;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: prog_dmabuf()+\n"));
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error =
+	    db->endcleared = db->blocks = db->wakeup = db->underrun = 0;
+/*
+* check for order within limits, but do not overwrite value, check
+* later for a fractional defaultorder (i.e. 100+).
+*/
+	if((defaultorder > 0) && (defaultorder < 12))
+		df = defaultorder;
+	else
+		df = 1;	
+
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = df; order >= DMABUF_MINORDER; order--)
+			if ( (db->rawbuf = (void *) pci_alloc_consistent(
+				s->pcidev, PAGE_SIZE << order, &db-> dmaaddr)))
+				    break;
+		if (!db->rawbuf) {
+			CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+				"cs4281: prog_dmabuf(): unable to allocate rawbuf\n"));
+			return -ENOMEM;
+		}
+		db->buforder = order;
+		// Now mark the pages as reserved; otherwise the 
+		// remap_page_range() in cs4281_mmap doesn't work.
+		// 1. get index to last page in mem_map array for rawbuf.
+		mapend = virt_to_page(db->rawbuf + 
+			(PAGE_SIZE << db->buforder) - 1);
+
+		// 2. mark each physical page in range as 'reserved'.
+		for (map = virt_to_page(db->rawbuf); map <= mapend; map++)
+			cs4x_mem_map_reserve(map);
+	}
+	if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) {
+		for (order = df; order >= DMABUF_MINORDER;
+		     order--)
+			if ( (s->tmpbuff = (void *) pci_alloc_consistent(
+					s->pcidev, PAGE_SIZE << order, 
+					&s->dmaaddr_tmpbuff)))
+				    break;
+		if (!s->tmpbuff) {
+			CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+				"cs4281: prog_dmabuf(): unable to allocate tmpbuff\n"));
+			return -ENOMEM;
+		}
+		s->buforder_tmpbuff = order;
+		// Now mark the pages as reserved; otherwise the 
+		// remap_page_range() in cs4281_mmap doesn't work.
+		// 1. get index to last page in mem_map array for rawbuf.
+		mapend = virt_to_page(s->tmpbuff + 
+				(PAGE_SIZE << s->buforder_tmpbuff) - 1);
+
+		// 2. mark each physical page in range as 'reserved'.
+		for (map = virt_to_page(s->tmpbuff); map <= mapend; map++)
+			cs4x_mem_map_reserve(map);
+	}
+	if (db->type == CS_TYPE_DAC) {
+		if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE))
+			sample_shift++;
+		if (s->prop_dac.channels > 1)
+			sample_shift++;
+		bytespersec = s->prop_dac.rate << sample_shift;
+	} else			// CS_TYPE_ADC
+	{
+		if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE))
+			sample_shift++;
+		if (s->prop_adc.channels > 1)
+			sample_shift++;
+		bytespersec = s->prop_adc.rate << sample_shift;
+	}
+	bufs = PAGE_SIZE << db->buforder;
+
+/*
+* added fractional "defaultorder" inputs. if >100 then use 
+* defaultorder-100 as power of 2 for the buffer size. example:
+* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
+*/
+	if(defaultorder >= 100)
+	{
+		bufs = 1 << (defaultorder-100);
+	}
+
+#define INTERRUPT_RATE_MS       100	// Interrupt rate in milliseconds.
+	db->numfrag = 2;
+/* 
+* Nominal frag size(bytes/interrupt)
+*/
+	temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS);
+	db->fragshift = 8;	// Min 256 bytes.
+	while (1 << db->fragshift < temp1)	// Calc power of 2 frag size.
+		db->fragshift += 1;
+	db->fragsize = 1 << db->fragshift;
+	db->dmasize = db->fragsize * 2;
+	db->fragsamples = db->fragsize >> sample_shift;	// # samples/fragment.
+
+// If the calculated size is larger than the allocated
+//  buffer, divide the allocated buffer into 2 fragments.
+	if (db->dmasize > bufs) {
+
+		db->numfrag = 2;	// Two fragments.
+		db->fragsize = bufs >> 1;	// Each 1/2 the alloc'ed buffer.
+		db->fragsamples = db->fragsize >> sample_shift;	// # samples/fragment.
+		db->dmasize = bufs;	// Use all the alloc'ed buffer.
+
+		db->fragshift = 0;	// Calculate 'fragshift'.
+		temp1 = db->fragsize;	// update_ptr() uses it 
+		while ((temp1 >>= 1) > 1)	// to calc 'total-bytes'
+			db->fragshift += 1;	// returned in DSP_GETI/OPTR. 
+	}
+	CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO
+		"cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n",
+			db->numfrag, db->fragsize, db->fragsamples, 
+			db->fragshift, bufs, 
+			(db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : 
+				s->prop_adc.fmt, 
+			(db->type == CS_TYPE_DAC) ? s->prop_dac.channels : 
+				s->prop_adc.channels));
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: prog_dmabuf()-\n"));
+	return 0;
+}
+
+
+static int prog_dmabuf_adc(struct cs4281_state *s)
+{
+	unsigned long va;
+	unsigned count;
+	int c;
+	stop_adc(s);
+	s->dma_adc.type = CS_TYPE_ADC;
+	if ((c = prog_dmabuf(s, &s->dma_adc)))
+		return c;
+
+	if (s->dma_adc.rawbuf) {
+		memset(s->dma_adc.rawbuf,
+		       (s->prop_adc.
+			fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0,
+		       s->dma_adc.dmasize);
+	}
+	if (s->tmpbuff) {
+		memset(s->tmpbuff,
+		       (s->prop_adc.
+			fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0,
+		       PAGE_SIZE << s->buforder_tmpbuff);
+	}
+
+	va = virt_to_bus(s->dma_adc.rawbuf);
+
+	count = s->dma_adc.dmasize;
+
+	if (s->prop_adc.
+	    fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE))
+		    count /= 2;	// 16-bit.
+
+	if (s->prop_adc.channels > 1)
+		count /= 2;	// Assume stereo.
+
+	CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO
+		"cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n",
+			count, (unsigned) va));
+
+	writel(va, s->pBA0 + BA0_DBA1);	// Set buffer start address.
+	writel(count - 1, s->pBA0 + BA0_DBC1);	// Set count. 
+	s->dma_adc.ready = 1;
+	return 0;
+}
+
+
+static int prog_dmabuf_dac(struct cs4281_state *s)
+{
+	unsigned long va;
+	unsigned count;
+	int c;
+	stop_dac(s);
+	s->dma_dac.type = CS_TYPE_DAC;
+	if ((c = prog_dmabuf(s, &s->dma_dac)))
+		return c;
+	memset(s->dma_dac.rawbuf,
+	       (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0,
+	       s->dma_dac.dmasize);
+
+	va = virt_to_bus(s->dma_dac.rawbuf);
+
+	count = s->dma_dac.dmasize;
+	if (s->prop_dac.
+	    fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE))
+		    count /= 2;	// 16-bit.
+
+	if (s->prop_dac.channels > 1)
+		count /= 2;	// Assume stereo.
+
+	writel(va, s->pBA0 + BA0_DBA0);	// Set buffer start address.
+	writel(count - 1, s->pBA0 + BA0_DBC0);	// Set count.             
+
+	CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO
+		"cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n",
+			count, (unsigned) va));
+
+	s->dma_dac.ready = 1;
+	return 0;
+}
+
+
+static void clear_advance(void *buf, unsigned bsize, unsigned bptr,
+			  unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *) buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
+		"cs4281: clear_advance(): memset %d at 0x%.8x for %d size \n",
+			(unsigned)c, (unsigned)((char *) buf) + bptr, len));
+	memset(((char *) buf) + bptr, c, len);
+}
+
+
+
+// call with spinlock held! 
+static void cs4281_update_ptr(struct cs4281_state *s, int intflag)
+{
+	int diff;
+	unsigned hwptr, va;
+
+	// update ADC pointer 
+	if (s->ena & FMODE_READ) {
+		hwptr = readl(s->pBA0 + BA0_DCA1);	// Read capture DMA address.
+		va = virt_to_bus(s->dma_adc.rawbuf);
+		hwptr -= (unsigned) va;
+		diff =
+		    (s->dma_adc.dmasize + hwptr -
+		     s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count > s->dma_adc.dmasize)
+			s->dma_adc.count = s->dma_adc.dmasize;
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >=
+			    (signed) s->dma_adc.fragsize) wake_up(&s->
+								  dma_adc.
+								  wait);
+		} else {
+			if (s->dma_adc.count > 0)
+				wake_up(&s->dma_adc.wait);
+		}
+		CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
+			"cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
+				(unsigned)s, s->dma_adc.hwptr, 
+				s->dma_adc.total_bytes, s->dma_adc.count));
+	}
+	// update DAC pointer 
+	//
+	// check for end of buffer, means that we are going to wait for another interrupt
+	// to allow silence to fill the fifos on the part, to keep pops down to a minimum.
+	//
+	if (s->ena & FMODE_WRITE) {
+		hwptr = readl(s->pBA0 + BA0_DCA0);	// Read play DMA address.
+		va = virt_to_bus(s->dma_dac.rawbuf);
+		hwptr -= (unsigned) va;
+		diff = (s->dma_dac.dmasize + hwptr -
+		     s->dma_dac.hwptr) % s->dma_dac.dmasize;
+		s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= s->dma_dac.fragsize) {
+				s->dma_dac.wakeup = 1;
+				wake_up(&s->dma_dac.wait);
+				if (s->dma_dac.count > s->dma_dac.dmasize)
+					s->dma_dac.count &=
+					    s->dma_dac.dmasize - 1;
+			}
+		} else {
+			s->dma_dac.count -= diff;
+			if (s->dma_dac.count <= 0) {
+				//
+				// fill with silence, and do not shut down the DAC.
+				// Continue to play silence until the _release.
+				//
+				CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO
+					"cs4281: cs4281_update_ptr(): memset %d at 0x%.8x for %d size \n",
+						(unsigned)(s->prop_dac.fmt & 
+						(AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, 
+						(unsigned)s->dma_dac.rawbuf, 
+						s->dma_dac.dmasize));
+				memset(s->dma_dac.rawbuf,
+				       (s->prop_dac.
+					fmt & (AFMT_U8 | AFMT_U16_LE)) ?
+				       0x80 : 0, s->dma_dac.dmasize);
+				if (s->dma_dac.count < 0) {
+					s->dma_dac.underrun = 1;
+					s->dma_dac.count = 0;
+					CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO
+					 "cs4281: cs4281_update_ptr(): underrun\n"));
+				}
+			} else if (s->dma_dac.count <=
+				   (signed) s->dma_dac.fragsize
+				   && !s->dma_dac.endcleared) {
+				clear_advance(s->dma_dac.rawbuf,
+					      s->dma_dac.dmasize,
+					      s->dma_dac.swptr,
+					      s->dma_dac.fragsize,
+					      (s->prop_dac.
+					       fmt & (AFMT_U8 |
+						      AFMT_U16_LE)) ? 0x80
+					      : 0);
+				s->dma_dac.endcleared = 1;
+			}
+			if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) ||
+				intflag)
+			{
+				wake_up(&s->dma_dac.wait);
+			}
+		}
+		CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
+			"cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
+				(unsigned) s, s->dma_dac.hwptr, 
+				s->dma_dac.total_bytes, s->dma_dac.count));
+	}
+}
+
+
+// --------------------------------------------------------------------- 
+
+static void prog_codec(struct cs4281_state *s, unsigned type)
+{
+	unsigned long flags;
+	unsigned temp1, format;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: prog_codec()+ \n"));
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (type == CS_TYPE_ADC) {
+		temp1 = readl(s->pBA0 + BA0_DCR1);
+		writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1);	// Stop capture DMA, if active.
+
+		// program sampling rates  
+		// Note, for CS4281, capture & play rates can be set independently.
+		cs4281_record_rate(s, s->prop_adc.rate);
+
+		// program ADC parameters 
+		format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE;
+		if (s->prop_adc.
+		    fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) {	// 16-bit
+			if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE))	// Big-endian?
+				format |= DMRn_BEND;
+			if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE))
+				format |= DMRn_USIGN;	// Unsigned.      
+		} else
+			format |= DMRn_SIZE8 | DMRn_USIGN;	// 8-bit, unsigned
+		if (s->prop_adc.channels < 2)
+			format |= DMRn_MONO;
+
+		writel(format, s->pBA0 + BA0_DMR1);
+
+		CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO
+			"cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n",
+				(format & DMRn_SIZE8) ? "8" : "16",
+				(format & DMRn_USIGN) ?  "Unsigned" : "Signed", 
+				(format & DMRn_MONO) ? "Mono" : "Stereo", 
+				s->prop_adc.rate, format));
+
+		s->ena &= ~FMODE_READ;	// not capturing data yet
+	}
+
+
+	if (type == CS_TYPE_DAC) {
+		temp1 = readl(s->pBA0 + BA0_DCR0);
+		writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0);	// Stop play DMA, if active.
+
+		// program sampling rates  
+		// Note, for CS4281, capture & play rates can be set independently.
+		cs4281_play_rate(s, s->prop_dac.rate);
+
+		// program DAC parameters 
+		format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ;
+		if (s->prop_dac.
+		    fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) {	// 16-bit
+			if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE))
+				format |= DMRn_BEND;	// Big Endian.
+			if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE))
+				format |= DMRn_USIGN;	// Unsigned.      
+		} else
+			format |= DMRn_SIZE8 | DMRn_USIGN;	// 8-bit, unsigned
+
+		if (s->prop_dac.channels < 2)
+			format |= DMRn_MONO;
+
+		writel(format, s->pBA0 + BA0_DMR0);
+
+
+		CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO
+			"cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n",
+				(format & DMRn_SIZE8) ? "8" : "16",
+				(format & DMRn_USIGN) ?  "Unsigned" : "Signed",
+				(format & DMRn_MONO) ? "Mono" : "Stereo", 
+				s->prop_dac.rate, format));
+
+		s->ena &= ~FMODE_WRITE;	// not capturing data yet
+
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: prog_codec()- \n"));
+}
+
+
+static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd,
+		       unsigned long arg)
+{
+	// Index to mixer_src[] is value of AC97 Input Mux Select Reg.
+	// Value of array member is recording source Device ID Mask.
+	static const unsigned int mixer_src[8] = {
+		SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1,
+		SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0
+	};
+
+	// Index of mixtable1[] member is Device ID 
+	// and must be <= SOUND_MIXER_NRDEVICES.
+	// Value of array member is index into s->mix.vol[]
+	static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
+		[SOUND_MIXER_PCM] = 1,	// voice 
+		[SOUND_MIXER_LINE1] = 2,	// AUX
+		[SOUND_MIXER_CD] = 3,	// CD 
+		[SOUND_MIXER_LINE] = 4,	// Line 
+		[SOUND_MIXER_SYNTH] = 5,	// FM
+		[SOUND_MIXER_MIC] = 6,	// Mic 
+		[SOUND_MIXER_SPEAKER] = 7,	// Speaker 
+		[SOUND_MIXER_RECLEV] = 8,	// Recording level 
+		[SOUND_MIXER_VOLUME] = 9	// Master Volume 
+	};
+
+
+	static const unsigned mixreg[] = {
+		BA0_AC97_PCM_OUT_VOLUME,
+		BA0_AC97_AUX_VOLUME,
+		BA0_AC97_CD_VOLUME,
+		BA0_AC97_LINE_IN_VOLUME
+	};
+	unsigned char l, r, rl, rr, vidx;
+	unsigned char attentbl[11] =
+	    { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 };
+	unsigned temp1;
+	int i, val;
+
+	VALIDATE_STATE(s);
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
+		 "cs4281: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n",
+			 (unsigned) s, cmd));
+#if CSDEBUG
+	cs_printioctl(cmd);
+#endif
+#if CSDEBUG_INTERFACE
+
+	if ((cmd == SOUND_MIXER_CS_GETDBGMASK) ||
+	    (cmd == SOUND_MIXER_CS_SETDBGMASK) ||
+	    (cmd == SOUND_MIXER_CS_GETDBGLEVEL) ||
+	    (cmd == SOUND_MIXER_CS_SETDBGLEVEL) ||
+	    (cmd == SOUND_MIXER_CS_APM))
+	{
+		switch (cmd) {
+
+		case SOUND_MIXER_CS_GETDBGMASK:
+			return put_user(cs_debugmask,
+					(unsigned long *) arg);
+
+		case SOUND_MIXER_CS_GETDBGLEVEL:
+			return put_user(cs_debuglevel,
+					(unsigned long *) arg);
+
+		case SOUND_MIXER_CS_SETDBGMASK:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			cs_debugmask = val;
+			return 0;
+
+		case SOUND_MIXER_CS_SETDBGLEVEL:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			cs_debuglevel = val;
+			return 0;
+#ifndef NOT_CS4281_PM
+		case SOUND_MIXER_CS_APM:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			if(val == CS_IOCTL_CMD_SUSPEND)
+				cs4281_suspend(s);
+			else if(val == CS_IOCTL_CMD_RESUME)
+				cs4281_resume(s);
+			else
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
+				    "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n",
+					val));
+			}
+			return 0;
+#endif
+		default:
+			CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
+				"cs4281: mixer_ioctl(): ERROR unknown debug cmd\n"));
+			return 0;
+		}
+	}
+#endif
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		// enable/disable/query mixer preamp 
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (val != -1) {
+			cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1);
+			temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf);
+			cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1);
+		}
+		cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1);
+		val = (temp1 & 0x40) ? 1 : 0;
+		return put_user(val, (int *) arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+		// enable/disable/query spatializer 
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (val != -1) {
+			temp1 = (val & 0x3f) >> 2;
+			cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1);
+			cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE,
+					 &temp1);
+			cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE,
+					  temp1 | 0x2000);
+		}
+		cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1);
+		return put_user((temp1 << 2) | 3, (int *) arg);
+	}
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "CS4281", sizeof(info.id));
+		strncpy(info.name, "Crystal CS4281", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *) arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "CS4281", sizeof(info.id));
+		strncpy(info.name, "Crystal CS4281", sizeof(info.name));
+		if (copy_to_user((void *) arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *) arg);
+
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+		return -EINVAL;
+
+	// If ioctl has only the SIOC_READ bit(bit 31)
+	// on, process the only-read commands. 
+	if (_SIOC_DIR(cmd) == _SIOC_READ) {
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC:	// Arg contains a bit for each recording source 
+			cs4281_read_ac97(s, BA0_AC97_RECORD_SELECT,
+					 &temp1);
+			return put_user(mixer_src[temp1 & 7], (int *) arg);
+
+		case SOUND_MIXER_DEVMASK:	// Arg contains a bit for each supported device 
+			return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH |
+					SOUND_MASK_CD | SOUND_MASK_LINE |
+					SOUND_MASK_LINE1 | SOUND_MASK_MIC |
+					SOUND_MASK_VOLUME |
+					SOUND_MASK_RECLEV |
+					SOUND_MASK_SPEAKER, (int *) arg);
+
+		case SOUND_MIXER_RECMASK:	// Arg contains a bit for each supported recording source 
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC |
+					SOUND_MASK_CD | SOUND_MASK_VOLUME |
+					SOUND_MASK_LINE1, (int *) arg);
+
+		case SOUND_MIXER_STEREODEVS:	// Mixer channels supporting stereo 
+			return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH |
+					SOUND_MASK_CD | SOUND_MASK_LINE |
+					SOUND_MASK_LINE1 | SOUND_MASK_MIC |
+					SOUND_MASK_VOLUME |
+					SOUND_MASK_RECLEV, (int *) arg);
+
+		case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
+
+		default:
+			i = _IOC_NR(cmd);
+			if (i >= SOUND_MIXER_NRDEVICES
+			    || !(vidx = mixtable1[i]))
+				return -EINVAL;
+			return put_user(s->mix.vol[vidx - 1], (int *) arg);
+		}
+	}
+	// If ioctl doesn't have both the SIOC_READ and 
+	// the SIOC_WRITE bit set, return invalid.
+	if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE))
+		return -EINVAL;
+
+	// Increment the count of volume writes.
+	s->mix.modcnt++;
+
+	// Isolate the command; it must be a write.
+	switch (_IOC_NR(cmd)) {
+
+	case SOUND_MIXER_RECSRC:	// Arg contains a bit for each recording source 
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		i = hweight32(val);	// i = # bits on in val.
+		if (i != 1)	// One & only 1 bit must be on.
+			return 0;
+		for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) {
+			if (val == mixer_src[i]) {
+				temp1 = (i << 8) | i;
+				cs4281_write_ac97(s,
+						  BA0_AC97_RECORD_SELECT,
+						  temp1);
+				return 0;
+			}
+		}
+		return 0;
+
+	case SOUND_MIXER_VOLUME:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;	// Max soundcard.h vol is 100.
+		if (l < 6) {
+			rl = 63;
+			l = 0;
+		} else
+			rl = attentbl[(10 * l) / 100];	// Convert 0-100 vol to 63-0 atten.
+
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;	// Max right volume is 100, too
+		if (r < 6) {
+			rr = 63;
+			r = 0;
+		} else
+			rr = attentbl[(10 * r) / 100];	// Convert volume to attenuation.
+
+		if ((rl > 60) && (rr > 60))	// If both l & r are 'low',          
+			temp1 = 0x8000;	//  turn on the mute bit.
+		else
+			temp1 = 0;
+
+		temp1 |= (rl << 8) | rr;
+
+		cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1);
+		cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[8] = ((unsigned int) r << 8) | l;
+#else
+		s->mix.vol[8] = val;
+#endif
+		return put_user(s->mix.vol[8], (int *) arg);
+
+	case SOUND_MIXER_SPEAKER:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (l < 3) {
+			rl = 0;
+			l = 0;
+		} else {
+			rl = (l * 2 - 5) / 13;	// Convert 0-100 range to 0-15.
+			l = (rl * 13 + 5) / 2;
+		}
+
+		if (rl < 3) {
+			temp1 = 0x8000;
+			rl = 0;
+		} else
+			temp1 = 0;
+		rl = 15 - rl;	// Convert volume to attenuation.
+		temp1 |= rl << 1;
+		cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[6] = l << 8;
+#else
+		s->mix.vol[6] = val;
+#endif
+		return put_user(s->mix.vol[6], (int *) arg);
+
+	case SOUND_MIXER_RECLEV:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		rl = (l * 2 - 5) / 13;	// Convert 0-100 scale to 0-15.
+		rr = (r * 2 - 5) / 13;
+		if (rl < 3 && rr < 3)
+			temp1 = 0x8000;
+		else
+			temp1 = 0;
+
+		temp1 = temp1 | (rl << 8) | rr;
+		cs4281_write_ac97(s, BA0_AC97_RECORD_GAIN, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[7] = ((unsigned int) r << 8) | l;
+#else
+		s->mix.vol[7] = val;
+#endif
+		return put_user(s->mix.vol[7], (int *) arg);
+
+	case SOUND_MIXER_MIC:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (l < 1) {
+			l = 0;
+			rl = 0;
+		} else {
+			rl = ((unsigned) l * 5 - 4) / 16;	// Convert 0-100 range to 0-31.
+			l = (rl * 16 + 4) / 5;
+		}
+		cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1);
+		temp1 &= 0x40;	// Isolate 20db gain bit.
+		if (rl < 3) {
+			temp1 |= 0x8000;
+			rl = 0;
+		}
+		rl = 31 - rl;	// Convert volume to attenuation.
+		temp1 |= rl;
+		cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[5] = val << 8;
+#else
+		s->mix.vol[5] = val;
+#endif
+		return put_user(s->mix.vol[5], (int *) arg);
+
+
+	case SOUND_MIXER_SYNTH:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		rl = (l * 2 - 11) / 3;	// Convert 0-100 range to 0-63.
+		rr = (r * 2 - 11) / 3;
+		if (rl < 3)	// If l is low, turn on
+			temp1 = 0x0080;	//  the mute bit.
+		else
+			temp1 = 0;
+
+		rl = 63 - rl;	// Convert vol to attenuation.
+		writel(temp1 | rl, s->pBA0 + BA0_FMLVC);
+		if (rr < 3)	//  If rr is low, turn on
+			temp1 = 0x0080;	//   the mute bit.
+		else
+			temp1 = 0;
+		rr = 63 - rr;	// Convert vol to attenuation.
+		writel(temp1 | rr, s->pBA0 + BA0_FMRVC);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[4] = (r << 8) | l;
+#else
+		s->mix.vol[4] = val;
+#endif
+		return put_user(s->mix.vol[4], (int *) arg);
+
+
+	default:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			"cs4281: mixer_ioctl(): default\n"));
+
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
+			return -EINVAL;
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (l < 1) {
+			l = 0;
+			rl = 31;
+		} else
+			rl = (attentbl[(l * 10) / 100]) >> 1;
+
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		if (r < 1) {
+			r = 0;
+			rr = 31;
+		} else
+			rr = (attentbl[(r * 10) / 100]) >> 1;
+		if ((rl > 30) && (rr > 30))
+			temp1 = 0x8000;
+		else
+			temp1 = 0;
+		temp1 = temp1 | (rl << 8) | rr;
+		cs4281_write_ac97(s, mixreg[vidx - 1], temp1);
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l;
+#else
+		s->mix.vol[vidx - 1] = val;
+#endif
+#ifndef NOT_CS4281_PM
+		CS_DBGOUT(CS_PM, 9, printk(KERN_INFO 
+			"write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", 
+				vidx-1,temp1,s->mix.vol[vidx-1]));
+#endif
+		return put_user(s->mix.vol[vidx - 1], (int *) arg);
+	}
+}
+
+
+// --------------------------------------------------------------------- 
+
+static int cs4281_open_mixdev(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct cs4281_state *s=NULL;
+	struct list_head *entry;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n"));
+
+	list_for_each(entry, &cs4281_devs)
+	{
+		s = list_entry(entry, struct cs4281_state, list);
+		if(s->dev_mixer == minor)
+			break;
+	}
+	if (!s)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
+			printk(KERN_INFO "cs4281: cs4281_open_mixdev()- -ENODEV\n"));
+		return -ENODEV;
+	}
+	VALIDATE_STATE(s);
+	file->private_data = s;
+	MOD_INC_USE_COUNT;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs4281: cs4281_open_mixdev()- 0\n"));
+
+	return 0;
+}
+
+
+static int cs4281_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+
+	VALIDATE_STATE(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file,
+			       unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct cs4281_state *) file->private_data, cmd,
+			   arg);
+}
+
+
+// ******************************************************************************************
+//   Mixer file operations struct.
+// ******************************************************************************************
+static /*const */ struct file_operations cs4281_mixer_fops = {
+	llseek:no_llseek,
+	ioctl:cs4281_ioctl_mixdev,
+	open:cs4281_open_mixdev,
+	release:cs4281_release_mixdev,
+};
+
+// --------------------------------------------------------------------- 
+
+
+static int drain_adc(struct cs4281_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count;
+	unsigned tmo;
+
+	if (s->dma_adc.mapped)
+		return 0;
+	add_wait_queue(&s->dma_adc.wait, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_adc.count;
+		CS_DBGOUT(CS_FUNCTION, 2,
+			  printk(KERN_INFO "cs4281: drain_adc() %d\n", count));
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0) {
+			CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO
+				 "cs4281: drain_adc() count<0\n"));
+			break;
+		}
+		if (signal_pending(current))
+			break;
+		if (nonblock) {
+			remove_wait_queue(&s->dma_adc.wait, &wait);
+			current->state = TASK_RUNNING;
+			return -EBUSY;
+		}
+		tmo =
+		    3 * HZ * (count +
+			      s->dma_adc.fragsize) / 2 / s->prop_adc.rate;
+		if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE))
+			tmo >>= 1;
+		if (s->prop_adc.channels > 1)
+			tmo >>= 1;
+		if (!schedule_timeout(tmo + 1))
+			printk(KERN_DEBUG "cs4281: dma timed out??\n");
+	}
+	remove_wait_queue(&s->dma_adc.wait, &wait);
+	current->state = TASK_RUNNING;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static int drain_dac(struct cs4281_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count;
+	unsigned tmo;
+
+	if (s->dma_dac.mapped)
+		return 0;
+	add_wait_queue(&s->dma_dac.wait, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+			break;
+		if (nonblock) {
+			remove_wait_queue(&s->dma_dac.wait, &wait);
+			current->state = TASK_RUNNING;
+			return -EBUSY;
+		}
+		tmo =
+		    3 * HZ * (count +
+			      s->dma_dac.fragsize) / 2 / s->prop_dac.rate;
+		if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE))
+			tmo >>= 1;
+		if (s->prop_dac.channels > 1)
+			tmo >>= 1;
+		if (!schedule_timeout(tmo + 1))
+			printk(KERN_DEBUG "cs4281: dma timed out??\n");
+	}
+	remove_wait_queue(&s->dma_dac.wait, &wait);
+	current->state = TASK_RUNNING;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+//****************************************************************************
+//
+// CopySamples copies 16-bit stereo samples from the source to the
+// destination, possibly converting down to either 8-bit or mono or both.
+// count specifies the number of output bytes to write.
+//
+//  Arguments:
+//
+//  dst             - Pointer to a destination buffer.
+//  src             - Pointer to a source buffer
+//  count           - The number of bytes to copy into the destination buffer.
+//  iChannels       - Stereo - 2
+//                    Mono   - 1
+//  fmt             - AFMT_xxx (soundcard.h formats)
+//
+// NOTES: only call this routine for conversion to 8bit from 16bit
+//
+//****************************************************************************
+static void CopySamples(char *dst, char *src, int count, int iChannels,
+			unsigned fmt)
+{
+
+	unsigned short *psSrc;
+	long lAudioSample;
+
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: CopySamples()+ "));
+	CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
+		 " dst=0x%x src=0x%x count=%d iChannels=%d fmt=0x%x\n",
+			 (unsigned) dst, (unsigned) src, (unsigned) count,
+			 (unsigned) iChannels, (unsigned) fmt));
+
+	// Gershwin does format conversion in hardware so normally
+	// we don't do any host based coversion. The data formatter
+	// truncates 16 bit data to 8 bit and that causes some hiss.
+	// We have already forced the HW to do 16 bit sampling and 
+	// 2 channel so that we can use software to round instead 
+	// of truncate
+
+	//
+	// See if the data should be output as 8-bit unsigned stereo.
+	// or if the data should be output at 8-bit unsigned mono.
+	//
+	if ( ((iChannels == 2) && (fmt & AFMT_U8)) ||
+		((iChannels == 1) && (fmt & AFMT_U8)) ) {
+		//
+		// Convert each 16-bit unsigned stereo sample to 8-bit unsigned 
+		// stereo using rounding.
+		//
+		psSrc = (unsigned short *) src;
+		count = count / 2;
+		while (count--) {
+			lAudioSample = (long) psSrc[count] + (long) 0x80;
+			if (lAudioSample > 0xffff) {
+				lAudioSample = 0xffff;
+			}
+			dst[count] = (char) (lAudioSample >> 8);
+		}
+	}
+	//
+	// check for 8-bit signed stereo.
+	//
+	else if ((iChannels == 2) && (fmt & AFMT_S8)) {
+		//
+		// Convert each 16-bit stereo sample to 8-bit stereo using rounding.
+		//
+		psSrc = (short *) src;
+		while (count--) {
+			lAudioSample =
+			    (((long) psSrc[0] + (long) psSrc[1]) / 2);
+			psSrc += 2;
+			*dst++ = (char) ((short) lAudioSample >> 8);
+		}
+	}
+	//
+	// Otherwise, the data should be output as 8-bit signed mono.
+	//
+	else if ((iChannels == 1) && (fmt & AFMT_S8)) {
+		//
+		// Convert each 16-bit signed mono sample to 8-bit signed mono 
+		// using rounding.
+		//
+		psSrc = (short *) src;
+		count = count / 2;
+		while (count--) {
+			lAudioSample =
+			    (((long) psSrc[0] + (long) psSrc[1]) / 2);
+			if (lAudioSample > 0x7fff) {
+				lAudioSample = 0x7fff;
+			}
+			psSrc += 2;
+			*dst++ = (char) ((short) lAudioSample >> 8);
+		}
+	}
+}
+
+//
+// cs_copy_to_user()
+// replacement for the standard copy_to_user, to allow for a conversion from
+// 16 bit to 8 bit if the record conversion is active.  the cs4281 has some
+// issues with 8 bit capture, so the driver always captures data in 16 bit
+// and then if the user requested 8 bit, converts from 16 to 8 bit.
+//
+static unsigned cs_copy_to_user(struct cs4281_state *s, void *dest,
+				unsigned *hwsrc, unsigned cnt,
+				unsigned *copied)
+{
+	void *src = hwsrc;	//default to the standard destination buffer addr
+
+	CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO
+		"cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=0x%.8x\n",
+			s->prop_adc.fmt, s->prop_adc.fmt_original,
+			(unsigned) cnt, (unsigned) dest));
+
+	if (cnt > s->dma_adc.dmasize) {
+		cnt = s->dma_adc.dmasize;
+	}
+	if (!cnt) {
+		*copied = 0;
+		return 0;
+	}
+	if (s->conversion) {
+		if (!s->tmpbuff) {
+			*copied = cnt / 2;
+			return 0;
+		}
+		CopySamples(s->tmpbuff, (void *) hwsrc, cnt,
+			    (unsigned) s->prop_adc.channels,
+			    s->prop_adc.fmt_original);
+		src = s->tmpbuff;
+		cnt = cnt / 2;
+	}
+
+	if (copy_to_user(dest, src, cnt)) {
+		*copied = 0;
+		return -EFAULT;
+	}
+	*copied = cnt;
+	CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO
+		"cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt));
+	return 0;
+}
+
+// --------------------------------------------------------------------- 
+
+static ssize_t cs4281_read(struct file *file, char *buffer, size_t count,
+			   loff_t * ppos)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+	unsigned copied = 0;
+
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
+		  printk(KERN_INFO "cs4281: cs4281_read()+ %d \n", count));
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+//
+// "count" is the amount of bytes to read (from app), is decremented each loop
+//      by the amount of bytes that have been returned to the user buffer.
+// "cnt" is the running total of each read from the buffer (changes each loop)
+// "buffer" points to the app's buffer
+// "ret" keeps a running total of the amount of bytes that have been copied
+//      to the user buffer.
+// "copied" is the total bytes copied into the user buffer for each loop.
+//
+	while (count > 0) {
+		CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
+			"_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n",
+				count, s->dma_adc.count,
+				s->dma_adc.swptr, s->dma_adc.hwptr));
+		spin_lock_irqsave(&s->lock, flags);
+
+		// get the current copy point of the sw buffer
+		swptr = s->dma_adc.swptr;
+
+		// cnt is the amount of unread bytes from the end of the 
+		// hw buffer to the current sw pointer
+		cnt = s->dma_adc.dmasize - swptr;
+
+		// dma_adc.count is the current total bytes that have not been read.
+		// if the amount of unread bytes from the current sw pointer to the
+		// end of the buffer is greater than the current total bytes that
+		// have not been read, then set the "cnt" (unread bytes) to the
+		// amount of unread bytes.  
+
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		//
+		// if we are converting from 8/16 then we need to copy
+		// twice the number of 16 bit bytes then 8 bit bytes.
+		// 
+		if (s->conversion) {
+			if (cnt > (count * 2))
+				cnt = (count * 2);
+		} else {
+			if (cnt > count)
+				cnt = count;
+		}
+		//
+		// "cnt" NOW is the smaller of the amount that will be read,
+		// and the amount that is requested in this read (or partial).
+		// if there are no bytes in the buffer to read, then start the
+		// ADC and wait for the interrupt handler to wake us up.
+		//
+		if (cnt <= 0) {
+
+			// start up the dma engine and then continue back to the top of
+			// the loop when wake up occurs.
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->dma_adc.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		// there are bytes in the buffer to read.
+		// copy from the hw buffer over to the user buffer.
+		// user buffer is designated by "buffer"
+		// virtual address to copy from is rawbuf+swptr
+		// the "cnt" is the number of bytes to read.
+
+		CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO
+			"_read() copy_to cnt=%d count=%d ", cnt, count));
+		CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
+			 " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n",
+				 s->dma_adc.dmasize, s->dma_adc.count,
+				 (unsigned) buffer, ret));
+
+		if (cs_copy_to_user
+		    (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= copied;
+		buffer += copied;
+		ret += copied;
+		start_adc(s);
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
+		  printk(KERN_INFO "cs4281: cs4281_read()- %d\n", ret));
+	return ret;
+}
+
+
+static ssize_t cs4281_write(struct file *file, const char *buffer,
+			    size_t count, loff_t * ppos)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr, hwptr, busaddr;
+	int cnt;
+
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
+		  printk(KERN_INFO "cs4281: cs4281_write()+ count=%d\n",
+			 count));
+	VALIDATE_STATE(s);
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		if (s->dma_dac.underrun) {
+			s->dma_dac.underrun = 0;
+			hwptr = readl(s->pBA0 + BA0_DCA0);
+			busaddr = virt_to_bus(s->dma_dac.rawbuf);
+			hwptr -= (unsigned) busaddr;
+			s->dma_dac.swptr = s->dma_dac.hwptr = hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize - swptr;
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->dma_dac.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(s);
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
+		  printk(KERN_INFO "cs4281: cs4281_write()- %d\n", ret));
+	return ret;
+}
+
+
+static unsigned int cs4281_poll(struct file *file,
+				struct poll_table_struct *wait)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+		  printk(KERN_INFO "cs4281: cs4281_poll()+\n"));
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+			  printk(KERN_INFO
+				 "cs4281: cs4281_poll() wait on FMODE_WRITE\n"));
+		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		poll_wait(file, &s->dma_dac.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+			  printk(KERN_INFO
+				 "cs4281: cs4281_poll() wait on FMODE_READ\n"));
+		if(!s->dma_dac.ready && prog_dmabuf_adc(s))
+			return 0;
+		poll_wait(file, &s->dma_adc.wait, wait);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	cs4281_update_ptr(s,CS_FALSE);
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >=
+			    (signed) s->dma_dac.fragsize) {
+				if (s->dma_dac.wakeup)
+					mask |= POLLOUT | POLLWRNORM;
+				else
+					mask = 0;
+				s->dma_dac.wakeup = 0;
+			}
+		} else {
+			if ((signed) (s->dma_dac.dmasize/2) >= s->dma_dac.count)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	} else if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) 
+				mask |= POLLIN | POLLRDNORM;
+		} else {
+			if (s->dma_adc.count > 0)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
+		  printk(KERN_INFO "cs4281: cs4281_poll()- 0x%.8x\n",
+			 mask));
+	return mask;
+}
+
+
+static int cs4281_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	struct dmabuf *db;
+	int ret;
+	unsigned long size;
+
+	CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4,
+		  printk(KERN_INFO "cs4281: cs4281_mmap()+\n"));
+
+	VALIDATE_STATE(s);
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_dac(s)) != 0)
+			return ret;
+		db = &s->dma_dac;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_adc(s)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else
+		return -EINVAL;
+//
+// only support PLAYBACK for now
+//
+	db = &s->dma_dac;
+
+	if (cs4x_pgoff(vma) != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		return -EINVAL;
+	if (remap_page_range
+	    (vma->vm_start, virt_to_phys(db->rawbuf), size,
+	     vma->vm_page_prot)) return -EAGAIN;
+	db->mapped = 1;
+
+	CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4,
+		  printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n",
+			 (unsigned) size));
+
+	return 0;
+}
+
+
+static int cs4281_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int val, mapped, ret;
+
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
+		 "cs4281: cs4281_ioctl(): file=0x%.8x cmd=0x%.8x\n",
+			 (unsigned) file, cmd));
+#if CSDEBUG
+	cs_printioctl(cmd);
+#endif
+	VALIDATE_STATE(s);
+	mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+	    ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			"cs4281: cs4281_ioctl(): SOUND_VERSION=0x%.8x\n",
+				 SOUND_VERSION));
+		return put_user(SOUND_VERSION, (int *) arg);
+
+	case SNDCTL_DSP_SYNC:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_SYNC\n"));
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s,
+					 0 /*file->f_flags & O_NONBLOCK */
+					 );
+		return 0;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
+				DSP_CAP_TRIGGER | DSP_CAP_MMAP,
+				(int *) arg);
+
+	case SNDCTL_DSP_RESET:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_RESET\n"));
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr =
+			    s->dma_dac.count = s->dma_dac.total_bytes =
+			    s->dma_dac.blocks = s->dma_dac.wakeup = 0;
+			prog_codec(s, CS_TYPE_DAC);
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr =
+			    s->dma_adc.count = s->dma_adc.total_bytes =
+			    s->dma_adc.blocks = s->dma_dac.wakeup = 0;
+			prog_codec(s, CS_TYPE_ADC);
+		}
+		return 0;
+
+	case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val));
+		//
+		// support independent capture and playback channels
+		// assume that the file mode bit determines the 
+		// direction of the data flow.
+		//
+		if (file->f_mode & FMODE_READ) {
+			if (val >= 0) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				// program sampling rates 
+				if (val > 48000)
+					val = 48000;
+				if (val < 6300)
+					val = 6300;
+				s->prop_adc.rate = val;
+				prog_codec(s, CS_TYPE_ADC);
+			}
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val >= 0) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				// program sampling rates 
+				if (val > 48000)
+					val = 48000;
+				if (val < 6300)
+					val = 6300;
+				s->prop_dac.rate = val;
+				prog_codec(s, CS_TYPE_DAC);
+			}
+		}
+
+		if (file->f_mode & FMODE_WRITE)
+			val = s->prop_dac.rate;
+		else if (file->f_mode & FMODE_READ)
+			val = s->prop_adc.rate;
+
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_STEREO val=%d\n", val));
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			s->prop_adc.channels = val ? 2 : 1;
+			prog_codec(s, CS_TYPE_ADC);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			s->prop_dac.channels = val ? 2 : 1;
+			prog_codec(s, CS_TYPE_DAC);
+		}
+		return 0;
+
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_CHANNELS val=%d\n",
+				 val));
+		if (val != 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					s->prop_adc.channels = 2;
+				else
+					s->prop_adc.channels = 1;
+				prog_codec(s, CS_TYPE_ADC);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					s->prop_dac.channels = 2;
+				else
+					s->prop_dac.channels = 1;
+				prog_codec(s, CS_TYPE_DAC);
+			}
+		}
+
+		if (file->f_mode & FMODE_WRITE)
+			val = s->prop_dac.channels;
+		else if (file->f_mode & FMODE_READ)
+			val = s->prop_adc.channels;
+
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_GETFMTS:	// Returns a mask 
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			"cs4281: cs4281_ioctl(): DSP_GETFMT val=0x%.8x\n",
+				 AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
+				 AFMT_U8));
+		return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
+				AFMT_U8, (int *) arg);
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_SETFMT val=0x%.8x\n",
+				 val));
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val != AFMT_S16_LE
+				    && val != AFMT_U16_LE && val != AFMT_S8
+				    && val != AFMT_U8)
+					val = AFMT_U8;
+				s->prop_adc.fmt = val;
+				s->prop_adc.fmt_original = s->prop_adc.fmt;
+				prog_codec(s, CS_TYPE_ADC);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val != AFMT_S16_LE
+				    && val != AFMT_U16_LE && val != AFMT_S8
+				    && val != AFMT_U8)
+					val = AFMT_U8;
+				s->prop_dac.fmt = val;
+				s->prop_dac.fmt_original = s->prop_dac.fmt;
+				prog_codec(s, CS_TYPE_DAC);
+			}
+		} else {
+			if (file->f_mode & FMODE_WRITE)
+				val = s->prop_dac.fmt_original;
+			else if (file->f_mode & FMODE_READ)
+				val = s->prop_adc.fmt_original;
+		}
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
+		  "cs4281: cs4281_ioctl(): DSP_SETFMT return val=0x%.8x\n", 
+			val));
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_POST:
+		CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
+			 "cs4281: cs4281_ioctl(): DSP_POST\n"));
+		return 0;
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & s->ena & FMODE_READ)
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & s->ena & FMODE_WRITE)
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready
+				    && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready
+				    && (ret = prog_dmabuf_dac(s)))
+					return ret;
+				start_dac(s);
+			} else
+				stop_dac(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)))
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4281_update_ptr(s,CS_FALSE);
+		abinfo.fragsize = s->dma_dac.fragsize;
+		if (s->dma_dac.mapped)
+			abinfo.bytes = s->dma_dac.dmasize;
+		else
+			abinfo.bytes =
+			    s->dma_dac.dmasize - s->dma_dac.count;
+		abinfo.fragstotal = s->dma_dac.numfrag;
+		abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
+		CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO
+			"cs4281: cs4281_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n",
+				abinfo.fragsize,abinfo.bytes,abinfo.fragstotal,
+				abinfo.fragments));
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &abinfo,
+				    sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)))
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4281_update_ptr(s,CS_FALSE);
+		if (s->conversion) {
+			abinfo.fragsize = s->dma_adc.fragsize / 2;
+			abinfo.bytes = s->dma_adc.count / 2;
+			abinfo.fragstotal = s->dma_adc.numfrag;
+			abinfo.fragments =
+			    abinfo.bytes >> (s->dma_adc.fragshift - 1);
+		} else {
+			abinfo.fragsize = s->dma_adc.fragsize;
+			abinfo.bytes = s->dma_adc.count;
+			abinfo.fragstotal = s->dma_adc.numfrag;
+			abinfo.fragments =
+			    abinfo.bytes >> s->dma_adc.fragshift;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &abinfo,
+				    sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_NONBLOCK:
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4281_update_ptr(s,CS_FALSE);
+		val = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if(!s->dma_adc.ready && prog_dmabuf_adc(s))
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4281_update_ptr(s,CS_FALSE);
+		cinfo.bytes = s->dma_adc.total_bytes;
+		if (s->dma_adc.mapped) {
+			cinfo.blocks =
+			    (cinfo.bytes >> s->dma_adc.fragshift) -
+			    s->dma_adc.blocks;
+			s->dma_adc.blocks =
+			    cinfo.bytes >> s->dma_adc.fragshift;
+		} else {
+			if (s->conversion) {
+				cinfo.blocks =
+				    s->dma_adc.count /
+				    2 >> (s->dma_adc.fragshift - 1);
+			} else
+				cinfo.blocks =
+				    s->dma_adc.count >> s->dma_adc.
+				    fragshift;
+		}
+		if (s->conversion)
+			cinfo.ptr = s->dma_adc.hwptr / 2;
+		else
+			cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize - 1;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4281_update_ptr(s,CS_FALSE);
+		cinfo.bytes = s->dma_dac.total_bytes;
+		if (s->dma_dac.mapped) {
+			cinfo.blocks =
+			    (cinfo.bytes >> s->dma_dac.fragshift) -
+			    s->dma_dac.blocks;
+			s->dma_dac.blocks =
+			    cinfo.bytes >> s->dma_dac.fragshift;
+		} else {
+			cinfo.blocks =
+			    s->dma_dac.count >> s->dma_dac.fragshift;
+		}
+		cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize - 1;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac(s)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *) arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		if (s->conversion)
+			return put_user(s->dma_adc.fragsize / 2,
+					(int *) arg);
+		else
+			return put_user(s->dma_adc.fragsize, (int *) arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		return 0;	// Say OK, but do nothing.
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision)
+		    || (file->f_mode & FMODE_WRITE
+			&& s->dma_dac.subdivision)) return -EINVAL;
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		else if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+	case SOUND_PCM_READ_RATE:
+		if (file->f_mode & FMODE_READ)
+			return put_user(s->prop_adc.rate, (int *) arg);
+		else if (file->f_mode & FMODE_WRITE)
+			return put_user(s->prop_dac.rate, (int *) arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		if (file->f_mode & FMODE_READ)
+			return put_user(s->prop_adc.channels, (int *) arg);
+		else if (file->f_mode & FMODE_WRITE)
+			return put_user(s->prop_dac.channels, (int *) arg);
+
+	case SOUND_PCM_READ_BITS:
+		if (file->f_mode & FMODE_READ)
+			return
+			    put_user(
+				     (s->prop_adc.
+				      fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
+				     (int *) arg);
+		else if (file->f_mode & FMODE_WRITE)
+			return
+			    put_user(
+				     (s->prop_dac.
+				      fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
+				     (int *) arg);
+
+	case SOUND_PCM_WRITE_FILTER:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_READ_FILTER:
+		return -EINVAL;
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+
+static int cs4281_release(struct inode *inode, struct file *file)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+
+	CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO
+		 "cs4281: cs4281_release(): inode=0x%.8x file=0x%.8x f_mode=%d\n",
+			 (unsigned) inode, (unsigned) file, file->f_mode));
+
+	VALIDATE_STATE(s);
+
+	if (file->f_mode & FMODE_WRITE) {
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+		down(&s->open_sem_dac);
+		stop_dac(s);
+		dealloc_dmabuf(s, &s->dma_dac);
+		s->open_mode &= ~FMODE_WRITE;
+		up(&s->open_sem_dac);
+		wake_up(&s->open_wait_dac);
+		MOD_DEC_USE_COUNT;
+	}
+	if (file->f_mode & FMODE_READ) {
+		drain_adc(s, file->f_flags & O_NONBLOCK);
+		down(&s->open_sem_adc);
+		stop_adc(s);
+		dealloc_dmabuf(s, &s->dma_adc);
+		s->open_mode &= ~FMODE_READ;
+		up(&s->open_sem_adc);
+		wake_up(&s->open_wait_adc);
+		MOD_DEC_USE_COUNT;
+	}
+	return 0;
+}
+
+static int cs4281_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct cs4281_state *s=NULL;
+	struct list_head *entry;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+		"cs4281: cs4281_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
+			(unsigned) inode, (unsigned) file, file->f_mode));
+
+	list_for_each(entry, &cs4281_devs)
+	{
+		s = list_entry(entry, struct cs4281_state, list);
+
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+	if (entry == &cs4281_devs)
+		return -ENODEV;
+	if (!s) {
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs4281: cs4281_open(): Error - unable to find audio state struct\n"));
+		return -ENODEV;
+	}
+	VALIDATE_STATE(s);
+	file->private_data = s;
+
+	// wait for device to become free 
+	if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) {
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO
+			 "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n"));
+		return -ENODEV;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		down(&s->open_sem_dac);
+		while (s->open_mode & FMODE_WRITE) {
+			if (file->f_flags & O_NONBLOCK) {
+				up(&s->open_sem_dac);
+				return -EBUSY;
+			}
+			up(&s->open_sem_dac);
+			interruptible_sleep_on(&s->open_wait_dac);
+
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+			down(&s->open_sem_dac);
+		}
+	}
+	if (file->f_mode & FMODE_READ) {
+		down(&s->open_sem_adc);
+		while (s->open_mode & FMODE_READ) {
+			if (file->f_flags & O_NONBLOCK) {
+				up(&s->open_sem_adc);
+				return -EBUSY;
+			}
+			up(&s->open_sem_adc);
+			interruptible_sleep_on(&s->open_wait_adc);
+
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+			down(&s->open_sem_adc);
+		}
+	}
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	if (file->f_mode & FMODE_READ) {
+		s->prop_adc.fmt = AFMT_U8;
+		s->prop_adc.fmt_original = s->prop_adc.fmt;
+		s->prop_adc.channels = 1;
+		s->prop_adc.rate = 8000;
+		s->prop_adc.clkdiv = 96 | 0x80;
+		s->conversion = 0;
+		s->ena &= ~FMODE_READ;
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
+		    s->dma_adc.subdivision = 0;
+		up(&s->open_sem_adc);
+		MOD_INC_USE_COUNT;
+
+		if (prog_dmabuf_adc(s)) {
+			CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
+				"cs4281: adc Program dmabufs failed.\n"));
+			cs4281_release(inode, file);
+			return -ENOMEM;
+		}
+		prog_codec(s, CS_TYPE_ADC);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->prop_dac.fmt = AFMT_U8;
+		s->prop_dac.fmt_original = s->prop_dac.fmt;
+		s->prop_dac.channels = 1;
+		s->prop_dac.rate = 8000;
+		s->prop_dac.clkdiv = 96 | 0x80;
+		s->conversion = 0;
+		s->ena &= ~FMODE_WRITE;
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
+		    s->dma_dac.subdivision = 0;
+		up(&s->open_sem_dac);
+		MOD_INC_USE_COUNT;
+
+		if (prog_dmabuf_dac(s)) {
+			CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
+				"cs4281: dac Program dmabufs failed.\n"));
+			cs4281_release(inode, file);
+			return -ENOMEM;
+		}
+		prog_codec(s, CS_TYPE_DAC);
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2,
+		  printk(KERN_INFO "cs4281: cs4281_open()- 0\n"));
+	return 0;
+}
+
+
+// ******************************************************************************************
+//   Wave (audio) file operations struct.
+// ******************************************************************************************
+static /*const */ struct file_operations cs4281_audio_fops = {
+	llseek:no_llseek,
+	read:cs4281_read,
+	write:cs4281_write,
+	poll:cs4281_poll,
+	ioctl:cs4281_ioctl,
+	mmap:cs4281_mmap,
+	open:cs4281_open,
+	release:cs4281_release,
+};
+
+// --------------------------------------------------------------------- 
+
+// hold spinlock for the following! 
+static void cs4281_handle_midi(struct cs4281_state *s)
+{
+	unsigned char ch;
+	int wake;
+	unsigned temp1;
+
+	wake = 0;
+	while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) {
+		ch = readl(s->pBA0 + BA0_MIDRP);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) {
+		temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff;
+		writel(temp1, s->pBA0 + BA0_MIDWP);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF - 16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+}
+
+
+
+static void cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct cs4281_state *s = (struct cs4281_state *) dev_id;
+	unsigned int temp1;
+
+	// fastpath out, to ease interrupt sharing 
+	temp1 = readl(s->pBA0 + BA0_HISR);	// Get Int Status reg.
+
+	CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
+		  "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1));
+/*
+* If not DMA or MIDI interrupt, then just return.
+*/
+	if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) {
+		writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR);
+		CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO
+			"cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n"));
+		return;
+	}
+
+	if (temp1 & HISR_DMA0)	// If play interrupt,
+		readl(s->pBA0 + BA0_HDSR0);	//   clear the source.
+
+	if (temp1 & HISR_DMA1)	// Same for play.
+		readl(s->pBA0 + BA0_HDSR1);
+	writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR);	// Local EOI
+
+	spin_lock(&s->lock);
+	cs4281_update_ptr(s,CS_TRUE);
+	cs4281_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+// **************************************************************************
+
+static void cs4281_midi_timer(unsigned long data)
+{
+	struct cs4281_state *s = (struct cs4281_state *) data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	cs4281_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->midi.timer.expires = jiffies + 1;
+	add_timer(&s->midi.timer);
+}
+
+
+// --------------------------------------------------------------------- 
+
+static ssize_t cs4281_midi_read(struct file *file, char *buffer,
+				size_t count, loff_t * ppos)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->midi.iwait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+	return ret;
+}
+
+
+static ssize_t cs4281_midi_write(struct file *file, const char *buffer,
+				 size_t count, loff_t * ppos)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0)
+			cs4281_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->midi.owait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		cs4281_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	return ret;
+}
+
+
+static unsigned int cs4281_midi_poll(struct file *file,
+				     struct poll_table_struct *wait)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_flags & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_flags & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_flags & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_flags & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+
+static int cs4281_midi_open(struct inode *inode, struct file *file)
+{
+	unsigned long flags, temp1;
+	unsigned int minor = minor(inode->i_rdev);
+	struct cs4281_state *s=NULL;
+	struct list_head *entry;
+	list_for_each(entry, &cs4281_devs)
+	{
+		s = list_entry(entry, struct cs4281_state, list);
+
+		if (s->dev_midi == minor)
+			break;
+	}
+
+	if (entry == &cs4281_devs)
+		return -ENODEV;
+	if (!s)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs4281: cs4281_open(): Error - unable to find audio state struct\n"));
+		return -ENODEV;
+	}
+	VALIDATE_STATE(s);
+	file->private_data = s;
+	// wait for device to become free 
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		writel(1, s->pBA0 + BA0_MIDCR);	// Reset the interface.
+		writel(0, s->pBA0 + BA0_MIDCR);	// Return to normal mode.
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		writel(0x0000000f, s->pBA0 + BA0_MIDCR);	// Enable transmit, record, ints.
+		temp1 = readl(s->pBA0 + BA0_HIMR);
+		writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR);	// Enable midi int. recognition.
+		writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR);	// Enable interrupts
+		init_timer(&s->midi.timer);
+		s->midi.timer.expires = jiffies + 1;
+		s->midi.timer.data = (unsigned long) s;
+		s->midi.timer.function = cs4281_midi_timer;
+		add_timer(&s->midi.timer);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |=
+	    (file->
+	     f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ |
+					    FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+static int cs4281_midi_release(struct inode *inode, struct file *file)
+{
+	struct cs4281_state *s =
+	    (struct cs4281_state *) file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	if (file->f_mode & FMODE_WRITE) {
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				current->state = TASK_RUNNING;
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				printk(KERN_DEBUG
+				       "cs4281: midi timed out??\n");
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		current->state = TASK_RUNNING;
+	}
+	down(&s->open_sem);
+	s->open_mode &=
+	    (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ |
+						     FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		writel(0, s->pBA0 + BA0_MIDCR);	// Disable Midi interrupts.  
+		del_timer(&s->midi.timer);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+// ******************************************************************************************
+//   Midi file operations struct.
+// ******************************************************************************************
+static /*const */ struct file_operations cs4281_midi_fops = {
+	llseek:no_llseek,
+	read:cs4281_midi_read,
+	write:cs4281_midi_write,
+	poll:cs4281_midi_poll,
+	open:cs4281_midi_open,
+	release:cs4281_midi_release,
+};
+
+
+// --------------------------------------------------------------------- 
+
+// maximum number of devices 
+#define NR_DEVICE 8		// Only eight devices supported currently.
+
+// --------------------------------------------------------------------- 
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+
+	{
+	SOUND_MIXER_WRITE_VOLUME, 0x4040}, {
+	SOUND_MIXER_WRITE_PCM, 0x4040}, {
+	SOUND_MIXER_WRITE_SYNTH, 0x4040}, {
+	SOUND_MIXER_WRITE_CD, 0x4040}, {
+	SOUND_MIXER_WRITE_LINE, 0x4040}, {
+	SOUND_MIXER_WRITE_LINE1, 0x4040}, {
+	SOUND_MIXER_WRITE_RECLEV, 0x0000}, {
+	SOUND_MIXER_WRITE_SPEAKER, 0x4040}, {
+	SOUND_MIXER_WRITE_MIC, 0x0000}
+};
+
+
+#ifndef NOT_CS4281_PM
+void __devinit cs4281_BuildFIFO(
+	struct cs4281_pipeline *p, 
+	struct cs4281_state *s)
+{
+	switch(p->number)
+	{
+		case 0:  /* playback */
+		{
+			p->u32FCRnAddress  =  BA0_FCR0;
+			p->u32FSICnAddress = BA0_FSIC0;
+			p->u32FPDRnAddress = BA0_FPDR0;
+			break;
+		}
+		case 1:  /* capture */
+		{
+			p->u32FCRnAddress  =  BA0_FCR1;
+			p->u32FSICnAddress = BA0_FSIC1;
+			p->u32FPDRnAddress = BA0_FPDR1;
+			break;
+		}
+
+		case 2: 
+		{
+			p->u32FCRnAddress  =  BA0_FCR2;
+			p->u32FSICnAddress = BA0_FSIC2;
+			p->u32FPDRnAddress = BA0_FPDR2;
+			break;
+		}
+		case 3: 
+		{
+			p->u32FCRnAddress  =  BA0_FCR3;
+			p->u32FSICnAddress = BA0_FSIC3;
+			p->u32FPDRnAddress = BA0_FPDR3;
+			break;
+		}
+		default:
+			break;
+	}
+	//
+	// first read the hardware to initialize the member variables
+	//
+	p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress);
+	p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress);
+	p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress);
+
+}
+
+void __devinit cs4281_BuildDMAengine(
+	struct cs4281_pipeline *p, 
+	struct cs4281_state *s)
+{
+/*
+* initialize all the addresses of this pipeline dma info.
+*/
+	switch(p->number)
+	{
+		case 0:  /* playback */
+		{
+			p->u32DBAnAddress = BA0_DBA0;
+			p->u32DCAnAddress = BA0_DCA0;
+			p->u32DBCnAddress = BA0_DBC0;
+			p->u32DCCnAddress = BA0_DCC0;
+			p->u32DMRnAddress = BA0_DMR0;
+			p->u32DCRnAddress = BA0_DCR0;
+			p->u32HDSRnAddress = BA0_HDSR0;
+			break;
+		}
+
+		case 1: /* capture */
+		{
+			p->u32DBAnAddress = BA0_DBA1;
+			p->u32DCAnAddress = BA0_DCA1;
+			p->u32DBCnAddress = BA0_DBC1;
+			p->u32DCCnAddress = BA0_DCC1;
+			p->u32DMRnAddress = BA0_DMR1;
+			p->u32DCRnAddress = BA0_DCR1;
+			p->u32HDSRnAddress = BA0_HDSR1;
+			break;
+		}
+
+		case 2:
+		{
+			p->u32DBAnAddress = BA0_DBA2;
+			p->u32DCAnAddress = BA0_DCA2;
+			p->u32DBCnAddress = BA0_DBC2;
+			p->u32DCCnAddress = BA0_DCC2;
+			p->u32DMRnAddress = BA0_DMR2;
+			p->u32DCRnAddress = BA0_DCR2;
+			p->u32HDSRnAddress = BA0_HDSR2;
+			break;
+		}
+
+		case 3:
+		{
+			p->u32DBAnAddress = BA0_DBA3;
+			p->u32DCAnAddress = BA0_DCA3;
+			p->u32DBCnAddress = BA0_DBC3;
+			p->u32DCCnAddress = BA0_DCC3;
+			p->u32DMRnAddress = BA0_DMR3;
+			p->u32DCRnAddress = BA0_DCR3;
+			p->u32HDSRnAddress = BA0_HDSR3;
+			break;
+		}
+		default:
+			break;
+	}
+
+//
+// Initialize the dma values for this pipeline
+//
+	p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress);
+	p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress);
+	p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress);
+	p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress);
+
+}
+
+void __devinit cs4281_InitPM(struct cs4281_state *s)
+{
+	int i;
+	struct cs4281_pipeline *p;
+
+	for(i=0;i<CS4281_NUMBER_OF_PIPELINES;i++)
+	{
+		p = &s->pl[i];
+		p->number = i;
+		cs4281_BuildDMAengine(p,s);
+		cs4281_BuildFIFO(p,s);
+	/*
+	* currently only  2 pipelines are used
+	* so, only set the valid bit on the playback and capture.
+	*/
+		if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || 
+			(i == CS4281_CAPTURE_PIPELINE_NUMBER))
+			p->flags |= CS4281_PIPELINE_VALID;
+	}
+	s->pm.u32SSPM_BITS = 0x7e;  /* rev c, use 0x7c for rev a or b */
+}
+#endif
+
+static int __devinit cs4281_probe(struct pci_dev *pcidev,
+				  const struct pci_device_id *pciid)
+{
+#ifndef NOT_CS4281_PM
+	struct pm_dev *pmdev;
+#endif
+	struct cs4281_state *s;
+	dma_addr_t dma_mask;
+	mm_segment_t fs;
+	int i, val;
+	unsigned int temp1, temp2;
+
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2,
+		  printk(KERN_INFO "cs4281: probe()+\n"));
+
+	if (pci_enable_device(pcidev)) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4281: pci_enable_device() failed\n"));
+		return -1;
+	}
+	if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) ||
+	    !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			 "cs4281: probe()- Memory region not assigned\n"));
+		return -ENODEV;
+	}
+	if (pcidev->irq == 0) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			 "cs4281: probe() IRQ not assigned\n"));
+		return -ENODEV;
+	}
+	dma_mask = 0xffffffff;	/* this enables playback and recording */
+	i = pci_set_dma_mask(pcidev, dma_mask);
+	if (i) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+		      "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n"));
+		return i;
+	}
+	if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+		      "cs4281: probe() no memory for state struct.\n"));
+		return -1;
+	}
+	memset(s, 0, sizeof(struct cs4281_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->open_wait_adc);
+	init_waitqueue_head(&s->open_wait_dac);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	init_MUTEX(&s->open_sem_adc);
+	init_MUTEX(&s->open_sem_dac);
+	spin_lock_init(&s->lock);
+	s->pBA0phys = pci_resource_start(pcidev, 0);
+	s->pBA1phys = pci_resource_start(pcidev, 1);
+
+	/* Convert phys to linear. */
+	s->pBA0 = ioremap_nocache(s->pBA0phys, 4096);
+	if (!s->pBA0) {
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR
+			 "cs4281: BA0 I/O mapping failed. Skipping part.\n"));
+		goto err_free;
+	}
+	s->pBA1 = ioremap_nocache(s->pBA1phys, 65536);
+	if (!s->pBA1) {
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR
+			 "cs4281: BA1 I/O mapping failed. Skipping part.\n"));
+		goto err_unmap;
+	}
+
+	temp1 = readl(s->pBA0 + BA0_PCICFG00);
+	temp2 = readl(s->pBA0 + BA0_PCICFG04);
+
+	CS_DBGOUT(CS_INIT, 2,
+		  printk(KERN_INFO
+			 "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=0x%.8x pBA1=0x%.8x \n",
+			 (unsigned) temp1, (unsigned) temp2,
+			 (unsigned) s->pBA0, (unsigned) s->pBA1));
+
+	CS_DBGOUT(CS_INIT, 2,
+		  printk(KERN_INFO
+			 "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n",
+			 (unsigned) s->pBA0phys, (unsigned) s->pBA1phys));
+
+#ifndef NOT_CS4281_PM
+	s->pm.flags = CS4281_PM_IDLE;
+#endif
+	temp1 = cs4281_hw_init(s);
+	if (temp1) {
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR
+			 "cs4281: cs4281_hw_init() failed. Skipping part.\n"));
+		goto err_irq;
+	}
+	s->magic = CS4281_MAGIC;
+	s->pcidev = pcidev;
+	s->irq = pcidev->irq;
+	if (request_irq
+	    (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1,
+			  printk(KERN_ERR "cs4281: irq %u in use\n", s->irq));
+		goto err_irq;
+	}
+	if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) <
+	    0) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4281: probe() register_sound_dsp() failed.\n"));
+		goto err_dev1;
+	}
+	if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) <
+	    0) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4281: probe() register_sound_mixer() failed.\n"));
+		goto err_dev2;
+	}
+	if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs4281: probe() register_sound_midi() failed.\n"));
+		goto err_dev3;
+	}
+#ifndef NOT_CS4281_PM
+	cs4281_InitPM(s);
+	pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), cs4281_pm_callback);
+	if (pmdev)
+	{
+		CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO
+			 "cs4281: probe() pm_register() succeeded (0x%x).\n",
+				(unsigned)pmdev));
+		pmdev->data = s;
+	}
+	else
+	{
+		CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 0, printk(KERN_INFO
+			 "cs4281: probe() pm_register() failed (0x%x).\n",
+				(unsigned)pmdev));
+		s->pm.flags |= CS4281_PM_NOT_REGISTERED;
+	}
+#endif
+
+	pci_set_master(pcidev);	// enable bus mastering 
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE;
+	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val);
+	for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val);
+	}
+	val = 1;		// enable mic preamp 
+	mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val);
+	set_fs(fs);
+
+	pci_set_drvdata(pcidev, s);
+	list_add(&s->list, &cs4281_devs);
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		"cs4281: probe()- device allocated successfully\n"));
+	return 0;
+
+      err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+      err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+      err_dev1:
+	free_irq(s->irq, s);
+      err_irq:
+	iounmap(s->pBA1);
+      err_unmap:
+	iounmap(s->pBA0);
+      err_free:
+	kfree(s);
+
+	CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO
+		"cs4281: probe()- no device allocated\n"));
+	return -ENODEV;
+} // probe_cs4281
+
+
+// --------------------------------------------------------------------- 
+
+static void __devinit cs4281_remove(struct pci_dev *pci_dev)
+{
+	struct cs4281_state *s = pci_get_drvdata(pci_dev);
+	// stop DMA controller 
+	synchronize_irq();
+	free_irq(s->irq, s);
+	unregister_sound_dsp(s->dev_audio);
+	unregister_sound_mixer(s->dev_mixer);
+	unregister_sound_midi(s->dev_midi);
+	iounmap(s->pBA1);
+	iounmap(s->pBA0);
+	pci_set_drvdata(pci_dev,NULL);
+	list_del(&s->list);
+	kfree(s);
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		 "cs4281: cs4281_remove()-: remove successful\n"));
+}
+
+static struct pci_device_id cs4281_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CRYSTAL_CS4281,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl);
+
+struct pci_driver cs4281_pci_driver = {
+	name:"cs4281",
+	id_table:cs4281_pci_tbl,
+	probe:cs4281_probe,
+	remove:cs4281_remove,
+	suspend:CS4281_SUSPEND_TBL,
+	resume:CS4281_RESUME_TBL,
+};
+
+int __init cs4281_init_module(void)
+{
+	int rtn = 0;
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO 
+		"cs4281: cs4281_init_module()+ \n"));
+	if (!pci_present()) {	/* No PCI bus in this machine! */
+		CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+			"cs4281: cs4281_init_module()- no pci bus found\n"));
+		return -ENODEV;
+	}
+	printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " "
+	       __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION,
+	       CS4281_ARCH);
+	rtn = pci_module_init(&cs4281_pci_driver);
+
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn));
+	return rtn;
+}
+
+void __exit cs4281_cleanup_module(void)
+{
+	pci_unregister_driver(&cs4281_pci_driver);
+#ifndef NOT_CS4281_PM
+	cs_pm_unregister_all(cs4281_pm_callback);
+#endif
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n"));
+}
+// --------------------------------------------------------------------- 
+
+MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com");
+MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver");
+MODULE_LICENSE("GPL");
+
+// --------------------------------------------------------------------- 
+
+module_init(cs4281_init_module);
+module_exit(cs4281_cleanup_module);
+
+#ifndef MODULE
+int __init init_cs4281(void)
+{
+	return cs4281_init_module();
+}
+#endif
diff -Nru linux/sound/oss/cs4281/cs4281pm-24.c linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm-24.c
--- linux/sound/oss/cs4281/cs4281pm-24.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm-24.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,84 @@
+/*******************************************************************************
+*
+*      "cs4281pm.c" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 12/22/00 trw - new file. 
+*
+*******************************************************************************/
+
+#ifndef NOT_CS4281_PM
+#include <linux/pm.h>
+
+#define cs_pm_register(a, b, c) pm_register((a), (b), (c));
+#define cs_pm_unregister_all(a) pm_unregister_all((a));
+
+int cs4281_suspend(struct cs4281_state *s);
+int cs4281_resume(struct cs4281_state *s);
+/* 
+* for now (12/22/00) only enable the pm_register PM support.
+* allow these table entries to be null.
+#define CS4281_SUSPEND_TBL cs4281_suspend_tbl
+#define CS4281_RESUME_TBL cs4281_resume_tbl
+*/
+#define CS4281_SUSPEND_TBL cs4281_null
+#define CS4281_RESUME_TBL cs4281_null
+
+int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	struct cs4281_state *state;
+
+	CS_DBGOUT(CS_PM, 2, printk(KERN_INFO 
+		"cs4281: cs4281_pm_callback dev=0x%x rqst=0x%x state=%d\n",
+			(unsigned)dev,(unsigned)rqst,(unsigned)data));
+	state = (struct cs4281_state *) dev->data;
+	if (state) {
+		switch(rqst) {
+			case PM_SUSPEND:
+				CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
+					"cs4281: PM suspend request\n"));
+				if(cs4281_suspend(state))
+				{
+				    CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					"cs4281: PM suspend request refused\n"));
+					return 1; 
+				}
+				break;
+			case PM_RESUME:
+				CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
+					"cs4281: PM resume request\n"));
+				if(cs4281_resume(state))
+				{
+				    CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					"cs4281: PM resume request refused\n"));
+					return 1;
+				}
+				break;
+		}
+	}
+
+	return 0;
+}
+
+#else /* CS4281_PM */
+#define CS4281_SUSPEND_TBL cs4281_null
+#define CS4281_RESUME_TBL cs4281_null
+#endif /* CS4281_PM */
+
diff -Nru linux/sound/oss/cs4281/cs4281pm.h linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm.h
--- linux/sound/oss/cs4281/cs4281pm.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,74 @@
+#ifndef NOT_CS4281_PM
+/*******************************************************************************
+*
+*      "cs4281pm.h" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 12/22/00 trw - new file. 
+*
+*******************************************************************************/
+/* general pm definitions */
+#define CS4281_AC97_HIGHESTREGTORESTORE 0x26
+#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1)
+
+/* pipeline definitions */
+#define CS4281_NUMBER_OF_PIPELINES 	4
+#define CS4281_PIPELINE_VALID 		0x0001
+#define CS4281_PLAYBACK_PIPELINE_NUMBER	0x0000
+#define CS4281_CAPTURE_PIPELINE_NUMBER 	0x0001
+
+/* PM state defintions */
+#define CS4281_PM_NOT_REGISTERED	0x1000
+#define CS4281_PM_IDLE			0x0001
+#define CS4281_PM_SUSPENDING		0x0002
+#define CS4281_PM_SUSPENDED		0x0004
+#define CS4281_PM_RESUMING		0x0008
+#define CS4281_PM_RESUMED		0x0010
+
+struct cs4281_pm {
+	unsigned long flags;
+	u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue;
+	u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR;
+	u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save;
+	u32 u32SSPM_BITS;
+	u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS];
+	u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono;
+	u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose;
+	u32 u32hwptr_playback,u32hwptr_capture;
+};
+
+struct cs4281_pipeline {
+	unsigned flags;
+	unsigned number;
+	u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue;
+	u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress;
+	u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress;
+	u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save;
+	u32 u32DCCn_Save,u32DCAn_Save;
+/* 
+* technically, these are fifo variables, but just map the 
+* first fifo with the first pipeline and then use the fifo
+* variables inside of the pipeline struct.
+*/
+	u32 u32FCRn_Save,u32FSICn_Save;
+	u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress;
+	u32 u32FPDRnValue,u32FPDRnAddress;
+};
+#endif
diff -Nru linux/sound/oss/cs461x.h linux-2.4.19-pre5-mjc/sound/oss/cs461x.h
--- linux/sound/oss/cs461x.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs461x.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1691 @@
+#ifndef __CS461X_H
+#define __CS461X_H
+
+/*
+ *  Copyright (c) by Cirrus Logic Corporation <pcaudio@crystal.cirrus.com>
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for Cirrus Logic CS461x chips
+ *
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS            0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4610
+#define PCI_DEVICE_ID_CIRRUS_4610       0x6001
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4612
+#define PCI_DEVICE_ID_CIRRUS_4612       0x6003
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4615
+#define PCI_DEVICE_ID_CIRRUS_4615       0x6004
+#endif
+
+/*
+ *  Direct registers
+ */
+
+/*
+ *  The following define the offsets of the registers accessed via base address
+ *  register zero on the CS461x part.
+ */
+#define BA0_HISR				0x00000000
+#define BA0_HSR0                                0x00000004
+#define BA0_HICR                                0x00000008
+#define BA0_DMSR                                0x00000100
+#define BA0_HSAR                                0x00000110
+#define BA0_HDAR                                0x00000114
+#define BA0_HDMR                                0x00000118
+#define BA0_HDCR                                0x0000011C
+#define BA0_PFMC                                0x00000200
+#define BA0_PFCV1                               0x00000204
+#define BA0_PFCV2                               0x00000208
+#define BA0_PCICFG00                            0x00000300
+#define BA0_PCICFG04                            0x00000304
+#define BA0_PCICFG08                            0x00000308
+#define BA0_PCICFG0C                            0x0000030C
+#define BA0_PCICFG10                            0x00000310
+#define BA0_PCICFG14                            0x00000314
+#define BA0_PCICFG18                            0x00000318
+#define BA0_PCICFG1C                            0x0000031C
+#define BA0_PCICFG20                            0x00000320
+#define BA0_PCICFG24                            0x00000324
+#define BA0_PCICFG28                            0x00000328
+#define BA0_PCICFG2C                            0x0000032C
+#define BA0_PCICFG30                            0x00000330
+#define BA0_PCICFG34                            0x00000334
+#define BA0_PCICFG38                            0x00000338
+#define BA0_PCICFG3C                            0x0000033C
+#define BA0_CLKCR1                              0x00000400
+#define BA0_CLKCR2                              0x00000404
+#define BA0_PLLM                                0x00000408
+#define BA0_PLLCC                               0x0000040C
+#define BA0_FRR                                 0x00000410 
+#define BA0_CFL1                                0x00000414
+#define BA0_CFL2                                0x00000418
+#define BA0_SERMC1                              0x00000420
+#define BA0_SERMC2                              0x00000424
+#define BA0_SERC1                               0x00000428
+#define BA0_SERC2                               0x0000042C
+#define BA0_SERC3                               0x00000430
+#define BA0_SERC4                               0x00000434
+#define BA0_SERC5                               0x00000438
+#define BA0_SERBSP                              0x0000043C
+#define BA0_SERBST                              0x00000440
+#define BA0_SERBCM                              0x00000444
+#define BA0_SERBAD                              0x00000448
+#define BA0_SERBCF                              0x0000044C
+#define BA0_SERBWP                              0x00000450
+#define BA0_SERBRP                              0x00000454
+#ifndef NO_CS4612
+#define BA0_ASER_FADDR                          0x00000458
+#endif
+#define BA0_ACCTL                               0x00000460
+#define BA0_ACSTS                               0x00000464
+#define BA0_ACOSV                               0x00000468
+#define BA0_ACCAD                               0x0000046C
+#define BA0_ACCDA                               0x00000470
+#define BA0_ACISV                               0x00000474
+#define BA0_ACSAD                               0x00000478
+#define BA0_ACSDA                               0x0000047C
+#define BA0_JSPT                                0x00000480
+#define BA0_JSCTL                               0x00000484
+#define BA0_JSC1                                0x00000488
+#define BA0_JSC2                                0x0000048C
+#define BA0_MIDCR                               0x00000490
+#define BA0_MIDSR                               0x00000494
+#define BA0_MIDWP                               0x00000498
+#define BA0_MIDRP                               0x0000049C
+#define BA0_JSIO                                0x000004A0
+#ifndef NO_CS4612
+#define BA0_ASER_MASTER                         0x000004A4
+#endif
+#define BA0_CFGI                                0x000004B0
+#define BA0_SSVID                               0x000004B4
+#define BA0_GPIOR                               0x000004B8
+#ifndef NO_CS4612
+#define BA0_EGPIODR                             0x000004BC
+#define BA0_EGPIOPTR                            0x000004C0
+#define BA0_EGPIOTR                             0x000004C4
+#define BA0_EGPIOWR                             0x000004C8
+#define BA0_EGPIOSR                             0x000004CC
+#define BA0_SERC6                               0x000004D0
+#define BA0_SERC7                               0x000004D4
+#define BA0_SERACC                              0x000004D8
+#define BA0_ACCTL2                              0x000004E0
+#define BA0_ACSTS2                              0x000004E4
+#define BA0_ACOSV2                              0x000004E8
+#define BA0_ACCAD2                              0x000004EC
+#define BA0_ACCDA2                              0x000004F0
+#define BA0_ACISV2                              0x000004F4
+#define BA0_ACSAD2                              0x000004F8
+#define BA0_ACSDA2                              0x000004FC
+#define BA0_IOTAC0                              0x00000500
+#define BA0_IOTAC1                              0x00000504
+#define BA0_IOTAC2                              0x00000508
+#define BA0_IOTAC3                              0x0000050C
+#define BA0_IOTAC4                              0x00000510
+#define BA0_IOTAC5                              0x00000514
+#define BA0_IOTAC6                              0x00000518
+#define BA0_IOTAC7                              0x0000051C
+#define BA0_IOTAC8                              0x00000520
+#define BA0_IOTAC9                              0x00000524
+#define BA0_IOTAC10                             0x00000528
+#define BA0_IOTAC11                             0x0000052C
+#define BA0_IOTFR0                              0x00000540
+#define BA0_IOTFR1                              0x00000544
+#define BA0_IOTFR2                              0x00000548
+#define BA0_IOTFR3                              0x0000054C
+#define BA0_IOTFR4                              0x00000550
+#define BA0_IOTFR5                              0x00000554
+#define BA0_IOTFR6                              0x00000558
+#define BA0_IOTFR7                              0x0000055C
+#define BA0_IOTFIFO                             0x00000580
+#define BA0_IOTRRD                              0x00000584
+#define BA0_IOTFP                               0x00000588
+#define BA0_IOTCR                               0x0000058C
+#define BA0_DPCID                               0x00000590
+#define BA0_DPCIA                               0x00000594
+#define BA0_DPCIC                               0x00000598
+#define BA0_PCPCIR                              0x00000600
+#define BA0_PCPCIG                              0x00000604
+#define BA0_PCPCIEN                             0x00000608
+#define BA0_EPCIPMC                             0x00000610
+#endif
+
+/*
+ *  The following define the offsets of the registers and memories accessed via
+ *  base address register one on the CS461x part.
+ */
+#define BA1_SP_DMEM0                            0x00000000
+#define BA1_SP_DMEM1                            0x00010000
+#define BA1_SP_PMEM                             0x00020000
+#define BA1_SP_REG				0x00030000
+#define BA1_SPCR                                0x00030000
+#define BA1_DREG                                0x00030004
+#define BA1_DSRWP                               0x00030008
+#define BA1_TWPR                                0x0003000C
+#define BA1_SPWR                                0x00030010
+#define BA1_SPIR                                0x00030014
+#define BA1_FGR1                                0x00030020
+#define BA1_SPCS                                0x00030028
+#define BA1_SDSR                                0x0003002C
+#define BA1_FRMT                                0x00030030
+#define BA1_FRCC                                0x00030034
+#define BA1_FRSC                                0x00030038
+#define BA1_OMNI_MEM                            0x000E0000
+
+/*
+ *  The following defines are for the flags in the host interrupt status
+ *  register.
+ */
+#define HISR_VC_MASK                            0x0000FFFF
+#define HISR_VC0                                0x00000001
+#define HISR_VC1                                0x00000002
+#define HISR_VC2                                0x00000004
+#define HISR_VC3                                0x00000008
+#define HISR_VC4                                0x00000010
+#define HISR_VC5                                0x00000020
+#define HISR_VC6                                0x00000040
+#define HISR_VC7                                0x00000080
+#define HISR_VC8                                0x00000100
+#define HISR_VC9                                0x00000200
+#define HISR_VC10                               0x00000400
+#define HISR_VC11                               0x00000800
+#define HISR_VC12                               0x00001000
+#define HISR_VC13                               0x00002000
+#define HISR_VC14                               0x00004000
+#define HISR_VC15                               0x00008000
+#define HISR_INT0                               0x00010000
+#define HISR_INT1                               0x00020000
+#define HISR_DMAI                               0x00040000
+#define HISR_FROVR                              0x00080000
+#define HISR_MIDI                               0x00100000
+#ifdef NO_CS4612
+#define HISR_RESERVED                           0x0FE00000
+#else
+#define HISR_SBINT                              0x00200000
+#define HISR_RESERVED                           0x0FC00000
+#endif
+#define HISR_H0P                                0x40000000
+#define HISR_INTENA                             0x80000000
+
+/*
+ *  The following defines are for the flags in the host signal register 0.
+ */
+#define HSR0_VC_MASK                            0xFFFFFFFF
+#define HSR0_VC16                               0x00000001
+#define HSR0_VC17                               0x00000002
+#define HSR0_VC18                               0x00000004
+#define HSR0_VC19                               0x00000008
+#define HSR0_VC20                               0x00000010
+#define HSR0_VC21                               0x00000020
+#define HSR0_VC22                               0x00000040
+#define HSR0_VC23                               0x00000080
+#define HSR0_VC24                               0x00000100
+#define HSR0_VC25                               0x00000200
+#define HSR0_VC26                               0x00000400
+#define HSR0_VC27                               0x00000800
+#define HSR0_VC28                               0x00001000
+#define HSR0_VC29                               0x00002000
+#define HSR0_VC30                               0x00004000
+#define HSR0_VC31                               0x00008000
+#define HSR0_VC32                               0x00010000
+#define HSR0_VC33                               0x00020000
+#define HSR0_VC34                               0x00040000
+#define HSR0_VC35                               0x00080000
+#define HSR0_VC36                               0x00100000
+#define HSR0_VC37                               0x00200000
+#define HSR0_VC38                               0x00400000
+#define HSR0_VC39                               0x00800000
+#define HSR0_VC40                               0x01000000
+#define HSR0_VC41                               0x02000000
+#define HSR0_VC42                               0x04000000
+#define HSR0_VC43                               0x08000000
+#define HSR0_VC44                               0x10000000
+#define HSR0_VC45                               0x20000000
+#define HSR0_VC46                               0x40000000
+#define HSR0_VC47                               0x80000000
+
+/*
+ *  The following defines are for the flags in the host interrupt control
+ *  register.
+ */
+#define HICR_IEV                                0x00000001
+#define HICR_CHGM                               0x00000002
+
+/*
+ *  The following defines are for the flags in the DMA status register.
+ */
+#define DMSR_HP                                 0x00000001
+#define DMSR_HR                                 0x00000002
+#define DMSR_SP                                 0x00000004
+#define DMSR_SR                                 0x00000008
+
+/*
+ *  The following defines are for the flags in the host DMA source address
+ *  register.
+ */
+#define HSAR_HOST_ADDR_MASK                     0xFFFFFFFF
+#define HSAR_DSP_ADDR_MASK                      0x0000FFFF
+#define HSAR_MEMID_MASK                         0x000F0000
+#define HSAR_MEMID_SP_DMEM0                     0x00000000
+#define HSAR_MEMID_SP_DMEM1                     0x00010000
+#define HSAR_MEMID_SP_PMEM                      0x00020000
+#define HSAR_MEMID_SP_DEBUG                     0x00030000
+#define HSAR_MEMID_OMNI_MEM                     0x000E0000
+#define HSAR_END                                0x40000000
+#define HSAR_ERR                                0x80000000
+
+/*
+ *  The following defines are for the flags in the host DMA destination address
+ *  register.
+ */
+#define HDAR_HOST_ADDR_MASK                     0xFFFFFFFF
+#define HDAR_DSP_ADDR_MASK                      0x0000FFFF
+#define HDAR_MEMID_MASK                         0x000F0000
+#define HDAR_MEMID_SP_DMEM0                     0x00000000
+#define HDAR_MEMID_SP_DMEM1                     0x00010000
+#define HDAR_MEMID_SP_PMEM                      0x00020000
+#define HDAR_MEMID_SP_DEBUG                     0x00030000
+#define HDAR_MEMID_OMNI_MEM                     0x000E0000
+#define HDAR_END                                0x40000000
+#define HDAR_ERR                                0x80000000
+
+/*
+ *  The following defines are for the flags in the host DMA control register.
+ */
+#define HDMR_AC_MASK                            0x0000F000
+#define HDMR_AC_8_16                            0x00001000
+#define HDMR_AC_M_S                             0x00002000
+#define HDMR_AC_B_L                             0x00004000
+#define HDMR_AC_S_U                             0x00008000
+
+/*
+ *  The following defines are for the flags in the host DMA control register.
+ */
+#define HDCR_COUNT_MASK                         0x000003FF
+#define HDCR_DONE                               0x00004000
+#define HDCR_OPT                                0x00008000
+#define HDCR_WBD                                0x00400000
+#define HDCR_WBS                                0x00800000
+#define HDCR_DMS_MASK                           0x07000000
+#define HDCR_DMS_LINEAR                         0x00000000
+#define HDCR_DMS_16_DWORDS                      0x01000000
+#define HDCR_DMS_32_DWORDS                      0x02000000
+#define HDCR_DMS_64_DWORDS                      0x03000000
+#define HDCR_DMS_128_DWORDS                     0x04000000
+#define HDCR_DMS_256_DWORDS                     0x05000000
+#define HDCR_DMS_512_DWORDS                     0x06000000
+#define HDCR_DMS_1024_DWORDS                    0x07000000
+#define HDCR_DH                                 0x08000000
+#define HDCR_SMS_MASK                           0x70000000
+#define HDCR_SMS_LINEAR                         0x00000000
+#define HDCR_SMS_16_DWORDS                      0x10000000
+#define HDCR_SMS_32_DWORDS                      0x20000000
+#define HDCR_SMS_64_DWORDS                      0x30000000
+#define HDCR_SMS_128_DWORDS                     0x40000000
+#define HDCR_SMS_256_DWORDS                     0x50000000
+#define HDCR_SMS_512_DWORDS                     0x60000000
+#define HDCR_SMS_1024_DWORDS                    0x70000000
+#define HDCR_SH                                 0x80000000
+#define HDCR_COUNT_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the performance monitor control
+ *  register.
+ */
+#define PFMC_C1SS_MASK                          0x0000001F
+#define PFMC_C1EV                               0x00000020
+#define PFMC_C1RS                               0x00008000
+#define PFMC_C2SS_MASK                          0x001F0000
+#define PFMC_C2EV                               0x00200000
+#define PFMC_C2RS                               0x80000000
+#define PFMC_C1SS_SHIFT                         0
+#define PFMC_C2SS_SHIFT                         16
+#define PFMC_BUS_GRANT                          0
+#define PFMC_GRANT_AFTER_REQ                    1
+#define PFMC_TRANSACTION                        2
+#define PFMC_DWORD_TRANSFER                     3
+#define PFMC_SLAVE_READ                         4
+#define PFMC_SLAVE_WRITE                        5
+#define PFMC_PREEMPTION                         6
+#define PFMC_DISCONNECT_RETRY                   7
+#define PFMC_INTERRUPT                          8
+#define PFMC_BUS_OWNERSHIP                      9
+#define PFMC_TRANSACTION_LAG                    10
+#define PFMC_PCI_CLOCK                          11
+#define PFMC_SERIAL_CLOCK                       12
+#define PFMC_SP_CLOCK                           13
+
+/*
+ *  The following defines are for the flags in the performance counter value 1
+ *  register.
+ */
+#define PFCV1_PC1V_MASK                         0xFFFFFFFF
+#define PFCV1_PC1V_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the performance counter value 2
+ *  register.
+ */
+#define PFCV2_PC2V_MASK                         0xFFFFFFFF
+#define PFCV2_PC2V_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the clock control register 1.
+ */
+#define CLKCR1_OSCS                             0x00000001
+#define CLKCR1_OSCP                             0x00000002
+#define CLKCR1_PLLSS_MASK                       0x0000000C
+#define CLKCR1_PLLSS_SERIAL                     0x00000000
+#define CLKCR1_PLLSS_CRYSTAL                    0x00000004
+#define CLKCR1_PLLSS_PCI                        0x00000008
+#define CLKCR1_PLLSS_RESERVED                   0x0000000C
+#define CLKCR1_PLLP                             0x00000010
+#define CLKCR1_SWCE                             0x00000020
+#define CLKCR1_PLLOS                            0x00000040
+
+/*
+ *  The following defines are for the flags in the clock control register 2.
+ */
+#define CLKCR2_PDIVS_MASK                       0x0000000F
+#define CLKCR2_PDIVS_1                          0x00000001
+#define CLKCR2_PDIVS_2                          0x00000002
+#define CLKCR2_PDIVS_4                          0x00000004
+#define CLKCR2_PDIVS_7                          0x00000007
+#define CLKCR2_PDIVS_8                          0x00000008
+#define CLKCR2_PDIVS_16                         0x00000000
+
+/*
+ *  The following defines are for the flags in the PLL multiplier register.
+ */
+#define PLLM_MASK                               0x000000FF
+#define PLLM_SHIFT                              0
+
+/*
+ *  The following defines are for the flags in the PLL capacitor coefficient
+ *  register.
+ */
+#define PLLCC_CDR_MASK                          0x00000007
+#ifndef NO_CS4610
+#define PLLCC_CDR_240_350_MHZ                   0x00000000
+#define PLLCC_CDR_184_265_MHZ                   0x00000001
+#define PLLCC_CDR_144_205_MHZ                   0x00000002
+#define PLLCC_CDR_111_160_MHZ                   0x00000003
+#define PLLCC_CDR_87_123_MHZ                    0x00000004
+#define PLLCC_CDR_67_96_MHZ                     0x00000005
+#define PLLCC_CDR_52_74_MHZ                     0x00000006
+#define PLLCC_CDR_45_58_MHZ                     0x00000007
+#endif
+#ifndef NO_CS4612
+#define PLLCC_CDR_271_398_MHZ                   0x00000000
+#define PLLCC_CDR_227_330_MHZ                   0x00000001
+#define PLLCC_CDR_167_239_MHZ                   0x00000002
+#define PLLCC_CDR_150_215_MHZ                   0x00000003
+#define PLLCC_CDR_107_154_MHZ                   0x00000004
+#define PLLCC_CDR_98_140_MHZ                    0x00000005
+#define PLLCC_CDR_73_104_MHZ                    0x00000006
+#define PLLCC_CDR_63_90_MHZ                     0x00000007
+#endif
+#define PLLCC_LPF_MASK                          0x000000F8
+#ifndef NO_CS4610
+#define PLLCC_LPF_23850_60000_KHZ               0x00000000
+#define PLLCC_LPF_7960_26290_KHZ                0x00000008
+#define PLLCC_LPF_4160_10980_KHZ                0x00000018
+#define PLLCC_LPF_1740_4580_KHZ                 0x00000038
+#define PLLCC_LPF_724_1910_KHZ                  0x00000078
+#define PLLCC_LPF_317_798_KHZ                   0x000000F8
+#endif
+#ifndef NO_CS4612
+#define PLLCC_LPF_25580_64530_KHZ               0x00000000
+#define PLLCC_LPF_14360_37270_KHZ               0x00000008
+#define PLLCC_LPF_6100_16020_KHZ                0x00000018
+#define PLLCC_LPF_2540_6690_KHZ                 0x00000038
+#define PLLCC_LPF_1050_2780_KHZ                 0x00000078
+#define PLLCC_LPF_450_1160_KHZ                  0x000000F8
+#endif
+
+/*
+ *  The following defines are for the flags in the feature reporting register.
+ */
+#define FRR_FAB_MASK                            0x00000003
+#define FRR_MASK_MASK                           0x0000001C
+#ifdef NO_CS4612
+#define FRR_CFOP_MASK                           0x000000E0
+#else
+#define FRR_CFOP_MASK                           0x00000FE0
+#endif
+#define FRR_CFOP_NOT_DVD                        0x00000020
+#define FRR_CFOP_A3D                            0x00000040
+#define FRR_CFOP_128_PIN                        0x00000080
+#ifndef NO_CS4612
+#define FRR_CFOP_CS4280                         0x00000800
+#endif
+#define FRR_FAB_SHIFT                           0
+#define FRR_MASK_SHIFT                          2
+#define FRR_CFOP_SHIFT                          5
+
+/*
+ *  The following defines are for the flags in the configuration load 1
+ *  register.
+ */
+#define CFL1_CLOCK_SOURCE_MASK                  0x00000003
+#define CFL1_CLOCK_SOURCE_CS423X                0x00000000
+#define CFL1_CLOCK_SOURCE_AC97                  0x00000001
+#define CFL1_CLOCK_SOURCE_CRYSTAL               0x00000002
+#define CFL1_CLOCK_SOURCE_DUAL_AC97             0x00000003
+#define CFL1_VALID_DATA_MASK                    0x000000FF
+
+/*
+ *  The following defines are for the flags in the configuration load 2
+ *  register.
+ */
+#define CFL2_VALID_DATA_MASK                    0x000000FF
+
+/*
+ *  The following defines are for the flags in the serial port master control
+ *  register 1.
+ */
+#define SERMC1_MSPE                             0x00000001
+#define SERMC1_PTC_MASK                         0x0000000E
+#define SERMC1_PTC_CS423X                       0x00000000
+#define SERMC1_PTC_AC97                         0x00000002
+#define SERMC1_PTC_DAC                          0x00000004
+#define SERMC1_PLB                              0x00000010
+#define SERMC1_XLB                              0x00000020
+
+/*
+ *  The following defines are for the flags in the serial port master control
+ *  register 2.
+ */
+#define SERMC2_LROE                             0x00000001
+#define SERMC2_MCOE                             0x00000002
+#define SERMC2_MCDIV                            0x00000004
+
+/*
+ *  The following defines are for the flags in the serial port 1 configuration
+ *  register.
+ */
+#define SERC1_SO1EN                             0x00000001
+#define SERC1_SO1F_MASK                         0x0000000E
+#define SERC1_SO1F_CS423X                       0x00000000
+#define SERC1_SO1F_AC97                         0x00000002
+#define SERC1_SO1F_DAC                          0x00000004
+#define SERC1_SO1F_SPDIF                        0x00000006
+
+/*
+ *  The following defines are for the flags in the serial port 2 configuration
+ *  register.
+ */
+#define SERC2_SI1EN                             0x00000001
+#define SERC2_SI1F_MASK                         0x0000000E
+#define SERC2_SI1F_CS423X                       0x00000000
+#define SERC2_SI1F_AC97                         0x00000002
+#define SERC2_SI1F_ADC                          0x00000004
+#define SERC2_SI1F_SPDIF                        0x00000006
+
+/*
+ *  The following defines are for the flags in the serial port 3 configuration
+ *  register.
+ */
+#define SERC3_SO2EN                             0x00000001
+#define SERC3_SO2F_MASK                         0x00000006
+#define SERC3_SO2F_DAC                          0x00000000
+#define SERC3_SO2F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port 4 configuration
+ *  register.
+ */
+#define SERC4_SO3EN                             0x00000001
+#define SERC4_SO3F_MASK                         0x00000006
+#define SERC4_SO3F_DAC                          0x00000000
+#define SERC4_SO3F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port 5 configuration
+ *  register.
+ */
+#define SERC5_SI2EN                             0x00000001
+#define SERC5_SI2F_MASK                         0x00000006
+#define SERC5_SI2F_ADC                          0x00000000
+#define SERC5_SI2F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor sample
+ *  pointer register.
+ */
+#define SERBSP_FSP_MASK                         0x0000000F
+#define SERBSP_FSP_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor status
+ *  register.
+ */
+#define SERBST_RRDY                             0x00000001
+#define SERBST_WBSY                             0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor command
+ *  register.
+ */
+#define SERBCM_RDC                              0x00000001
+#define SERBCM_WRC                              0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor address
+ *  register.
+ */
+#ifdef NO_CS4612
+#define SERBAD_FAD_MASK                         0x000000FF
+#else
+#define SERBAD_FAD_MASK                         0x000001FF
+#endif
+#define SERBAD_FAD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor
+ *  configuration register.
+ */
+#define SERBCF_HBP                              0x00000001
+
+/*
+ *  The following defines are for the flags in the serial port backdoor write
+ *  port register.
+ */
+#define SERBWP_FWD_MASK                         0x000FFFFF
+#define SERBWP_FWD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor read
+ *  port register.
+ */
+#define SERBRP_FRD_MASK                         0x000FFFFF
+#define SERBRP_FRD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the async FIFO address register.
+ */
+#ifndef NO_CS4612
+#define ASER_FADDR_A1_MASK                      0x000001FF
+#define ASER_FADDR_EN1                          0x00008000
+#define ASER_FADDR_A2_MASK                      0x01FF0000
+#define ASER_FADDR_EN2                          0x80000000
+#define ASER_FADDR_A1_SHIFT                     0
+#define ASER_FADDR_A2_SHIFT                     16
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 control register.
+ */
+#define ACCTL_RSTN                              0x00000001
+#define ACCTL_ESYN                              0x00000002
+#define ACCTL_VFRM                              0x00000004
+#define ACCTL_DCV                               0x00000008
+#define ACCTL_CRW                               0x00000010
+#define ACCTL_ASYN                              0x00000020
+#ifndef NO_CS4612
+#define ACCTL_TC                                0x00000040
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status register.
+ */
+#define ACSTS_CRDY                              0x00000001
+#define ACSTS_VSTS                              0x00000002
+#ifndef NO_CS4612
+#define ACSTS_WKUP                              0x00000004
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 output slot valid
+ *  register.
+ */
+#define ACOSV_SLV3                              0x00000001
+#define ACOSV_SLV4                              0x00000002
+#define ACOSV_SLV5                              0x00000004
+#define ACOSV_SLV6                              0x00000008
+#define ACOSV_SLV7                              0x00000010
+#define ACOSV_SLV8                              0x00000020
+#define ACOSV_SLV9                              0x00000040
+#define ACOSV_SLV10                             0x00000080
+#define ACOSV_SLV11                             0x00000100
+#define ACOSV_SLV12                             0x00000200
+
+/*
+ *  The following defines are for the flags in the AC97 command address
+ *  register.
+ */
+#define ACCAD_CI_MASK                           0x0000007F
+#define ACCAD_CI_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 command data register.
+ */
+#define ACCDA_CD_MASK                           0x0000FFFF
+#define ACCDA_CD_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 input slot valid
+ *  register.
+ */
+#define ACISV_ISV3                              0x00000001
+#define ACISV_ISV4                              0x00000002
+#define ACISV_ISV5                              0x00000004
+#define ACISV_ISV6                              0x00000008
+#define ACISV_ISV7                              0x00000010
+#define ACISV_ISV8                              0x00000020
+#define ACISV_ISV9                              0x00000040
+#define ACISV_ISV10                             0x00000080
+#define ACISV_ISV11                             0x00000100
+#define ACISV_ISV12                             0x00000200
+
+/*
+ *  The following defines are for the flags in the AC97 status address
+ *  register.
+ */
+#define ACSAD_SI_MASK                           0x0000007F
+#define ACSAD_SI_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 status data register.
+ */
+#define ACSDA_SD_MASK                           0x0000FFFF
+#define ACSDA_SD_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the joystick poll/trigger
+ *  register.
+ */
+#define JSPT_CAX                                0x00000001
+#define JSPT_CAY                                0x00000002
+#define JSPT_CBX                                0x00000004
+#define JSPT_CBY                                0x00000008
+#define JSPT_BA1                                0x00000010
+#define JSPT_BA2                                0x00000020
+#define JSPT_BB1                                0x00000040
+#define JSPT_BB2                                0x00000080
+
+/*
+ *  The following defines are for the flags in the joystick control register.
+ */
+#define JSCTL_SP_MASK                           0x00000003
+#define JSCTL_SP_SLOW                           0x00000000
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002
+#define JSCTL_SP_FAST                           0x00000003
+#define JSCTL_ARE                               0x00000004
+
+/*
+ *  The following defines are for the flags in the joystick coordinate pair 1
+ *  readback register.
+ */
+#define JSC1_Y1V_MASK                           0x0000FFFF
+#define JSC1_X1V_MASK                           0xFFFF0000
+#define JSC1_Y1V_SHIFT                          0
+#define JSC1_X1V_SHIFT                          16
+
+/*
+ *  The following defines are for the flags in the joystick coordinate pair 2
+ *  readback register.
+ */
+#define JSC2_Y2V_MASK                           0x0000FFFF
+#define JSC2_X2V_MASK                           0xFFFF0000
+#define JSC2_Y2V_SHIFT                          0
+#define JSC2_X2V_SHIFT                          16
+
+/*
+ *  The following defines are for the flags in the MIDI control register.
+ */
+#define MIDCR_TXE                               0x00000001	/* Enable transmitting. */
+#define MIDCR_RXE                               0x00000002	/* Enable receiving. */
+#define MIDCR_RIE                               0x00000004	/* Interrupt upon tx ready. */
+#define MIDCR_TIE                               0x00000008	/* Interrupt upon rx ready. */
+#define MIDCR_MLB                               0x00000010	/* Enable midi loopback. */
+#define MIDCR_MRST                              0x00000020	/* Reset interface. */
+
+/*
+ *  The following defines are for the flags in the MIDI status register.
+ */
+#define MIDSR_TBF                               0x00000001	/* Tx FIFO is full. */
+#define MIDSR_RBE                               0x00000002	/* Rx FIFO is empty. */
+
+/*
+ *  The following defines are for the flags in the MIDI write port register.
+ */
+#define MIDWP_MWD_MASK                          0x000000FF
+#define MIDWP_MWD_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the MIDI read port register.
+ */
+#define MIDRP_MRD_MASK                          0x000000FF
+#define MIDRP_MRD_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the joystick GPIO register.
+ */
+#define JSIO_DAX                                0x00000001
+#define JSIO_DAY                                0x00000002
+#define JSIO_DBX                                0x00000004
+#define JSIO_DBY                                0x00000008
+#define JSIO_AXOE                               0x00000010
+#define JSIO_AYOE                               0x00000020
+#define JSIO_BXOE                               0x00000040
+#define JSIO_BYOE                               0x00000080
+
+/*
+ *  The following defines are for the flags in the master async/sync serial
+ *  port enable register.
+ */
+#ifndef NO_CS4612
+#define ASER_MASTER_ME                          0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the configuration interface
+ *  register.
+ */
+#define CFGI_CLK                                0x00000001
+#define CFGI_DOUT                               0x00000002
+#define CFGI_DIN_EEN                            0x00000004
+#define CFGI_EELD                               0x00000008
+
+/*
+ *  The following defines are for the flags in the subsystem ID and vendor ID
+ *  register.
+ */
+#define SSVID_VID_MASK                          0x0000FFFF
+#define SSVID_SID_MASK                          0xFFFF0000
+#define SSVID_VID_SHIFT                         0
+#define SSVID_SID_SHIFT                         16
+
+/*
+ *  The following defines are for the flags in the GPIO pin interface register.
+ */
+#define GPIOR_VOLDN                             0x00000001
+#define GPIOR_VOLUP                             0x00000002
+#define GPIOR_SI2D                              0x00000004
+#define GPIOR_SI2OE                             0x00000008
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin direction
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIODR_GPOE0                           0x00000001
+#define EGPIODR_GPOE1                           0x00000002
+#define EGPIODR_GPOE2                           0x00000004
+#define EGPIODR_GPOE3                           0x00000008
+#define EGPIODR_GPOE4                           0x00000010
+#define EGPIODR_GPOE5                           0x00000020
+#define EGPIODR_GPOE6                           0x00000040
+#define EGPIODR_GPOE7                           0x00000080
+#define EGPIODR_GPOE8                           0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin polarity/
+ *  type register.
+ */
+#ifndef NO_CS4612
+#define EGPIOPTR_GPPT0                          0x00000001
+#define EGPIOPTR_GPPT1                          0x00000002
+#define EGPIOPTR_GPPT2                          0x00000004
+#define EGPIOPTR_GPPT3                          0x00000008
+#define EGPIOPTR_GPPT4                          0x00000010
+#define EGPIOPTR_GPPT5                          0x00000020
+#define EGPIOPTR_GPPT6                          0x00000040
+#define EGPIOPTR_GPPT7                          0x00000080
+#define EGPIOPTR_GPPT8                          0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin sticky
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOTR_GPS0                            0x00000001
+#define EGPIOTR_GPS1                            0x00000002
+#define EGPIOTR_GPS2                            0x00000004
+#define EGPIOTR_GPS3                            0x00000008
+#define EGPIOTR_GPS4                            0x00000010
+#define EGPIOTR_GPS5                            0x00000020
+#define EGPIOTR_GPS6                            0x00000040
+#define EGPIOTR_GPS7                            0x00000080
+#define EGPIOTR_GPS8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO ping wakeup
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOWR_GPW0                            0x00000001
+#define EGPIOWR_GPW1                            0x00000002
+#define EGPIOWR_GPW2                            0x00000004
+#define EGPIOWR_GPW3                            0x00000008
+#define EGPIOWR_GPW4                            0x00000010
+#define EGPIOWR_GPW5                            0x00000020
+#define EGPIOWR_GPW6                            0x00000040
+#define EGPIOWR_GPW7                            0x00000080
+#define EGPIOWR_GPW8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin status
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOSR_GPS0                            0x00000001
+#define EGPIOSR_GPS1                            0x00000002
+#define EGPIOSR_GPS2                            0x00000004
+#define EGPIOSR_GPS3                            0x00000008
+#define EGPIOSR_GPS4                            0x00000010
+#define EGPIOSR_GPS5                            0x00000020
+#define EGPIOSR_GPS6                            0x00000040
+#define EGPIOSR_GPS7                            0x00000080
+#define EGPIOSR_GPS8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port 6 configuration
+ *  register.
+ */
+#ifndef NO_CS4612
+#define SERC6_ASDO2EN                           0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port 7 configuration
+ *  register.
+ */
+#ifndef NO_CS4612
+#define SERC7_ASDI2EN                           0x00000001
+#define SERC7_POSILB                            0x00000002
+#define SERC7_SIPOLB                            0x00000004
+#define SERC7_SOSILB                            0x00000008
+#define SERC7_SISOLB                            0x00000010
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port AC link
+ *  configuration register.
+ */
+#ifndef NO_CS4612
+#define SERACC_CODEC_TYPE_MASK                  0x00000001
+#define SERACC_CODEC_TYPE_1_03                  0x00000000
+#define SERACC_CODEC_TYPE_2_0                   0x00000001
+#define SERACC_TWO_CODECS                       0x00000002
+#define SERACC_MDM                              0x00000004
+#define SERACC_HSP                              0x00000008
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 control register 2.
+ */
+#ifndef NO_CS4612
+#define ACCTL2_RSTN                             0x00000001
+#define ACCTL2_ESYN                             0x00000002
+#define ACCTL2_VFRM                             0x00000004
+#define ACCTL2_DCV                              0x00000008
+#define ACCTL2_CRW                              0x00000010
+#define ACCTL2_ASYN                             0x00000020
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status register 2.
+ */
+#ifndef NO_CS4612
+#define ACSTS2_CRDY                             0x00000001
+#define ACSTS2_VSTS                             0x00000002
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 output slot valid
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACOSV2_SLV3                             0x00000001
+#define ACOSV2_SLV4                             0x00000002
+#define ACOSV2_SLV5                             0x00000004
+#define ACOSV2_SLV6                             0x00000008
+#define ACOSV2_SLV7                             0x00000010
+#define ACOSV2_SLV8                             0x00000020
+#define ACOSV2_SLV9                             0x00000040
+#define ACOSV2_SLV10                            0x00000080
+#define ACOSV2_SLV11                            0x00000100
+#define ACOSV2_SLV12                            0x00000200
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 command address
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACCAD2_CI_MASK                          0x0000007F
+#define ACCAD2_CI_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 command data register
+ *  2.
+ */
+#ifndef NO_CS4612
+#define ACCDA2_CD_MASK                          0x0000FFFF
+#define ACCDA2_CD_SHIFT                         0  
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 input slot valid
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACISV2_ISV3                             0x00000001
+#define ACISV2_ISV4                             0x00000002
+#define ACISV2_ISV5                             0x00000004
+#define ACISV2_ISV6                             0x00000008
+#define ACISV2_ISV7                             0x00000010
+#define ACISV2_ISV8                             0x00000020
+#define ACISV2_ISV9                             0x00000040
+#define ACISV2_ISV10                            0x00000080
+#define ACISV2_ISV11                            0x00000100
+#define ACISV2_ISV12                            0x00000200
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status address
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACSAD2_SI_MASK                          0x0000007F
+#define ACSAD2_SI_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status data register 2.
+ */
+#ifndef NO_CS4612
+#define ACSDA2_SD_MASK                          0x0000FFFF
+#define ACSDA2_SD_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap address and control
+ *  registers (all 12).
+ */
+#ifndef NO_CS4612
+#define IOTAC_SA_MASK                           0x0000FFFF
+#define IOTAC_MSK_MASK                          0x000F0000
+#define IOTAC_IODC_MASK                         0x06000000
+#define IOTAC_IODC_16_BIT                       0x00000000
+#define IOTAC_IODC_10_BIT                       0x02000000
+#define IOTAC_IODC_12_BIT                       0x04000000
+#define IOTAC_WSPI                              0x08000000
+#define IOTAC_RSPI                              0x10000000
+#define IOTAC_WSE                               0x20000000
+#define IOTAC_WE                                0x40000000
+#define IOTAC_RE                                0x80000000
+#define IOTAC_SA_SHIFT                          0
+#define IOTAC_MSK_SHIFT                         16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap fast read registers
+ *  (all 8).
+ */
+#ifndef NO_CS4612
+#define IOTFR_D_MASK                            0x0000FFFF
+#define IOTFR_A_MASK                            0x000F0000
+#define IOTFR_R_MASK                            0x0F000000
+#define IOTFR_ALL                               0x40000000
+#define IOTFR_VL                                0x80000000
+#define IOTFR_D_SHIFT                           0
+#define IOTFR_A_SHIFT                           16
+#define IOTFR_R_SHIFT                           24
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap FIFO register.
+ */
+#ifndef NO_CS4612
+#define IOTFIFO_BA_MASK                         0x00003FFF
+#define IOTFIFO_S_MASK                          0x00FF0000
+#define IOTFIFO_OF                              0x40000000
+#define IOTFIFO_SPIOF                           0x80000000
+#define IOTFIFO_BA_SHIFT                        0
+#define IOTFIFO_S_SHIFT                         16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap retry read data
+ *  register.
+ */
+#ifndef NO_CS4612
+#define IOTRRD_D_MASK                           0x0000FFFF
+#define IOTRRD_RDV                              0x80000000
+#define IOTRRD_D_SHIFT                          0
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap FIFO pointer
+ *  register.
+ */
+#ifndef NO_CS4612
+#define IOTFP_CA_MASK                           0x00003FFF
+#define IOTFP_PA_MASK                           0x3FFF0000
+#define IOTFP_CA_SHIFT                          0
+#define IOTFP_PA_SHIFT                          16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap control register.
+ */
+#ifndef NO_CS4612
+#define IOTCR_ITD                               0x00000001
+#define IOTCR_HRV                               0x00000002
+#define IOTCR_SRV                               0x00000004
+#define IOTCR_DTI                               0x00000008
+#define IOTCR_DFI                               0x00000010
+#define IOTCR_DDP                               0x00000020
+#define IOTCR_JTE                               0x00000040
+#define IOTCR_PPE                               0x00000080
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI data register.
+ */
+#ifndef NO_CS4612
+#define DPCID_D_MASK                            0xFFFFFFFF
+#define DPCID_D_SHIFT                           0
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI address register.
+ */
+#ifndef NO_CS4612
+#define DPCIA_A_MASK                            0xFFFFFFFF
+#define DPCIA_A_SHIFT                           0
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI command register.
+ */
+#ifndef NO_CS4612
+#define DPCIC_C_MASK                            0x0000000F
+#define DPCIC_C_IOREAD                          0x00000002
+#define DPCIC_C_IOWRITE                         0x00000003
+#define DPCIC_BE_MASK                           0x000000F0
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI request register.
+ */
+#ifndef NO_CS4612
+#define PCPCIR_RDC_MASK                         0x00000007
+#define PCPCIR_C_MASK                           0x00007000
+#define PCPCIR_REQ                              0x00008000
+#define PCPCIR_RDC_SHIFT                        0
+#define PCPCIR_C_SHIFT                          12
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI grant register.
+ */ 
+#ifndef NO_CS4612
+#define PCPCIG_GDC_MASK                         0x00000007
+#define PCPCIG_VL                               0x00008000
+#define PCPCIG_GDC_SHIFT                        0
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI master enable
+ *  register.
+ */
+#ifndef NO_CS4612
+#define PCPCIEN_EN                              0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the extended PCI power
+ *  management control register.
+ */
+#ifndef NO_CS4612
+#define EPCIPMC_GWU                             0x00000001
+#define EPCIPMC_FSPC                            0x00000002
+#endif 
+
+/*
+ *  The following defines are for the flags in the SP control register.
+ */
+#define SPCR_RUN                                0x00000001
+#define SPCR_STPFR                              0x00000002
+#define SPCR_RUNFR                              0x00000004
+#define SPCR_TICK                               0x00000008
+#define SPCR_DRQEN                              0x00000020
+#define SPCR_RSTSP                              0x00000040
+#define SPCR_OREN                               0x00000080
+#ifndef NO_CS4612
+#define SPCR_PCIINT                             0x00000100
+#define SPCR_OINTD                              0x00000200
+#define SPCR_CRE                                0x00008000
+#endif
+
+/*
+ *  The following defines are for the flags in the debug index register.
+ */
+#define DREG_REGID_MASK                         0x0000007F
+#define DREG_DEBUG                              0x00000080
+#define DREG_RGBK_MASK                          0x00000700
+#define DREG_TRAP                               0x00000800
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_TRAPX                              0x00001000
+#endif
+#endif
+#define DREG_REGID_SHIFT                        0
+#define DREG_RGBK_SHIFT                         8
+#define DREG_RGBK_REGID_MASK                    0x0000077F
+#define DREG_REGID_R0                           0x00000010
+#define DREG_REGID_R1                           0x00000011
+#define DREG_REGID_R2                           0x00000012
+#define DREG_REGID_R3                           0x00000013
+#define DREG_REGID_R4                           0x00000014
+#define DREG_REGID_R5                           0x00000015
+#define DREG_REGID_R6                           0x00000016
+#define DREG_REGID_R7                           0x00000017
+#define DREG_REGID_R8                           0x00000018
+#define DREG_REGID_R9                           0x00000019
+#define DREG_REGID_RA                           0x0000001A
+#define DREG_REGID_RB                           0x0000001B
+#define DREG_REGID_RC                           0x0000001C
+#define DREG_REGID_RD                           0x0000001D
+#define DREG_REGID_RE                           0x0000001E
+#define DREG_REGID_RF                           0x0000001F
+#define DREG_REGID_RA_BUS_LOW                   0x00000020
+#define DREG_REGID_RA_BUS_HIGH                  0x00000038
+#define DREG_REGID_YBUS_LOW                     0x00000050
+#define DREG_REGID_YBUS_HIGH                    0x00000058
+#define DREG_REGID_TRAP_0                       0x00000100
+#define DREG_REGID_TRAP_1                       0x00000101
+#define DREG_REGID_TRAP_2                       0x00000102
+#define DREG_REGID_TRAP_3                       0x00000103
+#define DREG_REGID_TRAP_4                       0x00000104
+#define DREG_REGID_TRAP_5                       0x00000105
+#define DREG_REGID_TRAP_6                       0x00000106
+#define DREG_REGID_TRAP_7                       0x00000107
+#define DREG_REGID_INDIRECT_ADDRESS             0x0000010E
+#define DREG_REGID_TOP_OF_STACK                 0x0000010F
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_8                       0x00000110
+#define DREG_REGID_TRAP_9                       0x00000111
+#define DREG_REGID_TRAP_10                      0x00000112
+#define DREG_REGID_TRAP_11                      0x00000113
+#define DREG_REGID_TRAP_12                      0x00000114
+#define DREG_REGID_TRAP_13                      0x00000115
+#define DREG_REGID_TRAP_14                      0x00000116
+#define DREG_REGID_TRAP_15                      0x00000117
+#define DREG_REGID_TRAP_16                      0x00000118
+#define DREG_REGID_TRAP_17                      0x00000119
+#define DREG_REGID_TRAP_18                      0x0000011A
+#define DREG_REGID_TRAP_19                      0x0000011B
+#define DREG_REGID_TRAP_20                      0x0000011C
+#define DREG_REGID_TRAP_21                      0x0000011D
+#define DREG_REGID_TRAP_22                      0x0000011E
+#define DREG_REGID_TRAP_23                      0x0000011F
+#endif
+#endif
+#define DREG_REGID_RSA0_LOW                     0x00000200
+#define DREG_REGID_RSA0_HIGH                    0x00000201
+#define DREG_REGID_RSA1_LOW                     0x00000202
+#define DREG_REGID_RSA1_HIGH                    0x00000203
+#define DREG_REGID_RSA2                         0x00000204
+#define DREG_REGID_RSA3                         0x00000205
+#define DREG_REGID_RSI0_LOW                     0x00000206
+#define DREG_REGID_RSI0_HIGH                    0x00000207
+#define DREG_REGID_RSI1                         0x00000208
+#define DREG_REGID_RSI2                         0x00000209
+#define DREG_REGID_SAGUSTATUS                   0x0000020A
+#define DREG_REGID_RSCONFIG01_LOW               0x0000020B
+#define DREG_REGID_RSCONFIG01_HIGH              0x0000020C
+#define DREG_REGID_RSCONFIG23_LOW               0x0000020D
+#define DREG_REGID_RSCONFIG23_HIGH              0x0000020E
+#define DREG_REGID_RSDMA01E                     0x0000020F
+#define DREG_REGID_RSDMA23E                     0x00000210
+#define DREG_REGID_RSD0_LOW                     0x00000211
+#define DREG_REGID_RSD0_HIGH                    0x00000212
+#define DREG_REGID_RSD1_LOW                     0x00000213
+#define DREG_REGID_RSD1_HIGH                    0x00000214
+#define DREG_REGID_RSD2_LOW                     0x00000215
+#define DREG_REGID_RSD2_HIGH                    0x00000216
+#define DREG_REGID_RSD3_LOW                     0x00000217
+#define DREG_REGID_RSD3_HIGH                    0x00000218
+#define DREG_REGID_SRAR_HIGH                    0x0000021A
+#define DREG_REGID_SRAR_LOW                     0x0000021B
+#define DREG_REGID_DMA_STATE                    0x0000021C
+#define DREG_REGID_CURRENT_DMA_STREAM           0x0000021D
+#define DREG_REGID_NEXT_DMA_STREAM              0x0000021E
+#define DREG_REGID_CPU_STATUS                   0x00000300
+#define DREG_REGID_MAC_MODE                     0x00000301
+#define DREG_REGID_STACK_AND_REPEAT             0x00000302
+#define DREG_REGID_INDEX0                       0x00000304
+#define DREG_REGID_INDEX1                       0x00000305
+#define DREG_REGID_DMA_STATE_0_3                0x00000400
+#define DREG_REGID_DMA_STATE_4_7                0x00000404
+#define DREG_REGID_DMA_STATE_8_11               0x00000408
+#define DREG_REGID_DMA_STATE_12_15              0x0000040C
+#define DREG_REGID_DMA_STATE_16_19              0x00000410
+#define DREG_REGID_DMA_STATE_20_23              0x00000414
+#define DREG_REGID_DMA_STATE_24_27              0x00000418
+#define DREG_REGID_DMA_STATE_28_31              0x0000041C
+#define DREG_REGID_DMA_STATE_32_35              0x00000420
+#define DREG_REGID_DMA_STATE_36_39              0x00000424
+#define DREG_REGID_DMA_STATE_40_43              0x00000428
+#define DREG_REGID_DMA_STATE_44_47              0x0000042C
+#define DREG_REGID_DMA_STATE_48_51              0x00000430
+#define DREG_REGID_DMA_STATE_52_55              0x00000434
+#define DREG_REGID_DMA_STATE_56_59              0x00000438
+#define DREG_REGID_DMA_STATE_60_63              0x0000043C
+#define DREG_REGID_DMA_STATE_64_67              0x00000440
+#define DREG_REGID_DMA_STATE_68_71              0x00000444
+#define DREG_REGID_DMA_STATE_72_75              0x00000448
+#define DREG_REGID_DMA_STATE_76_79              0x0000044C
+#define DREG_REGID_DMA_STATE_80_83              0x00000450
+#define DREG_REGID_DMA_STATE_84_87              0x00000454
+#define DREG_REGID_DMA_STATE_88_91              0x00000458
+#define DREG_REGID_DMA_STATE_92_95              0x0000045C
+#define DREG_REGID_TRAP_SELECT                  0x00000500
+#define DREG_REGID_TRAP_WRITE_0                 0x00000500
+#define DREG_REGID_TRAP_WRITE_1                 0x00000501
+#define DREG_REGID_TRAP_WRITE_2                 0x00000502
+#define DREG_REGID_TRAP_WRITE_3                 0x00000503
+#define DREG_REGID_TRAP_WRITE_4                 0x00000504
+#define DREG_REGID_TRAP_WRITE_5                 0x00000505
+#define DREG_REGID_TRAP_WRITE_6                 0x00000506
+#define DREG_REGID_TRAP_WRITE_7                 0x00000507
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_WRITE_8                 0x00000510
+#define DREG_REGID_TRAP_WRITE_9                 0x00000511
+#define DREG_REGID_TRAP_WRITE_10                0x00000512
+#define DREG_REGID_TRAP_WRITE_11                0x00000513
+#define DREG_REGID_TRAP_WRITE_12                0x00000514
+#define DREG_REGID_TRAP_WRITE_13                0x00000515
+#define DREG_REGID_TRAP_WRITE_14                0x00000516
+#define DREG_REGID_TRAP_WRITE_15                0x00000517
+#define DREG_REGID_TRAP_WRITE_16                0x00000518
+#define DREG_REGID_TRAP_WRITE_17                0x00000519
+#define DREG_REGID_TRAP_WRITE_18                0x0000051A
+#define DREG_REGID_TRAP_WRITE_19                0x0000051B
+#define DREG_REGID_TRAP_WRITE_20                0x0000051C
+#define DREG_REGID_TRAP_WRITE_21                0x0000051D
+#define DREG_REGID_TRAP_WRITE_22                0x0000051E
+#define DREG_REGID_TRAP_WRITE_23                0x0000051F
+#endif
+#endif
+#define DREG_REGID_MAC0_ACC0_LOW                0x00000600
+#define DREG_REGID_MAC0_ACC1_LOW                0x00000601
+#define DREG_REGID_MAC0_ACC2_LOW                0x00000602
+#define DREG_REGID_MAC0_ACC3_LOW                0x00000603
+#define DREG_REGID_MAC1_ACC0_LOW                0x00000604
+#define DREG_REGID_MAC1_ACC1_LOW                0x00000605
+#define DREG_REGID_MAC1_ACC2_LOW                0x00000606
+#define DREG_REGID_MAC1_ACC3_LOW                0x00000607
+#define DREG_REGID_MAC0_ACC0_MID                0x00000608
+#define DREG_REGID_MAC0_ACC1_MID                0x00000609
+#define DREG_REGID_MAC0_ACC2_MID                0x0000060A
+#define DREG_REGID_MAC0_ACC3_MID                0x0000060B
+#define DREG_REGID_MAC1_ACC0_MID                0x0000060C
+#define DREG_REGID_MAC1_ACC1_MID                0x0000060D
+#define DREG_REGID_MAC1_ACC2_MID                0x0000060E
+#define DREG_REGID_MAC1_ACC3_MID                0x0000060F
+#define DREG_REGID_MAC0_ACC0_HIGH               0x00000610
+#define DREG_REGID_MAC0_ACC1_HIGH               0x00000611
+#define DREG_REGID_MAC0_ACC2_HIGH               0x00000612
+#define DREG_REGID_MAC0_ACC3_HIGH               0x00000613
+#define DREG_REGID_MAC1_ACC0_HIGH               0x00000614
+#define DREG_REGID_MAC1_ACC1_HIGH               0x00000615
+#define DREG_REGID_MAC1_ACC2_HIGH               0x00000616
+#define DREG_REGID_MAC1_ACC3_HIGH               0x00000617
+#define DREG_REGID_RSHOUT_LOW                   0x00000620
+#define DREG_REGID_RSHOUT_MID                   0x00000628
+#define DREG_REGID_RSHOUT_HIGH                  0x00000630
+
+/*
+ *  The following defines are for the flags in the DMA stream requestor write
+ */
+#define DSRWP_DSR_MASK                          0x0000000F
+#define DSRWP_DSR_BG_RQ                         0x00000001
+#define DSRWP_DSR_PRIORITY_MASK                 0x00000006
+#define DSRWP_DSR_PRIORITY_0                    0x00000000
+#define DSRWP_DSR_PRIORITY_1                    0x00000002
+#define DSRWP_DSR_PRIORITY_2                    0x00000004
+#define DSRWP_DSR_PRIORITY_3                    0x00000006
+#define DSRWP_DSR_RQ_PENDING                    0x00000008
+
+/*
+ *  The following defines are for the flags in the trap write port register.
+ */
+#define TWPR_TW_MASK                            0x0000FFFF
+#define TWPR_TW_SHIFT                           0
+
+/*
+ *  The following defines are for the flags in the stack pointer write
+ *  register.
+ */
+#define SPWR_STKP_MASK                          0x0000000F
+#define SPWR_STKP_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the SP interrupt register.
+ */
+#define SPIR_FRI                                0x00000001
+#define SPIR_DOI                                0x00000002
+#define SPIR_GPI2                               0x00000004
+#define SPIR_GPI3                               0x00000008
+#define SPIR_IP0                                0x00000010
+#define SPIR_IP1                                0x00000020
+#define SPIR_IP2                                0x00000040
+#define SPIR_IP3                                0x00000080
+
+/*
+ *  The following defines are for the flags in the functional group 1 register.
+ */
+#define FGR1_F1S_MASK                           0x0000FFFF
+#define FGR1_F1S_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the SP clock status register.
+ */
+#define SPCS_FRI                                0x00000001
+#define SPCS_DOI                                0x00000002
+#define SPCS_GPI2                               0x00000004
+#define SPCS_GPI3                               0x00000008
+#define SPCS_IP0                                0x00000010
+#define SPCS_IP1                                0x00000020
+#define SPCS_IP2                                0x00000040
+#define SPCS_IP3                                0x00000080
+#define SPCS_SPRUN                              0x00000100
+#define SPCS_SLEEP                              0x00000200
+#define SPCS_FG                                 0x00000400
+#define SPCS_ORUN                               0x00000800
+#define SPCS_IRQ                                0x00001000
+#define SPCS_FGN_MASK                           0x0000E000
+#define SPCS_FGN_SHIFT                          13
+
+/*
+ *  The following defines are for the flags in the SP DMA requestor status
+ *  register.
+ */
+#define SDSR_DCS_MASK                           0x000000FF
+#define SDSR_DCS_SHIFT                          0
+#define SDSR_DCS_NONE                           0x00000007
+
+/*
+ *  The following defines are for the flags in the frame timer register.
+ */
+#define FRMT_FTV_MASK                           0x0000FFFF
+#define FRMT_FTV_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the frame timer current count
+ *  register.
+ */
+#define FRCC_FCC_MASK                           0x0000FFFF
+#define FRCC_FCC_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the frame timer save count
+ *  register.
+ */
+#define FRSC_FCS_MASK                           0x0000FFFF
+#define FRSC_FCS_SHIFT                          0
+
+/*
+ *  The following define the various flags stored in the scatter/gather
+ *  descriptors.
+ */
+#define DMA_SG_NEXT_ENTRY_MASK                  0x00000FF8
+#define DMA_SG_SAMPLE_END_MASK                  0x0FFF0000
+#define DMA_SG_SAMPLE_END_FLAG                  0x10000000
+#define DMA_SG_LOOP_END_FLAG                    0x20000000
+#define DMA_SG_SIGNAL_END_FLAG                  0x40000000
+#define DMA_SG_SIGNAL_PAGE_FLAG                 0x80000000
+#define DMA_SG_NEXT_ENTRY_SHIFT                 3
+#define DMA_SG_SAMPLE_END_SHIFT                 16
+
+/*
+ *  The following define the offsets of the fields within the on-chip generic
+ *  DMA requestor.
+ */
+#define DMA_RQ_CONTROL1                         0x00000000
+#define DMA_RQ_CONTROL2                         0x00000004
+#define DMA_RQ_SOURCE_ADDR                      0x00000008
+#define DMA_RQ_DESTINATION_ADDR                 0x0000000C
+#define DMA_RQ_NEXT_PAGE_ADDR                   0x00000010
+#define DMA_RQ_NEXT_PAGE_SGDESC                 0x00000014
+#define DMA_RQ_LOOP_START_ADDR                  0x00000018
+#define DMA_RQ_POST_LOOP_ADDR                   0x0000001C
+#define DMA_RQ_PAGE_MAP_ADDR                    0x00000020
+
+/*
+ *  The following defines are for the flags in the first control word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C1_COUNT_MASK                    0x000003FF
+#define DMA_RQ_C1_DESTINATION_SCATTER           0x00001000
+#define DMA_RQ_C1_SOURCE_GATHER                 0x00002000
+#define DMA_RQ_C1_DONE_FLAG                     0x00004000
+#define DMA_RQ_C1_OPTIMIZE_STATE                0x00008000
+#define DMA_RQ_C1_SAMPLE_END_STATE_MASK         0x00030000
+#define DMA_RQ_C1_FULL_PAGE                     0x00000000
+#define DMA_RQ_C1_BEFORE_SAMPLE_END             0x00010000
+#define DMA_RQ_C1_PAGE_MAP_ERROR                0x00020000
+#define DMA_RQ_C1_AT_SAMPLE_END                 0x00030000
+#define DMA_RQ_C1_LOOP_END_STATE_MASK           0x000C0000
+#define DMA_RQ_C1_NOT_LOOP_END                  0x00000000
+#define DMA_RQ_C1_BEFORE_LOOP_END               0x00040000
+#define DMA_RQ_C1_2PAGE_LOOP_BEGIN              0x00080000
+#define DMA_RQ_C1_LOOP_BEGIN                    0x000C0000
+#define DMA_RQ_C1_PAGE_MAP_MASK                 0x00300000
+#define DMA_RQ_C1_PM_NONE_PENDING               0x00000000
+#define DMA_RQ_C1_PM_NEXT_PENDING               0x00100000
+#define DMA_RQ_C1_PM_RESERVED                   0x00200000
+#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING          0x00300000
+#define DMA_RQ_C1_WRITEBACK_DEST_FLAG           0x00400000
+#define DMA_RQ_C1_WRITEBACK_SRC_FLAG            0x00800000
+#define DMA_RQ_C1_DEST_SIZE_MASK                0x07000000
+#define DMA_RQ_C1_DEST_LINEAR                   0x00000000
+#define DMA_RQ_C1_DEST_MOD16                    0x01000000
+#define DMA_RQ_C1_DEST_MOD32                    0x02000000
+#define DMA_RQ_C1_DEST_MOD64                    0x03000000
+#define DMA_RQ_C1_DEST_MOD128                   0x04000000
+#define DMA_RQ_C1_DEST_MOD256                   0x05000000
+#define DMA_RQ_C1_DEST_MOD512                   0x06000000
+#define DMA_RQ_C1_DEST_MOD1024                  0x07000000
+#define DMA_RQ_C1_DEST_ON_HOST                  0x08000000
+#define DMA_RQ_C1_SOURCE_SIZE_MASK              0x70000000
+#define DMA_RQ_C1_SOURCE_LINEAR                 0x00000000
+#define DMA_RQ_C1_SOURCE_MOD16                  0x10000000
+#define DMA_RQ_C1_SOURCE_MOD32                  0x20000000
+#define DMA_RQ_C1_SOURCE_MOD64                  0x30000000
+#define DMA_RQ_C1_SOURCE_MOD128                 0x40000000
+#define DMA_RQ_C1_SOURCE_MOD256                 0x50000000
+#define DMA_RQ_C1_SOURCE_MOD512                 0x60000000
+#define DMA_RQ_C1_SOURCE_MOD1024                0x70000000
+#define DMA_RQ_C1_SOURCE_ON_HOST                0x80000000
+#define DMA_RQ_C1_COUNT_SHIFT                   0
+
+/*
+ *  The following defines are for the flags in the second control word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK          0x0000003F
+#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK           0x00000300
+#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL             0x00000000
+#define DMA_RQ_C2_SIGNAL_EVERY_DMA              0x00000100
+#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG        0x00000200
+#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG          0x00000300
+#define DMA_RQ_C2_AUDIO_CONVERT_MASK            0x0000F000
+#define DMA_RQ_C2_AC_NONE                       0x00000000
+#define DMA_RQ_C2_AC_8_TO_16_BIT                0x00001000
+#define DMA_RQ_C2_AC_MONO_TO_STEREO             0x00002000
+#define DMA_RQ_C2_AC_ENDIAN_CONVERT             0x00004000
+#define DMA_RQ_C2_AC_SIGNED_CONVERT             0x00008000
+#define DMA_RQ_C2_LOOP_END_MASK                 0x0FFF0000
+#define DMA_RQ_C2_LOOP_MASK                     0x30000000
+#define DMA_RQ_C2_NO_LOOP                       0x00000000
+#define DMA_RQ_C2_ONE_PAGE_LOOP                 0x10000000
+#define DMA_RQ_C2_TWO_PAGE_LOOP                 0x20000000
+#define DMA_RQ_C2_MULTI_PAGE_LOOP               0x30000000
+#define DMA_RQ_C2_SIGNAL_LOOP_BACK              0x40000000
+#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE        0x80000000
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT         0
+#define DMA_RQ_C2_LOOP_END_SHIFT                16
+
+/*
+ *  The following defines are for the flags in the source and destination words
+ *  of the on-chip generic DMA requestor.
+ */
+#define DMA_RQ_SD_ADDRESS_MASK                  0x0000FFFF
+#define DMA_RQ_SD_MEMORY_ID_MASK                0x000F0000
+#define DMA_RQ_SD_SP_PARAM_ADDR                 0x00000000
+#define DMA_RQ_SD_SP_SAMPLE_ADDR                0x00010000
+#define DMA_RQ_SD_SP_PROGRAM_ADDR               0x00020000
+#define DMA_RQ_SD_SP_DEBUG_ADDR                 0x00030000
+#define DMA_RQ_SD_OMNIMEM_ADDR                  0x000E0000
+#define DMA_RQ_SD_END_FLAG                      0x40000000
+#define DMA_RQ_SD_ERROR_FLAG                    0x80000000
+#define DMA_RQ_SD_ADDRESS_SHIFT                 0
+
+/*
+ *  The following defines are for the flags in the page map address word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK   0x00000FF8
+#define DMA_RQ_PMA_PAGE_TABLE_MASK              0xFFFFF000
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT  3
+#define DMA_RQ_PMA_PAGE_TABLE_SHIFT             12
+
+#define BA1_VARIDEC_BUF_1       0x000
+
+#define BA1_PDTC                0x0c0    /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */
+#define BA1_PFIE                0x0c4    /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */
+#define BA1_PBA                 0x0c8    /* BA1_PLAY_BUFFER_ADDRESS */
+#define BA1_PVOL                0x0f8    /* BA1_PLAY_VOLUME_REG */
+#define BA1_PSRC                0x288    /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_PCTL                0x2a4    /* BA1_PLAY_CONTROL_REG */
+#define BA1_PPI                 0x2b4    /* BA1_PLAY_PHASE_INCREMENT_REG */
+
+#define BA1_CCTL                0x064    /* BA1_CAPTURE_CONTROL_REG */
+#define BA1_CIE                 0x104    /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */
+#define BA1_CBA                 0x10c    /* BA1_CAPTURE_BUFFER_ADDRESS */
+#define BA1_CSRC                0x2c8    /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_CCI                 0x2d8    /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */
+#define BA1_CD                  0x2e0    /* BA1_CAPTURE_DELAY_REG */
+#define BA1_CPI                 0x2f4    /* BA1_CAPTURE_PHASE_INCREMENT_REG */
+#define BA1_CVOL                0x2f8    /* BA1_CAPTURE_VOLUME_REG */
+
+#define BA1_CFG1                0x134    /* BA1_CAPTURE_FRAME_GROUP_1_REG */
+#define BA1_CFG2                0x138    /* BA1_CAPTURE_FRAME_GROUP_2_REG */
+#define BA1_CCST                0x13c    /* BA1_CAPTURE_CONSTANT_REG */
+#define BA1_CSPB                0x340    /* BA1_CAPTURE_SPB_ADDRESS */
+
+/*
+ *
+ */
+
+#define CS461X_MODE_OUTPUT	(1<<0)	 /* MIDI UART - output */ 
+#define CS461X_MODE_INPUT	(1<<1)	 /* MIDI UART - input */
+
+//****************************************************************************
+//
+// The following define the offsets of the AC97 shadow registers, which appear
+// as a virtual extension to the base address register zero memory range.
+//
+//****************************************************************************
+#define AC97_REG_OFFSET_MASK                    0x0000007EL
+#define AC97_CODEC_NUMBER_MASK                  0x00003000L
+
+#define BA0_AC97_RESET                          0x00001000L
+#define BA0_AC97_MASTER_VOLUME                  0x00001002L
+#define BA0_AC97_HEADPHONE_VOLUME               0x00001004L
+#define BA0_AC97_MASTER_VOLUME_MONO             0x00001006L
+#define BA0_AC97_MASTER_TONE                    0x00001008L
+#define BA0_AC97_PC_BEEP_VOLUME                 0x0000100AL
+#define BA0_AC97_PHONE_VOLUME                   0x0000100CL
+#define BA0_AC97_MIC_VOLUME                     0x0000100EL
+#define BA0_AC97_LINE_IN_VOLUME                 0x00001010L
+#define BA0_AC97_CD_VOLUME                      0x00001012L
+#define BA0_AC97_VIDEO_VOLUME                   0x00001014L
+#define BA0_AC97_AUX_VOLUME                     0x00001016L
+#define BA0_AC97_PCM_OUT_VOLUME                 0x00001018L
+#define BA0_AC97_RECORD_SELECT                  0x0000101AL
+#define BA0_AC97_RECORD_GAIN                    0x0000101CL
+#define BA0_AC97_RECORD_GAIN_MIC                0x0000101EL
+#define BA0_AC97_GENERAL_PURPOSE                0x00001020L
+#define BA0_AC97_3D_CONTROL                     0x00001022L
+#define BA0_AC97_MODEM_RATE                     0x00001024L
+#define BA0_AC97_POWERDOWN                      0x00001026L
+#define BA0_AC97_EXT_AUDIO_ID                   0x00001028L
+#define BA0_AC97_EXT_AUDIO_POWER                0x0000102AL
+#define BA0_AC97_PCM_FRONT_DAC_RATE             0x0000102CL
+#define BA0_AC97_PCM_SURR_DAC_RATE              0x0000102EL
+#define BA0_AC97_PCM_LFE_DAC_RATE               0x00001030L
+#define BA0_AC97_PCM_LR_ADC_RATE                0x00001032L
+#define BA0_AC97_MIC_ADC_RATE                   0x00001034L
+#define BA0_AC97_6CH_VOL_C_LFE                  0x00001036L
+#define BA0_AC97_6CH_VOL_SURROUND               0x00001038L
+#define BA0_AC97_RESERVED_3A                    0x0000103AL
+#define BA0_AC97_EXT_MODEM_ID                   0x0000103CL
+#define BA0_AC97_EXT_MODEM_POWER                0x0000103EL
+#define BA0_AC97_LINE1_CODEC_RATE               0x00001040L
+#define BA0_AC97_LINE2_CODEC_RATE               0x00001042L
+#define BA0_AC97_HANDSET_CODEC_RATE             0x00001044L
+#define BA0_AC97_LINE1_CODEC_LEVEL              0x00001046L
+#define BA0_AC97_LINE2_CODEC_LEVEL              0x00001048L
+#define BA0_AC97_HANDSET_CODEC_LEVEL            0x0000104AL
+#define BA0_AC97_GPIO_PIN_CONFIG                0x0000104CL
+#define BA0_AC97_GPIO_PIN_TYPE                  0x0000104EL
+#define BA0_AC97_GPIO_PIN_STICKY                0x00001050L
+#define BA0_AC97_GPIO_PIN_WAKEUP                0x00001052L
+#define BA0_AC97_GPIO_PIN_STATUS                0x00001054L
+#define BA0_AC97_MISC_MODEM_AFE_STAT            0x00001056L
+#define BA0_AC97_RESERVED_58                    0x00001058L
+#define BA0_AC97_CRYSTAL_REV_N_FAB_ID           0x0000105AL
+#define BA0_AC97_TEST_AND_MISC_CTRL             0x0000105CL
+#define BA0_AC97_AC_MODE                        0x0000105EL
+#define BA0_AC97_MISC_CRYSTAL_CONTROL           0x00001060L
+#define BA0_AC97_LINE1_HYPRID_CTRL              0x00001062L
+#define BA0_AC97_VENDOR_RESERVED_64             0x00001064L
+#define BA0_AC97_VENDOR_RESERVED_66             0x00001066L
+#define BA0_AC97_SPDIF_CONTROL                  0x00001068L
+#define BA0_AC97_VENDOR_RESERVED_6A             0x0000106AL
+#define BA0_AC97_VENDOR_RESERVED_6C             0x0000106CL
+#define BA0_AC97_VENDOR_RESERVED_6E             0x0000106EL
+#define BA0_AC97_VENDOR_RESERVED_70             0x00001070L
+#define BA0_AC97_VENDOR_RESERVED_72             0x00001072L
+#define BA0_AC97_VENDOR_RESERVED_74             0x00001074L
+#define BA0_AC97_CAL_ADDRESS                    0x00001076L
+#define BA0_AC97_CAL_DATA                       0x00001078L
+#define BA0_AC97_VENDOR_RESERVED_7A             0x0000107AL
+#define BA0_AC97_VENDOR_ID1                     0x0000107CL
+#define BA0_AC97_VENDOR_ID2                     0x0000107EL
+#endif				/* __CS461X_H */
diff -Nru linux/sound/oss/cs461x_image.h linux-2.4.19-pre5-mjc/sound/oss/cs461x_image.h
--- linux/sound/oss/cs461x_image.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs461x_image.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,322 @@
+/****************************************************************************
+ * "CWCIMAGE.H"-- For CS46XX. Ver 1.04
+ *      Copyright 1998-2001 (c) Cirrus Logic Corp.
+ *      Version 1.04
+ ****************************************************************************
+ */
+#ifndef __CS_IMAGE_H
+#define __CS_IMAGE_H
+
+#define CLEAR__COUNT     3
+#define FILL__COUNT      4
+#define BA1__DWORD_SIZE  13*1024+512
+
+static struct
+{
+        unsigned BA1__DestByteOffset;
+        unsigned BA1__SourceSize;
+} ClrStat[CLEAR__COUNT] ={ {0x00000000, 0x00003000 },
+                           {0x00010000, 0x00003800 },
+                           {0x00020000, 0x00007000 } };
+
+static u32 FillArray1[]={
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00200040,0x00008010,0x00000000,
+0x00000000,0x80000001,0x00000001,0x00060000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00900080,0x00000173,0x00000000,
+0x00000000,0x00000010,0x00800000,0x00900000,
+0xf2c0000f,0x00000200,0x00000000,0x00010600,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x330300c2,
+0x06000000,0x00000000,0x80008000,0x80008000,
+0x3fc0000f,0x00000301,0x00010400,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00b00000,0x00d0806d,0x330480c3,
+0x04800000,0x00000001,0x00800001,0x0000ffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x066a0600,0x06350070,0x0000929d,0x929d929d,
+0x00000000,0x0000735a,0x00000600,0x00000000,
+0x929d735a,0x00000000,0x00010000,0x735a735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000804f,0x000000c3,
+0x05000000,0x00a00010,0x00000000,0x80008000,
+0x00000000,0x00000000,0x00000700,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000080,0x00a00000,0x0000809a,0x000000c2,
+0x07400000,0x00000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x000107a0,
+0x00c80028,0x000000c2,0x06800000,0x00000000,
+0x06e00080,0x00300000,0x000080bb,0x000000c9,
+0x07a00000,0x04000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x00000780,
+0x00c80028,0x000000c5,0xff800000,0x00000000,
+0x00640080,0x00c00000,0x00008197,0x000000c9,
+0x07800000,0x04000000,0x80008000,0xffffffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000805e,0x000000c1,
+0x00000000,0x00800000,0x80008000,0x80008000,
+0x00020000,0x0000ffff,0x00000000,0x00000000};
+
+static u32 FillArray2[]={
+0x929d0600,0x929d929d,0x929d929d,0x929d0000,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x00100635,0x060b013f,0x00000004,
+0x00000001,0x007a0002,0x00000000,0x066e0610,
+0x0105929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
+0x00000000,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x00000000,0x06400136,
+0x0000270f,0x00010000,0x007a0000,0x00000000,
+0x068e0645,0x0105929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x735a0100,0x00000000,0x00000000,0x00000000};
+
+static u32 FillArray3[]={
+0x00000000,0x00000000,0x00000000,0x00010004};
+
+static u32 FillArray4[]={
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00001705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00009705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00011705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00019705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00021705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00029705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00031705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00039705,0x00001400,0x000a411e,0x00001003,
+0x000fe19e,0x00001003,0x0009c730,0x00001003,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00009705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00011705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00019705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00021705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00029705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00031705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00039705,0x00001400,0x000a211e,0x00001003,
+0x0000a730,0x00001008,0x000e2730,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x00000000,0x00000000,0x000f619c,0x00001003,
+0x0007f801,0x000c0000,0x00000037,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0000373c,0x00001000,0x00000000,0x00000000,
+0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000273c,0x00001000,
+0x00000033,0x00001000,0x000e679e,0x00001003,
+0x00007705,0x00001400,0x000ac71e,0x00001003,
+0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000a730,0x00001003,
+0x00000033,0x00001000,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x000c0000,
+0x00000032,0x00001000,0x0000273d,0x00001000,
+0x0004a730,0x00001003,0x00000f41,0x00097140,
+0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+0x00002725,0x000aa400,0x00013705,0x00093a00,
+0x0000002e,0x0009d6c0,0x00038630,0x00001004,
+0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
+0x00000000,0x000c70e0,0x0007d182,0x0002c640,
+0x00000630,0x00001004,0x000799b8,0x0002c6c0,
+0x00031705,0x00092240,0x00039f05,0x000932c0,
+0x0003520a,0x00000000,0x00040731,0x0000100b,
+0x00010705,0x000b20c0,0x00000000,0x000eba44,
+0x00032108,0x000c60c4,0x00065208,0x000c2917,
+0x000406b0,0x00001007,0x00012f05,0x00036880,
+0x0002818e,0x000c0000,0x0004410a,0x00000000,
+0x00040630,0x00001007,0x00029705,0x000c0000,
+0x00000000,0x00000000,0x00003fc1,0x0003fc40,
+0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
+0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
+0x000037c1,0x00000000,0x00003fc1,0x000991c0,
+0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
+0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
+0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
+0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
+0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
+0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
+0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
+0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
+0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
+0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
+0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
+0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
+0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
+0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
+0x000683ad,0x00095241,0x00020f05,0x000991c1,
+0x00000000,0x00000000,0x00086f88,0x00001000,
+0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
+0x0009de81,0x000bd300,0x0009d601,0x000b1700,
+0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
+0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
+0x000a1681,0x000b97c0,0x00021601,0x00002500,
+0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
+0x00021681,0x00002d00,0x00020f81,0x000bd800,
+0x000a0701,0x000b5bc0,0x00021601,0x00003500,
+0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
+0x00021681,0x00003d00,0x00020f81,0x000b1d00,
+0x000a0701,0x000b1fc0,0x00021601,0x00020500,
+0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
+0x00021681,0x00020d00,0x00020f81,0x000bde80,
+0x000a0701,0x000bdfc0,0x00021601,0x00021500,
+0x00020f81,0x000b9341,0x00020701,0x000b53c1,
+0x00021681,0x00021d00,0x000a0f81,0x000d0380,
+0x0000b601,0x000b15c0,0x00007b01,0x00000000,
+0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
+0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
+0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
+0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
+0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
+0x0007e48a,0x00000000,0x00011f05,0x00084080,
+0x00000000,0x00000000,0x00001705,0x000b3540,
+0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
+0x00055488,0x00000000,0x0000d482,0x0003fc40,
+0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
+0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
+0x000c86b0,0x00001007,0x00008281,0x000bb240,
+0x0000b801,0x000b7140,0x00007888,0x00000000,
+0x0000073c,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x00055288,0x000c555c,
+0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+0x0000fa88,0x00000000,0x00000032,0x00001000,
+0x0000073d,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x0008c01c,0x00001003,
+0x00002705,0x00001008,0x0008b201,0x000c1392,
+0x0000ba01,0x00000000,0x00008731,0x00001400,
+0x0004c108,0x000fe0c4,0x00057488,0x00000000,
+0x000a6388,0x00001001,0x0008b334,0x000bc141,
+0x0003020e,0x00000000,0x000886b0,0x00001008,
+0x00003625,0x000c5dfa,0x000a638a,0x00001001,
+0x0008020e,0x00001002,0x0008a6b0,0x00001008,
+0x0007f301,0x00000000,0x00000000,0x00000000,
+0x00002725,0x000a8c40,0x000000ae,0x00000000,
+0x000d8630,0x00001008,0x00000000,0x000c74e0,
+0x0007d182,0x0002d640,0x000a8630,0x00001008,
+0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
+0x0007420a,0x000c0000,0x00062208,0x000c4117,
+0x00070630,0x00001009,0x00000000,0x000c0000,
+0x0001022e,0x00000000,0x0003a630,0x00001009,
+0x00000000,0x000c0000,0x00000036,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x0002a730,0x00001008,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0002a730,0x00001008,
+0x00000033,0x00001000,0x0002a705,0x00001008,
+0x00007a01,0x000c0000,0x000e6288,0x000d550a,
+0x0006428a,0x00000000,0x00060730,0x0000100a,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
+0x00057488,0x00000000,0x00033b94,0x00081140,
+0x000183ae,0x00000000,0x000786b0,0x0000100b,
+0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+0x00042731,0x00001003,0x0007aab0,0x00034880,
+0x00048fb0,0x0000100a,0x00057488,0x00000000,
+0x00033b94,0x00081140,0x000183ae,0x00000000,
+0x000806b0,0x0000100b,0x00022f05,0x00000000,
+0x00007401,0x00091140,0x00048f05,0x000951c0,
+0x00042731,0x00001003,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x000fe19e,0x00001003,0x00000000,0x00000000,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00000f41,0x00097140,0x0000a841,0x0009b240,
+0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
+0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
+0x00055208,0x00000000,0x00010705,0x000a2880,
+0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
+0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
+0x00065308,0x000c2997,0x000d86b0,0x0000100a,
+0x0004410a,0x000d40c7,0x00000000,0x00000000,
+0x00080730,0x00001004,0x00056f0a,0x000ea105,
+0x00000000,0x00000000,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x0000273d,0x00001000,0x00000000,0x000eba44,
+0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x00000000,0x000e5084,
+0x00000000,0x000eba44,0x00087401,0x000e4782,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
+0x0007e721,0x000bed40,0x00005f25,0x000badc0,
+0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
+0x00033217,0x00003ec0,0x00065590,0x000b8e40,
+0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
+0x000283a0,0x0000100c,0x000ee388,0x00042970,
+0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
+0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
+0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
+0x00078898,0x00001000,0x00038894,0x00000032,
+0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
+0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
+0x00041705,0x0009ed40,0x00058730,0x00001400,
+0x000d7488,0x000c3a00,0x00048f05,0x00000000};
+
+static struct
+{   u32 Offset;
+    u32 Size;
+    u32 *pFill;
+} FillStat[FILL__COUNT] = {
+                            {0x00000000, sizeof(FillArray1), FillArray1},
+                            {0x00001800, sizeof(FillArray2), FillArray2},
+                            {0x000137f0, sizeof(FillArray3), FillArray3},
+                            {0x00020000, sizeof(FillArray4), FillArray4}
+                          };
+
+
+#endif
diff -Nru linux/sound/oss/cs46xx.c linux-2.4.19-pre5-mjc/sound/oss/cs46xx.c
--- linux/sound/oss/cs46xx.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs46xx.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,5727 @@
+/*
+ *	Crystal SoundFusion CS46xx driver
+ *
+ *	Copyright 1998-2001 Cirrus Logic Corporation <pcaudio@crystal.cirrus.com>
+ *						<twoller@crystal.cirrus.com>
+ *	Copyright 1999-2000 Jaroslav Kysela <perex@suse.cz>
+ *	Copyright 2000 Alan Cox <alan@redhat.com>
+ *
+ *	The core of this code is taken from the ALSA project driver by 
+ *	Jaroslav. Please send Jaroslav the credit for the driver and 
+ *	report bugs in this port to <alan@redhat.com>
+ *
+ *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *	Current maintainers:
+ *		Cirrus Logic Corporation, Thomas Woller (tw)
+ *			<twoller@crystal.cirrus.com>
+ *		Nils Faerber (nf)
+ *			<nils@kernelconcepts.de>
+ *		Thanks to David Pollard for testing.
+ *
+ *	Changes:
+ *	20000909-nf	Changed cs_read, cs_write and drain_dac
+ *	20001025-tw	Separate Playback/Capture structs and buffers.
+ *			Added Scatter/Gather support for Playback.
+ *			Added Capture.
+ *	20001027-nf	Port to kernel 2.4.0-test9, some clean-ups
+ *			Start of powermanagement support (CS46XX_PM).
+ *	20001128-tw	Add module parm for default buffer order.
+ *			added DMA_GFP flag to kmalloc dma buffer allocs.
+ *			backfill silence to eliminate stuttering on
+ *			underruns.
+ *	20001201-tw	add resyncing of swptr on underruns.
+ *	20001205-tw-nf	fixed GETOSPACE ioctl() after open()
+ *	20010113-tw	patch from Hans Grobler general cleanup.
+ *	20010117-tw	2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0
+ *	20010118-tw	basic PM support for 2.2.16+ and 2.4.0/2.4.2.
+ *	20010228-dh	patch from David Huggins - cs_update_ptr recursion.
+ *	20010409-tw	add hercules game theatre XP amp code.
+ *	20010420-tw	cleanup powerdown/up code.
+ *	20010521-tw	eliminate pops, and fixes for powerdown.
+ *	20010525-tw	added fixes for thinkpads with powerdown logic.
+ *	20010723-sh     patch from Horms (Simon Horman) -
+ *	                SOUND_PCM_READ_BITS returns bits as set in driver
+ *	                rather than a logical or of the possible values.
+ *	                Various ioctls handle the case where the device
+ *	                is open for reading or writing but not both better.
+ *
+ *	Status:
+ *	Playback/Capture supported from 8k-48k.
+ *	16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported.
+ *
+ *	APM/PM - 2.2.x APM is enabled and functioning fine. APM can also
+ *	be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro
+ *	definition.
+ *
+ *      Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp,
+ *	so, use the drain/polarity to enable.  
+ *	hercules_egpio_disable set to 1, will force a 0 to EGPIODR.
+ *
+ *	VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control
+ *	the external amplifier for the "back" speakers, since we do not
+ *	support the secondary codec then this external amp is also not
+ *	turned on.
+ */
+ 
+#include <linux/list.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/ac97_codec.h>
+#include "cs46xxpm-24.h"
+#include "cs46xx_wrapper-24.h"
+
+#include "cs461x.h"
+
+/* MIDI buffer sizes */
+#define CS_MIDIINBUF  500
+#define CS_MIDIOUTBUF 500
+
+#define ADC_RUNNING	1
+#define DAC_RUNNING	2
+
+#define CS_FMT_16BIT	1		/* These are fixed in fact */
+#define CS_FMT_STEREO	2
+#define CS_FMT_MASK	3
+
+#define CS_TYPE_ADC	1
+#define CS_TYPE_DAC	2
+
+#define CS_TRUE 	1
+#define CS_FALSE 	0
+
+#define CS_INC_USE_COUNT(m) (atomic_inc(m))
+#define CS_DEC_USE_COUNT(m) (atomic_dec(m))
+#define CS_DEC_AND_TEST(m) (atomic_dec_and_test(m))
+#define CS_IN_USE(m) (atomic_read(m) != 0)
+
+#define CS_DBGBREAKPOINT {__asm__("INT $3");}
+/*
+ *	CS461x definitions
+ */
+ 
+#define CS461X_BA0_SIZE		0x2000
+#define CS461X_BA1_DATA0_SIZE	0x3000
+#define CS461X_BA1_DATA1_SIZE	0x3800
+#define CS461X_BA1_PRG_SIZE	0x7000
+#define CS461X_BA1_REG_SIZE	0x0100
+
+#define GOF_PER_SEC	200
+
+#define CSDEBUG_INTERFACE 1
+#define CSDEBUG 1
+/*
+ * Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG
+ *
+ *
+ * CSDEBUG is usual mode is set to 1, then use the
+ * cs_debuglevel and cs_debugmask to turn on or off debugging.
+ * Debug level of 1 has been defined to be kernel errors and info
+ * that should be printed on any released driver.
+ */
+#if CSDEBUG
+#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask)) {x;} 
+#else
+#define CS_DBGOUT(mask,level,x) 
+#endif
+/*
+ * cs_debugmask areas
+ */
+#define CS_INIT	 	0x00000001		/* initialization and probe functions */
+#define CS_ERROR 	0x00000002		/* tmp debugging bit placeholder */
+#define CS_INTERRUPT	0x00000004		/* interrupt handler (separate from all other) */
+#define CS_FUNCTION 	0x00000008		/* enter/leave functions */
+#define CS_WAVE_WRITE 	0x00000010		/* write information for wave */
+#define CS_WAVE_READ 	0x00000020		/* read information for wave */
+#define CS_MIDI_WRITE 	0x00000040		/* write information for midi */
+#define CS_MIDI_READ 	0x00000080		/* read information for midi */
+#define CS_MPU401_WRITE 0x00000100		/* write information for mpu401 */
+#define CS_MPU401_READ 	0x00000200		/* read information for mpu401 */
+#define CS_OPEN		0x00000400		/* all open functions in the driver */
+#define CS_RELEASE	0x00000800		/* all release functions in the driver */
+#define CS_PARMS	0x00001000		/* functional and operational parameters */
+#define CS_IOCTL	0x00002000		/* ioctl (non-mixer) */
+#define CS_PM		0x00004000		/* PM */
+#define CS_TMP		0x10000000		/* tmp debug mask bit */
+
+#define CS_IOCTL_CMD_SUSPEND	0x1	// suspend
+#define CS_IOCTL_CMD_RESUME	0x2	// resume
+
+#if CSDEBUG
+static unsigned long cs_debuglevel=1;			/* levels range from 1-9 */
+MODULE_PARM(cs_debuglevel, "i");
+static unsigned long cs_debugmask=CS_INIT | CS_ERROR;	/* use CS_DBGOUT with various mask values */
+MODULE_PARM(cs_debugmask, "i");
+#endif
+static unsigned long hercules_egpio_disable=0;  /* if non-zero set all EGPIO to 0 */
+MODULE_PARM(hercules_egpio_disable, "i");
+static unsigned long initdelay=700;  /* PM delay in millisecs */
+MODULE_PARM(initdelay, "i");
+static unsigned long powerdown=-1;  /* turn on/off powerdown processing in driver */
+MODULE_PARM(powerdown, "i");
+#define DMABUF_DEFAULTORDER 3
+static unsigned long defaultorder=DMABUF_DEFAULTORDER;
+MODULE_PARM(defaultorder, "i");
+
+static int external_amp;
+MODULE_PARM(external_amp, "i");
+static int thinkpad;
+MODULE_PARM(thinkpad, "i");
+
+/*
+* set the powerdown module parm to 0 to disable all 
+* powerdown. also set thinkpad to 1 to disable powerdown, 
+* but also to enable the clkrun functionality.
+*/
+static unsigned cs_powerdown=1;
+static unsigned cs_laptop_wait=1;
+
+/* An instance of the 4610 channel */
+struct cs_channel 
+{
+	int used;
+	int num;
+	void *state;
+};
+
+#define CS46XX_MAJOR_VERSION "1"
+#define CS46XX_MINOR_VERSION "28"
+
+#ifdef __ia64__
+#define CS46XX_ARCH	     	"64"	//architecture key
+#else
+#define CS46XX_ARCH	     	"32"	//architecture key
+#endif
+
+struct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs };
+
+/* magic numbers to protect our data structures */
+#define CS_CARD_MAGIC		0x43525553 /* "CRUS" */
+#define CS_STATE_MAGIC		0x4c4f4749 /* "LOGI" */
+#define NR_HW_CH		3
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define NR_AC97		2
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+/* "software" or virtual channel, an instance of opened /dev/dsp */
+struct cs_state {
+	unsigned int magic;
+	struct cs_card *card;	/* Card info */
+
+	/* single open lock mechanism, only used for recording */
+	struct semaphore open_sem;
+	wait_queue_head_t open_wait;
+
+	/* file mode */
+	mode_t open_mode;
+
+	/* virtual channel number */
+	int virt;
+	
+	struct dmabuf {
+		/* wave sample stuff */
+		unsigned int rate;
+		unsigned char fmt, enable;
+
+		/* hardware channel */
+		struct cs_channel *channel;
+		int pringbuf;		/* Software ring slot */
+		void *pbuf;		/* 4K hardware DMA buffer */
+
+		/* OSS buffer management stuff */
+		void *rawbuf;
+		dma_addr_t dma_handle;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned divisor;
+		unsigned type;
+		void *tmpbuff;			/* tmp buffer for sample conversions */
+		dma_addr_t dmaaddr;
+		dma_addr_t dmaaddr_tmpbuff;
+		unsigned buforder_tmpbuff;	/* Log base 2 of size in bytes.. */
+
+		/* our buffer acts like a circular ring */
+		unsigned hwptr;		/* where dma last started, updated by update_ptr */
+		unsigned swptr;		/* where driver last clear/filled, updated by read/write */
+		int count;		/* bytes to be comsumed or been generated by dma machine */
+		unsigned total_bytes;	/* total bytes dmaed by hardware */
+		unsigned blocks;	/* total blocks */
+
+		unsigned error;		/* number of over/underruns */
+		unsigned underrun;	/* underrun pending before next write has occurred */
+		wait_queue_head_t wait;	/* put process on wait queue when no more space in buffer */
+
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned SGok:1;
+		unsigned update_flag;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dmabuf;
+	/* Guard against mmap/write/read races */
+	struct semaphore sem;
+};
+
+struct cs_card {
+	struct cs_channel channel[2];
+	unsigned int magic;
+
+	/* We keep cs461x cards in a linked list */
+	struct cs_card *next;
+
+	/* The cs461x has a certain amount of cross channel interaction
+	   so we use a single per card lock */
+	spinlock_t lock;
+
+	/* mixer use count */
+	atomic_t mixer_use_cnt;
+
+	/* PCI device stuff */
+	struct pci_dev * pci_dev;
+	struct list_head list;
+
+	unsigned int pctl, cctl;	/* Hardware DMA flag sets */
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_midi;
+
+	/* structures for abstraction of hardware facilities, codecs, banks and channels*/
+	struct ac97_codec *ac97_codec[NR_AC97];
+	struct cs_state *states[2];
+
+	u16 ac97_features;
+	
+	int amplifier;			/* Amplifier control */
+	void (*amplifier_ctrl)(struct cs_card *, int);
+	void (*amp_init)(struct cs_card *);
+	
+	int active;			/* Active clocking */
+	void (*active_ctrl)(struct cs_card *, int);
+	
+	/* hardware resources */
+	unsigned long ba0_addr;
+	unsigned long ba1_addr;
+	u32 irq;
+	
+	/* mappings */
+	void *ba0;
+	union
+	{
+		struct
+		{
+			u8 *data0;
+			u8 *data1;
+			u8 *pmem;
+			u8 *reg;
+		} name;
+		u8 *idx[4];
+	} ba1;
+	
+	/* Function support */
+	struct cs_channel *(*alloc_pcm_channel)(struct cs_card *);
+	struct cs_channel *(*alloc_rec_pcm_channel)(struct cs_card *);
+	void (*free_pcm_channel)(struct cs_card *, int chan);
+
+	/* /dev/midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t open_wait;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		spinlock_t lock;
+		unsigned char ibuf[CS_MIDIINBUF];
+		unsigned char obuf[CS_MIDIOUTBUF];
+		mode_t open_mode;
+		struct semaphore open_sem;
+	} midi;
+	struct cs46xx_pm pm;
+};
+
+static int cs_open_mixdev(struct inode *inode, struct file *file);
+static int cs_release_mixdev(struct inode *inode, struct file *file);
+static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+				unsigned long arg);
+static int cs_hardware_init(struct cs_card *card);
+static int cs46xx_powerup(struct cs_card *card, unsigned int type);
+static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag);
+static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type);
+static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state);
+static int cs46xx_resume_tbl(struct pci_dev *pcidev);
+
+static inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+#if CSDEBUG
+
+/* DEBUG ROUTINES */
+
+#define SOUND_MIXER_CS_GETDBGLEVEL 	_SIOWR('M',120, int)
+#define SOUND_MIXER_CS_SETDBGLEVEL 	_SIOWR('M',121, int)
+#define SOUND_MIXER_CS_GETDBGMASK 	_SIOWR('M',122, int)
+#define SOUND_MIXER_CS_SETDBGMASK 	_SIOWR('M',123, int)
+#define SOUND_MIXER_CS_APM	 	_SIOWR('M',124, int)
+
+void printioctl(unsigned int x)
+{
+    unsigned int i;
+    unsigned char vidx;
+	/* these values are incorrect for the ac97 driver, fix.
+         * Index of mixtable1[] member is Device ID 
+         * and must be <= SOUND_MIXER_NRDEVICES.
+         * Value of array member is index into s->mix.vol[]
+         */
+        static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
+                [SOUND_MIXER_PCM]     = 1,   /* voice */
+                [SOUND_MIXER_LINE1]   = 2,   /* AUX */
+                [SOUND_MIXER_CD]      = 3,   /* CD */
+                [SOUND_MIXER_LINE]    = 4,   /* Line */
+                [SOUND_MIXER_SYNTH]   = 5,   /* FM */
+                [SOUND_MIXER_MIC]     = 6,   /* Mic */
+                [SOUND_MIXER_SPEAKER] = 7,   /* Speaker */
+                [SOUND_MIXER_RECLEV]  = 8,   /* Recording level */
+                [SOUND_MIXER_VOLUME]  = 9    /* Master Volume */
+        };
+        
+    switch(x) 
+    {
+	case SOUND_MIXER_CS_GETDBGMASK:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGMASK: ") );
+		break;
+	case SOUND_MIXER_CS_GETDBGLEVEL:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGLEVEL: ") );
+		break;
+	case SOUND_MIXER_CS_SETDBGMASK:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGMASK: ") );
+		break;
+	case SOUND_MIXER_CS_SETDBGLEVEL:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGLEVEL: ") );
+		break;
+        case OSS_GETVERSION:
+		CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION: ") );
+		break;
+        case SNDCTL_DSP_SYNC:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC: ") );
+		break;
+        case SNDCTL_DSP_SETDUPLEX:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX: ") );
+		break;
+        case SNDCTL_DSP_GETCAPS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS: ") );
+		break;
+        case SNDCTL_DSP_RESET:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET: ") );
+		break;
+        case SNDCTL_DSP_SPEED:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED: ") );
+		break;
+        case SNDCTL_DSP_STEREO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO: ") );
+		break;
+        case SNDCTL_DSP_CHANNELS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS: ") );
+		break;
+        case SNDCTL_DSP_GETFMTS: 
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS: ") );
+		break;
+        case SNDCTL_DSP_SETFMT: 
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT: ") );
+		break;
+        case SNDCTL_DSP_POST:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST: ") );
+		break;
+        case SNDCTL_DSP_GETTRIGGER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER: ") );
+		break;
+        case SNDCTL_DSP_SETTRIGGER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER: ") );
+		break;
+        case SNDCTL_DSP_GETOSPACE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE: ") );
+		break;
+        case SNDCTL_DSP_GETISPACE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE: ") );
+		break;
+        case SNDCTL_DSP_NONBLOCK:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK: ") );
+		break;
+        case SNDCTL_DSP_GETODELAY:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY: ") );
+		break;
+        case SNDCTL_DSP_GETIPTR:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR: ") );
+		break;
+        case SNDCTL_DSP_GETOPTR:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR: ") );
+		break;
+        case SNDCTL_DSP_GETBLKSIZE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE: ") );
+		break;
+        case SNDCTL_DSP_SETFRAGMENT:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT: ") );
+		break;
+        case SNDCTL_DSP_SUBDIVIDE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE: ") );
+		break;
+        case SOUND_PCM_READ_RATE:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE: ") );
+		break;
+        case SOUND_PCM_READ_CHANNELS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_CHANNELS: ") );
+		break;
+        case SOUND_PCM_READ_BITS:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS: ") );
+		break;
+        case SOUND_PCM_WRITE_FILTER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_WRITE_FILTER: ") );
+		break;
+        case SNDCTL_DSP_SETSYNCRO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO: ") );
+		break;
+        case SOUND_PCM_READ_FILTER:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") );
+		break;
+
+        case SOUND_MIXER_PRIVATE1:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") );
+		break;
+        case SOUND_MIXER_PRIVATE2:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2: ") );
+		break;
+        case SOUND_MIXER_PRIVATE3:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3: ") );
+		break;
+        case SOUND_MIXER_PRIVATE4:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4: ") );
+		break;
+        case SOUND_MIXER_PRIVATE5:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5: ") );
+		break;
+        case SOUND_MIXER_INFO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO: ") );
+		break;
+        case SOUND_OLD_MIXER_INFO:
+		CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO: ") );
+		break;
+
+	default:
+		switch (_IOC_NR(x)) 
+		{
+			case SOUND_MIXER_VOLUME:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME: ") );
+				break;
+			case SOUND_MIXER_SPEAKER:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SPEAKER: ") );
+				break;
+			case SOUND_MIXER_RECLEV:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECLEV: ") );
+				break;
+			case SOUND_MIXER_MIC:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_MIC: ") );
+				break;
+			case SOUND_MIXER_SYNTH:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SYNTH: ") );
+				break;
+			case SOUND_MIXER_RECSRC: 
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECSRC: ") );
+				break;
+			case SOUND_MIXER_DEVMASK:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_DEVMASK: ") );
+				break;
+			case SOUND_MIXER_RECMASK:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECMASK: ") );
+				break;
+			case SOUND_MIXER_STEREODEVS: 
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_STEREODEVS: ") );
+				break;
+			case SOUND_MIXER_CAPS:
+				CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:") );
+				break;
+			default:
+				i = _IOC_NR(x);
+				if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
+				{
+					CS_DBGOUT(CS_IOCTL, 4, printk("UNKNOWN IOCTL: 0x%.8x NR=%d ",x,i) );
+				}
+				else
+				{
+					CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d ",
+							x,i) );
+				}
+				break;
+		}
+    }
+    CS_DBGOUT(CS_IOCTL, 4, printk("command = 0x%x IOC_NR=%d\n",x, _IOC_NR(x)) );
+}
+#endif
+
+/*
+ *  common I/O routines
+ */
+
+static void cs461x_poke(struct cs_card *codec, unsigned long reg, unsigned int val)
+{
+	writel(val, codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff));
+}
+
+static unsigned int cs461x_peek(struct cs_card *codec, unsigned long reg)
+{
+	return readl(codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff));
+}
+
+static void cs461x_pokeBA0(struct cs_card *codec, unsigned long reg, unsigned int val)
+{
+	writel(val, codec->ba0+reg);
+}
+
+static unsigned int cs461x_peekBA0(struct cs_card *codec, unsigned long reg)
+{
+	return readl(codec->ba0+reg);
+}
+
+
+static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg);
+static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
+
+static struct cs_channel *cs_alloc_pcm_channel(struct cs_card *card)
+{
+	if(card->channel[1].used==1)
+		return NULL;
+	card->channel[1].used=1;
+	card->channel[1].num=1;
+	return &card->channel[1];
+}
+
+static struct cs_channel *cs_alloc_rec_pcm_channel(struct cs_card *card)
+{
+	if(card->channel[0].used==1)
+		return NULL;
+	card->channel[0].used=1;
+	card->channel[0].num=0;
+	return &card->channel[0];
+}
+
+static void cs_free_pcm_channel(struct cs_card *card, int channel)
+{
+	card->channel[channel].state = NULL;
+	card->channel[channel].used=0;
+}
+
+/*
+ * setup a divisor value to help with conversion from
+ * 16bit Stereo, down to 8bit stereo/mono or 16bit mono.
+ * assign a divisor of 1 if using 16bit Stereo as that is
+ * the only format that the static image will capture.
+ */
+static void cs_set_divisor(struct dmabuf *dmabuf)
+{
+	if(dmabuf->type == CS_TYPE_DAC)
+		dmabuf->divisor = 1;
+	else if( !(dmabuf->fmt & CS_FMT_STEREO) && 
+	    (dmabuf->fmt & CS_FMT_16BIT))
+		dmabuf->divisor = 2;
+	else if( (dmabuf->fmt & CS_FMT_STEREO) && 
+	    !(dmabuf->fmt & CS_FMT_16BIT))
+		dmabuf->divisor = 2;
+	else if( !(dmabuf->fmt & CS_FMT_STEREO) && 
+	    !(dmabuf->fmt & CS_FMT_16BIT))
+		dmabuf->divisor = 4;
+	else
+		dmabuf->divisor = 1;
+
+	CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, printk(
+		"cs46xx: cs_set_divisor()- %s %d\n",
+			(dmabuf->type == CS_TYPE_ADC) ? "ADC" : "DAC", 
+			dmabuf->divisor) );
+}
+
+/*
+* mute some of the more prevalent registers to avoid popping.
+*/
+static void cs_mute(struct cs_card *card, int state) 
+{
+	struct ac97_codec *dev=card->ac97_codec[0];
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n",
+		(state == CS_TRUE) ? "Muting" : "UnMuting") );
+
+	if(state == CS_TRUE)
+	{
+	/*
+	* fix pops when powering up on thinkpads
+	*/
+		card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, 
+				(u8)BA0_AC97_MASTER_VOLUME); 
+		card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, 
+				(u8)BA0_AC97_HEADPHONE_VOLUME); 
+		card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, 
+				(u8)BA0_AC97_MASTER_VOLUME_MONO); 
+		card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, 
+				(u8)BA0_AC97_PCM_OUT_VOLUME);
+			
+		cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000);
+		cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000);
+		cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
+		cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000);
+	}
+	else
+	{
+		cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, card->pm.u32AC97_master_volume);
+		cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, card->pm.u32AC97_headphone_volume);
+		cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono);
+		cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, card->pm.u32AC97_pcm_out_volume);
+	}
+	CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()-\n"));
+}
+
+/* set playback sample rate */
+static unsigned int cs_set_dac_rate(struct cs_state * state, unsigned int rate)
+{	
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned int tmp1, tmp2;
+	unsigned int phiIncr;
+	unsigned int correctionPerGOF, correctionPerSec;
+	unsigned long flags;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()+ %d\n",rate) );
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *  phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+         *                                   GOF_PER_SEC)
+         *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
+         *                       GOF_PER_SEC * correctionPerGOF
+	 *
+	 *  i.e.
+	 *
+	 *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF:correctionPerSec =
+	 *      dividend:remainder(ulOther / GOF_PER_SEC)
+	 */
+	tmp1 = rate << 16;
+	phiIncr = tmp1 / 48000;
+	tmp1 -= phiIncr * 48000;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / 48000;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * 48000;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+
+	/*
+	 *  Fill in the SampleRateConverter control block.
+	 */
+	 
+	spin_lock_irqsave(&state->card->lock, flags);
+	cs461x_poke(state->card, BA1_PSRC,
+	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+	cs461x_poke(state->card, BA1_PPI, phiIncr);
+	spin_unlock_irqrestore(&state->card->lock, flags);
+	dmabuf->rate = rate;
+	
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()- %d\n",rate) );
+	return rate;
+}
+
+/* set recording sample rate */
+static unsigned int cs_set_adc_rate(struct cs_state * state, unsigned int rate)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card = state->card;
+	unsigned int phiIncr, coeffIncr, tmp1, tmp2;
+	unsigned int correctionPerGOF, correctionPerSec, initialDelay;
+	unsigned int frameGroupLength, cnt;
+	unsigned long flags;
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()+ %d\n",rate) );
+
+	/*
+	 *  We can only decimate by up to a factor of 1/9th the hardware rate.
+	 *  Correct the value if an attempt is made to stray outside that limit.
+	 */
+	if ((rate * 9) < 48000)
+		rate = 48000 / 9;
+
+	/*
+	 *  We can not capture at at rate greater than the Input Rate (48000).
+	 *  Return an error if an attempt is made to stray outside that limit.
+	 */
+	if (rate > 48000)
+		rate = 48000;
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *     coeffIncr = -floor((Fs,out * 2^23) / Fs,in)
+	 *     phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *     correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+	 *                                GOF_PER_SEC)
+	 *     correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
+	 *                          GOF_PER_SEC * correctionPerGOF
+	 *     initialDelay = ceil((24 * Fs,in) / Fs,out)
+	 *
+	 * i.e.
+	 *
+	 *     coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
+	 *     phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *     correctionPerGOF:correctionPerSec =
+	 * 	    dividend:remainder(ulOther / GOF_PER_SEC)
+	 *     initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
+	 */
+
+	tmp1 = rate << 16;
+	coeffIncr = tmp1 / 48000;
+	tmp1 -= coeffIncr * 48000;
+	tmp1 <<= 7;
+	coeffIncr <<= 7;
+	coeffIncr += tmp1 / 48000;
+	coeffIncr ^= 0xFFFFFFFF;
+	coeffIncr++;
+	tmp1 = 48000 << 16;
+	phiIncr = tmp1 / rate;
+	tmp1 -= phiIncr * rate;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / rate;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * rate;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+	initialDelay = ((48000 * 24) + rate - 1) / rate;
+
+	/*
+	 *  Fill in the VariDecimate control block.
+	 */
+	spin_lock_irqsave(&card->lock, flags);
+	cs461x_poke(card, BA1_CSRC,
+		((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+	cs461x_poke(card, BA1_CCI, coeffIncr);
+	cs461x_poke(card, BA1_CD,
+		(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
+	cs461x_poke(card, BA1_CPI, phiIncr);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	/*
+	 *  Figure out the frame group length for the write back task.  Basically,
+	 *  this is just the factors of 24000 (2^6*3*5^3) that are not present in
+	 *  the output sample rate.
+	 */
+	frameGroupLength = 1;
+	for (cnt = 2; cnt <= 64; cnt *= 2) {
+		if (((rate / cnt) * cnt) != rate)
+			frameGroupLength *= 2;
+	}
+	if (((rate / 3) * 3) != rate) {
+		frameGroupLength *= 3;
+	}
+	for (cnt = 5; cnt <= 125; cnt *= 5) {
+		if (((rate / cnt) * cnt) != rate) 
+			frameGroupLength *= 5;
+        }
+
+	/*
+	 * Fill in the WriteBack control block.
+	 */
+	spin_lock_irqsave(&card->lock, flags);
+	cs461x_poke(card, BA1_CFG1, frameGroupLength);
+	cs461x_poke(card, BA1_CFG2, (0x00800000 | frameGroupLength));
+	cs461x_poke(card, BA1_CCST, 0x0000FFFF);
+	cs461x_poke(card, BA1_CSPB, ((65536 * rate) / 24000));
+	cs461x_poke(card, (BA1_CSPB + 4), 0x0000FFFF);
+	spin_unlock_irqrestore(&card->lock, flags);
+	dmabuf->rate = rate;
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()- %d\n",rate) );
+	return rate;
+}
+
+/* prepare channel attributes for playback */ 
+static void cs_play_setup(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card = state->card;
+        unsigned int tmp, Count, playFormat;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()+\n") );
+        cs461x_poke(card, BA1_PVOL, 0x80008000);
+        if(!dmabuf->SGok)
+               cs461x_poke(card, BA1_PBA, virt_to_bus(dmabuf->pbuf));
+    
+        Count = 4;                                                          
+        playFormat=cs461x_peek(card, BA1_PFIE);                             
+        if ((dmabuf->fmt & CS_FMT_STEREO)) {                                
+                playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO;                 
+                Count *= 2;                                                 
+        }                                                                   
+        else                                                                
+                playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO;                  
+                                                                            
+        if ((dmabuf->fmt & CS_FMT_16BIT)) {                                 
+                playFormat &= ~(DMA_RQ_C2_AC_8_TO_16_BIT                    
+                           | DMA_RQ_C2_AC_SIGNED_CONVERT);                  
+                Count *= 2;                                                 
+        }                                                                   
+        else                                                                
+                playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT                     
+                           | DMA_RQ_C2_AC_SIGNED_CONVERT);                  
+                                                                            
+        cs461x_poke(card, BA1_PFIE, playFormat);                            
+                                                                            
+        tmp = cs461x_peek(card, BA1_PDTC);                                  
+        tmp &= 0xfffffe00;                                                  
+        cs461x_poke(card, BA1_PDTC, tmp | --Count);                         
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()-\n") );
+
+}
+
+struct InitStruct
+{
+    u32 long off;
+    u32 long val;
+} InitArray[] = { {0x00000040, 0x3fc0000f},
+                  {0x0000004c, 0x04800000},
+
+                  {0x000000b3, 0x00000780},
+                  {0x000000b7, 0x00000000},
+                  {0x000000bc, 0x07800000},
+
+                  {0x000000cd, 0x00800000},
+                };
+
+/*
+ * "SetCaptureSPValues()" -- Initialize record task values before each
+ * 	capture startup.  
+ */
+void SetCaptureSPValues(struct cs_card *card)
+{
+	unsigned i, offset;
+	CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()+\n") );
+	for(i=0; i<sizeof(InitArray)/sizeof(struct InitStruct); i++)
+	{
+		offset = InitArray[i].off*4; /* 8bit to 32bit offset value */
+		cs461x_poke(card, offset, InitArray[i].val );
+	}
+	CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()-\n") );
+}
+
+/* prepare channel attributes for recording */
+static void cs_rec_setup(struct cs_state *state)
+{
+	struct cs_card *card = state->card;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n") );
+
+	SetCaptureSPValues(card);
+
+	/*
+	 * set the attenuation to 0dB 
+	 */
+	cs461x_poke(card, BA1_CVOL, 0x80008000);
+
+	/*
+	 * set the physical address of the capture buffer into the SP
+	 */
+	cs461x_poke(card, BA1_CBA, virt_to_bus(dmabuf->rawbuf));
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()-\n") );
+}
+
+
+/* get current playback/recording dma buffer pointer (byte offset from LBA),
+   called with spinlock held! */
+   
+static inline unsigned cs_get_dma_addr(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	u32 offset;
+	
+	if ( (!(dmabuf->enable & DAC_RUNNING)) &&
+	     (!(dmabuf->enable & ADC_RUNNING) ) )
+	{
+		CS_DBGOUT(CS_ERROR, 2, printk(
+			"cs46xx: ERROR cs_get_dma_addr(): not enabled \n") );
+		return 0;
+	}
+		
+	/*
+	 * ganularity is byte boundry, good part.
+	 */
+	if(dmabuf->enable & DAC_RUNNING)
+	{
+		offset = cs461x_peek(state->card, BA1_PBA);                                  
+	}
+	else /* ADC_RUNNING must be set */
+	{
+		offset = cs461x_peek(state->card, BA1_CBA);                                  
+	}
+	CS_DBGOUT(CS_PARMS | CS_FUNCTION, 9, 
+		printk("cs46xx: cs_get_dma_addr() %d\n",offset) );
+	offset = (u32)bus_to_virt((unsigned long)offset) - (u32)dmabuf->rawbuf;
+	CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, 
+		printk("cs46xx: cs_get_dma_addr()- %d\n",offset) );
+	return offset;
+}
+
+static void resync_dma_ptrs(struct cs_state *state)
+{
+	struct dmabuf *dmabuf;
+	
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") );
+	if(state)
+	{
+		dmabuf = &state->dmabuf;
+		dmabuf->hwptr=dmabuf->swptr = 0;
+		dmabuf->pringbuf = 0;
+	}
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()- \n") );
+}
+	
+/* Stop recording (lock held) */
+static inline void __stop_adc(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card = state->card;
+	unsigned int tmp;
+	
+	dmabuf->enable &= ~ADC_RUNNING;
+	
+	tmp = cs461x_peek(card, BA1_CCTL);
+	tmp &= 0xFFFF0000;
+	cs461x_poke(card, BA1_CCTL, tmp );
+}
+
+static void stop_adc(struct cs_state *state)
+{
+	unsigned long flags;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()+ \n") );
+	spin_lock_irqsave(&state->card->lock, flags);
+	__stop_adc(state);
+	spin_unlock_irqrestore(&state->card->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()- \n") );
+}
+
+static void start_adc(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card = state->card;
+	unsigned long flags;
+	unsigned int tmp;
+
+	spin_lock_irqsave(&card->lock, flags);
+	if (!(dmabuf->enable & ADC_RUNNING) && 
+	     ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) 
+	       && dmabuf->ready) && 
+	       ((card->pm.flags & CS46XX_PM_IDLE) || 
+	        (card->pm.flags & CS46XX_PM_RESUMED)) )
+	{
+		dmabuf->enable |= ADC_RUNNING;
+		cs_set_divisor(dmabuf);
+		tmp = cs461x_peek(card, BA1_CCTL);
+		tmp &= 0xFFFF0000;
+		tmp |= card->cctl;
+		CS_DBGOUT(CS_FUNCTION, 2, printk(
+			"cs46xx: start_adc() poke 0x%x \n",tmp) );
+		cs461x_poke(card, BA1_CCTL, tmp);
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* stop playback (lock held) */
+static inline void __stop_dac(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card = state->card;
+	unsigned int tmp;
+
+	dmabuf->enable &= ~DAC_RUNNING;
+	
+	tmp=cs461x_peek(card, BA1_PCTL);
+	tmp&=0xFFFF;
+	cs461x_poke(card, BA1_PCTL, tmp);
+}
+
+static void stop_dac(struct cs_state *state)
+{
+	unsigned long flags;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()+ \n") );
+	spin_lock_irqsave(&state->card->lock, flags);
+	__stop_dac(state);
+	spin_unlock_irqrestore(&state->card->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()- \n") );
+}	
+
+static void start_dac(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card = state->card;
+	unsigned long flags;
+	int tmp;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()+ \n") );
+	spin_lock_irqsave(&card->lock, flags);
+	if (!(dmabuf->enable & DAC_RUNNING) && 
+	    ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) &&
+	       ((card->pm.flags & CS46XX_PM_IDLE) || 
+	        (card->pm.flags & CS46XX_PM_RESUMED)) )
+	{
+		dmabuf->enable |= DAC_RUNNING;
+		tmp = cs461x_peek(card, BA1_PCTL);
+		tmp &= 0xFFFF;
+		tmp |= card->pctl;
+		CS_DBGOUT(CS_PARMS, 6, printk(
+		    "cs46xx: start_dac() poke card=0x%.08x tmp=0x%.08x addr=0x%.08x \n",
+		    (unsigned)card, (unsigned)tmp, 
+		    (unsigned)card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) );
+		cs461x_poke(card, BA1_PCTL, tmp);
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()- \n") );
+}
+
+#define DMABUF_MINORDER 1
+
+/*
+ * allocate DMA buffer, playback and recording buffers are separate.
+ */
+static int alloc_dmabuf(struct cs_state *state)
+{
+
+	struct cs_card *card=state->card;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	void *rawbuf = NULL;
+	void *tmpbuff = NULL;
+	int order;
+	struct page *map, *mapend;
+	unsigned long df;
+	
+	dmabuf->ready  = dmabuf->mapped = 0;
+	dmabuf->SGok = 0;
+/*
+* check for order within limits, but do not overwrite value.
+*/
+	if((defaultorder > 1) && (defaultorder < 12))
+		df = defaultorder;
+	else
+		df = 2;	
+
+	for (order = df; order >= DMABUF_MINORDER; order--)
+		if ( (rawbuf = (void *) pci_alloc_consistent(
+			card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr)))
+			    break;
+	if (!rawbuf) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			"cs46xx: alloc_dmabuf(): unable to allocate rawbuf\n"));
+		return -ENOMEM;
+	}
+	dmabuf->buforder = order;
+	dmabuf->rawbuf = rawbuf;
+	// Now mark the pages as reserved; otherwise the 
+	// remap_page_range() in cs46xx_mmap doesn't work.
+	// 1. get index to last page in mem_map array for rawbuf.
+	mapend = virt_to_page(dmabuf->rawbuf + 
+		(PAGE_SIZE << dmabuf->buforder) - 1);
+
+	// 2. mark each physical page in range as 'reserved'.
+	for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
+		cs4x_mem_map_reserve(map);
+
+	CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: alloc_dmabuf(): allocated %ld (order = %d) bytes at %p\n",
+	       PAGE_SIZE << order, order, rawbuf) );
+
+/*
+*  only allocate the conversion buffer for the ADC
+*/
+	if(dmabuf->type == CS_TYPE_DAC)
+	{
+		dmabuf->tmpbuff = NULL;
+		dmabuf->buforder_tmpbuff = 0;
+		return 0;
+	}
+/*
+ * now the temp buffer for 16/8 conversions
+ */
+
+	tmpbuff = (void *) pci_alloc_consistent(
+		card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr_tmpbuff);
+
+	if (!tmpbuff)
+		return -ENOMEM;
+	CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: allocated %ld (order = %d) bytes at %p\n",
+	       PAGE_SIZE << order, order, tmpbuff) );
+
+	dmabuf->tmpbuff = tmpbuff;
+	dmabuf->buforder_tmpbuff = order;
+	
+	// Now mark the pages as reserved; otherwise the 
+	// remap_page_range() in cs46xx_mmap doesn't work.
+	// 1. get index to last page in mem_map array for rawbuf.
+	mapend = virt_to_page(dmabuf->tmpbuff + 
+		(PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
+
+	// 2. mark each physical page in range as 'reserved'.
+	for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++)
+		cs4x_mem_map_reserve(map);
+	return 0;
+}
+
+/* free DMA buffer */
+static void dealloc_dmabuf(struct cs_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct page *map, *mapend;
+
+	if (dmabuf->rawbuf) {
+		// Undo prog_dmabuf()'s marking the pages as reserved 
+		mapend = virt_to_page(dmabuf->rawbuf + 
+				(PAGE_SIZE << dmabuf->buforder) - 1);
+		for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
+			cs4x_mem_map_unreserve(map);
+		free_dmabuf(state->card, dmabuf);
+	}
+
+	if (dmabuf->tmpbuff) {
+		// Undo prog_dmabuf()'s marking the pages as reserved 
+		mapend = virt_to_page(dmabuf->tmpbuff +
+				(PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1);
+		for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++)
+			cs4x_mem_map_unreserve(map);
+		free_dmabuf2(state->card, dmabuf);
+	}
+
+	dmabuf->rawbuf = NULL;
+	dmabuf->tmpbuff = NULL;
+	dmabuf->mapped = dmabuf->ready = 0;
+	dmabuf->SGok = 0;
+}
+
+static int __prog_dmabuf(struct cs_state *state)
+{
+        struct dmabuf *dmabuf = &state->dmabuf;
+        unsigned long flags;
+        unsigned long allocated_pages, allocated_bytes;                     
+        unsigned long tmp1, tmp2, fmt=0;                                           
+        unsigned long *ptmp = (unsigned long *) dmabuf->pbuf;               
+        unsigned long SGarray[9], nSGpages=0;                               
+        int ret;
+
+	CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()+ \n"));
+/*
+ * check for CAPTURE and use only non-sg for initial release
+ */
+	if(dmabuf->type == CS_TYPE_ADC)
+	{
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() ADC\n"));
+		/* 
+		 * add in non-sg support for capture.
+		 */
+		spin_lock_irqsave(&state->card->lock, flags);
+	/* add code to reset the rawbuf memory. TRW */
+		resync_dma_ptrs(state);
+		dmabuf->total_bytes = dmabuf->blocks = 0;
+		dmabuf->count = dmabuf->error = dmabuf->underrun = 0;
+
+		dmabuf->SGok = 0;                                                   
+
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		/* allocate DMA buffer if not allocated yet */
+		if (!dmabuf->rawbuf || !dmabuf->tmpbuff)
+			if ((ret = alloc_dmabuf(state)))
+				return ret; 
+	/*
+	 * static image only supports 16Bit signed, stereo - hard code fmt
+	 */
+		fmt = CS_FMT_16BIT | CS_FMT_STEREO;
+
+		dmabuf->numfrag = 2;                                        
+		dmabuf->fragsize = 2048;                                    
+		dmabuf->fragsamples = 2048 >> sample_shift[fmt];    
+		dmabuf->dmasize = 4096;                                     
+		dmabuf->fragshift = 11;                                     
+
+		memset(dmabuf->rawbuf, (fmt & CS_FMT_16BIT) ? 0 : 0x80,
+		       dmabuf->dmasize);
+        	memset(dmabuf->tmpbuff, (fmt & CS_FMT_16BIT) ? 0 : 0x80, 
+			PAGE_SIZE<<dmabuf->buforder_tmpbuff);      
+
+		/*
+		 *      Now set up the ring
+		 */
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		cs_rec_setup(state);
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		/* set the ready flag for the dma buffer */
+		dmabuf->ready = 1;
+
+		CS_DBGOUT(CS_PARMS, 4, printk(
+			"cs46xx: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d "
+			"fragsize=%d dmasize=%d\n",
+			    dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
+			    dmabuf->fragsize, dmabuf->dmasize) );
+
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- 0 \n"));
+		return 0;
+	}
+	else if (dmabuf->type == CS_TYPE_DAC)
+	{
+	/*
+	 * Must be DAC
+	 */
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() DAC\n"));
+		spin_lock_irqsave(&state->card->lock, flags);
+		resync_dma_ptrs(state);
+		dmabuf->total_bytes = dmabuf->blocks = 0;
+		dmabuf->count = dmabuf->error = dmabuf->underrun = 0;
+
+		dmabuf->SGok = 0;                                                   
+
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		/* allocate DMA buffer if not allocated yet */
+		if (!dmabuf->rawbuf)
+			if ((ret = alloc_dmabuf(state)))
+				return ret;
+
+		allocated_pages = 1 << dmabuf->buforder;                            
+		allocated_bytes = allocated_pages*PAGE_SIZE;                        
+										    
+		if(allocated_pages < 2)                                             
+		{
+			CS_DBGOUT(CS_FUNCTION, 4, printk(
+			    "cs46xx: prog_dmabuf() Error: allocated_pages too small (%d)\n",
+				(unsigned)allocated_pages));
+			return -ENOMEM;
+		}
+										    
+		/* Use all the pages allocated, fragsize 4k. */
+		/* Use 'pbuf' for S/G page map table. */
+		dmabuf->SGok = 1;           /* Use S/G. */
+
+		nSGpages = allocated_bytes/4096;    /* S/G pages always 4k. */
+										    
+		     /* Set up S/G variables. */
+		*ptmp = virt_to_bus(dmabuf->rawbuf);                                
+		*(ptmp+1) = 0x00000008;                                             
+		for(tmp1= 1; tmp1 < nSGpages; tmp1++) {                             
+			*(ptmp+2*tmp1) = virt_to_bus( (dmabuf->rawbuf)+4096*tmp1);  
+			if( tmp1 == nSGpages-1)                                     
+				tmp2 = 0xbfff0000;
+			else                                                        
+				tmp2 = 0x80000000+8*(tmp1+1);                       
+			*(ptmp+2*tmp1+1) = tmp2;                                    
+		}                                                                   
+		SGarray[0] = 0x82c0200d;                                            
+		SGarray[1] = 0xffff0000;                                            
+		SGarray[2] = *ptmp;                                                 
+		SGarray[3] = 0x00010600;                                            
+		SGarray[4] = *(ptmp+2);                                             
+		SGarray[5] = 0x80000010;                                            
+		SGarray[6] = *ptmp;                                                 
+		SGarray[7] = *(ptmp+2);                                             
+		SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10;        
+
+		if (dmabuf->SGok) {                                                 
+			dmabuf->numfrag = nSGpages;                                 
+			dmabuf->fragsize = 4096;                                    
+			dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt];    
+			dmabuf->fragshift = 12;                                     
+			dmabuf->dmasize = dmabuf->numfrag*4096;                     
+		}                                                                   
+		else {                                                              
+			SGarray[0] = 0xf2c0000f;                                    
+			SGarray[1] = 0x00000200;                                    
+			SGarray[2] = 0;                                             
+			SGarray[3] = 0x00010600;                                    
+			SGarray[4]=SGarray[5]=SGarray[6]=SGarray[7]=SGarray[8] = 0; 
+			dmabuf->numfrag = 2;                                        
+			dmabuf->fragsize = 2048;                                    
+			dmabuf->fragsamples = 2048 >> sample_shift[dmabuf->fmt];    
+			dmabuf->dmasize = 4096;                                     
+			dmabuf->fragshift = 11;                                     
+		}
+		for(tmp1 = 0; tmp1 < sizeof(SGarray)/4; tmp1++)                     
+			cs461x_poke( state->card, BA1_PDTC+tmp1*4, SGarray[tmp1]);  
+
+		memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
+		       dmabuf->dmasize);
+
+		/*
+		 *      Now set up the ring
+		 */
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		cs_play_setup(state);
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		/* set the ready flag for the dma buffer */
+		dmabuf->ready = 1;
+
+		CS_DBGOUT(CS_PARMS, 4, printk(
+			"cs46xx: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d "
+			"fragsize=%d dmasize=%d\n",
+			    dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
+			    dmabuf->fragsize, dmabuf->dmasize) );
+
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- \n"));
+		return 0;
+	}
+	else
+	{
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- Invalid Type %d\n",
+			dmabuf->type));
+	}
+	return 1;
+}
+
+static int prog_dmabuf(struct cs_state *state)
+{
+	int ret;
+	
+	down(&state->sem);
+	ret = __prog_dmabuf(state);
+	up(&state->sem);
+	
+	return ret;
+}
+
+static void cs_clear_tail(struct cs_state *state)
+{
+}
+
+static int drain_dac(struct cs_state *state, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct cs_card *card=state->card;
+	unsigned long flags;
+	unsigned long tmo;
+	int count;
+
+	CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()+ \n"));
+	if (dmabuf->mapped || !dmabuf->ready)
+	{
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0, not ready\n"));
+		return 0;
+	}
+
+	add_wait_queue(&dmabuf->wait, &wait);
+	for (;;) {
+		/* It seems that we have to set the current state to TASK_INTERRUPTIBLE
+		   every time to make the process really go to sleep */
+		current->state = TASK_INTERRUPTIBLE;
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		count = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (count <= 0)
+			break;
+
+		if (signal_pending(current))
+			break;
+
+		if (nonblock) {
+			remove_wait_queue(&dmabuf->wait, &wait);
+			current->state = TASK_RUNNING;
+			return -EBUSY;
+		}
+
+		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
+		tmo >>= sample_shift[dmabuf->fmt];
+		tmo += (2048*HZ)/dmabuf->rate;
+		
+		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
+			printk(KERN_ERR "cs46xx: drain_dac, dma timeout? %d\n", count);
+			break;
+		}
+	}
+	remove_wait_queue(&dmabuf->wait, &wait);
+	current->state = TASK_RUNNING;
+	if (signal_pending(current))
+	{
+		CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- -ERESTARTSYS\n"));
+		/*
+		* set to silence and let that clear the fifos.
+		*/
+		cs461x_clear_serial_FIFOs(card, CS_TYPE_DAC);
+		return -ERESTARTSYS;
+	}
+
+	CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0\n"));
+	return 0;
+}
+
+
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
+static void cs_update_ptr(struct cs_card *card, int wake)
+{
+	struct cs_state *state;
+	struct dmabuf *dmabuf;
+	unsigned hwptr;
+	int diff;
+
+	/* error handling and process wake up for ADC */
+	state = card->states[0];
+	if(state)
+	{
+		dmabuf = &state->dmabuf;
+		if (dmabuf->enable & ADC_RUNNING) {
+			/* update hardware pointer */
+			hwptr = cs_get_dma_addr(state);
+
+			diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+			CS_DBGOUT(CS_PARMS, 9, printk(
+				"cs46xx: cs_update_ptr()+ ADC hwptr=%d diff=%d\n", 
+				hwptr,diff) );
+			dmabuf->hwptr = hwptr;
+			dmabuf->total_bytes += diff;
+			dmabuf->count += diff;
+			if (dmabuf->count > dmabuf->dmasize)
+				dmabuf->count = dmabuf->dmasize;
+
+			if(dmabuf->mapped)
+			{
+				if (wake && dmabuf->count >= (signed)dmabuf->fragsize)
+					wake_up(&dmabuf->wait);
+			} else 
+			{
+				if (wake && dmabuf->count > 0)
+					wake_up(&dmabuf->wait);
+			}
+		}
+	}
+
+/*
+ * Now the DAC
+ */
+	state = card->states[1];
+	if(state)
+	{
+		dmabuf = &state->dmabuf;
+		/* error handling and process wake up for DAC */
+		if (dmabuf->enable & DAC_RUNNING) {
+			/* update hardware pointer */
+			hwptr = cs_get_dma_addr(state);
+
+			diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+			CS_DBGOUT(CS_PARMS, 9, printk(
+				"cs46xx: cs_update_ptr()+ DAC hwptr=%d diff=%d\n", 
+				hwptr,diff) );
+			dmabuf->hwptr = hwptr;
+			dmabuf->total_bytes += diff;
+			if (dmabuf->mapped) {
+				dmabuf->count += diff;
+				if (wake && dmabuf->count >= (signed)dmabuf->fragsize)
+					wake_up(&dmabuf->wait);
+				/*
+				 * other drivers use fragsize, but don't see any sense
+				 * in that, since dmasize is the buffer asked for
+				 * via mmap.
+				 */
+				if( dmabuf->count > dmabuf->dmasize)
+					dmabuf->count &= dmabuf->dmasize-1;
+			} else {
+				dmabuf->count -= diff;
+				/*
+				 * backfill with silence and clear out the last 
+				 * "diff" number of bytes.
+				 */
+				if(hwptr >= diff)
+				{
+					memset(dmabuf->rawbuf + hwptr - diff, 
+						(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, diff);
+				}
+				else
+				{
+					memset(dmabuf->rawbuf, 
+						(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
+						(unsigned)hwptr);
+					memset((void *)((unsigned)dmabuf->rawbuf + 
+							dmabuf->dmasize + hwptr - diff),
+						(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, 
+						diff - hwptr); 
+				}
+
+				if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
+					CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					  "cs46xx: ERROR DAC count<0 or count > dmasize (%d)\n",
+					  	dmabuf->count));
+					/* 
+					* buffer underrun or buffer overrun, reset the
+					* count of bytes written back to 0.
+					*/
+					if(dmabuf->count < 0)
+						dmabuf->underrun=1;
+					dmabuf->count = 0;
+					dmabuf->error++;
+				}
+				if (wake && dmabuf->count < (signed)dmabuf->dmasize/2)
+					wake_up(&dmabuf->wait);
+			}
+		}
+	}
+}
+
+
+/* hold spinlock for the following! */
+static void cs_handle_midi(struct cs_card *card)
+{
+        unsigned char ch;
+        int wake;
+        unsigned temp1;
+
+        wake = 0;
+        while (!(cs461x_peekBA0(card,  BA0_MIDSR) & MIDSR_RBE)) {
+                ch = cs461x_peekBA0(card, BA0_MIDRP);
+                if (card->midi.icnt < CS_MIDIINBUF) {
+                        card->midi.ibuf[card->midi.iwr] = ch;
+                        card->midi.iwr = (card->midi.iwr + 1) % CS_MIDIINBUF;
+                        card->midi.icnt++;
+                }
+                wake = 1;
+        }
+        if (wake)
+                wake_up(&card->midi.iwait);
+        wake = 0;
+        while (!(cs461x_peekBA0(card,  BA0_MIDSR) & MIDSR_TBF) && card->midi.ocnt > 0) {
+                temp1 = ( card->midi.obuf[card->midi.ord] ) & 0x000000ff;
+                cs461x_pokeBA0(card, BA0_MIDWP,temp1);
+                card->midi.ord = (card->midi.ord + 1) % CS_MIDIOUTBUF;
+                card->midi.ocnt--;
+                if (card->midi.ocnt < CS_MIDIOUTBUF-16)
+                        wake = 1;
+        }
+        if (wake)
+                wake_up(&card->midi.owait);
+}
+
+static void cs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct cs_card *card = (struct cs_card *)dev_id;
+	/* Single channel card */
+	struct cs_state *recstate = card->channel[0].state;
+	struct cs_state *playstate = card->channel[1].state;
+	u32 status;
+
+	CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()+ \n"));
+
+	spin_lock(&card->lock);
+
+	status = cs461x_peekBA0(card, BA0_HISR);
+	
+	if ((status & 0x7fffffff) == 0)
+	{
+		cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV);
+		spin_unlock(&card->lock);
+		return;
+	}
+	
+	/*
+	 * check for playback or capture interrupt only
+	 */
+	if( ((status & HISR_VC0) && playstate && playstate->dmabuf.ready) || 
+	    (((status & HISR_VC1) && recstate && recstate->dmabuf.ready)) )
+	{
+		CS_DBGOUT(CS_INTERRUPT, 8, printk(
+			"cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status));
+		cs_update_ptr(card, CS_TRUE);
+	}
+
+        if( status & HISR_MIDI )
+                cs_handle_midi(card);
+	
+ 	/* clear 'em */
+	cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV);
+	spin_unlock(&card->lock);
+	CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()- \n"));
+}
+
+
+/**********************************************************************/
+
+static ssize_t cs_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+        struct cs_card *card = (struct cs_card *)file->private_data;
+        ssize_t ret;
+        unsigned long flags;
+        unsigned ptr;
+        int cnt;
+
+        if (ppos != &file->f_pos)
+                return -ESPIPE;
+        if (!access_ok(VERIFY_WRITE, buffer, count))
+                return -EFAULT;
+        ret = 0;
+        while (count > 0) {
+                spin_lock_irqsave(&card->lock, flags);
+                ptr = card->midi.ird;
+                cnt = CS_MIDIINBUF - ptr;
+                if (card->midi.icnt < cnt)
+                        cnt = card->midi.icnt;
+                spin_unlock_irqrestore(&card->lock, flags);
+                if (cnt > count)
+                        cnt = count;
+                if (cnt <= 0) {
+                        if (file->f_flags & O_NONBLOCK)
+                                return ret ? ret : -EAGAIN;
+                        interruptible_sleep_on(&card->midi.iwait);
+                        if (signal_pending(current))
+                                return ret ? ret : -ERESTARTSYS;
+                        continue;
+                }
+                if (copy_to_user(buffer, card->midi.ibuf + ptr, cnt))
+                        return ret ? ret : -EFAULT;
+                ptr = (ptr + cnt) % CS_MIDIINBUF;
+                spin_lock_irqsave(&card->lock, flags);
+                card->midi.ird = ptr;
+                card->midi.icnt -= cnt;
+                spin_unlock_irqrestore(&card->lock, flags);
+                count -= cnt;
+                buffer += cnt;
+                ret += cnt;
+        }
+        return ret;
+}
+
+
+static ssize_t cs_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+        struct cs_card *card = (struct cs_card *)file->private_data;
+        ssize_t ret;
+        unsigned long flags;
+        unsigned ptr;
+        int cnt;
+
+        if (ppos != &file->f_pos)
+                return -ESPIPE;
+        if (!access_ok(VERIFY_READ, buffer, count))
+                return -EFAULT;
+        ret = 0;
+        while (count > 0) {
+                spin_lock_irqsave(&card->lock, flags);
+                ptr = card->midi.owr;
+                cnt = CS_MIDIOUTBUF - ptr;
+                if (card->midi.ocnt + cnt > CS_MIDIOUTBUF)
+                        cnt = CS_MIDIOUTBUF - card->midi.ocnt;
+                if (cnt <= 0)
+                        cs_handle_midi(card);
+                spin_unlock_irqrestore(&card->lock, flags);
+                if (cnt > count)
+                        cnt = count;
+                if (cnt <= 0) {
+                        if (file->f_flags & O_NONBLOCK)
+                                return ret ? ret : -EAGAIN;
+                        interruptible_sleep_on(&card->midi.owait);
+                        if (signal_pending(current))
+                                return ret ? ret : -ERESTARTSYS;
+                        continue;
+                }
+                if (copy_from_user(card->midi.obuf + ptr, buffer, cnt))
+                        return ret ? ret : -EFAULT;
+                ptr = (ptr + cnt) % CS_MIDIOUTBUF;
+                spin_lock_irqsave(&card->lock, flags);
+                card->midi.owr = ptr;
+                card->midi.ocnt += cnt;
+                spin_unlock_irqrestore(&card->lock, flags);
+                count -= cnt;
+                buffer += cnt;
+                ret += cnt;
+                spin_lock_irqsave(&card->lock, flags);
+                cs_handle_midi(card);
+                spin_unlock_irqrestore(&card->lock, flags);
+        }
+        return ret;
+}
+
+
+static unsigned int cs_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+        struct cs_card *card = (struct cs_card *)file->private_data;
+        unsigned long flags;
+        unsigned int mask = 0;
+
+        if (file->f_flags & FMODE_WRITE)
+                poll_wait(file, &card->midi.owait, wait);
+        if (file->f_flags & FMODE_READ)
+                poll_wait(file, &card->midi.iwait, wait);
+        spin_lock_irqsave(&card->lock, flags);
+        if (file->f_flags & FMODE_READ) {
+                if (card->midi.icnt > 0)
+                        mask |= POLLIN | POLLRDNORM;
+        }
+        if (file->f_flags & FMODE_WRITE) {
+                if (card->midi.ocnt < CS_MIDIOUTBUF)
+                        mask |= POLLOUT | POLLWRNORM;
+        }
+        spin_unlock_irqrestore(&card->lock, flags);
+        return mask;
+}
+
+
+static int cs_midi_open(struct inode *inode, struct file *file)
+{
+        unsigned int minor = minor(inode->i_rdev);
+        struct cs_card *card=NULL;
+        unsigned long flags;
+	struct list_head *entry;
+
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
+		if (card->dev_midi == minor)
+			break;
+	}
+
+	if (entry == &cs46xx_devs)
+		return -ENODEV;
+	if (!card)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n"));
+		return -ENODEV;
+	}
+
+        file->private_data = card;
+        /* wait for device to become free */
+        down(&card->midi.open_sem);
+        while (card->midi.open_mode & file->f_mode) {
+                if (file->f_flags & O_NONBLOCK) {
+                        up(&card->midi.open_sem);
+                        return -EBUSY;
+                }
+                up(&card->midi.open_sem);
+                interruptible_sleep_on(&card->midi.open_wait);
+                if (signal_pending(current))
+                        return -ERESTARTSYS;
+                down(&card->midi.open_sem);
+        }
+        spin_lock_irqsave(&card->midi.lock, flags);
+        if (!(card->midi.open_mode & (FMODE_READ | FMODE_WRITE))) {
+                card->midi.ird = card->midi.iwr = card->midi.icnt = 0;
+                card->midi.ord = card->midi.owr = card->midi.ocnt = 0;
+                card->midi.ird = card->midi.iwr = card->midi.icnt = 0;
+                cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f);            /* Enable xmit, rcv. */
+                cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM);   /* Enable interrupts */
+        }
+        if (file->f_mode & FMODE_READ) {
+                card->midi.ird = card->midi.iwr = card->midi.icnt = 0;
+        }
+        if (file->f_mode & FMODE_WRITE) {
+                card->midi.ord = card->midi.owr = card->midi.ocnt = 0;
+        }
+        spin_unlock_irqrestore(&card->midi.lock, flags);
+        card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE));
+        up(&card->midi.open_sem);
+        MOD_INC_USE_COUNT; /* for 2.2 */
+        return 0;
+}
+
+
+static int cs_midi_release(struct inode *inode, struct file *file)
+{
+        struct cs_card *card = (struct cs_card *)file->private_data;
+        DECLARE_WAITQUEUE(wait, current);
+        unsigned long flags;
+        unsigned count, tmo;
+
+        if (file->f_mode & FMODE_WRITE) {
+                current->state = TASK_INTERRUPTIBLE;
+                add_wait_queue(&card->midi.owait, &wait);
+                for (;;) {
+                        spin_lock_irqsave(&card->midi.lock, flags);
+                        count = card->midi.ocnt;
+                        spin_unlock_irqrestore(&card->midi.lock, flags);
+                        if (count <= 0)
+                                break;
+                        if (signal_pending(current))
+                                break;
+                        if (file->f_flags & O_NONBLOCK) {
+                                remove_wait_queue(&card->midi.owait, &wait);
+                                current->state = TASK_RUNNING;
+                                return -EBUSY;
+                        }                      
+                        tmo = (count * HZ) / 3100;
+                        if (!schedule_timeout(tmo ? : 1) && tmo)
+                                printk(KERN_DEBUG "cs46xx: midi timed out??\n");
+                }
+                remove_wait_queue(&card->midi.owait, &wait);
+                current->state = TASK_RUNNING;
+        }
+        down(&card->midi.open_sem);
+        card->midi.open_mode &= (~(file->f_mode & (FMODE_READ | FMODE_WRITE)));
+        up(&card->midi.open_sem);
+        wake_up(&card->midi.open_wait);
+        MOD_DEC_USE_COUNT; /* for 2.2 */
+        return 0;
+}
+
+/*
+ *   Midi file operations struct.
+ */
+static /*const*/ struct file_operations cs_midi_fops = {
+	CS_OWNER	CS_THIS_MODULE
+	llseek:		no_llseek,
+	read:		cs_midi_read,
+	write:		cs_midi_write,
+	poll:		cs_midi_poll,
+	open:		cs_midi_open,
+	release:	cs_midi_release,
+};
+
+/*
+ *
+ * CopySamples copies 16-bit stereo signed samples from the source to the
+ * destination, possibly converting down to unsigned 8-bit and/or mono.
+ * count specifies the number of output bytes to write.
+ *
+ *  Arguments:
+ *
+ *  dst             - Pointer to a destination buffer.
+ *  src             - Pointer to a source buffer
+ *  count           - The number of bytes to copy into the destination buffer.
+ *  fmt             - CS_FMT_16BIT and/or CS_FMT_STEREO bits
+ *  dmabuf          - pointer to the dma buffer structure
+ *
+ * NOTES: only call this routine if the output desired is not 16 Signed Stereo
+ * 	
+ *
+ */
+static void CopySamples(char *dst, char *src, int count, unsigned fmt, 
+		struct dmabuf *dmabuf)
+{
+
+    s32 s32AudioSample;
+    s16 *psSrc=(s16 *)src;
+    s16 *psDst=(s16 *)dst;
+    u8 *pucDst=(u8 *)dst;
+
+    CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") );
+    CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
+	" dst=0x%x src=0x%x count=%d fmt=0x%x\n",
+	(unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) );
+
+    /*
+     * See if the data should be output as 8-bit unsigned stereo.
+     */
+    if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT))
+    {
+        /*
+         * Convert each 16-bit signed stereo sample to 8-bit unsigned 
+	 * stereo using rounding.
+         */
+        psSrc = (s16 *)src;
+	count = count/2;
+        while(count--)
+        {
+            *(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8);
+        }
+    }
+    /*
+     * See if the data should be output at 8-bit unsigned mono.
+     */
+    else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT))
+    {
+        /*
+         * Convert each 16-bit signed stereo sample to 8-bit unsigned 
+	 * mono using averaging and rounding.
+         */
+        psSrc = (s16 *)src;
+	count = count/2;
+        while(count--)
+        {
+	    s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80;
+	    if(s32AudioSample > 0x7fff)
+	    	s32AudioSample = 0x7fff;
+            *(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8);
+	    psSrc += 2;
+        }
+    }
+    /*
+     * See if the data should be output at 16-bit signed mono.
+     */
+    else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT))
+    {
+        /*
+         * Convert each 16-bit signed stereo sample to 16-bit signed 
+	 * mono using averaging.
+         */
+        psSrc = (s16 *)src;
+	count = count/2;
+        while(count--)
+        {
+            *(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2;
+	    psSrc += 2;
+        }
+    }
+}
+
+/*
+ * cs_copy_to_user()
+ * replacement for the standard copy_to_user, to allow for a conversion from
+ * 16 bit to 8 bit and from stereo to mono, if the record conversion is active.  
+ * The current CS46xx/CS4280 static image only records in 16bit unsigned Stereo, 
+ * so we convert from any of the other format combinations.
+ */
+static unsigned cs_copy_to_user(
+	struct cs_state *s, 
+	void *dest, 
+	void *hwsrc, 
+	unsigned cnt, 
+	unsigned *copied)
+{
+	struct dmabuf *dmabuf = &s->dmabuf;
+	void *src = hwsrc;  /* default to the standard destination buffer addr */
+
+	CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO 
+		"cs_copy_to_user()+ fmt=0x%x cnt=%d dest=0x%.8x\n",
+		dmabuf->fmt,(unsigned)cnt,(unsigned)dest) );
+
+	if(cnt > dmabuf->dmasize)
+	{
+		cnt = dmabuf->dmasize;
+	}
+	if(!cnt)
+	{
+		*copied = 0;
+		return 0;
+	}
+	if(dmabuf->divisor != 1)
+	{
+		if(!dmabuf->tmpbuff)
+		{
+			*copied = cnt/dmabuf->divisor;
+			return 0;
+		}
+
+		CopySamples((char *)dmabuf->tmpbuff, (char *)hwsrc, cnt, 
+			dmabuf->fmt, dmabuf);
+		src = dmabuf->tmpbuff;
+		cnt = cnt/dmabuf->divisor;
+	}
+        if (copy_to_user(dest, src, cnt))
+	{
+		CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR 
+			"cs46xx: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n",
+				(unsigned)dest,(unsigned)src,cnt) );
+		*copied = 0;
+		return -EFAULT;
+	}
+	*copied = cnt;
+	CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO 
+		"cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) );
+	return 0;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
+   the user's buffer.  it is filled by the dma machine and drained by this loop. */
+static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct cs_card *card = (struct cs_card *) file->private_data;
+	struct cs_state *state;
+	DECLARE_WAITQUEUE(wait, current);
+	struct dmabuf *dmabuf;
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+	unsigned copied=0;
+
+	CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, 
+		printk("cs46xx: cs_read()+ %d\n",count) );
+	state = (struct cs_state *)card->states[0];
+	if(!state)
+		return -ENODEV;
+	dmabuf = &state->dmabuf;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	
+	down(&state->sem);
+	if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
+		goto out;
+
+	add_wait_queue(&state->dmabuf.wait, &wait);
+	while (count > 0) {
+		while(!(card->pm.flags & CS46XX_PM_IDLE))
+		{
+			schedule();
+			if (signal_pending(current)) {
+				if(!ret) ret = -ERESTARTSYS;
+				goto out;
+			}
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		swptr = dmabuf->swptr;
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count < cnt)
+			cnt = dmabuf->count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (cnt > (count * dmabuf->divisor))
+			cnt = count * dmabuf->divisor;
+		if (cnt <= 0) {
+			/* buffer is empty, start the dma machine and wait for data to be
+			   recorded */
+			start_adc(state);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				goto out;
+ 			}
+			up(&state->sem);
+			schedule();
+			if (signal_pending(current)) {
+				if(!ret) ret = -ERESTARTSYS;
+				goto out;
+			}
+			down(&state->sem);
+			if (dmabuf->mapped) 
+			{
+				if(!ret)
+					ret = -ENXIO;
+				goto out;
+			}
+ 			continue;
+		}
+
+		CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO 
+			"_read() copy_to cnt=%d count=%d ", cnt,count) );
+		CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO 
+			" .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n",
+			dmabuf->dmasize,dmabuf->count,(unsigned)buffer,ret) );
+
+                if (cs_copy_to_user(state, buffer, 
+			(void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied))
+		{
+			if (!ret) ret = -EFAULT;
+			goto out;
+		}
+                swptr = (swptr + cnt) % dmabuf->dmasize;
+                spin_lock_irqsave(&card->lock, flags);
+                dmabuf->swptr = swptr;
+                dmabuf->count -= cnt;
+                spin_unlock_irqrestore(&card->lock, flags);
+                count -= copied;
+                buffer += copied;
+                ret += copied;
+                start_adc(state);
+	}
+out:
+	up(&state->sem);
+	remove_wait_queue(&state->dmabuf.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, 
+		printk("cs46xx: cs_read()- %d\n",ret) );
+	return ret;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
+   the soundcard.  it is drained by the dma machine and filled by this loop. */
+static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct cs_card *card = (struct cs_card *) file->private_data;
+	struct cs_state *state;
+	DECLARE_WAITQUEUE(wait, current);
+	struct dmabuf *dmabuf;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4,
+		printk("cs46xx: cs_write called, count = %d\n", count) );
+	state = (struct cs_state *)card->states[1];
+	if(!state)
+		return -ENODEV;
+	dmabuf = &state->dmabuf;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	down(&state->sem);
+	if (dmabuf->mapped)
+	{
+		ret = -ENXIO;
+		goto out;
+	}
+
+	if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
+		goto out;
+	if (!access_ok(VERIFY_READ, buffer, count))
+	{
+	ret = -EFAULT;
+	goto out;
+	}
+	add_wait_queue(&state->dmabuf.wait, &wait);
+	ret = 0;
+/*
+* Start the loop to read from the user's buffer and write to the dma buffer.
+* check for PM events and underrun/overrun in the loop.
+*/
+	while (count > 0) {
+		while(!(card->pm.flags & CS46XX_PM_IDLE))
+		{
+			schedule();
+			if (signal_pending(current)) {
+				if(!ret) ret = -ERESTARTSYS;
+				goto out;
+			}
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		if (dmabuf->count < 0) {
+			/* buffer underrun, we are recovering from sleep_on_timeout,
+			   resync hwptr and swptr */
+			dmabuf->count = 0;
+			dmabuf->swptr = dmabuf->hwptr;
+		}
+		if (dmabuf->underrun)
+		{
+			dmabuf->underrun = 0;
+			dmabuf->hwptr = cs_get_dma_addr(state);
+			dmabuf->swptr = dmabuf->hwptr;
+		}
+
+		swptr = dmabuf->swptr;
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count + cnt > dmabuf->dmasize)
+			cnt = dmabuf->dmasize - dmabuf->count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			/* buffer is full, start the dma machine and wait for data to be
+			   played */
+			start_dac(state);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				goto out;
+ 			}
+			up(&state->sem);
+			schedule();
+ 			if (signal_pending(current)) {
+				if(!ret) ret = -ERESTARTSYS;
+				goto out;
+ 			}
+			down(&state->sem);
+			if (dmabuf->mapped)
+			{
+				if(!ret)
+					ret = -ENXIO;
+				goto out;
+			}
+ 			continue;
+ 		}
+		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+			if (!ret) ret = -EFAULT;
+			goto out;
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		swptr = (swptr + cnt) % dmabuf->dmasize;
+		dmabuf->swptr = swptr;
+		dmabuf->count += cnt;
+		if(dmabuf->count > dmabuf->dmasize)
+		{
+			CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk(
+			    "cs46xx: cs_write() d->count > dmasize - resetting\n"));
+			dmabuf->count = dmabuf->dmasize;
+		}
+		dmabuf->endcleared = 0;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(state);
+	}
+out:
+	up(&state->sem);
+	remove_wait_queue(&state->dmabuf.wait, &wait);
+	set_current_state(TASK_RUNNING);
+
+	CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2, 
+		printk("cs46xx: cs_write()- ret=0x%x\n", ret) );
+	return ret;
+}
+
+static unsigned int cs_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct cs_card *card = (struct cs_card *)file->private_data;
+	struct dmabuf *dmabuf;
+	struct cs_state *state;
+
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()+ \n"));
+	if (!(file->f_mode & (FMODE_WRITE | FMODE_READ)))
+	{
+		return -EINVAL;
+	}
+	if (file->f_mode & FMODE_WRITE)
+	{
+		state = card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			poll_wait(file, &dmabuf->wait, wait);
+		}
+	}
+	if (file->f_mode & FMODE_READ)
+	{
+		state = card->states[0];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			poll_wait(file, &dmabuf->wait, wait);
+		}
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	cs_update_ptr(card, CS_FALSE);
+	if (file->f_mode & FMODE_READ) {
+		state = card->states[0];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			if (dmabuf->count >= (signed)dmabuf->fragsize)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		state = card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			if (dmabuf->mapped) {
+				if (dmabuf->count >= (signed)dmabuf->fragsize)
+				    mask |= POLLOUT | POLLWRNORM;
+			} else {
+				if ((signed)dmabuf->dmasize >= dmabuf->count 
+					+ (signed)dmabuf->fragsize)
+				    mask |= POLLOUT | POLLWRNORM;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- (0x%x) \n",
+		mask));
+	return mask;
+}
+
+/*
+ *	We let users mmap the ring buffer. Its not the real DMA buffer but
+ *	that side of the code is hidden in the IRQ handling. We do a software
+ *	emulation of DMA from a 64K or so buffer into a 2K FIFO. 
+ *	(the hardware probably deserves a moan here but Crystal send me nice
+ *	toys ;)).
+ */
+ 
+static int cs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cs_card *card = (struct cs_card *)file->private_data;
+	struct cs_state *state;
+	struct dmabuf *dmabuf;
+	int ret = 0;
+	unsigned long size;
+
+	CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=0x%x %s %s\n", 
+		(unsigned)file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "",
+		vma->vm_flags & VM_READ ? "VM_READ" : "") );
+
+	if (vma->vm_flags & VM_WRITE) {
+		state = card->states[1];
+		if(state)
+		{
+			CS_DBGOUT(CS_OPEN, 2, printk(
+			  "cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") );
+			if ((ret = prog_dmabuf(state)) != 0)
+				return ret;
+		}
+	} else if (vma->vm_flags & VM_READ) {
+		state = card->states[0];
+		if(state)
+		{
+			CS_DBGOUT(CS_OPEN, 2, printk(
+			  "cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") );
+			if ((ret = prog_dmabuf(state)) != 0)
+				return ret;
+		}
+	} else {
+		CS_DBGOUT(CS_ERROR, 2, printk(
+		  "cs46xx: cs_mmap() return -EINVAL\n") );
+		return -EINVAL;
+	}
+
+/*
+ * For now ONLY support playback, but seems like the only way to use
+ * mmap() is to open an FD with RDWR, just read or just write access
+ * does not function, get an error back from the kernel.
+ * Also, QuakeIII opens with RDWR!  So, there must be something
+ * to needing read/write access mapping.  So, allow read/write but 
+ * use the DAC only.
+ */
+	state = card->states[1];  
+	if(!(unsigned)state)
+	{
+		ret = -EINVAL;
+		goto out;
+	}
+
+	down(&state->sem);	
+	dmabuf = &state->dmabuf;
+	if (cs4x_pgoff(vma) != 0)
+	{
+		ret = -EINVAL;
+		goto out;
+	}
+	size = vma->vm_end - vma->vm_start;
+
+	CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) );
+
+	if (size > (PAGE_SIZE << dmabuf->buforder))
+	{
+		ret = -EINVAL;
+		goto out;
+	}
+	if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+			     size, vma->vm_page_prot))
+	{
+		ret = -EAGAIN;
+		goto out;
+	}
+	dmabuf->mapped = 1;
+
+	CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mmap()-\n") );
+out:
+	up(&state->sem);
+	return ret;	
+}
+
+static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct cs_card *card = (struct cs_card *)file->private_data;
+	struct cs_state *state;
+	struct dmabuf *dmabuf=0;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int val, valsave, mapped, ret;
+
+	state = (struct cs_state *)card->states[0];
+	if(state)
+	{
+		dmabuf = &state->dmabuf;
+		mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped;
+	}
+	state = (struct cs_state *)card->states[1];
+	if(state)
+	{
+		dmabuf = &state->dmabuf;
+		mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped;
+	}
+		
+#if CSDEBUG
+	printioctl(cmd);
+#endif
+
+	switch (cmd) 
+	{
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_RESET:
+		/* FIXME: spin_lock ? */
+		if (file->f_mode & FMODE_WRITE) {
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				stop_dac(state);
+				synchronize_irq();
+				dmabuf->ready = 0;
+				resync_dma_ptrs(state);
+				dmabuf->swptr = dmabuf->hwptr = 0;
+				dmabuf->count = dmabuf->total_bytes = 0;
+				dmabuf->blocks = 0;
+				dmabuf->SGok = 0;
+			}
+		}
+		if (file->f_mode & FMODE_READ) {
+			state = (struct cs_state *)card->states[0];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				stop_adc(state);
+				synchronize_irq();
+				resync_dma_ptrs(state);
+				dmabuf->ready = 0;
+				dmabuf->swptr = dmabuf->hwptr = 0;
+				dmabuf->count = dmabuf->total_bytes = 0;
+				dmabuf->blocks = 0;
+				dmabuf->SGok = 0;
+			}
+		}
+		CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") );
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(state, file->f_flags & O_NONBLOCK);
+		return 0;
+
+	case SNDCTL_DSP_SPEED: /* set sample rate */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				state = (struct cs_state *)card->states[0];
+				if(state)
+				{
+					dmabuf = &state->dmabuf;
+					stop_adc(state);
+					dmabuf->ready = 0;
+					dmabuf->SGok = 0;
+					cs_set_adc_rate(state, val);
+					cs_set_divisor(dmabuf);
+				}
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				state = (struct cs_state *)card->states[1];
+				if(state)
+				{
+					dmabuf = &state->dmabuf;
+					stop_dac(state);
+					dmabuf->ready = 0;
+					dmabuf->SGok = 0;
+					cs_set_dac_rate(state, val);
+					cs_set_divisor(dmabuf);
+				}
+			}
+			CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
+			    "cs46xx: cs_ioctl() DSP_SPEED %s %s %d\n",
+				file->f_mode & FMODE_WRITE ? "DAC" : "",
+				file->f_mode & FMODE_READ ? "ADC" : "",
+				dmabuf->rate ) );
+			return put_user(dmabuf->rate, (int *)arg);
+		}
+		return put_user(0, (int *)arg);
+
+	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_WRITE) {
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				stop_dac(state);
+				dmabuf->ready = 0;
+				dmabuf->SGok = 0;
+				if(val)
+					dmabuf->fmt |= CS_FMT_STEREO;
+				else
+					dmabuf->fmt &= ~CS_FMT_STEREO;
+				cs_set_divisor(dmabuf);
+				CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
+				    "cs46xx: DSP_STEREO() DAC %s\n",
+				    (dmabuf->fmt & CS_FMT_STEREO) ?
+					"STEREO":"MONO") );
+			}
+		}
+		if (file->f_mode & FMODE_READ) {
+			state = (struct cs_state *)card->states[0];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				stop_adc(state);
+				dmabuf->ready = 0;
+				dmabuf->SGok = 0;
+				if(val)
+					dmabuf->fmt |= CS_FMT_STEREO;
+				else
+					dmabuf->fmt &= ~CS_FMT_STEREO;
+				cs_set_divisor(dmabuf);
+				CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
+				    "cs46xx: DSP_STEREO() ADC %s\n",
+				    (dmabuf->fmt & CS_FMT_STEREO) ?
+					"STEREO":"MONO") );
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if ((val = prog_dmabuf(state)))
+					return val;
+				return put_user(dmabuf->fragsize, (int *)arg);
+			}
+		}
+		if (file->f_mode & FMODE_READ) {
+			state = (struct cs_state *)card->states[0];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if ((val = prog_dmabuf(state)))
+					return val;
+				return put_user(dmabuf->fragsize/dmabuf->divisor, 
+						(int *)arg);
+			}
+		}
+		return put_user(0, (int *)arg);
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+		return put_user(AFMT_S16_LE | AFMT_U8, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT: /* Select sample format */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
+		    "cs46xx: cs_ioctl() DSP_SETFMT %s %s %s %s\n",
+			file->f_mode & FMODE_WRITE ? "DAC" : "",
+			file->f_mode & FMODE_READ ? "ADC" : "",
+			val == AFMT_S16_LE ? "16Bit Signed" : "",
+			val == AFMT_U8 ? "8Bit Unsigned" : "") );
+		valsave = val;
+		if (val != AFMT_QUERY) {
+			if(val==AFMT_S16_LE || val==AFMT_U8)
+			{
+				if (file->f_mode & FMODE_WRITE) {
+					state = (struct cs_state *)card->states[1];
+					if(state)
+					{
+						dmabuf = &state->dmabuf;
+						stop_dac(state);
+						dmabuf->ready = 0;
+						dmabuf->SGok = 0;
+						if(val==AFMT_S16_LE)
+							dmabuf->fmt |= CS_FMT_16BIT;
+						else
+							dmabuf->fmt &= ~CS_FMT_16BIT;
+						cs_set_divisor(dmabuf);
+						if((ret = prog_dmabuf(state)))
+							return ret;
+					}
+				}
+				if (file->f_mode & FMODE_READ) {
+					val = valsave;
+					state = (struct cs_state *)card->states[0];
+					if(state)
+					{
+						dmabuf = &state->dmabuf;
+						stop_adc(state);
+						dmabuf->ready = 0;
+						dmabuf->SGok = 0;
+						if(val==AFMT_S16_LE)
+							dmabuf->fmt |= CS_FMT_16BIT;
+						else
+							dmabuf->fmt &= ~CS_FMT_16BIT;
+						cs_set_divisor(dmabuf);
+						if((ret = prog_dmabuf(state)))
+							return ret;
+					}
+				}
+			}
+			else
+			{
+				CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk(
+				    "cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n",
+					valsave) );
+			}
+		}
+		else
+		{
+			if(file->f_mode & FMODE_WRITE)
+			{
+				state = (struct cs_state *)card->states[1];
+				if(state)
+					dmabuf = &state->dmabuf;
+			}
+			else if(file->f_mode & FMODE_READ)
+			{
+				state = (struct cs_state *)card->states[0];
+				if(state)
+					dmabuf = &state->dmabuf;
+			}
+		}
+		if(dmabuf)
+		{
+			if(dmabuf->fmt & CS_FMT_16BIT)
+				return put_user(AFMT_S16_LE, (int *)arg);
+			else
+				return put_user(AFMT_U8, (int *)arg);
+		}
+		return put_user(0, (int *)arg);
+
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			if (file->f_mode & FMODE_WRITE) {
+				state = (struct cs_state *)card->states[1];
+				if(state)
+				{
+					dmabuf = &state->dmabuf;
+					stop_dac(state);
+					dmabuf->ready = 0;
+					dmabuf->SGok = 0;
+					if(val>1)
+						dmabuf->fmt |= CS_FMT_STEREO;
+					else
+						dmabuf->fmt &= ~CS_FMT_STEREO;
+					cs_set_divisor(dmabuf);
+					if (prog_dmabuf(state))
+						return 0;
+				}
+			}
+			if (file->f_mode & FMODE_READ) {
+				state = (struct cs_state *)card->states[0];
+				if(state)
+				{
+					dmabuf = &state->dmabuf;
+					stop_adc(state);
+					dmabuf->ready = 0;
+					dmabuf->SGok = 0;
+					if(val>1)
+						dmabuf->fmt |= CS_FMT_STEREO;
+					else
+						dmabuf->fmt &= ~CS_FMT_STEREO;
+					cs_set_divisor(dmabuf);
+					if (prog_dmabuf(state))
+						return 0;
+				}
+			}
+		}
+		return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1,
+				(int *)arg);
+
+	case SNDCTL_DSP_POST:
+		/*
+		 * There will be a longer than normal pause in the data.
+		 * so... do nothing, because there is nothing that we can do.
+		 */
+		return 0;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if (file->f_mode & FMODE_WRITE) {
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if (dmabuf->subdivision)
+					return -EINVAL;
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				if (val != 1 && val != 2)
+					return -EINVAL;
+				dmabuf->subdivision = val;
+			}
+		}
+		if (file->f_mode & FMODE_READ) {
+			state = (struct cs_state *)card->states[0];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if (dmabuf->subdivision)
+					return -EINVAL;
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				if (val != 1 && val != 2)
+					return -EINVAL;
+				dmabuf->subdivision = val;
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (file->f_mode & FMODE_WRITE) {
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				dmabuf->ossfragshift = val & 0xffff;
+				dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+			}
+		}
+		if (file->f_mode & FMODE_READ) {
+			state = (struct cs_state *)card->states[0];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				dmabuf->ossfragshift = val & 0xffff;
+				dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		state = (struct cs_state *)card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			spin_lock_irqsave(&state->card->lock, flags);
+			cs_update_ptr(card, CS_TRUE);
+			abinfo.fragsize = dmabuf->fragsize;
+			abinfo.fragstotal = dmabuf->numfrag;
+		/*
+		 * for mmap we always have total space available
+		 */
+			if (dmabuf->mapped)
+				abinfo.bytes = dmabuf->dmasize;
+			else
+				abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+
+			abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+			spin_unlock_irqrestore(&state->card->lock, flags);
+			return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		}
+		return -ENODEV;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		state = (struct cs_state *)card->states[0];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			spin_lock_irqsave(&state->card->lock, flags);
+			cs_update_ptr(card, CS_TRUE);
+			abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor;
+			abinfo.bytes = dmabuf->count/dmabuf->divisor;
+			abinfo.fragstotal = dmabuf->numfrag;
+			abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+			spin_unlock_irqrestore(&state->card->lock, flags);
+			return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		}
+		return -ENODEV;
+
+	case SNDCTL_DSP_NONBLOCK:
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
+			    (int *)arg);
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") );
+		if (file->f_mode & FMODE_WRITE)
+		{
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if(dmabuf->enable & DAC_RUNNING)
+					val |= PCM_ENABLE_INPUT;
+			}
+		}
+		if (file->f_mode & FMODE_READ)
+		{
+			if(state)
+			{
+				state = (struct cs_state *)card->states[0];
+				dmabuf = &state->dmabuf;
+				if(dmabuf->enable & ADC_RUNNING)
+					val |= PCM_ENABLE_OUTPUT;
+			}
+		}
+		CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()- val=0x%x\n",val) );
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			state = (struct cs_state *)card->states[0];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if (val & PCM_ENABLE_INPUT) {
+					if (!dmabuf->ready && (ret = prog_dmabuf(state)))
+						return ret;
+					start_adc(state);
+				} else
+					stop_adc(state);
+			}
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			state = (struct cs_state *)card->states[1];
+			if(state)
+			{
+				dmabuf = &state->dmabuf;
+				if (val & PCM_ENABLE_OUTPUT) {
+					if (!dmabuf->ready && (ret = prog_dmabuf(state)))
+						return ret;
+					start_dac(state);
+				} else
+					stop_dac(state);
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		state = (struct cs_state *)card->states[0];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			spin_lock_irqsave(&state->card->lock, flags);
+			cs_update_ptr(card, CS_TRUE);
+			cinfo.bytes = dmabuf->total_bytes/dmabuf->divisor;
+			cinfo.blocks = dmabuf->count/dmabuf->divisor >> dmabuf->fragshift;
+			cinfo.ptr = dmabuf->hwptr/dmabuf->divisor;
+			spin_unlock_irqrestore(&state->card->lock, flags);
+			return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+		}
+		return -ENODEV;
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		state = (struct cs_state *)card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			spin_lock_irqsave(&state->card->lock, flags);
+			cs_update_ptr(card, CS_TRUE);
+			cinfo.bytes = dmabuf->total_bytes;
+			if (dmabuf->mapped)
+			{
+				cinfo.blocks = (cinfo.bytes >> dmabuf->fragshift) 
+							- dmabuf->blocks;
+				CS_DBGOUT(CS_PARMS, 8, 
+					printk("total_bytes=%d blocks=%d dmabuf->blocks=%d\n", 
+					cinfo.bytes,cinfo.blocks,dmabuf->blocks) );
+				dmabuf->blocks = cinfo.bytes >> dmabuf->fragshift;
+			}
+			else
+			{
+				cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+			}
+			cinfo.ptr = dmabuf->hwptr;
+
+			CS_DBGOUT(CS_PARMS, 4, printk(
+			    "cs46xx: GETOPTR bytes=%d blocks=%d ptr=%d\n",
+				cinfo.bytes,cinfo.blocks,cinfo.ptr) );
+			spin_unlock_irqrestore(&state->card->lock, flags);
+			return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+		}
+		return -ENODEV;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		state = (struct cs_state *)card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			spin_lock_irqsave(&state->card->lock, flags);
+			cs_update_ptr(card, CS_TRUE);
+			val = dmabuf->count;
+			spin_unlock_irqrestore(&state->card->lock, flags);
+		}
+		else
+			val = 0;
+		return put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_RATE:
+		if(file->f_mode & FMODE_READ)
+			state = (struct cs_state *)card->states[0];
+		else 
+			state = (struct cs_state *)card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			return put_user(dmabuf->rate, (int *)arg);
+		}
+		return put_user(0, (int *)arg);
+		
+
+	case SOUND_PCM_READ_CHANNELS:
+		if(file->f_mode & FMODE_READ)
+			state = (struct cs_state *)card->states[0];
+		else 
+			state = (struct cs_state *)card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1,
+				(int *)arg);
+		}
+		return put_user(0, (int *)arg);
+
+	case SOUND_PCM_READ_BITS:
+		if(file->f_mode & FMODE_READ)
+			state = (struct cs_state *)card->states[0];
+		else 
+			state = (struct cs_state *)card->states[1];
+		if(state)
+		{
+			dmabuf = &state->dmabuf;
+			return put_user((dmabuf->fmt & CS_FMT_16BIT) ? 
+			  	AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+		}
+		return put_user(0, (int *)arg);
+
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_WRITE_FILTER:
+	case SOUND_PCM_READ_FILTER:
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+
+/*
+ *	AMP control - null AMP
+ */
+ 
+static void amp_none(struct cs_card *card, int change)
+{	
+}
+
+/*
+ *	Crystal EAPD mode
+ */
+ 
+static void amp_voyetra(struct cs_card *card, int change)
+{
+	/* Manage the EAPD bit on the Crystal 4297 
+	   and the Analog AD1885 */
+	   
+	int old=card->amplifier;
+	
+	card->amplifier+=change;
+	if(card->amplifier && !old)
+	{
+		/* Turn the EAPD amp on */
+		cs_ac97_set(card->ac97_codec[0],  AC97_POWER_CONTROL, 
+			cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) |
+				0x8000);
+	}
+	else if(old && !card->amplifier)
+	{
+		/* Turn the EAPD amp off */
+		cs_ac97_set(card->ac97_codec[0],  AC97_POWER_CONTROL, 
+			cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) &
+				~0x8000);
+	}
+}
+
+		       
+/*
+ *	Game Theatre XP card - EGPIO[2] is used to enable the external amp.
+ */
+ 
+static void amp_hercules(struct cs_card *card, int change)
+{
+	int old=card->amplifier;
+	if(!card)
+	{
+		CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO 
+			"cs46xx: amp_hercules() called before initialized.\n"));
+		return;
+	}
+	card->amplifier+=change;
+	if( (card->amplifier && !old) && !(hercules_egpio_disable))
+	{
+		CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO 
+			"cs46xx: amp_hercules() external amp enabled\n"));
+		cs461x_pokeBA0(card, BA0_EGPIODR, 
+			EGPIODR_GPOE2);     /* enable EGPIO2 output */
+		cs461x_pokeBA0(card, BA0_EGPIOPTR, 
+			EGPIOPTR_GPPT2);   /* open-drain on output */
+	}
+	else if(old && !card->amplifier)
+	{
+		CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO 
+			"cs46xx: amp_hercules() external amp disabled\n"));
+		cs461x_pokeBA0(card, BA0_EGPIODR, 0); /* disable */
+		cs461x_pokeBA0(card, BA0_EGPIOPTR, 0); /* disable */
+	}
+}
+
+/*
+ *	Handle the CLKRUN on a thinkpad. We must disable CLKRUN support
+ *	whenever we need to beat on the chip.
+ *
+ *	The original idea and code for this hack comes from David Kaiser at
+ *	Linuxcare. Perhaps one day Crystal will document their chips well
+ *	enough to make them useful.
+ */
+ 
+static void clkrun_hack(struct cs_card *card, int change)
+{
+	struct pci_dev *acpi_dev;
+	u16 control;
+	u8 pp;
+	unsigned long port;
+	int old=card->active;
+	
+	card->active+=change;
+	
+	acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+	if(acpi_dev == NULL)
+		return;		/* Not a thinkpad thats for sure */
+
+	/* Find the control port */		
+	pci_read_config_byte(acpi_dev, 0x41, &pp);
+	port=pp<<8;
+
+	/* Read ACPI port */	
+	control=inw(port+0x10);
+
+	/* Flip CLKRUN off while running */
+	if(!card->active && old)
+	{
+		CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO
+			"cs46xx: clkrun() enable clkrun - change=%d active=%d\n",
+				change,card->active));
+		outw(control|0x2000, port+0x10);
+	}
+	else 
+	{
+	/*
+	* sometimes on a resume the bit is set, so always reset the bit.
+	*/
+		CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO
+			"cs46xx: clkrun() disable clkrun - change=%d active=%d\n",
+				change,card->active));
+		outw(control&~0x2000, port+0x10);
+	}
+}
+
+	
+static int cs_open(struct inode *inode, struct file *file)
+{
+	struct cs_card *card = (struct cs_card *)file->private_data;
+	struct cs_state *state = NULL;
+	struct dmabuf *dmabuf = NULL;
+	struct list_head *entry;
+        unsigned int minor = minor(inode->i_rdev);
+	int ret=0;
+	unsigned int tmp;
+
+	CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=0x%x %s %s\n",
+		(unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
+		file->f_mode & FMODE_READ ? "FMODE_READ" : "") );
+
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
+
+		if (!((card->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+	if (entry == &cs46xx_devs)
+		return -ENODEV;
+	if (!card) {
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
+			"cs46xx: cs_open(): Error - unable to find audio card struct\n"));
+		return -ENODEV;
+	}
+
+	/*
+	 * hardcode state[0] for capture, [1] for playback
+	 */
+	if(file->f_mode & FMODE_READ)
+	{
+		CS_DBGOUT(CS_WAVE_READ, 2, printk("cs46xx: cs_open() FMODE_READ\n") );
+		if (card->states[0] == NULL) {
+			state = card->states[0] = (struct cs_state *)
+				kmalloc(sizeof(struct cs_state), GFP_KERNEL);
+			if (state == NULL)
+				return -ENOMEM;
+			memset(state, 0, sizeof(struct cs_state));
+			init_MUTEX(&state->sem);
+			dmabuf = &state->dmabuf;
+			dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA);
+			if(dmabuf->pbuf==NULL)
+			{
+				kfree(state);
+				card->states[0]=NULL;
+				return -ENOMEM;
+			}
+		}
+		else
+		{
+			state = card->states[0];
+			if(state->open_mode & FMODE_READ)
+				return -EBUSY;
+		}
+		dmabuf->channel = card->alloc_rec_pcm_channel(card);
+			
+		if (dmabuf->channel == NULL) {
+			kfree (card->states[0]);
+			card->states[0] = NULL;;
+			return -ENODEV;
+		}
+
+		/* Now turn on external AMP if needed */
+		state->card = card;
+		state->card->active_ctrl(state->card,1);
+		state->card->amplifier_ctrl(state->card,1);
+		
+		if( (tmp = cs46xx_powerup(card, CS_POWER_ADC)) )
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs46xx_powerup of ADC failed (0x%x)\n",tmp) );
+			return -EIO;
+		}
+
+		dmabuf->channel->state = state;
+		/* initialize the virtual channel */
+		state->virt = 0;
+		state->magic = CS_STATE_MAGIC;
+		init_waitqueue_head(&dmabuf->wait);
+		init_MUTEX(&state->open_sem);
+		file->private_data = card;
+
+		down(&state->open_sem);
+
+		/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
+		   should be default to unsigned 8-bits, mono, with sample rate 8kHz and
+		   /dev/dspW will accept 16-bits sample */
+
+		/* Default input is 8bit mono */
+		dmabuf->fmt &= ~CS_FMT_MASK;
+		dmabuf->type = CS_TYPE_ADC;
+		dmabuf->ossfragshift = 0;
+		dmabuf->ossmaxfrags  = 0;
+		dmabuf->subdivision  = 0;
+		cs_set_adc_rate(state, 8000);
+		cs_set_divisor(dmabuf);
+
+		state->open_mode |= FMODE_READ;
+		up(&state->open_sem);
+	}
+	if(file->f_mode & FMODE_WRITE)
+	{
+		CS_DBGOUT(CS_OPEN, 2, printk("cs46xx: cs_open() FMODE_WRITE\n") );
+		if (card->states[1] == NULL) {
+			state = card->states[1] = (struct cs_state *)
+				kmalloc(sizeof(struct cs_state), GFP_KERNEL);
+			if (state == NULL)
+				return -ENOMEM;
+			memset(state, 0, sizeof(struct cs_state));
+			init_MUTEX(&state->sem);
+			dmabuf = &state->dmabuf;
+			dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA);
+			if(dmabuf->pbuf==NULL)
+			{
+				kfree(state);
+				card->states[1]=NULL;
+				return -ENOMEM;
+			}
+		}
+		else
+		{
+			state = card->states[1];
+			if(state->open_mode & FMODE_WRITE)
+				return -EBUSY;
+		}
+		dmabuf->channel = card->alloc_pcm_channel(card);
+			
+		if (dmabuf->channel == NULL) {
+			kfree (card->states[1]);
+			card->states[1] = NULL;;
+			return -ENODEV;
+		}
+
+		/* Now turn on external AMP if needed */
+		state->card = card;
+		state->card->active_ctrl(state->card,1);
+		state->card->amplifier_ctrl(state->card,1);
+
+		if( (tmp = cs46xx_powerup(card, CS_POWER_DAC)) )
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs46xx_powerup of DAC failed (0x%x)\n",tmp) );
+			return -EIO;
+		}
+		
+		dmabuf->channel->state = state;
+		/* initialize the virtual channel */
+		state->virt = 1;
+		state->magic = CS_STATE_MAGIC;
+		init_waitqueue_head(&dmabuf->wait);
+		init_MUTEX(&state->open_sem);
+		file->private_data = card;
+
+		down(&state->open_sem);
+
+		/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
+		   should be default to unsigned 8-bits, mono, with sample rate 8kHz and
+		   /dev/dspW will accept 16-bits sample */
+
+		/* Default output is 8bit mono. */
+		dmabuf->fmt &= ~CS_FMT_MASK;
+		dmabuf->type = CS_TYPE_DAC;
+		dmabuf->ossfragshift = 0;
+		dmabuf->ossmaxfrags  = 0;
+		dmabuf->subdivision  = 0;
+		cs_set_dac_rate(state, 8000);
+		cs_set_divisor(dmabuf);
+
+		state->open_mode |= FMODE_WRITE;
+		up(&state->open_sem);
+		if((ret = prog_dmabuf(state)))
+			return ret;
+	}
+	MOD_INC_USE_COUNT;	/* for 2.2 */
+	CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") );
+	return 0;
+}
+
+static int cs_release(struct inode *inode, struct file *file)
+{
+	struct cs_card *card = (struct cs_card *)file->private_data;
+	struct dmabuf *dmabuf;
+	struct cs_state *state;
+	unsigned int tmp;
+	CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=0x%x %s %s\n",
+		(unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
+		file->f_mode & FMODE_READ ? "FMODE_READ" : "") );
+
+	if (!(file->f_mode & (FMODE_WRITE | FMODE_READ)))
+	{
+		return -EINVAL;
+	}
+	state = card->states[1];
+	if(state)
+	{
+		if ( (state->open_mode & FMODE_WRITE) & (file->f_mode & FMODE_WRITE) )
+		{
+			CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_WRITE\n") );
+			dmabuf = &state->dmabuf;
+			cs_clear_tail(state);
+			drain_dac(state, file->f_flags & O_NONBLOCK);
+			/* stop DMA state machine and free DMA buffers/channels */
+			down(&state->open_sem);
+			stop_dac(state);
+			dealloc_dmabuf(state);
+			state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+			free_page((unsigned long)state->dmabuf.pbuf);
+
+			/* we're covered by the open_sem */
+			up(&state->open_sem);
+			state->card->states[state->virt] = NULL;
+			state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+
+			if( (tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE )) )
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO 
+					"cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) );
+			}
+
+			/* Now turn off external AMP if needed */
+			state->card->amplifier_ctrl(state->card, -1);
+			state->card->active_ctrl(state->card, -1);
+
+			kfree(state);
+		}
+	}
+
+	state = card->states[0];
+	if(state)
+	{
+		if ( (state->open_mode & FMODE_READ) & (file->f_mode & FMODE_READ) )
+		{
+			CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_READ\n") );
+			dmabuf = &state->dmabuf;
+			down(&state->open_sem);
+			stop_adc(state);
+			dealloc_dmabuf(state);
+			state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+			free_page((unsigned long)state->dmabuf.pbuf);
+
+			/* we're covered by the open_sem */
+			up(&state->open_sem);
+			state->card->states[state->virt] = NULL;
+			state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+
+			if( (tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE )) )
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO 
+					"cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) );
+			}
+
+			/* Now turn off external AMP if needed */
+			state->card->amplifier_ctrl(state->card, -1);
+			state->card->active_ctrl(state->card, -1);
+
+			kfree(state);
+		}
+	}
+
+	CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk("cs46xx: cs_release()- 0\n") );
+	MOD_DEC_USE_COUNT;	/* For 2.2 */
+	return 0;
+}
+
+static void printpm(struct cs_card *s)
+{
+	CS_DBGOUT(CS_PM, 9, printk("pm struct:\n"));
+	CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n",
+		(unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue));
+	CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n",
+		s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n",
+		s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n",
+		s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue));
+	CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n",
+		s->pm.u32SSCR,s->pm.u32SRCSA));
+	CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n",
+		s->pm.u32DacASR,s->pm.u32AdcASR));
+	CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n",
+		s->pm.u32DacSR,s->pm.u32AdcSR));
+	CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n",
+		s->pm.u32MIDCR_Save));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_powerdown: 0x%x _general_purpose 0x%x\n",
+		s->pm.u32AC97_powerdown,s->pm.u32AC97_general_purpose));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume: 0x%x\n",
+		s->pm.u32AC97_master_volume));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_headphone_volume: 0x%x\n",
+		s->pm.u32AC97_headphone_volume));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume_mono: 0x%x\n",
+		s->pm.u32AC97_master_volume_mono));
+	CS_DBGOUT(CS_PM, 9, printk("u32AC97_pcm_out_volume: 0x%x\n",
+		s->pm.u32AC97_pcm_out_volume));
+	CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_play: 0x%x dmabuf_count_play: %d\n",
+		s->pm.dmabuf_swptr_play,s->pm.dmabuf_count_play));
+	CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_capture: 0x%x dmabuf_count_capture: %d\n",
+		s->pm.dmabuf_swptr_capture,s->pm.dmabuf_count_capture));
+
+}
+
+/****************************************************************************
+*
+*  Suspend - save the ac97 regs, mute the outputs and power down the part.  
+*
+****************************************************************************/
+void cs46xx_ac97_suspend(struct cs_card *card)
+{
+	int Count,i;
+	struct ac97_codec *dev=card->ac97_codec[0];
+	unsigned int tmp;
+
+	CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n"));
+
+	if(card->states[1])
+	{
+		stop_dac(card->states[1]);
+		resync_dma_ptrs(card->states[1]);
+	}
+	if(card->states[0])
+	{
+		stop_adc(card->states[0]);
+		resync_dma_ptrs(card->states[0]);
+	}
+
+	for(Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE)
+			&& (i < CS46XX_AC97_NUMBER_RESTORE_REGS); 
+		Count += 2, i++)
+	{
+		card->pm.ac97[i] = cs_ac97_get(dev, BA0_AC97_RESET + Count);
+	}
+/*
+* Save the ac97 volume registers as well as the current powerdown state.
+* Now, mute the all the outputs (master, headphone, and mono), as well
+* as the PCM volume, in preparation for powering down the entire part.
+	card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, 
+			(u8)BA0_AC97_MASTER_VOLUME); 
+	card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, 
+			(u8)BA0_AC97_HEADPHONE_VOLUME); 
+	card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, 
+			(u8)BA0_AC97_MASTER_VOLUME_MONO); 
+	card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, 
+			(u8)BA0_AC97_PCM_OUT_VOLUME);
+*/ 
+/*
+* mute the outputs
+*/
+	cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000);
+	cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000);
+	cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
+	cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000);
+
+/*
+* save the registers that cause pops
+*/
+	card->pm.u32AC97_powerdown = (u32)cs_ac97_get(dev, (u8)AC97_POWER_CONTROL); 
+	card->pm.u32AC97_general_purpose = (u32)cs_ac97_get(dev, (u8)BA0_AC97_GENERAL_PURPOSE); 
+/*
+* And power down everything on the AC97 codec.
+* well, for now, only power down the DAC/ADC and MIXER VREFON components. 
+* trouble with removing VREF.
+*/
+	if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
+			CS_POWER_MIXVON, CS_TRUE )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) );
+	}
+
+	CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()-\n"));
+}
+
+/****************************************************************************
+*
+*  Resume - power up the part and restore its registers..  
+*
+****************************************************************************/
+void cs46xx_ac97_resume(struct cs_card *card)
+{
+	int Count,i;
+	struct ac97_codec *dev=card->ac97_codec[0];
+
+	CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()+\n"));
+
+/*
+* First, we restore the state of the general purpose register.  This
+* contains the mic select (mic1 or mic2) and if we restore this after
+* we restore the mic volume/boost state and mic2 was selected at
+* suspend time, we will end up with a brief period of time where mic1
+* is selected with the volume/boost settings for mic2, causing
+* acoustic feedback.  So we restore the general purpose register
+* first, thereby getting the correct mic selected before we restore
+* the mic volume/boost.
+*/
+	cs_ac97_set(dev, (u8)BA0_AC97_GENERAL_PURPOSE, 
+		(u16)card->pm.u32AC97_general_purpose);
+/*
+* Now, while the outputs are still muted, restore the state of power
+* on the AC97 part.
+*/
+	cs_ac97_set(dev, (u8)BA0_AC97_POWERDOWN, (u16)card->pm.u32AC97_powerdown);
+	mdelay(5 * cs_laptop_wait);
+/*
+* Restore just the first set of registers, from register number
+* 0x02 to the register number that ulHighestRegToRestore specifies.
+*/
+	for(	Count = 0x2, i=0; 
+		(Count <= CS46XX_AC97_HIGHESTREGTORESTORE)
+			&& (i < CS46XX_AC97_NUMBER_RESTORE_REGS); 
+		Count += 2, i++)
+	{
+		cs_ac97_set(dev, (u8)(BA0_AC97_RESET + Count), (u16)card->pm.ac97[i]);
+	}
+
+	/* Check if we have to init the amplifier */
+	if(card->amp_init)
+		card->amp_init(card);
+        
+	CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()-\n"));
+}
+
+
+static int cs46xx_restart_part(struct cs_card *card)
+{
+	struct dmabuf *dmabuf;
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs46xx: cs46xx_restart_part()+\n"));
+	if(card->states[1])
+	{
+		dmabuf = &card->states[1]->dmabuf;
+		dmabuf->ready = 0;
+		resync_dma_ptrs(card->states[1]);
+		cs_set_divisor(dmabuf);
+		if(__prog_dmabuf(card->states[1]))
+		{
+			CS_DBGOUT(CS_PM | CS_ERROR, 1, 
+				printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n"));
+			return -1;
+		}
+		cs_set_dac_rate(card->states[1], dmabuf->rate);
+	}
+	if(card->states[0])
+	{
+		dmabuf = &card->states[0]->dmabuf;
+		dmabuf->ready = 0;
+		resync_dma_ptrs(card->states[0]);
+		cs_set_divisor(dmabuf);
+		if(__prog_dmabuf(card->states[0]))
+		{
+			CS_DBGOUT(CS_PM | CS_ERROR, 1, 
+				printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n"));
+			return -1;
+		}
+		cs_set_adc_rate(card->states[0], dmabuf->rate);
+	}
+	card->pm.flags |= CS46XX_PM_RESUMED;
+	if(card->states[0])
+		start_adc(card->states[0]);
+	if(card->states[1])
+		start_dac(card->states[1]);
+
+	card->pm.flags |= CS46XX_PM_IDLE;
+	card->pm.flags &= ~(CS46XX_PM_SUSPENDING | CS46XX_PM_SUSPENDED 
+			| CS46XX_PM_RESUMING | CS46XX_PM_RESUMED);
+	if(card->states[0])
+		wake_up(&card->states[0]->dmabuf.wait);
+	if(card->states[1])
+		wake_up(&card->states[1]->dmabuf.wait);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs46xx: cs46xx_restart_part()-\n"));
+	return 0;
+}
+
+
+static void cs461x_reset(struct cs_card *card);
+static void cs461x_proc_stop(struct cs_card *card);
+static int cs46xx_suspend(struct cs_card *card, u32 state)
+{
+	unsigned int tmp;
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=0x%x\n",
+			(unsigned)card->pm.flags,(unsigned)card));
+/*
+* check the current state, only suspend if IDLE
+*/
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 2, 
+			printk("cs46xx: cs46xx_suspend() unable to suspend, not IDLE\n"));
+		return 1;
+	}
+	card->pm.flags &= ~CS46XX_PM_IDLE;
+	card->pm.flags |= CS46XX_PM_SUSPENDING;
+
+	card->active_ctrl(card,1);
+	
+	tmp = cs461x_peek(card, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	tmp |=  0x00000010;
+	cs461x_poke(card, BA1_PFIE, tmp);	/* playback interrupt disable */
+
+	tmp = cs461x_peek(card, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000011;
+	cs461x_poke(card, BA1_CIE, tmp);	/* capture interrupt disable */
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_PCTL);
+	cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_CCTL);
+	cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000);
+
+	if(card->states[1])
+	{
+		card->pm.dmabuf_swptr_play = card->states[1]->dmabuf.swptr;
+		card->pm.dmabuf_count_play = card->states[1]->dmabuf.count;
+	}
+	if(card->states[0])
+	{
+		card->pm.dmabuf_swptr_capture = card->states[0]->dmabuf.swptr;
+		card->pm.dmabuf_count_capture = card->states[0]->dmabuf.count;
+	}
+
+	cs46xx_ac97_suspend(card);
+
+	/*
+         *  Reset the processor.
+         */
+	cs461x_reset(card);
+
+	cs461x_proc_stop(card);
+
+	/*
+	 *  Power down the DAC and ADC.  For now leave the other areas on.
+	 */
+	cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, 0x0300);
+
+	/*
+	 *  Power down the PLL.
+	 */
+	cs461x_pokeBA0(card, BA0_CLKCR1, 0);
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE;
+	cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+
+	card->active_ctrl(card,-1);
+
+	card->pm.flags &= ~CS46XX_PM_SUSPENDING;
+	card->pm.flags |= CS46XX_PM_SUSPENDED;
+
+	printpm(card);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk("cs46xx: cs46xx_suspend()- flags=0x%x\n",
+			(unsigned)card->pm.flags));
+	return 0;
+}
+
+static int cs46xx_resume(struct cs_card *card)
+{
+	int i;
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, 
+		printk( "cs46xx: cs46xx_resume()+ flags=0x%x\n",
+			(unsigned)card->pm.flags));
+	if(!(card->pm.flags & CS46XX_PM_SUSPENDED))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 2, 
+			printk("cs46xx: cs46xx_resume() unable to resume, not SUSPENDED\n"));
+		return 1;
+	}
+	card->pm.flags |= CS46XX_PM_RESUMING;
+	card->pm.flags &= ~CS46XX_PM_SUSPENDED;
+	printpm(card);
+	card->active_ctrl(card, 1);
+
+	for(i=0;i<5;i++)
+	{
+		if (cs_hardware_init(card) != 0)
+		{
+			CS_DBGOUT(CS_PM | CS_ERROR, 4, printk(
+				"cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n"));
+			mdelay(10 * cs_laptop_wait);
+			cs461x_reset(card);
+			continue;
+		}
+		break;
+	}
+	if(i>=4)
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 1, printk(
+			"cs46xx: cs46xx_resume()- cs_hardware_init() failed, retried %d times.\n",i));
+		return 0;
+	}
+
+	if(cs46xx_restart_part(card))
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 4, printk(
+			"cs46xx: cs46xx_resume(): cs46xx_restart_part() returned error\n"));
+	}
+
+	card->active_ctrl(card, -1);
+
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk("cs46xx: cs46xx_resume()- flags=0x%x\n",
+		(unsigned)card->pm.flags));
+	return 0;
+}
+
+static /*const*/ struct file_operations cs461x_fops = {
+	CS_OWNER	CS_THIS_MODULE
+	llseek:		no_llseek,
+	read:		cs_read,
+	write:		cs_write,
+	poll:		cs_poll,
+	ioctl:		cs_ioctl,
+	mmap:		cs_mmap,
+	open:		cs_open,
+	release:	cs_release,
+};
+
+/* Write AC97 codec registers */
+
+
+static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg)
+{
+	struct cs_card *card = dev->private_data;
+	int count,loopcnt;
+	unsigned int tmp;
+	
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97 
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+	 *  5. if DCV not cleared, break and return error
+	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
+	 */
+
+
+	cs461x_peekBA0(card, BA0_ACSDA);
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  set CRW - Read command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+	 */
+
+	cs461x_pokeBA0(card, BA0_ACCAD, reg);
+	cs461x_pokeBA0(card, BA0_ACCDA, 0);
+	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
+					     ACCTL_VFRM | ACCTL_ESYN |
+					     ACCTL_RSTN);
+
+
+	/*
+	 *  Wait for the read to occur.
+	 */
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+		loopcnt = 2000;
+	else
+		loopcnt = 500 * cs_laptop_wait;
+ 	loopcnt *= cs_laptop_wait;
+	for (count = 0; count < loopcnt; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+	 	 */
+		udelay(10 * cs_laptop_wait);
+		/*
+		 *  Now, check to see if the read has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
+		 */
+		if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV))
+			break;
+	}
+
+	/*
+	 *  Make sure the read completed.
+	 */
+	if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+			"cs46xx: AC'97 read problem (ACCTL_DCV), reg = 0x%x returning 0xffff\n", reg));
+		return 0xffff;
+	}
+
+	/*
+	 *  Wait for the valid status bit to go active.
+	 */
+
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+		loopcnt = 2000;
+	else
+		loopcnt = 1000;
+ 	loopcnt *= cs_laptop_wait;
+	for (count = 0; count < loopcnt; count++) {
+		/*
+		 *  Read the AC97 status register.
+		 *  ACSTS = Status Register = 464h
+		 *  VSTS - Valid Status
+		 */
+		if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS)
+			break;
+		udelay(10 * cs_laptop_wait);
+	}
+	
+	/*
+	 *  Make sure we got valid status.
+	 */
+	if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) {
+		CS_DBGOUT(CS_ERROR, 2, printk(KERN_WARNING 
+			"cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x 0xffff \n", 
+				reg, tmp));
+		return 0xffff;
+	}
+
+	/*
+	 *  Read the data returned from the AC97 register.
+	 *  ACSDA = Status Data Register = 474h
+	 */
+	CS_DBGOUT(CS_FUNCTION, 9, printk(KERN_INFO
+		"cs46xx: cs_ac97_get() reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", 
+			reg, cs461x_peekBA0(card, BA0_ACSDA),
+			cs461x_peekBA0(card, BA0_ACCAD)));
+	return(cs461x_peekBA0(card, BA0_ACSDA));
+}
+
+static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val)
+{
+	struct cs_card *card = dev->private_data;
+	int count;
+	int val2 = 0;
+	
+	if(reg == AC97_CD_VOL)
+	{
+		val2 = cs_ac97_get(dev, AC97_CD_VOL);
+	}
+	
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+	 *  5. if DCV not cleared, break and return error
+	 */
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  reset CRW - Write command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+         */
+	cs461x_pokeBA0(card, BA0_ACCAD, reg);
+	cs461x_pokeBA0(card, BA0_ACCDA, val);
+	cs461x_peekBA0(card, BA0_ACCTL);
+	cs461x_pokeBA0(card, BA0_ACCTL, 0 | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |
+				             ACCTL_ESYN | ACCTL_RSTN);
+	for (count = 0; count < 1000; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+		 */
+		udelay(10 * cs_laptop_wait);
+		/*
+		 *  Now, check to see if the write has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 07h
+		 */
+		if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV))
+			break;
+	}
+	/*
+	 *  Make sure the write completed.
+	 */
+	if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)
+	{
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+			"cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val));
+	}
+
+	/*
+	 *	Adjust power if the mixer is selected/deselected according
+	 *	to the CD.
+	 *
+	 *	IF the CD is a valid input source (mixer or direct) AND
+	 *		the CD is not muted THEN power is needed
+	 *
+	 *	We do two things. When record select changes the input to
+	 *	add/remove the CD we adjust the power count if the CD is
+	 *	unmuted.
+	 *
+	 *	When the CD mute changes we adjust the power level if the
+	 *	CD was a valid input.
+	 *
+	 *      We also check for CD volume != 0, as the CD mute isn't
+	 *      normally tweaked from userspace.
+	 */
+	 
+	/* CD mute change ? */
+	
+	if(reg==AC97_CD_VOL)
+	{
+		/* Mute bit change ? */
+		if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val))
+		{
+			/* This is a hack but its cleaner than the alternatives.
+			   Right now card->ac97_codec[0] might be NULL as we are
+			   still doing codec setup. This does an early assignment
+			   to avoid the problem if it occurs */
+			   
+			if(card->ac97_codec[0]==NULL)
+				card->ac97_codec[0]=dev;
+				
+			/* Mute on */
+			if(val&0x8000 || val == 0x1f1f)
+				card->amplifier_ctrl(card, -1);
+			else /* Mute off power on */
+			{
+				if(card->amp_init)
+					card->amp_init(card);
+				card->amplifier_ctrl(card, 1);
+			}
+		}
+	}
+}
+
+
+/* OSS /dev/mixer file operation methods */
+
+static int cs_open_mixdev(struct inode *inode, struct file *file)
+{
+	int i=0;
+	unsigned int minor = minor(inode->i_rdev);
+	struct cs_card *card=NULL;
+	struct list_head *entry;
+	unsigned int tmp;
+
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs46xx: cs_open_mixdev()+\n"));
+
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
+		for (i = 0; i < NR_AC97; i++)
+			if (card->ac97_codec[i] != NULL &&
+			    card->ac97_codec[i]->dev_mixer == minor)
+				goto match;
+	}
+	if (!card)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
+			printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n"));
+		return -ENODEV;
+	}
+ match:
+	if(!card->ac97_codec[i])
+		return -ENODEV;
+	file->private_data = card->ac97_codec[i];
+
+	card->active_ctrl(card,1);
+	if(!CS_IN_USE(&card->mixer_use_cnt))
+	{
+		if( (tmp = cs46xx_powerup(card, CS_POWER_MIXVON )) )
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs_open_mixdev() powerup failure (0x%x)\n",tmp) );
+			return -EIO;
+		}
+	}
+	card->amplifier_ctrl(card, 1);
+	CS_INC_USE_COUNT(&card->mixer_use_cnt);
+	MOD_INC_USE_COUNT; /* for 2.2 */
+	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
+		  printk(KERN_INFO "cs46xx: cs_open_mixdev()- 0\n"));
+	return 0;
+}
+
+static int cs_release_mixdev(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct cs_card *card=NULL;
+	struct list_head *entry;
+	int i;
+	unsigned int tmp;
+
+	CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4,
+		  printk(KERN_INFO "cs46xx: cs_release_mixdev()+\n"));
+	list_for_each(entry, &cs46xx_devs)
+	{
+		card = list_entry(entry, struct cs_card, list);
+		for (i = 0; i < NR_AC97; i++)
+			if (card->ac97_codec[i] != NULL &&
+			    card->ac97_codec[i]->dev_mixer == minor)
+				goto match;
+	}
+	if (!card)
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
+			printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n"));
+		return -ENODEV;
+	}
+match:
+	MOD_DEC_USE_COUNT; /* for 2.2 */
+	if(!CS_DEC_AND_TEST(&card->mixer_use_cnt))
+	{
+		CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4,
+			  printk(KERN_INFO "cs46xx: cs_release_mixdev()- no powerdown, usecnt>0\n"));
+		card->active_ctrl(card, -1);
+		card->amplifier_ctrl(card, -1);
+		return 0;
+	}
+/*
+* ok, no outstanding mixer opens, so powerdown.
+*/
+	if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) );
+		card->active_ctrl(card, -1);
+		card->amplifier_ctrl(card, -1);
+		return -EIO;
+	}
+	card->active_ctrl(card, -1);
+	card->amplifier_ctrl(card, -1);
+	CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4,
+		  printk(KERN_INFO "cs46xx: cs_release_mixdev()- 0\n"));
+	return 0;
+}
+
+void __exit cs46xx_cleanup_module(void);
+static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+	struct cs_card *card=NULL;
+	struct list_head *entry;
+
+#if CSDEBUG_INTERFACE
+        int val;
+
+	if( 	(cmd == SOUND_MIXER_CS_GETDBGMASK) || 
+		(cmd == SOUND_MIXER_CS_SETDBGMASK) ||
+		(cmd == SOUND_MIXER_CS_GETDBGLEVEL) ||
+		(cmd == SOUND_MIXER_CS_SETDBGLEVEL) ||
+		(cmd == SOUND_MIXER_CS_APM))
+	{
+	    switch(cmd)
+	    {
+
+		case SOUND_MIXER_CS_GETDBGMASK:
+			return put_user(cs_debugmask, (unsigned long *)arg);
+		
+		case SOUND_MIXER_CS_GETDBGLEVEL:
+			return put_user(cs_debuglevel, (unsigned long *)arg);
+
+		case SOUND_MIXER_CS_SETDBGMASK:
+			if (get_user(val, (unsigned long *)arg))
+				return -EFAULT;
+			cs_debugmask = val;
+			return 0;
+
+		case SOUND_MIXER_CS_SETDBGLEVEL:
+			if (get_user(val, (unsigned long *)arg))
+				return -EFAULT;
+			cs_debuglevel = val;
+			return 0;
+
+		case SOUND_MIXER_CS_APM:
+			if (get_user(val, (unsigned long *) arg))
+				return -EFAULT;
+			if(val == CS_IOCTL_CMD_SUSPEND) 
+			{
+				list_for_each(entry, &cs46xx_devs)
+				{
+					card = list_entry(entry, struct cs_card, list);
+					cs46xx_suspend(card, 0);
+				}
+
+			}
+			else if(val == CS_IOCTL_CMD_RESUME)
+			{
+				list_for_each(entry, &cs46xx_devs)
+				{
+					card = list_entry(entry, struct cs_card, list);
+					cs46xx_resume(card);
+				}
+			}
+			else
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
+				    "cs46xx: mixer_ioctl(): invalid APM cmd (%d)\n",
+					val));
+			}
+			return 0;
+
+		default:
+			CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO 
+				"cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n") );
+			return 0;
+	    }
+	}
+#endif
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations cs_mixer_fops = {
+	CS_OWNER	CS_THIS_MODULE
+	llseek:		no_llseek,
+	ioctl:		cs_ioctl_mixdev,
+	open:		cs_open_mixdev,
+	release:	cs_release_mixdev,
+};
+
+/* AC97 codec initialisation. */
+static int __init cs_ac97_init(struct cs_card *card)
+{
+	int num_ac97 = 0;
+	int ready_2nd = 0;
+	struct ac97_codec *codec;
+	u16 eid;
+
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_ac97_init()+\n") );
+
+	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+		if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		memset(codec, 0, sizeof(struct ac97_codec));
+
+		/* initialize some basic codec information, other fields will be filled
+		   in ac97_probe_codec */
+		codec->private_data = card;
+		codec->id = num_ac97;
+
+		codec->codec_read = cs_ac97_get;
+		codec->codec_write = cs_ac97_set;
+	
+		if (ac97_probe_codec(codec) == 0)
+		{
+			CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+				"cs46xx: cs_ac97_init()- codec number %d not found\n",
+					num_ac97) );
+			card->ac97_codec[num_ac97] = 0;
+			break;
+		}
+		CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+			"cs46xx: cs_ac97_init() found codec %d\n",num_ac97) );
+
+		eid = cs_ac97_get(codec, AC97_EXTENDED_ID);
+		
+		if(eid==0xFFFFFF)
+		{
+			printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97);
+			kfree(codec);
+			break;
+		}
+		
+		card->ac97_features = eid;
+			
+		if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) {
+			printk(KERN_ERR "cs46xx: couldn't register mixer!\n");
+			kfree(codec);
+			break;
+		}
+		card->ac97_codec[num_ac97] = codec;
+
+		CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+			"cs46xx: cs_ac97_init() ac97_codec[%d] set to 0x%x\n",
+				(unsigned int)num_ac97,
+				(unsigned int)codec));
+		/* if there is no secondary codec at all, don't probe any more */
+		if (!ready_2nd)
+		{
+			num_ac97 += 1;
+			break;
+		}
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_ac97_init()- %d\n", (unsigned int)num_ac97));
+	return num_ac97;
+}
+
+/*
+ * load the static image into the DSP
+ */
+#include "cs461x_image.h"
+static void cs461x_download_image(struct cs_card *card)
+{
+    unsigned i, j, temp1, temp2, offset, count;
+    unsigned char *pBA1 = ioremap(card->ba1_addr, 0x40000);
+    for( i=0; i < CLEAR__COUNT; i++)
+    {
+        offset = ClrStat[i].BA1__DestByteOffset;
+        count  = ClrStat[i].BA1__SourceSize;
+        for(  temp1 = offset; temp1<(offset+count); temp1+=4 );
+              writel(0, pBA1+temp1);
+    }
+
+    for(i=0; i<FILL__COUNT; i++)
+    {
+        temp2 = FillStat[i].Offset;
+        for(j=0; j<(FillStat[i].Size)/4; j++)
+        {
+            temp1 = (FillStat[i]).pFill[j];
+            writel(temp1, pBA1+temp2+j*4);
+        }
+    }
+    iounmap(pBA1);
+}
+
+
+/*
+ *  Chip reset
+ */
+
+static void cs461x_reset(struct cs_card *card)
+{
+	int idx;
+
+	/*
+	 *  Write the reset bit of the SP control register.
+	 */
+	cs461x_poke(card, BA1_SPCR, SPCR_RSTSP);
+
+	/*
+	 *  Write the control register.
+	 */
+	cs461x_poke(card, BA1_SPCR, SPCR_DRQEN);
+
+	/*
+	 *  Clear the trap registers.
+	 */
+	for (idx = 0; idx < 8; idx++) {
+		cs461x_poke(card, BA1_DREG, DREG_REGID_TRAP_SELECT + idx);
+		cs461x_poke(card, BA1_TWPR, 0xFFFF);
+	}
+	cs461x_poke(card, BA1_DREG, 0);
+
+	/*
+	 *  Set the frame timer to reflect the number of cycles per frame.
+	 */
+	cs461x_poke(card, BA1_FRMT, 0xadf);
+}
+
+static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type)
+{
+	int idx, loop, startfifo=0, endfifo=0, powerdown1 = 0;
+	unsigned int tmp;
+
+	/*
+	 *  See if the devices are powered down.  If so, we must power them up first
+	 *  or they will not respond.
+	 */
+	if (!((tmp = cs461x_peekBA0(card, BA0_CLKCR1)) & CLKCR1_SWCE)) {
+		cs461x_pokeBA0(card, BA0_CLKCR1, tmp | CLKCR1_SWCE);
+		powerdown1 = 1;
+	}
+
+	/*
+	 *  We want to clear out the serial port FIFOs so we don't end up playing
+	 *  whatever random garbage happens to be in them.  We fill the sample FIFOS
+	 *  with zero (silence).
+         */
+	cs461x_pokeBA0(card, BA0_SERBWP, 0);
+
+	/*
+	* Check for which FIFO locations to clear, if we are currently
+	* playing or capturing then we don't want to put in 128 bytes of
+	* "noise".
+	 */
+	if(type & CS_TYPE_DAC)
+	{
+		startfifo = 128;
+		endfifo = 256;
+	}
+	if(type & CS_TYPE_ADC)
+	{
+		startfifo = 0;
+		if(!endfifo)
+			endfifo = 128;
+	}
+	/*
+	 *  Fill sample FIFO locations (256 locations total).
+	 */
+	for (idx = startfifo; idx < endfifo; idx++) {
+		/*
+		 *  Make sure the previous FIFO write operation has completed.
+		 */
+		for (loop = 0; loop < 5; loop++) {
+			udelay(50);
+			if (!(cs461x_peekBA0(card, BA0_SERBST) & SERBST_WBSY))
+				break;
+		}
+		if (cs461x_peekBA0(card, BA0_SERBST) & SERBST_WBSY) {
+			if (powerdown1)
+				cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+		}
+		/*
+		 *  Write the serial port FIFO index.
+		 */
+		cs461x_pokeBA0(card, BA0_SERBAD, idx);
+		/*
+		 *  Tell the serial port to load the new value into the FIFO location.
+		 */
+		cs461x_pokeBA0(card, BA0_SERBCM, SERBCM_WRC);
+	}
+	/*
+	 *  Now, if we powered up the devices, then power them back down again.
+	 *  This is kinda ugly, but should never happen.
+	 */
+	if (powerdown1)
+		cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+}
+
+
+static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag)
+{
+	int count;
+	unsigned int tmp=0,muted=0;
+
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO 
+		"cs46xx: cs461x_powerdown()+ type=0x%x\n",type));
+	if(!cs_powerdown && !suspendflag)
+	{
+		CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+			"cs46xx: cs461x_powerdown() DISABLED exiting\n"));
+		return 0;
+	}
+	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+	CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+		"cs46xx: cs461x_powerdown() powerdown reg=0x%x\n",tmp));
+/*
+* if powering down only the VREF, and not powering down the DAC/ADC,
+* then do not power down the VREF, UNLESS both the DAC and ADC are not
+* currently powered down.  If powering down DAC and ADC, then
+* it is possible to power down the VREF (ON).
+*/
+	if (    ((type & CS_POWER_MIXVON) && 
+		 (!(type & CS_POWER_ADC) || (!(type & CS_POWER_DAC))) )
+	      && 
+		((tmp & CS_AC97_POWER_CONTROL_ADC_ON) ||
+		 (tmp & CS_AC97_POWER_CONTROL_DAC_ON) ) )
+	{
+		CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+			"cs46xx: cs461x_powerdown()- 0  unable to powerdown. tmp=0x%x\n",tmp));
+		return 0;
+	}
+/*
+* for now, always keep power to the mixer block.
+* not sure why it's a problem but it seems to be if we power off.
+*/
+	type &= ~CS_POWER_MIXVON;
+	type &= ~CS_POWER_MIXVOFF;
+
+	/*
+	 *  Power down indicated areas.
+	 */
+	if(type & CS_POWER_MIXVOFF)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVOFF\n"));
+		/*
+		 *  Power down the MIXER (VREF ON) on the AC97 card.  
+		 */
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp |= CS_AC97_POWER_CONTROL_MIXVOFF;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVOFF_ON))
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVOFF_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown MIXVOFF failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_MIXVON)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVON\n"));
+		/*
+		 *  Power down the MIXER (VREF ON) on the AC97 card.  
+		 */
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp |= CS_AC97_POWER_CONTROL_MIXVON;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVON_ON))
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVON_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown MIXVON failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_ADC)
+	{
+		/*
+		 *  Power down the ADC on the AC97 card.  
+		 */
+		CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ ADC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_ADC_ON)
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp |= CS_AC97_POWER_CONTROL_ADC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_ADC_ON))
+					break;
+			}
+
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_ADC_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown ADC failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_DAC)
+	{
+		/*
+		 *  Power down the DAC on the AC97 card.  
+		 */
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs461x_powerdown()+ DAC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (tmp & CS_AC97_POWER_CONTROL_DAC_ON)
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp |= CS_AC97_POWER_CONTROL_DAC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_DAC_ON))
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_DAC_ON)
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerdown DAC failed\n"));
+				return 1;
+			}
+		}
+	}
+	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+	if(muted)
+		cs_mute(card, CS_FALSE);
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO 
+		"cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp));
+	return 0;
+}
+
+static int cs46xx_powerup(struct cs_card *card, unsigned int type)
+{
+	int count;
+	unsigned int tmp=0,muted=0;
+
+	CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO 
+		"cs46xx: cs46xx_powerup()+ type=0x%x\n",type));
+	/*
+	* check for VREF and powerup if need to.
+	*/
+	if(type & CS_POWER_MIXVON)
+		type |= CS_POWER_MIXVOFF;
+	if(type & (CS_POWER_DAC | CS_POWER_ADC))
+		type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF;
+
+	/*
+	 *  Power up indicated areas.
+	 */
+	if(type & CS_POWER_MIXVOFF)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVOFF\n"));
+		/*
+		 *  Power up the MIXER (VREF ON) on the AC97 card.  
+		 */
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON))
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVOFF_ON)
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVOFF_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup MIXVOFF failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_MIXVON)
+	{
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVON\n"));
+		/*
+		 *  Power up the MIXER (VREF ON) on the AC97 card.  
+		 */
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON))
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp &= ~CS_AC97_POWER_CONTROL_MIXVON;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_MIXVON_ON)
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_MIXVON_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup MIXVON failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_ADC)
+	{
+		/*
+		 *  Power up the ADC on the AC97 card.  
+		 */
+		CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ ADC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON))
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp &= ~CS_AC97_POWER_CONTROL_ADC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_ADC_ON)
+					break;
+			}
+
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_ADC_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup ADC failed\n"));
+				return 1;
+			}
+		}
+	}
+	if(type & CS_POWER_DAC)
+	{
+		/*
+		 *  Power up the DAC on the AC97 card.  
+		 */
+
+		CS_DBGOUT(CS_FUNCTION, 4, 
+			printk(KERN_INFO "cs46xx: cs46xx_powerup()+ DAC\n"));
+		tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+		if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON))
+		{
+			if(!muted)
+			{
+				cs_mute(card, CS_TRUE);
+				muted=1;
+			}
+			tmp &= ~CS_AC97_POWER_CONTROL_DAC;
+			cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp );
+			/*
+			 *  Now, we wait until we sample a ready state.
+			 */
+			for (count = 0; count < 32; count++) {
+				/*
+				 *  First, lets wait a short while to let things settle out a
+				 *  bit, and to prevent retrying the read too quickly.
+				 */
+				udelay(500);
+
+				/*
+				 *  Read the current state of the power control register.
+				 */
+				if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+					CS_AC97_POWER_CONTROL_DAC_ON)
+					break;
+			}
+			
+			/*
+			 *  Check the status..
+			 */
+			if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & 
+				CS_AC97_POWER_CONTROL_DAC_ON))
+			{
+				CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING 
+					"cs46xx: powerup DAC failed\n"));
+				return 1;
+			}
+		}
+	}
+	tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL);
+	if(muted)
+		cs_mute(card, CS_FALSE);
+	CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO 
+		"cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp));
+	return 0;
+}
+
+
+static void cs461x_proc_start(struct cs_card *card)
+{
+	int cnt;
+
+	/*
+	 *  Set the frame timer to reflect the number of cycles per frame.
+	 */
+	cs461x_poke(card, BA1_FRMT, 0xadf);
+	/*
+	 *  Turn on the run, run at frame, and DMA enable bits in the local copy of
+	 *  the SP control register.
+	 */
+	cs461x_poke(card, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
+	/*
+	 *  Wait until the run at frame bit resets itself in the SP control
+	 *  register.
+	 */
+	for (cnt = 0; cnt < 25; cnt++) {
+		udelay(50);
+		if (!(cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR))
+			break;
+	}
+
+	if (cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR)
+		printk(KERN_WARNING "cs46xx: SPCR_RUNFR never reset\n");
+}
+
+static void cs461x_proc_stop(struct cs_card *card)
+{
+	/*
+	 *  Turn off the run, run at frame, and DMA enable bits in the local copy of
+	 *  the SP control register.
+	 */
+	cs461x_poke(card, BA1_SPCR, 0);
+}
+
+static int cs_hardware_init(struct cs_card *card)
+{
+	unsigned long end_time;
+	unsigned int tmp,count;
+	
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_hardware_init()+\n") );
+	/* 
+	 *  First, blast the clock control register to zero so that the PLL starts
+         *  out in a known state, and blast the master serial port control register
+         *  to zero so that the serial ports also start out in a known state.
+         */
+        cs461x_pokeBA0(card, BA0_CLKCR1, 0);
+        cs461x_pokeBA0(card, BA0_SERMC1, 0);
+
+	/*
+	 *  If we are in AC97 mode, then we must set the part to a host controlled
+         *  AC-link.  Otherwise, we won't be able to bring up the link.
+         */        
+        cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03);	/* 1.03 card */
+        /* cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); */ /* 2.00 card */
+
+        /*
+         *  Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+         *  spec) and then drive it high.  This is done for non AC97 modes since
+         *  there might be logic external to the CS461x that uses the ARST# line
+         *  for a reset.
+         */
+        cs461x_pokeBA0(card, BA0_ACCTL, 1);
+        udelay(50);
+        cs461x_pokeBA0(card, BA0_ACCTL, 0);
+        udelay(50);
+        cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_RSTN);
+
+	/*
+	 *  The first thing we do here is to enable sync generation.  As soon
+	 *  as we start receiving bit clock, we'll start producing the SYNC
+	 *  signal.
+	 */
+	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
+
+	/*
+	 *  Now wait for a short while to allow the AC97 part to start
+	 *  generating bit clock (so we don't try to start the PLL without an
+	 *  input clock).
+	 */
+	mdelay(5 * cs_laptop_wait);		/* 1 should be enough ?? (and pigs might fly) */
+
+	/*
+	 *  Set the serial port timing configuration, so that
+	 *  the clock control circuit gets its clock from the correct place.
+	 */
+	cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97);
+
+	/*
+	* The part seems to not be ready for a while after a resume.
+	* so, if we are resuming, then wait for 700 mils.  Note that 600 mils
+	* is not enough for some platforms! tested on an IBM Thinkpads and 
+	* reference cards.
+	*/
+	if(!(card->pm.flags & CS46XX_PM_IDLE))
+		mdelay(initdelay);
+	/*
+	 *  Write the selected clock control setup to the hardware.  Do not turn on
+	 *  SWCE yet (if requested), so that the devices clocked by the output of
+	 *  PLL are not clocked until the PLL is stable.
+	 */
+	cs461x_pokeBA0(card, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
+	cs461x_pokeBA0(card, BA0_PLLM, 0x3a);
+	cs461x_pokeBA0(card, BA0_CLKCR2, CLKCR2_PDIVS_8);
+
+	/*
+	 *  Power up the PLL.
+	 */
+	cs461x_pokeBA0(card, BA0_CLKCR1, CLKCR1_PLLP);
+
+	/*
+         *  Wait until the PLL has stabilized.
+	 */
+	mdelay(5 * cs_laptop_wait);		/* Again 1 should be enough ?? */
+
+	/*
+	 *  Turn on clocking of the core so that we can setup the serial ports.
+	 */
+	tmp = cs461x_peekBA0(card, BA0_CLKCR1) | CLKCR1_SWCE;
+	cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+
+	/*
+	 *  Fill the serial port FIFOs with silence.
+	 */
+	cs461x_clear_serial_FIFOs(card,CS_TYPE_DAC | CS_TYPE_ADC);
+
+	/*
+	 *  Set the serial port FIFO pointer to the first sample in the FIFO.
+	 */
+	/* cs461x_pokeBA0(card, BA0_SERBSP, 0); */
+
+	/*
+	 *  Write the serial port configuration to the part.  The master
+	 *  enable bit is not set until all other values have been written.
+	 */
+	cs461x_pokeBA0(card, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
+	cs461x_pokeBA0(card, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
+	cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
+
+
+	mdelay(5 * cs_laptop_wait);		/* Shouldnt be needed ?? */
+	
+/*
+* If we are resuming under 2.2.x then we can not schedule a timeout.
+* so, just spin the CPU.
+*/
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
+	/*
+	 * Wait for the card ready signal from the AC97 card.
+	 */
+		end_time = jiffies + 3 * (HZ >> 2);
+		do {
+		/*
+		 *  Read the AC97 status register to see if we've seen a CODEC READY
+		 *  signal from the AC97 card.
+		 */
+			if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)
+				break;
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		} while (time_before(jiffies, end_time));
+	}
+	else
+	{
+		for (count = 0; count < 100; count++) {
+		// First, we want to wait for a short time.
+			udelay(25 * cs_laptop_wait);
+
+			if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)
+				break;
+		}
+	}
+
+	/*
+	 *  Make sure CODEC is READY.
+	 */
+	if (!(cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)) {
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING  
+			"cs46xx: create - never read card ready from AC'97\n"));
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING  
+			"cs46xx: probably not a bug, try using the CS4232 driver,\n"));
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING  
+			"cs46xx: or turn off any automatic Power Management support in the BIOS.\n"));
+		return -EIO;
+	}
+
+	/*
+	 *  Assert the vaid frame signal so that we can start sending commands
+	 *  to the AC97 card.
+	 */
+	cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
+	/*
+	 *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
+	 *  the card is pumping ADC data across the AC-link.
+	 */
+		end_time = jiffies + 3 * (HZ >> 2);
+		do {
+			/*
+			 *  Read the input slot valid register and see if input slots 3 and
+			 *  4 are valid yet.
+			 */
+			if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+				break;
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		} while (time_before(jiffies, end_time));
+	}
+	else
+	{
+		for (count = 0; count < 100; count++) {
+		// First, we want to wait for a short time.
+			udelay(25 * cs_laptop_wait);
+
+			if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+				break;
+		}
+	}
+	/*
+	 *  Make sure input slots 3 and 4 are valid.  If not, then return
+	 *  an error.
+	 */
+	if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) {
+		printk(KERN_WARNING "cs46xx: create - never read ISV3 & ISV4 from AC'97\n");
+		return -EIO;
+	}
+
+	/*
+	 *  Now, assert valid frame and the slot 3 and 4 valid bits.  This will
+	 *  commense the transfer of digital audio data to the AC97 card.
+	 */
+	cs461x_pokeBA0(card, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	/* tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; */
+	/* cs461x_pokeBA0(card, BA0_CLKCR1, tmp); */
+
+	/*
+         *  Reset the processor.
+         */
+	cs461x_reset(card);
+
+	/*
+         *  Download the image to the processor.
+	 */
+	
+	cs461x_download_image(card);
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_PCTL);
+	card->pctl = tmp & 0xffff0000;
+	cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_CCTL);
+	card->cctl = tmp & 0x0000ffff;
+	cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000);
+
+	/* initialize AC97 codec and register /dev/mixer */
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
+		if (cs_ac97_init(card) <= 0)
+		{
+			CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+				"cs46xx: cs_ac97_init() failure\n") );
+			return -EIO;
+		}
+	}
+	else
+	{
+		cs46xx_ac97_resume(card);
+	}
+	
+	cs461x_proc_start(card);
+
+	/*
+	 *  Enable interrupts on the part.
+	 */
+	cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM);
+
+	tmp = cs461x_peek(card, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	cs461x_poke(card, BA1_PFIE, tmp);	/* playback interrupt enable */
+
+	tmp = cs461x_peek(card, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000001;
+	cs461x_poke(card, BA1_CIE, tmp);	/* capture interrupt enable */	
+
+	/*
+	 *  If IDLE then Power down the part.  We will power components up 
+	 *  when we need them.  
+	 */
+	if(card->pm.flags & CS46XX_PM_IDLE)
+	{
+		if(!cs_powerdown)
+		{
+			if( (tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC |
+					CS_POWER_MIXVON )) )
+			{
+				CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+					"cs46xx: cs461x_powerup() failure (0x%x)\n",tmp) );
+				return -EIO;
+			}
+		}
+		else
+		{
+			if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
+					CS_POWER_MIXVON, CS_FALSE )) )
+			{
+				CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+					"cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) );
+				return -EIO;
+			}
+		}
+	}
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO 
+		"cs46xx: cs_hardware_init()- 0\n"));
+	return 0;
+}
+
+/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered 
+   until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+   
+/*
+ *	Card subid table
+ */
+ 
+struct cs_card_type
+{
+	u16 vendor;
+	u16 id;
+	char *name;
+	void (*amp)(struct cs_card *, int);
+	void (*amp_init)(struct cs_card *);
+	void (*active)(struct cs_card *, int);
+};
+
+static struct cs_card_type cards[]={
+	{0x1489, 0x7001, "Genius Soundmaker 128 value", amp_none, NULL, NULL},
+	{0x5053, 0x3357, "Voyetra", amp_voyetra, NULL, NULL},
+	{0x1071, 0x6003, "Mitac MI6020/21", amp_voyetra, NULL, NULL},
+	{0x14AF, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	{0x1681, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	{0x1681, 0x0051, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	{0x1681, 0x0052, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	{0x1681, 0x0053, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	{0x1681, 0x0054, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	/* Not sure if the 570 needs the clkrun hack */
+	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, NULL, clkrun_hack},
+	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, NULL, clkrun_hack},
+	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL},
+	{0, 0, "Card without SSID set", NULL, NULL, NULL },
+	{0, 0, NULL, NULL, NULL}
+};
+
+MODULE_AUTHOR("Alan Cox <alan@redhat.com>, Jaroslav Kysela, <pcaudio@crystal.cirrus.com>");
+MODULE_DESCRIPTION("Crystal SoundFusion Audio Support");
+MODULE_LICENSE("GPL");
+
+
+static const char cs46xx_banner[] = KERN_INFO "Crystal 4280/46xx + AC97 Audio, version " CS46XX_MAJOR_VERSION "." CS46XX_MINOR_VERSION "." CS46XX_ARCH ", " __TIME__ " " __DATE__ "\n";
+static const char fndmsg[] = KERN_INFO "cs46xx: Found %d audio device(s).\n";
+
+static int __devinit cs46xx_probe(struct pci_dev *pci_dev,
+				  const struct pci_device_id *pciid)
+{
+	struct pm_dev *pmdev;
+	int i,j;
+	u16 ss_card, ss_vendor;
+	struct cs_card *card;
+	dma_addr_t dma_mask;
+	struct cs_card_type *cp = &cards[0];
+
+	CS_DBGOUT(CS_FUNCTION | CS_INIT, 2,
+		  printk(KERN_INFO "cs46xx: probe()+\n"));
+
+	dma_mask = 0xffffffff;	/* this enables playback and recording */
+	if (pci_enable_device(pci_dev)) {
+		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
+			 "cs46xx: pci_enable_device() failed\n"));
+		return -1;
+	}
+	if (!RSRCISMEMORYREGION(pci_dev, 0) ||
+	    !RSRCISMEMORYREGION(pci_dev, 1)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			 "cs46xx: probe()- Memory region not assigned\n"));
+		return -1;
+	}
+	if (pci_dev->irq == 0) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+			 "cs46xx: probe() IRQ not assigned\n"));
+		return -1;
+	}
+	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+		      "cs46xx: probe() architecture does not support 32bit PCI busmaster DMA\n"));
+		return -1;
+	}
+	pci_read_config_word(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
+	pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &ss_card);
+
+	if ((card = kmalloc(sizeof(struct cs_card), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "cs46xx: out of memory\n");
+		return -ENOMEM;
+	}
+	memset(card, 0, sizeof(*card));
+	card->ba0_addr = RSRCADDRESS(pci_dev, 0);
+	card->ba1_addr = RSRCADDRESS(pci_dev, 1);
+	card->pci_dev = pci_dev;
+	card->irq = pci_dev->irq;
+	card->magic = CS_CARD_MAGIC;
+	spin_lock_init(&card->lock);
+
+	pci_set_master(pci_dev);
+
+	printk(cs46xx_banner);
+	printk(KERN_INFO "cs46xx: Card found at 0x%08lx and 0x%08lx, IRQ %d\n",
+	       card->ba0_addr, card->ba1_addr, card->irq);
+
+	card->alloc_pcm_channel = cs_alloc_pcm_channel;
+	card->alloc_rec_pcm_channel = cs_alloc_rec_pcm_channel;
+	card->free_pcm_channel = cs_free_pcm_channel;
+	card->amplifier_ctrl = amp_none;
+	card->active_ctrl = amp_none;
+
+	while (cp->name)
+	{
+		if(cp->vendor == ss_vendor && cp->id == ss_card)
+		{
+			card->amplifier_ctrl = cp->amp;
+			if(cp->active)
+				card->active_ctrl = cp->active;
+			if(cp->amp_init)
+				card->amp_init = cp->amp_init;
+			break;
+		}
+		cp++;
+	}
+	if (cp->name==NULL)
+	{
+		printk(KERN_INFO "cs46xx: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n",
+			ss_vendor, ss_card, card->ba0_addr, card->ba1_addr,  card->irq);
+	}
+	else
+	{
+		printk(KERN_INFO "cs46xx: %s (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n",
+			cp->name, ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq);
+	}
+	
+	if (card->amplifier_ctrl==NULL)
+	{
+		card->amplifier_ctrl = amp_none;
+		card->active_ctrl = clkrun_hack;
+	}		
+
+	if (external_amp == 1)
+	{
+		printk(KERN_INFO "cs46xx: Crystal EAPD support forced on.\n");
+		card->amplifier_ctrl = amp_voyetra;
+	}
+
+	if (thinkpad == 1)
+	{
+		printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n");
+		card->active_ctrl = clkrun_hack;
+	}
+/*
+* The thinkpads don't work well without runtime updating on their kernel 
+* delay values (or any laptop with variable CPU speeds really).
+* so, just to be safe set the init delay to 2100.  Eliminates
+* failures on T21 Thinkpads.  remove this code when the udelay
+* and mdelay kernel code is replaced by a pm timer, or the delays
+* work well for battery and/or AC power both.
+*/
+	if(card->active_ctrl == clkrun_hack)
+	{
+		initdelay = 2100;
+		cs_laptop_wait = 5;
+	}
+	if((card->active_ctrl == clkrun_hack) && !(powerdown == 1))
+	{
+/*
+* for some currently unknown reason, powering down the DAC and ADC component
+* blocks on thinkpads causes some funky behavior... distoorrrtion and ac97 
+* codec access problems.  probably the serial clock becomes unsynced. 
+* added code to sync the chips back up, but only helped about 70% the time.
+*/
+		cs_powerdown = 0;
+	}
+	if(powerdown == 0)
+		cs_powerdown = 0;
+	card->active_ctrl(card, 1);
+
+	/* claim our iospace and irq */
+	
+	card->ba0 = ioremap_nocache(card->ba0_addr, CS461X_BA0_SIZE);
+	card->ba1.name.data0 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
+	card->ba1.name.data1 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
+	card->ba1.name.pmem = ioremap_nocache(card->ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
+	card->ba1.name.reg = ioremap_nocache(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
+	
+	CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO 
+		"cs46xx: card=0x%x card->ba0=0x%.08x\n",(unsigned)card,(unsigned)card->ba0) );
+	CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO 
+		"cs46xx: card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n",
+			(unsigned)card->ba1.name.data0,
+			(unsigned)card->ba1.name.data1,
+			(unsigned)card->ba1.name.pmem,
+			(unsigned)card->ba1.name.reg) );
+
+	if(card->ba0 == 0 || card->ba1.name.data0 == 0 ||
+		card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 ||
+		card->ba1.name.reg == 0)
+		goto fail2;
+		
+	if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) {
+		printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq);
+		goto fail2;
+	}
+	/* register /dev/dsp */
+	if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) {
+		printk(KERN_ERR "cs46xx: unable to register dsp\n");
+		goto fail;
+	}
+
+        /* register /dev/midi */
+        if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0)
+                printk(KERN_ERR "cs46xx: unable to register midi\n");
+                
+	card->pm.flags |= CS46XX_PM_IDLE;
+	for(i=0;i<5;i++)
+	{
+		if (cs_hardware_init(card) != 0)
+		{
+			CS_DBGOUT(CS_ERROR, 4, printk(
+				"cs46xx: ERROR in cs_hardware_init()... retrying\n"));
+			for (j = 0; j < NR_AC97; j++)
+				if (card->ac97_codec[j] != NULL) {
+					unregister_sound_mixer(card->ac97_codec[j]->dev_mixer);
+					kfree (card->ac97_codec[j]);
+				}
+			mdelay(10 * cs_laptop_wait);
+			continue;
+		}
+		break;
+	}
+	if(i>=4)
+	{
+		CS_DBGOUT(CS_PM | CS_ERROR, 1, printk(
+			"cs46xx: cs46xx_probe()- cs_hardware_init() failed, retried %d times.\n",i));
+                unregister_sound_dsp(card->dev_audio);
+                if(card->dev_midi)
+                        unregister_sound_midi(card->dev_midi);
+                goto fail;
+	}
+
+        init_waitqueue_head(&card->midi.open_wait);
+        init_MUTEX(&card->midi.open_sem);
+        init_waitqueue_head(&card->midi.iwait);
+        init_waitqueue_head(&card->midi.owait);
+        cs461x_pokeBA0(card, BA0_MIDCR, MIDCR_MRST);   
+        cs461x_pokeBA0(card, BA0_MIDCR, 0);   
+
+	/* 
+	* Check if we have to init the amplifier, but probably already done
+	* since the CD logic in the ac97 init code will turn on the ext amp.
+	*/
+	if(cp->amp_init)
+		cp->amp_init(card);
+        card->active_ctrl(card, -1);
+
+	PCI_SET_DRIVER_DATA(pci_dev, card);
+	PCI_SET_DMA_MASK(pci_dev, dma_mask);
+	list_add(&card->list, &cs46xx_devs);
+
+	pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), cs46xx_pm_callback);
+	if (pmdev)
+	{
+		CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO
+			 "cs46xx: probe() pm_register() succeeded (0x%x).\n",
+				(unsigned)pmdev));
+		pmdev->data = card;
+	}
+	else
+	{
+		CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 2, printk(KERN_INFO
+			 "cs46xx: probe() pm_register() failed (0x%x).\n",
+				(unsigned)pmdev));
+		card->pm.flags |= CS46XX_PM_NOT_REGISTERED;
+	}
+
+	CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=0x%x\n",
+		(unsigned)card->pm.flags,(unsigned)card));
+
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		"cs46xx: probe()- device allocated successfully\n"));
+        return 0;
+
+fail:
+	free_irq(card->irq, card);
+fail2:
+	if(card->ba0)
+		iounmap(card->ba0);
+	if(card->ba1.name.data0)
+		iounmap(card->ba1.name.data0);
+	if(card->ba1.name.data1)
+		iounmap(card->ba1.name.data1);
+	if(card->ba1.name.pmem)
+		iounmap(card->ba1.name.pmem);
+	if(card->ba1.name.reg)
+		iounmap(card->ba1.name.reg);
+	kfree(card);
+	CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO
+		"cs46xx: probe()- no device allocated\n"));
+	return -ENODEV;
+} // probe_cs46xx
+
+// --------------------------------------------------------------------- 
+
+static void __devinit cs46xx_remove(struct pci_dev *pci_dev)
+{
+	struct cs_card *card = PCI_GET_DRIVER_DATA(pci_dev);
+	int i;
+	unsigned int tmp;
+	
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		 "cs46xx: cs46xx_remove()+\n"));
+
+	card->active_ctrl(card,1);
+	
+	tmp = cs461x_peek(card, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	tmp |=  0x00000010;
+	cs461x_poke(card, BA1_PFIE, tmp);	/* playback interrupt disable */
+
+	tmp = cs461x_peek(card, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000011;
+	cs461x_poke(card, BA1_CIE, tmp);	/* capture interrupt disable */
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_PCTL);
+	cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = cs461x_peek(card, BA1_CCTL);
+	cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000);
+
+	/*
+         *  Reset the processor.
+         */
+	cs461x_reset(card);
+
+	cs461x_proc_stop(card);
+
+	/*
+	 *  Power down the DAC and ADC.  We will power them up (if) when we need
+	 *  them.
+	 */
+	if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC |
+			CS_POWER_MIXVON, CS_TRUE )) )
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO 
+			"cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) );
+	}
+
+	/*
+	 *  Power down the PLL.
+	 */
+	cs461x_pokeBA0(card, BA0_CLKCR1, 0);
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE;
+	cs461x_pokeBA0(card, BA0_CLKCR1, tmp);
+
+	card->active_ctrl(card,-1);
+
+	/* free hardware resources */
+	free_irq(card->irq, card);
+	iounmap(card->ba0);
+	iounmap(card->ba1.name.data0);
+	iounmap(card->ba1.name.data1);
+	iounmap(card->ba1.name.pmem);
+	iounmap(card->ba1.name.reg);
+	
+	/* unregister audio devices */
+	for (i = 0; i < NR_AC97; i++)
+		if (card->ac97_codec[i] != NULL) {
+			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+			kfree (card->ac97_codec[i]);
+		}
+	unregister_sound_dsp(card->dev_audio);
+        if(card->dev_midi)
+                unregister_sound_midi(card->dev_midi);
+	list_del(&card->list);
+	kfree(card);
+	PCI_SET_DRIVER_DATA(pci_dev,NULL);
+
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+		 "cs46xx: cs46xx_remove()-: remove successful\n"));
+}
+
+enum {
+	CS46XX_4610 = 0,
+	CS46XX_4612,  	/* same as 4630 */
+	CS46XX_4615,  	/* same as 4624 */
+};
+
+static struct pci_device_id cs46xx_pci_tbl[] __devinitdata = {
+	
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4610},
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4612},
+	{PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4615},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cs46xx_pci_tbl);
+
+struct pci_driver cs46xx_pci_driver = {
+	name:"cs46xx",
+	id_table:cs46xx_pci_tbl,
+	probe:cs46xx_probe,
+	remove:cs46xx_remove,
+	suspend:CS46XX_SUSPEND_TBL,
+	resume:CS46XX_RESUME_TBL,
+};
+
+int __init cs46xx_init_module(void)
+{
+	int rtn = 0;
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO 
+		"cs46xx: cs46xx_init_module()+ \n"));
+	if (!pci_present()) {	/* No PCI bus in this machine! */
+		CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
+			"cs46xx: cs46xx_init_module()- no pci bus found\n"));
+		return -ENODEV;
+	}
+	rtn = pci_module_init(&cs46xx_pci_driver);
+
+	if(rtn == -ENODEV)
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( 
+			"cs46xx: Unable to detect valid cs46xx device\n"));
+	}
+
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs46xx: cs46xx_init_module()- (%d)\n",rtn));
+	return rtn;
+}
+
+void __exit cs46xx_cleanup_module(void)
+{
+	pci_unregister_driver(&cs46xx_pci_driver);
+	cs_pm_unregister_all(cs46xx_pm_callback);
+	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs46xx: cleanup_cs46xx() finished\n"));
+}
+
+module_init(cs46xx_init_module);
+module_exit(cs46xx_cleanup_module);
+
+int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	struct cs_card *card;
+
+	CS_DBGOUT(CS_PM, 2, printk(KERN_INFO 
+		"cs46xx: cs46xx_pm_callback dev=0x%x rqst=0x%x card=%d\n",
+			(unsigned)dev,(unsigned)rqst,(unsigned)data));
+	card = (struct cs_card *) dev->data;
+	if (card) {
+		switch(rqst) {
+			case PM_SUSPEND:
+				CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
+					"cs46xx: PM suspend request\n"));
+				if(cs46xx_suspend(card, 0))
+				{
+				    CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					"cs46xx: PM suspend request refused\n"));
+					return 1; 
+				}
+				break;
+			case PM_RESUME:
+				CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
+					"cs46xx: PM resume request\n"));
+				if(cs46xx_resume(card))
+				{
+				    CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+					"cs46xx: PM resume request refused\n"));
+					return 1;
+				}
+				break;
+		}
+	}
+
+	return 0;
+}
+
+#if CS46XX_ACPI_SUPPORT
+static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state)
+{
+	struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 2, 
+		printk(KERN_INFO "cs46xx: cs46xx_suspend_tbl request\n"));
+	cs46xx_suspend(s, 0);
+	return 0;
+}
+
+static int cs46xx_resume_tbl(struct pci_dev *pcidev)
+{
+	struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev);
+	CS_DBGOUT(CS_PM | CS_FUNCTION, 2, 
+		printk(KERN_INFO "cs46xx: cs46xx_resume_tbl request\n"));
+	cs46xx_resume(s);
+	return 0;
+}
+#endif
diff -Nru linux/sound/oss/cs46xx_wrapper-24.h linux-2.4.19-pre5-mjc/sound/oss/cs46xx_wrapper-24.h
--- linux/sound/oss/cs46xx_wrapper-24.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs46xx_wrapper-24.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,56 @@
+/*******************************************************************************
+*
+*      "cs46xx_wrapper.c" --  Cirrus Logic-Crystal CS46XX linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (pcaudio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 01/11/2001 trw - new file from cs4281 wrapper code. 
+*
+*******************************************************************************/
+#ifndef __CS46XX_WRAPPER24_H
+#define __CS46XX_WRAPPER24_H
+
+#include <linux/spinlock.h>
+
+#define CS_OWNER owner:
+#define CS_THIS_MODULE THIS_MODULE,
+void cs46xx_null(struct pci_dev *pcidev) { return; }
+#define cs4x_mem_map_reserve(page) mem_map_reserve(page)
+#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page)
+
+#define free_dmabuf(card, dmabuf) \
+	pci_free_consistent((card)->pci_dev, \
+			    PAGE_SIZE << (dmabuf)->buforder, \
+			    (dmabuf)->rawbuf, (dmabuf)->dmaaddr);
+#define free_dmabuf2(card, dmabuf) \
+	pci_free_consistent((card)->pci_dev, \
+				    PAGE_SIZE << (dmabuf)->buforder_tmpbuff, \
+				    (dmabuf)->tmpbuff, (dmabuf)->dmaaddr_tmpbuff);
+#define cs4x_pgoff(vma) ((vma)->vm_pgoff)
+
+#define RSRCISIOREGION(dev,num) ((dev)->resource[(num)].start != 0 && \
+	 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+#define RSRCISMEMORYREGION(dev,num) ((dev)->resource[(num)].start != 0 && \
+	 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY)
+#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
+#define PCI_GET_DRIVER_DATA pci_get_drvdata
+#define PCI_SET_DRIVER_DATA pci_set_drvdata
+#define PCI_SET_DMA_MASK(pcidev,mask) pcidev->dma_mask = mask
+
+#endif
diff -Nru linux/sound/oss/cs46xxpm-24.h linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm-24.h
--- linux/sound/oss/cs46xxpm-24.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm-24.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,53 @@
+/*******************************************************************************
+*
+*      "cs46xxpm-24.h" --  Cirrus Logic-Crystal CS46XX linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (pcaudio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 12/22/00 trw - new file. 
+*
+*******************************************************************************/
+#ifndef __CS46XXPM24_H
+#define __CS46XXPM24_H
+
+#include <linux/pm.h>
+#include "cs46xxpm.h"
+
+
+#define CS46XX_ACPI_SUPPORT 1
+#ifdef CS46XX_ACPI_SUPPORT
+/* 
+* for now (12/22/00) only enable the pm_register PM support.
+* allow these table entries to be null.
+*/
+static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state);
+static int cs46xx_resume_tbl(struct pci_dev *pcidev);
+#define cs_pm_register(a, b, c)  0
+#define cs_pm_unregister_all(a) 
+#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl
+#define CS46XX_RESUME_TBL cs46xx_resume_tbl
+#else
+#define cs_pm_register(a, b, c) pm_register((a), (b), (c));
+#define cs_pm_unregister_all(a) pm_unregister_all((a));
+#define CS46XX_SUSPEND_TBL cs46xx_null
+#define CS46XX_RESUME_TBL cs46xx_null
+#endif
+int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+#endif
diff -Nru linux/sound/oss/cs46xxpm.h linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm.h
--- linux/sound/oss/cs46xxpm.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,70 @@
+/*******************************************************************************
+*
+*      "cs46xxpm.h" --  Cirrus Logic-Crystal CS46XX linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (pcaudio@crystal.cirrus.com).
+*
+*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 12/22/00 trw - new file. 
+*
+*******************************************************************************/
+#ifndef __CS46XXPM_H
+#define __CS46XXPM_H
+
+#define CS46XX_AC97_HIGHESTREGTORESTORE 0x26
+#define CS46XX_AC97_NUMBER_RESTORE_REGS (CS46XX_AC97_HIGHESTREGTORESTORE/2-1)
+
+/* PM state defintions */
+#define CS46XX_PM_NOT_REGISTERED	0x1000
+#define CS46XX_PM_IDLE			0x0001
+#define CS46XX_PM_SUSPENDING		0x0002
+#define CS46XX_PM_SUSPENDED		0x0004
+#define CS46XX_PM_RESUMING		0x0008
+#define CS46XX_PM_RESUMED		0x0010
+
+#define CS_POWER_DAC			0x0001
+#define CS_POWER_ADC			0x0002
+#define CS_POWER_MIXVON			0x0004
+#define CS_POWER_MIXVOFF		0x0008
+#define CS_AC97_POWER_CONTROL_ON	0xf000  /* always on bits (inverted) */
+#define CS_AC97_POWER_CONTROL_ADC	0x0100
+#define CS_AC97_POWER_CONTROL_DAC	0x0200
+#define CS_AC97_POWER_CONTROL_MIXVON	0x0400
+#define CS_AC97_POWER_CONTROL_MIXVOFF	0x0800
+#define CS_AC97_POWER_CONTROL_ADC_ON	0x0001
+#define CS_AC97_POWER_CONTROL_DAC_ON	0x0002
+#define CS_AC97_POWER_CONTROL_MIXVON_ON	0x0004
+#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008
+
+struct cs46xx_pm {
+	unsigned long flags;
+	u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue;
+	u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR;
+	u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save;
+	u32 u32SSPM_BITS;
+	u32 ac97[CS46XX_AC97_NUMBER_RESTORE_REGS];
+	u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono;
+	u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose;
+	u32 u32hwptr_playback,u32hwptr_capture;
+	unsigned dmabuf_swptr_play;
+	int dmabuf_count_play;
+	unsigned dmabuf_swptr_capture;
+	int dmabuf_count_capture;
+};
+
+#endif
diff -Nru linux/sound/oss/dev_table.c linux-2.4.19-pre5-mjc/sound/oss/dev_table.c
--- linux/sound/oss/dev_table.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dev_table.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,221 @@
+/*
+ * sound/dev_table.c
+ *
+ * Device call tables.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+#include <linux/init.h>
+
+#define _DEV_TABLE_C_
+#include "sound_config.h"
+
+int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
+			int driver_size, int flags, unsigned int format_mask,
+			void *devc, int dma1, int dma2)
+{
+	struct audio_driver *d;
+	struct audio_operations *op;
+	int l, num;
+
+	if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
+		printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
+		return -(EINVAL);
+	}
+	num = sound_alloc_audiodev();
+
+	if (num == -1) {
+		printk(KERN_ERR "sound: Too many audio drivers\n");
+		return -(EBUSY);
+	}
+	d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
+
+	if (sound_nblocks < 1024)
+		sound_nblocks++;
+
+	op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations)));
+
+	if (sound_nblocks < 1024)
+		sound_nblocks++;
+	if (d == NULL || op == NULL) {
+		printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
+		sound_unload_audiodev(num);
+		return -(ENOMEM);
+	}
+	memset((char *) op, 0, sizeof(struct audio_operations));
+	init_waitqueue_head(&op->in_sleeper);
+	init_waitqueue_head(&op->out_sleeper);	
+	init_waitqueue_head(&op->poll_sleeper);
+	if (driver_size < sizeof(struct audio_driver))
+		memset((char *) d, 0, sizeof(struct audio_driver));
+
+	memcpy((char *) d, (char *) driver, driver_size);
+
+	op->d = d;
+	l = strlen(name) + 1;
+	if (l > sizeof(op->name))
+		l = sizeof(op->name);
+	strncpy(op->name, name, l);
+	op->name[l - 1] = 0;
+	op->flags = flags;
+	op->format_mask = format_mask;
+	op->devc = devc;
+
+	/*
+	 *    Hardcoded defaults
+	 */
+	audio_devs[num] = op;
+
+	DMAbuf_init(num, dma1, dma2);
+
+	audio_init_devices();
+	return num;
+}
+
+int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
+	int driver_size, void *devc)
+{
+	struct mixer_operations *op;
+	int l;
+
+	int n = sound_alloc_mixerdev();
+
+	if (n == -1) {
+		printk(KERN_ERR "Sound: Too many mixer drivers\n");
+		return -EBUSY;
+	}
+	if (vers != MIXER_DRIVER_VERSION ||
+		driver_size > sizeof(struct mixer_operations)) {
+		printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
+		return -EINVAL;
+	}
+	
+	/* FIXME: This leaks a mixer_operations struct every time its called
+	   until you unload sound! */
+	   
+	op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations)));
+
+	if (sound_nblocks < 1024)
+		sound_nblocks++;
+	if (op == NULL) {
+		printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
+		return -ENOMEM;
+	}
+	memset((char *) op, 0, sizeof(struct mixer_operations));
+	memcpy((char *) op, (char *) driver, driver_size);
+
+	l = strlen(name) + 1;
+	if (l > sizeof(op->name))
+		l = sizeof(op->name);
+	strncpy(op->name, name, l);
+	op->name[l - 1] = 0;
+	op->devc = devc;
+
+	mixer_devs[n] = op;
+	return n;
+}
+
+void sound_unload_audiodev(int dev)
+{
+	if (dev != -1) {
+		DMAbuf_deinit(dev);
+		audio_devs[dev] = NULL;
+		unregister_sound_dsp((dev<<4)+3);
+	}
+}
+
+int sound_alloc_audiodev(void)
+{ 
+	int i = register_sound_dsp(&oss_sound_fops, -1);
+	if(i==-1)
+		return i;
+	i>>=4;
+	if(i>=num_audiodevs)
+		num_audiodevs = i + 1;
+	return i;
+}
+
+int sound_alloc_mididev(void)
+{
+	int i = register_sound_midi(&oss_sound_fops, -1);
+	if(i==-1)
+		return i;
+	i>>=4;
+	if(i>=num_midis)
+		num_midis = i + 1;
+	return i;
+}
+
+int sound_alloc_synthdev(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_SYNTH_DEV; i++) {
+		if (synth_devs[i] == NULL) {
+			if (i >= num_synths)
+				num_synths++;
+			return i;
+		}
+	}
+	return -1;
+}
+
+int sound_alloc_mixerdev(void)
+{
+	int i = register_sound_mixer(&oss_sound_fops, -1);
+	if(i==-1)
+		return -1;
+	i>>=4;
+	if(i>=num_mixers)
+		num_mixers = i + 1;
+	return i;
+}
+
+int sound_alloc_timerdev(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_TIMER_DEV; i++) {
+		if (sound_timer_devs[i] == NULL) {
+			if (i >= num_sound_timers)
+				num_sound_timers++;
+			return i;
+		}
+	}
+	return -1;
+}
+
+void sound_unload_mixerdev(int dev)
+{
+	if (dev != -1) {
+		mixer_devs[dev] = NULL;
+		unregister_sound_mixer(dev<<4);
+		num_mixers--;
+	}
+}
+
+void sound_unload_mididev(int dev)
+{
+	if (dev != -1) {
+		midi_devs[dev] = NULL;
+		unregister_sound_midi((dev<<4)+2);
+	}
+}
+
+void sound_unload_synthdev(int dev)
+{
+	if (dev != -1)
+		synth_devs[dev] = NULL;
+}
+
+void sound_unload_timerdev(int dev)
+{
+	if (dev != -1)
+		sound_timer_devs[dev] = NULL;
+}
diff -Nru linux/sound/oss/dev_table.h linux-2.4.19-pre5-mjc/sound/oss/dev_table.h
--- linux/sound/oss/dev_table.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dev_table.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,403 @@
+/*
+ *	dev_table.h
+ *
+ *	Global definitions for device call tables
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+#ifndef _DEV_TABLE_H_
+#define _DEV_TABLE_H_
+
+/*
+ * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
+ * Numbers 1000 to N are reserved for driver's internal use.
+ */
+
+#define SNDCARD_DESKPROXL		27	/* Compaq Deskpro XL */
+#define SNDCARD_VIDC			28	/* ARMs VIDC */
+#define SNDCARD_SBPNP			29
+#define SNDCARD_SOFTOSS			36
+#define SNDCARD_VMIDI			37
+#define SNDCARD_OPL3SA1			38	/* Note: clash in msnd.h */
+#define SNDCARD_OPL3SA1_SB		39
+#define SNDCARD_OPL3SA1_MPU		40
+#define SNDCARD_WAVEFRONT               41
+#define SNDCARD_OPL3SA2                 42
+#define SNDCARD_OPL3SA2_MPU             43
+#define SNDCARD_WAVEARTIST              44	/* Waveartist */
+#define SNDCARD_OPL3SA2_MSS             45	/* Originally missed */
+#define SNDCARD_AD1816                  88
+
+/*
+ *	NOTE! 	NOTE!	NOTE!	NOTE!
+ *
+ *	If you modify this file, please check the dev_table.c also.
+ *
+ *	NOTE! 	NOTE!	NOTE!	NOTE!
+ */
+
+struct driver_info 
+{
+	char *driver_id;
+	int card_subtype;	/* Driver specific. Usually 0 */
+	int card_type;		/*	From soundcard.h	*/
+	char *name;
+	void (*attach) (struct address_info *hw_config);
+	int (*probe) (struct address_info *hw_config);
+	void (*unload) (struct address_info *hw_config);
+};
+
+struct card_info 
+{
+	int card_type;	/* Link (search key) to the driver list */
+	struct address_info config;
+	int enabled;
+	void *for_driver_use;
+};
+
+
+/*
+ * Device specific parameters (used only by dmabuf.c)
+ */
+#define MAX_SUB_BUFFERS		(32*MAX_REALTIME_FACTOR)
+
+#define DMODE_NONE		0
+#define DMODE_OUTPUT		PCM_ENABLE_OUTPUT
+#define DMODE_INPUT		PCM_ENABLE_INPUT
+
+struct dma_buffparms 
+{
+	int      dma_mode;	/* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+	int	 closing;
+
+	/*
+ 	 * Pointers to raw buffers
+ 	 */
+
+  	char     *raw_buf;
+    	unsigned long   raw_buf_phys;
+	int buffsize;
+
+     	/*
+         * Device state tables
+         */
+
+	unsigned long flags;
+#define DMA_BUSY	0x00000001
+#define DMA_RESTART	0x00000002
+#define DMA_ACTIVE	0x00000004
+#define DMA_STARTED	0x00000008
+#define DMA_EMPTY	0x00000010	
+#define DMA_ALLOC_DONE	0x00000020
+#define DMA_SYNCING	0x00000040
+#define DMA_DIRTY	0x00000080
+#define DMA_POST	0x00000100
+#define DMA_NODMA	0x00000200
+#define DMA_NOTIMEOUT	0x00000400
+
+	int      open_mode;
+
+	/*
+	 * Queue parameters.
+	 */
+       	int      qlen;
+       	int      qhead;
+       	int      qtail;
+	int	 cfrag;	/* Current incomplete fragment (write) */
+
+	int      nbufs;
+	int      counts[MAX_SUB_BUFFERS];
+	int      subdivision;
+
+	int      fragment_size;
+        int	 needs_reorg;
+	int	 max_fragments;
+
+	int	 bytes_in_use;
+
+	int	 underrun_count;
+	unsigned long	 byte_counter;
+	unsigned long	 user_counter;
+	unsigned long	 max_byte_counter;
+	int	 data_rate; /* Bytes/second */
+
+	int	 mapping_flags;
+#define			DMA_MAP_MAPPED		0x00000001
+	char	neutral_byte;
+	int	dma;		/* DMA channel */
+
+	int     applic_profile;	/* Application profile (APF_*) */
+	/* Interrupt callback stuff */
+	void (*audio_callback) (int dev, int parm);
+	int callback_parm;
+
+	int	 buf_flags[MAX_SUB_BUFFERS];
+#define		 BUFF_EOF		0x00000001 /* Increment eof count */
+#define		 BUFF_DIRTY		0x00000002 /* Buffer written */
+};
+
+/*
+ * Structure for use with various microcontrollers and DSP processors 
+ * in the recent sound cards.
+ */
+typedef struct coproc_operations 
+{
+	char name[64];
+	struct module *owner;
+	int (*open) (void *devc, int sub_device);
+	void (*close) (void *devc, int sub_device);
+	int (*ioctl) (void *devc, unsigned int cmd, caddr_t arg, int local);
+	void (*reset) (void *devc);
+
+	void *devc;		/* Driver specific info */
+} coproc_operations;
+
+struct audio_driver 
+{
+	struct module *owner;
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	void (*output_block) (int dev, unsigned long buf, 
+			      int count, int intrflag);
+	void (*start_input) (int dev, unsigned long buf, 
+			     int count, int intrflag);
+	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
+	int (*prepare_for_input) (int dev, int bufsize, int nbufs);
+	int (*prepare_for_output) (int dev, int bufsize, int nbufs);
+	void (*halt_io) (int dev);
+	int (*local_qlen)(int dev);
+	void (*copy_user) (int dev,
+			char *localbuf, int localoffs,
+                        const char *userbuf, int useroffs,
+                        int max_in, int max_out,
+                        int *used, int *returned,
+                        int len);
+	void (*halt_input) (int dev);
+	void (*halt_output) (int dev);
+	void (*trigger) (int dev, int bits);
+	int (*set_speed)(int dev, int speed);
+	unsigned int (*set_bits)(int dev, unsigned int bits);
+	short (*set_channels)(int dev, short channels);
+	void (*postprocess_write)(int dev); 	/* Device spesific postprocessing for written data */
+	void (*preprocess_read)(int dev); 	/* Device spesific preprocessing for read data */
+	void (*mmap)(int dev);
+};
+
+struct audio_operations 
+{
+        char name[128];
+	int flags;
+#define NOTHING_SPECIAL 	0x00
+#define NEEDS_RESTART		0x01
+#define DMA_AUTOMODE		0x02
+#define DMA_DUPLEX		0x04
+#define DMA_PSEUDO_AUTOMODE	0x08
+#define DMA_HARDSTOP		0x10
+#define DMA_EXACT		0x40
+#define DMA_NORESET		0x80
+	int  format_mask;	/* Bitmask for supported audio formats */
+	void *devc;		/* Driver specific info */
+	struct audio_driver *d;
+	void *portc;		/* Driver spesific info */
+	struct dma_buffparms *dmap_in, *dmap_out;
+	struct coproc_operations *coproc;
+	int mixer_dev;
+	int enable_bits;
+ 	int open_mode;
+	int go;
+	int min_fragment;	/* 0 == unlimited */
+	int max_fragment;	/* 0 == unlimited */
+	int parent_dev;		/* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
+
+	/* fields formerly in dmabuf.c */
+	wait_queue_head_t in_sleeper;
+	wait_queue_head_t out_sleeper;
+	wait_queue_head_t poll_sleeper;
+
+	/* fields formerly in audio.c */
+	int audio_mode;
+
+#define		AM_NONE		0
+#define		AM_WRITE	OPEN_WRITE
+#define 	AM_READ		OPEN_READ
+
+	int local_format;
+	int audio_format;
+	int local_conversion;
+#define CNV_MU_LAW	0x00000001
+
+	/* large structures at the end to keep offsets small */
+	struct dma_buffparms dmaps[2];
+};
+
+int *load_mixer_volumes(char *name, int *levels, int present);
+
+struct mixer_operations 
+{
+	struct module *owner;
+	char id[16];
+	char name[64];
+	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
+	
+	void *devc;
+	int modify_counter;
+};
+
+struct synth_operations 
+{
+	struct module *owner;
+	char *id;	/* Unique identifier (ASCII) max 29 char */
+	struct synth_info *info;
+	int midi_dev;
+	int synth_type;
+	int synth_subtype;
+
+	int (*open) (int dev, int mode);
+	void (*close) (int dev);
+	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
+	int (*kill_note) (int dev, int voice, int note, int velocity);
+	int (*start_note) (int dev, int voice, int note, int velocity);
+	int (*set_instr) (int dev, int voice, int instr);
+	void (*reset) (int dev);
+	void (*hw_control) (int dev, unsigned char *event);
+	int (*load_patch) (int dev, int format, const char *addr,
+	     int offs, int count, int pmgr_flag);
+	void (*aftertouch) (int dev, int voice, int pressure);
+	void (*controller) (int dev, int voice, int ctrl_num, int value);
+	void (*panning) (int dev, int voice, int value);
+	void (*volume_method) (int dev, int mode);
+	void (*bender) (int dev, int chn, int value);
+	int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
+	void (*setup_voice) (int dev, int voice, int chn);
+	int (*send_sysex)(int dev, unsigned char *bytes, int len);
+
+ 	struct voice_alloc_info alloc;
+ 	struct channel_info chn_info[16];
+	int emulation;
+#define	EMU_GM			1	/* General MIDI */
+#define	EMU_XG			2	/* Yamaha XG */
+#define MAX_SYSEX_BUF	64
+	unsigned char sysex_buf[MAX_SYSEX_BUF];
+	int sysex_ptr;
+};
+
+struct midi_input_info 
+{
+	/* MIDI input scanner variables */
+#define MI_MAX	10
+	int             m_busy;
+    	unsigned char   m_buf[MI_MAX];
+	unsigned char	m_prev_status;	/* For running status */
+    	int             m_ptr;
+#define MST_INIT			0
+#define MST_DATA			1
+#define MST_SYSEX			2
+    	int             m_state;
+    	int             m_left;
+};
+
+struct midi_operations 
+{
+	struct module *owner;
+	struct midi_info info;
+	struct synth_operations *converter;
+	struct midi_input_info in_info;
+	int (*open) (int dev, int mode,
+		void (*inputintr)(int dev, unsigned char data),
+		void (*outputintr)(int dev)
+		);
+	void (*close) (int dev);
+	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
+	int (*outputc) (int dev, unsigned char data);
+	int (*start_read) (int dev);
+	int (*end_read) (int dev);
+	void (*kick)(int dev);
+	int (*command) (int dev, unsigned char *data);
+	int (*buffer_status) (int dev);
+	int (*prefix_cmd) (int dev, unsigned char status);
+	struct coproc_operations *coproc;
+	void *devc;
+};
+
+struct sound_lowlev_timer 
+{
+	int dev;
+	int priority;
+	unsigned int (*tmr_start)(int dev, unsigned int usecs);
+	void (*tmr_disable)(int dev);
+	void (*tmr_restart)(int dev);
+};
+
+struct sound_timer_operations 
+{
+	struct module *owner;
+	struct sound_timer_info info;
+	int priority;
+	int devlink;
+	int (*open)(int dev, int mode);
+	void (*close)(int dev);
+	int (*event)(int dev, unsigned char *ev);
+	unsigned long (*get_time)(int dev);
+	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
+	void (*arm_timer)(int dev, long time);
+};
+
+#ifdef _DEV_TABLE_C_   
+struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+int num_audiodevs;
+struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+int num_mixers;
+struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+int num_synths;
+struct midi_operations *midi_devs[MAX_MIDI_DEV];
+int num_midis;
+
+extern struct sound_timer_operations default_sound_timer;
+struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
+	&default_sound_timer, NULL
+}; 
+int num_sound_timers = 1;
+#else
+extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
+extern int num_audiodevs;
+extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
+extern int num_mixers;
+extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
+extern int num_synths;
+extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
+extern int num_midis;
+extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
+extern int num_sound_timers;
+#endif	/* _DEV_TABLE_C_ */
+
+extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
+void sound_timer_init (struct sound_lowlev_timer *t, char *name);
+void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
+
+#define AUDIO_DRIVER_VERSION	2
+#define MIXER_DRIVER_VERSION	2
+int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
+			int driver_size, int flags, unsigned int format_mask,
+			void *devc, int dma1, int dma2);
+int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
+			int driver_size, void *devc);
+
+void sound_unload_audiodev(int dev);
+void sound_unload_mixerdev(int dev);
+void sound_unload_mididev(int dev);
+void sound_unload_synthdev(int dev);
+void sound_unload_timerdev(int dev);
+int sound_alloc_audiodev(void);
+int sound_alloc_mixerdev(void);
+int sound_alloc_timerdev(void);
+int sound_alloc_synthdev(void);
+int sound_alloc_mididev(void);
+#endif	/* _DEV_TABLE_H_ */
+
diff -Nru linux/sound/oss/dm.h linux-2.4.19-pre5-mjc/sound/oss/dm.h
--- linux/sound/oss/dm.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dm.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,79 @@
+#ifndef _DRIVERS_SOUND_DM_H
+#define _DRIVERS_SOUND_DM_H
+
+/*
+ *	Definitions of the 'direct midi sound' interface used
+ *	by the newer commercial OSS package. We should export
+ *	this to userland somewhere in glibc later.
+ */
+
+/*
+ * Data structure composing an FM "note" or sound event.
+ */
+
+struct dm_fm_voice
+{
+	u8 op;
+	u8 voice;
+	u8 am;
+	u8 vibrato;
+	u8 do_sustain;
+	u8 kbd_scale;
+	u8 harmonic;
+	u8 scale_level;
+	u8 volume;
+	u8 attack;
+	u8 decay;
+	u8 sustain;
+	u8 release;
+	u8 feedback;
+	u8 connection;
+	u8 left;
+	u8 right;
+	u8 waveform;
+};
+
+/*
+ *	This describes an FM note by its voice, octave, frequency number (10bit)
+ *	and key on/off.
+ */
+
+struct dm_fm_note
+{
+	u8 voice;
+	u8 octave;
+	u32 fnum;
+	u8 key_on;
+};
+
+/*
+ * FM parameters that apply globally to all voices, and thus are not "notes"
+ */
+
+struct dm_fm_params
+{
+	u8 am_depth;
+	u8 vib_depth;
+	u8 kbd_split;
+	u8 rhythm;
+
+	/* This block is the percussion instrument data */
+	u8 bass;
+	u8 snare;
+	u8 tomtom;
+	u8 cymbal;
+	u8 hihat;
+};
+
+/*
+ *	FM mode ioctl settings
+ */
+ 
+#define FM_IOCTL_RESET        0x20
+#define FM_IOCTL_PLAY_NOTE    0x21
+#define FM_IOCTL_SET_VOICE    0x22
+#define FM_IOCTL_SET_PARAMS   0x23
+#define FM_IOCTL_SET_MODE     0x24
+#define FM_IOCTL_SET_OPL      0x25
+
+#endif
diff -Nru linux/sound/oss/dmabuf.c linux-2.4.19-pre5-mjc/sound/oss/dmabuf.c
--- linux/sound/oss/dmabuf.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmabuf.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1296 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Thomas Sailer   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
+ * Thomas Sailer   : remove {in,out}_sleep_flag. It was used for the sleeper to
+ *                   determine if it was woken up by the expiring timeout or by
+ *                   an explicit wake_up. The return value from schedule_timeout
+ *		     can be used instead; if 0, the wakeup was due to the timeout.
+ *
+ * Rob Riggs		Added persistent DMA buffers (1998/10/17)
+ */
+
+#define BE_CONSERVATIVE
+#define SAMPLE_ROUNDUP 0
+
+#include "sound_config.h"
+#include <linux/wrapper.h>
+
+#define DMAP_FREE_ON_CLOSE      0
+#define DMAP_KEEP_ON_CLOSE      1
+extern int sound_dmap_flag;
+
+static void dma_reset_output(int dev);
+static void dma_reset_input(int dev);
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);
+
+
+
+static int debugmem = 0;	/* switched off by default */
+static int dma_buffsize = DSP_BUFFSIZE;
+
+static long dmabuf_timeout(struct dma_buffparms *dmap)
+{
+	long tmout;
+
+	tmout = (dmap->fragment_size * HZ) / dmap->data_rate;
+	tmout += HZ / 5;	/* Some safety distance */
+	if (tmout < (HZ / 2))
+		tmout = HZ / 2;
+	if (tmout > 20 * HZ)
+		tmout = 20 * HZ;
+	return tmout;
+}
+
+static int sound_alloc_dmap(struct dma_buffparms *dmap)
+{
+	char *start_addr, *end_addr;
+	int dma_pagesize;
+	int sz, size;
+	struct page *page;
+
+	dmap->mapping_flags &= ~DMA_MAP_MAPPED;
+
+	if (dmap->raw_buf != NULL)
+		return 0;	/* Already done */
+	if (dma_buffsize < 4096)
+		dma_buffsize = 4096;
+	dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+	
+	/*
+	 *	Now check for the Cyrix problem.
+	 */
+	 
+	if(isa_dma_bridge_buggy==2)
+		dma_pagesize=32768;
+	 
+	dmap->raw_buf = NULL;
+	dmap->buffsize = dma_buffsize;
+	if (dmap->buffsize > dma_pagesize)
+		dmap->buffsize = dma_pagesize;
+	start_addr = NULL;
+	/*
+	 * Now loop until we get a free buffer. Try to get smaller buffer if
+	 * it fails. Don't accept smaller than 8k buffer for performance
+	 * reasons.
+	 */
+	while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) {
+		for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+		dmap->buffsize = PAGE_SIZE * (1 << sz);
+		start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
+		if (start_addr == NULL)
+			dmap->buffsize /= 2;
+	}
+
+	if (start_addr == NULL) {
+		printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
+		return -ENOMEM;
+	} else {
+		/* make some checks */
+		end_addr = start_addr + dmap->buffsize - 1;
+
+		if (debugmem)
+			printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
+		
+		/* now check if it fits into the same dma-pagesize */
+
+		if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
+		    || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
+			printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
+			return -EFAULT;
+		}
+	}
+	dmap->raw_buf = start_addr;
+	dmap->raw_buf_phys = virt_to_bus(start_addr);
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		mem_map_reserve(page);
+	return 0;
+}
+
+static void sound_free_dmap(struct dma_buffparms *dmap)
+{
+	int sz, size;
+	struct page *page;
+	unsigned long start_addr, end_addr;
+
+	if (dmap->raw_buf == NULL)
+		return;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+		return;		/* Don't free mmapped buffer. Will use it next time */
+	for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+
+	start_addr = (unsigned long) dmap->raw_buf;
+	end_addr = start_addr + dmap->buffsize;
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		mem_map_unreserve(page);
+
+	free_pages((unsigned long) dmap->raw_buf, sz);
+	dmap->raw_buf = NULL;
+}
+
+
+/* Intel version !!!!!!!!! */
+
+static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode)
+{
+	unsigned long flags;
+	int chan = dmap->dma;
+
+	/* printk( "Start DMA%d %d, %d\n",  chan,  (int)(physaddr-dmap->raw_buf_phys),  count); */
+
+	flags = claim_dma_lock();
+	disable_dma(chan);
+	clear_dma_ff(chan);
+	set_dma_mode(chan, dma_mode);
+	set_dma_addr(chan, physaddr);
+	set_dma_count(chan, count);
+	enable_dma(chan);
+	release_dma_lock(flags);
+
+	return 0;
+}
+
+static void dma_init_buffers(struct dma_buffparms *dmap)
+{
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	dmap->byte_counter = 0;
+	dmap->max_byte_counter = 8000 * 60 * 60;
+	dmap->bytes_in_use = dmap->buffsize;
+
+	dmap->dma_mode = DMODE_NONE;
+	dmap->mapping_flags = 0;
+	dmap->neutral_byte = 0x80;
+	dmap->data_rate = 8000;
+	dmap->cfrag = -1;
+	dmap->closing = 0;
+	dmap->nbufs = 1;
+	dmap->flags = DMA_BUSY;	/* Other flags off */
+}
+
+static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap)
+{
+	int err;
+	
+	if (dmap->flags & DMA_BUSY)
+		return -EBUSY;
+	if ((err = sound_alloc_dmap(dmap)) < 0)
+		return err;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_WARNING "Sound: DMA buffers not available\n");
+		return -ENOSPC;	/* Memory allocation failed during boot */
+	}
+	if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) {
+		printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma);
+		return -EBUSY;
+	}
+	dma_init_buffers(dmap);
+	dmap->open_mode = mode;
+	dmap->subdivision = dmap->underrun_count = 0;
+	dmap->fragment_size = 0;
+	dmap->max_fragments = 65536;	/* Just a large value */
+	dmap->byte_counter = 0;
+	dmap->max_byte_counter = 8000 * 60 * 60;
+	dmap->applic_profile = APF_NORMAL;
+	dmap->needs_reorg = 1;
+	dmap->audio_callback = NULL;
+	dmap->callback_parm = 0;
+	return 0;
+}
+
+static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap)
+{
+	unsigned long flags;
+	
+	if (dmap->dma >= 0) {
+		sound_close_dma(dmap->dma);
+		flags=claim_dma_lock();
+		disable_dma(dmap->dma);
+		release_dma_lock(flags);
+	}
+	if (dmap->flags & DMA_BUSY)
+		dmap->dma_mode = DMODE_NONE;
+	dmap->flags &= ~DMA_BUSY;
+	
+	if (sound_dmap_flag == DMAP_FREE_ON_CLOSE)
+		sound_free_dmap(dmap);
+}
+
+
+static unsigned int default_set_bits(int dev, unsigned int bits)
+{
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits);
+	set_fs(fs);
+	return bits;
+}
+
+static int default_set_speed(int dev, int speed)
+{
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed);
+	set_fs(fs);
+	return speed;
+}
+
+static short default_set_channels(int dev, short channels)
+{
+	int c = channels;
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c);
+	set_fs(fs);
+	return c;
+}
+
+static void check_driver(struct audio_driver *d)
+{
+	if (d->set_speed == NULL)
+		d->set_speed = default_set_speed;
+	if (d->set_bits == NULL)
+		d->set_bits = default_set_bits;
+	if (d->set_channels == NULL)
+		d->set_channels = default_set_channels;
+}
+
+int DMAbuf_open(int dev, int mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int retval;
+	struct dma_buffparms *dmap_in = NULL;
+	struct dma_buffparms *dmap_out = NULL;
+
+	if (!adev)
+		  return -ENXIO;
+	if (!(adev->flags & DMA_DUPLEX))
+		adev->dmap_in = adev->dmap_out;
+	check_driver(adev->d);
+
+	if ((retval = adev->d->open(dev, mode)) < 0)
+		return retval;
+	dmap_out = adev->dmap_out;
+	dmap_in = adev->dmap_in;
+	if (dmap_in == dmap_out)
+		adev->flags &= ~DMA_DUPLEX;
+
+	if (mode & OPEN_WRITE) {
+		if ((retval = open_dmap(adev, mode, dmap_out)) < 0) {
+			adev->d->close(dev);
+			return retval;
+		}
+	}
+	adev->enable_bits = mode;
+
+	if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) {
+		if ((retval = open_dmap(adev, mode, dmap_in)) < 0) {
+			adev->d->close(dev);
+			if (mode & OPEN_WRITE)
+				close_dmap(adev, dmap_out);
+			return retval;
+		}
+	}
+	adev->open_mode = mode;
+	adev->go = 1;
+
+	adev->d->set_bits(dev, 8);
+	adev->d->set_channels(dev, 1);
+	adev->d->set_speed(dev, DSP_DEFAULT_SPEED);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) 
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+		       adev->dmap_out->bytes_in_use);
+	return 0;
+}
+
+void DMAbuf_reset(int dev)
+{
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+		dma_reset_output(dev);
+
+	if (audio_devs[dev]->open_mode & OPEN_READ)
+		dma_reset_input(dev);
+}
+
+static void dma_reset_output(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags,f ;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
+		return;
+
+	/*
+	 *	First wait until the current fragment has been played completely
+	 */
+	save_flags(flags);
+	cli();
+	adev->dmap_out->flags |= DMA_SYNCING;
+
+	adev->dmap_out->underrun_count = 0;
+	if (!signal_pending(current) && adev->dmap_out->qlen && 
+	    adev->dmap_out->underrun_count == 0)
+		interruptible_sleep_on_timeout(&adev->out_sleeper,
+					       dmabuf_timeout(dmap));
+	adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+
+	/*
+	 *	Finally shut the device off
+	 */
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output)
+		adev->d->halt_io(dev);
+	else
+		adev->d->halt_output(dev);
+	adev->dmap_out->flags &= ~DMA_STARTED;
+	
+	f=claim_dma_lock();
+	clear_dma_ff(dmap->dma);
+	disable_dma(dmap->dma);
+	release_dma_lock(f);
+	
+	restore_flags(flags);
+	dmap->byte_counter = 0;
+	reorganize_buffers(dev, adev->dmap_out, 0);
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+}
+
+static void dma_reset_input(int dev)
+{
+        struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	save_flags(flags);
+	cli();
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input)
+		adev->d->halt_io(dev);
+	else
+		adev->d->halt_input(dev);
+	adev->dmap_in->flags &= ~DMA_STARTED;
+	restore_flags(flags);
+
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	dmap->byte_counter = 0;
+	reorganize_buffers(dev, adev->dmap_in, 1);
+}
+
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT))
+		return;		/* Don't start DMA yet */
+	dmap->dma_mode = DMODE_OUTPUT;
+
+	if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
+		if (!(dmap->flags & DMA_STARTED)) {
+			reorganize_buffers(dev, dmap, 0);
+			if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs))
+				return;
+			if (!(dmap->flags & DMA_NODMA))
+				local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE);
+			dmap->flags |= DMA_STARTED;
+		}
+		if (dmap->counts[dmap->qhead] == 0)
+			dmap->counts[dmap->qhead] = dmap->fragment_size;
+		dmap->dma_mode = DMODE_OUTPUT;
+		adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size,
+				      dmap->counts[dmap->qhead], 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+	dmap->flags |= DMA_ACTIVE;
+}
+
+int DMAbuf_sync(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int n = 0;
+	struct dma_buffparms *dmap;
+
+	if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT))
+		return 0;
+
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
+		dmap = adev->dmap_out;
+		save_flags(flags);
+		cli();
+		if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE))
+			DMAbuf_launch_output(dev, dmap);
+		adev->dmap_out->flags |= DMA_SYNCING;
+		adev->dmap_out->underrun_count = 0;
+		while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && 
+		       adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
+			long t = dmabuf_timeout(dmap);
+			t = interruptible_sleep_on_timeout(&adev->out_sleeper,
+							   t);
+			if (!t) {
+				adev->dmap_out->flags &= ~DMA_SYNCING;
+				restore_flags(flags);
+				return adev->dmap_out->qlen;
+			}
+		}
+		adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+		restore_flags(flags);
+		
+		/*
+		 * Some devices such as GUS have huge amount of on board RAM for the
+		 * audio data. We have to wait until the device has finished playing.
+		 */
+
+		save_flags(flags);
+		cli();
+		if (adev->d->local_qlen) {   /* Device has hidden buffers */
+			while (!signal_pending(current) &&
+			       adev->d->local_qlen(dev))
+				interruptible_sleep_on_timeout(&adev->out_sleeper,
+							       dmabuf_timeout(dmap));
+		}
+		restore_flags(flags);
+	}
+	adev->dmap_out->dma_mode = DMODE_NONE;
+	return adev->dmap_out->qlen;
+}
+
+int DMAbuf_release(int dev, int mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+
+	if (adev->open_mode & OPEN_WRITE)
+		adev->dmap_out->closing = 1;
+	if (adev->open_mode & OPEN_READ)
+		adev->dmap_in->closing = 1;
+
+	if (adev->open_mode & OPEN_WRITE)
+		if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED))
+			if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT))
+				DMAbuf_sync(dev);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use);
+	save_flags(flags);
+	cli();
+
+	DMAbuf_reset(dev);
+	adev->d->close(dev);
+
+	if (adev->open_mode & OPEN_WRITE)
+		close_dmap(adev, adev->dmap_out);
+
+	if (adev->open_mode == OPEN_READ ||
+	    (adev->open_mode != OPEN_WRITE &&
+	     (adev->flags & DMA_DUPLEX)))
+		close_dmap(adev, adev->dmap_in);
+	adev->open_mode = 0;
+	restore_flags(flags);
+	return 0;
+}
+
+int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int  err;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (!(adev->enable_bits & PCM_ENABLE_INPUT))
+		return 0;
+	if (dmap->dma_mode == DMODE_OUTPUT) {	/* Direction change */
+		DMAbuf_sync(dev);
+		DMAbuf_reset(dev);
+		dmap->dma_mode = DMODE_NONE;
+	}
+	if (!dmap->dma_mode) {
+		reorganize_buffers(dev, dmap, 1);
+		if ((err = adev->d->prepare_for_input(dev,
+				dmap->fragment_size, dmap->nbufs)) < 0)
+			return err;
+		dmap->dma_mode = DMODE_INPUT;
+	}
+	if (!(dmap->flags & DMA_ACTIVE)) {
+		if (dmap->needs_reorg)
+			reorganize_buffers(dev, dmap, 0);
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+				     dmap->fragment_size, 0);
+		dmap->flags |= DMA_ACTIVE;
+		if (adev->d->trigger)
+			adev->d->trigger(dev, adev->enable_bits * adev->go);
+	}
+	return 0;
+}
+
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int err = 0, n = 0;
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int go;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return -EIO;
+	if (dmap->needs_reorg)
+		reorganize_buffers(dev, dmap, 0);
+	save_flags(flags);
+	cli();
+	if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) {
+/*		  printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/
+		  restore_flags(flags);
+		  return -EINVAL;
+	} else while (dmap->qlen <= 0 && n++ < 10) {
+		long timeout = MAX_SCHEDULE_TIMEOUT;
+		if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) {
+			restore_flags(flags);
+			return -EAGAIN;
+		}
+		if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) {
+			restore_flags(flags);
+			return err;
+		}
+		/* Wait for the next block */
+
+		if (dontblock) {
+			restore_flags(flags);
+			return -EAGAIN;
+		}
+		if ((go = adev->go))
+			timeout = dmabuf_timeout(dmap);
+		timeout = interruptible_sleep_on_timeout(&adev->in_sleeper,
+							 timeout);
+		if (!timeout) {
+			/* FIXME: include device name */
+			err = -EIO;
+			printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
+			dma_reset_input(dev);
+		} else
+			err = -EINTR;
+	}
+	restore_flags(flags);
+
+	if (dmap->qlen <= 0)
+		return err ? err : -EINTR;
+	*buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
+	*len = dmap->fragment_size - dmap->counts[dmap->qhead];
+
+	return dmap->qhead;
+}
+
+int DMAbuf_rmchars(int dev, int buff_no, int c)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int p = dmap->counts[dmap->qhead] + c;
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+/*		  printk("Sound: Can't read from mmapped device (2)\n");*/
+		return -EINVAL;
+	}
+	else if (dmap->qlen <= 0)
+		return -EIO;
+	else if (p >= dmap->fragment_size) {  /* This buffer is completely empty */
+		dmap->counts[dmap->qhead] = 0;
+		dmap->qlen--;
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+	}
+	else dmap->counts[dmap->qhead] = p;
+
+	return 0;
+}
+
+int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction)
+{
+	/*
+	 *	Try to approximate the active byte position of the DMA pointer within the
+	 *	buffer area as well as possible.
+	 */
+
+	int pos;
+	unsigned long flags;
+	unsigned long f;
+
+	save_flags(flags);
+	cli();
+	if (!(dmap->flags & DMA_ACTIVE))
+		pos = 0;
+	else {
+		int chan = dmap->dma;
+		
+		f=claim_dma_lock();
+		clear_dma_ff(chan);
+		
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		
+		pos = get_dma_residue(chan);
+		
+		pos = dmap->bytes_in_use - pos;
+
+		if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) {
+			if (direction == DMODE_OUTPUT) {
+				if (dmap->qhead == 0)
+					if (pos > dmap->fragment_size)
+						pos = 0;
+			} else {
+				if (dmap->qtail == 0)
+					if (pos > dmap->fragment_size)
+						pos = 0;
+			}
+		}
+		if (pos < 0)
+			pos = 0;
+		if (pos >= dmap->bytes_in_use)
+			pos = 0;
+		
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+			
+		release_dma_lock(f);
+	}
+	restore_flags(flags);
+	/* printk( "%04x ",  pos); */
+
+	return pos;
+}
+
+/*
+ *	DMAbuf_start_devices() is called by the /dev/music driver to start
+ *	one or more audio devices at desired moment.
+ */
+
+void DMAbuf_start_devices(unsigned int devmask)
+{
+	struct audio_operations *adev;
+	int dev;
+
+	for (dev = 0; dev < num_audiodevs; dev++) {
+		if (!(devmask & (1 << dev)))
+			continue;
+		if (!(adev = audio_devs[dev]))
+			continue;
+		if (adev->open_mode == 0)
+			continue;
+		if (adev->go)
+			continue;
+		/* OK to start the device */
+		adev->go = 1;
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+}
+
+int DMAbuf_space_in_queue(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int len, max, tmp;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int lim = dmap->nbufs;
+
+	if (lim < 2)
+		lim = 2;
+
+	if (dmap->qlen >= lim)	/* No space at all */
+		return 0;
+
+	/*
+	 *	Verify that there are no more pending buffers than the limit
+	 *	defined by the process.
+	 */
+
+	max = dmap->max_fragments;
+	if (max > lim)
+		max = lim;
+	len = dmap->qlen;
+
+	if (adev->d->local_qlen) {
+		tmp = adev->d->local_qlen(dev);
+		if (tmp && len)
+			tmp--;	/* This buffer has been counted twice */
+		len += tmp;
+	}
+	if (dmap->byte_counter % dmap->fragment_size)	/* There is a partial fragment */
+		len = len + 1;
+
+	if (len >= max)
+		return 0;
+	return max - len;
+}
+
+static int output_sleep(int dev, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int err = 0;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	long timeout;
+	long timeout_value;
+
+	if (dontblock)
+		return -EAGAIN;
+	if (!(adev->enable_bits & PCM_ENABLE_OUTPUT))
+		return -EAGAIN;
+
+	/*
+	 * Wait for free space
+	 */
+	if (signal_pending(current))
+		return -EINTR;
+	timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT));
+	if (timeout) 
+		timeout_value = dmabuf_timeout(dmap);
+	else
+		timeout_value = MAX_SCHEDULE_TIMEOUT;
+	timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper,
+						       timeout_value);
+	if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) {
+		printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+		dma_reset_output(dev);
+	} else {
+		if (signal_pending(current))
+			err = -EINTR;
+	}
+	return err;
+}
+
+static int find_output_space(int dev, char **buf, int *size)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	unsigned long flags;
+	unsigned long active_offs;
+	long len, offs;
+	int maxfrags;
+	int occupied_bytes = (dmap->user_counter % dmap->fragment_size);
+
+	*buf = dmap->raw_buf;
+	if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes)
+		return 0;
+	save_flags(flags);
+	cli();
+
+#ifdef BE_CONSERVATIVE
+	active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
+#else
+	active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT);
+	/* Check for pointer wrapping situation */
+	if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
+		active_offs = 0;
+	active_offs += dmap->byte_counter;
+#endif
+
+	offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
+	if (offs < 0 || offs >= dmap->bytes_in_use) {
+		restore_flags(flags);
+		printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
+		printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
+		return 0;
+	}
+	*buf = dmap->raw_buf + offs;
+
+	len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
+
+	if ((offs + len) > dmap->bytes_in_use)
+		len = dmap->bytes_in_use - offs;
+	if (len < 0) {
+		restore_flags(flags);
+		return 0;
+	}
+	if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
+		len = (maxfrags * dmap->fragment_size) - occupied_bytes;
+	*size = len & ~SAMPLE_ROUNDUP;
+	restore_flags(flags);
+	return (*size > 0);
+}
+
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int err = -EIO;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	if (dmap->needs_reorg)
+		reorganize_buffers(dev, dmap, 0);
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+/*		printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/
+		return -EINVAL;
+	}
+	if (dmap->dma_mode == DMODE_INPUT) {	/* Direction change */
+		DMAbuf_reset(dev);
+		dmap->dma_mode = DMODE_NONE;
+	}
+	dmap->dma_mode = DMODE_OUTPUT;
+
+	save_flags(flags);
+	cli();
+	while (find_output_space(dev, buf, size) <= 0) {
+		if ((err = output_sleep(dev, dontblock)) < 0) {
+			restore_flags(flags);
+			return err;
+		}
+	}
+	restore_flags(flags);
+
+	return 0;
+}
+
+int DMAbuf_move_wrpointer(int dev, int l)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	unsigned long ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+	unsigned long end_ptr, p;
+	int post = (dmap->flags & DMA_POST);
+
+	dmap->flags &= ~DMA_POST;
+	dmap->cfrag = -1;
+	dmap->user_counter += l;
+	dmap->flags |= DMA_DIRTY;
+
+	if (dmap->byte_counter >= dmap->max_byte_counter) {
+		/* Wrap the byte counters */
+		long decr = dmap->byte_counter;
+		dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+		decr -= dmap->byte_counter;
+		dmap->user_counter -= decr;
+	}
+	end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+
+	p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+	dmap->neutral_byte = dmap->raw_buf[p];
+
+	/* Update the fragment based bookkeeping too */
+	while (ptr < end_ptr) {
+		dmap->counts[dmap->qtail] = dmap->fragment_size;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		dmap->qlen++;
+		ptr += dmap->fragment_size;
+	}
+
+	dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+	/*
+	 *	Let the low level driver to perform some postprocessing to
+	 *	the written data.
+	 */
+	if (adev->d->postprocess_write)
+		adev->d->postprocess_write(dev);
+
+	if (!(dmap->flags & DMA_ACTIVE))
+		if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
+			DMAbuf_launch_output(dev, dmap);
+	return 0;
+}
+
+int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "sound: DMA buffer(1) == NULL\n");
+		printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in");
+		return 0;
+	}
+	if (dmap->dma < 0)
+		return 0;
+	sound_start_dma(dmap, physaddr, count, dma_mode);
+	return count;
+}
+
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode)
+{
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "sound: DMA buffer(2) == NULL\n");
+		printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in");
+		return 0;
+	}
+	if (dmap->flags & DMA_NODMA)
+		return 1;
+	if (dmap->dma < 0)
+		return 0;
+	sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT);
+	dmap->flags |= DMA_STARTED;
+	return count;
+}
+
+static void finish_output_interrupt(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (dmap->audio_callback != NULL)
+		dmap->audio_callback(dev, dmap->callback_parm);
+	wake_up(&adev->out_sleeper);
+	wake_up(&adev->poll_sleeper);
+}
+
+static void do_outputintr(int dev, int dummy)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int this_fragment;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev);
+		return;
+	}
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {	/* Virtual memory mapped access */
+		/* mmapped access */
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+		if (dmap->qhead == 0) {	    /* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+		dmap->qlen++;	/* Yes increment it (don't decrement) */
+		if (!(adev->flags & DMA_AUTOMODE))
+			dmap->flags &= ~DMA_ACTIVE;
+		dmap->counts[dmap->qhead] = dmap->fragment_size;
+		DMAbuf_launch_output(dev, dmap);
+		finish_output_interrupt(dev, dmap);
+		return;
+	}
+	save_flags(flags);
+	cli();
+
+	dmap->qlen--;
+	this_fragment = dmap->qhead;
+	dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+
+	if (dmap->qhead == 0) {	/* Wrapped */
+		dmap->byte_counter += dmap->bytes_in_use;
+		if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+			long decr = dmap->byte_counter;
+			dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+			decr -= dmap->byte_counter;
+			dmap->user_counter -= decr;
+		}
+	}
+	if (!(adev->flags & DMA_AUTOMODE))
+		dmap->flags &= ~DMA_ACTIVE;
+		
+	/*
+	 *	This is  dmap->qlen <= 0 except when closing when
+	 *	dmap->qlen < 0
+	 */
+	 
+	while (dmap->qlen <= -dmap->closing) {
+		dmap->underrun_count++;
+		dmap->qlen++;
+		if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) {
+			dmap->flags &= ~DMA_DIRTY;
+			memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+			       adev->dmap_out->buffsize);
+		}
+		dmap->user_counter += dmap->fragment_size;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+	}
+	if (dmap->qlen > 0)
+		DMAbuf_launch_output(dev, dmap);
+	restore_flags(flags);
+	finish_output_interrupt(dev, dmap);
+}
+
+void DMAbuf_outputintr(int dev, int notify_only)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	save_flags(flags);
+	cli();
+	if (!(dmap->flags & DMA_NODMA)) {
+		int chan = dmap->dma, pos, n;
+		unsigned long f;
+		
+		f=claim_dma_lock();
+		
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		clear_dma_ff(chan);
+		pos = dmap->bytes_in_use - get_dma_residue(chan);
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+		release_dma_lock(f);
+		
+		pos = pos / dmap->fragment_size;	/* Actual qhead */
+		if (pos < 0 || pos >= dmap->nbufs)
+			pos = 0;
+		n = 0;
+		while (dmap->qhead != pos && n++ < dmap->nbufs)
+			do_outputintr(dev, notify_only);
+	}
+	else
+		do_outputintr(dev, notify_only);
+	restore_flags(flags);
+}
+
+static void do_inputintr(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+		return;
+	}
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		if (dmap->qtail == 0) {		/* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+		dmap->qlen++;
+
+		if (!(adev->flags & DMA_AUTOMODE)) {
+			if (dmap->needs_reorg)
+				reorganize_buffers(dev, dmap, 0);
+			local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ);
+			adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+					     dmap->fragment_size, 1);
+			if (adev->d->trigger)
+				adev->d->trigger(dev, adev->enable_bits * adev->go);
+		}
+		dmap->flags |= DMA_ACTIVE;
+	} else if (dmap->qlen >= (dmap->nbufs - 1)) {
+		printk(KERN_WARNING "Sound: Recording overrun\n");
+		dmap->underrun_count++;
+
+		/* Just throw away the oldest fragment but keep the engine running */
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+	} else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) {
+		dmap->qlen++;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		if (dmap->qtail == 0) {		/* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+	}
+	if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+	dmap->flags |= DMA_ACTIVE;
+	if (dmap->qlen > 0)
+	{
+		wake_up(&adev->in_sleeper);
+		wake_up(&adev->poll_sleeper);
+	}
+}
+
+void DMAbuf_inputintr(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if (!(dmap->flags & DMA_NODMA)) {
+		int chan = dmap->dma, pos, n;
+		unsigned long f;
+		
+		f=claim_dma_lock();
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		clear_dma_ff(chan);
+		pos = dmap->bytes_in_use - get_dma_residue(chan);
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+		release_dma_lock(f);
+
+		pos = pos / dmap->fragment_size;	/* Actual qhead */
+		if (pos < 0 || pos >= dmap->nbufs)
+			pos = 0;
+
+		n = 0;
+		while (dmap->qtail != pos && ++n < dmap->nbufs)
+			do_inputintr(dev);
+	} else
+		do_inputintr(dev);
+	restore_flags(flags);
+}
+
+int DMAbuf_open_dma(int dev)
+{
+	/*
+	 *    NOTE!  This routine opens only the primary DMA channel (output).
+	 */
+	struct audio_operations *adev = audio_devs[dev];
+	int err;
+
+	if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0)
+		return -EBUSY;
+	dma_init_buffers(adev->dmap_out);
+	adev->dmap_out->flags |= DMA_ALLOC_DONE;
+	adev->dmap_out->fragment_size = adev->dmap_out->buffsize;
+
+	if (adev->dmap_out->dma >= 0) {
+		unsigned long flags;
+
+		flags=claim_dma_lock();
+		clear_dma_ff(adev->dmap_out->dma);
+		disable_dma(adev->dmap_out->dma);
+		release_dma_lock(flags);
+	}
+	return 0;
+}
+
+void DMAbuf_close_dma(int dev)
+{
+	close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out);
+}
+
+void DMAbuf_init(int dev, int dma1, int dma2)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	/*
+	 * NOTE! This routine could be called several times.
+	 */
+
+	/* drag in audio_syms.o */
+	{
+		extern char audio_syms_symbol;
+		audio_syms_symbol = 0;
+	}
+
+	if (adev && adev->dmap_out == NULL) {
+		if (adev->d == NULL)
+			panic("OSS: audio_devs[%d]->d == NULL\n", dev);
+
+		if (adev->parent_dev) {	 /* Use DMA map of the parent dev */
+			int parent = adev->parent_dev - 1;
+			adev->dmap_out = audio_devs[parent]->dmap_out;
+			adev->dmap_in = audio_devs[parent]->dmap_in;
+		} else {
+			adev->dmap_out = adev->dmap_in = &adev->dmaps[0];
+			adev->dmap_out->dma = dma1;
+			if (adev->flags & DMA_DUPLEX) {
+				adev->dmap_in = &adev->dmaps[1];
+				adev->dmap_in->dma = dma2;
+			}
+		}
+		/* Persistent DMA buffers allocated here */
+		if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
+			if (adev->dmap_in->raw_buf == NULL)
+				sound_alloc_dmap(adev->dmap_in);
+			if (adev->dmap_out->raw_buf == NULL)
+				sound_alloc_dmap(adev->dmap_out);
+		}
+	}
+}
+
+/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */
+static unsigned int poll_input(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		if (dmap->qlen)
+			return POLLIN | POLLRDNORM;
+		return 0;
+	}
+	if (dmap->dma_mode != DMODE_INPUT) {
+		if (dmap->dma_mode == DMODE_NONE &&
+		    adev->enable_bits & PCM_ENABLE_INPUT &&
+		    !dmap->qlen && adev->go) {
+			unsigned long flags;
+			
+			save_flags(flags);
+			cli();
+			DMAbuf_activate_recording(dev, dmap);
+			restore_flags(flags);
+		}
+		return 0;
+	}
+	if (!dmap->qlen)
+		return 0;
+	return POLLIN | POLLRDNORM;
+}
+
+static unsigned int poll_output(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	
+	if (!(adev->open_mode & OPEN_WRITE))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		if (dmap->qlen)
+			return POLLOUT | POLLWRNORM;
+		return 0;
+	}
+	if (dmap->dma_mode == DMODE_INPUT)
+		return 0;
+	if (dmap->dma_mode == DMODE_NONE)
+		return POLLOUT | POLLWRNORM;
+	if (!DMAbuf_space_in_queue(dev))
+		return 0;
+	return POLLOUT | POLLWRNORM;
+}
+
+unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	poll_wait(file, &adev->poll_sleeper, wait);
+	return poll_input(file, dev, wait) | poll_output(file, dev, wait);
+}
+
+void DMAbuf_deinit(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	/* This routine is called when driver is being unloaded */
+	if (!adev)
+		return;
+
+	/* Persistent DMA buffers deallocated here */
+	if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
+		sound_free_dmap(adev->dmap_out);
+		if (adev->flags & DMA_DUPLEX)
+			sound_free_dmap(adev->dmap_in);
+	}
+}
diff -Nru linux/sound/oss/dmasound/Config.help linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.help
--- linux/sound/oss/dmasound/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.help	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,47 @@
+CONFIG_DMASOUND
+  Support built-in audio chips accessible by DMA on various machines
+  that have them.  Note that this symbol does not affect the kernel
+  directly; rather, it controls whether configuration questions
+  enabling DMA sound drivers for various specific machine
+  architectures will be used.
+
+CONFIG_DMASOUND_ATARI
+  If you want to use the internal audio of your Atari in Linux, answer
+  Y to this question. This will provide a Sun-like /dev/audio,
+  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+
+CONFIG_DMASOUND_AWACS
+  If you want to use the internal audio of your PowerMac in Linux,
+  answer Y to this question. This will provide a Sun-like /dev/audio,
+  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+
+CONFIG_DMASOUND_PAULA
+  If you want to use the internal audio of your Amiga in Linux, answer
+  Y to this question. This will provide a Sun-like /dev/audio,
+  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+
+CONFIG_DMASOUND_Q40
+  If you want to use the internal audio of your Q40 in Linux, answer
+  Y to this question. This will provide a Sun-like /dev/audio,
+  compatible with the Linux/i386 sound system. Otherwise, say N.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+
diff -Nru linux/sound/oss/dmasound/Config.in linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.in
--- linux/sound/oss/dmasound/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.in	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,27 @@
+# drivers/sound/dmasound/Config.in
+
+if [ "$CONFIG_ATARI" = "y" ]; then
+   dep_tristate '  Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND_ALSA
+fi
+if [ "$CONFIG_ALL_PPC" = "y" ]; then
+   dep_tristate '  PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND_ALSA
+fi
+if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then
+   dep_tristate '  Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND_ALSA
+fi
+if [ "$CONFIG_Q40" = "y" ]; then
+   dep_tristate '  Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND_ALSA
+fi
+if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \
+     "$CONFIG_DMASOUND_AWACS" = "y" -o \
+     "$CONFIG_DMASOUND_PAULA" = "y" -o \
+     "$CONFIG_DMASOUND_Q40" = "y" ]; then
+   define_tristate CONFIG_DMASOUND y
+else
+   if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \
+	"$CONFIG_DMASOUND_AWACS" = "m" -o \
+	"$CONFIG_DMASOUND_PAULA" = "m" -o \
+	"$CONFIG_DMASOUND_Q40" = "m" ]; then
+      define_tristate CONFIG_DMASOUND m
+   fi
+fi
diff -Nru linux/sound/oss/dmasound/Makefile linux-2.4.19-pre5-mjc/sound/oss/dmasound/Makefile
--- linux/sound/oss/dmasound/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,21 @@
+#
+# Makefile for the DMA sound driver
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+export-objs := dmasound_core.o
+
+obj-$(CONFIG_DMASOUND_ATARI)  += dmasound_core.o dmasound_atari.o
+obj-$(CONFIG_DMASOUND_AWACS)  += dmasound_core.o dmasound_awacs.o
+obj-$(CONFIG_DMASOUND_PAULA)  += dmasound_core.o dmasound_paula.o
+obj-$(CONFIG_DMASOUND_Q40)    += dmasound_core.o dmasound_q40.o
+
+ifeq ($(CONFIG_DMASOUND),y)
+  O_TARGET = dmasound.o
+endif
+
+include $(TOPDIR)/Rules.make
diff -Nru linux/sound/oss/dmasound/awacs_defs.h linux-2.4.19-pre5-mjc/sound/oss/dmasound/awacs_defs.h
--- linux/sound/oss/dmasound/awacs_defs.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/awacs_defs.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,229 @@
+/*********************************************************/
+/* This file was written by someone, somewhere, sometime */
+/* And is released into the Public Domain                */
+/*********************************************************/
+
+#ifndef _AWACS_DEFS_H_
+#define _AWACS_DEFS_H_
+
+/*******************************/
+/* AWACs Audio Register Layout */
+/*******************************/
+
+struct awacs_regs {
+    unsigned	control;	/* Audio control register */
+    unsigned	pad0[3];
+    unsigned	codec_ctrl;	/* Codec control register */
+    unsigned	pad1[3];
+    unsigned	codec_stat;	/* Codec status register */
+    unsigned	pad2[3];
+    unsigned	clip_count;	/* Clipping count register */
+    unsigned	pad3[3];
+    unsigned	byteswap;	/* Data is little-endian if 1 */
+};
+
+/*******************/
+/* Audio Bit Masks */
+/*******************/
+
+/* Audio Control Reg Bit Masks */
+/* ----- ------- --- --- ----- */
+#define MASK_ISFSEL	(0xf)		/* Input SubFrame Select */
+#define MASK_OSFSEL	(0xf << 4)	/* Output SubFrame Select */
+#define MASK_RATE	(0x7 << 8)	/* Sound Rate */
+#define MASK_CNTLERR	(0x1 << 11)	/* Error */
+#define MASK_PORTCHG	(0x1 << 12)	/* Port Change */
+#define MASK_IEE	(0x1 << 13)	/* Enable Interrupt on Error */
+#define MASK_IEPC	(0x1 << 14)	/* Enable Interrupt on Port Change */
+#define MASK_SSFSEL	(0x3 << 15)	/* Status SubFrame Select */
+
+/* Audio Codec Control Reg Bit Masks */
+/* ----- ----- ------- --- --- ----- */
+#define MASK_NEWECMD	(0x1 << 24)	/* Lock: don't write to reg when 1 */
+#define MASK_EMODESEL	(0x3 << 22)	/* Send info out on which frame? */
+#define MASK_EXMODEADDR	(0x3ff << 12)	/* Extended Mode Address -- 10 bits */
+#define MASK_EXMODEDATA	(0xfff)		/* Extended Mode Data -- 12 bits */
+
+/* Audio Codec Control Address Values / Masks */
+/* ----- ----- ------- ------- ------ - ----- */
+#define MASK_ADDR0	(0x0 << 12)	/* Expanded Data Mode Address 0 */
+#define MASK_ADDR_MUX	MASK_ADDR0	/* Mux Control */
+#define MASK_ADDR_GAIN	MASK_ADDR0
+
+#define MASK_ADDR1	(0x1 << 12)	/* Expanded Data Mode Address 1 */
+#define MASK_ADDR_MUTE	MASK_ADDR1
+#define MASK_ADDR_RATE	MASK_ADDR1
+
+#define MASK_ADDR2	(0x2 << 12)	/* Expanded Data Mode Address 2 */
+#define MASK_ADDR_VOLA	MASK_ADDR2	/* Volume Control A -- Headphones */
+#define MASK_ADDR_VOLHD MASK_ADDR2
+
+#define MASK_ADDR4	(0x4 << 12)	/* Expanded Data Mode Address 4 */
+#define MASK_ADDR_VOLC	MASK_ADDR4	/* Volume Control C -- Speaker */
+#define MASK_ADDR_VOLSPK MASK_ADDR4
+
+/* additional registers of screamer */
+#define MASK_ADDR5	(0x5 << 12)	/* Expanded Data Mode Address 5 */
+#define MASK_ADDR6	(0x6 << 12)	/* Expanded Data Mode Address 6 */
+#define MASK_ADDR7	(0x7 << 12)	/* Expanded Data Mode Address 7 */
+
+/* Address 0 Bit Masks & Macros */
+/* ------- - --- ----- - ------ */
+#define MASK_GAINRIGHT	(0xf)		/* Gain Right Mask */
+#define MASK_GAINLEFT	(0xf << 4)	/* Gain Left Mask */
+#define MASK_GAINLINE	(0x1 << 8)	/* Change Gain for Line??? */
+#define MASK_GAINMIC	(0x0 << 8)	/* Change Gain for Mic??? */
+
+#define MASK_MUX_CD	(0x1 << 9)	/* Select CD in MUX */
+#define MASK_MUX_AUDIN	(0x1 << 10)	/* Select Audio In in MUX */
+#define MASK_MUX_MIC	(0x1 << 11)	/* Select Mic in MUX */
+#define MASK_MUX_LINE	MASK_MUX_AUDIN
+
+#define GAINRIGHT(x)	((x) & MASK_GAINRIGHT)
+#define GAINLEFT(x)	(((x) << 4) & MASK_GAINLEFT)
+
+/* Address 1 Bit Masks */
+/* ------- - --- ----- */
+#define MASK_ADDR1RES1	(0x3)		/* Reserved */
+#define MASK_RECALIBRATE (0x1 << 2)	/* Recalibrate */
+#define MASK_SAMPLERATE	(0x7 << 3)	/* Sample Rate: */
+#define MASK_LOOPTHRU	(0x1 << 6)	/* Loopthrough Enable */
+#define MASK_CMUTE	(0x1 << 7)	/* Output C (Speaker) Mute when 1 */
+#define MASK_SPKMUTE	MASK_CMUTE
+#define MASK_ADDR1RES2	(0x1 << 8)	/* Reserved */
+#define MASK_AMUTE	(0x1 << 9)	/* Output A (Headphone) Mute when 1 */
+#define MASK_HDMUTE	MASK_AMUTE
+#define MASK_PAROUT	(0x3 << 10)	/* Parallel Out (???) */
+
+#define SAMPLERATE_48000	(0x0 << 3)	/* 48 or 44.1 kHz */
+#define SAMPLERATE_32000	(0x1 << 3)	/* 32 or 29.4 kHz */
+#define SAMPLERATE_24000	(0x2 << 3)	/* 24 or 22.05 kHz */
+#define SAMPLERATE_19200	(0x3 << 3)	/* 19.2 or 17.64 kHz */
+#define SAMPLERATE_16000	(0x4 << 3)	/* 16 or 14.7 kHz */
+#define SAMPLERATE_12000	(0x5 << 3)	/* 12 or 11.025 kHz */
+#define SAMPLERATE_9600		(0x6 << 3)	/* 9.6 or 8.82 kHz */
+#define SAMPLERATE_8000		(0x7 << 3)	/* 8 or 7.35 kHz */
+
+/* Address 2 & 4 Bit Masks & Macros */
+/* ------- - - - --- ----- - ------ */
+#define MASK_OUTVOLRIGHT (0xf)		/* Output Right Volume */
+#define MASK_ADDR2RES1	(0x2 << 4)	/* Reserved */
+#define MASK_ADDR4RES1	MASK_ADDR2RES1
+#define MASK_OUTVOLLEFT	(0xf << 6)	/* Output Left Volume */
+#define MASK_ADDR2RES2	(0x2 << 10)	/* Reserved */
+#define MASK_ADDR4RES2	MASK_ADDR2RES2
+
+#define VOLRIGHT(x)	(((~(x)) & MASK_OUTVOLRIGHT))
+#define VOLLEFT(x)	(((~(x)) << 6) & MASK_OUTVOLLEFT)
+
+/* Audio Codec Status Reg Bit Masks */
+/* ----- ----- ------ --- --- ----- */
+#define MASK_EXTEND	(0x1 << 23)	/* Extend */
+#define MASK_VALID	(0x1 << 22)	/* Valid Data? */
+#define MASK_OFLEFT	(0x1 << 21)	/* Overflow Left */
+#define MASK_OFRIGHT	(0x1 << 20)	/* Overflow Right */
+#define MASK_ERRCODE	(0xf << 16)	/* Error Code */
+#define MASK_REVISION	(0xf << 12)	/* Revision Number */
+#define MASK_MFGID	(0xf << 8)	/* Mfg. ID */
+#define MASK_CODSTATRES	(0xf << 4)	/* bits 4 - 7 reserved */
+#define MASK_INPPORT	(0xf)		/* Input Port */
+#define MASK_HDPCONN	8		/* headphone plugged in */
+
+/* Clipping Count Reg Bit Masks */
+/* -------- ----- --- --- ----- */
+#define MASK_CLIPLEFT	(0xff << 7)	/* Clipping Count, Left Channel */
+#define MASK_CLIPRIGHT	(0xff)		/* Clipping Count, Right Channel */
+
+/* DBDMA ChannelStatus Bit Masks */
+/* ----- ------------- --- ----- */
+#define MASK_CSERR	(0x1 << 7)	/* Error */
+#define MASK_EOI	(0x1 << 6)	/* End of Input -- only for Input Channel */
+#define MASK_CSUNUSED	(0x1f << 1)	/* bits 1-5 not used */
+#define MASK_WAIT	(0x1)		/* Wait */
+
+/* Various Rates */
+/* ------- ----- */
+#define RATE_48000	(0x0 << 8)	/* 48 kHz */
+#define RATE_44100	(0x0 << 8)	/* 44.1 kHz */
+#define RATE_32000	(0x1 << 8)	/* 32 kHz */
+#define RATE_29400	(0x1 << 8)	/* 29.4 kHz */
+#define RATE_24000	(0x2 << 8)	/* 24 kHz */
+#define RATE_22050	(0x2 << 8)	/* 22.05 kHz */
+#define RATE_19200	(0x3 << 8)	/* 19.2 kHz */
+#define RATE_17640	(0x3 << 8)	/* 17.64 kHz */
+#define RATE_16000	(0x4 << 8)	/* 16 kHz */
+#define RATE_14700	(0x4 << 8)	/* 14.7 kHz */
+#define RATE_12000	(0x5 << 8)	/* 12 kHz */
+#define RATE_11025	(0x5 << 8)	/* 11.025 kHz */
+#define RATE_9600	(0x6 << 8)	/* 9.6 kHz */
+#define RATE_8820	(0x6 << 8)	/* 8.82 kHz */
+#define RATE_8000	(0x7 << 8)	/* 8 kHz */
+#define RATE_7350	(0x7 << 8)	/* 7.35 kHz */
+
+#define RATE_LOW	1	/* HIGH = 48kHz, etc;  LOW = 44.1kHz, etc. */
+
+
+/* Burgundy values */
+
+#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
+#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
+
+#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
+
+#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
+#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
+
+#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
+
+#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
+
+#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
+#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
+#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
+#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
+#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
+#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
+#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+
+/* These are all default values for the burgundy */
+#define DEF_BURGUNDY_INPSEL21 (0xAA)
+#define DEF_BURGUNDY_INPSEL3 (0x0A)
+
+#define DEF_BURGUNDY_GAINCD (0x33)
+#define DEF_BURGUNDY_GAINLINE (0x44)
+#define DEF_BURGUNDY_GAINMIC (0x44)
+#define DEF_BURGUNDY_GAINMODEM (0x06)
+
+/* Remember: lowest volume here is 0x9b */
+#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
+#define DEF_BURGUNDY_VOLLINE (0x00000000)
+#define DEF_BURGUNDY_VOLMIC (0x00000000)
+#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
+
+#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
+#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
+
+#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF)
+
+#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
+
+#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
+#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
+#define DEF_BURGUNDY_ATTENHP (0xCC)
+
+#endif /* _AWACS_DEFS_H_ */
diff -Nru linux/sound/oss/dmasound/dmasound.h linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound.h
--- linux/sound/oss/dmasound/dmasound.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,248 @@
+
+/*
+ *  linux/drivers/sound/dmasound/dmasound.h
+ *
+ *
+ *  Minor numbers for the sound driver.
+ *
+ *  Unfortunately Creative called the codec chip of SB as a DSP. For this
+ *  reason the /dev/dsp is reserved for digitized audio use. There is a
+ *  device for true DSP processors but it will be called something else.
+ *  In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+
+#include <linux/config.h>
+
+
+#define SND_NDEVS	256	/* Number of supported devices */
+#define SND_DEV_CTL	0	/* Control port /dev/mixer */
+#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
+				   synthesizer and MIDI output) */
+#define SND_DEV_MIDIN	2	/* Raw midi access */
+#define SND_DEV_DSP	3	/* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO	4	/* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16	5	/* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS	6	/* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2	8	/* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS	SND_DEV_SNDPROC
+
+#define DSP_DEFAULT_SPEED	8000
+
+#define ON		1
+#define OFF		0
+
+#define MAX_AUDIO_DEV	5
+#define MAX_MIXER_DEV	2
+#define MAX_SYNTH_DEV	3
+#define MAX_MIDI_DEV	6
+#define MAX_TIMER_DEV	3
+
+
+#define MAX_CATCH_RADIUS	10
+#define MIN_BUFFERS		4
+#define MIN_BUFSIZE		4	/* in KB */
+#define MAX_BUFSIZE		128	/* Limit for Amiga in KB */
+
+
+#define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
+#define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
+
+#define IOCTL_IN(arg, ret) \
+	do { int error = get_user(ret, (int *)(arg)); \
+		if (error) return error; \
+	} while (0)
+#define IOCTL_OUT(arg, ret)	ioctl_return((int *)(arg), ret)
+
+static inline int ioctl_return(int *addr, int value)
+{
+	return value < 0 ? value : put_user(value, addr);
+}
+
+
+    /*
+     *  Configuration
+     */
+
+#undef HAS_8BIT_TABLES
+#undef HAS_14BIT_TABLES
+#undef HAS_16BIT_TABLES
+#undef HAS_RECORD
+
+#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
+    defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
+    defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
+#define HAS_8BIT_TABLES
+#endif
+#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE)
+#define HAS_16BIT_TABLES
+#define HAS_RECORD
+#endif
+
+
+    /*
+     *  Initialization
+     */
+
+extern int dmasound_init(void);
+#ifdef MODULE
+extern void dmasound_deinit(void);
+#else
+#define dmasound_deinit()	do { } while (0)
+#endif
+
+
+    /*
+     *  Machine definitions
+     */
+
+typedef struct {
+    const char *name;
+    const char *name2;
+    void (*open)(void);
+    void (*release)(void);
+    void *(*dma_alloc)(unsigned int, int);
+    void (*dma_free)(void *, unsigned int);
+    int (*irqinit)(void);
+#ifdef MODULE
+    void (*irqcleanup)(void);
+#endif
+    void (*init)(void);
+    void (*silence)(void);
+    int (*setFormat)(int);
+    int (*setVolume)(int);
+    int (*setBass)(int);
+    int (*setTreble)(int);
+    int (*setGain)(int);
+    void (*play)(void);
+    void (*record)(void);			/* optional */
+    void (*mixer_init)(void);			/* optional */
+    int (*mixer_ioctl)(u_int, u_long);		/* optional */
+    void (*write_sq_setup)(void);		/* optional */
+    void (*read_sq_setup)(void);		/* optional */
+    void (*sq_open)(void);			/* optional */
+    int (*state_info)(char *);			/* optional */
+    void (*abort_read)(void);			/* optional */
+    int min_dsp_speed;
+} MACHINE;
+
+
+    /*
+     *  Low level stuff
+     */
+
+typedef struct {
+    int format;		/* AFMT_* */
+    int stereo;		/* 0 = mono, 1 = stereo */
+    int size;		/* 8/16 bit*/
+    int speed;		/* speed */
+} SETTINGS;
+
+typedef struct {
+    ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+    ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+} TRANS;
+
+struct sound_settings {
+    MACHINE mach;	/* machine dependent things */
+    SETTINGS hard;	/* hardware settings */
+    SETTINGS soft;	/* software settings */
+    SETTINGS dsp;	/* /dev/dsp default settings */
+    TRANS *trans_write;	/* supported translations */
+#ifdef HAS_RECORD
+    TRANS *trans_read;	/* supported translations */
+#endif
+    int volume_left;	/* volume (range is machine dependent) */
+    int volume_right;
+    int bass;		/* tone (range is machine dependent) */
+    int treble;
+    int gain;
+    int minDev;		/* minor device number currently open */
+};
+
+extern struct sound_settings dmasound;
+
+extern char dmasound_ulaw2dma8[];
+extern char dmasound_alaw2dma8[];
+extern short dmasound_ulaw2dma16[];
+extern short dmasound_alaw2dma16[];
+
+
+    /*
+     *  Mid level stuff
+     */
+
+static inline int dmasound_set_volume(int volume)
+{
+	return dmasound.mach.setVolume(volume);
+}
+
+static inline int dmasound_set_bass(int bass)
+{
+	return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50;
+}
+
+static inline int dmasound_set_treble(int treble)
+{
+	return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50;
+}
+
+static inline int dmasound_set_gain(int gain)
+{
+	return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100;
+}
+
+
+    /*
+     * Sound queue stuff, the heart of the driver
+     */
+
+struct sound_queue {
+    /* buffers allocated for this queue */
+    int numBufs;
+    int bufSize;			/* in bytes */
+    char **buffers;
+
+    /* current parameters */
+    int max_count;
+    int block_size;			/* in bytes */
+    int max_active;
+
+    /* it shouldn't be necessary to declare any of these volatile */
+    int front, rear, count;
+    int rear_size;
+    /*
+     *	The use of the playing field depends on the hardware
+     *
+     *	Atari, PMac: The number of frames that are loaded/playing
+     *
+     *	Amiga: Bit 0 is set: a frame is loaded
+     *	       Bit 1 is set: a frame is playing
+     */
+    int active;
+    wait_queue_head_t action_queue, open_queue, sync_queue;
+    int open_mode;
+    int busy, syncing;
+};
+
+#define SLEEP(queue)		interruptible_sleep_on_timeout(&queue, HZ)
+#define WAKE_UP(queue)		(wake_up_interruptible(&queue))
+
+extern struct sound_queue dmasound_write_sq;
+extern struct sound_queue dmasound_read_sq;
+
+#define write_sq	dmasound_write_sq
+#define read_sq		dmasound_read_sq
+
+extern int dmasound_catchRadius;
+
+#define catchRadius	dmasound_catchRadius
+
diff -Nru linux/sound/oss/dmasound/dmasound_atari.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_atari.c
--- linux/sound/oss/dmasound/dmasound_atari.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_atari.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1562 @@
+
+/*
+ *  linux/drivers/sound/dmasound/dmasound_atari.c
+ *
+ *  Atari TT and Falcon DMA Sound Driver
+ *
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/mm.h>
+
+#include <asm/pgalloc.h>
+#include <asm/uaccess.h>
+#include <asm/atariints.h>
+#include <asm/atari_stram.h>
+
+#include "dmasound.h"
+
+
+extern void atari_microwire_cmd(int cmd);
+
+
+static int is_falcon;
+static int write_sq_ignore_int = 0;	/* ++TeSche: used for Falcon */
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Translations ************************************************************/
+
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft);
+static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft);
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void AtaOpen(void);
+static void AtaRelease(void);
+static void *AtaAlloc(unsigned int size, int flags);
+static void AtaFree(void *, unsigned int size);
+static int AtaIrqInit(void);
+#ifdef MODULE
+static void AtaIrqCleanUp(void);
+#endif /* MODULE */
+static int AtaSetBass(int bass);
+static int AtaSetTreble(int treble);
+static void TTSilence(void);
+static void TTInit(void);
+static int TTSetFormat(int format);
+static int TTSetVolume(int volume);
+static int TTSetGain(int gain);
+static void FalconSilence(void);
+static void FalconInit(void);
+static int FalconSetFormat(int format);
+static int FalconSetVolume(int volume);
+static void AtaPlayNextFrame(int index);
+static void AtaPlay(void);
+static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp);
+
+/*** Mid level stuff *********************************************************/
+
+static void TTMixerInit(void);
+static void FalconMixerInit(void);
+static int AtaMixerIoctl(u_int cmd, u_long arg);
+static int TTMixerIoctl(u_int cmd, u_long arg);
+static int FalconMixerIoctl(u_int cmd, u_long arg);
+static void AtaWriteSqSetup(void);
+static void AtaSqOpen(void);
+static int TTStateInfo(char *buffer);
+static int FalconStateInfo(char *buffer);
+
+
+/*** Translations ************************************************************/
+
+
+static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8
+							  : dmasound_alaw2dma8;
+	ssize_t count, used;
+	u_char *p = &frame[*frameUsed];
+
+	count = min_t(unsigned long, userCount, frameLeft);
+	if (dmasound.soft.stereo)
+		count &= ~1;
+	used = count;
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		*p++ = table[data];
+		count--;
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+	void *p = &frame[*frameUsed];
+
+	count = min_t(unsigned long, userCount, frameLeft);
+	if (dmasound.soft.stereo)
+		count &= ~1;
+	used = count;
+	if (copy_from_user(p, userPtr, count))
+		return -EFAULT;
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed,
+			 ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft);
+		used = count;
+		while (count > 0) {
+			u_char data;
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			*p++ = data ^ 0x80;
+			count--;
+		}
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			*p++ = data ^ 0x8080;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		void *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft) & ~3;
+		used = count;
+		if (copy_from_user(p, userPtr, count))
+			return -EFAULT;
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			data ^= 0x8000;
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, ((u_int *)userPtr)++))
+				return -EFAULT;
+			*p++ = data ^ 0x80008000;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	count = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			data = le2be16(data);
+			*p++ = data;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count*4;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, ((u_int *)userPtr)++))
+				return -EFAULT;
+			data = le2be16dbl(data);
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	count = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>1;
+		used = count*2;
+		while (count > 0) {
+			u_short data;
+			if (get_user(data, ((u_short *)userPtr)++))
+				return -EFAULT;
+			data = le2be16(data) ^ 0x8000;
+			*p++ = data;
+			*p++ = data;
+		}
+		*frameUsed += used*2;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft)>>2;
+		used = count;
+		while (count > 0) {
+			u_long data;
+			if (get_user(data, ((u_int *)userPtr)++))
+				return -EFAULT;
+			data = le2be16dbl(data) ^ 0x80008000;
+			*p++ = data;
+			count--;
+		}
+		*frameUsed += used;
+	}
+	return used;
+}
+
+
+static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8
+							  : dmasound_alaw2dma8;
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			u_char c;
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = table[c];
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			u_char c;
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = table[c] << 8;
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data |= table[c];
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(data, userPtr++))
+					return -EFAULT;
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_char *p = &frame[*frameUsed];
+		u_char data = expand_data;
+		while (frameLeft) {
+			if (bal < 0) {
+				if (!userCount)
+					break;
+				if (get_user(data, userPtr++))
+					return -EFAULT;
+				data ^= 0x80;
+				userCount--;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft--;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 2) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data ^= 0x8080;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 2;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data ^= 0x8000;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				data ^= 0x80008000;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data = le2be16(data);
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				data = le2be16dbl(data);
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount,
+			     u_char frame[], ssize_t *frameUsed,
+			     ssize_t frameLeft)
+{
+	/* this should help gcc to stuff everything into registers */
+	long bal = expand_bal;
+	long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	ssize_t used, usedf;
+
+	used = userCount;
+	usedf = frameLeft;
+	if (!dmasound.soft.stereo) {
+		u_short *p = (u_short *)&frame[*frameUsed];
+		u_short data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 2)
+					break;
+				if (get_user(data, ((u_short *)userPtr)++))
+					return -EFAULT;
+				data = le2be16(data) ^ 0x8000;
+				userCount -= 2;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	} else {
+		u_long *p = (u_long *)&frame[*frameUsed];
+		u_long data = expand_data;
+		while (frameLeft >= 4) {
+			if (bal < 0) {
+				if (userCount < 4)
+					break;
+				if (get_user(data, ((u_int *)userPtr)++))
+					return -EFAULT;
+				data = le2be16dbl(data) ^ 0x80008000;
+				userCount -= 4;
+				bal += hSpeed;
+			}
+			*p++ = data;
+			frameLeft -= 4;
+			bal -= sSpeed;
+		}
+		expand_data = data;
+	}
+	expand_bal = bal;
+	used -= userCount;
+	*frameUsed += usedf-frameLeft;
+	return used;
+}
+
+
+static TRANS transTTNormal = {
+	ct_ulaw:	ata_ct_law,
+	ct_alaw:	ata_ct_law,
+	ct_s8:		ata_ct_s8,
+	ct_u8:		ata_ct_u8,
+};
+
+static TRANS transTTExpanding = {
+	ct_ulaw:	ata_ctx_law,
+	ct_alaw:	ata_ctx_law,
+	ct_s8:		ata_ctx_s8,
+	ct_u8:		ata_ctx_u8,
+};
+
+static TRANS transFalconNormal = {
+	ct_ulaw:	ata_ct_law,
+	ct_alaw:	ata_ct_law,
+	ct_s8:		ata_ct_s8,
+	ct_u8:		ata_ct_u8,
+	ct_s16be:	ata_ct_s16be,
+	ct_u16be:	ata_ct_u16be,
+	ct_s16le:	ata_ct_s16le,
+	ct_u16le:	ata_ct_u16le
+};
+
+static TRANS transFalconExpanding = {
+	ct_ulaw:	ata_ctx_law,
+	ct_alaw:	ata_ctx_law,
+	ct_s8:		ata_ctx_s8,
+	ct_u8:		ata_ctx_u8,
+	ct_s16be:	ata_ctx_s16be,
+	ct_u16be:	ata_ctx_u16be,
+	ct_s16le:	ata_ctx_s16le,
+	ct_u16le:	ata_ctx_u16le,
+};
+
+
+/*** Low level stuff *********************************************************/
+
+
+
+/*
+ * Atari (TT/Falcon)
+ */
+
+static void AtaOpen(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void AtaRelease(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static void *AtaAlloc(unsigned int size, int flags)
+{
+	return atari_stram_alloc(size, "dmasound");
+}
+
+static void AtaFree(void *obj, unsigned int size)
+{
+	atari_stram_free( obj );
+}
+
+static int __init AtaIrqInit(void)
+{
+	/* Set up timer A. Timer A
+	   will receive a signal upon end of playing from the sound
+	   hardware. Furthermore Timer A is able to count events
+	   and will cause an interrupt after a programmed number
+	   of events. So all we need to keep the music playing is
+	   to provide the sound hardware with new data upon
+	   an interrupt from timer A. */
+	mfp.tim_ct_a = 0;	/* ++roman: Stop timer before programming! */
+	mfp.tim_dt_a = 1;	/* Cause interrupt after first event. */
+	mfp.tim_ct_a = 8;	/* Turn on event counting. */
+	/* Register interrupt handler. */
+	request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound",
+		    AtaInterrupt);
+	mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
+	mfp.int_mk_a |= 0x20;
+	return 1;
+}
+
+#ifdef MODULE
+static void AtaIrqCleanUp(void)
+{
+	mfp.tim_ct_a = 0;	/* stop timer */
+	mfp.int_en_a &= ~0x20;	/* turn interrupt off */
+	free_irq(IRQ_MFP_TIMA, AtaInterrupt);
+}
+#endif /* MODULE */
+
+
+#define TONE_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25)
+#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50)
+
+
+static int AtaSetBass(int bass)
+{
+	dmasound.bass = TONE_VOXWARE_TO_DB(bass);
+	atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass));
+	return TONE_DB_TO_VOXWARE(dmasound.bass);
+}
+
+
+static int AtaSetTreble(int treble)
+{
+	dmasound.treble = TONE_VOXWARE_TO_DB(treble);
+	atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble));
+	return TONE_DB_TO_VOXWARE(dmasound.treble);
+}
+
+
+
+/*
+ * TT
+ */
+
+
+static void TTSilence(void)
+{
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */
+}
+
+
+static void TTInit(void)
+{
+	int mode, i, idx;
+	const int freq[4] = {50066, 25033, 12517, 6258};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < ARRAY_SIZE(freq); i++)
+		/* this isn't as much useful for a TT than for a Falcon, but
+		 * then it doesn't hurt very much to implement it for a TT too.
+		 */
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+			idx = i;
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transTTNormal;
+	} else
+		dmasound.trans_write = &transTTExpanding;
+
+	TTSilence();
+	dmasound.hard = dmasound.soft;
+
+	if (dmasound.hard.speed > 50066) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 50066;
+		mode = DMASND_MODE_50KHZ;
+		dmasound.trans_write = &transTTNormal;
+	} else if (dmasound.hard.speed > 25033) {
+		dmasound.hard.speed = 50066;
+		mode = DMASND_MODE_50KHZ;
+	} else if (dmasound.hard.speed > 12517) {
+		dmasound.hard.speed = 25033;
+		mode = DMASND_MODE_25KHZ;
+	} else if (dmasound.hard.speed > 6258) {
+		dmasound.hard.speed = 12517;
+		mode = DMASND_MODE_12KHZ;
+	} else {
+		dmasound.hard.speed = 6258;
+		mode = DMASND_MODE_6KHZ;
+	}
+
+	tt_dmasnd.mode = (dmasound.hard.stereo ?
+			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		DMASND_MODE_8BIT | mode;
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int TTSetFormat(int format)
+{
+	/* TT sound DMA supports only 8bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_S8:
+	case AFMT_U8:
+		break;
+	default:
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = 8;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = 8;
+	}
+	TTInit();
+
+	return format;
+}
+
+
+#define VOLUME_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40)
+#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2)
+
+
+static int TTSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
+	atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left));
+	dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
+	atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right));
+	return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) |
+	       (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8);
+}
+
+
+#define GAIN_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80)
+#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4)
+
+static int TTSetGain(int gain)
+{
+	dmasound.gain = GAIN_VOXWARE_TO_DB(gain);
+	atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain));
+	return GAIN_DB_TO_VOXWARE(dmasound.gain);
+}
+
+
+
+/*
+ * Falcon
+ */
+
+
+static void FalconSilence(void)
+{
+	/* stop playback, set sample rate 50kHz for PSG sound */
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
+	tt_dmasnd.int_div = 0; /* STE compatible divider */
+	tt_dmasnd.int_ctrl = 0x0;
+	tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */
+	tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */
+	tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */
+	tt_dmasnd.adc_src = 3; /* ADC Input = PSG */
+}
+
+
+static void FalconInit(void)
+{
+	int divider, i, idx;
+	const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < ARRAY_SIZE(freq); i++)
+		/* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
+		 * be playable without expanding, but that now a kernel runtime
+		 * option
+		 */
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+			idx = i;
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transFalconNormal;
+	} else
+		dmasound.trans_write = &transFalconExpanding;
+
+	FalconSilence();
+	dmasound.hard = dmasound.soft;
+
+	if (dmasound.hard.size == 16) {
+		/* the Falcon can play 16bit samples only in stereo */
+		dmasound.hard.stereo = 1;
+	}
+
+	if (dmasound.hard.speed > 49170) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 49170;
+		divider = 1;
+		dmasound.trans_write = &transFalconNormal;
+	} else if (dmasound.hard.speed > 32780) {
+		dmasound.hard.speed = 49170;
+		divider = 1;
+	} else if (dmasound.hard.speed > 24585) {
+		dmasound.hard.speed = 32780;
+		divider = 2;
+	} else if (dmasound.hard.speed > 19668) {
+		dmasound.hard.speed = 24585;
+		divider = 3;
+	} else if (dmasound.hard.speed > 16390) {
+		dmasound.hard.speed = 19668;
+		divider = 4;
+	} else if (dmasound.hard.speed > 12292) {
+		dmasound.hard.speed = 16390;
+		divider = 5;
+	} else if (dmasound.hard.speed > 9834) {
+		dmasound.hard.speed = 12292;
+		divider = 7;
+	} else if (dmasound.hard.speed > 8195) {
+		dmasound.hard.speed = 9834;
+		divider = 9;
+	} else {
+		dmasound.hard.speed = 8195;
+		divider = 11;
+	}
+	tt_dmasnd.int_div = divider;
+
+	/* Setup Falcon sound DMA for playback */
+	tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */
+	tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */
+	tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */
+	tt_dmasnd.cbar_dst = 0x0000;
+	tt_dmasnd.rec_track_select = 0;
+	tt_dmasnd.dac_src = 2; /* connect matrix to DAC */
+	tt_dmasnd.adc_src = 0; /* ADC Input = Mic */
+
+	tt_dmasnd.mode = (dmasound.hard.stereo ?
+			  DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		((dmasound.hard.size == 8) ?
+		 DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
+		DMASND_MODE_6KHZ;
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int FalconSetFormat(int format)
+{
+	int size;
+	/* Falcon sound DMA supports 8bit and 16bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+		size = 8;
+		break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		size = 16;
+		break;
+	default: /* :-) */
+		size = 8;
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = size;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = dmasound.soft.size;
+	}
+
+	FalconInit();
+
+	return format;
+}
+
+
+/* This is for the Falcon output *attenuation* in 1.5dB steps,
+ * i.e. output level from 0 to -22.5dB in -1.5dB steps.
+ */
+#define VOLUME_VOXWARE_TO_ATT(v) \
+	((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20)
+#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3)
+
+
+static int FalconSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
+	dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
+	tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4;
+	return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
+	       VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8;
+}
+
+
+static void AtaPlayNextFrame(int index)
+{
+	char *start, *end;
+
+	/* used by AtaPlay() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	end = start+((write_sq.count == index) ? write_sq.rear_size
+					       : write_sq.block_size);
+	/* end might not be a legal virtual address. */
+	DMASNDSetEnd(virt_to_phys(end - 1) + 1);
+	DMASNDSetBase(virt_to_phys(start));
+	/* Since only an even number of samples per frame can
+	   be played, we might lose one byte here. (TO DO) */
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active++;
+	tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
+}
+
+
+static void AtaPlay(void)
+{
+	/* ++TeSche: Note that write_sq.active is no longer just a flag but
+	 * holds the number of frames the DMA is currently programmed for
+	 * instead, may be 0, 1 (currently being played) or 2 (pre-programmed).
+	 *
+	 * Changes done to write_sq.count and write_sq.active are a bit more
+	 * subtle again so now I must admit I also prefer disabling the irq
+	 * here rather than considering all possible situations. But the point
+	 * is that disabling the irq doesn't have any bad influence on this
+	 * version of the driver as we benefit from having pre-programmed the
+	 * DMA wherever possible: There's no need to reload the DMA at the
+	 * exact time of an interrupt but only at some time while the
+	 * pre-programmed frame is playing!
+	 */
+	atari_disable_irq(IRQ_MFP_TIMA);
+
+	if (write_sq.active == 2 ||	/* DMA is 'full' */
+	    write_sq.count <= 0) {	/* nothing to do */
+		atari_enable_irq(IRQ_MFP_TIMA);
+		return;
+	}
+
+	if (write_sq.active == 0) {
+		/* looks like there's nothing 'in' the DMA yet, so try
+		 * to put two frames into it (at least one is available).
+		 */
+		if (write_sq.count == 1 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, the only existing frame is not
+			 * yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(1);
+		if (write_sq.count == 1) {
+			/* no more frames */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		if (write_sq.count == 2 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, there were two frames, but the second
+			 * one is not yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(2);
+	} else {
+		/* there's already a frame being played so we may only stuff
+		 * one new into the DMA, but even if this may be the last
+		 * frame existing the previous one is still on write_sq.count.
+		 */
+		if (write_sq.count == 2 &&
+		    write_sq.rear_size < write_sq.block_size &&
+		    !write_sq.syncing) {
+			/* hmmm, the only existing frame is not
+			 * yet filled and we're not syncing?
+			 */
+			atari_enable_irq(IRQ_MFP_TIMA);
+			return;
+		}
+		AtaPlayNextFrame(2);
+	}
+	atari_enable_irq(IRQ_MFP_TIMA);
+}
+
+
+static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+#if 0
+	/* ++TeSche: if you should want to test this... */
+	static int cnt = 0;
+	if (write_sq.active == 2)
+		if (++cnt == 10) {
+			/* simulate losing an interrupt */
+			cnt = 0;
+			return;
+		}
+#endif
+
+	if (write_sq_ignore_int && is_falcon) {
+		/* ++TeSche: Falcon only: ignore first irq because it comes
+		 * immediately after starting a frame. after that, irqs come
+		 * (almost) like on the TT.
+		 */
+		write_sq_ignore_int = 0;
+		return;
+	}
+
+	if (!write_sq.active) {
+		/* playing was interrupted and sq_reset() has already cleared
+		 * the sq variables, so better don't do anything here.
+		 */
+		WAKE_UP(write_sq.sync_queue);
+		return;
+	}
+
+	/* Probably ;) one frame is finished. Well, in fact it may be that a
+	 * pre-programmed one is also finished because there has been a long
+	 * delay in interrupt delivery and we've completely lost one, but
+	 * there's no way to detect such a situation. In such a case the last
+	 * frame will be played more than once and the situation will recover
+	 * as soon as the irq gets through.
+	 */
+	write_sq.count--;
+	write_sq.active--;
+
+	if (!write_sq.active) {
+		tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+		write_sq_ignore_int = 1;
+	}
+
+	WAKE_UP(write_sq.action_queue);
+	/* At least one block of the queue is free now
+	   so wake up a writing process blocked because
+	   of a full queue. */
+
+	if ((write_sq.active != 1) || (write_sq.count != 1))
+		/* We must be a bit carefully here: write_sq.count indicates the
+		 * number of buffers used and not the number of frames to be
+		 * played. If write_sq.count==1 and write_sq.active==1 that
+		 * means the only remaining frame was already programmed
+		 * earlier (and is currently running) so we mustn't call
+		 * AtaPlay() here, otherwise we'll play one frame too much.
+		 */
+		AtaPlay();
+
+	if (!write_sq.active) WAKE_UP(write_sq.sync_queue);
+	/* We are not playing after AtaPlay(), so there
+	   is nothing to play any more. Wake up a process
+	   waiting for audio output to drain. */
+}
+
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+#define RECLEVEL_VOXWARE_TO_GAIN(v)	\
+	((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20)
+#define RECLEVEL_GAIN_TO_VOXWARE(v)	(((v) * 20 + 2) / 3)
+
+
+static void __init TTMixerInit(void)
+{
+	atari_microwire_cmd(MW_LM1992_VOLUME(0));
+	dmasound.volume_left = 0;
+	atari_microwire_cmd(MW_LM1992_BALLEFT(0));
+	dmasound.volume_right = 0;
+	atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
+	atari_microwire_cmd(MW_LM1992_TREBLE(0));
+	atari_microwire_cmd(MW_LM1992_BASS(0));
+}
+
+static void __init FalconMixerInit(void)
+{
+	dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
+	dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
+}
+
+static int AtaMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    cli();
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = sound_ym.rd_data_reg_sel;
+			    sti();
+			    return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100);
+		    }
+		    break;
+	    case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_volume(data));
+	    case SOUND_MIXER_WRITE_SPEAKER:
+		    if (is_falcon || MACH_IS_TT) {
+			    int porta;
+			    IOCTL_IN(arg, data);
+			    cli();
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+				    (data < 50 ? 0x40 : 0);
+			    sound_ym.wd_data = porta;
+			    sti();
+			    return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100);
+		    }
+	}
+	return -EINVAL;
+}
+
+
+static int TTMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_RECMASK:
+		return IOCTL_OUT(arg, 0);
+	    case SOUND_MIXER_READ_DEVMASK:
+		return IOCTL_OUT(arg,
+				 SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
+				 (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0));
+	    case SOUND_MIXER_READ_STEREODEVS:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
+	    case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg,
+				 VOLUME_DB_TO_VOXWARE(dmasound.volume_left) |
+				 (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8));
+	    case SOUND_MIXER_READ_BASS:
+		return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass));
+	    case SOUND_MIXER_READ_TREBLE:
+		return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble));
+	    case SOUND_MIXER_READ_OGAIN:
+		return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain));
+	    case SOUND_MIXER_WRITE_BASS:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_bass(data));
+	    case SOUND_MIXER_WRITE_TREBLE:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_treble(data));
+	    case SOUND_MIXER_WRITE_OGAIN:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, dmasound_set_gain(data));
+	}
+	return AtaMixerIoctl(cmd, arg);
+}
+
+static int FalconMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_RECMASK:
+		return IOCTL_OUT(arg, SOUND_MASK_MIC);
+	    case SOUND_MIXER_READ_DEVMASK:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
+	    case SOUND_MIXER_READ_STEREODEVS:
+		return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+	    case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg,
+			VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
+			VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
+	    case SOUND_MIXER_READ_CAPS:
+		return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
+	    case SOUND_MIXER_WRITE_MIC:
+		IOCTL_IN(arg, data);
+		tt_dmasnd.input_gain =
+			RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
+			RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
+		/* fall thru, return set value */
+	    case SOUND_MIXER_READ_MIC:
+		return IOCTL_OUT(arg,
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
+	}
+	return AtaMixerIoctl(cmd, arg);
+}
+
+static void AtaWriteSqSetup(void)
+{
+	write_sq_ignore_int = 0;
+}
+
+static void AtaSqOpen(void)
+{
+	write_sq_ignore_int = 1;
+}
+
+static int TTStateInfo(char *buffer)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n",
+		       dmasound.volume_right);
+	len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n",
+		       dmasound.bass);
+	len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n",
+		       dmasound.treble);
+	return len;
+}
+
+static int FalconStateInfo(char *buffer)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n",
+		       dmasound.volume_right);
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+
+static MACHINE machTT = {
+	name:		"Atari",
+	name2:		"TT",
+	open:		AtaOpen,
+	release:	AtaRelease,
+	dma_alloc:	AtaAlloc,
+	dma_free:	AtaFree,
+	irqinit:	AtaIrqInit,
+#ifdef MODULE
+	irqcleanup:	AtaIrqCleanUp,
+#endif /* MODULE */
+	init:		TTInit,
+	silence:	TTSilence,
+	setFormat:	TTSetFormat,
+	setVolume:	TTSetVolume,
+	setBass:	AtaSetBass,
+	setTreble:	AtaSetTreble,
+	setGain:	TTSetGain,
+	play:		AtaPlay,
+	mixer_init:	TTMixerInit,
+	mixer_ioctl:	TTMixerIoctl,
+	write_sq_setup:	AtaWriteSqSetup,
+	sq_open:	AtaSqOpen,
+	state_info:	TTStateInfo,
+	min_dsp_speed:	6258,
+};
+
+static MACHINE machFalcon = {
+	name:		"Atari",
+	name2:		"FALCON",
+	dma_alloc:	AtaAlloc,
+	dma_free:	AtaFree,
+	irqinit:	AtaIrqInit,
+#ifdef MODULE
+	irqcleanup:	AtaIrqCleanUp,
+#endif /* MODULE */
+	init:		FalconInit,
+	silence:	FalconSilence,
+	setFormat:	FalconSetFormat,
+	setVolume:	FalconSetVolume,
+	setBass:	AtaSetBass,
+	setTreble:	AtaSetTreble,
+	play:		AtaPlay,
+	mixer_init:	FalconMixerInit,
+	mixer_ioctl:	FalconMixerIoctl,
+	write_sq_setup:	AtaWriteSqSetup,
+	sq_open:	AtaSqOpen,
+	state_info:	FalconStateInfo,
+	min_dsp_speed:	8195,
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+static int __init dmasound_atari_init(void)
+{
+	if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) {
+	    if (ATARIHW_PRESENT(CODEC)) {
+		dmasound.mach = machFalcon;
+		is_falcon = 1;
+	    } else if (ATARIHW_PRESENT(MICROWIRE)) {
+		dmasound.mach = machTT;
+		is_falcon = 0;
+	    } else
+		return -ENODEV;
+	    if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0)
+		return dmasound_init();
+	    else {
+		printk("DMA sound driver: Timer A interrupt already in use\n");
+		return -EBUSY;
+	    }
+	}
+	return -ENODEV;
+}
+
+static void __exit dmasound_atari_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_atari_init);
+module_exit(dmasound_atari_cleanup);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/dmasound/dmasound_awacs.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_awacs.c
--- linux/sound/oss/dmasound/dmasound_awacs.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_awacs.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,2183 @@
+
+/*
+ *  linux/drivers/sound/dmasound/dmasound_awacs.c
+ *
+ *  PowerMac `AWACS' and `Burgundy' DMA Sound Driver
+ *
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ */
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/soundcard.h>
+#include <linux/adb.h>
+#include <linux/nvram.h>
+#include <linux/tty.h>
+#include <linux/vt_kern.h>
+#ifdef CONFIG_ADB_CUDA
+#include <linux/cuda.h>
+#endif
+#ifdef CONFIG_ADB_PMU
+#include <linux/pmu.h>
+#endif
+
+#include <asm/uaccess.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/feature.h>
+#include <asm/irq.h>
+
+#include "awacs_defs.h"
+#include "dmasound.h"
+
+
+/*
+ * Interrupt numbers and addresses, obtained from the device tree.
+ */
+static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
+static volatile struct awacs_regs *awacs;
+static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma;
+static int awacs_rate_index;
+static int awacs_subframe;
+static int awacs_spkr_vol;
+static struct device_node* awacs_node;
+
+static char awacs_name[64];
+static int awacs_revision;
+int awacs_is_screamer = 0;
+int awacs_device_id = 0;
+int awacs_has_iic = 0;
+#define AWACS_BURGUNDY	100		/* fake revision # for burgundy */
+
+/*
+ * Space for the DBDMA command blocks.
+ */
+static void *awacs_tx_cmd_space;
+static volatile struct dbdma_cmd *awacs_tx_cmds;
+
+static void *awacs_rx_cmd_space;
+static volatile struct dbdma_cmd *awacs_rx_cmds;
+
+/*
+ * Cached values of AWACS registers (we can't read them).
+ * Except on the burgundy. XXX
+ */
+int awacs_reg[8];
+
+#define HAS_16BIT_TABLES
+#undef HAS_8BIT_TABLES
+
+/*
+ * Stuff for outputting a beep.  The values range from -327 to +327
+ * so we can multiply by an amplitude in the range 0..100 to get a
+ * signed short value to put in the output buffer.
+ */
+static short beep_wform[256] = {
+	0,	40,	79,	117,	153,	187,	218,	245,
+	269,	288,	304,	316,	323,	327,	327,	324,
+	318,	310,	299,	288,	275,	262,	249,	236,
+	224,	213,	204,	196,	190,	186,	183,	182,
+	182,	183,	186,	189,	192,	196,	200,	203,
+	206,	208,	209,	209,	209,	207,	204,	201,
+	197,	193,	188,	183,	179,	174,	170,	166,
+	163,	161,	160,	159,	159,	160,	161,	162,
+	164,	166,	168,	169,	171,	171,	171,	170,
+	169,	167,	163,	159,	155,	150,	144,	139,
+	133,	128,	122,	117,	113,	110,	107,	105,
+	103,	103,	103,	103,	104,	104,	105,	105,
+	105,	103,	101,	97,	92,	86,	78,	68,
+	58,	45,	32,	18,	3,	-11,	-26,	-41,
+	-55,	-68,	-79,	-88,	-95,	-100,	-102,	-102,
+	-99,	-93,	-85,	-75,	-62,	-48,	-33,	-16,
+	0,	16,	33,	48,	62,	75,	85,	93,
+	99,	102,	102,	100,	95,	88,	79,	68,
+	55,	41,	26,	11,	-3,	-18,	-32,	-45,
+	-58,	-68,	-78,	-86,	-92,	-97,	-101,	-103,
+	-105,	-105,	-105,	-104,	-104,	-103,	-103,	-103,
+	-103,	-105,	-107,	-110,	-113,	-117,	-122,	-128,
+	-133,	-139,	-144,	-150,	-155,	-159,	-163,	-167,
+	-169,	-170,	-171,	-171,	-171,	-169,	-168,	-166,
+	-164,	-162,	-161,	-160,	-159,	-159,	-160,	-161,
+	-163,	-166,	-170,	-174,	-179,	-183,	-188,	-193,
+	-197,	-201,	-204,	-207,	-209,	-209,	-209,	-208,
+	-206,	-203,	-200,	-196,	-192,	-189,	-186,	-183,
+	-182,	-182,	-183,	-186,	-190,	-196,	-204,	-213,
+	-224,	-236,	-249,	-262,	-275,	-288,	-299,	-310,
+	-318,	-324,	-327,	-327,	-323,	-316,	-304,	-288,
+	-269,	-245,	-218,	-187,	-153,	-117,	-79,	-40,
+};
+
+#define BEEP_SRATE	22050	/* 22050 Hz sample rate */
+#define BEEP_BUFLEN	512
+#define BEEP_VOLUME	15	/* 0 - 100 */
+
+static int beep_volume = BEEP_VOLUME;
+static int beep_playing = 0;
+static int awacs_beep_state = 0;
+static short *beep_buf;
+static volatile struct dbdma_cmd *beep_dbdma_cmd;
+static void (*orig_mksound)(unsigned int, unsigned int);
+static int is_pbook_3400;
+static unsigned char *latch_base;
+static int is_pbook_G3;
+static unsigned char *macio_base;
+
+/* Burgundy functions */
+static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
+static unsigned awacs_burgundy_rcw(unsigned addr);
+static void awacs_burgundy_write_volume(unsigned address, int volume);
+static int awacs_burgundy_read_volume(unsigned address);
+static void awacs_burgundy_write_mvolume(unsigned address, int volume);
+static int awacs_burgundy_read_mvolume(unsigned address);
+
+#ifdef CONFIG_PMAC_PBOOK
+/*
+ * Stuff for restoring after a sleep.
+ */
+static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when);
+struct pmu_sleep_notifier awacs_sleep_notifier = {
+	awacs_sleep_notify, SLEEP_LEVEL_SOUND,
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Translations ************************************************************/
+
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void PMacOpen(void);
+static void PMacRelease(void);
+static void *PMacAlloc(unsigned int size, int flags);
+static void PMacFree(void *ptr, unsigned int size);
+static int PMacIrqInit(void);
+#ifdef MODULE
+static void PMacIrqCleanup(void);
+#endif
+static void PMacSilence(void);
+static void PMacInit(void);
+static int PMacSetFormat(int format);
+static int PMacSetVolume(int volume);
+static void PMacPlay(void);
+static void PMacRecord(void);
+static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs);
+static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs);
+static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs);
+static void awacs_write(int val);
+static int awacs_get_volume(int reg, int lshift);
+static int awacs_volume_setter(int volume, int n, int mute, int lshift);
+static void awacs_mksound(unsigned int hz, unsigned int ticks);
+static void awacs_nosound(unsigned long xx);
+
+
+/*** Mid level stuff **********************************************************/
+
+
+static int PMacMixerIoctl(u_int cmd, u_long arg);
+static void PMacWriteSqSetup(void);
+static void PMacReadSqSetup(void);
+static void PMacAbortRead(void);
+
+
+/*** Translations ************************************************************/
+
+
+static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	short *table = dmasound.soft.format == AFMT_MU_LAW
+		? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = table[data];
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = table[data];
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = data << 8;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = data << 8;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = (data ^ 0x80) << 8;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = (data ^ 0x80) << 8;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	if (!stereo) {
+		short *up = (short *) userPtr;
+		while (count > 0) {
+			short data;
+			if (get_user(data, up++))
+				return -EFAULT;
+			*fp++ = data;
+			*fp++ = data;
+			count--;
+		}
+	} else {
+		if (copy_from_user(fp, userPtr, count * 4))
+			return -EFAULT;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+	short *up = (short *) userPtr;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		int data;
+		if (get_user(data, up++))
+			return -EFAULT;
+		data ^= mask;
+		*fp++ = data;
+		if (stereo) {
+			if (get_user(data, up++))
+				return -EFAULT;
+			data ^= mask;
+		}
+		*fp++ = data;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+
+static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned short *table = (unsigned short *)
+		(dmasound.soft.format == AFMT_MU_LAW
+		 ? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
+	unsigned int data = expand_data;
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+	int stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = table[c];
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + table[c];
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c << 8;
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + (c << 8);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = (c ^ 0x80) << 8;
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + ((c ^ 0x80) << 8);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	unsigned short *up = (unsigned short *) userPtr;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		unsigned short c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(data, up++))
+				return -EFAULT;
+			if (stereo) {
+				if (get_user(c, up++))
+					return -EFAULT;
+				data = (data << 16) + c;
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 4: utotal * 2;
+}
+
+
+static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	unsigned short *up = (unsigned short *) userPtr;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		unsigned short c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(data, up++))
+				return -EFAULT;
+			data ^= mask;
+			if (stereo) {
+				if (get_user(c, up++))
+					return -EFAULT;
+				data = (data << 16) + (c ^ mask);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 4: utotal * 2;
+}
+
+static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+
+		val = *p++;
+		data = val >> 8;
+		if (put_user(data, (u_char *)userPtr++))
+			return -EFAULT;
+		if (stereo) {
+			val = *p;
+			data = val >> 8;
+			if (put_user(data, (u_char *)userPtr++))
+				return -EFAULT;
+		}
+		p++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+
+		val = *p++;
+		data = (val >> 8) ^ 0x80;
+		if (put_user(data, (u_char *)userPtr++))
+			return -EFAULT;
+		if (stereo) {
+			val = *p;
+			data = (val >> 8) ^ 0x80;
+			if (put_user(data, (u_char *)userPtr++))
+				return -EFAULT;
+		}
+		p++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	if (!stereo) {
+		short *up = (short *) userPtr;
+		while (count > 0) {
+			short data;
+			data = *fp;
+			if (put_user(data, up++))
+				return -EFAULT;
+			fp+=2;
+			count--;
+		}
+	} else {
+		if (copy_to_user((u_char *)userPtr, fp, count * 4))
+			return -EFAULT;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+	short *up = (short *) userPtr;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		int data;
+
+		data = *fp++;
+		data ^= mask;
+		if (put_user(data, up++))
+			return -EFAULT;
+		if (stereo) {
+			data = *fp;
+			data ^= mask;
+			if (put_user(data, up++))
+				return -EFAULT;
+		}
+		fp++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+
+static TRANS transAwacsNormal = {
+	ct_ulaw:	pmac_ct_law,
+	ct_alaw:	pmac_ct_law,
+	ct_s8:		pmac_ct_s8,
+	ct_u8:		pmac_ct_u8,
+	ct_s16be:	pmac_ct_s16,
+	ct_u16be:	pmac_ct_u16,
+	ct_s16le:	pmac_ct_s16,
+	ct_u16le:	pmac_ct_u16,
+};
+
+static TRANS transAwacsExpand = {
+	ct_ulaw:	pmac_ctx_law,
+	ct_alaw:	pmac_ctx_law,
+	ct_s8:		pmac_ctx_s8,
+	ct_u8:		pmac_ctx_u8,
+	ct_s16be:	pmac_ctx_s16,
+	ct_u16be:	pmac_ctx_u16,
+	ct_s16le:	pmac_ctx_s16,
+	ct_u16le:	pmac_ctx_u16,
+};
+
+static TRANS transAwacsNormalRead = {
+	ct_s8:		pmac_ct_s8_read,
+	ct_u8:		pmac_ct_u8_read,
+	ct_s16be:	pmac_ct_s16_read,
+	ct_u16be:	pmac_ct_u16_read,
+	ct_s16le:	pmac_ct_s16_read,
+	ct_u16le:	pmac_ct_u16_read,
+};
+
+/*** Low level stuff *********************************************************/
+
+
+
+/*
+ * PCI PowerMac, with AWACS and DBDMA.
+ */
+
+static void PMacOpen(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void PMacRelease(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static void *PMacAlloc(unsigned int size, int flags)
+{
+	return kmalloc(size, flags);
+}
+
+static void PMacFree(void *ptr, unsigned int size)
+{
+	kfree(ptr);
+}
+
+static int __init PMacIrqInit(void)
+{
+	if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0)
+	    || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)
+	    || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0))
+		return 0;
+	return 1;
+}
+
+#ifdef MODULE
+static void PMacIrqCleanup(void)
+{
+	/* turn off output dma */
+	out_le32(&awacs_txdma->control, RUN<<16);
+	/* disable interrupts from awacs interface */
+	out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
+#ifdef CONFIG_PMAC_PBOOK
+	if (is_pbook_G3) {
+		feature_clear(awacs_node, FEATURE_Sound_power);
+		feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
+	}
+#endif
+	free_irq(awacs_irq, 0);
+	free_irq(awacs_tx_irq, 0);
+	free_irq(awacs_rx_irq, 0);
+	kfree(awacs_tx_cmd_space);
+	if (awacs_rx_cmd_space)
+		kfree(awacs_rx_cmd_space);
+	if (beep_buf)
+		kfree(beep_buf);
+	kd_mksound = orig_mksound;
+#ifdef CONFIG_PMAC_PBOOK
+	pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
+#endif
+}
+#endif /* MODULE */
+
+static void PMacSilence(void)
+{
+	/* turn off output dma */
+	out_le32(&awacs_txdma->control, RUN<<16);
+}
+
+static int awacs_freqs[8] = {
+	44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
+};
+static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
+static void PMacInit(void)
+{
+	int i, tolerance;
+
+	switch (dmasound.soft.format) {
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		dmasound.hard.format = AFMT_S16_LE;
+		break;
+	default:
+		dmasound.hard.format = AFMT_S16_BE;
+		break;
+	}
+	dmasound.hard.stereo = 1;
+	dmasound.hard.size = 16;
+
+	/*
+	 * If we have a sample rate which is within catchRadius percent
+	 * of the requested value, we don't have to expand the samples.
+	 * Otherwise choose the next higher rate.
+	 * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz.
+	 */
+	i = 8;
+	do {
+		tolerance = catchRadius * awacs_freqs[--i] / 100;
+		if (awacs_freqs_ok[i]
+		    && dmasound.soft.speed <= awacs_freqs[i] + tolerance)
+			break;
+	} while (i > 0);
+	if (dmasound.soft.speed >= awacs_freqs[i] - tolerance)
+		dmasound.trans_write = &transAwacsNormal;
+	else
+		dmasound.trans_write = &transAwacsExpand;
+	dmasound.trans_read = &transAwacsNormalRead;
+	dmasound.hard.speed = awacs_freqs[i];
+	awacs_rate_index = i;
+
+	/* XXX disable error interrupt on burgundy for now */
+	out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
+		 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
+	awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
+	awacs_write(awacs_reg[1] | MASK_ADDR1);
+	out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+
+	/* We really want to execute a DMA stop command, after the AWACS
+	 * is initialized.
+	 * For reasons I don't understand, it stops the hissing noise
+	 * common to many PowerBook G3 systems (like mine :-).
+	 */
+	out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
+	st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
+	out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+	out_le32(&awacs_txdma->control, RUN | (RUN << 16));
+
+	expand_bal = -dmasound.soft.speed;
+}
+
+static int PMacSetFormat(int format)
+{
+	int size;
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+		size = 8;
+		break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		size = 16;
+		break;
+	default: /* :-) */
+		printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n",
+		       format);
+		size = 8;
+		format = AFMT_U8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = size;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = size;
+	}
+
+	PMacInit();
+
+	return format;
+}
+
+#define AWACS_VOLUME_TO_MASK(x)	(15 - ((((x) - 1) * 15) / 99))
+#define AWACS_MASK_TO_VOLUME(y)	(100 - ((y) * 99 / 15))
+
+static int awacs_get_volume(int reg, int lshift)
+{
+	int volume;
+
+	volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf);
+	volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8;
+	return volume;
+}
+
+static int awacs_volume_setter(int volume, int n, int mute, int lshift)
+{
+	int r1, rn;
+
+	if (mute && volume == 0) {
+		r1 = awacs_reg[1] | mute;
+	} else {
+		r1 = awacs_reg[1] & ~mute;
+		rn = awacs_reg[n] & ~(0xf | (0xf << lshift));
+		rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift);
+		rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf;
+		awacs_reg[n] = rn;
+		awacs_write((n << 12) | rn);
+		volume = awacs_get_volume(rn, lshift);
+	}
+	if (r1 != awacs_reg[1]) {
+		awacs_reg[1] = r1;
+		awacs_write(r1 | MASK_ADDR1);
+	}
+	return volume;
+}
+
+static int PMacSetVolume(int volume)
+{
+	return awacs_volume_setter(volume, 2, MASK_AMUTE, 6);
+}
+
+static void PMacPlay(void)
+{
+	volatile struct dbdma_cmd *cp;
+	int i, count;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	if (awacs_beep_state) {
+		/* sound takes precedence over beeps */
+		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+		out_le32(&awacs->control,
+			 (in_le32(&awacs->control) & ~0x1f00)
+			 | (awacs_rate_index << 8));
+		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count])));
+
+		beep_playing = 0;
+		awacs_beep_state = 0;
+	}
+	i = write_sq.front + write_sq.active;
+	if (i >= write_sq.max_count)
+		i -= write_sq.max_count;
+	while (write_sq.active < 2 && write_sq.active < write_sq.count) {
+		count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size;
+		if (count < write_sq.block_size && !write_sq.syncing)
+			/* last block not yet filled, and we're not syncing. */
+			break;
+		cp = &awacs_tx_cmds[i];
+		st_le16(&cp->req_count, count);
+		st_le16(&cp->xfer_status, 0);
+		if (++i >= write_sq.max_count)
+			i = 0;
+		out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP);
+		out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+		if (write_sq.active == 0)
+			out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
+		out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
+		++write_sq.active;
+	}
+	restore_flags(flags);
+}
+
+
+static void PMacRecord(void)
+{
+	unsigned long flags;
+
+	if (read_sq.active)
+		return;
+
+	save_flags(flags); cli();
+
+	/* This is all we have to do......Just start it up.
+	*/
+	out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
+	read_sq.active = 1;
+
+	restore_flags(flags);
+}
+
+
+static void
+pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	int i = write_sq.front;
+	int stat;
+	volatile struct dbdma_cmd *cp;
+
+	while (write_sq.active > 0) {
+		cp = &awacs_tx_cmds[i];
+		stat = ld_le16(&cp->xfer_status);
+		if ((stat & ACTIVE) == 0)
+			break;	/* this frame is still going */
+		--write_sq.count;
+		--write_sq.active;
+		if (++i >= write_sq.max_count)
+			i = 0;
+	}
+	if (i != write_sq.front)
+		WAKE_UP(write_sq.action_queue);
+	write_sq.front = i;
+
+	PMacPlay();
+
+	if (!write_sq.active)
+		WAKE_UP(write_sq.sync_queue);
+}
+
+
+static void
+pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
+{
+
+	/* For some reason on my PowerBook G3, I get one interrupt
+	 * when the interrupt vector is installed (like something is
+	 * pending).  This happens before the dbdma is initialize by
+	 * us, so I just check the command pointer and if it is zero,
+	 * just blow it off.
+	 */
+	if (in_le32(&awacs_rxdma->cmdptr) == 0)
+		return;
+
+	/* We also want to blow 'em off when shutting down.
+	*/
+	if (read_sq.active == 0)
+		return;
+
+	/* Check multiple buffers in case we were held off from
+	 * interrupt processing for a long time.  Geeze, I really hope
+	 * this doesn't happen.
+	 */
+	while (awacs_rx_cmds[read_sq.rear].xfer_status) {
+
+		/* Clear status and move on to next buffer.
+		*/
+		awacs_rx_cmds[read_sq.rear].xfer_status = 0;
+		read_sq.rear++;
+
+		/* Wrap the buffer ring.
+		*/
+		if (read_sq.rear >= read_sq.max_active)
+			read_sq.rear = 0;
+
+		/* If we have caught up to the front buffer, bump it.
+		 * This will cause weird (but not fatal) results if the
+		 * read loop is currently using this buffer.  The user is
+		 * behind in this case anyway, so weird things are going
+		 * to happen.
+		 */
+		if (read_sq.rear == read_sq.front) {
+			read_sq.front++;
+			if (read_sq.front >= read_sq.max_active)
+				read_sq.front = 0;
+		}
+	}
+
+	WAKE_UP(read_sq.action_queue);
+}
+
+
+static void
+pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	int ctrl = in_le32(&awacs->control);
+
+	if (ctrl & MASK_PORTCHG) {
+		/* do something when headphone is plugged/unplugged? */
+	}
+	if (ctrl & MASK_CNTLERR) {
+		int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
+		if (err != 0 && awacs_revision < AWACS_BURGUNDY)
+			printk(KERN_ERR "AWACS: error %x\n", err);
+	}
+	/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
+	out_le32(&awacs->control, ctrl);
+}
+
+static void
+awacs_write(int val)
+{
+	if (awacs_revision >= AWACS_BURGUNDY)
+		return;
+	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
+		;	/* XXX should have timeout */
+	out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
+}
+
+static void awacs_nosound(unsigned long xx)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	if (beep_playing) {
+		st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
+		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+		out_le32(&awacs->control,
+			 (in_le32(&awacs->control) & ~0x1f00)
+			 | (awacs_rate_index << 8));
+		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		beep_playing = 0;
+	}
+	restore_flags(flags);
+}
+
+static struct timer_list beep_timer = {
+	function: awacs_nosound
+};
+
+static void awacs_mksound(unsigned int hz, unsigned int ticks)
+{
+	unsigned long flags;
+	int beep_speed = 0;
+	int srate;
+	int period, ncycles, nsamples;
+	int i, j, f;
+	short *p;
+	static int beep_hz_cache;
+	static int beep_nsamples_cache;
+	static int beep_volume_cache;
+
+	for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
+		if (awacs_freqs_ok[i])
+			beep_speed = i;
+	srate = awacs_freqs[beep_speed];
+
+	if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
+#if 1
+		/* this is a hack for broken X server code */
+		hz = 750;
+		ticks = 12;
+#else
+		/* cancel beep currently playing */
+		awacs_nosound(0);
+		return;
+#endif
+	}
+	save_flags(flags); cli();
+	del_timer(&beep_timer);
+	if (ticks) {
+		beep_timer.expires = jiffies + ticks;
+		add_timer(&beep_timer);
+	}
+	if (beep_playing || write_sq.active || beep_buf == NULL) {
+		restore_flags(flags);
+		return;		/* too hard, sorry :-( */
+	}
+	beep_playing = 1;
+	st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
+	restore_flags(flags);
+
+	if (hz == beep_hz_cache && beep_volume == beep_volume_cache) {
+		nsamples = beep_nsamples_cache;
+	} else {
+		period = srate * 256 / hz;	/* fixed point */
+		ncycles = BEEP_BUFLEN * 256 / period;
+		nsamples = (period * ncycles) >> 8;
+		f = ncycles * 65536 / nsamples;
+		j = 0;
+		p = beep_buf;
+		for (i = 0; i < nsamples; ++i, p += 2) {
+			p[0] = p[1] = beep_wform[j >> 8] * beep_volume;
+			j = (j + f) & 0xffff;
+		}
+		beep_hz_cache = hz;
+		beep_volume_cache = beep_volume;
+		beep_nsamples_cache = nsamples;
+	}
+
+	st_le16(&beep_dbdma_cmd->req_count, nsamples*4);
+	st_le16(&beep_dbdma_cmd->xfer_status, 0);
+	st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd));
+	st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf));
+	awacs_beep_state = 1;
+
+	save_flags(flags); cli();
+	if (beep_playing) {	/* i.e. haven't been terminated already */
+		out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
+		out_le32(&awacs->control,
+			 (in_le32(&awacs->control) & ~0x1f00)
+			 | (beep_speed << 8));
+		out_le32(&awacs->byteswap, 0);
+		out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+		out_le32(&awacs_txdma->control, RUN | (RUN << 16));
+	}
+	restore_flags(flags);
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+/*
+ * Save state when going to sleep, restore it afterwards.
+ */
+static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	switch (when) {
+	case PBOOK_SLEEP_NOW:
+		/* XXX we should stop any dma in progress when going to sleep
+		   and restart it when we wake. */
+		PMacSilence();
+		disable_irq(awacs_irq);
+		disable_irq(awacs_tx_irq);
+		if (is_pbook_G3) {
+			feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
+			feature_clear(awacs_node, FEATURE_Sound_power);
+		}
+		break;
+	case PBOOK_WAKE:
+		/* There is still a problem on wake. Sound seems to work fine
+		   if I launch mpg123 and resumes fine if mpg123 was playing,
+		   but the console beep is dead until I do something with the
+		   mixer. Probably yet another timing issue */
+		if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable)
+		    || !feature_test(awacs_node, FEATURE_Sound_power)) {
+			/* these aren't present on the 3400 AFAIK -- paulus */
+			feature_set(awacs_node, FEATURE_Sound_CLK_enable);
+			feature_set(awacs_node, FEATURE_Sound_power);
+			mdelay(1000);
+		}
+		out_le32(&awacs->control, MASK_IEPC
+			 | (awacs_rate_index << 8) | 0x11
+			 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
+		awacs_write(awacs_reg[0] | MASK_ADDR0);
+		awacs_write(awacs_reg[1] | MASK_ADDR1);
+		awacs_write(awacs_reg[2] | MASK_ADDR2);
+		awacs_write(awacs_reg[4] | MASK_ADDR4);
+		if (awacs_is_screamer) {
+			awacs_write(awacs_reg[5] + MASK_ADDR5);
+			awacs_write(awacs_reg[6] + MASK_ADDR6);
+			awacs_write(awacs_reg[7] + MASK_ADDR7);
+		}
+		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		enable_irq(awacs_irq);
+		enable_irq(awacs_tx_irq);
+		if (awacs_revision == 3) {
+			mdelay(100);
+			awacs_write(0x6000);
+			mdelay(2);
+			awacs_write(awacs_reg[1] | MASK_ADDR1);
+		}
+		/* enable CD sound input */
+		if (macio_base && is_pbook_G3) {
+			out_8(macio_base + 0x37, 3);
+		} else if (is_pbook_3400) {
+			feature_set(awacs_node, FEATURE_IOBUS_enable);
+			udelay(10);
+			in_8(latch_base + 0x190);
+		}
+		/* Resume pending sounds. */
+		PMacPlay();
+	}
+	return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+
+/* All the burgundy functions: */
+
+/* Waits for busy flag to clear */
+inline static void
+awacs_burgundy_busy_wait(void)
+{
+	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
+		;
+}
+
+inline static void
+awacs_burgundy_extend_wait(void)
+{
+	while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND))
+		;
+	while (in_le32(&awacs->codec_stat) & MASK_EXTEND)
+		;
+}
+
+static void
+awacs_burgundy_wcw(unsigned addr, unsigned val)
+{
+	out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
+	awacs_burgundy_busy_wait();
+	out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
+	awacs_burgundy_busy_wait();
+	out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
+	awacs_burgundy_busy_wait();
+	out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
+	awacs_burgundy_busy_wait();
+}
+
+static unsigned
+awacs_burgundy_rcw(unsigned addr)
+{
+	unsigned val = 0;
+	unsigned long flags;
+
+	/* should have timeouts here */
+	save_flags(flags); cli();
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100000);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100100);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8;
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100200);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16;
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100300);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24;
+
+	restore_flags(flags);
+
+	return val;
+}
+
+
+static void
+awacs_burgundy_wcb(unsigned addr, unsigned val)
+{
+	out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
+	awacs_burgundy_busy_wait();
+}
+
+static unsigned
+awacs_burgundy_rcb(unsigned addr)
+{
+	unsigned val = 0;
+	unsigned long flags;
+
+	/* should have timeouts here */
+	save_flags(flags); cli();
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100000);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
+
+	restore_flags(flags);
+
+	return val;
+}
+
+static int
+awacs_burgundy_check(void)
+{
+	/* Checks to see the chip is alive and kicking */
+	int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE;
+
+	return error == 0xf0000;
+}
+
+static int
+awacs_burgundy_init(void)
+{
+	if (awacs_burgundy_check()) {
+		printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n");
+		return 1;
+	}
+
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES,
+			   DEF_BURGUNDY_OUTPUTENABLES);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+			   DEF_BURGUNDY_MORE_OUTPUTENABLES);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
+			   DEF_BURGUNDY_OUTPUTSELECTS);
+
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21,
+			   DEF_BURGUNDY_INPSEL21);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3,
+			   DEF_BURGUNDY_INPSEL3);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD,
+			   DEF_BURGUNDY_GAINCD);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE,
+			   DEF_BURGUNDY_GAINLINE);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC,
+			   DEF_BURGUNDY_GAINMIC);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM,
+			   DEF_BURGUNDY_GAINMODEM);
+
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER,
+			   DEF_BURGUNDY_ATTENSPEAKER);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT,
+			   DEF_BURGUNDY_ATTENLINEOUT);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP,
+			   DEF_BURGUNDY_ATTENHP);
+
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME,
+			   DEF_BURGUNDY_MASTER_VOLUME);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD,
+			   DEF_BURGUNDY_VOLCD);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE,
+			   DEF_BURGUNDY_VOLLINE);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC,
+			   DEF_BURGUNDY_VOLMIC);
+	return 0;
+}
+
+static void
+awacs_burgundy_write_volume(unsigned address, int volume)
+{
+	int hardvolume,lvolume,rvolume;
+
+	lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0;
+	rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0;
+
+	hardvolume = lvolume + (rvolume << 16);
+
+	awacs_burgundy_wcw(address, hardvolume);
+}
+
+static int
+awacs_burgundy_read_volume(unsigned address)
+{
+	int softvolume,wvolume;
+
+	wvolume = awacs_burgundy_rcw(address);
+
+	softvolume = (wvolume & 0xff) - 155;
+	softvolume += (((wvolume >> 16) & 0xff) - 155)<<8;
+
+	return softvolume > 0 ? softvolume : 0;
+}
+
+
+
+
+static int
+awacs_burgundy_read_mvolume(unsigned address)
+{
+	int lvolume,rvolume,wvolume;
+
+	wvolume = awacs_burgundy_rcw(address);
+
+	wvolume &= 0xffff;
+
+	rvolume = (wvolume & 0xff) - 155;
+	lvolume = ((wvolume & 0xff00)>>8) - 155;
+
+	return lvolume + (rvolume << 8);
+}
+
+
+static void
+awacs_burgundy_write_mvolume(unsigned address, int volume)
+{
+	int lvolume,rvolume,hardvolume;
+
+	lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0;
+	rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0;
+
+	hardvolume = lvolume + (rvolume << 8);
+	hardvolume += (hardvolume << 16);
+
+	awacs_burgundy_wcw(address, hardvolume);
+}
+
+/* End burgundy functions */
+
+
+
+
+
+/* Turn on sound output, needed on G3 desktop powermacs */
+static void
+awacs_enable_amp(int spkr_vol)
+{
+	struct adb_request req;
+
+	awacs_spkr_vol = spkr_vol;
+	if (sys_ctrler != SYS_CTRLER_CUDA)
+		return;
+
+#ifdef CONFIG_ADB_CUDA
+	/* turn on headphones */
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 4, 0);
+	while (!req.complete) cuda_poll();
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 6, 0);
+	while (!req.complete) cuda_poll();
+
+	/* turn on speaker */
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100);
+	while (!req.complete) cuda_poll();
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100);
+	while (!req.complete) cuda_poll();
+
+	cuda_request(&req, NULL, 5, CUDA_PACKET,
+		     CUDA_GET_SET_IIC, 0x8a, 1, 0x29);
+	while (!req.complete) cuda_poll();
+#endif /* CONFIG_ADB_CUDA */
+}
+
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+static int awacs_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+
+	switch (cmd) {
+	case SOUND_MIXER_READ_DEVMASK:
+		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+			| SOUND_MASK_LINE | SOUND_MASK_MIC
+			| SOUND_MASK_CD | SOUND_MASK_RECLEV
+			| SOUND_MASK_ALTPCM
+			| SOUND_MASK_MONITOR;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_RECMASK:
+		data = SOUND_MASK_LINE | SOUND_MASK_MIC
+			| SOUND_MASK_CD;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		if (awacs_reg[0] & MASK_MUX_AUDIN)
+			data |= SOUND_MASK_LINE;
+		if (awacs_reg[0] & MASK_MUX_MIC)
+			data |= SOUND_MASK_MIC;
+		if (awacs_reg[0] & MASK_MUX_CD)
+			data |= SOUND_MASK_CD;
+		if (awacs_reg[1] & MASK_LOOPTHRU)
+			data |= SOUND_MASK_MONITOR;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data &= (SOUND_MASK_LINE
+			 | SOUND_MASK_MIC | SOUND_MASK_CD
+			 | SOUND_MASK_MONITOR);
+		awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
+				  | MASK_MUX_AUDIN);
+		awacs_reg[1] &= ~MASK_LOOPTHRU;
+		if (data & SOUND_MASK_LINE)
+			awacs_reg[0] |= MASK_MUX_AUDIN;
+		if (data & SOUND_MASK_MIC)
+			awacs_reg[0] |= MASK_MUX_MIC;
+		if (data & SOUND_MASK_CD)
+			awacs_reg[0] |= MASK_MUX_CD;
+		if (data & SOUND_MASK_MONITOR)
+			awacs_reg[1] |= MASK_LOOPTHRU;
+		awacs_write(awacs_reg[0] | MASK_ADDR0);
+		awacs_write(awacs_reg[1] | MASK_ADDR1);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_STEREODEVS:
+		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+			| SOUND_MASK_RECLEV;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_CAPS:
+		return IOCTL_OUT(arg, 0);
+	case SOUND_MIXER_READ_VOLUME:
+		data = (awacs_reg[1] & MASK_AMUTE)? 0:
+			awacs_get_volume(awacs_reg[2], 6);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_VOLUME:
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, PMacSetVolume(data));
+	case SOUND_MIXER_READ_SPEAKER:
+		if (awacs_revision == 3
+		    && sys_ctrler == SYS_CTRLER_CUDA)
+			data = awacs_spkr_vol;
+		else
+			data = (awacs_reg[1] & MASK_CMUTE)? 0:
+				awacs_get_volume(awacs_reg[4], 6);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_SPEAKER:
+		IOCTL_IN(arg, data);
+		if (awacs_revision == 3
+		    && sys_ctrler == SYS_CTRLER_CUDA)
+			awacs_enable_amp(data);
+		else
+			data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
+		IOCTL_IN(arg, data);
+		beep_volume = data & 0xff;
+				/* fall through */
+	case SOUND_MIXER_READ_ALTPCM:
+		return IOCTL_OUT(arg, beep_volume);
+	case SOUND_MIXER_WRITE_LINE:
+		IOCTL_IN(arg, data);
+		awacs_reg[0] &= ~MASK_MUX_AUDIN;
+		if ((data & 0xff) >= 50)
+			awacs_reg[0] |= MASK_MUX_AUDIN;
+		awacs_write(MASK_ADDR0 | awacs_reg[0]);
+				/* fall through */
+	case SOUND_MIXER_READ_LINE:
+		data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_MIC:
+		IOCTL_IN(arg, data);
+		data &= 0xff;
+		awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
+		if (data >= 25) {
+			awacs_reg[0] |= MASK_MUX_MIC;
+			if (data >= 75)
+				awacs_reg[0] |= MASK_GAINLINE;
+		}
+		awacs_write(MASK_ADDR0 | awacs_reg[0]);
+				/* fall through */
+	case SOUND_MIXER_READ_MIC:
+		data = (awacs_reg[0] & MASK_MUX_MIC)?
+			(awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_CD:
+		IOCTL_IN(arg, data);
+		awacs_reg[0] &= ~MASK_MUX_CD;
+		if ((data & 0xff) >= 50)
+			awacs_reg[0] |= MASK_MUX_CD;
+		awacs_write(MASK_ADDR0 | awacs_reg[0]);
+				/* fall through */
+	case SOUND_MIXER_READ_CD:
+		data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_RECLEV:
+		IOCTL_IN(arg, data);
+		data = awacs_volume_setter(data, 0, 0, 4);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_RECLEV:
+		data = awacs_get_volume(awacs_reg[0], 4);
+		return IOCTL_OUT(arg, data);
+	case MIXER_WRITE(SOUND_MIXER_MONITOR):
+		IOCTL_IN(arg, data);
+		awacs_reg[1] &= ~MASK_LOOPTHRU;
+		if ((data & 0xff) >= 50)
+			awacs_reg[1] |= MASK_LOOPTHRU;
+		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		/* fall through */
+	case MIXER_READ(SOUND_MIXER_MONITOR):
+		data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
+		return IOCTL_OUT(arg, data);
+	}
+	return -EINVAL;
+}
+
+static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+
+	/* We are, we are, we are... Burgundy or better */
+	switch(cmd) {
+	case SOUND_MIXER_READ_DEVMASK:
+		data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
+			SOUND_MASK_LINE | SOUND_MASK_MIC |
+			SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_RECMASK:
+		data = SOUND_MASK_LINE | SOUND_MASK_MIC
+			| SOUND_MASK_CD;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		if (awacs_reg[0] & MASK_MUX_AUDIN)
+			data |= SOUND_MASK_LINE;
+		if (awacs_reg[0] & MASK_MUX_MIC)
+			data |= SOUND_MASK_MIC;
+		if (awacs_reg[0] & MASK_MUX_CD)
+			data |= SOUND_MASK_CD;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data &= (SOUND_MASK_LINE
+			 | SOUND_MASK_MIC | SOUND_MASK_CD);
+		awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
+				  | MASK_MUX_AUDIN);
+		if (data & SOUND_MASK_LINE)
+			awacs_reg[0] |= MASK_MUX_AUDIN;
+		if (data & SOUND_MASK_MIC)
+			awacs_reg[0] |= MASK_MUX_MIC;
+		if (data & SOUND_MASK_CD)
+			awacs_reg[0] |= MASK_MUX_CD;
+		awacs_write(awacs_reg[0] | MASK_ADDR0);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_STEREODEVS:
+		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+			| SOUND_MASK_RECLEV | SOUND_MASK_CD
+			| SOUND_MASK_LINE;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_CAPS:
+		return IOCTL_OUT(arg, 0);
+	case SOUND_MIXER_WRITE_VOLUME:
+		IOCTL_IN(arg, data);
+		awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data);
+				/* Fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
+	case SOUND_MIXER_WRITE_SPEAKER:
+		IOCTL_IN(arg, data);
+
+		if (!(data & 0xff)) {
+			/* Mute the left speaker */
+			awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+					   awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2);
+		} else {
+			/* Unmute the left speaker */
+			awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+					   awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2);
+		}
+		if (!(data & 0xff00)) {
+			/* Mute the right speaker */
+			awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+					   awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4);
+		} else {
+			/* Unmute the right speaker */
+			awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+					   awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4);
+		}
+
+		data = (((data&0xff)*16)/100 > 0xf ? 0xf :
+			(((data&0xff)*16)/100)) + 
+			((((data>>8)*16)/100 > 0xf ? 0xf :
+			  ((((data>>8)*16)/100)))<<4);
+
+		awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data);
+				/* Fall through */
+	case SOUND_MIXER_READ_SPEAKER:
+		data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
+		data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
+		return IOCTL_OUT(arg, ~data);
+	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
+		IOCTL_IN(arg, data);
+		beep_volume = data & 0xff;
+				/* fall through */
+	case SOUND_MIXER_READ_ALTPCM:
+		return IOCTL_OUT(arg, beep_volume);
+	case SOUND_MIXER_WRITE_LINE:
+		IOCTL_IN(arg, data);
+		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);
+
+				/* fall through */
+	case SOUND_MIXER_READ_LINE:
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);				
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_MIC:
+		IOCTL_IN(arg, data);
+				/* Mic is mono device */
+		data = (data << 8) + (data << 24);
+		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
+				/* fall through */
+	case SOUND_MIXER_READ_MIC:
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);				
+		data <<= 24;
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_CD:
+		IOCTL_IN(arg, data);
+		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
+				/* fall through */
+	case SOUND_MIXER_READ_CD:
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_WRITE_RECLEV:
+		IOCTL_IN(arg, data);
+		data = awacs_volume_setter(data, 0, 0, 4);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_READ_RECLEV:
+		data = awacs_get_volume(awacs_reg[0], 4);
+		return IOCTL_OUT(arg, data);
+	case SOUND_MIXER_OUTMASK:
+		break;
+	case SOUND_MIXER_OUTSRC:
+		break;
+	}
+	return -EINVAL;
+}
+
+static int PMacMixerIoctl(u_int cmd, u_long arg)
+{
+	/* Different IOCTLS for burgundy*/
+	if (awacs_revision >= AWACS_BURGUNDY)
+		return burgundy_mixer_ioctl(cmd, arg);
+	return awacs_mixer_ioctl(cmd, arg);
+}
+
+
+static void PMacWriteSqSetup(void)
+{
+	int i;
+	volatile struct dbdma_cmd *cp;
+
+	cp = awacs_tx_cmds;
+	memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd));
+	for (i = 0; i < write_sq.numBufs; ++i, ++cp) {
+		st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i]));
+	}
+	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
+	st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
+	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
+}
+
+static void PMacReadSqSetup(void)
+{
+	int i;
+	volatile struct dbdma_cmd *cp;
+
+	cp = awacs_rx_cmds;
+	memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd));
+
+	/* Set dma buffers up in a loop */
+	for (i = 0; i < read_sq.numBufs; i++,cp++) {
+		st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i]));
+		st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
+		st_le16(&cp->req_count, read_sq.block_size);
+		st_le16(&cp->xfer_status, 0);
+	}
+
+	/* The next two lines make the thing loop around.
+	*/
+	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
+	st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));
+
+	/* Don't start until the first read is done.
+	 * This will also abort any operations in progress if the DMA
+	 * happens to be running (and it shouldn't).
+	 */
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
+
+}
+
+static void PMacAbortRead(void)
+{
+	int i;
+	volatile struct dbdma_cmd *cp;
+
+	cp = awacs_rx_cmds;
+	for (i = 0; i < read_sq.numBufs; i++,cp++)
+		st_le16(&cp->command, DBDMA_STOP);
+	/*
+	 * We should probably wait for the thing to stop before we
+	 * release the memory
+	 */
+}
+
+
+/*** Machine definitions *****************************************************/
+
+
+static MACHINE machPMac = {
+	name:		awacs_name,
+	name2:		"AWACS",
+	open:		PMacOpen,
+	release:	PMacRelease,
+	dma_alloc:	PMacAlloc,
+	dma_free:	PMacFree,
+	irqinit:	PMacIrqInit,
+#ifdef MODULE
+	irqcleanup:	PMacIrqCleanup,
+#endif /* MODULE */
+	init:		PMacInit,
+	silence:	PMacSilence,
+	setFormat:	PMacSetFormat,
+	setVolume:	PMacSetVolume,
+	play:		PMacPlay,
+	record:		PMacRecord,
+	mixer_ioctl:	PMacMixerIoctl,
+	write_sq_setup:	PMacWriteSqSetup,
+	read_sq_setup:	PMacReadSqSetup,
+	abort_read:	PMacAbortRead,
+	min_dsp_speed:	8000
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+int __init dmasound_awacs_init(void)
+{
+	struct device_node *np;
+
+	if (_machine != _MACH_Pmac)
+		return -ENODEV;
+
+	awacs_subframe = 0;
+	awacs_revision = 0;
+	np = find_devices("awacs");
+	if (np == 0) {
+		/*
+		 * powermac G3 models have a node called "davbus"
+		 * with a child called "sound".
+		 */
+		struct device_node *sound;
+		np = find_devices("davbus");
+		sound = find_devices("sound");
+		if (sound != 0 && sound->parent == np) {
+			unsigned int *prop, l, i;
+			prop = (unsigned int *)
+				get_property(sound, "sub-frame", 0);
+			if (prop != 0 && *prop >= 0 && *prop < 16)
+				awacs_subframe = *prop;
+			if (device_is_compatible(sound, "burgundy"))
+				awacs_revision = AWACS_BURGUNDY;
+			/* This should be verified on older screamers */
+			if (device_is_compatible(sound, "screamer"))
+				awacs_is_screamer = 1;
+			prop = (unsigned int *)get_property(sound, "device-id", 0);
+			if (prop != 0)
+				awacs_device_id = *prop;
+			awacs_has_iic = (find_devices("perch") != NULL);
+
+			/* look for a property saying what sample rates
+			   are available */
+			for (i = 0; i < 8; ++i)
+				awacs_freqs_ok[i] = 0;
+			prop = (unsigned int *) get_property
+				(sound, "sample-rates", &l);
+			if (prop == 0)
+				prop = (unsigned int *) get_property
+					(sound, "output-frame-rates", &l);
+			if (prop != 0) {
+				for (l /= sizeof(int); l > 0; --l) {
+					/* sometimes the rate is in the
+					   high-order 16 bits (?) */
+					unsigned int r = *prop++;
+					if (r >= 0x10000)
+						r >>= 16;
+					for (i = 0; i < 8; ++i) {
+						if (r == awacs_freqs[i]) {
+							awacs_freqs_ok[i] = 1;
+							break;
+						}
+					}
+				}
+			} else {
+				/* assume just 44.1k is OK */
+				awacs_freqs_ok[0] = 1;
+			}
+		}
+	}
+	if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
+		int vol;
+		dmasound.mach = machPMac;
+
+		awacs = (volatile struct awacs_regs *)
+			ioremap(np->addrs[0].address, 0x80);
+		awacs_txdma = (volatile struct dbdma_regs *)
+			ioremap(np->addrs[1].address, 0x100);
+		awacs_rxdma = (volatile struct dbdma_regs *)
+			ioremap(np->addrs[2].address, 0x100);
+
+		awacs_irq = np->intrs[0].line;
+		awacs_tx_irq = np->intrs[1].line;
+		awacs_rx_irq = np->intrs[2].line;
+
+		awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
+					     GFP_KERNEL);
+		if (awacs_tx_cmd_space == NULL) {
+			printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
+			return -ENOMEM;
+		}
+		awacs_node = np;
+#ifdef CONFIG_PMAC_PBOOK
+		if (machine_is_compatible("PowerBook1,1")
+		    || machine_is_compatible("AAPL,PowerBook1998")) {
+			pmu_suspend();
+			feature_set(np, FEATURE_Sound_CLK_enable);
+			feature_set(np, FEATURE_Sound_power);
+			/* Shorter delay will not work */
+			mdelay(1000);
+			pmu_resume();
+		}
+#endif
+		awacs_tx_cmds = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(awacs_tx_cmd_space);
+
+
+		awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
+					     GFP_KERNEL);
+		if (awacs_rx_cmd_space == NULL) {
+		  printk("DMA sound driver: No memory for input");
+		}
+		awacs_rx_cmds = (volatile struct dbdma_cmd *)
+		  DBDMA_ALIGN(awacs_rx_cmd_space);
+
+
+
+		awacs_reg[0] = MASK_MUX_CD;
+		/* FIXME: Only machines with external SRS module need MASK_PAROUT */
+		awacs_reg[1] = MASK_LOOPTHRU;
+		if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8
+			|| */awacs_device_id == 0xb)
+			awacs_reg[1] |= MASK_PAROUT;
+		/* get default volume from nvram */
+		vol = (~nvram_read_byte(0x1308) & 7) << 1;
+		awacs_reg[2] = vol + (vol << 6);
+		awacs_reg[4] = vol + (vol << 6);
+		awacs_reg[5] = 0;
+		awacs_reg[6] = 0;
+		awacs_reg[7] = 0;
+		out_le32(&awacs->control, 0x11);
+		awacs_write(awacs_reg[0] + MASK_ADDR0);
+		awacs_write(awacs_reg[1] + MASK_ADDR1);
+		awacs_write(awacs_reg[2] + MASK_ADDR2);
+		awacs_write(awacs_reg[4] + MASK_ADDR4);
+		if (awacs_is_screamer) {
+			awacs_write(awacs_reg[5] + MASK_ADDR5);
+			awacs_write(awacs_reg[6] + MASK_ADDR6);
+			awacs_write(awacs_reg[7] + MASK_ADDR7);
+		}
+
+		/* Initialize recent versions of the awacs */
+		if (awacs_revision == 0) {
+			awacs_revision =
+				(in_le32(&awacs->codec_stat) >> 12) & 0xf;
+			if (awacs_revision == 3) {
+				mdelay(100);
+				awacs_write(0x6000);
+				mdelay(2);
+				awacs_write(awacs_reg[1] + MASK_ADDR1);
+				awacs_enable_amp(100 * 0x101);
+			}
+		}
+		if (awacs_revision >= AWACS_BURGUNDY)
+			awacs_burgundy_init();
+
+		/* Initialize beep stuff */
+		beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1);
+		orig_mksound = kd_mksound;
+		kd_mksound = awacs_mksound;
+		beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
+		if (beep_buf == NULL)
+			printk(KERN_WARNING "dmasound: no memory for "
+			       "beep buffer\n");
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&awacs_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+
+		/* Powerbooks have odd ways of enabling inputs such as
+		   an expansion-bay CD or sound from an internal modem
+		   or a PC-card modem. */
+		if (machine_is_compatible("AAPL,3400/2400")
+			|| machine_is_compatible("AAPL,3500")) {
+			is_pbook_3400 = 1;
+			/*
+			 * Enable CD and PC-card sound inputs.
+			 * This is done by reading from address
+			 * f301a000, + 0x10 to enable the expansion-bay
+			 * CD sound input, + 0x80 to enable the PC-card
+			 * sound input.  The 0x100 enables the SCSI bus
+			 * terminator power.
+			 */
+			latch_base = (unsigned char *) ioremap
+				(0xf301a000, 0x1000);
+			in_8(latch_base + 0x190);
+		} else if (machine_is_compatible("PowerBook1,1")
+			   || machine_is_compatible("AAPL,PowerBook1998")) {
+			struct device_node* mio;
+			macio_base = 0;
+			is_pbook_G3 = 1;
+			for (mio = np->parent; mio; mio = mio->parent) {
+				if (strcmp(mio->name, "mac-io") == 0
+				    && mio->n_addrs > 0) {
+					macio_base = (unsigned char *) ioremap
+						(mio->addrs[0].address, 0x40);
+					break;
+				}
+			}
+			/*
+			 * Enable CD sound input.
+			 * The relevant bits for writing to this byte are 0x8f.
+			 * I haven't found out what the 0x80 bit does.
+			 * For the 0xf bits, writing 3 or 7 enables the CD
+			 * input, any other value disables it.  Values
+			 * 1, 3, 5, 7 enable the microphone.  Values 0, 2,
+			 * 4, 6, 8 - f enable the input from the modem.
+			 */
+			if (macio_base)
+				out_8(macio_base + 0x37, 3);
+		}
+		sprintf(awacs_name, "PowerMac (AWACS rev %d) ",
+			awacs_revision);
+		return dmasound_init();
+	}
+	return -ENODEV;
+}
+
+static void __exit dmasound_awacs_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_awacs_init);
+module_exit(dmasound_awacs_cleanup);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/dmasound/dmasound_core.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_core.c
--- linux/sound/oss/dmasound/dmasound_core.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_core.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1313 @@
+
+/*
+ *  linux/drivers/sound/dmasound/dmasound_core.c
+ *
+ *
+ *  OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for
+ *  Linux/m68k
+ *  Extended to support Power Macintosh for Linux/ppc by Paul Mackerras
+ *
+ *  (c) 1995 by Michael Schlueter & Michael Marte
+ *
+ *  Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
+ *  interface and the u-law to signed byte conversion.
+ *
+ *  Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
+ *  /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
+ *  to thank:
+ *    - Michael Schlueter for initial ideas and documentation on the MFP and
+ *	the DMA sound hardware.
+ *    - Therapy? for their CD 'Troublegum' which really made me rock.
+ *
+ *  /dev/sndstat is based on code by Hannu Savolainen, the author of the
+ *  VoxWare family of drivers.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ *  History:
+ *
+ *	1995/8/25	First release
+ *
+ *	1995/9/02	Roman Hodek:
+ *			  - Fixed atari_stram_alloc() call, the timer
+ *			    programming and several race conditions
+ *	1995/9/14	Roman Hodek:
+ *			  - After some discussion with Michael Schlueter,
+ *			    revised the interrupt disabling
+ *			  - Slightly speeded up U8->S8 translation by using
+ *			    long operations where possible
+ *			  - Added 4:3 interpolation for /dev/audio
+ *
+ *	1995/9/20	Torsten Scherer:
+ *			  - Fixed a bug in sq_write and changed /dev/audio
+ *			    converting to play at 12517Hz instead of 6258Hz.
+ *
+ *	1995/9/23	Torsten Scherer:
+ *			  - Changed sq_interrupt() and sq_play() to pre-program
+ *			    the DMA for another frame while there's still one
+ *			    running. This allows the IRQ response to be
+ *			    arbitrarily delayed and playing will still continue.
+ *
+ *	1995/10/14	Guenther Kelleter, Torsten Scherer:
+ *			  - Better support for Falcon audio (the Falcon doesn't
+ *			    raise an IRQ at the end of a frame, but at the
+ *			    beginning instead!). uses 'if (codec_dma)' in lots
+ *			    of places to simply switch between Falcon and TT
+ *			    code.
+ *
+ *	1995/11/06	Torsten Scherer:
+ *			  - Started introducing a hardware abstraction scheme
+ *			    (may perhaps also serve for Amigas?)
+ *			  - Can now play samples at almost all frequencies by
+ *			    means of a more generalized expand routine
+ *			  - Takes a good deal of care to cut data only at
+ *			    sample sizes
+ *			  - Buffer size is now a kernel runtime option
+ *			  - Implemented fsync() & several minor improvements
+ *			Guenther Kelleter:
+ *			  - Useful hints and bug fixes
+ *			  - Cross-checked it for Falcons
+ *
+ *	1996/3/9	Geert Uytterhoeven:
+ *			  - Support added for Amiga, A-law, 16-bit little
+ *			    endian.
+ *			  - Unification to drivers/sound/dmasound.c.
+ *
+ *	1996/4/6	Martin Mitchell:
+ *			  - Updated to 1.3 kernel.
+ *
+ *	1996/6/13       Topi Kanerva:
+ *			  - Fixed things that were broken (mainly the amiga
+ *			    14-bit routines)
+ *			  - /dev/sndstat shows now the real hardware frequency
+ *			  - The lowpass filter is disabled by default now
+ *
+ *	1996/9/25	Geert Uytterhoeven:
+ *			  - Modularization
+ *
+ *	1998/6/10	Andreas Schwab:
+ *			  - Converted to use sound_core
+ *
+ *	1999/12/28	Richard Zidlicky:
+ *			  - Added support for Q40
+ *
+ *	2000/2/27	Geert Uytterhoeven:
+ *			  - Clean up and split the code into 4 parts:
+ *			      o dmasound_core: machine-independent code
+ *			      o dmasound_atari: Atari TT and Falcon support
+ *			      o dmasound_awacs: Apple PowerMac support
+ *			      o dmasound_paula: Amiga support
+ *
+ *	2000/3/25	Geert Uytterhoeven:
+ *			  - Integration of dmasound_q40
+ *			  - Small clean ups
+ */
+
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sound.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+#include "dmasound.h"
+
+
+    /*
+     *  Declarations
+     */
+
+int dmasound_catchRadius = 0;
+static unsigned int numWriteBufs = 4;
+static unsigned int writeBufSize = 32;	/* in KB! */
+#ifdef HAS_RECORD
+static unsigned int numReadBufs = 4;
+static unsigned int readBufSize = 32;	/* in KB! */
+#endif
+
+MODULE_PARM(dmasound_catchRadius, "i");
+MODULE_PARM(numWriteBufs, "i");
+MODULE_PARM(writeBufSize, "i");
+MODULE_PARM(numReadBufs, "i");
+MODULE_PARM(readBufSize, "i");
+MODULE_LICENSE("GPL");
+
+#ifdef MODULE
+static int sq_unit = -1;
+static int mixer_unit = -1;
+static int state_unit = -1;
+static int irq_installed = 0;
+#endif /* MODULE */
+
+
+    /*
+     *  Conversion tables
+     */
+
+#ifdef HAS_8BIT_TABLES
+/* 8 bit mu-law */
+
+char dmasound_ulaw2dma8[] = {
+	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
+	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
+	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
+	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
+	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
+	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
+	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
+	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
+	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
+	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
+	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
+	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
+	125,	121,	117,	113,	109,	105,	101,	97,
+	93,	89,	85,	81,	77,	73,	69,	65,
+	62,	60,	58,	56,	54,	52,	50,	48,
+	46,	44,	42,	40,	38,	36,	34,	32,
+	30,	29,	28,	27,	26,	25,	24,	23,
+	22,	21,	20,	19,	18,	17,	16,	15,
+	15,	14,	14,	13,	13,	12,	12,	11,
+	11,	10,	10,	9,	9,	8,	8,	7,
+	7,	7,	6,	6,	6,	6,	5,	5,
+	5,	5,	4,	4,	4,	4,	3,	3,
+	3,	3,	3,	3,	2,	2,	2,	2,
+	2,	2,	2,	2,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0
+};
+
+/* 8 bit A-law */
+
+char dmasound_alaw2dma8[] = {
+	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
+	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
+	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
+	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
+	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
+	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
+	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
+	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
+	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
+	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
+	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
+	21,	20,	23,	22,	17,	16,	19,	18,
+	29,	28,	31,	30,	25,	24,	27,	26,
+	10,	10,	11,	11,	8,	8,	9,	9,
+	14,	14,	15,	15,	12,	12,	13,	13,
+	86,	82,	94,	90,	70,	66,	78,	74,
+	118,	114,	126,	122,	102,	98,	110,	106,
+	43,	41,	47,	45,	35,	33,	39,	37,
+	59,	57,	63,	61,	51,	49,	55,	53,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	5,	5,	5,	5,	4,	4,	4,	4,
+	7,	7,	7,	7,	6,	6,	6,	6,
+	2,	2,	2,	2,	2,	2,	2,	2,
+	3,	3,	3,	3,	3,	3,	3,	3
+};
+#endif /* HAS_8BIT_TABLES */
+
+#ifdef HAS_16BIT_TABLES
+
+/* 16 bit mu-law */
+
+short dmasound_ulaw2dma16[] = {
+	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
+	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
+	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
+	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
+	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
+	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
+	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
+	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
+	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
+	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
+	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
+	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
+	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
+	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
+	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
+	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
+	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
+	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
+	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
+	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
+	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
+	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
+	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
+	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
+	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
+	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
+	876,	844,	812,	780,	748,	716,	684,	652,
+	620,	588,	556,	524,	492,	460,	428,	396,
+	372,	356,	340,	324,	308,	292,	276,	260,
+	244,	228,	212,	196,	180,	164,	148,	132,
+	120,	112,	104,	96,	88,	80,	72,	64,
+	56,	48,	40,	32,	24,	16,	8,	0,
+};
+
+/* 16 bit A-law */
+
+short dmasound_alaw2dma16[] = {
+	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
+	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
+	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
+	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
+	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
+	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
+	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
+	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
+	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
+	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
+	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
+	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
+	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
+	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
+	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
+	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
+	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
+	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
+	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
+	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
+	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
+	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
+	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
+	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
+	344,	328,	376,	360,	280,	264,	312,	296,
+	472,	456,	504,	488,	408,	392,	440,	424,
+	88,	72,	120,	104,	24,	8,	56,	40,
+	216,	200,	248,	232,	152,	136,	184,	168,
+	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
+	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
+	688,	656,	752,	720,	560,	528,	624,	592,
+	944,	912,	1008,	976,	816,	784,	880,	848,
+};
+#endif /* HAS_16BIT_TABLES */
+
+
+#ifdef HAS_14BIT_TABLES
+
+    /*
+     *  Unused for now. Where are the MSB parts anyway??
+     */
+
+/* 14 bit mu-law (LSB) */
+
+char dmasound_ulaw2dma14l[] = {
+	33,	33,	33,	33,	33,	33,	33,	33,
+	33,	33,	33,	33,	33,	33,	33,	33,
+	33,	33,	33,	33,	33,	33,	33,	33,
+	33,	33,	33,	33,	33,	33,	33,	33,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	49,	17,	49,	17,	49,	17,	49,	17,
+	49,	17,	49,	17,	49,	17,	49,	17,
+	41,	57,	9,	25,	41,	57,	9,	25,
+	41,	57,	9,	25,	41,	57,	9,	25,
+	37,	45,	53,	61,	5,	13,	21,	29,
+	37,	45,	53,	61,	5,	13,	21,	29,
+	35,	39,	43,	47,	51,	55,	59,	63,
+	3,	7,	11,	15,	19,	23,	27,	31,
+	34,	36,	38,	40,	42,	44,	46,	48,
+	50,	52,	54,	56,	58,	60,	62,	0,
+	31,	31,	31,	31,	31,	31,	31,	31,
+	31,	31,	31,	31,	31,	31,	31,	31,
+	31,	31,	31,	31,	31,	31,	31,	31,
+	31,	31,	31,	31,	31,	31,	31,	31,
+	63,	63,	63,	63,	63,	63,	63,	63,
+	63,	63,	63,	63,	63,	63,	63,	63,
+	15,	47,	15,	47,	15,	47,	15,	47,
+	15,	47,	15,	47,	15,	47,	15,	47,
+	23,	7,	55,	39,	23,	7,	55,	39,
+	23,	7,	55,	39,	23,	7,	55,	39,
+	27,	19,	11,	3,	59,	51,	43,	35,
+	27,	19,	11,	3,	59,	51,	43,	35,
+	29,	25,	21,	17,	13,	9,	5,	1,
+	61,	57,	53,	49,	45,	41,	37,	33,
+	30,	28,	26,	24,	22,	20,	18,	16,
+	14,	12,	10,	8,	6,	4,	2,	0
+};
+
+/* 14 bit A-law (LSB) */
+
+char dmasound_alaw2dma14l[] = {
+	32,	32,	32,	32,	32,	32,	32,	32,
+	32,	32,	32,	32,	32,	32,	32,	32,
+	16,	48,	16,	48,	16,	48,	16,	48,
+	16,	48,	16,	48,	16,	48,	16,	48,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	42,	46,	34,	38,	58,	62,	50,	54,
+	10,	14,	2,	6,	26,	30,	18,	22,
+	42,	46,	34,	38,	58,	62,	50,	54,
+	10,	14,	2,	6,	26,	30,	18,	22,
+	40,	56,	8,	24,	40,	56,	8,	24,
+	40,	56,	8,	24,	40,	56,	8,	24,
+	20,	28,	4,	12,	52,	60,	36,	44,
+	20,	28,	4,	12,	52,	60,	36,	44,
+	32,	32,	32,	32,	32,	32,	32,	32,
+	32,	32,	32,	32,	32,	32,	32,	32,
+	48,	16,	48,	16,	48,	16,	48,	16,
+	48,	16,	48,	16,	48,	16,	48,	16,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	22,	18,	30,	26,	6,	2,	14,	10,
+	54,	50,	62,	58,	38,	34,	46,	42,
+	22,	18,	30,	26,	6,	2,	14,	10,
+	54,	50,	62,	58,	38,	34,	46,	42,
+	24,	8,	56,	40,	24,	8,	56,	40,
+	24,	8,	56,	40,	24,	8,	56,	40,
+	44,	36,	60,	52,	12,	4,	28,	20,
+	44,	36,	60,	52,	12,	4,	28,	20
+};
+#endif /* HAS_14BIT_TABLES */
+
+
+    /*
+     *  Mid level stuff
+     */
+
+struct sound_settings dmasound;
+
+static inline void sound_silence(void)
+{
+	/* update hardware settings one more */
+	dmasound.mach.init();
+
+	dmasound.mach.silence();
+}
+
+static inline void sound_init(void)
+{
+	dmasound.mach.init();
+}
+
+static inline int sound_set_format(int format)
+{
+	return dmasound.mach.setFormat(format);
+}
+
+static int sound_set_speed(int speed)
+{
+	if (speed < 0)
+		return dmasound.soft.speed;
+
+	dmasound.soft.speed = speed;
+	dmasound.mach.init();
+	if (dmasound.minDev == SND_DEV_DSP)
+		dmasound.dsp.speed = dmasound.soft.speed;
+
+	return dmasound.soft.speed;
+}
+
+static int sound_set_stereo(int stereo)
+{
+	if (stereo < 0)
+		return dmasound.soft.stereo;
+
+	stereo = !!stereo;    /* should be 0 or 1 now */
+
+	dmasound.soft.stereo = stereo;
+	if (dmasound.minDev == SND_DEV_DSP)
+		dmasound.dsp.stereo = stereo;
+	dmasound.mach.init();
+
+	return stereo;
+}
+
+static ssize_t sound_copy_translate(TRANS *trans, const u_char *userPtr,
+				    size_t userCount, u_char frame[],
+				    ssize_t *frameUsed, ssize_t frameLeft)
+{
+	ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+
+	switch (dmasound.soft.format) {
+	    case AFMT_MU_LAW:
+		ct_func = trans->ct_ulaw;
+		break;
+	    case AFMT_A_LAW:
+		ct_func = trans->ct_alaw;
+		break;
+	    case AFMT_S8:
+		ct_func = trans->ct_s8;
+		break;
+	    case AFMT_U8:
+		ct_func = trans->ct_u8;
+		break;
+	    case AFMT_S16_BE:
+		ct_func = trans->ct_s16be;
+		break;
+	    case AFMT_U16_BE:
+		ct_func = trans->ct_u16be;
+		break;
+	    case AFMT_S16_LE:
+		ct_func = trans->ct_s16le;
+		break;
+	    case AFMT_U16_LE:
+		ct_func = trans->ct_u16le;
+		break;
+	    default:
+		return 0;
+	}
+	return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+}
+
+
+    /*
+     *  /dev/mixer abstraction
+     */
+
+static struct {
+    int busy;
+    int modify_counter;
+} mixer;
+
+static int mixer_open(struct inode *inode, struct file *file)
+{
+	dmasound.mach.open();
+	mixer.busy = 1;
+	return 0;
+}
+
+static int mixer_release(struct inode *inode, struct file *file)
+{
+	lock_kernel();
+	mixer.busy = 0;
+	dmasound.mach.release();
+	unlock_kernel();
+	return 0;
+}
+static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg)
+{
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+	    mixer.modify_counter++;
+	switch (cmd) {
+	    case OSS_GETVERSION:
+		return IOCTL_OUT(arg, SOUND_VERSION);
+	    case SOUND_MIXER_INFO:
+		{
+		    mixer_info info;
+		    strncpy(info.id, dmasound.mach.name2, sizeof(info.id));
+		    strncpy(info.name, dmasound.mach.name2, sizeof(info.name));
+		    info.name[sizeof(info.name)-1] = 0;
+		    info.modify_counter = mixer.modify_counter;
+		    if (copy_to_user((int *)arg, &info, sizeof(info)))
+			    return -EFAULT;
+		    return 0;
+		}
+	}
+	if (dmasound.mach.mixer_ioctl)
+	    return dmasound.mach.mixer_ioctl(cmd, arg);
+	return -EINVAL;
+}
+
+static struct file_operations mixer_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		mixer_ioctl,
+	open:		mixer_open,
+	release:	mixer_release,
+};
+
+static void __init mixer_init(void)
+{
+#ifndef MODULE
+	int mixer_unit;
+#endif
+	mixer_unit = register_sound_mixer(&mixer_fops, -1);
+	if (mixer_unit < 0)
+		return;
+
+	mixer.busy = 0;
+	dmasound.treble = 0;
+	dmasound.bass = 0;
+	if (dmasound.mach.mixer_init)
+	    dmasound.mach.mixer_init();
+}
+
+
+    /*
+     *  Sound queue stuff, the heart of the driver
+     */
+
+struct sound_queue dmasound_write_sq;
+#ifdef HAS_RECORD
+struct sound_queue dmasound_read_sq;
+#endif
+
+static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
+{
+	int i;
+
+	if (sq->buffers)
+		return 0;
+	sq->numBufs = num;
+	sq->bufSize = size;
+	sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL);
+	if (!sq->buffers)
+		return -ENOMEM;
+	for (i = 0; i < num; i++) {
+		sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL);
+		if (!sq->buffers[i]) {
+			while (i--)
+				dmasound.mach.dma_free(sq->buffers[i], size);
+			kfree(sq->buffers);
+			sq->buffers = 0;
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static void sq_release_buffers(struct sound_queue *sq)
+{
+	int i;
+
+	if (sq->buffers) {
+		if (sq != &write_sq && dmasound.mach.abort_read)
+			dmasound.mach.abort_read();
+		for (i = 0; i < sq->numBufs; i++)
+			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
+		kfree(sq->buffers);
+		sq->buffers = NULL;
+	}
+}
+
+static void sq_setup(struct sound_queue *sq, int max_count, int max_active,
+		     int block_size)
+{
+	void (*setup_func)(void);
+
+	sq->max_count = max_count;
+	sq->max_active = max_active;
+	sq->block_size = block_size;
+
+	sq->front = sq->count = sq->rear_size = 0;
+	sq->syncing = 0;
+	sq->active = 0;
+
+	if (sq == &write_sq) {
+	    sq->rear = -1;
+	    setup_func = dmasound.mach.write_sq_setup;
+	} else {
+	    sq->rear = 0;
+	    setup_func = dmasound.mach.read_sq_setup;
+	}
+	if (setup_func)
+	    setup_func();
+}
+
+static inline void sq_play(void)
+{
+	dmasound.mach.play();
+}
+
+static ssize_t sq_write(struct file *file, const char *src, size_t uLeft,
+			loff_t *ppos)
+{
+	ssize_t uWritten = 0;
+	u_char *dest;
+	ssize_t uUsed, bUsed, bLeft;
+
+	/* ++TeSche: Is something like this necessary?
+	 * Hey, that's an honest question! Or does any other part of the
+	 * filesystem already checks this situation? I really don't know.
+	 */
+	if (uLeft == 0)
+		return 0;
+
+	/* The interrupt doesn't start to play the last, incomplete frame.
+	 * Thus we can append to it without disabling the interrupts! (Note
+	 * also that write_sq.rear isn't affected by the interrupt.)
+	 */
+
+	if (write_sq.count > 0 &&
+	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
+		dest = write_sq.buffers[write_sq.rear];
+		bUsed = write_sq.rear_size;
+		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
+					     dest, &bUsed, bLeft);
+		if (uUsed <= 0)
+			return uUsed;
+		src += uUsed;
+		uWritten += uUsed;
+		uLeft -= uUsed;
+		write_sq.rear_size = bUsed;
+	}
+
+	do {
+		while (write_sq.count == write_sq.max_active) {
+			sq_play();
+			if (write_sq.open_mode & O_NONBLOCK)
+				return uWritten > 0 ? uWritten : -EAGAIN;
+			SLEEP(write_sq.action_queue);
+			if (signal_pending(current))
+				return uWritten > 0 ? uWritten : -EINTR;
+		}
+
+		/* Here, we can avoid disabling the interrupt by first
+		 * copying and translating the data, and then updating
+		 * the write_sq variables. Until this is done, the interrupt
+		 * won't see the new frame and we can work on it
+		 * undisturbed.
+		 */
+
+		dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count];
+		bUsed = 0;
+		bLeft = write_sq.block_size;
+		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
+					     dest, &bUsed, bLeft);
+		if (uUsed <= 0)
+			break;
+		src += uUsed;
+		uWritten += uUsed;
+		uLeft -= uUsed;
+		if (bUsed) {
+			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
+			write_sq.rear_size = bUsed;
+			write_sq.count++;
+		}
+	} while (bUsed);   /* uUsed may have been 0 */
+
+	sq_play();
+
+	return uUsed < 0? uUsed: uWritten;
+}
+
+#ifdef HAS_RECORD
+    /*
+     *  Here is how the values are used for reading.
+     *  The value 'active' simply indicates the DMA is running.  This is done
+     *  so the driver semantics are DMA starts when the first read is posted.
+     *  The value 'front' indicates the buffer we should next send to the user.
+     *  The value 'rear' indicates the buffer the DMA is currently filling.
+     *  When 'front' == 'rear' the buffer "ring" is empty (we always have an
+     *  empty available).  The 'rear_size' is used to track partial offsets
+     *  into the current buffer.  Right now, I just keep the DMA running.  If
+     *  the reader can't keep up, the interrupt tosses the oldest buffer.  We
+     *  could also shut down the DMA in this case.
+     */
+
+static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
+		       loff_t *ppos)
+{
+
+	ssize_t	uRead, bLeft, bUsed, uUsed;
+
+	if (uLeft == 0)
+		return 0;
+
+	if (!read_sq.active && dmasound.mach.record)
+		dmasound.mach.record();	/* Kick off the record process. */
+
+	uRead = 0;
+
+	/* Move what the user requests, depending upon other options.
+	*/
+	while (uLeft > 0) {
+
+		/* When front == rear, the DMA is not done yet.
+		*/
+		while (read_sq.front == read_sq.rear) {
+			if (read_sq.open_mode & O_NONBLOCK) {
+			       return uRead > 0 ? uRead : -EAGAIN;
+			}
+			SLEEP(read_sq.action_queue);
+			if (signal_pending(current))
+				return uRead > 0 ? uRead : -EINTR;
+		}
+
+		/* The amount we move is either what is left in the
+		 * current buffer or what the user wants.
+		 */
+		bLeft = read_sq.block_size - read_sq.rear_size;
+		bUsed = read_sq.rear_size;
+		uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft,
+					     read_sq.buffers[read_sq.front],
+					     &bUsed, bLeft);
+		if (uUsed <= 0)
+			return uUsed;
+		dst += uUsed;
+		uRead += uUsed;
+		uLeft -= uUsed;
+		read_sq.rear_size += bUsed;
+		if (read_sq.rear_size >= read_sq.block_size) {
+			read_sq.rear_size = 0;
+			read_sq.front++;
+			if (read_sq.front >= read_sq.max_active)
+				read_sq.front = 0;
+		}
+	}
+	return uRead;
+}
+#endif /* HAS_RECORD */
+
+static inline void sq_init_waitqueue(struct sound_queue *sq)
+{
+	init_waitqueue_head(&sq->action_queue);
+	init_waitqueue_head(&sq->open_queue);
+	init_waitqueue_head(&sq->sync_queue);
+	sq->busy = 0;
+}
+
+static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
+			      mode_t mode)
+{
+	if (file->f_mode & mode) {
+		sq->busy = 0;
+		WAKE_UP(sq->open_queue);
+	}
+}
+
+static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode,
+		    int numbufs, int bufsize)
+{
+	int rc = 0;
+
+	if (file->f_mode & mode) {
+		if (sq->busy) {
+			rc = -EBUSY;
+			if (file->f_flags & O_NONBLOCK)
+				return rc;
+			rc = -EINTR;
+			while (sq->busy) {
+				SLEEP(sq->open_queue);
+				if (signal_pending(current))
+					return rc;
+			}
+			rc = 0;
+		}
+		sq->busy = 1; /* Let's play spot-the-race-condition */
+
+		if (sq_allocate_buffers(sq, numbufs, bufsize)) {
+			sq_wake_up(sq, file, mode);
+			return rc;
+		}
+
+		sq_setup(sq, numbufs, numbufs, bufsize);
+		sq->open_mode = file->f_mode;
+	}
+	return rc;
+}
+
+#define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
+#define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
+#define write_sq_release_buffers()	sq_release_buffers(&write_sq)
+#define write_sq_open(file)	\
+	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10)
+
+#ifdef HAS_RECORD
+#define read_sq_init_waitqueue()	sq_init_waitqueue(&read_sq)
+#define read_sq_wake_up(file)		sq_wake_up(&read_sq, file, FMODE_READ)
+#define read_sq_release_buffers()	sq_release_buffers(&read_sq)
+#define read_sq_open(file)	\
+	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10)
+#else /* !HAS_RECORD */
+#define read_sq_init_waitqueue()	do {} while (0)
+#define read_sq_wake_up(file)		do {} while (0)
+#define read_sq_release_buffers()	do {} while (0)
+#define read_sq_open(file)		(0)
+#endif /* !HAS_RECORD */
+
+static int sq_open(struct inode *inode, struct file *file)
+{
+	int rc;
+
+	dmasound.mach.open();
+	if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) {
+		dmasound.mach.release();
+		return rc;
+	}
+
+	if (dmasound.mach.sq_open)
+	    dmasound.mach.sq_open();
+	dmasound.minDev = MINOR(inode->i_rdev) & 0x0f;
+	dmasound.soft = dmasound.dsp;
+	dmasound.hard = dmasound.dsp;
+	sound_init();
+	if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) {
+		sound_set_speed(8000);
+		sound_set_stereo(0);
+		sound_set_format(AFMT_MU_LAW);
+	}
+
+#if 0
+	if (file->f_mode == FMODE_READ && dmasound.mach.record) {
+		/* Start dma'ing straight away */
+		dmasound.mach.record();
+	}
+#endif
+
+	return 0;
+}
+
+static void sq_reset(void)
+{
+	sound_silence();
+	write_sq.active = 0;
+	write_sq.count = 0;
+	write_sq.front = (write_sq.rear+1) % write_sq.max_count;
+}
+
+static int sq_fsync(struct file *filp, struct dentry *dentry)
+{
+	int rc = 0;
+
+	write_sq.syncing = 1;
+	sq_play();	/* there may be an incomplete frame waiting */
+
+	while (write_sq.active) {
+		SLEEP(write_sq.sync_queue);
+		if (signal_pending(current)) {
+			/* While waiting for audio output to drain, an
+			 * interrupt occurred.  Stop audio output immediately
+			 * and clear the queue. */
+			sq_reset();
+			rc = -EINTR;
+			break;
+		}
+	}
+
+	write_sq.syncing = 0;
+	return rc;
+}
+
+static int sq_release(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+
+	lock_kernel();
+	if (write_sq.busy)
+		rc = sq_fsync(file, file->f_dentry);
+	dmasound.soft = dmasound.dsp;
+	dmasound.hard = dmasound.dsp;
+	sound_silence();
+
+	write_sq_release_buffers();
+	read_sq_release_buffers();
+	dmasound.mach.release();
+
+	/* There is probably a DOS atack here. They change the mode flag. */
+	/* XXX add check here */
+	read_sq_wake_up(file);
+	write_sq_wake_up(file);
+
+	/* Wake up a process waiting for the queue being released.
+	 * Note: There may be several processes waiting for a call
+	 * to open() returning. */
+	unlock_kernel();
+
+	return rc;
+}
+
+static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		    u_long arg)
+{
+	int val;
+	u_long fmt;
+	int data;
+	int size, nbufs;
+	audio_buf_info info;
+
+	switch (cmd) {
+	case SNDCTL_DSP_RESET:
+		sq_reset();
+		return 0;
+	case SNDCTL_DSP_POST:
+	case SNDCTL_DSP_SYNC:
+		return sq_fsync(file, file->f_dentry);
+
+		/* ++TeSche: before changing any of these it's
+		 * probably wise to wait until sound playing has
+		 * settled down. */
+	case SNDCTL_DSP_SPEED:
+		sq_fsync(file, file->f_dentry);
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, sound_set_speed(data));
+	case SNDCTL_DSP_STEREO:
+		sq_fsync(file, file->f_dentry);
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, sound_set_stereo(data));
+	case SOUND_PCM_WRITE_CHANNELS:
+		sq_fsync(file, file->f_dentry);
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+	case SNDCTL_DSP_SETFMT:
+		sq_fsync(file, file->f_dentry);
+		IOCTL_IN(arg, data);
+		return IOCTL_OUT(arg, sound_set_format(data));
+	case SNDCTL_DSP_GETFMTS:
+		fmt = 0;
+		if (dmasound.trans_write) {
+			if (dmasound.trans_write->ct_ulaw)
+				fmt |= AFMT_MU_LAW;
+			if (dmasound.trans_write->ct_alaw)
+				fmt |= AFMT_A_LAW;
+			if (dmasound.trans_write->ct_s8)
+				fmt |= AFMT_S8;
+			if (dmasound.trans_write->ct_u8)
+				fmt |= AFMT_U8;
+			if (dmasound.trans_write->ct_s16be)
+				fmt |= AFMT_S16_BE;
+			if (dmasound.trans_write->ct_u16be)
+				fmt |= AFMT_U16_BE;
+			if (dmasound.trans_write->ct_s16le)
+				fmt |= AFMT_S16_LE;
+			if (dmasound.trans_write->ct_u16le)
+				fmt |= AFMT_U16_LE;
+		}
+		return IOCTL_OUT(arg, fmt);
+	case SNDCTL_DSP_GETBLKSIZE:
+		size = write_sq.block_size
+			* dmasound.soft.size * (dmasound.soft.stereo + 1)
+			/ (dmasound.hard.size * (dmasound.hard.stereo + 1));
+		return IOCTL_OUT(arg, size);
+	case SNDCTL_DSP_SUBDIVIDE:
+		break;
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (write_sq.count || write_sq.active || write_sq.syncing)
+			return -EINVAL;
+		IOCTL_IN(arg, size);
+		nbufs = size >> 16;
+		if (nbufs < 2 || nbufs > write_sq.numBufs)
+			nbufs = write_sq.numBufs;
+		size &= 0xffff;
+		if (size >= 8 && size <= 29) {
+			size = 1 << size;
+			size *= dmasound.hard.size * (dmasound.hard.stereo + 1);
+			size /= dmasound.soft.size * (dmasound.soft.stereo + 1);
+			if (size > write_sq.bufSize)
+				size = write_sq.bufSize;
+		} else
+			size = write_sq.bufSize;
+		sq_setup(&write_sq, write_sq.numBufs, nbufs, size);
+		return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16);
+	case SNDCTL_DSP_GETOSPACE:
+		info.fragments = write_sq.max_active - write_sq.count;
+		info.fragstotal = write_sq.max_active;
+		info.fragsize = write_sq.block_size;
+		info.bytes = info.fragments * info.fragsize;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	case SNDCTL_DSP_GETCAPS:
+		val = 1;        /* Revision level of this ioctl() */
+		return IOCTL_OUT(arg,val);
+
+	default:
+		return mixer_ioctl(inode, file, cmd, arg);
+	}
+	return -EINVAL;
+}
+
+static struct file_operations sq_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	write:		sq_write,
+	ioctl:		sq_ioctl,
+	open:		sq_open,
+	release:	sq_release,
+#ifdef HAS_RECORD
+	read:		sq_read,
+#endif
+};
+
+static void __init sq_init(void)
+{
+#ifndef MODULE
+	int sq_unit;
+#endif
+	sq_unit = register_sound_dsp(&sq_fops, -1);
+	if (sq_unit < 0)
+		return;
+
+	write_sq_init_waitqueue();
+	read_sq_init_waitqueue();
+
+	/* whatever you like as startup mode for /dev/dsp,
+	 * (/dev/audio hasn't got a startup mode). note that
+	 * once changed a new open() will *not* restore these!
+	 */
+	dmasound.dsp.format = AFMT_U8;
+	dmasound.dsp.stereo = 0;
+	dmasound.dsp.size = 8;
+
+	/* set minimum rate possible without expanding */
+	dmasound.dsp.speed = dmasound.mach.min_dsp_speed;
+
+	/* before the first open to /dev/dsp this wouldn't be set */
+	dmasound.soft = dmasound.dsp;
+	dmasound.hard = dmasound.dsp;
+
+	sound_silence();
+}
+
+
+    /*
+     *  /dev/sndstat
+     */
+
+static struct {
+    int busy;
+    char buf[512];	/* state.buf should not overflow! */
+    int len, ptr;
+} state;
+
+static int state_open(struct inode *inode, struct file *file)
+{
+	char *buffer = state.buf;
+	int len = 0;
+
+	if (state.busy)
+		return -EBUSY;
+
+	dmasound.mach.open();
+	state.ptr = 0;
+	state.busy = 1;
+
+	len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name);
+
+	len += sprintf(buffer+len, "\tsound.format = 0x%x",
+		       dmasound.soft.format);
+	switch (dmasound.soft.format) {
+	    case AFMT_MU_LAW:
+		len += sprintf(buffer+len, " (mu-law)");
+		break;
+	    case AFMT_A_LAW:
+		len += sprintf(buffer+len, " (A-law)");
+		break;
+	    case AFMT_U8:
+		len += sprintf(buffer+len, " (unsigned 8 bit)");
+		break;
+	    case AFMT_S8:
+		len += sprintf(buffer+len, " (signed 8 bit)");
+		break;
+	    case AFMT_S16_BE:
+		len += sprintf(buffer+len, " (signed 16 bit big)");
+		break;
+	    case AFMT_U16_BE:
+		len += sprintf(buffer+len, " (unsigned 16 bit big)");
+		break;
+	    case AFMT_S16_LE:
+		len += sprintf(buffer+len, " (signed 16 bit little)");
+		break;
+	    case AFMT_U16_LE:
+		len += sprintf(buffer+len, " (unsigned 16 bit little)");
+		break;
+	}
+	len += sprintf(buffer+len, "\n");
+	len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
+		       dmasound.soft.speed, dmasound.hard.speed);
+	len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
+		       dmasound.soft.stereo,
+		       dmasound.soft.stereo ? "stereo" : "mono");
+	if (dmasound.mach.state_info)
+	    len += dmasound.mach.state_info(buffer);
+	len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d"
+		       " sq.max_active = %d\n",
+		       write_sq.block_size, write_sq.max_count,
+		       write_sq.max_active);
+	len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n",
+		       write_sq.count, write_sq.rear_size);
+	len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n",
+		       write_sq.active, write_sq.syncing);
+	state.len = len;
+	return 0;
+}
+
+static int state_release(struct inode *inode, struct file *file)
+{
+	lock_kernel();
+	state.busy = 0;
+	dmasound.mach.release();
+	unlock_kernel();
+	return 0;
+}
+
+static ssize_t state_read(struct file *file, char *buf, size_t count,
+			  loff_t *ppos)
+{
+	int n = state.len - state.ptr;
+	if (n > count)
+		n = count;
+	if (n <= 0)
+		return 0;
+	if (copy_to_user(buf, &state.buf[state.ptr], n))
+		return -EFAULT;
+	state.ptr += n;
+	return n;
+}
+
+static struct file_operations state_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		state_read,
+	open:		state_open,
+	release:	state_release,
+};
+
+static void __init state_init(void)
+{
+#ifndef MODULE
+	int state_unit;
+#endif
+	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
+	if (state_unit < 0)
+		return;
+	state.busy = 0;
+}
+
+
+    /*
+     *  Config & Setup
+     *
+     *  This function is called by _one_ chipset-specific driver
+     */
+
+int __init dmasound_init(void)
+{
+#ifdef MODULE
+	if (irq_installed)
+		return -EBUSY;
+#endif
+
+	/* Set up sound queue, /dev/audio and /dev/dsp. */
+
+	/* Set default settings. */
+	sq_init();
+
+	/* Set up /dev/sndstat. */
+	state_init();
+
+	/* Set up /dev/mixer. */
+	mixer_init();
+
+	if (!dmasound.mach.irqinit()) {
+		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
+		return -ENODEV;
+	}
+#ifdef MODULE
+	irq_installed = 1;
+#endif
+
+	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n",
+	       numWriteBufs, writeBufSize);
+
+	return 0;
+}
+
+#ifdef MODULE
+
+void dmasound_deinit(void)
+{
+	if (irq_installed) {
+		sound_silence();
+		dmasound.mach.irqcleanup();
+	}
+
+	write_sq_release_buffers();
+	read_sq_release_buffers();
+
+	if (mixer_unit >= 0)
+		unregister_sound_mixer(mixer_unit);
+	if (state_unit >= 0)
+		unregister_sound_special(state_unit);
+	if (sq_unit >= 0)
+		unregister_sound_dsp(sq_unit);
+}
+
+#else /* !MODULE */
+
+static int __init dmasound_setup(char *str)
+{
+	int ints[6];
+
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	/* check the bootstrap parameter for "dmasound=" */
+
+	switch (ints[0]) {
+	case 3:
+		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
+			printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+		else
+			catchRadius = ints[3];
+		/* fall through */
+	case 2:
+		if (ints[1] < MIN_BUFFERS)
+			printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs);
+		else
+			numWriteBufs = ints[1];
+		if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
+			printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize);
+		else
+			writeBufSize = ints[2];
+		break;
+	case 0:
+		break;
+	default:
+		printk("dmasound_setup: illegal number of arguments\n");
+		return 0;
+	}
+	return 1;
+}
+
+__setup("dmasound=", dmasound_setup);
+
+#endif /* !MODULE */
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(dmasound);
+EXPORT_SYMBOL(dmasound_init);
+#ifdef MODULE
+EXPORT_SYMBOL(dmasound_deinit);
+#endif
+EXPORT_SYMBOL(dmasound_write_sq);
+#ifdef HAS_RECORD
+EXPORT_SYMBOL(dmasound_read_sq);
+#endif
+EXPORT_SYMBOL(dmasound_catchRadius);
+#ifdef HAS_8BIT_TABLES
+EXPORT_SYMBOL(dmasound_ulaw2dma8);
+EXPORT_SYMBOL(dmasound_alaw2dma8);
+#endif
+#ifdef HAS_16BIT_TABLES
+EXPORT_SYMBOL(dmasound_ulaw2dma16);
+EXPORT_SYMBOL(dmasound_alaw2dma16);
+#endif
+#ifdef HAS_14BIT_TABLES
+EXPORT_SYMBOL(dmasound_ulaw2dma14l);
+EXPORT_SYMBOL(dmasound_ulaw2dma14h);
+EXPORT_SYMBOL(dmasound_alaw2dma14l);
+EXPORT_SYMBOL(dmasound_alaw2dma14h);
+#endif
+
diff -Nru linux/sound/oss/dmasound/dmasound_paula.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_paula.c
--- linux/sound/oss/dmasound/dmasound_paula.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_paula.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,723 @@
+
+/*
+ *  linux/drivers/sound/dmasound/dmasound_paula.c
+ *
+ *  Amiga `Paula' DMA Sound Driver
+ *
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ */
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/soundcard.h>
+
+#include <asm/uaccess.h>
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/machdep.h>
+
+#include "dmasound.h"
+
+
+   /*
+    *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern volatile u_short amiga_audio_min_period;
+
+
+   /*
+    *	amiga_mksound() should be able to restore the period after beeping
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern u_short amiga_audio_period;
+
+
+   /*
+    *	Audio DMA masks
+    */
+
+#define AMI_AUDIO_OFF	(DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
+#define AMI_AUDIO_8	(DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
+#define AMI_AUDIO_14	(AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
+
+
+    /*
+     *  Helper pointers for 16(14)-bit sound
+     */
+
+static int write_sq_block_size_half, write_sq_block_size_quarter;
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void AmiOpen(void);
+static void AmiRelease(void);
+static void *AmiAlloc(unsigned int size, int flags);
+static void AmiFree(void *obj, unsigned int size);
+static int AmiIrqInit(void);
+#ifdef MODULE
+static void AmiIrqCleanUp(void);
+#endif
+static void AmiSilence(void);
+static void AmiInit(void);
+static int AmiSetFormat(int format);
+static int AmiSetVolume(int volume);
+static int AmiSetTreble(int treble);
+static void AmiPlayNextFrame(int index);
+static void AmiPlay(void);
+static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp);
+
+#ifdef CONFIG_HEARTBEAT
+
+    /*
+     *  Heartbeat interferes with sound since the 7 kHz low-pass filter and the
+     *  power LED are controlled by the same line.
+     */
+
+#ifdef CONFIG_APUS
+#define mach_heartbeat	ppc_md.heartbeat
+#endif
+
+static void (*saved_heartbeat)(int) = NULL;
+
+static inline void disable_heartbeat(void)
+{
+	if (mach_heartbeat) {
+	    saved_heartbeat = mach_heartbeat;
+	    mach_heartbeat = NULL;
+	}
+	AmiSetTreble(dmasound.treble);
+}
+
+static inline void enable_heartbeat(void)
+{
+	if (saved_heartbeat)
+	    mach_heartbeat = saved_heartbeat;
+}
+#else /* !CONFIG_HEARTBEAT */
+#define disable_heartbeat()	do { } while (0)
+#define enable_heartbeat()	do { } while (0)
+#endif /* !CONFIG_HEARTBEAT */
+
+
+/*** Mid level stuff *********************************************************/
+
+static void AmiMixerInit(void);
+static int AmiMixerIoctl(u_int cmd, u_long arg);
+static void AmiWriteSqSetup(void);
+static int AmiStateInfo(char *buffer);
+
+
+/*** Translations ************************************************************/
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+
+    /*
+     *  Native format
+     */
+
+static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount,
+			 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
+{
+	ssize_t count, used;
+
+	if (!dmasound.soft.stereo) {
+		void *p = &frame[*frameUsed];
+		count = min_t(unsigned long, userCount, frameLeft) & ~1;
+		used = count;
+		if (copy_from_user(p, userPtr, count))
+			return -EFAULT;
+	} else {
+		u_char *left = &frame[*frameUsed>>1];
+		u_char *right = left+write_sq_block_size_half;
+		count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
+		used = count*2;
+		while (count > 0) {
+			if (get_user(*left++, userPtr++)
+			    || get_user(*right++, userPtr++))
+				return -EFAULT;
+			count--;
+		}
+	}
+	*frameUsed += used;
+	return used;
+}
+
+
+    /*
+     *  Copy and convert 8 bit data
+     */
+
+#define GENERATE_AMI_CT8(funcname, convsample)				\
+static ssize_t funcname(const u_char *userPtr, size_t userCount,	\
+			u_char frame[], ssize_t *frameUsed,		\
+			ssize_t frameLeft)				\
+{									\
+	ssize_t count, used;						\
+									\
+	if (!dmasound.soft.stereo) {					\
+		u_char *p = &frame[*frameUsed];				\
+		count = min_t(size_t, userCount, frameLeft) & ~1;	\
+		used = count;						\
+		while (count > 0) {					\
+			u_char data;					\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*p++ = convsample(data);			\
+			count--;					\
+		}							\
+	} else {							\
+		u_char *left = &frame[*frameUsed>>1];			\
+		u_char *right = left+write_sq_block_size_half;		\
+		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
+		used = count*2;						\
+		while (count > 0) {					\
+			u_char data;					\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*left++ = convsample(data);			\
+			if (get_user(data, userPtr++))			\
+				return -EFAULT;				\
+			*right++ = convsample(data);			\
+			count--;					\
+		}							\
+	}								\
+	*frameUsed += used;						\
+	return used;							\
+}
+
+#define AMI_CT_ULAW(x)	(dmasound_ulaw2dma8[(x)])
+#define AMI_CT_ALAW(x)	(dmasound_alaw2dma8[(x)])
+#define AMI_CT_U8(x)	((x) ^ 0x80)
+
+GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
+GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
+GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
+
+
+    /*
+     *  Copy and convert 16 bit data
+     */
+
+#define GENERATE_AMI_CT_16(funcname, convsample)			\
+static ssize_t funcname(const u_char *userPtr, size_t userCount,	\
+			u_char frame[], ssize_t *frameUsed,		\
+			ssize_t frameLeft)				\
+{									\
+	ssize_t count, used;						\
+	u_short data;							\
+									\
+	if (!dmasound.soft.stereo) {					\
+		u_char *high = &frame[*frameUsed>>1];			\
+		u_char *low = high+write_sq_block_size_half;		\
+		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\
+		used = count*2;						\
+		while (count > 0) {					\
+			if (get_user(data, ((u_short *)userPtr)++))	\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*high++ = data>>8;				\
+			*low++ = (data>>2) & 0x3f;			\
+			count--;					\
+		}							\
+	} else {							\
+		u_char *lefth = &frame[*frameUsed>>2];			\
+		u_char *leftl = lefth+write_sq_block_size_quarter;	\
+		u_char *righth = lefth+write_sq_block_size_half;	\
+		u_char *rightl = righth+write_sq_block_size_quarter;	\
+		count = min_t(size_t, userCount, frameLeft)>>2 & ~1;	\
+		used = count*4;						\
+		while (count > 0) {					\
+			if (get_user(data, ((u_short *)userPtr)++))	\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*lefth++ = data>>8;				\
+			*leftl++ = (data>>2) & 0x3f;			\
+			if (get_user(data, ((u_short *)userPtr)++))	\
+				return -EFAULT;				\
+			data = convsample(data);			\
+			*righth++ = data>>8;				\
+			*rightl++ = (data>>2) & 0x3f;			\
+			count--;					\
+		}							\
+	}								\
+	*frameUsed += used;						\
+	return used;							\
+}
+
+#define AMI_CT_S16BE(x)	(x)
+#define AMI_CT_U16BE(x)	((x) ^ 0x8000)
+#define AMI_CT_S16LE(x)	(le2be16((x)))
+#define AMI_CT_U16LE(x)	(le2be16((x)) ^ 0x8000)
+
+GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
+GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
+GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
+GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
+
+
+static TRANS transAmiga = {
+	ct_ulaw:	ami_ct_ulaw,
+	ct_alaw:	ami_ct_alaw,
+	ct_s8:		ami_ct_s8,
+	ct_u8:		ami_ct_u8,
+	ct_s16be:	ami_ct_s16be,
+	ct_u16be:	ami_ct_u16be,
+	ct_s16le:	ami_ct_s16le,
+	ct_u16le:	ami_ct_u16le,
+};
+
+/*** Low level stuff *********************************************************/
+
+
+static void AmiOpen(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void AmiRelease(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static inline void StopDMA(void)
+{
+	custom.aud[0].audvol = custom.aud[1].audvol = 0;
+	custom.aud[2].audvol = custom.aud[3].audvol = 0;
+	custom.dmacon = AMI_AUDIO_OFF;
+	enable_heartbeat();
+}
+
+static void *AmiAlloc(unsigned int size, int flags)
+{
+	return amiga_chip_alloc((long)size, "dmasound [Paula]");
+}
+
+static void AmiFree(void *obj, unsigned int size)
+{
+	amiga_chip_free (obj);
+}
+
+static int __init AmiIrqInit(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+
+	/* Register interrupt handler. */
+	if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
+			AmiInterrupt))
+		return 0;
+	return 1;
+}
+
+#ifdef MODULE
+static void AmiIrqCleanUp(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+	/* release the interrupt */
+	free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
+}
+#endif /* MODULE */
+
+static void AmiSilence(void)
+{
+	/* turn off DMA for audio channels */
+	StopDMA();
+}
+
+
+static void AmiInit(void)
+{
+	int period, i;
+
+	AmiSilence();
+
+	if (dmasound.soft.speed)
+		period = amiga_colorclock/dmasound.soft.speed-1;
+	else
+		period = amiga_audio_min_period;
+	dmasound.hard = dmasound.soft;
+	dmasound.trans_write = &transAmiga;
+
+	if (period < amiga_audio_min_period) {
+		/* we would need to squeeze the sound, but we won't do that */
+		period = amiga_audio_min_period;
+	} else if (period > 65535) {
+		period = 65535;
+	}
+	dmasound.hard.speed = amiga_colorclock/(period+1);
+
+	for (i = 0; i < 4; i++)
+		custom.aud[i].audper = period;
+	amiga_audio_period = period;
+}
+
+
+static int AmiSetFormat(int format)
+{
+	int size;
+
+	/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return dmasound.soft.format;
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+		size = 8;
+		break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+		size = 16;
+		break;
+	default: /* :-) */
+		size = 8;
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = size;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = dmasound.soft.size;
+	}
+	AmiInit();
+
+	return format;
+}
+
+
+#define VOLUME_VOXWARE_TO_AMI(v) \
+	(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
+#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
+
+static int AmiSetVolume(int volume)
+{
+	dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
+	custom.aud[0].audvol = dmasound.volume_left;
+	dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
+	custom.aud[1].audvol = dmasound.volume_right;
+	if (dmasound.hard.size == 16) {
+		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
+			custom.aud[2].audvol = 1;
+			custom.aud[3].audvol = 1;
+		} else {
+			custom.aud[2].audvol = 0;
+			custom.aud[3].audvol = 0;
+		}
+	}
+	return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
+	       (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
+}
+
+static int AmiSetTreble(int treble)
+{
+	dmasound.treble = treble;
+	if (treble < 50)
+		ciaa.pra &= ~0x02;
+	else
+		ciaa.pra |= 0x02;
+	return treble;
+}
+
+
+#define AMI_PLAY_LOADED		1
+#define AMI_PLAY_PLAYING	2
+#define AMI_PLAY_MASK		3
+
+
+static void AmiPlayNextFrame(int index)
+{
+	u_char *start, *ch0, *ch1, *ch2, *ch3;
+	u_long size;
+
+	/* used by AmiPlay() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	size = (write_sq.count == index ? write_sq.rear_size
+					: write_sq.block_size)>>1;
+
+	if (dmasound.hard.stereo) {
+		ch0 = start;
+		ch1 = start+write_sq_block_size_half;
+		size >>= 1;
+	} else {
+		ch0 = start;
+		ch1 = start;
+	}
+
+	disable_heartbeat();
+	custom.aud[0].audvol = dmasound.volume_left;
+	custom.aud[1].audvol = dmasound.volume_right;
+	if (dmasound.hard.size == 8) {
+		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+		custom.aud[0].audlen = size;
+		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+		custom.aud[1].audlen = size;
+		custom.dmacon = AMI_AUDIO_8;
+	} else {
+		size >>= 1;
+		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+		custom.aud[0].audlen = size;
+		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+		custom.aud[1].audlen = size;
+		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
+			/* We can play pseudo 14-bit only with the maximum volume */
+			ch3 = ch0+write_sq_block_size_quarter;
+			ch2 = ch1+write_sq_block_size_quarter;
+			custom.aud[2].audvol = 1;  /* we are being affected by the beeps */
+			custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */
+			custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
+			custom.aud[2].audlen = size;
+			custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
+			custom.aud[3].audlen = size;
+			custom.dmacon = AMI_AUDIO_14;
+		} else {
+			custom.aud[2].audvol = 0;
+			custom.aud[3].audvol = 0;
+			custom.dmacon = AMI_AUDIO_8;
+		}
+	}
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active |= AMI_PLAY_LOADED;
+}
+
+
+static void AmiPlay(void)
+{
+	int minframes = 1;
+
+	custom.intena = IF_AUD0;
+
+	if (write_sq.active & AMI_PLAY_LOADED) {
+		/* There's already a frame loaded */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	if (write_sq.active & AMI_PLAY_PLAYING)
+		/* Increase threshold: frame 1 is already being played */
+		minframes = 2;
+
+	if (write_sq.count < minframes) {
+		/* Nothing to do */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	if (write_sq.count <= minframes &&
+	    write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
+		/* hmmm, the only existing frame is not
+		 * yet filled and we're not syncing?
+		 */
+		custom.intena = IF_SETCLR | IF_AUD0;
+		return;
+	}
+
+	AmiPlayNextFrame(minframes);
+
+	custom.intena = IF_SETCLR | IF_AUD0;
+}
+
+
+static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+	int minframes = 1;
+
+	custom.intena = IF_AUD0;
+
+	if (!write_sq.active) {
+		/* Playing was interrupted and sq_reset() has already cleared
+		 * the sq variables, so better don't do anything here.
+		 */
+		WAKE_UP(write_sq.sync_queue);
+		return;
+	}
+
+	if (write_sq.active & AMI_PLAY_PLAYING) {
+		/* We've just finished a frame */
+		write_sq.count--;
+		WAKE_UP(write_sq.action_queue);
+	}
+
+	if (write_sq.active & AMI_PLAY_LOADED)
+		/* Increase threshold: frame 1 is already being played */
+		minframes = 2;
+
+	/* Shift the flags */
+	write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
+
+	if (!write_sq.active)
+		/* No frame is playing, disable audio DMA */
+		StopDMA();
+
+	custom.intena = IF_SETCLR | IF_AUD0;
+
+	if (write_sq.count >= minframes)
+		/* Try to play the next frame */
+		AmiPlay();
+
+	if (!write_sq.active)
+		/* Nothing to play anymore.
+		   Wake up a process waiting for audio output to drain. */
+		WAKE_UP(write_sq.sync_queue);
+}
+
+/*** Mid level stuff *********************************************************/
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+static void __init AmiMixerInit(void)
+{
+	dmasound.volume_left = 64;
+	dmasound.volume_right = 64;
+	custom.aud[0].audvol = dmasound.volume_left;
+	custom.aud[3].audvol = 1;	/* For pseudo 14bit */
+	custom.aud[1].audvol = dmasound.volume_right;
+	custom.aud[2].audvol = 1;	/* For pseudo 14bit */
+	dmasound.treble = 50;
+}
+
+static int AmiMixerIoctl(u_int cmd, u_long arg)
+{
+	int data;
+	switch (cmd) {
+	    case SOUND_MIXER_READ_DEVMASK:
+		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
+	    case SOUND_MIXER_READ_RECMASK:
+		    return IOCTL_OUT(arg, 0);
+	    case SOUND_MIXER_READ_STEREODEVS:
+		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
+	    case SOUND_MIXER_READ_VOLUME:
+		    return IOCTL_OUT(arg,
+			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
+			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
+	    case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_volume(data));
+	    case SOUND_MIXER_READ_TREBLE:
+		    return IOCTL_OUT(arg, dmasound.treble);
+	    case SOUND_MIXER_WRITE_TREBLE:
+		    IOCTL_IN(arg, data);
+		    return IOCTL_OUT(arg, dmasound_set_treble(data));
+	}
+	return -EINVAL;
+}
+
+
+static void AmiWriteSqSetup(void)
+{
+	write_sq_block_size_half = write_sq.block_size>>1;
+	write_sq_block_size_quarter = write_sq_block_size_half>>1;
+}
+
+
+static int AmiStateInfo(char *buffer)
+{
+	int len = 0;
+	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
+		       dmasound.volume_left);
+	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
+		       dmasound.volume_right);
+	return len;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+
+static MACHINE machAmiga = {
+	name:		"Amiga",
+	name2:		"AMIGA",
+	open:		AmiOpen,
+	release:	AmiRelease,
+	dma_alloc:	AmiAlloc,
+	dma_free:	AmiFree,
+	irqinit:	AmiIrqInit,
+#ifdef MODULE
+	irqcleanup:	AmiIrqCleanUp,
+#endif /* MODULE */
+	init:		AmiInit,
+	silence:	AmiSilence,
+	setFormat:	AmiSetFormat,
+	setVolume:	AmiSetVolume,
+	setTreble:	AmiSetTreble,
+	play:		AmiPlay,
+	mixer_init:	AmiMixerInit,
+	mixer_ioctl:	AmiMixerIoctl,
+	write_sq_setup:	AmiWriteSqSetup,
+	state_info:	AmiStateInfo,
+	min_dsp_speed:	8000
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+int __init dmasound_paula_init(void)
+{
+	int err;
+
+	if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) {
+	    if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40,
+				    "dmasound [Paula]"))
+		return -EBUSY;
+	    dmasound.mach = machAmiga;
+	    err = dmasound_init();
+	    if (err)
+		release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
+	    return err;
+	} else
+	    return -ENODEV;
+}
+
+static void __exit dmasound_paula_cleanup(void)
+{
+	dmasound_deinit();
+	release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
+}
+
+module_init(dmasound_paula_init);
+module_exit(dmasound_paula_cleanup);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/dmasound/dmasound_q40.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_q40.c
--- linux/sound/oss/dmasound/dmasound_q40.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_q40.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,588 @@
+
+/*
+ *  linux/drivers/sound/dmasound/dmasound_q40.c
+ *
+ *  Q40 DMA Sound Driver
+ *
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+
+#include <asm/uaccess.h>
+#include <asm/q40_master.h>
+
+#include "dmasound.h"
+
+
+static int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void Q40Open(void);
+static void Q40Release(void);
+static void *Q40Alloc(unsigned int size, int flags);
+static void Q40Free(void *, unsigned int);
+static int Q40IrqInit(void);
+#ifdef MODULE
+static void Q40IrqCleanUp(void);
+#endif
+static void Q40Silence(void);
+static void Q40Init(void);
+static int Q40SetFormat(int format);
+static int Q40SetVolume(int volume);
+static void Q40PlayNextFrame(int index);
+static void Q40Play(void);
+static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp);
+static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp);
+static void Q40Interrupt(void);
+
+
+/*** Mid level stuff *********************************************************/
+
+
+#if 1
+/* userCount, frameUsed, frameLeft == byte counts */
+static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	while (count > 0) {
+		*p = table[*p]+128;
+		p++;
+		count--;
+	}
+	*frameUsed += used ;
+	return used;
+}
+#else
+static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	u_char val;
+	int stereo = sound.soft.stereo;
+
+
+	frameLeft >>= 1;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(size_t, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = table[data]+128;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = table[data]+128;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 2;
+	return stereo? used * 2: used;
+}
+#endif
+
+#if 1
+static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	while (count > 0) {
+		*p = *p + 128;
+		p++;
+		count--;
+	}
+	*frameUsed += used;
+	return used;
+}
+#else
+static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	u_char val;
+	int stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 1;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(size_t, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = data + 128;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = data + 128;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 2;
+	return stereo? used * 2: used;
+}
+#endif
+
+#if 1
+static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+
+	used = count = min_t(size_t, userCount, frameLeft);
+	if (copy_from_user(p,userPtr,count))
+	  return -EFAULT;
+	*frameUsed += used;
+	return used;
+}
+#else
+static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	u_char val;
+	int stereo = dmasound.soft.stereo;
+
+
+	frameLeft >>= 1;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(size_t, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = data;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = data;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 2;
+	return stereo? used * 2: used;
+}
+#endif
+
+/* a bit too complicated to optimise right now ..*/
+static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned char *table = (unsigned char *)
+		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
+	unsigned int data = expand_data;
+	u_char *p = (u_char *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+ 
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = table[c];
+			data += 0x80;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c ;
+			data += 0x80;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft);
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	u_char *p = (u_char *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c ;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) ;
+	utotal -= userCount;
+	return utotal;
+}
+
+
+static TRANS transQ40Normal = {
+	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transQ40Expanding = {
+	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
+};
+
+
+/*** Low level stuff *********************************************************/
+
+
+static void Q40Open(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void Q40Release(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void *Q40Alloc(unsigned int size, int flags)
+{
+         return kmalloc(size, flags); /* change to vmalloc */
+}
+
+static void Q40Free(void *ptr, unsigned int size)
+{
+	kfree(ptr);
+}
+
+static int __init Q40IrqInit(void)
+{
+	/* Register interrupt handler. */
+	request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
+		    "DMA sound", Q40Interrupt);
+
+	return(1);
+}
+
+
+#ifdef MODULE
+static void Q40IrqCleanUp(void)
+{
+        master_outb(0,SAMPLE_ENABLE_REG);
+	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+}
+#endif /* MODULE */
+
+
+static void Q40Silence(void)
+{
+        master_outb(0,SAMPLE_ENABLE_REG);
+	*DAC_LEFT=*DAC_RIGHT=0;
+}
+
+static char *q40_pp=NULL;
+static unsigned int q40_sc=0;
+
+static void Q40PlayNextFrame(int index)
+{
+	u_char *start;
+	u_long size;
+	u_char speed;
+
+	/* used by Q40Play() if all doubts whether there really is something
+	 * to be played are already wiped out.
+	 */
+	start = write_sq.buffers[write_sq.front];
+	size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
+
+	q40_pp=start;
+	q40_sc=size;
+		
+	write_sq.front = (write_sq.front+1) % write_sq.max_count;
+	write_sq.active++;
+
+	speed=(dmasound.hard.speed==10000 ? 0 : 1);
+
+	master_outb( 0,SAMPLE_ENABLE_REG);
+	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+	if (dmasound.soft.stereo)
+	  	request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
+		    "Q40 sound", Q40Interrupt);
+	  else
+	        request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
+		    "Q40 sound", Q40Interrupt);
+
+	master_outb( speed, SAMPLE_RATE_REG);
+	master_outb( 1,SAMPLE_CLEAR_REG);
+	master_outb( 1,SAMPLE_ENABLE_REG);
+}
+
+static void Q40Play(void)
+{
+        unsigned long flags;
+
+	if (write_sq.active || write_sq.count<=0 ) {
+		/* There's already a frame loaded */
+		return;
+	}
+
+	/* nothing in the queue */
+	if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
+	         /* hmmm, the only existing frame is not
+		  * yet filled and we're not syncing?
+		  */
+	         return;
+	}
+	save_flags(flags); cli();
+	Q40PlayNextFrame(1);
+	restore_flags(flags);
+}
+
+static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+        if (q40_sc>1){
+            *DAC_LEFT=*q40_pp++;
+	    *DAC_RIGHT=*q40_pp++;
+	    q40_sc -=2;
+	    master_outb(1,SAMPLE_CLEAR_REG);
+	}else Q40Interrupt();
+}
+static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+        if (q40_sc>0){
+            *DAC_LEFT=*q40_pp;
+	    *DAC_RIGHT=*q40_pp++;
+	    q40_sc --;
+	    master_outb(1,SAMPLE_CLEAR_REG);	    
+	}else Q40Interrupt();
+}
+static void Q40Interrupt(void)
+{
+	if (!write_sq.active) {
+	          /* playing was interrupted and sq_reset() has already cleared
+		   * the sq variables, so better don't do anything here.
+		   */
+	           WAKE_UP(write_sq.sync_queue);
+		   master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
+		   goto exit;
+	} else write_sq.active=0;
+	write_sq.count--;
+	Q40Play();
+
+	if (q40_sc<2)
+	      { /* there was nothing to play, disable irq */
+		master_outb(0,SAMPLE_ENABLE_REG);
+		*DAC_LEFT=*DAC_RIGHT=0;
+	      }
+	WAKE_UP(write_sq.action_queue);
+
+ exit:
+	master_outb(1,SAMPLE_CLEAR_REG);
+}
+
+
+static void Q40Init(void)
+{
+	int i, idx;
+	const int freq[] = {10000, 20000};
+
+	/* search a frequency that fits into the allowed error range */
+
+	idx = -1;
+	for (i = 0; i < 2; i++)
+		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
+			idx = i;
+
+	dmasound.hard = dmasound.soft;
+	/*sound.hard.stereo=1;*/ /* no longer true */
+	dmasound.hard.size=8;
+
+	if (idx > -1) {
+		dmasound.soft.speed = freq[idx];
+		dmasound.trans_write = &transQ40Normal;
+	} else
+		dmasound.trans_write = &transQ40Expanding;
+
+	Q40Silence();
+
+	if (dmasound.hard.speed > 20000) {
+		/* we would need to squeeze the sound, but we won't do that */
+		dmasound.hard.speed = 20000;
+		dmasound.trans_write = &transQ40Normal;
+	} else if (dmasound.hard.speed > 10000) {
+		dmasound.hard.speed = 20000;
+	} else {
+		dmasound.hard.speed = 10000;
+	}
+	expand_bal = -dmasound.soft.speed;
+}
+
+
+static int Q40SetFormat(int format)
+{
+	/* Q40 sound supports only 8bit modes */
+
+	switch (format) {
+	case AFMT_QUERY:
+		return(dmasound.soft.format);
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_S8:
+	case AFMT_U8:
+		break;
+	default:
+		format = AFMT_S8;
+	}
+
+	dmasound.soft.format = format;
+	dmasound.soft.size = 8;
+	if (dmasound.minDev == SND_DEV_DSP) {
+		dmasound.dsp.format = format;
+		dmasound.dsp.size = 8;
+	}
+	Q40Init();
+
+	return(format);
+}
+
+static int Q40SetVolume(int volume)
+{
+    return 0;
+}
+
+
+/*** Machine definitions *****************************************************/
+
+
+static MACHINE machQ40 = {
+	name:		"Q40",
+	name2:		"Q40",
+	open:		Q40Open,
+	release:	Q40Release,
+	dma_alloc:	Q40Alloc,
+	dma_free:	Q40Free,
+	irqinit:	Q40IrqInit,
+#ifdef MODULE
+	irqcleanup:	Q40IrqCleanUp,
+#endif /* MODULE */
+	init:		Q40Init,
+	silence:	Q40Silence, 
+	setFormat:	Q40SetFormat, 
+	setVolume:	Q40SetVolume,
+	play:		Q40Play
+};
+
+
+/*** Config & Setup **********************************************************/
+
+
+int __init dmasound_q40_init(void)
+{
+	if (MACH_IS_Q40) {
+	    dmasound.mach = machQ40;
+	    return dmasound_init();
+	} else
+	    return -ENODEV;
+}
+
+static void __exit dmasound_q40_cleanup(void)
+{
+	dmasound_deinit();
+}
+
+module_init(dmasound_q40_init);
+module_exit(dmasound_q40_cleanup);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/emu10k1/8010.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/8010.h
--- linux/sound/oss/emu10k1/8010.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/8010.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,624 @@
+/*
+ **********************************************************************
+ *     8010.h
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date		    Author	    Summary of changes
+ *     ----		    ------	    ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999     Alan Cox	    Cleaned of 8bit chars, DOS
+ *					    line endings
+ *     December 8, 1999     Jon Taylor	    Added lots of new register info
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ *
+ **********************************************************************
+ */
+
+
+#ifndef _8010_H
+#define _8010_H
+
+#include <linux/types.h>
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+						/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+						/* be modified independently of each other.	*/
+#define PTR_CHANNELNUM_MASK	0x0000003f	/* For each per-channel register, indicates the	*/
+						/* channel number of the register to be		*/
+						/* accessed.  For non per-channel registers the	*/
+						/* value should be set to zero.			*/
+#define PTR_ADDRESS_MASK	0x07ff0000	/* Register index				*/
+
+#define DATA			0x04		/* Indexed register set data register		*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+						/* Clear pending interrupts by writing a 1 to	*/
+						/* the relevant bits and zero to the other bits	*/
+#define IPR_SAMPLERATETRACKER	0x01000000	/* Sample rate tracker lock status change	*/
+#define IPR_FXDSP		0x00800000	/* Enable FX DSP interrupts			*/
+#define IPR_FORCEINT		0x00400000	/* Force Sound Blaster interrupt		*/
+#define IPR_PCIERROR		0x00200000	/* PCI bus error				*/
+#define IPR_VOLINCR		0x00100000	/* Volume increment button pressed		*/
+#define IPR_VOLDECR		0x00080000	/* Volume decrement button pressed		*/
+#define IPR_MUTE		0x00040000	/* Mute button pressed				*/
+#define IPR_MICBUFFULL		0x00020000	/* Microphone buffer full			*/
+#define IPR_MICBUFHALFFULL	0x00010000	/* Microphone buffer half full			*/
+#define IPR_ADCBUFFULL		0x00008000	/* ADC buffer full				*/
+#define IPR_ADCBUFHALFFULL	0x00004000	/* ADC buffer half full				*/
+#define IPR_EFXBUFFULL		0x00002000	/* Effects buffer full				*/
+#define IPR_EFXBUFHALFFULL	0x00001000	/* Effects buffer half full			*/
+#define IPR_GPSPDIFSTATUSCHANGE	0x00000800	/* GPSPDIF channel status change		*/
+#define IPR_CDROMSTATUSCHANGE	0x00000400	/* CD-ROM channel status change			*/
+#define IPR_INTERVALTIMER	0x00000200	/* Interval timer terminal count		*/
+#define IPR_MIDITRANSBUFEMPTY	0x00000100	/* MIDI UART transmit buffer empty		*/
+#define IPR_MIDIRECVBUFEMPTY	0x00000080	/* MIDI UART receive buffer empty		*/
+#define IPR_CHANNELLOOP		0x00000040	/* One or more channel loop interrupts pending	*/
+#define IPR_CHANNELNUMBERMASK	0x0000003f	/* When IPR_CHANNELLOOP is set, indicates the	*/
+						/* Highest set channel in CLIPL or CLIPH.  When	*/
+						/* IP is written with CL set, the bit in CLIPL	*/
+						/* or CLIPH corresponding to the CIN value 	*/
+						/* written will be cleared.			*/
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+#define INTE_VIRTUALSB_MASK	0xc0000000	/* Virtual Soundblaster I/O port capture	*/
+#define INTE_VIRTUALSB_220	0x00000000	/* Capture at I/O base address 0x220-0x22f	*/
+#define INTE_VIRTUALSB_240	0x40000000	/* Capture at I/O base address 0x240		*/
+#define INTE_VIRTUALSB_260	0x80000000	/* Capture at I/O base address 0x260		*/
+#define INTE_VIRTUALSB_280	0xc0000000	/* Capture at I/O base address 0x280		*/
+#define INTE_VIRTUALMPU_MASK	0x30000000	/* Virtual MPU I/O port capture			*/
+#define INTE_VIRTUALMPU_300	0x00000000	/* Capture at I/O base address 0x300-0x301	*/
+#define INTE_VIRTUALMPU_310	0x10000000	/* Capture at I/O base address 0x310		*/
+#define INTE_VIRTUALMPU_320	0x20000000	/* Capture at I/O base address 0x320		*/
+#define INTE_VIRTUALMPU_330	0x30000000	/* Capture at I/O base address 0x330		*/
+#define INTE_MASTERDMAENABLE	0x08000000	/* Master DMA emulation at 0x000-0x00f		*/
+#define INTE_SLAVEDMAENABLE	0x04000000	/* Slave DMA emulation at 0x0c0-0x0df		*/
+#define INTE_MASTERPICENABLE	0x02000000	/* Master PIC emulation at 0x020-0x021		*/
+#define INTE_SLAVEPICENABLE	0x01000000	/* Slave PIC emulation at 0x0a0-0x0a1		*/
+#define INTE_VSBENABLE		0x00800000	/* Enable virtual Soundblaster			*/
+#define INTE_ADLIBENABLE	0x00400000	/* Enable AdLib emulation at 0x388-0x38b	*/
+#define INTE_MPUENABLE		0x00200000	/* Enable virtual MPU				*/
+#define INTE_FORCEINT		0x00100000	/* Continuously assert INTAN			*/
+
+#define INTE_MRHANDENABLE	0x00080000	/* Enable the "Mr. Hand" logic			*/
+						/* NOTE: There is no reason to use this under	*/
+						/* Linux, and it will cause odd hardware 	*/
+						/* behavior and possibly random segfaults and	*/
+						/* lockups if enabled.				*/
+
+#define INTE_SAMPLERATETRACKER	0x00002000	/* Enable sample rate tracker interrupts	*/
+						/* NOTE: This bit must always be enabled       	*/
+#define INTE_FXDSPENABLE	0x00001000	/* Enable FX DSP interrupts			*/
+#define INTE_PCIERRORENABLE	0x00000800	/* Enable PCI bus error interrupts		*/
+#define INTE_VOLINCRENABLE	0x00000400	/* Enable volume increment button interrupts	*/
+#define INTE_VOLDECRENABLE	0x00000200	/* Enable volume decrement button interrupts	*/
+#define INTE_MUTEENABLE		0x00000100	/* Enable mute button interrupts		*/
+#define INTE_MICBUFENABLE	0x00000080	/* Enable microphone buffer interrupts		*/
+#define INTE_ADCBUFENABLE	0x00000040	/* Enable ADC buffer interrupts			*/
+#define INTE_EFXBUFENABLE	0x00000020	/* Enable Effects buffer interrupts		*/
+#define INTE_GPSPDIFENABLE	0x00000010	/* Enable GPSPDIF status interrupts		*/
+#define INTE_CDSPDIFENABLE	0x00000008	/* Enable CDSPDIF status interrupts		*/
+#define INTE_INTERVALTIMERENB	0x00000004	/* Enable interval timer interrupts		*/
+#define INTE_MIDITXENABLE	0x00000002	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_MIDIRXENABLE	0x00000001	/* Enable MIDI receive-buffer-empty interrupts	*/
+
+#define WC			0x10		/* Wall Clock register				*/
+#define WC_SAMPLECOUNTER_MASK	0x03FFFFC0	/* Sample periods elapsed since reset		*/
+#define WC_SAMPLECOUNTER	0x14060010
+#define WC_CURRENTCHANNEL	0x0000003F	/* Channel [0..63] currently being serviced	*/
+						/* NOTE: Each channel takes 1/64th of a sample	*/
+						/* period to be serviced.			*/
+
+#define HCFG			0x14		/* Hardware config register			*/
+						/* NOTE: There is no reason to use the legacy	*/
+						/* SoundBlaster emulation stuff described below	*/
+						/* under Linux, and all kinds of weird hardware	*/
+						/* behavior can result if you try.  Don't.	*/
+#define HCFG_LEGACYFUNC_MASK	0xe0000000	/* Legacy function number 			*/
+#define HCFG_LEGACYFUNC_MPU	0x00000000	/* Legacy MPU	 				*/
+#define HCFG_LEGACYFUNC_SB	0x40000000	/* Legacy SB					*/
+#define HCFG_LEGACYFUNC_AD	0x60000000	/* Legacy AD					*/
+#define HCFG_LEGACYFUNC_MPIC	0x80000000	/* Legacy MPIC					*/
+#define HCFG_LEGACYFUNC_MDMA	0xa0000000	/* Legacy MDMA					*/
+#define HCFG_LEGACYFUNC_SPCI	0xc0000000	/* Legacy SPCI					*/
+#define HCFG_LEGACYFUNC_SDMA	0xe0000000	/* Legacy SDMA					*/
+#define HCFG_IOCAPTUREADDR	0x1f000000	/* The 4 LSBs of the captured I/O address.	*/
+#define HCFG_LEGACYWRITE	0x00800000	/* 1 = write, 0 = read 				*/
+#define HCFG_LEGACYWORD		0x00400000	/* 1 = word, 0 = byte 				*/
+#define HCFG_LEGACYINT		0x00200000	/* 1 = legacy event captured. Write 1 to clear.	*/
+						/* NOTE: The rest of the bits in this register	*/
+						/* _are_ relevant under Linux.			*/
+#define HCFG_CODECFORMAT_MASK	0x00070000	/* CODEC format					*/
+#define HCFG_CODECFORMAT_AC97	0x00000000	/* AC97 CODEC format -- Primary Output		*/
+#define HCFG_CODECFORMAT_I2S	0x00010000	/* I2S CODEC format -- Secondary (Rear) Output	*/
+#define HCFG_GPINPUT0		0x00004000	/* External pin112				*/
+#define HCFG_GPINPUT1		0x00002000	/* External pin110				*/
+
+#define HCFG_GPOUTPUT_MASK	0x00001c00	/* External pins which may be controlled	*/
+#define HCFG_GPOUT0		0x00001000	/* set to enable digital out on 5.1 cards	*/
+
+#define HCFG_JOYENABLE      	0x00000200	/* Internal joystick enable    			*/
+#define HCFG_PHASETRACKENABLE	0x00000100	/* Phase tracking enable			*/
+						/* 1 = Force all 3 async digital inputs to use	*/
+						/* the same async sample rate tracker (ZVIDEO)	*/
+#define HCFG_AC3ENABLE_MASK	0x0x0000e0	/* AC3 async input control - Not implemented	*/
+#define HCFG_AC3ENABLE_ZVIDEO	0x00000080	/* Channels 0 and 1 replace ZVIDEO		*/
+#define HCFG_AC3ENABLE_CDSPDIF	0x00000040	/* Channels 0 and 1 replace CDSPDIF		*/
+#define HCFG_AC3ENABLE_GPSPDIF  0x00000020      /* Channels 0 and 1 replace GPSPDIF             */
+#define HCFG_AUTOMUTE		0x00000010	/* When set, the async sample rate convertors	*/
+						/* will automatically mute their output when	*/
+						/* they are not rate-locked to the external	*/
+						/* async audio source  				*/
+#define HCFG_LOCKSOUNDCACHE	0x00000008	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCKTANKCACHE_MASK	0x00000004	/* 1 = Cancel bustmaster accesses to tankcache	*/
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCKTANKCACHE	0x01020014
+#define HCFG_MUTEBUTTONENABLE	0x00000002	/* 1 = Master mute button sets AUDIOENABLE = 0.	*/
+						/* NOTE: This is a 'cheap' way to implement a	*/
+						/* master mute function on the mute button, and	*/
+						/* in general should not be used unless a more	*/
+						/* sophisticated master mute function has not	*/
+						/* been written.       				*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+						/* Should be set to 1 when the EMU10K1 is	*/
+						/* completely initialized.			*/
+
+#define MUDATA			0x18		/* MPU401 data register (8 bits)       		*/
+
+#define MUCMD			0x19		/* MPU401 command register (8 bits)    		*/
+#define MUCMD_RESET		0xff		/* RESET command				*/
+#define MUCMD_ENTERUARTMODE	0x3f		/* Enter_UART_mode command			*/
+						/* NOTE: All other commands are ignored		*/
+
+#define MUSTAT			MUCMD		/* MPU401 status register (8 bits)     		*/
+#define MUSTAT_IRDYN		0x80		/* 0 = MIDI data or command ACK			*/
+#define MUSTAT_ORDYN		0x40		/* 0 = MUDATA can accept a command or data	*/
+
+#define TIMER			0x1a		/* Timer terminal count register		*/
+						/* NOTE: After the rate is changed, a maximum	*/
+						/* of 1024 sample periods should be allowed	*/
+						/* before the new rate is guaranteed accurate.	*/
+#define TIMER_RATE_MASK		0x000003ff	/* Timer interrupt rate in sample periods	*/
+						/* 0 == 1024 periods, [1..4] are not useful	*/
+#define TIMER_RATE		0x0a00001a
+
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+#define AC97ADDRESS_READY	0x80		/* Read-only bit, reflects CODEC READY signal	*/
+#define AC97ADDRESS_ADDRESS	0x7f		/* Address of indexed AC97 register		*/
+
+/********************************************************************************************************/
+/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers			*/
+/********************************************************************************************************/
+
+#define CPF			0x00		/* Current pitch and fraction register			*/
+#define CPF_CURRENTPITCH_MASK	0xffff0000	/* Current pitch (linear, 0x4000 == unity pitch shift) 	*/
+#define CPF_CURRENTPITCH	0x10100000
+#define CPF_STEREO_MASK		0x00008000	/* 1 = Even channel interleave, odd channel locked	*/
+#define CPF_STOP_MASK		0x00004000	/* 1 = Current pitch forced to 0			*/
+#define CPF_FRACADDRESS_MASK	0x00003fff	/* Linear fractional address of the current channel	*/
+
+#define PTRX			0x01		/* Pitch target and send A/B amounts register		*/
+#define PTRX_PITCHTARGET_MASK	0xffff0000	/* Pitch target of specified channel			*/
+#define PTRX_PITCHTARGET	0x10100001
+#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00	/* Linear level of channel output sent to FX send bus A	*/
+#define PTRX_FXSENDAMOUNT_A	0x08080001
+#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff	/* Linear level of channel output sent to FX send bus B	*/
+#define PTRX_FXSENDAMOUNT_B	0x08000001
+
+#define CVCF			0x02		/* Current volume and filter cutoff register		*/
+#define CVCF_CURRENTVOL_MASK	0xffff0000	/* Current linear volume of specified channel		*/
+#define CVCF_CURRENTVOL		0x10100002
+#define CVCF_CURRENTFILTER_MASK	0x0000ffff	/* Current filter cutoff frequency of specified channel	*/
+#define CVCF_CURRENTFILTER	0x10000002
+
+#define VTFT			0x03		/* Volume target and filter cutoff target register	*/
+#define VTFT_VOLUMETARGET_MASK	0xffff0000	/* Volume target of specified channel			*/
+#define VTFT_FILTERTARGET_MASK	0x0000ffff	/* Filter cutoff target of specified channel		*/
+
+#define Z1			0x05		/* Filter delay memory 1 register			*/
+
+#define Z2			0x04		/* Filter delay memory 2 register			*/
+
+#define PSST			0x06		/* Send C amount and loop start address register	*/
+#define PSST_FXSENDAMOUNT_C_MASK 0xff000000	/* Linear level of channel output sent to FX send bus C	*/
+
+#define PSST_FXSENDAMOUNT_C	0x08180006
+
+#define PSST_LOOPSTARTADDR_MASK	0x00ffffff	/* Loop start address of the specified channel		*/
+#define PSST_LOOPSTARTADDR	0x18000006
+
+#define DSL			0x07		/* Send D amount and loop start address register	*/
+#define DSL_FXSENDAMOUNT_D_MASK	0xff000000	/* Linear level of channel output sent to FX send bus D	*/
+
+#define DSL_FXSENDAMOUNT_D	0x08180007
+
+#define DSL_LOOPENDADDR_MASK	0x00ffffff	/* Loop end address of the specified channel		*/
+#define DSL_LOOPENDADDR		0x18000007
+
+#define CCCA			0x08		/* Filter Q, interp. ROM, byte size, cur. addr register */
+#define CCCA_RESONANCE		0xf0000000	/* Lowpass filter resonance (Q) height			*/
+#define CCCA_INTERPROMMASK	0x0e000000	/* Selects passband of interpolation ROM		*/
+						/* 1 == full band, 7 == lowpass				*/
+						/* ROM 0 is used when pitch shifting downward or less	*/
+						/* then 3 semitones upward.  Increasingly higher ROM	*/
+						/* numbers are used, typically in steps of 3 semitones,	*/
+						/* as upward pitch shifting is performed.		*/
+#define CCCA_INTERPROM_0	0x00000000	/* Select interpolation ROM 0				*/
+#define CCCA_INTERPROM_1	0x02000000	/* Select interpolation ROM 1				*/
+#define CCCA_INTERPROM_2	0x04000000	/* Select interpolation ROM 2				*/
+#define CCCA_INTERPROM_3	0x06000000	/* Select interpolation ROM 3				*/
+#define CCCA_INTERPROM_4	0x08000000	/* Select interpolation ROM 4				*/
+#define CCCA_INTERPROM_5	0x0a000000	/* Select interpolation ROM 5				*/
+#define CCCA_INTERPROM_6	0x0c000000	/* Select interpolation ROM 6				*/
+#define CCCA_INTERPROM_7	0x0e000000	/* Select interpolation ROM 7				*/
+#define CCCA_8BITSELECT		0x01000000	/* 1 = Sound memory for this channel uses 8-bit samples	*/
+#define CCCA_CURRADDR_MASK	0x00ffffff	/* Current address of the selected channel		*/
+#define CCCA_CURRADDR		0x18000008
+
+#define CCR			0x09		/* Cache control register				*/
+#define CCR_CACHEINVALIDSIZE	0x07190009
+#define CCR_CACHEINVALIDSIZE_MASK	0xfe000000	/* Number of invalid samples cache for this channel    	*/
+#define CCR_CACHELOOPFLAG	0x01000000	/* 1 = Cache has a loop service pending			*/
+#define CCR_INTERLEAVEDSAMPLES	0x00800000	/* 1 = A cache service will fetch interleaved samples	*/
+#define CCR_WORDSIZEDSAMPLES	0x00400000	/* 1 = A cache service will fetch word sized samples	*/
+#define CCR_READADDRESS		0x06100009
+#define CCR_READADDRESS_MASK	0x003f0000	/* Location of cache just beyond current cache service	*/
+#define CCR_LOOPINVALSIZE	0x0000fe00	/* Number of invalid samples in cache prior to loop	*/
+						/* NOTE: This is valid only if CACHELOOPFLAG is set	*/
+#define CCR_LOOPFLAG		0x00000100	/* Set for a single sample period when a loop occurs	*/
+#define CCR_CACHELOOPADDRHI	0x000000ff	/* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set	*/
+
+#define CLP			0x0a		/* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */
+						/* NOTE: This register is normally not used		*/
+#define CLP_CACHELOOPADDR	0x0000ffff	/* Cache loop address (DSL_LOOPSTARTADDR [0..15])	*/
+
+#define FXRT			0x0b		/* Effects send routing register			*/
+						/* NOTE: It is illegal to assign the same routing to	*/
+						/* two effects sends.					*/
+#define FXRT_CHANNELA		0x000f0000	/* Effects send bus number for channel's effects send A	*/
+#define FXRT_CHANNELB		0x00f00000	/* Effects send bus number for channel's effects send B	*/
+#define FXRT_CHANNELC		0x0f000000	/* Effects send bus number for channel's effects send C	*/
+#define FXRT_CHANNELD		0xf0000000	/* Effects send bus number for channel's effects send D	*/
+
+#define MAPA			0x0c		/* Cache map A						*/
+
+#define MAPB			0x0d		/* Cache map B						*/
+
+#define MAP_PTE_MASK		0xffffe000	/* The 19 MSBs of the PTE indexed by the PTI		*/
+#define MAP_PTI_MASK		0x00001fff	/* The 13 bit index to one of the 8192 PTE dwords      	*/
+
+#define ENVVOL			0x10		/* Volume envelope register				*/
+#define ENVVOL_MASK		0x0000ffff	/* Current value of volume envelope state variable	*/  
+						/* 0x8000-n == 666*n usec delay	       			*/
+
+#define ATKHLDV 		0x11		/* Volume envelope hold and attack register		*/
+#define ATKHLDV_PHASE0		0x00008000	/* 0 = Begin attack phase				*/
+#define ATKHLDV_HOLDTIME_MASK	0x00007f00	/* Envelope hold time (127-n == n*88.2msec)		*/
+#define ATKHLDV_ATTACKTIME_MASK	0x0000007f	/* Envelope attack time, log encoded			*/
+						/* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec	*/
+
+#define DCYSUSV 		0x12		/* Volume envelope sustain and decay register		*/
+#define DCYSUSV_PHASE1_MASK	0x00008000	/* 0 = Begin attack phase, 1 = begin release phase	*/
+#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00	/* 127 = full, 0 = off, 0.75dB increments		*/
+#define DCYSUSV_CHANNELENABLE_MASK 0x00000080	/* 1 = Inhibit envelope engine from writing values in	*/
+						/* this channel and from writing to pitch, filter and	*/
+						/* volume targets.					*/
+#define DCYSUSV_DECAYTIME_MASK	0x0000007f	/* Volume envelope decay time, log encoded     		*/
+						/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec		*/
+
+#define LFOVAL1 		0x13		/* Modulation LFO value					*/
+#define LFOVAL_MASK		0x0000ffff	/* Current value of modulation LFO state variable	*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define ENVVAL			0x14		/* Modulation envelope register				*/
+#define ENVVAL_MASK		0x0000ffff	/* Current value of modulation envelope state variable 	*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define ATKHLDM			0x15		/* Modulation envelope hold and attack register		*/
+#define ATKHLDM_PHASE0		0x00008000	/* 0 = Begin attack phase				*/
+#define ATKHLDM_HOLDTIME	0x00007f00	/* Envelope hold time (127-n == n*42msec)		*/
+#define ATKHLDM_ATTACKTIME	0x0000007f	/* Envelope attack time, log encoded			*/
+						/* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec		*/
+
+#define DCYSUSM			0x16		/* Modulation envelope decay and sustain register	*/
+#define DCYSUSM_PHASE1_MASK	0x00008000	/* 0 = Begin attack phase, 1 = begin release phase	*/
+#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00	/* 127 = full, 0 = off, 0.75dB increments		*/
+#define DCYSUSM_DECAYTIME_MASK	0x0000007f	/* Envelope decay time, log encoded			*/
+						/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec		*/
+
+#define LFOVAL2 		0x17		/* Vibrato LFO register					*/
+#define LFOVAL2_MASK		0x0000ffff	/* Current value of vibrato LFO state variable 		*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define IP			0x18		/* Initial pitch register				*/
+#define IP_MASK			0x0000ffff	/* Exponential initial pitch shift			*/
+						/* 4 bits of octave, 12 bits of fractional octave	*/
+#define IP_UNITY		0x0000e000	/* Unity pitch shift					*/
+
+#define IFATN			0x19		/* Initial filter cutoff and attenuation register	*/
+#define IFATN_FILTERCUTOFF_MASK	0x0000ff00	/* Initial filter cutoff frequency in exponential units	*/
+						/* 6 most significant bits are semitones		*/
+						/* 2 least significant bits are fractions		*/
+#define IFATN_FILTERCUTOFF	0x08080019
+#define IFATN_ATTENUATION_MASK	0x000000ff	/* Initial attenuation in 0.375dB steps			*/
+#define IFATN_ATTENUATION	0x08000019
+
+
+#define PEFE			0x1a		/* Pitch envelope and filter envelope amount register	*/
+#define PEFE_PITCHAMOUNT_MASK	0x0000ff00	/* Pitch envlope amount					*/
+						/* Signed 2's complement, +/- one octave peak extremes	*/
+#define PEFE_PITCHAMOUNT	0x0808001a
+#define PEFE_FILTERAMOUNT_MASK	0x000000ff	/* Filter envlope amount				*/
+						/* Signed 2's complement, +/- six octaves peak extremes */
+#define PEFE_FILTERAMOUNT	0x0800001a
+#define FMMOD			0x1b		/* Vibrato/filter modulation from LFO register		*/
+#define FMMOD_MODVIBRATO	0x0000ff00	/* Vibrato LFO modulation depth				*/
+						/* Signed 2's complement, +/- one octave extremes	*/
+#define FMMOD_MOFILTER		0x000000ff	/* Filter LFO modulation depth				*/
+						/* Signed 2's complement, +/- three octave extremes	*/
+
+
+#define TREMFRQ 		0x1c		/* Tremolo amount and modulation LFO frequency register	*/
+#define TREMFRQ_DEPTH		0x0000ff00	/* Tremolo depth					*/
+						/* Signed 2's complement, with +/- 12dB extremes	*/
+
+#define FM2FRQ2 		0x1d		/* Vibrato amount and vibrato LFO frequency register	*/
+#define FM2FRQ2_DEPTH		0x0000ff00	/* Vibrato LFO vibrato depth				*/
+						/* Signed 2's complement, +/- one octave extremes	*/
+#define FM2FRQ2_FREQUENCY	0x000000ff	/* Vibrato LFO frequency				*/
+						/* 0.039Hz steps, maximum of 9.85 Hz.			*/
+
+#define TEMPENV 		0x1e		/* Tempory envelope register				*/
+#define TEMPENV_MASK		0x0000ffff	/* 16-bit value						*/
+						/* NOTE: All channels contain internal variables; do	*/
+						/* not write to these locations.			*/
+
+#define CD0			0x20		/* Cache data 0 register				*/
+#define CD1			0x21		/* Cache data 1 register				*/
+#define CD2			0x22		/* Cache data 2 register				*/
+#define CD3			0x23		/* Cache data 3 register				*/
+#define CD4			0x24		/* Cache data 4 register				*/
+#define CD5			0x25		/* Cache data 5 register				*/
+#define CD6			0x26		/* Cache data 6 register				*/
+#define CD7			0x27		/* Cache data 7 register				*/
+#define CD8			0x28		/* Cache data 8 register				*/
+#define CD9			0x29		/* Cache data 9 register				*/
+#define CDA			0x2a		/* Cache data A register				*/
+#define CDB			0x2b		/* Cache data B register				*/
+#define CDC			0x2c		/* Cache data C register				*/
+#define CDD			0x2d		/* Cache data D register				*/
+#define CDE			0x2e		/* Cache data E register				*/
+#define CDF			0x2f		/* Cache data F register				*/
+
+#define PTB			0x40		/* Page table base register				*/
+#define PTB_MASK		0xfffff000	/* Physical address of the page table in host memory	*/
+
+#define TCB			0x41		/* Tank cache base register    				*/
+#define TCB_MASK		0xfffff000	/* Physical address of the bottom of host based TRAM	*/
+
+#define ADCCR			0x42		/* ADC sample rate/stereo control register		*/
+#define ADCCR_RCHANENABLE	0x00000010	/* Enables right channel for writing to the host       	*/
+#define ADCCR_LCHANENABLE	0x00000008	/* Enables left channel for writing to the host		*/
+						/* NOTE: To guarantee phase coherency, both channels	*/
+						/* must be disabled prior to enabling both channels.	*/
+#define ADCCR_SAMPLERATE_MASK	0x00000007	/* Sample rate convertor output rate			*/
+#define ADCCR_SAMPLERATE_48	0x00000000	/* 48kHz sample rate					*/
+#define ADCCR_SAMPLERATE_44	0x00000001	/* 44.1kHz sample rate					*/
+#define ADCCR_SAMPLERATE_32	0x00000002	/* 32kHz sample rate					*/
+#define ADCCR_SAMPLERATE_24	0x00000003	/* 24kHz sample rate					*/
+#define ADCCR_SAMPLERATE_22	0x00000004	/* 22.05kHz sample rate					*/
+#define ADCCR_SAMPLERATE_16	0x00000005	/* 16kHz sample rate					*/
+#define ADCCR_SAMPLERATE_11	0x00000006	/* 11.025kHz sample rate				*/
+#define ADCCR_SAMPLERATE_8	0x00000007	/* 8kHz sample rate					*/
+
+#define FXWC			0x43		/* FX output write channels register			*/
+						/* When set, each bit enables the writing of the	*/
+						/* corresponding FX output channel into host memory	*/
+
+#define TCBS			0x44		/* Tank cache buffer size register			*/
+#define TCBS_MASK		0x00000007	/* Tank cache buffer size field				*/
+#define TCBS_BUFFSIZE_16K	0x00000000
+#define TCBS_BUFFSIZE_32K	0x00000001
+#define TCBS_BUFFSIZE_64K	0x00000002
+#define TCBS_BUFFSIZE_128K	0x00000003
+#define TCBS_BUFFSIZE_256K	0x00000004
+#define TCBS_BUFFSIZE_512K	0x00000005
+#define TCBS_BUFFSIZE_1024K	0x00000006
+#define TCBS_BUFFSIZE_2048K	0x00000007
+
+#define MICBA			0x45		/* AC97 microphone buffer address register		*/
+#define MICBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define ADCBA			0x46		/* ADC buffer address register				*/
+#define ADCBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define FXBA			0x47		/* FX Buffer Address */
+#define FXBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define MICBS			0x49		/* Microphone buffer size register			*/
+
+#define ADCBS			0x4a		/* ADC buffer size register				*/
+
+#define FXBS			0x4b		/* FX buffer size register				*/
+
+/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */
+#define ADCBS_BUFSIZE_NONE	0x00000000
+#define ADCBS_BUFSIZE_384	0x00000001
+#define ADCBS_BUFSIZE_448	0x00000002
+#define ADCBS_BUFSIZE_512	0x00000003
+#define ADCBS_BUFSIZE_640	0x00000004
+#define ADCBS_BUFSIZE_768	0x00000005
+#define ADCBS_BUFSIZE_896	0x00000006
+#define ADCBS_BUFSIZE_1024	0x00000007
+#define ADCBS_BUFSIZE_1280	0x00000008
+#define ADCBS_BUFSIZE_1536	0x00000009
+#define ADCBS_BUFSIZE_1792	0x0000000a
+#define ADCBS_BUFSIZE_2048	0x0000000b
+#define ADCBS_BUFSIZE_2560	0x0000000c
+#define ADCBS_BUFSIZE_3072	0x0000000d
+#define ADCBS_BUFSIZE_3584	0x0000000e
+#define ADCBS_BUFSIZE_4096	0x0000000f
+#define ADCBS_BUFSIZE_5120	0x00000010
+#define ADCBS_BUFSIZE_6144	0x00000011
+#define ADCBS_BUFSIZE_7168	0x00000012
+#define ADCBS_BUFSIZE_8192	0x00000013
+#define ADCBS_BUFSIZE_10240	0x00000014
+#define ADCBS_BUFSIZE_12288	0x00000015
+#define ADCBS_BUFSIZE_14366	0x00000016
+#define ADCBS_BUFSIZE_16384	0x00000017
+#define ADCBS_BUFSIZE_20480	0x00000018
+#define ADCBS_BUFSIZE_24576	0x00000019
+#define ADCBS_BUFSIZE_28672	0x0000001a
+#define ADCBS_BUFSIZE_32768	0x0000001b
+#define ADCBS_BUFSIZE_40960	0x0000001c
+#define ADCBS_BUFSIZE_49152	0x0000001d
+#define ADCBS_BUFSIZE_57344	0x0000001e
+#define ADCBS_BUFSIZE_65536	0x0000001f
+
+
+#define CDCS			0x50		/* CD-ROM digital channel status register	*/
+
+#define GPSCS			0x51		/* General Purpose SPDIF channel status register*/
+
+#define DBG			0x52		/* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+
+/* definitions for debug register - taken from the alsa drivers */
+#define DBG_ZC                  0x80000000      /* zero tram counter */
+#define DBG_SATURATION_OCCURED  0x02000000      /* saturation control */
+#define DBG_SATURATION_ADDR     0x01ff0000      /* saturation address */
+#define DBG_SINGLE_STEP         0x00008000      /* single step mode */
+#define DBG_STEP                0x00004000      /* start single step */
+#define DBG_CONDITION_CODE      0x00003e00      /* condition code */
+#define DBG_SINGLE_STEP_ADDR    0x000001ff      /* single step address */
+
+
+#define REG53			0x53		/* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+
+#define SPCS0			0x54		/* SPDIF output Channel Status 0 register	*/
+
+#define SPCS1			0x55		/* SPDIF output Channel Status 1 register	*/
+
+#define SPCS2			0x56		/* SPDIF output Channel Status 2 register	*/
+
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status      		*/
+#define CLIEL			0x58		/* Channel loop interrupt enable low register	*/
+
+#define CLIEH			0x59		/* Channel loop interrupt enable high register	*/
+
+#define CLIPL			0x5a		/* Channel loop interrupt pending low register	*/
+
+#define CLIPH			0x5b		/* Channel loop interrupt pending high register	*/
+
+#define SOLEL			0x5c		/* Stop on loop enable low register		*/
+
+#define SOLEH			0x5d		/* Stop on loop enable high register		*/
+
+#define SPBYPASS		0x5e		/* SPDIF BYPASS mode register			*/
+#define SPBYPASS_ENABLE		0x00000001	/* Enable SPDIF bypass mode			*/
+
+#define AC97SLOT		0x5f		/* additional AC97 slots enable bits */
+#define AC97SLOT_CNTR		0x10		/* Center enable */
+#define AC97SLOT_LFE		0x20		/* LFE enable */
+
+#define CDSRCS			0x60		/* CD-ROM Sample Rate Converter status register	*/
+
+#define GPSRCS			0x61		/* General Purpose SPDIF sample rate cvt status */
+
+#define ZVSRCS			0x62		/* ZVideo sample rate converter status		*/
+						/* NOTE: This one has no SPDIFLOCKED field	*/
+						/* Assumes sample lock				*/
+
+/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS.			*/
+#define SRCS_SPDIFLOCKED	0x02000000	/* SPDIF stream locked				*/
+#define SRCS_RATELOCKED		0x01000000	/* Sample rate locked				*/
+#define SRCS_ESTSAMPLERATE	0x0007ffff	/* Do not modify this field.			*/
+
+#define MICIDX                  0x63            /* Microphone recording buffer index register   */
+#define MICIDX_MASK             0x0000ffff      /* 16-bit value                                 */
+#define MICIDX_IDX		0x10000063
+
+#define ADCIDX			0x64		/* ADC recording buffer index register		*/
+#define ADCIDX_MASK		0x0000ffff	/* 16 bit index field				*/
+#define ADCIDX_IDX		0x10000064
+
+#define FXIDX			0x65		/* FX recording buffer index register		*/
+#define FXIDX_MASK		0x0000ffff	/* 16-bit value					*/
+#define FXIDX_IDX		0x10000065
+
+/* Each FX general purpose register is 32 bits in length, all bits are used			*/
+#define FXGPREGBASE		0x100		/* FX general purpose registers base       	*/
+
+/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is	*/
+/* decompressed back to 20 bits on a read.  There are a total of 160 locations, the last 32	*/
+/* locations are for external TRAM. 								*/
+#define TANKMEMDATAREGBASE	0x200		/* Tank memory data registers base     		*/
+#define TANKMEMDATAREG_MASK	0x000fffff	/* 20 bit tank audio data field			*/
+
+/* Combined address field and memory opcode or flag field.  160 locations, last 32 are external	*/
+#define TANKMEMADDRREGBASE	0x300		/* Tank memory address registers base		*/
+#define TANKMEMADDRREG_ADDR_MASK 0x000fffff	/* 20 bit tank address field			*/
+#define TANKMEMADDRREG_CLEAR	0x00800000	/* Clear tank memory				*/
+#define TANKMEMADDRREG_ALIGN	0x00400000	/* Align read or write relative to tank access	*/
+#define TANKMEMADDRREG_WRITE	0x00200000	/* Write to tank memory				*/
+#define TANKMEMADDRREG_READ	0x00100000	/* Read from tank memory			*/
+
+#define MICROCODEBASE		0x400		/* Microcode data base address			*/
+
+/* Each DSP microcode instruction is mapped into 2 doublewords 					*/
+/* NOTE: When writing, always write the LO doubleword first.  Reads can be in either order.	*/
+#define LOWORD_OPX_MASK		0x000ffc00	/* Instruction operand X			*/
+#define LOWORD_OPY_MASK		0x000003ff	/* Instruction operand Y			*/
+#define HIWORD_OPCODE_MASK	0x00f00000	/* Instruction opcode				*/
+#define HIWORD_RESULT_MASK	0x000ffc00	/* Instruction result				*/
+#define HIWORD_OPA_MASK		0x000003ff	/* Instruction operand A			*/
+
+#endif /* _8010_H */
diff -Nru linux/sound/oss/emu10k1/Makefile linux-2.4.19-pre5-mjc/sound/oss/emu10k1/Makefile
--- linux/sound/oss/emu10k1/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/Makefile	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,23 @@
+# Makefile for Creative Labs EMU10K1 
+#
+# 12 Apr 2000 Rui Sousa
+
+O_TARGET := emu10k1.o
+
+obj-y :=     audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \
+             efxmgr.o emuadxmg.o hwaccess.o irqmgr.o main.o midi.o \
+             mixer.o passthrough.o recmgr.o timer.o voicemgr.o
+obj-m := $(O_TARGET)
+
+ifdef DEBUG
+    EXTRA_CFLAGS += -DEMU10K1_DEBUG
+endif
+
+ifdef CONFIG_MIDI_EMU10K1
+    EXTRA_CFLAGS += -DEMU10K1_SEQUENCER
+endif
+
+include $(TOPDIR)/Rules.make
+
+clean:
+	rm -f core *.o *.a *.s
diff -Nru linux/sound/oss/emu10k1/audio.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.c
--- linux/sound/oss/emu10k1/audio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1570 @@
+/*
+ **********************************************************************
+ *     audio.c -- /dev/dsp interface for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999	    Alan Cox        cleaned up types/leaks
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+
+
+#include "hwaccess.h"
+#include "cardwo.h"
+#include "cardwi.h"
+#include "recmgr.h"
+#include "irqmgr.h"
+#include "audio.h"
+#include "8010.h"
+
+static void calculate_ofrag(struct woinst *);
+static void calculate_ifrag(struct wiinst *);
+
+/* Audio file operations */
+static ssize_t emu10k1_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	ssize_t ret = 0;
+	unsigned long flags;
+
+	DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count);
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+
+	spin_lock_irqsave(&wiinst->lock, flags);
+
+	if (wiinst->mmapped) {
+		spin_unlock_irqrestore(&wiinst->lock, flags);
+		return -ENXIO;
+	}
+
+	if (wiinst->state == WAVE_STATE_CLOSED) {
+		calculate_ifrag(wiinst);
+
+		while (emu10k1_wavein_open(wave_dev) < 0) {
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			interruptible_sleep_on(&wave_dev->card->open_wait);
+
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			spin_lock_irqsave(&wiinst->lock, flags);
+		}
+	}
+
+	spin_unlock_irqrestore(&wiinst->lock, flags);
+
+	while (count > 0) {
+		u32 bytestocopy;
+
+		spin_lock_irqsave(&wiinst->lock, flags);
+
+		if (!(wiinst->state & WAVE_STATE_STARTED)
+		    && (wave_dev->enablebits & PCM_ENABLE_INPUT))
+			emu10k1_wavein_start(wave_dev);
+
+		emu10k1_wavein_update(wave_dev->card, wiinst);
+		emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
+
+		spin_unlock_irqrestore(&wiinst->lock, flags);
+
+		DPD(3, "bytestocopy --> %d\n", bytestocopy);
+
+		if ((bytestocopy >= wiinst->buffer.fragment_size)
+		    || (bytestocopy >= count)) {
+			bytestocopy = min_t(u32, bytestocopy, count);
+
+			emu10k1_wavein_xferdata(wiinst, (u8 *) buffer, &bytestocopy);
+
+			count -= bytestocopy;
+			buffer += bytestocopy;
+			ret += bytestocopy;
+		}
+
+		if (count > 0) {
+			if ((file->f_flags & O_NONBLOCK)
+			    || (!(wave_dev->enablebits & PCM_ENABLE_INPUT)))
+				return (ret ? ret : -EAGAIN);
+
+			interruptible_sleep_on(&wiinst->wait_queue);
+
+			if (signal_pending(current))
+				return (ret ? ret : -ERESTARTSYS);
+
+		}
+	}
+
+	DPD(3, "bytes copied -> %d\n", (u32) ret);
+
+	return ret;
+}
+
+static ssize_t emu10k1_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	struct woinst *woinst = wave_dev->woinst;
+	ssize_t ret;
+	unsigned long flags;
+
+	DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count);
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+
+	spin_lock_irqsave(&woinst->lock, flags);
+
+	if (woinst->mmapped) {
+		spin_unlock_irqrestore(&woinst->lock, flags);
+		return -ENXIO;
+	}
+
+	if (woinst->format.passthrough) {
+		int r;
+		
+		woinst->buffer.ossfragshift = PT_BLOCKSIZE_LOG2;
+		woinst->buffer.numfrags = PT_BLOCKCOUNT;
+		calculate_ofrag(woinst);
+		
+		r = emu10k1_pt_write(file, buffer, count);
+		spin_unlock_irqrestore(&woinst->lock, flags);
+		return r;
+	}
+
+	if (woinst->state == WAVE_STATE_CLOSED) {
+		calculate_ofrag(woinst);
+
+		while (emu10k1_waveout_open(wave_dev) < 0) {
+			spin_unlock_irqrestore(&woinst->lock, flags);
+
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			interruptible_sleep_on(&wave_dev->card->open_wait);
+
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			spin_lock_irqsave(&woinst->lock, flags);
+		}
+	}
+
+	spin_unlock_irqrestore(&woinst->lock, flags);
+
+	ret = 0;
+	if (count % woinst->format.bytespersample)
+                return -EINVAL;
+
+	count /= woinst->num_voices;
+
+	while (count > 0) {
+		u32 bytestocopy;
+
+		spin_lock_irqsave(&woinst->lock, flags);
+		emu10k1_waveout_update(woinst);
+		emu10k1_waveout_getxfersize(woinst, &bytestocopy);
+		spin_unlock_irqrestore(&woinst->lock, flags);
+
+		DPD(3, "bytestocopy --> %d\n", bytestocopy);
+
+		if ((bytestocopy >= woinst->buffer.fragment_size)
+		    || (bytestocopy >= count)) {
+
+			bytestocopy = min_t(u32, bytestocopy, count);
+
+			emu10k1_waveout_xferdata(woinst, (u8 *) buffer, &bytestocopy);
+
+			count -= bytestocopy;
+			buffer += bytestocopy * woinst->num_voices;
+			ret += bytestocopy * woinst->num_voices;
+
+			spin_lock_irqsave(&woinst->lock, flags);
+			woinst->total_copied += bytestocopy;
+
+			if (!(woinst->state & WAVE_STATE_STARTED)
+			    && (wave_dev->enablebits & PCM_ENABLE_OUTPUT)
+			    && (woinst->total_copied >= woinst->buffer.fragment_size))
+				emu10k1_waveout_start(wave_dev);
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+		}
+
+		if (count > 0) {
+			if ((file->f_flags & O_NONBLOCK)
+			    || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT)))
+				return (ret ? ret : -EAGAIN);
+
+			interruptible_sleep_on(&woinst->wait_queue);
+
+			if (signal_pending(current))
+				return (ret ? ret : -ERESTARTSYS);
+		}
+	}
+
+	DPD(3, "bytes copied -> %d\n", (u32) ret);
+
+	return ret;
+}
+
+static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	struct woinst *woinst = NULL;
+	struct wiinst *wiinst = NULL;
+	int val = 0;
+	u32 bytestocopy;
+	unsigned long flags;
+
+	DPF(4, "emu10k1_audio_ioctl()\n");
+
+	if (file->f_mode & FMODE_WRITE)
+		woinst = wave_dev->woinst;
+
+	if (file->f_mode & FMODE_READ)
+		wiinst = wave_dev->wiinst;
+
+	switch (cmd) {
+	case OSS_GETVERSION:
+		DPF(2, "OSS_GETVERSION:\n");
+		return put_user(SOUND_VERSION, (int *) arg);
+
+	case SNDCTL_DSP_RESET:
+		DPF(2, "SNDCTL_DSP_RESET:\n");
+		wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;
+
+		if (file->f_mode & FMODE_WRITE) {
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			if (woinst->state & WAVE_STATE_OPEN) {
+				emu10k1_waveout_close(wave_dev);
+			}
+
+			woinst->mmapped = 0;
+			woinst->total_copied = 0;
+			woinst->total_played = 0;
+			woinst->blocks = 0;
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+		}
+
+		if (file->f_mode & FMODE_READ) {
+			spin_lock_irqsave(&wiinst->lock, flags);
+
+			if (wiinst->state & WAVE_STATE_OPEN) {
+				emu10k1_wavein_close(wave_dev);
+			}
+
+			wiinst->mmapped = 0;
+			wiinst->total_recorded = 0;
+			wiinst->blocks = 0;
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+		}
+
+		break;
+
+	case SNDCTL_DSP_SYNC:
+		DPF(2, "SNDCTL_DSP_SYNC:\n");
+
+		if (file->f_mode & FMODE_WRITE) {
+
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			if (woinst->state & WAVE_STATE_OPEN) {
+
+				if (woinst->state & WAVE_STATE_STARTED)
+					while ((woinst->total_played < woinst->total_copied)
+					       && !signal_pending(current)) {
+						spin_unlock_irqrestore(&woinst->lock, flags);
+						interruptible_sleep_on(&woinst->wait_queue);
+						spin_lock_irqsave(&woinst->lock, flags);
+					}
+				emu10k1_waveout_close(wave_dev);
+			}
+
+			woinst->mmapped = 0;
+			woinst->total_copied = 0;
+			woinst->total_played = 0;
+			woinst->blocks = 0;
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+		}
+
+		if (file->f_mode & FMODE_READ) {
+			spin_lock_irqsave(&wiinst->lock, flags);
+
+			if (wiinst->state & WAVE_STATE_OPEN) {
+				emu10k1_wavein_close(wave_dev);
+			}
+
+			wiinst->mmapped = 0;
+			wiinst->total_recorded = 0;
+			wiinst->blocks = 0;
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+		}
+
+		break;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		DPF(2, "SNDCTL_DSP_SETDUPLEX:\n");
+		break;
+
+	case SNDCTL_DSP_GETCAPS:
+		DPF(2, "SNDCTL_DSP_GETCAPS:\n");
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_COPROC, (int *) arg);
+
+	case SNDCTL_DSP_SPEED:
+		DPF(2, "SNDCTL_DSP_SPEED:\n");
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
+		DPD(2, "val is %d\n", val);
+
+		if (val > 0) {
+			if (file->f_mode & FMODE_READ) {
+				struct wave_format format;
+
+				spin_lock_irqsave(&wiinst->lock, flags);
+
+				format = wiinst->format;
+				format.samplingrate = val;
+
+				if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+					spin_unlock_irqrestore(&wiinst->lock, flags);
+					return -EINVAL;
+				}
+
+				val = wiinst->format.samplingrate;
+
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+
+				DPD(2, "set recording sampling rate -> %d\n", val);
+			}
+
+			if (file->f_mode & FMODE_WRITE) {
+				struct wave_format format;
+
+				spin_lock_irqsave(&woinst->lock, flags);
+
+				format = woinst->format;
+				format.samplingrate = val;
+
+				if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+					spin_unlock_irqrestore(&woinst->lock, flags);
+					return -EINVAL;
+				}
+
+				val = woinst->format.samplingrate;
+
+				spin_unlock_irqrestore(&woinst->lock, flags);
+
+				DPD(2, "set playback sampling rate -> %d\n", val);
+			}
+
+			return put_user(val, (int *) arg);
+		} else {
+			if (file->f_mode & FMODE_READ)
+				val = wiinst->format.samplingrate;
+			else if (file->f_mode & FMODE_WRITE)
+				val = woinst->format.samplingrate;
+
+			return put_user(val, (int *) arg);
+		}
+		break;
+
+	case SNDCTL_DSP_STEREO:
+		DPF(2, "SNDCTL_DSP_STEREO:\n");
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
+		DPD(2, " val is %d\n", val);
+
+		if (file->f_mode & FMODE_READ) {
+			struct wave_format format;
+
+			spin_lock_irqsave(&wiinst->lock, flags);
+
+			format = wiinst->format;
+			format.channels = val ? 2 : 1;
+
+			if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+				return -EINVAL;
+			}
+
+			val = wiinst->format.channels - 1;
+
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+			DPD(2, "set recording stereo -> %d\n", val);
+		}
+
+		if (file->f_mode & FMODE_WRITE) {
+			struct wave_format format;
+
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			format = woinst->format;
+			format.channels = val ? 2 : 1;
+
+			if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+				spin_unlock_irqrestore(&woinst->lock, flags);
+				return -EINVAL;
+			}
+
+			val = woinst->format.channels - 1;
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+
+			DPD(2, "set playback stereo -> %d\n", val);
+		}
+
+		return put_user(val, (int *) arg);
+
+		break;
+
+	case SNDCTL_DSP_CHANNELS:
+		DPF(2, "SNDCTL_DSP_CHANNELS:\n");
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
+		DPD(2, " val is %d\n", val);
+
+		if (val > 0) {
+			if (file->f_mode & FMODE_READ) {
+				struct wave_format format;
+
+				spin_lock_irqsave(&wiinst->lock, flags);
+
+				format = wiinst->format;
+				format.channels = val;
+
+				if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+					spin_unlock_irqrestore(&wiinst->lock, flags);
+					return -EINVAL;
+				}
+				val = wiinst->format.channels;
+
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+				DPD(2, "set recording number of channels -> %d\n", val);
+			}
+
+			if (file->f_mode & FMODE_WRITE) {
+				struct wave_format format;
+
+				spin_lock_irqsave(&woinst->lock, flags);
+
+				format = woinst->format;
+				format.channels = val;
+
+				if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+					spin_unlock_irqrestore(&woinst->lock, flags);
+					return -EINVAL;
+				}
+
+				val = woinst->format.channels;
+
+				spin_unlock_irqrestore(&woinst->lock, flags);
+				DPD(2, "set playback number of channels -> %d\n", val);
+			}
+
+			return put_user(val, (int *) arg);
+		} else {
+			if (file->f_mode & FMODE_READ)
+				val = wiinst->format.channels;
+			else if (file->f_mode & FMODE_WRITE)
+				val = woinst->format.channels;
+
+			return put_user(val, (int *) arg);
+		}
+		break;
+
+	case SNDCTL_DSP_GETFMTS:
+		DPF(2, "SNDCTL_DSP_GETFMTS:\n");
+
+		if (file->f_mode & FMODE_READ)
+			val = AFMT_S16_LE;
+		else if (file->f_mode & FMODE_WRITE) {
+			val = AFMT_S16_LE | AFMT_U8;
+			if (emu10k1_find_control_gpr(&wave_dev->card->mgr,
+			    			     wave_dev->card->pt.patch_name, 
+			    			     wave_dev->card->pt.enable_gpr_name) >= 0)
+				val |= AFMT_AC3;
+		}
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_SETFMT:	/* Same as SNDCTL_DSP_SAMPLESIZE */
+		DPF(2, "SNDCTL_DSP_SETFMT:\n");
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
+		DPD(2, " val is %d\n", val);
+
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				struct wave_format format;
+
+				spin_lock_irqsave(&wiinst->lock, flags);
+
+				format = wiinst->format;
+				format.id = val;
+
+				if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
+					spin_unlock_irqrestore(&wiinst->lock, flags);
+					return -EINVAL;
+				}
+
+				val = wiinst->format.id;
+
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+				DPD(2, "set recording format -> %d\n", val);
+			}
+
+			if (file->f_mode & FMODE_WRITE) {
+				struct wave_format format;
+
+				spin_lock_irqsave(&woinst->lock, flags);
+
+				format = woinst->format;
+				format.id = val;
+
+				if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
+					spin_unlock_irqrestore(&woinst->lock, flags);
+					return -EINVAL;
+				}
+
+				val = woinst->format.id;
+
+				spin_unlock_irqrestore(&woinst->lock, flags);
+				DPD(2, "set playback format -> %d\n", val);
+			}
+
+			return put_user(val, (int *) arg);
+		} else {
+			if (file->f_mode & FMODE_READ)
+				val = wiinst->format.id;
+			else if (file->f_mode & FMODE_WRITE)
+				val = woinst->format.id;
+
+			return put_user(val, (int *) arg);
+		}
+		break;
+
+	case SOUND_PCM_READ_BITS:
+
+		if (file->f_mode & FMODE_READ)
+			val = wiinst->format.bitsperchannel;
+		else if (file->f_mode & FMODE_WRITE)
+			val = woinst->format.bitsperchannel;
+
+		return put_user(val, (int *) arg);
+
+	case SOUND_PCM_READ_RATE:
+
+		if (file->f_mode & FMODE_READ)
+			val = wiinst->format.samplingrate;
+		else if (file->f_mode & FMODE_WRITE)
+			val = woinst->format.samplingrate;
+
+		return put_user(val, (int *) arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+
+		if (file->f_mode & FMODE_READ)
+			val = wiinst->format.channels;
+		else if (file->f_mode & FMODE_WRITE)
+			val = woinst->format.channels;
+
+		return put_user(val, (int *) arg);
+
+	case SOUND_PCM_WRITE_FILTER:
+		DPF(2, "SOUND_PCM_WRITE_FILTER: not implemented\n");
+		break;
+
+	case SOUND_PCM_READ_FILTER:
+		DPF(2, "SOUND_PCM_READ_FILTER: not implemented\n");
+		break;
+
+	case SNDCTL_DSP_SETSYNCRO:
+		DPF(2, "SNDCTL_DSP_SETSYNCRO: not implemented\n");
+		break;
+
+	case SNDCTL_DSP_GETTRIGGER:
+		DPF(2, "SNDCTL_DSP_GETTRIGGER:\n");
+
+		if (file->f_mode & FMODE_WRITE && (wave_dev->enablebits & PCM_ENABLE_OUTPUT))
+			val |= PCM_ENABLE_OUTPUT;
+
+		if (file->f_mode & FMODE_READ && (wave_dev->enablebits & PCM_ENABLE_INPUT))
+			val |= PCM_ENABLE_INPUT;
+
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		DPF(2, "SNDCTL_DSP_SETTRIGGER:\n");
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
+		if (file->f_mode & FMODE_WRITE) {
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			if (val & PCM_ENABLE_OUTPUT) {
+				wave_dev->enablebits |= PCM_ENABLE_OUTPUT;
+				if (woinst->state & WAVE_STATE_OPEN)
+					emu10k1_waveout_start(wave_dev);
+			} else {
+				wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT;
+				if (woinst->state & WAVE_STATE_STARTED)
+					emu10k1_waveout_stop(wave_dev);
+			}
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+		}
+
+		if (file->f_mode & FMODE_READ) {
+			spin_lock_irqsave(&wiinst->lock, flags);
+
+			if (val & PCM_ENABLE_INPUT) {
+				wave_dev->enablebits |= PCM_ENABLE_INPUT;
+				if (wiinst->state & WAVE_STATE_OPEN)
+					emu10k1_wavein_start(wave_dev);
+			} else {
+				wave_dev->enablebits &= ~PCM_ENABLE_INPUT;
+				if (wiinst->state & WAVE_STATE_STARTED)
+					emu10k1_wavein_stop(wave_dev);
+			}
+
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+		}
+		break;
+
+	case SNDCTL_DSP_GETOSPACE:
+		{
+			audio_buf_info info;
+
+			DPF(4, "SNDCTL_DSP_GETOSPACE:\n");
+
+			if (!(file->f_mode & FMODE_WRITE))
+				return -EINVAL;
+
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			if (woinst->state & WAVE_STATE_OPEN) {
+				emu10k1_waveout_update(woinst);
+				emu10k1_waveout_getxfersize(woinst, &bytestocopy);
+				info.bytes = bytestocopy;
+			} else {
+				calculate_ofrag(woinst);
+				info.bytes = woinst->buffer.size;
+			}
+			spin_unlock_irqrestore(&woinst->lock, flags);
+
+			info.bytes *= woinst->num_voices;
+			info.fragsize = woinst->buffer.fragment_size * woinst->num_voices;
+			info.fragstotal = woinst->buffer.numfrags * woinst->num_voices;
+			info.fragments = info.bytes / info.fragsize;
+
+			if (copy_to_user((int *) arg, &info, sizeof(info)))
+				return -EFAULT;
+		}
+		break;
+
+	case SNDCTL_DSP_GETISPACE:
+		{
+			audio_buf_info info;
+
+			DPF(4, "SNDCTL_DSP_GETISPACE:\n");
+
+			if (!(file->f_mode & FMODE_READ))
+				return -EINVAL;
+
+			spin_lock_irqsave(&wiinst->lock, flags);
+			if (wiinst->state & WAVE_STATE_OPEN) {
+				emu10k1_wavein_update(wave_dev->card, wiinst);
+				emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
+				info.bytes = bytestocopy;
+			} else {
+				calculate_ifrag(wiinst);
+				info.bytes = 0;
+			}
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+
+			info.fragstotal = wiinst->buffer.numfrags;
+			info.fragments = info.bytes / wiinst->buffer.fragment_size;
+			info.fragsize = wiinst->buffer.fragment_size;
+
+			if (copy_to_user((int *) arg, &info, sizeof(info)))
+				return -EFAULT;
+		}
+		break;
+
+	case SNDCTL_DSP_NONBLOCK:
+		DPF(2, "SNDCTL_DSP_NONBLOCK:\n");
+
+		file->f_flags |= O_NONBLOCK;
+		break;
+
+	case SNDCTL_DSP_GETODELAY:
+		DPF(4, "SNDCTL_DSP_GETODELAY:\n");
+
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+
+		spin_lock_irqsave(&woinst->lock, flags);
+		if (woinst->state & WAVE_STATE_OPEN) {
+			emu10k1_waveout_update(woinst);
+			emu10k1_waveout_getxfersize(woinst, &bytestocopy);
+			val = woinst->buffer.size - bytestocopy;
+		} else
+			val = 0;
+
+		val *= woinst->num_voices;
+		spin_unlock_irqrestore(&woinst->lock, flags);
+
+		return put_user(val, (int *) arg);
+
+	case SNDCTL_DSP_GETIPTR:
+		{
+			count_info cinfo;
+
+			DPF(4, "SNDCTL_DSP_GETIPTR: \n");
+
+			if (!(file->f_mode & FMODE_READ))
+				return -EINVAL;
+
+			spin_lock_irqsave(&wiinst->lock, flags);
+
+			if (wiinst->state & WAVE_STATE_OPEN) {
+				emu10k1_wavein_update(wave_dev->card, wiinst);
+				cinfo.ptr = wiinst->buffer.hw_pos;
+				cinfo.bytes = cinfo.ptr + wiinst->total_recorded - wiinst->total_recorded % wiinst->buffer.size;
+				cinfo.blocks = cinfo.bytes / wiinst->buffer.fragment_size - wiinst->blocks;
+				wiinst->blocks = cinfo.bytes / wiinst->buffer.fragment_size;
+			} else {
+				cinfo.ptr = 0;
+				cinfo.bytes = 0;
+				cinfo.blocks = 0;
+			}
+
+			if(wiinst->mmapped)
+				wiinst->buffer.bytestocopy %= wiinst->buffer.fragment_size;
+
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+
+			if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+		}
+		break;
+
+	case SNDCTL_DSP_GETOPTR:
+		{
+			count_info cinfo;
+
+			DPF(4, "SNDCTL_DSP_GETOPTR:\n");
+
+			if (!(file->f_mode & FMODE_WRITE))
+				return -EINVAL;
+
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			if (woinst->state & WAVE_STATE_OPEN || 
+			    (woinst->format.passthrough && wave_dev->card->pt.state)) {
+				int num_fragments;
+				if (woinst->format.passthrough) {
+					emu10k1_pt_waveout_update(wave_dev);
+					cinfo.bytes = woinst->total_played;
+				} else {
+					emu10k1_waveout_update(woinst);
+					cinfo.bytes = woinst->total_played;
+				}
+				cinfo.ptr = woinst->buffer.hw_pos;
+				num_fragments = cinfo.bytes / woinst->buffer.fragment_size;
+				cinfo.blocks = num_fragments - woinst->blocks;
+				woinst->blocks = num_fragments;
+
+				cinfo.bytes *= woinst->num_voices;
+				cinfo.ptr *= woinst->num_voices;
+			} else {
+				cinfo.ptr = 0;
+				cinfo.bytes = 0;
+				cinfo.blocks = 0;
+			}
+
+			if (woinst->mmapped)
+				woinst->buffer.free_bytes %= woinst->buffer.fragment_size;
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+
+			if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+		}
+		break;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		DPF(2, "SNDCTL_DSP_GETBLKSIZE:\n");
+
+		if (file->f_mode & FMODE_WRITE) {
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			calculate_ofrag(woinst);
+			val = woinst->buffer.fragment_size * woinst->num_voices;
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+		}
+
+		if (file->f_mode & FMODE_READ) {
+			spin_lock_irqsave(&wiinst->lock, flags);
+
+			calculate_ifrag(wiinst);
+			val = wiinst->buffer.fragment_size;
+
+			spin_unlock_irqrestore(&wiinst->lock, flags);
+		}
+
+		return put_user(val, (int *) arg);
+
+		break;
+
+	case SNDCTL_DSP_POST:
+		if (file->f_mode & FMODE_WRITE) {
+			spin_lock_irqsave(&woinst->lock, flags);
+
+			if (!(woinst->state & WAVE_STATE_STARTED)
+			    && (wave_dev->enablebits & PCM_ENABLE_OUTPUT)
+			    && (woinst->total_copied > 0))
+				emu10k1_waveout_start(wave_dev);
+
+			spin_unlock_irqrestore(&woinst->lock, flags);
+		}
+
+		break;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		DPF(2, "SNDCTL_DSP_SUBDIVIDE: not implemented\n");
+		break;
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n");
+
+		if (get_user(val, (int *) arg))
+			return -EFAULT;
+
+		DPD(2, "val is %#x\n", val);
+
+		if (val == 0)
+			return -EIO;
+
+		if (file->f_mode & FMODE_WRITE) {
+			/* digital pass-through fragment count and size are fixed values */
+			if (woinst->state & WAVE_STATE_OPEN || woinst->format.passthrough)
+				return -EINVAL;	/* too late to change */
+
+			woinst->buffer.ossfragshift = val & 0xffff;
+			woinst->buffer.numfrags = (val >> 16) & 0xffff;
+		}
+
+		if (file->f_mode & FMODE_READ) {
+			if (wiinst->state & WAVE_STATE_OPEN)
+				return -EINVAL;	/* too late to change */
+
+			wiinst->buffer.ossfragshift = val & 0xffff;
+			wiinst->buffer.numfrags = (val >> 16) & 0xffff;
+		}
+
+		break;
+
+	case SNDCTL_COPR_LOAD:
+		{
+			copr_buffer *buf;
+			u32 i;
+
+			DPF(4, "SNDCTL_COPR_LOAD:\n");
+
+			buf = kmalloc(sizeof(copr_buffer), GFP_KERNEL);
+			if (!buf)
+				return -ENOMEM;
+
+			if (copy_from_user(buf, (copr_buffer *) arg, sizeof(copr_buffer))) {
+				kfree (buf);
+				return -EFAULT;
+			}
+
+			if ((buf->command != CMD_READ) && (buf->command != CMD_WRITE)) {
+				kfree (buf);
+				return -EINVAL;
+			}
+#ifdef DBGEMU
+			if ( (buf->offs < 0) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) {
+#else
+			if ( ((buf->offs < 0x100 ) || (buf->offs + buf->len > 0x800) || (buf->len > 1000))
+			     && !( ( buf->offs == DBG) && (buf->len ==1) )){
+#endif	
+				kfree(buf);
+				return -EINVAL;
+			}
+
+			if (buf->command == CMD_READ) {
+				for (i = 0; i < buf->len; i++)
+					((u32 *) buf->data)[i] = sblive_readptr(wave_dev->card, buf->offs + i, 0);
+
+				if (copy_to_user((copr_buffer *) arg, buf, sizeof(copr_buffer))) {
+					kfree(buf);
+					return -EFAULT;
+				}
+			} else {
+				for (i = 0; i < buf->len; i++)
+					sblive_writeptr(wave_dev->card, buf->offs + i, 0, ((u32 *) buf->data)[i]);
+			}
+
+			kfree (buf);
+			break;
+		}
+
+	default:		/* Default is unrecognized command */
+		DPD(2, "default: %#x\n", cmd);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct page *emu10k1_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access)
+{
+	struct emu10k1_wavedevice *wave_dev = vma->vm_private_data;
+	struct woinst *woinst = wave_dev->woinst;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	struct page *dmapage;
+	unsigned long pgoff;
+	int rd, wr;
+
+	DPF(4, "emu10k1_mm_nopage()\n");
+	DPD(4, "addr: %#lx\n", address);
+
+	if (address > vma->vm_end) {
+		DPF(2, "EXIT, returning NOPAGE_SIGBUS\n");
+		return NOPAGE_SIGBUS; /* Disallow mremap */
+	}
+
+	pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT);
+	if (woinst != NULL)
+		wr = woinst->mmapped;
+	else
+		wr = 0;
+
+	if (wiinst != NULL)
+		rd = wiinst->mmapped;
+	else
+		rd = 0;
+
+	/* if full-duplex (read+write) and we have two sets of bufs,
+	* then the playback buffers come first, sez soundcard.c */
+	if (wr) {
+		if (pgoff >= woinst->buffer.pages) {
+			pgoff -= woinst->buffer.pages;
+			dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE);
+		} else
+			dmapage = virt_to_page (woinst->buffer.mem[0].addr[pgoff]);
+	} else {
+		dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE);
+	}
+
+	get_page (dmapage);
+
+	DPD(4, "page: %#lx\n", dmapage);
+	return dmapage;
+}
+
+struct vm_operations_struct emu10k1_mm_ops = {
+	nopage:         emu10k1_mm_nopage,
+};
+
+static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	unsigned long max_pages, n_pages, pgoffset;
+	struct woinst *woinst = NULL;
+	struct wiinst *wiinst = NULL;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_audio_mmap()\n");
+
+	max_pages = 0;
+	if (vma->vm_flags & VM_WRITE) {
+		woinst = wave_dev->woinst;
+
+		spin_lock_irqsave(&woinst->lock, flags);
+
+		/* No m'mapping possible for multichannel */
+		if (woinst->num_voices > 1) {
+			spin_unlock_irqrestore(&woinst->lock, flags);
+                	return -EINVAL;
+		}
+
+		if (woinst->state == WAVE_STATE_CLOSED) {
+			calculate_ofrag(woinst);
+
+			if (emu10k1_waveout_open(wave_dev) < 0) {
+				spin_unlock_irqrestore(&woinst->lock, flags);
+				ERROR();
+				return -EINVAL;
+			}
+		}
+
+		woinst->mmapped = 1;
+		max_pages += woinst->buffer.pages;
+		spin_unlock_irqrestore(&woinst->lock, flags);
+	}
+
+	if (vma->vm_flags & VM_READ) {
+		wiinst = wave_dev->wiinst;
+
+		spin_lock_irqsave(&wiinst->lock, flags);
+		if (wiinst->state == WAVE_STATE_CLOSED) {
+			calculate_ifrag(wiinst);
+
+			if (emu10k1_wavein_open(wave_dev) < 0) {
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+				ERROR();
+				return -EINVAL;
+			}
+		}
+
+		wiinst->mmapped = 1;
+		max_pages += wiinst->buffer.pages;
+		spin_unlock_irqrestore(&wiinst->lock, flags);
+	}
+
+	n_pages = ((vma->vm_end - vma->vm_start) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	pgoffset = vma->vm_pgoff;
+
+	DPD(3, "vma_start: %#lx, vma_end: %#lx, vma_offset: %d\n", vma->vm_start, vma->vm_end, pgoffset);
+	DPD(3, "n_pages: %d, max_pages: %d\n", n_pages, max_pages);
+
+	if (pgoffset + n_pages > max_pages)
+		return -EINVAL;
+
+	vma->vm_flags |= VM_RESERVED;
+	vma->vm_ops = &emu10k1_mm_ops;
+	vma->vm_private_data = wave_dev;
+
+	return 0;
+}
+
+static int emu10k1_audio_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct emu10k1_card *card = NULL;
+	struct list_head *entry;
+	struct emu10k1_wavedevice *wave_dev;
+
+	DPF(2, "emu10k1_audio_open()\n");
+
+	/* Check for correct device to open */
+
+	list_for_each(entry, &emu10k1_devs) {
+		card = list_entry(entry, struct emu10k1_card, list);
+
+		if (!((card->audio_dev ^ minor) & ~0xf) || !((card->audio_dev1 ^ minor) & ~0xf))
+			goto match;
+	}
+
+	return -ENODEV;
+
+match:
+
+	wave_dev = (struct emu10k1_wavedevice *) kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL);
+
+	if (wave_dev == NULL) { 
+		ERROR();
+		return -ENOMEM;
+	}
+
+	wave_dev->card = card;
+	wave_dev->wiinst = NULL;
+	wave_dev->woinst = NULL;
+	wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT;	/* Default */
+
+	if (file->f_mode & FMODE_READ) {
+		/* Recording */
+		struct wiinst *wiinst;
+
+		if ((wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL)) == NULL) {
+			ERROR();
+			return -ENODEV;
+		}
+
+		wiinst->recsrc = card->wavein.recsrc;
+                wiinst->fxwc = card->wavein.fxwc;
+
+		switch (wiinst->recsrc) {
+		case WAVERECORD_AC97:
+			wiinst->format.id = AFMT_S16_LE;
+			wiinst->format.samplingrate = 8000;
+			wiinst->format.bitsperchannel = 16;
+			wiinst->format.channels = 1;
+			break;
+		case WAVERECORD_MIC:
+			wiinst->format.id = AFMT_S16_LE;
+			wiinst->format.samplingrate = 8000;
+			wiinst->format.bitsperchannel = 16;
+			wiinst->format.channels = 1;
+			break;
+		case WAVERECORD_FX:
+			wiinst->format.id = AFMT_S16_LE;
+			wiinst->format.samplingrate = 48000;
+			wiinst->format.bitsperchannel = 16;
+			wiinst->format.channels = hweight32(wiinst->fxwc);
+			break;
+		default:
+			BUG();
+			break;
+		}
+
+		wiinst->state = WAVE_STATE_CLOSED;
+
+		wiinst->buffer.ossfragshift = 0;
+		wiinst->buffer.fragment_size = 0;
+		wiinst->buffer.numfrags = 0;
+
+		init_waitqueue_head(&wiinst->wait_queue);
+
+		wiinst->mmapped = 0;
+		wiinst->total_recorded = 0;
+		wiinst->blocks = 0;
+		wiinst->lock = SPIN_LOCK_UNLOCKED;
+		tasklet_init(&wiinst->timer.tasklet, emu10k1_wavein_bh, (unsigned long) wave_dev);
+		wave_dev->wiinst = wiinst;
+		emu10k1_wavein_setformat(wave_dev, &wiinst->format);
+	}
+
+	if (file->f_mode & FMODE_WRITE) {
+		struct woinst *woinst;
+		int i;
+
+		if ((woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL)) == NULL) {
+			ERROR();
+			return -ENODEV;
+		}
+
+		if (wave_dev->wiinst != NULL) {
+			woinst->format = wave_dev->wiinst->format;
+		} else {
+			woinst->format.id = AFMT_U8;
+			woinst->format.samplingrate = 8000;
+			woinst->format.bitsperchannel = 8;
+			woinst->format.channels = 1;
+		}
+
+		woinst->state = WAVE_STATE_CLOSED;
+
+		woinst->buffer.fragment_size = 0;
+		woinst->buffer.ossfragshift = 0;
+		woinst->buffer.numfrags = 0;
+		woinst->device = (card->audio_dev1 == minor);
+		woinst->timer.state = TIMER_STATE_UNINSTALLED;
+		woinst->num_voices = 1;
+		for (i = 0; i < WAVEOUT_MAXVOICES; i++) {
+			woinst->voice[i].usage = VOICE_USAGE_FREE;
+			woinst->buffer.mem[i].emupageindex = -1;
+		}
+
+		init_waitqueue_head(&woinst->wait_queue);
+
+		woinst->mmapped = 0;
+		woinst->total_copied = 0;
+		woinst->total_played = 0;
+		woinst->blocks = 0;
+		woinst->lock = SPIN_LOCK_UNLOCKED;
+		tasklet_init(&woinst->timer.tasklet, emu10k1_waveout_bh, (unsigned long) wave_dev);
+		wave_dev->woinst = woinst;
+		emu10k1_waveout_setformat(wave_dev, &woinst->format);
+	}
+
+	file->private_data = (void *) wave_dev;
+
+	return 0;
+}
+
+static int emu10k1_audio_release(struct inode *inode, struct file *file)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	struct emu10k1_card *card;
+	unsigned long flags;
+
+	card = wave_dev->card;
+
+	DPF(2, "emu10k1_audio_release()\n");
+
+	if (file->f_mode & FMODE_WRITE) {
+		struct woinst *woinst = wave_dev->woinst;
+
+		spin_lock_irqsave(&woinst->lock, flags);
+
+		if (woinst->format.passthrough && card->pt.state != PT_STATE_INACTIVE) {
+			spin_lock(&card->pt.lock);
+                        emu10k1_pt_stop(card);
+			spin_unlock(&card->pt.lock);
+		}
+		if (woinst->state & WAVE_STATE_OPEN) {
+			if (woinst->state & WAVE_STATE_STARTED) {
+				if (!(file->f_flags & O_NONBLOCK)) {
+					while (!signal_pending(current)
+					       && (woinst->total_played < woinst->total_copied)) {
+						DPF(4, "Buffer hasn't been totally played, sleep....\n");
+						spin_unlock_irqrestore(&woinst->lock, flags);
+						interruptible_sleep_on(&woinst->wait_queue);
+						spin_lock_irqsave(&woinst->lock, flags);
+					}
+				}
+			}
+			emu10k1_waveout_close(wave_dev);
+		}
+
+		spin_unlock_irqrestore(&woinst->lock, flags);
+		/* remove the tasklet */
+		tasklet_kill(&woinst->timer.tasklet);
+		kfree(wave_dev->woinst);
+	}
+
+	if (file->f_mode & FMODE_READ) {
+		struct wiinst *wiinst = wave_dev->wiinst;
+
+		spin_lock_irqsave(&wiinst->lock, flags);
+
+		if (wiinst->state & WAVE_STATE_OPEN) {
+			emu10k1_wavein_close(wave_dev);
+		}
+
+		spin_unlock_irqrestore(&wiinst->lock, flags);
+		tasklet_kill(&wiinst->timer.tasklet);
+		kfree(wave_dev->wiinst);
+	}
+
+	kfree(wave_dev);
+
+	if (waitqueue_active(&card->open_wait))
+		wake_up_interruptible(&card->open_wait);
+
+	return 0;
+}
+
+/* FIXME sort out poll() + mmap() */
+static unsigned int emu10k1_audio_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	struct woinst *woinst = wave_dev->woinst;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	unsigned int mask = 0;
+	u32 bytestocopy;
+	unsigned long flags;
+
+	DPF(4, "emu10k1_audio_poll()\n");
+
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &woinst->wait_queue, wait);
+
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &wiinst->wait_queue, wait);
+
+	if (file->f_mode & FMODE_WRITE) {
+		spin_lock_irqsave(&woinst->lock, flags);
+
+		if (woinst->state & WAVE_STATE_OPEN) {
+			emu10k1_waveout_update(woinst);
+			emu10k1_waveout_getxfersize(woinst, &bytestocopy);
+
+			if (bytestocopy >= woinst->buffer.fragment_size)
+				mask |= POLLOUT | POLLWRNORM;
+		} else
+			mask |= POLLOUT | POLLWRNORM;
+
+		spin_unlock_irqrestore(&woinst->lock, flags);
+	}
+
+	if (file->f_mode & FMODE_READ) {
+		spin_lock_irqsave(&wiinst->lock, flags);
+
+		if (wiinst->state == WAVE_STATE_CLOSED) {
+			calculate_ifrag(wiinst);
+			if (emu10k1_wavein_open(wave_dev) < 0) {
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+				return (mask |= POLLERR);
+			}
+		}
+
+		if (!(wiinst->state & WAVE_STATE_STARTED)) {
+			wave_dev->enablebits |= PCM_ENABLE_INPUT;
+			emu10k1_wavein_start(wave_dev);
+		}
+		emu10k1_wavein_update(wave_dev->card, wiinst);
+		emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
+
+		if (bytestocopy >= wiinst->buffer.fragment_size)
+			mask |= POLLIN | POLLRDNORM;
+
+		spin_unlock_irqrestore(&wiinst->lock, flags);
+	}
+
+	return mask;
+}
+
+static void calculate_ofrag(struct woinst *woinst)
+{
+	struct waveout_buffer *buffer = &woinst->buffer;
+	u32 fragsize;
+
+	if (buffer->fragment_size)
+		return;
+
+	if (!buffer->ossfragshift) {
+		fragsize = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1;
+
+		while (fragsize) {
+			fragsize >>= 1;
+			buffer->ossfragshift++;
+		}
+	}
+
+	if (buffer->ossfragshift < WAVEOUT_MINFRAGSHIFT)
+		buffer->ossfragshift = WAVEOUT_MINFRAGSHIFT;
+
+	buffer->fragment_size = 1 << buffer->ossfragshift;
+
+	if (!buffer->numfrags) {
+		u32 numfrags;
+
+		numfrags = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTBUFLEN) /
+			   (buffer->fragment_size * 1000) - 1;
+
+		buffer->numfrags = 1;
+
+		while (numfrags) {
+			numfrags >>= 1;
+			buffer->numfrags <<= 1;
+		}
+	}
+
+	if (buffer->numfrags < MINFRAGS)
+		buffer->numfrags = MINFRAGS;
+
+	if (buffer->numfrags * buffer->fragment_size > WAVEOUT_MAXBUFSIZE) {
+		buffer->numfrags = WAVEOUT_MAXBUFSIZE / buffer->fragment_size;
+
+		if (buffer->numfrags < MINFRAGS) {
+			buffer->numfrags = MINFRAGS;
+			buffer->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS;
+		}
+
+	} else if (buffer->numfrags * buffer->fragment_size < WAVEOUT_MINBUFSIZE)
+		buffer->numfrags = WAVEOUT_MINBUFSIZE / buffer->fragment_size;
+
+	buffer->size = buffer->fragment_size * buffer->numfrags;
+	buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0);
+
+	DPD(2, " calculated playback fragment_size -> %d\n", buffer->fragment_size);
+	DPD(2, " calculated playback numfrags -> %d\n", buffer->numfrags);
+
+	return;
+}
+
+static void calculate_ifrag(struct wiinst *wiinst)
+{
+	struct wavein_buffer *buffer = &wiinst->buffer;
+	u32 fragsize, bufsize, size[4];
+	int i, j;
+
+	if (buffer->fragment_size)
+		return;
+
+	if (!buffer->ossfragshift) {
+		fragsize = (wiinst->format.bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1;
+
+		while (fragsize) {
+			fragsize >>= 1;
+			buffer->ossfragshift++;
+		}
+	}
+
+	if (buffer->ossfragshift < WAVEIN_MINFRAGSHIFT)
+		buffer->ossfragshift = WAVEIN_MINFRAGSHIFT;
+
+	buffer->fragment_size = 1 << buffer->ossfragshift;
+
+	if (!buffer->numfrags)
+		buffer->numfrags = (wiinst->format.bytespersec * WAVEIN_DEFAULTBUFLEN) / (buffer->fragment_size * 1000) - 1;
+
+	if (buffer->numfrags < MINFRAGS)
+		buffer->numfrags = MINFRAGS;
+
+	if (buffer->numfrags * buffer->fragment_size > WAVEIN_MAXBUFSIZE) {
+		buffer->numfrags = WAVEIN_MAXBUFSIZE / buffer->fragment_size;
+
+		if (buffer->numfrags < MINFRAGS) {
+			buffer->numfrags = MINFRAGS;
+			buffer->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS;
+		}
+	} else if (buffer->numfrags * buffer->fragment_size < WAVEIN_MINBUFSIZE)
+		buffer->numfrags = WAVEIN_MINBUFSIZE / buffer->fragment_size;
+
+	bufsize = buffer->fragment_size * buffer->numfrags;
+
+	if (bufsize >= 0x10000) {
+		buffer->size = 0x10000;
+		buffer->sizeregval = 0x1f;
+	} else {
+		buffer->size = 0;
+		size[0] = 384;
+		size[1] = 448;
+		size[2] = 512;
+		size[3] = 640;
+
+		for (i = 0; i < 8; i++)
+			for (j = 0; j < 4; j++)
+				if (bufsize >= size[j]) {
+					buffer->size = size[j];
+					size[j] *= 2;
+					buffer->sizeregval = i * 4 + j + 1;
+				} else
+					goto exitloop;
+	      exitloop:
+		if (buffer->size == 0) {
+			buffer->size = 384;
+			buffer->sizeregval = 0x01;
+		}
+	}
+
+	buffer->numfrags = buffer->size / buffer->fragment_size;
+	buffer->pages =  buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0);
+	if (buffer->size % buffer->fragment_size)
+		BUG();
+
+	DPD(2, " calculated recording fragment_size -> %d\n", buffer->fragment_size);
+	DPD(2, " calculated recording numfrags -> %d\n", buffer->numfrags);
+	DPD(2, " buffer size register -> %#04x\n", buffer->sizeregval);
+
+	return;
+}
+
+void emu10k1_wavein_bh(unsigned long refdata)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	u32 bytestocopy;
+	unsigned long flags;
+
+	if (!wiinst)
+		return;
+
+	spin_lock_irqsave(&wiinst->lock, flags);
+
+	if (!(wiinst->state & WAVE_STATE_STARTED)) {
+		spin_unlock_irqrestore(&wiinst->lock, flags);
+		return;
+	}
+
+	emu10k1_wavein_update(wave_dev->card, wiinst);
+	emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
+
+	spin_unlock_irqrestore(&wiinst->lock, flags);
+
+	if (bytestocopy >= wiinst->buffer.fragment_size) {
+	 	if (waitqueue_active(&wiinst->wait_queue))
+			wake_up_interruptible(&wiinst->wait_queue);
+	} else
+		DPD(3, "Not enough transfer size, %d\n", bytestocopy);
+
+	return;
+}
+
+void emu10k1_waveout_bh(unsigned long refdata)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata;
+	struct woinst *woinst = wave_dev->woinst;
+	u32 bytestocopy;
+	unsigned long flags;
+
+	if (!woinst)
+		return;
+
+	spin_lock_irqsave(&woinst->lock, flags);
+
+	if (!(woinst->state & WAVE_STATE_STARTED)) {
+		spin_unlock_irqrestore(&woinst->lock, flags);
+		return;
+	}
+
+	emu10k1_waveout_update(woinst);
+	emu10k1_waveout_getxfersize(woinst, &bytestocopy);
+
+	if (woinst->buffer.fill_silence) {
+		spin_unlock_irqrestore(&woinst->lock, flags);
+		emu10k1_waveout_fillsilence(woinst);
+	} else
+		spin_unlock_irqrestore(&woinst->lock, flags);
+
+	if (bytestocopy >= woinst->buffer.fragment_size) {
+		if (waitqueue_active(&woinst->wait_queue))
+			wake_up_interruptible(&woinst->wait_queue);
+	} else
+		DPD(3, "Not enough transfer size -> %d\n", bytestocopy);
+
+	return;
+}
+
+struct file_operations emu10k1_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		emu10k1_audio_read,
+	write:		emu10k1_audio_write,
+	poll:		emu10k1_audio_poll,
+	ioctl:		emu10k1_audio_ioctl,
+	mmap:		emu10k1_audio_mmap,
+	open:		emu10k1_audio_open,
+	release:	emu10k1_audio_release,
+};
diff -Nru linux/sound/oss/emu10k1/audio.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.h
--- linux/sound/oss/emu10k1/audio.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,49 @@
+/*
+ **********************************************************************
+ *     audio.c -- /dev/dsp interface for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999	    Alan Cox        cleaned up types/leaks
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _AUDIO_H
+#define _AUDIO_H
+
+#define MINFRAGS	2	/* _don't_ got bellow 2 */
+
+struct emu10k1_wavedevice
+{
+        struct emu10k1_card *card;
+        struct wiinst *wiinst;
+        struct woinst *woinst;
+        u16 enablebits;
+};
+
+void emu10k1_waveout_bh(unsigned long);
+void emu10k1_wavein_bh(unsigned long);
+
+#endif /* _AUDIO_H */
diff -Nru linux/sound/oss/emu10k1/cardmi.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.c
--- linux/sound/oss/emu10k1/cardmi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,817 @@
+/*
+ **********************************************************************
+ *     sblive_mi.c - MIDI UART input HAL for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999     Alan Cox        clean up
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "cardmi.h"
+#include "irqmgr.h"
+
+static struct {
+	int (*Fn) (struct emu10k1_mpuin *, u8);
+} midistatefn[] = {
+
+	{
+	sblive_miStateParse}, {
+	sblive_miState3Byte},	/* 0x8n, 0x9n, 0xAn, 0xBn, 0xEn */
+	{
+	sblive_miState3ByteKey},	/* Byte 1                       */
+	{
+	sblive_miState3ByteVel},	/* Byte 2                       */
+	{
+	sblive_miState2Byte},	/* 0xCn, 0xDn                   */
+	{
+	sblive_miState2ByteKey},	/* Byte 1                       */
+	{
+	sblive_miStateSysCommon2},	/* 0xF1 , 0xF3                  */
+	{
+	sblive_miStateSysCommon2Key},	/* 0xF1 , 0xF3, Byte 1          */
+	{
+	sblive_miStateSysCommon3},	/* 0xF2                         */
+	{
+	sblive_miStateSysCommon3Key},	/* 0xF2 , Byte 1                */
+	{
+	sblive_miStateSysCommon3Vel},	/* 0xF2 , Byte 2                */
+	{
+	sblive_miStateSysExNorm},	/* 0xF0, 0xF7, Normal mode      */
+	{
+	sblive_miStateSysReal}	/* 0xF4 - 0xF6 ,0xF8 - 0xFF     */
+};
+
+/* Installs the IRQ handler for the MPU in port                 */
+
+/* and initialize parameters                                    */
+
+int emu10k1_mpuin_open(struct emu10k1_card *card, struct midi_openinfo *openinfo)
+{
+	struct emu10k1_mpuin *card_mpuin = card->mpuin;
+
+	DPF(2, "emu10k1_mpuin_open\n");
+
+	if (!(card_mpuin->status & FLAGS_AVAILABLE))
+		return -1;
+
+	/* Copy open info and mark channel as in use */
+	card_mpuin->openinfo = *openinfo;
+	card_mpuin->status &= ~FLAGS_AVAILABLE;	/* clear */
+	card_mpuin->status |= FLAGS_READY;	/* set */
+	card_mpuin->status &= ~FLAGS_MIDM_STARTED;	/* clear */
+	card_mpuin->firstmidiq = NULL;
+	card_mpuin->lastmidiq = NULL;
+	card_mpuin->qhead = 0;
+	card_mpuin->qtail = 0;
+
+	sblive_miStateInit(card_mpuin);
+
+	emu10k1_mpu_reset(card);
+	emu10k1_mpu_acquire(card);
+
+	return 0;
+}
+
+int emu10k1_mpuin_close(struct emu10k1_card *card)
+{
+	struct emu10k1_mpuin *card_mpuin = card->mpuin;
+
+	DPF(2, "emu10k1_mpuin_close()\n");
+
+	/* Check if there are pending input SysEx buffers */
+	if (card_mpuin->firstmidiq != NULL) {
+		ERROR();
+		return -1;
+	}
+
+	/* Disable RX interrupt */
+	emu10k1_irq_disable(card, INTE_MIDIRXENABLE);
+
+	emu10k1_mpu_release(card);
+
+	card_mpuin->status |= FLAGS_AVAILABLE;	/* set */
+	card_mpuin->status &= ~FLAGS_MIDM_STARTED;	/* clear */
+
+	return 0;
+}
+
+/* Adds MIDI buffer to local queue list                         */
+
+int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *card_mpuin, struct midi_hdr *midihdr)
+{
+	struct midi_queue *midiq;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_mpuin_add_buffer()\n");
+
+	/* Update MIDI buffer flags */
+	midihdr->flags |= MIDIBUF_INQUEUE;	/* set */
+	midihdr->flags &= ~MIDIBUF_DONE;	/* clear */
+
+	if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC)) == NULL) {
+		/* Message lost */
+		return -1;
+	}
+
+	midiq->next = NULL;
+	midiq->qtype = 1;
+	midiq->length = midihdr->bufferlength;
+	midiq->sizeLeft = midihdr->bufferlength;
+	midiq->midibyte = midihdr->data;
+	midiq->refdata = (unsigned long) midihdr;
+
+	spin_lock_irqsave(&card_mpuin->lock, flags);
+
+	if (card_mpuin->firstmidiq == NULL) {
+		card_mpuin->firstmidiq = midiq;
+		card_mpuin->lastmidiq = midiq;
+	} else {
+		(card_mpuin->lastmidiq)->next = midiq;
+		card_mpuin->lastmidiq = midiq;
+	}
+
+	spin_unlock_irqrestore(&card_mpuin->lock, flags);
+
+	return 0;
+}
+
+/* First set the Time Stamp if MIDI IN has not started.         */
+
+/* Then enable RX Irq.                                          */
+
+int emu10k1_mpuin_start(struct emu10k1_card *card)
+{
+	struct emu10k1_mpuin *card_mpuin = card->mpuin;
+	u8 dummy;
+
+	DPF(2, "emu10k1_mpuin_start()\n");
+
+	/* Set timestamp if not set */
+	if (card_mpuin->status & FLAGS_MIDM_STARTED) {
+		DPF(2, "Time Stamp not changed\n");
+	} else {
+		while (!emu10k1_mpu_read_data(card, &dummy));
+
+		card_mpuin->status |= FLAGS_MIDM_STARTED;	/* set */
+
+		/* Set new time stamp */
+		card_mpuin->timestart = (jiffies * 1000) / HZ;
+		DPD(2, "New Time Stamp = %d\n", card_mpuin->timestart);
+
+		card_mpuin->qhead = 0;
+		card_mpuin->qtail = 0;
+
+		emu10k1_irq_enable(card, INTE_MIDIRXENABLE);
+	}
+
+	return 0;
+}
+
+/* Disable the RX Irq.  If a partial recorded buffer            */
+
+/* exist, send it up to IMIDI level.                            */
+
+int emu10k1_mpuin_stop(struct emu10k1_card *card)
+{
+	struct emu10k1_mpuin *card_mpuin = card->mpuin;
+	struct midi_queue *midiq;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_mpuin_stop()\n");
+
+	emu10k1_irq_disable(card, INTE_MIDIRXENABLE);
+
+	card_mpuin->status &= ~FLAGS_MIDM_STARTED;	/* clear */
+
+	if (card_mpuin->firstmidiq) {
+		spin_lock_irqsave(&card_mpuin->lock, flags);
+
+		midiq = card_mpuin->firstmidiq;
+		if (midiq != NULL) {
+			if (midiq->sizeLeft == midiq->length)
+				midiq = NULL;
+			else {
+				card_mpuin->firstmidiq = midiq->next;
+				if (card_mpuin->firstmidiq == NULL)
+					card_mpuin->lastmidiq = NULL;
+			}
+		}
+
+		spin_unlock_irqrestore(&card_mpuin->lock, flags);
+
+		if (midiq) {
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0);
+			kfree(midiq);
+		}
+	}
+
+	return 0;
+}
+
+/* Disable the RX Irq.  If any buffer                           */
+
+/* exist, send it up to IMIDI level.                            */
+int emu10k1_mpuin_reset(struct emu10k1_card *card)
+{
+	struct emu10k1_mpuin *card_mpuin = card->mpuin;
+	struct midi_queue *midiq;
+
+	DPF(2, "emu10k1_mpuin_reset()\n");
+
+	emu10k1_irq_disable(card, INTE_MIDIRXENABLE);
+
+	while (card_mpuin->firstmidiq) {
+		midiq = card_mpuin->firstmidiq;
+		card_mpuin->firstmidiq = midiq->next;
+
+		if (midiq->sizeLeft == midiq->length)
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0);
+		else
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0);
+
+		kfree(midiq);
+	}
+
+	card_mpuin->lastmidiq = NULL;
+	card_mpuin->status &= ~FLAGS_MIDM_STARTED;
+
+	return 0;
+}
+
+/* Passes the message with the data back to the client          */
+
+/* via IRQ & DPC callbacks to Ring 3                            */
+int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid)
+{
+	unsigned long timein;
+	struct midi_queue *midiq;
+	unsigned long callback_msg[3];
+	struct midi_hdr *midihdr;
+
+	/* Called during ISR. The data & code touched are:
+	 * 1. card_mpuin
+	 * 2. The function to be called
+	 */
+
+	timein = card_mpuin->timein;
+	if (card_mpuin->timestart <= timein)
+		callback_msg[0] = timein - card_mpuin->timestart;
+	else
+		callback_msg[0] = (~0x0L - card_mpuin->timestart) + timein;
+
+	if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) {
+		callback_msg[1] = data;
+		callback_msg[2] = bytesvalid;
+		DPD(2, "emu10k1_mpuin_callback: midimsg = %#lx\n", data);
+	} else {
+		midiq = (struct midi_queue *) data;
+		midihdr = (struct midi_hdr *) midiq->refdata;
+
+		callback_msg[1] = midiq->length - midiq->sizeLeft;
+		callback_msg[2] = midiq->refdata;
+		midihdr->flags &= ~MIDIBUF_INQUEUE;
+		midihdr->flags |= MIDIBUF_DONE;
+
+		midihdr->bytesrecorded = midiq->length - midiq->sizeLeft;
+	}
+
+	/* Notify client that Sysex buffer has been sent */
+	emu10k1_midi_callback(msg, card_mpuin->openinfo.refdata, callback_msg);
+
+	return 0;
+}
+
+void emu10k1_mpuin_bh(unsigned long refdata)
+{
+	u8 data;
+	unsigned idx;
+	struct emu10k1_mpuin *card_mpuin = (struct emu10k1_mpuin *) refdata;
+	unsigned long flags;
+
+	while (card_mpuin->qhead != card_mpuin->qtail) {
+		spin_lock_irqsave(&card_mpuin->lock, flags);
+		idx = card_mpuin->qhead;
+		data = card_mpuin->midiq[idx].data;
+		card_mpuin->timein = card_mpuin->midiq[idx].timein;
+		idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
+		card_mpuin->qhead = idx;
+		spin_unlock_irqrestore(&card_mpuin->lock, flags);
+
+		sblive_miStateEntry(card_mpuin, data);
+	}
+
+	return;
+}
+
+/* IRQ callback handler routine for the MPU in port */
+
+int emu10k1_mpuin_irqhandler(struct emu10k1_card *card)
+{
+	unsigned idx;
+	unsigned count;
+	u8 MPUIvalue;
+	struct emu10k1_mpuin *card_mpuin = card->mpuin;
+
+	/* IRQ service routine. The data and code touched are:
+	 * 1. card_mpuin
+	 */
+
+	count = 0;
+	idx = card_mpuin->qtail;
+
+	while (1) {
+		if (emu10k1_mpu_read_data(card, &MPUIvalue) < 0) {
+			break;
+		} else {
+			++count;
+			card_mpuin->midiq[idx].data = MPUIvalue;
+			card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ;
+			idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
+		}
+	}
+
+	if (count) {
+		card_mpuin->qtail = idx;
+
+		tasklet_hi_schedule(&card_mpuin->tasklet);
+	}
+
+	return 0;
+}
+
+/*****************************************************************************/
+
+/*   Supporting functions for Midi-In Interpretation State Machine           */
+
+/*****************************************************************************/
+
+/* FIXME: This should be a macro */
+int sblive_miStateInit(struct emu10k1_mpuin *card_mpuin)
+{
+	card_mpuin->status = 0;	/* For MIDI running status */
+	card_mpuin->fstatus = 0;	/* For 0xFn status only */
+	card_mpuin->curstate = STIN_PARSE;
+	card_mpuin->laststate = STIN_PARSE;
+	card_mpuin->data = 0;
+	card_mpuin->timestart = 0;
+	card_mpuin->timein = 0;
+
+	return 0;
+}
+
+/* FIXME: This should be a macro */
+int sblive_miStateEntry(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
+}
+
+int sblive_miStateParse(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	switch (data & 0xf0) {
+	case 0x80:
+	case 0x90:
+	case 0xA0:
+	case 0xB0:
+	case 0xE0:
+		card_mpuin->curstate = STIN_3BYTE;
+		break;
+
+	case 0xC0:
+	case 0xD0:
+		card_mpuin->curstate = STIN_2BYTE;
+		break;
+
+	case 0xF0:
+		/* System messages do not affect the previous running status! */
+		switch (data & 0x0f) {
+		case 0x0:
+			card_mpuin->laststate = card_mpuin->curstate;
+			card_mpuin->curstate = STIN_SYS_EX_NORM;
+
+			if (card_mpuin->firstmidiq) {
+				struct midi_queue *midiq;
+
+				midiq = card_mpuin->firstmidiq;
+				*midiq->midibyte = data;
+				--midiq->sizeLeft;
+				++midiq->midibyte;
+			}
+
+			return CTSTATUS_NEXT_BYTE;
+
+		case 0x7:
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, 0xf7, 0);
+			return -1;
+
+		case 0x2:
+			card_mpuin->laststate = card_mpuin->curstate;
+			card_mpuin->curstate = STIN_SYS_COMMON_3;
+			break;
+
+		case 0x1:
+		case 0x3:
+			card_mpuin->laststate = card_mpuin->curstate;
+			card_mpuin->curstate = STIN_SYS_COMMON_2;
+			break;
+
+		default:
+			/* includes 0xF4 - 0xF6, 0xF8 - 0xFF */
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+		}
+
+		break;
+
+	default:
+		DPF(2, "BUG: default case hit\n");
+		return -1;
+	}
+
+	return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
+}
+
+int sblive_miState3Byte(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	u8 temp = data & 0xf0;
+
+	if (temp < 0x80) {
+		return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data);
+	} else if (temp <= 0xe0 && temp != 0xc0 && temp != 0xd0) {
+		card_mpuin->status = data;
+		card_mpuin->curstate = STIN_3BYTE_KEY;
+
+		return CTSTATUS_NEXT_BYTE;
+	}
+
+	return midistatefn[STIN_PARSE].Fn(card_mpuin, data);
+}
+
+int sblive_miState3ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data)
+
+/* byte 1 */
+{
+	unsigned long tmp;
+
+	if (data > 0x7f) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = STIN_PARSE;
+		tmp = ((unsigned long) data) << 8;
+		tmp |= (unsigned long) card_mpuin->status;
+
+		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
+
+		return -1;
+	}
+
+	card_mpuin->data = data;
+	card_mpuin->curstate = STIN_3BYTE_VEL;
+
+	return CTSTATUS_NEXT_BYTE;
+}
+
+int sblive_miState3ByteVel(struct emu10k1_mpuin *card_mpuin, u8 data)
+
+/* byte 2 */
+{
+	unsigned long tmp;
+
+	if (data > 0x7f) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = STIN_PARSE;
+		tmp = ((unsigned long) data) << 8;
+		tmp |= card_mpuin->data;
+		tmp = tmp << 8;
+		tmp |= (unsigned long) card_mpuin->status;
+
+		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
+
+		return -1;
+	}
+
+	card_mpuin->curstate = STIN_3BYTE;
+	tmp = (unsigned long) data;
+	tmp = tmp << 8;
+	tmp |= (unsigned long) card_mpuin->data;
+	tmp = tmp << 8;
+	tmp |= (unsigned long) card_mpuin->status;
+
+	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);
+
+	return 0;
+}
+
+int sblive_miState2Byte(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	u8 temp = data & 0xf0;
+
+	if ((temp == 0xc0) || (temp == 0xd0)) {
+		card_mpuin->status = data;
+		card_mpuin->curstate = STIN_2BYTE_KEY;
+
+		return CTSTATUS_NEXT_BYTE;
+	}
+
+	if (temp < 0x80)
+		return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data);
+
+	return midistatefn[STIN_PARSE].Fn(card_mpuin, data);
+}
+
+int sblive_miState2ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data)
+
+/* byte 1 */
+{
+	unsigned long tmp;
+
+	if (data > 0x7f) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = STIN_PARSE;
+		tmp = (unsigned long) data;
+		tmp = tmp << 8;
+		tmp |= (unsigned long) card_mpuin->status;
+
+		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
+
+		return -1;
+	}
+
+	card_mpuin->curstate = STIN_2BYTE;
+	tmp = (unsigned long) data;
+	tmp = tmp << 8;
+	tmp |= (unsigned long) card_mpuin->status;
+
+	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);
+
+	return 0;
+}
+
+int sblive_miStateSysCommon2(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	card_mpuin->fstatus = data;
+	card_mpuin->curstate = STIN_SYS_COMMON_2_KEY;
+
+	return CTSTATUS_NEXT_BYTE;
+}
+
+int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *card_mpuin, u8 data)
+
+/* byte 1 */
+{
+	unsigned long tmp;
+
+	if (data > 0x7f) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = card_mpuin->laststate;
+		tmp = (unsigned long) data;
+		tmp = tmp << 8;
+		tmp |= (unsigned long) card_mpuin->fstatus;
+
+		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
+
+		return -1;
+	}
+
+	card_mpuin->curstate = card_mpuin->laststate;
+	tmp = (unsigned long) data;
+	tmp = tmp << 8;
+	tmp |= (unsigned long) card_mpuin->fstatus;
+
+	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);
+
+	return 0;
+}
+
+int sblive_miStateSysCommon3(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	card_mpuin->fstatus = data;
+	card_mpuin->curstate = STIN_SYS_COMMON_3_KEY;
+
+	return CTSTATUS_NEXT_BYTE;
+}
+
+int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *card_mpuin, u8 data)
+
+/* byte 1 */
+{
+	unsigned long tmp;
+
+	if (data > 0x7f) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = card_mpuin->laststate;
+		tmp = (unsigned long) data;
+		tmp = tmp << 8;
+		tmp |= (unsigned long) card_mpuin->fstatus;
+
+		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
+
+		return -1;
+	}
+
+	card_mpuin->data = data;
+	card_mpuin->curstate = STIN_SYS_COMMON_3_VEL;
+
+	return CTSTATUS_NEXT_BYTE;
+}
+
+int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *card_mpuin, u8 data)
+
+/* byte 2 */
+{
+	unsigned long tmp;
+
+	if (data > 0x7f) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = card_mpuin->laststate;
+		tmp = (unsigned long) data;
+		tmp = tmp << 8;
+		tmp |= (unsigned long) card_mpuin->data;
+		tmp = tmp << 8;
+		tmp |= (unsigned long) card_mpuin->fstatus;
+
+		emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
+
+		return -1;
+	}
+
+	card_mpuin->curstate = card_mpuin->laststate;
+	tmp = (unsigned long) data;
+	tmp = tmp << 8;
+	tmp |= (unsigned long) card_mpuin->data;
+	tmp = tmp << 8;
+	tmp |= (unsigned long) card_mpuin->fstatus;
+
+	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);
+
+	return 0;
+}
+
+int sblive_miStateSysExNorm(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	unsigned long flags;
+
+	if ((data > 0x7f) && (data != 0xf7)) {
+		/* Real-time messages check */
+		if (data > 0xf7)
+			return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
+
+		/* Invalid Data! */
+		DPF(2, "Invalid data!\n");
+
+		card_mpuin->curstate = card_mpuin->laststate;
+
+		if (card_mpuin->firstmidiq) {
+			struct midi_queue *midiq;
+
+			midiq = card_mpuin->firstmidiq;
+			*midiq->midibyte = data;
+			--midiq->sizeLeft;
+			++midiq->midibyte;
+
+			spin_lock_irqsave(&card_mpuin->lock, flags);
+
+			card_mpuin->firstmidiq = midiq->next;
+			if (card_mpuin->firstmidiq == NULL)
+				card_mpuin->lastmidiq = NULL;
+
+			spin_unlock_irqrestore(&card_mpuin->lock, flags);
+
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0);
+
+			kfree(midiq);
+		}
+
+		return -1;
+	}
+
+	if (card_mpuin->firstmidiq) {
+		struct midi_queue *midiq;
+
+		midiq = card_mpuin->firstmidiq;
+		*midiq->midibyte = data;
+		--midiq->sizeLeft;
+		++midiq->midibyte;
+	}
+
+	if (data == 0xf7) {
+		/* End of Sysex buffer */
+		/* Send down the buffer */
+
+		card_mpuin->curstate = card_mpuin->laststate;
+
+		if (card_mpuin->firstmidiq) {
+			struct midi_queue *midiq;
+
+			midiq = card_mpuin->firstmidiq;
+
+			spin_lock_irqsave(&card_mpuin->lock, flags);
+
+			card_mpuin->firstmidiq = midiq->next;
+			if (card_mpuin->firstmidiq == NULL)
+				card_mpuin->lastmidiq = NULL;
+
+			spin_unlock_irqrestore(&card_mpuin->lock, flags);
+
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0);
+
+			kfree(midiq);
+		}
+
+		return 0;
+	}
+
+	if (card_mpuin->firstmidiq) {
+		struct midi_queue *midiq;
+
+		midiq = card_mpuin->firstmidiq;
+
+		if (midiq->sizeLeft == 0) {
+			/* Special case */
+
+			spin_lock_irqsave(&card_mpuin->lock, flags);
+
+			card_mpuin->firstmidiq = midiq->next;
+			if (card_mpuin->firstmidiq == NULL)
+				card_mpuin->lastmidiq = NULL;
+
+			spin_unlock_irqrestore(&card_mpuin->lock, flags);
+
+			emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0);
+
+			kfree(midiq);
+
+			return CTSTATUS_NEXT_BYTE;
+		}
+	}
+
+	return CTSTATUS_NEXT_BYTE;
+}
+
+int sblive_miStateSysReal(struct emu10k1_mpuin *card_mpuin, u8 data)
+{
+	emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, data, 1);
+
+	return CTSTATUS_NEXT_BYTE;
+}
diff -Nru linux/sound/oss/emu10k1/cardmi.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.h
--- linux/sound/oss/emu10k1/cardmi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,114 @@
+/*
+ **********************************************************************
+ *     sblive_mi.h
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999     Alan Cox        cleaned up
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _CARDMI_H
+#define _CARDMI_H
+
+#include "icardmid.h"
+#include <linux/interrupt.h>
+
+typedef enum
+{
+	STIN_PARSE = 0,
+	STIN_3BYTE,                     /* 0x80, 0x90, 0xA0, 0xB0, 0xE0 */
+	STIN_3BYTE_KEY,                 /* Byte 1 */
+	STIN_3BYTE_VEL,                 /* Byte 1 */
+	STIN_2BYTE,                     /* 0xC0, 0xD0 */
+	STIN_2BYTE_KEY,                 /* Byte 1 */
+	STIN_SYS_COMMON_2,              /* 0xF1, 0xF3  */
+	STIN_SYS_COMMON_2_KEY,
+	STIN_SYS_COMMON_3,              /* 0xF2 */
+	STIN_SYS_COMMON_3_KEY,
+	STIN_SYS_COMMON_3_VEL,
+	STIN_SYS_EX_NORM,               /* 0xF0, Normal mode */
+	STIN_SYS_REAL
+} midi_in_state;
+
+
+/* flags for card MIDI in object */
+#define FLAGS_MIDM_STARTED          0x00001000      // Data has started to come in after Midm Start
+#define MIDIIN_MAX_BUFFER_SIZE      200             // Definition for struct emu10k1_mpuin
+
+struct midi_data
+{
+	u8 data;
+	u32 timein;
+};
+
+struct emu10k1_mpuin
+{
+	spinlock_t        lock;
+	struct midi_queue *firstmidiq;
+	struct midi_queue *lastmidiq;
+	unsigned          qhead, qtail;
+	struct midi_data  midiq[MIDIIN_MAX_BUFFER_SIZE];
+	struct tasklet_struct tasklet;
+	struct midi_openinfo    openinfo;
+
+	/* For MIDI state machine */
+	u8              status;        /* For MIDI running status */
+	u8              fstatus;       /* For 0xFn status only */
+	midi_in_state   curstate;
+	midi_in_state   laststate;
+	u32             timestart;
+	u32             timein;
+	u8              data;
+};
+
+int emu10k1_mpuin_open(struct emu10k1_card *, struct midi_openinfo *);
+int emu10k1_mpuin_close(struct emu10k1_card *);
+int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *, struct midi_hdr *);
+int emu10k1_mpuin_start(struct emu10k1_card *);
+int emu10k1_mpuin_stop(struct emu10k1_card *);
+int emu10k1_mpuin_reset(struct emu10k1_card *);
+
+int sblive_miStateInit(struct emu10k1_mpuin *);
+int sblive_miStateEntry(struct emu10k1_mpuin *, u8);
+int sblive_miStateParse(struct emu10k1_mpuin *, u8);
+int sblive_miState3Byte(struct emu10k1_mpuin *, u8);
+int sblive_miState3ByteKey(struct emu10k1_mpuin *, u8);
+int sblive_miState3ByteVel(struct emu10k1_mpuin *, u8);
+int sblive_miState2Byte(struct emu10k1_mpuin *, u8);
+int sblive_miState2ByteKey(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysCommon2(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysCommon3(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysExNorm(struct emu10k1_mpuin *, u8);
+int sblive_miStateSysReal(struct emu10k1_mpuin *, u8);
+
+int emu10k1_mpuin_irqhandler(struct emu10k1_card *);
+void emu10k1_mpuin_bh(unsigned long);
+int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid);
+
+#endif  /* _CARDMI_H */
diff -Nru linux/sound/oss/emu10k1/cardmo.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.c
--- linux/sound/oss/emu10k1/cardmo.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,229 @@
+/*     
+ **********************************************************************
+ *     cardmo.c - MIDI UART output HAL for emu10k1 driver 
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ *     November 2, 1999     Alan Cox        cleaned up
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#include <linux/slab.h>
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "cardmo.h"
+#include "irqmgr.h"
+
+/* Installs the IRQ handler for the MPU out port               *
+ * and initialize parameters                                    */
+
+int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo)
+{
+	struct emu10k1_mpuout *card_mpuout = card->mpuout;
+
+	DPF(2, "emu10k1_mpuout_open()\n");
+
+	if (!(card_mpuout->status & FLAGS_AVAILABLE))
+		return -1;
+
+	/* Copy open info and mark channel as in use */
+	card_mpuout->intr = 0;
+	card_mpuout->openinfo = *openinfo;
+	card_mpuout->status &= ~FLAGS_AVAILABLE;
+	card_mpuout->laststatus = 0x80;
+	card_mpuout->firstmidiq = NULL;
+	card_mpuout->lastmidiq = NULL;
+
+	emu10k1_mpu_reset(card);
+	emu10k1_mpu_acquire(card);
+
+	return 0;
+}
+
+int emu10k1_mpuout_close(struct emu10k1_card *card)
+{
+	struct emu10k1_mpuout *card_mpuout = card->mpuout;
+	struct midi_queue *midiq;
+	struct midi_hdr *midihdr;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_mpuout_close()\n");
+
+	emu10k1_irq_disable(card, INTE_MIDITXENABLE);
+
+	spin_lock_irqsave(&card_mpuout->lock, flags);
+
+	while (card_mpuout->firstmidiq != NULL) {
+		midiq = card_mpuout->firstmidiq;
+		midihdr = (struct midi_hdr *) midiq->refdata;
+
+		card_mpuout->firstmidiq = midiq->next;
+
+		kfree(midihdr->data);
+		kfree(midihdr);
+		kfree(midiq);
+	}
+
+	card_mpuout->lastmidiq = NULL;
+
+	emu10k1_mpu_release(card);
+
+	card_mpuout->status |= FLAGS_AVAILABLE;
+
+	spin_unlock_irqrestore(&card_mpuout->lock, flags);
+
+	return 0;
+}
+
+/* If there isn't enough buffer space, reject Midi Buffer.     *
+* Otherwise, disable TX, create object to hold Midi            *
+*  uffer, update buffer flags and other parameters             *
+* before enabling TX again.                                    */
+
+int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr)
+{
+	struct emu10k1_mpuout *card_mpuout = card->mpuout;
+	struct midi_queue *midiq;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_mpuout_add_buffer()\n");
+
+	if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND)
+		return 0;
+
+	midihdr->flags |= MIDIBUF_INQUEUE;
+	midihdr->flags &= ~MIDIBUF_DONE;
+
+	if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) {
+		/* Message lost */
+		return -1;
+	}
+
+	midiq->next = NULL;
+	midiq->qtype = 1;
+	midiq->length = midihdr->bufferlength;
+	midiq->sizeLeft = midihdr->bufferlength;
+	midiq->midibyte = midihdr->data;
+
+	midiq->refdata = (unsigned long) midihdr;
+
+	spin_lock_irqsave(&card_mpuout->lock, flags);
+
+	if (card_mpuout->firstmidiq == NULL) {
+		card_mpuout->firstmidiq = midiq;
+		card_mpuout->lastmidiq = midiq;
+	} else {
+		(card_mpuout->lastmidiq)->next = midiq;
+		card_mpuout->lastmidiq = midiq;
+	}
+
+	card_mpuout->intr = 0;
+
+	emu10k1_irq_enable(card, INTE_MIDITXENABLE);
+
+	spin_unlock_irqrestore(&card_mpuout->lock, flags);
+
+	return 0;
+}
+
+void emu10k1_mpuout_bh(unsigned long refdata)
+{
+	struct emu10k1_card *card = (struct emu10k1_card *) refdata;
+	struct emu10k1_mpuout *card_mpuout = card->mpuout;
+	int cByteSent = 0;
+	struct midi_queue *midiq;
+	struct midi_queue *doneq = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card_mpuout->lock, flags);
+
+	while (card_mpuout->firstmidiq != NULL) {
+		midiq = card_mpuout->firstmidiq;
+
+		while (cByteSent < 4 && midiq->sizeLeft) {
+			if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) {
+				DPF(2, "emu10k1_mpuoutDpcCallback error!!\n");
+			} else {
+				++cByteSent;
+				--midiq->sizeLeft;
+				++midiq->midibyte;
+			}
+		}
+
+		if (midiq->sizeLeft == 0) {
+			if (doneq == NULL)
+				doneq = midiq;
+			card_mpuout->firstmidiq = midiq->next;
+		} else
+			break;
+	}
+
+	if (card_mpuout->firstmidiq == NULL)
+		card_mpuout->lastmidiq = NULL;
+
+	if (doneq != NULL) {
+		while (doneq != card_mpuout->firstmidiq) {
+			unsigned long callback_msg[3];
+
+			midiq = doneq;
+			doneq = midiq->next;
+
+			if (midiq->qtype) {
+				callback_msg[0] = 0;
+				callback_msg[1] = midiq->length;
+				callback_msg[2] = midiq->refdata;
+
+				emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg);
+			} else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F)
+				card_mpuout->laststatus = (u8) midiq->refdata;
+
+			kfree(midiq);
+		}
+	}
+
+	if ((card_mpuout->firstmidiq != NULL) || cByteSent) {
+		card_mpuout->intr = 0;
+		emu10k1_irq_enable(card, INTE_MIDITXENABLE);
+	}
+
+	spin_unlock_irqrestore(&card_mpuout->lock, flags);
+
+	return;
+}
+
+int emu10k1_mpuout_irqhandler(struct emu10k1_card *card)
+{
+	struct emu10k1_mpuout *card_mpuout = card->mpuout;
+
+	DPF(4, "emu10k1_mpuout_irqhandler\n");
+
+	card_mpuout->intr = 1;
+	emu10k1_irq_disable(card, INTE_MIDITXENABLE);
+
+	tasklet_hi_schedule(&card_mpuout->tasklet);
+
+	return 0;
+}
diff -Nru linux/sound/oss/emu10k1/cardmo.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.h
--- linux/sound/oss/emu10k1/cardmo.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,62 @@
+/*
+ **********************************************************************
+ *     cardmo.h
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999     Alan Cox	    cleaned up
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _CARDMO_H
+#define _CARDMO_H
+
+#include "icardmid.h"
+#include <linux/interrupt.h>
+
+#define CARDMIDIOUT_STATE_DEFAULT    0x00000000
+#define CARDMIDIOUT_STATE_SUSPEND    0x00000001
+
+struct emu10k1_mpuout
+{
+	u32			status;
+	u32			state;
+	volatile int		intr;
+	struct midi_queue	*firstmidiq;
+	struct midi_queue	*lastmidiq;
+	u8			laststatus;
+	struct tasklet_struct 	tasklet;
+	spinlock_t		lock;
+	struct midi_openinfo	openinfo;
+};
+
+int emu10k1_mpuout_open(struct emu10k1_card *, struct midi_openinfo *);
+int emu10k1_mpuout_close(struct emu10k1_card *);
+int emu10k1_mpuout_add_buffer(struct emu10k1_card *, struct midi_hdr *);
+
+int emu10k1_mpuout_irqhandler(struct emu10k1_card *);
+void emu10k1_mpuout_bh(unsigned long);
+
+#endif  /* _CARDMO_H */
diff -Nru linux/sound/oss/emu10k1/cardwi.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.c
--- linux/sound/oss/emu10k1/cardwi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,371 @@
+/*
+ **********************************************************************
+ *     cardwi.c - PCM input HAL for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include <linux/poll.h>
+#include "hwaccess.h"
+#include "timer.h"
+#include "recmgr.h"
+#include "audio.h"
+#include "cardwi.h"
+
+/**
+ * query_format - returns a valid sound format
+ *
+ * This function will return a valid sound format as close
+ * to the requested one as possible. 
+ */
+void query_format(int recsrc, struct wave_format *wave_fmt)
+{
+
+	switch (recsrc) {
+	case WAVERECORD_AC97:
+
+		if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2))
+			wave_fmt->channels = 2;
+
+		if (wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2)
+			wave_fmt->samplingrate = 0xBB80;
+		else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2)
+			wave_fmt->samplingrate = 0xAC44;
+		else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2)
+			wave_fmt->samplingrate = 0x7D00;
+		else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2)
+			wave_fmt->samplingrate = 0x5DC0;
+		else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2)
+			wave_fmt->samplingrate = 0x5622;
+		else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2)
+			wave_fmt->samplingrate = 0x3E80;
+		else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2)
+			wave_fmt->samplingrate = 0x2B11;
+		else
+			wave_fmt->samplingrate = 0x1F40;
+
+		switch (wave_fmt->id) {
+		case AFMT_S16_LE:
+			wave_fmt->bitsperchannel = 16;
+			break;
+		case AFMT_U8:
+			wave_fmt->bitsperchannel = 8;
+			break;
+		default:
+			wave_fmt->id = AFMT_S16_LE;
+			wave_fmt->bitsperchannel = 16;
+			break;
+		}
+
+		break;
+
+	/* these can't be changed from the original values */
+	case WAVERECORD_MIC:
+	case WAVERECORD_FX:
+		break;
+
+	default:
+		BUG();
+		break;
+	}
+
+	wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
+	wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
+	wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
+}
+
+static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer)
+{
+	buffer->addr = pci_alloc_consistent(card->pci_dev, buffer->size * buffer->cov,
+					    &buffer->dma_handle);
+	if (buffer->addr == NULL)
+		return -1;
+
+	return 0;
+}
+
+static void free_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer)
+{
+	if (buffer->addr != NULL)
+		pci_free_consistent(card->pci_dev, buffer->size * buffer->cov,
+				    buffer->addr, buffer->dma_handle);
+}
+
+int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	struct wiinst **wiinst_tmp = NULL;
+	u32 delay;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_wavein_open()\n");
+
+	switch (wiinst->recsrc) {
+	case WAVERECORD_AC97:
+		wiinst_tmp = &card->wavein.ac97;
+		break;
+	case WAVERECORD_MIC:
+		wiinst_tmp = &card->wavein.mic;
+		break;
+	case WAVERECORD_FX:
+		wiinst_tmp = &card->wavein.fx;
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	if (*wiinst_tmp != NULL) {
+		spin_unlock_irqrestore(&card->lock, flags);
+		return -1;
+	}
+
+	*wiinst_tmp = wiinst;
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	/* handle 8 bit recording */
+	if (wiinst->format.bytesperchannel == 1) {
+		if (wiinst->buffer.size > 0x8000) {
+			wiinst->buffer.size = 0x8000;
+			wiinst->buffer.sizeregval = 0x1f;
+		} else
+			wiinst->buffer.sizeregval += 4;
+
+		wiinst->buffer.cov = 2;
+	} else
+		wiinst->buffer.cov = 1;
+
+	if (alloc_buffer(card, &wiinst->buffer) < 0) {
+		ERROR();
+		emu10k1_wavein_close(wave_dev);
+		return -1;
+	}
+
+	emu10k1_set_record_src(card, wiinst);
+
+	delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec;
+
+	emu10k1_timer_install(card, &wiinst->timer, delay / 2);
+
+	wiinst->state = WAVE_STATE_OPEN;
+
+	return 0;
+}
+
+void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_wavein_close()\n");
+
+	emu10k1_wavein_stop(wave_dev);
+
+	emu10k1_timer_uninstall(card, &wiinst->timer);
+
+	free_buffer(card, &wiinst->buffer);
+
+	spin_lock_irqsave(&card->lock, flags);
+	switch (wave_dev->wiinst->recsrc) {
+	case WAVERECORD_AC97:
+		card->wavein.ac97 = NULL;
+		break;
+	case WAVERECORD_MIC:
+		card->wavein.mic = NULL;
+		break;
+	case WAVERECORD_FX:
+		card->wavein.fx = NULL;
+		break;
+	default:
+		BUG();
+		break;
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	wiinst->state = WAVE_STATE_CLOSED;
+}
+
+void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct wiinst *wiinst = wave_dev->wiinst;
+
+	DPF(2, "emu10k1_wavein_start()\n");
+
+	emu10k1_start_record(card, &wiinst->buffer);
+	emu10k1_timer_enable(wave_dev->card, &wiinst->timer);
+
+	wiinst->buffer.hw_pos = 0;
+	wiinst->buffer.pos = 0;
+	wiinst->buffer.bytestocopy = 0;
+
+	wiinst->state |= WAVE_STATE_STARTED;
+}
+
+void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct wiinst *wiinst = wave_dev->wiinst;
+
+	DPF(2, "emu10k1_wavein_stop()\n");
+
+	if (!(wiinst->state & WAVE_STATE_STARTED))
+		return;
+
+	emu10k1_timer_disable(card, &wiinst->timer);
+	emu10k1_stop_record(card, &wiinst->buffer);
+
+	wiinst->state &= ~WAVE_STATE_STARTED;
+}
+
+int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	u32 delay;
+
+	DPF(2, "emu10k1_wavein_setformat()\n");
+
+	if (wiinst->state & WAVE_STATE_STARTED)
+		return -1;
+
+	query_format(wiinst->recsrc, format);
+
+	if ((wiinst->format.samplingrate != format->samplingrate)
+	    || (wiinst->format.bitsperchannel != format->bitsperchannel)
+	    || (wiinst->format.channels != format->channels)) {
+
+		wiinst->format = *format;
+
+		if (wiinst->state == WAVE_STATE_CLOSED)
+			return 0;
+
+		wiinst->buffer.size *= wiinst->buffer.cov;
+
+		if (wiinst->format.bytesperchannel == 1) {
+			wiinst->buffer.cov = 2;
+			wiinst->buffer.size /= wiinst->buffer.cov;
+		} else
+			wiinst->buffer.cov = 1;
+
+		emu10k1_timer_uninstall(card, &wiinst->timer);
+
+		delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec;
+
+		emu10k1_timer_install(card, &wiinst->timer, delay / 2);
+	}
+
+	return 0;
+}
+
+void emu10k1_wavein_getxfersize(struct wiinst *wiinst, u32 * size)
+{
+	struct wavein_buffer *buffer = &wiinst->buffer;
+
+	*size = buffer->bytestocopy;
+
+	if (wiinst->mmapped)
+		return;
+
+	if (*size > buffer->size) {
+		*size = buffer->size;
+		buffer->pos = buffer->hw_pos;
+		buffer->bytestocopy = buffer->size;
+		DPF(1, "buffer overrun\n");
+	}
+}
+
+static void copy_block(u8 *dst, u8 * src, u32 str, u32 len, u8 cov)
+{
+	if (cov == 1)
+		__copy_to_user(dst, src + str, len);
+	else {
+		u8 byte;
+		u32 i;
+
+		src += 1 + 2 * str;
+
+		for (i = 0; i < len; i++) {
+			byte = src[2 * i] ^ 0x80;
+			__copy_to_user(dst + i, &byte, 1);
+		}
+	}
+}
+
+void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 * data, u32 * size)
+{
+	struct wavein_buffer *buffer = &wiinst->buffer;
+	u32 sizetocopy, sizetocopy_now, start;
+	unsigned long flags;
+
+	sizetocopy = min_t(u32, buffer->size, *size);
+	*size = sizetocopy;
+
+	if (!sizetocopy)
+		return;
+
+	spin_lock_irqsave(&wiinst->lock, flags);
+	start = buffer->pos;
+	buffer->pos += sizetocopy;
+	buffer->pos %= buffer->size;
+	buffer->bytestocopy -= sizetocopy;
+	sizetocopy_now = buffer->size - start;
+
+	spin_unlock_irqrestore(&wiinst->lock, flags);
+
+	if (sizetocopy > sizetocopy_now) {
+		sizetocopy -= sizetocopy_now;
+
+		copy_block(data, buffer->addr, start, sizetocopy_now, buffer->cov);
+		copy_block(data + sizetocopy_now, buffer->addr, 0, sizetocopy, buffer->cov);
+	} else {
+		copy_block(data, buffer->addr, start, sizetocopy, buffer->cov);
+	}
+}
+
+void emu10k1_wavein_update(struct emu10k1_card *card, struct wiinst *wiinst)
+{
+	u32 hw_pos;
+	u32 diff;
+
+	/* There is no actual start yet */
+	if (!(wiinst->state & WAVE_STATE_STARTED)) {
+		hw_pos = wiinst->buffer.hw_pos;
+	} else {
+		/* hw_pos in byte units */
+		hw_pos = sblive_readptr(card, wiinst->buffer.idxreg, 0) / wiinst->buffer.cov;
+	}
+
+	diff = (wiinst->buffer.size + hw_pos - wiinst->buffer.hw_pos) % wiinst->buffer.size;
+	wiinst->total_recorded += diff;
+	wiinst->buffer.bytestocopy += diff;
+
+	wiinst->buffer.hw_pos = hw_pos;
+}
diff -Nru linux/sound/oss/emu10k1/cardwi.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.h
--- linux/sound/oss/emu10k1/cardwi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,90 @@
+/*
+ **********************************************************************
+ *     cardwi.h -- header file for card wave input functions
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+#ifndef _CARDWI_H
+#define _CARDWI_H
+
+#include "icardwav.h"
+#include "audio.h"
+#include "timer.h"
+
+struct wavein_buffer {
+	u16 ossfragshift;
+        u32 fragment_size;
+        u32 numfrags;
+	u32 hw_pos;		/* hardware cursor position */
+	u32 pos;		/* software cursor position */
+	u32 bytestocopy;	/* bytes of recorded data available */
+	u32 size;
+	u32 pages;
+	u32 sizereg;
+	u32 sizeregval;
+        u32 addrreg;
+        u32 idxreg;
+        u32 adcctl;
+	void *addr;
+	u8 cov;
+	dma_addr_t dma_handle;	
+};
+
+struct wiinst
+{
+	u8 state;
+	struct emu_timer timer;
+	struct wave_format format;
+	struct wavein_buffer buffer;
+	wait_queue_head_t wait_queue;
+	u8 mmapped;
+	u32 total_recorded;	/* total bytes read() from device */
+	u32 blocks;
+	spinlock_t lock;
+	u8 recsrc;
+	u16 fxwc;
+};
+
+#define WAVEIN_MAXBUFSIZE         65536
+#define WAVEIN_MINBUFSIZE	  368
+
+#define WAVEIN_DEFAULTFRAGLEN     100 
+#define WAVEIN_DEFAULTBUFLEN      1000
+
+#define WAVEIN_MINFRAGSHIFT   	  8 
+
+int emu10k1_wavein_open(struct emu10k1_wavedevice *);
+void emu10k1_wavein_close(struct emu10k1_wavedevice *);
+void emu10k1_wavein_start(struct emu10k1_wavedevice *);
+void emu10k1_wavein_stop(struct emu10k1_wavedevice *);
+void emu10k1_wavein_getxfersize(struct wiinst *, u32 *);
+void emu10k1_wavein_xferdata(struct wiinst *, u8 *, u32 *);
+int emu10k1_wavein_setformat(struct emu10k1_wavedevice *, struct wave_format *);
+void emu10k1_wavein_update(struct emu10k1_card *, struct wiinst *);
+
+
+#endif /* _CARDWI_H */
diff -Nru linux/sound/oss/emu10k1/cardwo.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.c
--- linux/sound/oss/emu10k1/cardwo.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,693 @@
+/*
+ **********************************************************************
+ *     cardwo.c - PCM output HAL for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include <linux/poll.h>
+#include "hwaccess.h"
+#include "8010.h"
+#include "voicemgr.h"
+#include "cardwo.h"
+#include "audio.h"
+
+static u32 samplerate_to_linearpitch(u32 samplingrate)
+{
+	samplingrate = (samplingrate << 8) / 375;
+	return (samplingrate >> 1) + (samplingrate & 1);
+}
+
+static void query_format(struct emu10k1_wavedevice *wave_dev, struct wave_format *wave_fmt)
+{
+	int i, j, do_passthrough = 0, is_ac3 = 0;
+	struct emu10k1_card *card = wave_dev->card;
+	struct woinst *woinst = wave_dev->woinst;
+
+	if ((wave_fmt->channels > 2) && (wave_fmt->id != AFMT_S16_LE) && (wave_fmt->id != AFMT_U8))
+		wave_fmt->channels = 2;
+
+	if ((wave_fmt->channels < 1) || (wave_fmt->channels > WAVEOUT_MAXVOICES))
+		wave_fmt->channels = 2;
+
+	if (wave_fmt->channels == 2)
+		woinst->num_voices = 1;
+	else
+		woinst->num_voices = wave_fmt->channels;
+
+	if (wave_fmt->samplingrate >= 0x2ee00)
+		wave_fmt->samplingrate = 0x2ee00;
+
+	wave_fmt->passthrough = 0;
+	do_passthrough = is_ac3 = 0;
+
+	if (card->pt.selected)
+		do_passthrough = 1;
+
+	switch (wave_fmt->id) {
+	case AFMT_S16_LE:
+		wave_fmt->bitsperchannel = 16;
+		break;
+	case AFMT_U8:
+		wave_fmt->bitsperchannel = 8;
+		break;
+	case AFMT_AC3:
+		do_passthrough = 1;
+		is_ac3 = 1;
+		break;
+	default:
+		wave_fmt->id = AFMT_S16_LE;
+		wave_fmt->bitsperchannel = 16;
+		break;
+	}	
+	if (do_passthrough) {
+		i = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.intr_gpr_name);
+		j = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.enable_gpr_name);
+		/* currently only one waveout instance may use pass-through */
+		if (i < 0 || j < 0 || woinst->state != WAVE_STATE_CLOSED || 
+		    card->pt.state != PT_STATE_INACTIVE ||
+		    (wave_fmt->samplingrate != 48000 && !is_ac3) ||
+		    (wave_fmt->samplingrate != 48000 && !is_ac3)) {
+			DPF(2, "unable to set pass-through mode\n");
+		} else {
+			wave_fmt->samplingrate = 48000;
+			wave_fmt->channels = 2;
+			wave_fmt->passthrough = 1;
+			card->pt.intr_gpr = i;
+			card->pt.enable_gpr = j;
+			card->pt.state = PT_STATE_INACTIVE;
+			card->pt.pos_gpr = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.pos_gpr_name);
+			DPD(2, "is_ac3 is %d\n", is_ac3);
+			card->pt.ac3data = is_ac3;
+	                wave_fmt->bitsperchannel = 16;
+		}
+	}
+
+	wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
+
+	if (wave_fmt->channels == 2)
+		wave_fmt->bytespervoicesample = wave_fmt->channels * wave_fmt->bytesperchannel;
+	else
+		wave_fmt->bytespervoicesample = wave_fmt->bytesperchannel;
+
+	wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
+	wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
+}
+
+/**
+ * alloc_buffer -
+ *
+ * allocates the memory buffer for a voice. Two page tables are kept for each buffer.
+ * One (dma_handle) keeps track of the host memory pages used and the other (virtualpagetable)
+ * is passed to the device so that it can do DMA to host memory. 
+ *
+ */
+static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum)
+{
+	u32 pageindex, pagecount;
+	unsigned long busaddx;
+	int i;
+
+	DPD(2, "requested pages is: %d\n", buffer->pages);
+
+	if ((buffer->mem[voicenum].emupageindex =
+	 emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0)
+		return -1;
+
+	/* Fill in virtual memory table */
+	for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
+		if ((buffer->mem[voicenum].addr[pagecount] =
+		 pci_alloc_consistent(card->pci_dev, PAGE_SIZE,
+		 &buffer->mem[voicenum].dma_handle[pagecount])) == NULL) {
+			buffer->pages = pagecount;
+			return -1;
+		}
+
+		DPD(2, "Virtual Addx: %p\n", buffer->mem[voicenum].addr[pagecount]);
+
+		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
+			busaddx = buffer->mem[voicenum].dma_handle[pagecount] + i * EMUPAGESIZE;
+
+			DPD(3, "Bus Addx: %#lx\n", busaddx);
+
+			pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+
+			((u32 *) card->virtualpagetable.addr)[pageindex] = cpu_to_le32((busaddx * 2) | pageindex);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * free_buffer -
+ *
+ * frees the memory buffer for a voice.
+ */
+static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum)
+{
+	u32 pagecount, pageindex;
+	int i;
+
+	if (buffer->mem[voicenum].emupageindex < 0)
+		return;
+
+	for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
+		pci_free_consistent(card->pci_dev, PAGE_SIZE,
+		 buffer->mem[voicenum].addr[pagecount],
+		 buffer->mem[voicenum].dma_handle[pagecount]);
+
+		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
+			pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+			((u32 *) card->virtualpagetable.addr)[pageindex] =
+			 cpu_to_le32((card->silentpage.dma_handle * 2) | pageindex);
+		}
+	}
+
+	emu10k1_addxmgr_free(card, buffer->mem[voicenum].emupageindex);
+	buffer->mem[voicenum].emupageindex = -1;
+}
+
+static int get_voice(struct emu10k1_card *card, struct woinst *woinst, unsigned int voicenum)
+{
+	struct emu_voice *voice = &woinst->voice[voicenum];
+	/* Allocate voices here, if no voices available, return error.
+	 * Init voice_allocdesc first.*/
+
+	voice->usage = VOICE_USAGE_PLAYBACK;
+
+	voice->flags = 0;
+
+	if (woinst->format.channels == 2)
+		voice->flags |= VOICE_FLAGS_STEREO;
+
+	if (woinst->format.bitsperchannel == 16)
+		voice->flags |= VOICE_FLAGS_16BIT;
+
+	if (emu10k1_voice_alloc(card, voice) < 0) {
+		voice->usage = VOICE_USAGE_FREE;
+		return -1;
+	}
+
+	/* Calculate pitch */
+	voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8);
+	voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate);
+
+	DPD(2, "Initial pitch --> %#x\n", voice->initial_pitch);
+
+	voice->startloop = (woinst->buffer.mem[voicenum].emupageindex << 12) /
+	 woinst->format.bytespervoicesample;
+	voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespervoicesample;
+	voice->start = voice->startloop;
+
+	if (voice->flags & VOICE_FLAGS_STEREO) {
+		voice->params[0].send_a = card->waveout.send_a[1];
+		voice->params[0].send_b = card->waveout.send_b[1];
+		voice->params[0].send_c = card->waveout.send_c[1];
+		voice->params[0].send_d = card->waveout.send_d[1];
+
+		if (woinst->device)
+			voice->params[0].send_routing = 0x7654;
+		else
+			voice->params[0].send_routing = card->waveout.send_routing[1];
+
+		voice->params[0].volume_target = 0xffff;
+		voice->params[0].initial_fc = 0xff;
+		voice->params[0].initial_attn = 0x00;
+		voice->params[0].byampl_env_sustain = 0x7f;
+		voice->params[0].byampl_env_decay = 0x7f;
+
+		voice->params[1].send_a = card->waveout.send_a[2];
+		voice->params[1].send_b = card->waveout.send_b[2];
+		voice->params[1].send_c = card->waveout.send_c[2];
+		voice->params[1].send_d = card->waveout.send_d[2];
+
+		if (woinst->device)
+			voice->params[1].send_routing = 0x7654;
+		else
+			voice->params[1].send_routing = card->waveout.send_routing[2];
+
+		voice->params[1].volume_target = 0xffff;
+		voice->params[1].initial_fc = 0xff;
+		voice->params[1].initial_attn = 0x00;
+		voice->params[1].byampl_env_sustain = 0x7f;
+		voice->params[1].byampl_env_decay = 0x7f;
+	} else {
+		if (woinst->num_voices > 1) {
+			voice->params[0].send_a = 0xff;
+			voice->params[0].send_b = 0;
+			voice->params[0].send_c = 0;
+			voice->params[0].send_d = 0;
+
+			voice->params[0].send_routing =
+			 0xfff0 + card->mchannel_fx + voicenum;
+		} else {
+			voice->params[0].send_a = card->waveout.send_a[0];
+			voice->params[0].send_b = card->waveout.send_b[0];
+			voice->params[0].send_c = card->waveout.send_c[0];
+			voice->params[0].send_d = card->waveout.send_d[0];
+
+			if (woinst->device)
+				voice->params[0].send_routing = 0x7654;
+			else
+				voice->params[0].send_routing = card->waveout.send_routing[0];
+		}	
+
+		voice->params[0].volume_target = 0xffff;
+		voice->params[0].initial_fc = 0xff;
+		voice->params[0].initial_attn = 0x00;
+		voice->params[0].byampl_env_sustain = 0x7f;
+		voice->params[0].byampl_env_decay = 0x7f;
+	}
+
+	DPD(2, "voice: startloop=%#x, endloop=%#x\n", voice->startloop, voice->endloop);
+
+	emu10k1_voice_playback_setup(voice);
+
+	return 0;
+}
+
+int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct woinst *woinst = wave_dev->woinst;
+	struct waveout_buffer *buffer = &woinst->buffer;
+	unsigned int voicenum;
+	u32 delay;
+
+	DPF(2, "emu10k1_waveout_open()\n");
+
+	for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
+		if (alloc_buffer(card, buffer, voicenum) < 0) {
+			ERROR();
+			emu10k1_waveout_close(wave_dev);
+			return -1;
+		}
+
+		if (get_voice(card, woinst, voicenum) < 0) {
+			ERROR();
+			emu10k1_waveout_close(wave_dev);
+			return -1;
+		}
+	}
+
+	buffer->fill_silence = 0;
+	buffer->silence_bytes = 0;
+	buffer->silence_pos = 0;
+	buffer->hw_pos = 0;
+	buffer->free_bytes = woinst->buffer.size;
+
+	delay = (48000 * woinst->buffer.fragment_size) /
+		 (woinst->format.samplingrate * woinst->format.bytespervoicesample);
+
+	emu10k1_timer_install(card, &woinst->timer, delay / 2);
+
+	woinst->state = WAVE_STATE_OPEN;
+
+	return 0;
+}
+
+void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct woinst *woinst = wave_dev->woinst;
+	unsigned int voicenum;
+
+	DPF(2, "emu10k1_waveout_close()\n");
+
+	emu10k1_waveout_stop(wave_dev);
+
+	emu10k1_timer_uninstall(card, &woinst->timer);
+
+	for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
+		emu10k1_voice_free(&woinst->voice[voicenum]);
+		free_buffer(card, &woinst->buffer, voicenum);
+	}
+
+	woinst->state = WAVE_STATE_CLOSED;
+}
+
+void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct woinst *woinst = wave_dev->woinst;
+
+	DPF(2, "emu10k1_waveout_start()\n");
+
+	/* Actual start */
+	emu10k1_voices_start(woinst->voice, woinst->num_voices, woinst->total_played);
+
+	emu10k1_timer_enable(card, &woinst->timer);
+
+	woinst->state |= WAVE_STATE_STARTED;
+}
+
+int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct woinst *woinst = wave_dev->woinst;
+	unsigned int voicenum;
+	u32 delay;
+
+	DPF(2, "emu10k1_waveout_setformat()\n");
+
+	if (woinst->state & WAVE_STATE_STARTED)
+		return -1;
+
+	query_format(wave_dev, format);
+
+	if (woinst->format.samplingrate != format->samplingrate ||
+	    woinst->format.channels != format->channels ||
+	    woinst->format.bitsperchannel != format->bitsperchannel) {
+
+		woinst->format = *format;
+
+		if (woinst->state == WAVE_STATE_CLOSED)
+			return 0;
+
+		emu10k1_timer_uninstall(card, &woinst->timer);
+
+		for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
+			emu10k1_voice_free(&woinst->voice[voicenum]);
+
+			if (get_voice(card, woinst, voicenum) < 0) {
+				ERROR();
+				emu10k1_waveout_close(wave_dev);
+				return -1;
+			}
+		}
+
+		delay = (48000 * woinst->buffer.fragment_size) /
+			 (woinst->format.samplingrate * woinst->format.bytespervoicesample);
+
+		emu10k1_timer_install(card, &woinst->timer, delay / 2);
+	}
+
+	return 0;
+}
+
+void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev)
+{
+	struct emu10k1_card *card = wave_dev->card;
+	struct woinst *woinst = wave_dev->woinst;
+
+	DPF(2, "emu10k1_waveout_stop()\n");
+
+	if (!(woinst->state & WAVE_STATE_STARTED))
+		return;
+
+	emu10k1_timer_disable(card, &woinst->timer);
+
+	/* Stop actual voices */
+	emu10k1_voices_stop(woinst->voice, woinst->num_voices);
+
+	emu10k1_waveout_update(woinst);
+
+	woinst->state &= ~WAVE_STATE_STARTED;
+}
+
+/**
+ * emu10k1_waveout_getxfersize -
+ *
+ * gives the total free bytes on the voice buffer, including silence bytes
+ * (basically: total_free_bytes = free_bytes + silence_bytes).
+ *
+ */
+void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 *total_free_bytes)
+{
+	struct waveout_buffer *buffer = &woinst->buffer;
+	int pending_bytes;
+
+	if (woinst->mmapped) {
+		*total_free_bytes = buffer->free_bytes;
+		return;
+	}
+
+	pending_bytes = buffer->size - buffer->free_bytes;
+
+	buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size) ? 1 : 0;
+
+	if (pending_bytes > (signed) buffer->silence_bytes) {
+		*total_free_bytes = (buffer->free_bytes + buffer->silence_bytes);
+	} else {
+		*total_free_bytes = buffer->size;
+		buffer->silence_bytes = pending_bytes;
+		if (pending_bytes < 0) {
+			buffer->silence_pos = buffer->hw_pos;
+			buffer->silence_bytes = 0;
+			buffer->free_bytes = buffer->size;
+			DPF(1, "buffer underrun\n");
+		}
+	}
+}
+
+/**
+ * copy_block -
+ *
+ * copies a block of pcm data to a voice buffer.
+ * Notice that the voice buffer is actually a set of disjointed memory pages.
+ *
+ */
+static void copy_block(void **dst, u32 str, u8 *src, u32 len)
+{
+	unsigned int pg;
+	unsigned int pgoff;
+	unsigned int k;
+
+	pg = str / PAGE_SIZE;
+	pgoff = str % PAGE_SIZE;
+
+	if (len > PAGE_SIZE - pgoff) {
+		k = PAGE_SIZE - pgoff;
+		__copy_from_user((u8 *)dst[pg] + pgoff, src, k);
+		len -= k;
+		while (len > PAGE_SIZE) {
+			__copy_from_user(dst[++pg], src + k, PAGE_SIZE);
+			k += PAGE_SIZE;
+			len -= PAGE_SIZE;
+		}
+		__copy_from_user(dst[++pg], src + k, len);
+
+	} else
+		__copy_from_user((u8 *)dst[pg] + pgoff, src, len);
+}
+
+/**
+ * copy_ilv_block -
+ *
+ * copies a block of pcm data containing n interleaved channels to n mono voice buffers.
+ * Notice that the voice buffer is actually a set of disjointed memory pages.
+ *
+ */
+static void copy_ilv_block(struct woinst *woinst, u32 str, u8 *src, u32 len) 
+{
+        unsigned int pg;
+	unsigned int pgoff;
+	unsigned int voice_num;
+	struct waveout_mem *mem = woinst->buffer.mem;
+
+	pg = str / PAGE_SIZE;
+	pgoff = str % PAGE_SIZE;
+
+	while (len) { 
+		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) {
+			__copy_from_user((u8 *)(mem[voice_num].addr[pg]) + pgoff, src, woinst->format.bytespervoicesample);
+			src += woinst->format.bytespervoicesample;
+		}
+
+		len -= woinst->format.bytespervoicesample;
+
+		pgoff += woinst->format.bytespervoicesample;
+		if (pgoff >= PAGE_SIZE) {
+			pgoff = 0;
+			pg++;
+		}
+	}
+}
+
+/**
+ * fill_block -
+ *
+ * fills a set voice buffers with a block of a given sample.
+ *
+ */
+static void fill_block(struct woinst *woinst, u32 str, u8 data, u32 len)
+{
+	unsigned int pg;
+	unsigned int pgoff;
+	unsigned int voice_num;
+        struct waveout_mem *mem = woinst->buffer.mem;
+	unsigned int  k;
+
+	pg = str / PAGE_SIZE;
+	pgoff = str % PAGE_SIZE;
+
+	if (len > PAGE_SIZE - pgoff) {
+		k = PAGE_SIZE - pgoff;
+		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
+			memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, k);
+		len -= k;
+		while (len > PAGE_SIZE) {
+			pg++;
+			for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
+				memset(mem[voice_num].addr[pg], data, PAGE_SIZE);
+
+			len -= PAGE_SIZE;
+		}
+		pg++;
+		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
+			memset(mem[voice_num].addr[pg], data, len);
+
+	} else {
+		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
+			memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, len);
+	}
+}
+
+/**
+ * emu10k1_waveout_xferdata -
+ *
+ * copies pcm data to the voice buffer. Silence samples
+ * previously added to the buffer are overwritten.
+ *
+ */
+void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size)
+{
+	struct waveout_buffer *buffer = &woinst->buffer;
+	u32 sizetocopy, sizetocopy_now, start;
+	unsigned long flags;
+
+	sizetocopy = min_t(u32, buffer->size, *size);
+	*size = sizetocopy;
+
+	if (!sizetocopy)
+		return;
+	
+	spin_lock_irqsave(&woinst->lock, flags);
+	start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size;
+
+	if (sizetocopy > buffer->silence_bytes) {
+		buffer->silence_pos += sizetocopy - buffer->silence_bytes;
+		buffer->free_bytes -= sizetocopy - buffer->silence_bytes;
+		buffer->silence_bytes = 0;
+	} else
+		buffer->silence_bytes -= sizetocopy;
+
+	spin_unlock_irqrestore(&woinst->lock, flags);
+
+	sizetocopy_now = buffer->size - start;
+	if (sizetocopy > sizetocopy_now) {
+		sizetocopy -= sizetocopy_now;
+		if (woinst->num_voices > 1) {
+			copy_ilv_block(woinst, start, data, sizetocopy_now);
+			copy_ilv_block(woinst, 0, data + sizetocopy_now * woinst->num_voices, sizetocopy);
+		} else {
+			copy_block(buffer->mem[0].addr, start, data, sizetocopy_now);
+			copy_block(buffer->mem[0].addr, 0, data + sizetocopy_now, sizetocopy);
+		}
+	} else {
+		if (woinst->num_voices > 1)
+			copy_ilv_block(woinst, start, data, sizetocopy);
+		else
+			copy_block(buffer->mem[0].addr, start, data, sizetocopy);
+	}
+}
+
+/**
+ * emu10k1_waveout_fillsilence -
+ *
+ * adds samples of silence to the voice buffer so that we
+ * don't loop over stale pcm data.
+ *
+ */
+void emu10k1_waveout_fillsilence(struct woinst *woinst)
+{
+	struct waveout_buffer *buffer = &woinst->buffer;
+	u32 sizetocopy, sizetocopy_now, start;
+	u8 filldata;
+	unsigned long flags;
+
+	sizetocopy = buffer->fragment_size;
+
+	if (woinst->format.bitsperchannel == 16)
+		filldata = 0x00;
+	else
+		filldata = 0x80;
+
+	spin_lock_irqsave(&woinst->lock, flags);
+	buffer->silence_bytes += sizetocopy;
+	buffer->free_bytes -= sizetocopy;
+	buffer->silence_pos %= buffer->size;
+	start = buffer->silence_pos;
+	buffer->silence_pos += sizetocopy;
+	spin_unlock_irqrestore(&woinst->lock, flags);
+
+	sizetocopy_now = buffer->size - start;
+
+	if (sizetocopy > sizetocopy_now) {
+		sizetocopy -= sizetocopy_now;
+		fill_block(woinst, start, filldata, sizetocopy_now);
+		fill_block(woinst, 0, filldata, sizetocopy);
+	} else {
+		fill_block(woinst, start, filldata, sizetocopy);
+	}
+}
+
+/**
+ * emu10k1_waveout_update -
+ *
+ * updates the position of the voice buffer hardware pointer (hw_pos)
+ * and the number of free bytes on the buffer (free_bytes).
+ * The free bytes _don't_ include silence bytes that may have been
+ * added to the buffer.
+ *
+ */
+void emu10k1_waveout_update(struct woinst *woinst)
+{
+	u32 hw_pos;
+	u32 diff;
+	
+	/* There is no actual start yet */
+	if (!(woinst->state & WAVE_STATE_STARTED)) {
+		hw_pos = woinst->buffer.hw_pos;
+	} else {
+		/* hw_pos in sample units */
+		hw_pos = sblive_readptr(woinst->voice[0].card, CCCA_CURRADDR, woinst->voice[0].num);
+
+		if(hw_pos < woinst->voice[0].start)
+			hw_pos += woinst->buffer.size / woinst->format.bytespervoicesample - woinst->voice[0].start;
+		else
+			hw_pos -= woinst->voice[0].start;
+
+		hw_pos *= woinst->format.bytespervoicesample;
+	}
+
+	diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size;
+	woinst->total_played += diff;
+	woinst->buffer.free_bytes += diff;
+	woinst->buffer.hw_pos = hw_pos;
+}
diff -Nru linux/sound/oss/emu10k1/cardwo.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.h
--- linux/sound/oss/emu10k1/cardwo.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,98 @@
+/*     
+ **********************************************************************
+ *     cardwo.h -- header file for card wave out functions
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#ifndef _CARDWO_H
+#define _CARDWO_H
+
+#include "icardwav.h"
+#include "audio.h"
+#include "voicemgr.h"
+#include "timer.h"
+
+/* setting this to other than a power of two may break some applications */
+#define WAVEOUT_MAXBUFSIZE	MAXBUFSIZE
+#define WAVEOUT_MINBUFSIZE	64
+
+#define WAVEOUT_DEFAULTFRAGLEN	20 /* Time to play a fragment in ms (latency) */
+#define WAVEOUT_DEFAULTBUFLEN	500 /* Time to play the entire buffer in ms */
+
+#define WAVEOUT_MINFRAGSHIFT	6
+#define WAVEOUT_MAXVOICES 6
+
+/* waveout_mem is cardwo internal */
+struct waveout_mem {
+	int emupageindex;
+	void *addr[BUFMAXPAGES];
+	dma_addr_t dma_handle[BUFMAXPAGES];
+};
+
+struct waveout_buffer {
+	u16 ossfragshift;
+	u32 numfrags;
+	u32 fragment_size;	/* in bytes units */
+	u32 size;		/* in bytes units */
+	u32 pages;		/* buffer size in page units*/
+	struct waveout_mem mem[WAVEOUT_MAXVOICES];
+	u32 silence_pos;	/* software cursor position (including silence bytes) */
+	u32 hw_pos;		/* hardware cursor position */
+	u32 free_bytes;		/* free bytes available on the buffer (not including silence bytes) */
+	u8 fill_silence;
+	u32 silence_bytes;      /* silence bytes on the buffer */
+};
+
+struct woinst 
+{
+	u8 state;
+	u8 num_voices;
+	struct emu_voice voice[WAVEOUT_MAXVOICES];
+	struct emu_timer timer;
+	struct wave_format format;
+	struct waveout_buffer buffer;
+	wait_queue_head_t wait_queue;
+	u8 mmapped;
+	u32 total_copied;	/* total number of bytes written() to the buffer (excluding silence) */
+	u32 total_played;	/* total number of bytes played including silence */
+	u32 blocks;
+	u8 device;
+	spinlock_t lock;
+};
+
+int emu10k1_waveout_open(struct emu10k1_wavedevice *);
+void emu10k1_waveout_close(struct emu10k1_wavedevice *);
+void emu10k1_waveout_start(struct emu10k1_wavedevice *);
+void emu10k1_waveout_stop(struct emu10k1_wavedevice *);
+void emu10k1_waveout_getxfersize(struct woinst*, u32 *);
+void emu10k1_waveout_xferdata(struct woinst*, u8*, u32 *);
+void emu10k1_waveout_fillsilence(struct woinst*);
+int emu10k1_waveout_setformat(struct emu10k1_wavedevice*, struct wave_format*);
+void emu10k1_waveout_update(struct woinst*);
+
+#endif /* _CARDWO_H */
diff -Nru linux/sound/oss/emu10k1/ecard.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.c
--- linux/sound/oss/emu10k1/ecard.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,157 @@
+/*     
+ **********************************************************************
+ *     ecard.c - E-card initialization code
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */ 
+
+#include "ecard.h"
+#include "hwaccess.h"
+
+/* Private routines */
+static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16);
+static void ecard_write(struct emu10k1_card *, u32);
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller.  The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel.  The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain)
+{
+	u32 currbit;
+	ecard->adc_gain = gain;
+
+	/* Enable writing to the TRIM registers */
+	ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN);
+
+	/* Do it again to insure that we meet hold time requirements */
+	ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN);
+
+	for (currbit = (1L << 15); currbit; currbit >>= 1) {
+
+		u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA);
+
+		if (gain & currbit)
+		      value |= EC_TRIM_SDATA;
+
+		/* Clock the bit */
+		ecard_write(card, value);
+		ecard_write(card, value | EC_TRIM_SCLK);
+		ecard_write(card, value);
+	}
+
+	ecard_write(card, ecard->control_bits);
+}
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch.  The Ecard uses a
+ *  control latch will is loaded bit-serially by toggling the Modem control
+ *  lines from function 2 on the E8010.  This function hides these details
+ *  and presents the illusion that we are actually writing to a distinct
+ *  register.
+ */
+static void ecard_write(struct emu10k1_card *card, u32 value)
+{
+	u16 count;
+	u32 data, hcvalue;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	hcvalue = inl(card->iobase + HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT);
+
+	outl(card->iobase + HCFG, hcvalue);
+
+	for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) {
+	
+		/* Set up the value */
+		data = ((value & 0x1) ? PULSEN_BIT : 0);
+		value >>= 1;
+
+		outl(card->iobase + HCFG, hcvalue | data);
+
+		/* Clock the shift register */
+		outl(card->iobase + HCFG, hcvalue | data | HANDN_BIT);
+		outl(card->iobase + HCFG, hcvalue | data);
+	}
+
+	/* Latch the bits */
+	outl(card->iobase + HCFG, hcvalue | HOOKN_BIT);
+	outl(card->iobase + HCFG, hcvalue);
+
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+void __devinit emu10k1_ecard_init(struct emu10k1_card *card)
+{
+	u32 hcvalue;
+	struct ecard_state ecard;
+
+	/* Set up the initial settings */
+	ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL;
+	ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL;
+	ecard.mux2_setting = 0;
+	ecard.adc_gain = EC_DEFAULT_ADC_GAIN;
+	ecard.control_bits = EC_RAW_RUN_MODE | 
+                             EC_SPDIF0_SELECT(ecard.mux0_setting) |
+			     EC_SPDIF1_SELECT(ecard.mux1_setting);
+
+
+	/* Step 0: Set the codec type in the hardware control register 
+	 * and enable audio output */
+	hcvalue = emu10k1_readfn0(card, HCFG);
+	emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S);
+
+	/* Step 1: Turn off the led and deassert TRIM_CS */
+	ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 2: Calibrate the ADC and DAC */
+	ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 3: Wait for awhile; FIXME: Is this correct? */
+
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(HZ);
+
+	/* Step 4: Switch off the DAC and ADC calibration.  Note
+	 * That ADC_CAL is actually an inverted signal, so we assert
+	 * it here to stop calibration.  */
+	ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 4: Switch into run mode */
+	ecard_write(card, ecard.control_bits);
+
+	/* Step 5: Set the analog input gain */
+	ecard_setadcgain(card, &ecard, ecard.adc_gain);
+}
+
+
diff -Nru linux/sound/oss/emu10k1/ecard.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.h
--- linux/sound/oss/emu10k1/ecard.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,113 @@
+/*     
+ **********************************************************************
+ *     ecard.h
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */ 
+
+#ifndef _ECARD_H
+#define _ECARD_H
+
+#include "8010.h"
+#include "hwaccess.h"
+#include <linux/init.h>
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT   (1L << 12)
+#define HANDN_BIT   (1L << 11)
+#define PULSEN_BIT  (1L << 10)
+
+#define EC_GDI1 (1 << 13)
+#define EC_GDI0 (1 << 14)
+
+#define EC_NUM_CONTROL_BITS 20
+
+#define EC_AC3_DATA_SELN  0x0001L
+#define EC_EE_DATA_SEL    0x0002L
+#define EC_EE_CNTRL_SELN  0x0004L
+#define EC_EECLK          0x0008L
+#define EC_EECS           0x0010L
+#define EC_EESDO          0x0020L
+#define EC_TRIM_CSN	  0x0040L
+#define EC_TRIM_SCLK	  0x0080L
+#define EC_TRIM_SDATA	  0x0100L
+#define EC_TRIM_MUTEN	  0x0200L
+#define EC_ADCCAL	  0x0400L
+#define EC_ADCRSTN	  0x0800L
+#define EC_DACCAL	  0x1000L
+#define EC_DACMUTEN	  0x2000L
+#define EC_LEDN		  0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT	15
+#define EC_SPDIF1_SEL_SHIFT	17	
+#define EC_SPDIF0_SEL_MASK	(0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK	(0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory.  This should
+                                      * be incremented any time the EEPROM's
+                                      * format is changed.  */
+
+#define EC_EEPROM_SIZE	        0x40 /* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR	0x20	/* Address of the current prom version */
+#define EC_BOARDREV0_ADDR	0x21	/* LSW of board rev */
+#define EC_BOARDREV1_ADDR 	0x22	/* MSW of board rev */ 
+
+#define EC_LAST_PROMFILE_ADDR	0x2f
+
+#define EC_SERIALNUM_ADD	0x30	/* First word of serial number.  The number
+                                         * can be up to 30 characters in length
+                                         * and is stored as a NULL-terminated
+                                         * ASCII string.  Any unused bytes must be
+                                         * filled with zeros */
+#define EC_CHECKSUM_ADDR	0x3f    /* Location at which checksum is stored */
+
+
+
+/* Most of this stuff is pretty self-evident.  According to the hardware 
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC 
+ * offset problem.  Weird.
+ */
+#define EC_RAW_RUN_MODE	(EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN   0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL 0x0
+#define EC_DEFAULT_SPDIF1_SEL 0x4
+
+#define HC_EA 0x01L
+
+/* ECARD state structure.  This structure maintains the state
+ * for various portions of the ECARD's onboard hardware.
+ */
+struct ecard_state {
+	u32 control_bits;
+	u16 adc_gain;
+	u16 mux0_setting;
+	u16 mux1_setting;
+	u16 mux2_setting;
+};
+
+void emu10k1_ecard_init(struct emu10k1_card *) __devinit;
+
+#endif /* _ECARD_H */
diff -Nru linux/sound/oss/emu10k1/efxmgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.c
--- linux/sound/oss/emu10k1/efxmgr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,218 @@
+/*
+ **********************************************************************
+ *     efxmgr.c
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#include <linux/bitops.h>
+#include "hwaccess.h"
+#include "efxmgr.h"
+
+int emu10k1_find_control_gpr(struct patch_manager *mgr, const char *patch_name, const char *gpr_name)
+{
+        struct dsp_patch *patch;
+	struct dsp_rpatch *rpatch;
+	char s[PATCH_NAME_SIZE + 4];
+	u32 *gpr_used;
+	int i;
+
+	DPD(2, "emu10k1_find_control_gpr(): %s %s\n", patch_name, gpr_name);
+
+	rpatch = &mgr->rpatch;
+	if (!strcmp(rpatch->name, patch_name)) {
+		gpr_used = rpatch->gpr_used;
+		goto match;
+	}
+
+	for (i = 0; i < mgr->current_pages * PATCHES_PER_PAGE; i++) {
+		patch = PATCH(mgr, i);
+			sprintf(s,"%s", patch->name);
+
+		if (!strcmp(s, patch_name)) {
+			gpr_used = patch->gpr_used;
+			goto match;
+		}
+	}
+
+	return -1;
+
+  match:
+	for (i = 0; i < NUM_GPRS; i++)
+		if (mgr->gpr[i].type == GPR_TYPE_CONTROL &&
+		    test_bit(i, gpr_used) &&
+		    !strcmp(mgr->gpr[i].name, gpr_name))
+			return i;
+
+	return -1;
+}
+
+void emu10k1_set_control_gpr(struct emu10k1_card *card, int addr, s32 val, int flag)
+{
+	struct patch_manager *mgr = &card->mgr;
+
+	DPD(2, "emu10k1_set_control_gpr(): %d %x\n", addr, val);
+
+	if (addr < 0 || addr >= NUM_GPRS)
+		return;
+
+	if (flag)
+		val += sblive_readptr(card, GPR_BASE + addr, 0);
+
+	if (val > mgr->gpr[addr].max)
+		val = mgr->gpr[addr].max;
+	else if (val < mgr->gpr[addr].min)
+		val = mgr->gpr[addr].min;
+
+	sblive_writeptr(card, GPR_BASE + addr, 0, val);
+}
+
+//TODO: make this configurable:
+#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME
+#define VOLCTRL_STEP_SIZE        5
+
+//An internal function for setting OSS mixer controls.
+void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer,
+			 unsigned int left, unsigned int right)
+{
+	extern char volume_params[SOUND_MIXER_NRDEVICES];
+
+	card->ac97.mixer_state[oss_mixer] = (right << 8) | left;
+
+	if (!card->isaps)
+		card->ac97.write_mixer(&card->ac97, oss_mixer, left, right);
+	
+	emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left,
+			       volume_params[oss_mixer]);
+
+	emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right,
+			       volume_params[oss_mixer]);
+}
+
+//FIXME: mute should unmute when pressed a second time
+void emu10k1_mute_irqhandler(struct emu10k1_card *card)
+{
+	int oss_channel = VOLCTRL_CHANNEL;
+	int left, right;
+	static int val = 0;
+
+	if (val) {
+		left = val & 0xff;
+		right = (val >> 8) & 0xff;
+		val = 0;
+	} else {
+		val = card->ac97.mixer_state[oss_channel];
+		left = 0;
+		right = 0;
+	}
+
+	emu10k1_set_oss_vol(card, oss_channel, left, right);
+}
+
+void emu10k1_volincr_irqhandler(struct emu10k1_card *card)
+{
+	int oss_channel = VOLCTRL_CHANNEL;
+	int left, right;
+
+	left = card->ac97.mixer_state[oss_channel] & 0xff;
+	right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff;
+
+	if ((left += VOLCTRL_STEP_SIZE) > 100)
+		left = 100;
+
+	if ((right += VOLCTRL_STEP_SIZE) > 100)
+		right = 100;
+
+	emu10k1_set_oss_vol(card, oss_channel, left, right);
+}
+
+void emu10k1_voldecr_irqhandler(struct emu10k1_card *card)
+{
+	int oss_channel = VOLCTRL_CHANNEL;
+	int left, right;
+
+	left = card->ac97.mixer_state[oss_channel] & 0xff;
+	right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff;
+
+	if ((left -= VOLCTRL_STEP_SIZE) < 0)
+		left = 0;
+
+	if ((right -= VOLCTRL_STEP_SIZE) < 0)
+		right = 0;
+
+	emu10k1_set_oss_vol(card, oss_channel, left, right);
+}
+
+void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale)
+{
+	struct patch_manager *mgr = &card->mgr;
+	unsigned long flags;
+	int muting;
+
+	const s32 log2lin[5] ={                  //  attenuation (dB)
+		0x7fffffff,                      //       0.0         
+		0x7fffffff * 0.840896415253715 , //       1.5          
+		0x7fffffff * 0.707106781186548,  //       3.0
+		0x7fffffff * 0.594603557501361 , //       4.5
+	};
+
+	if (addr < 0)
+		return;
+
+	muting = (scale == 0x10) ? 0x7f: scale;
+	
+	vol = (100 - vol ) * scale / 100;
+
+	// Thanks to the comp.dsp newsgroup for this neat trick:
+	vol = (vol >= muting) ? 0 : (log2lin[vol & 3] >> (vol >> 2));
+
+	spin_lock_irqsave(&mgr->lock, flags);
+	emu10k1_set_control_gpr(card, addr, vol, 0);
+	spin_unlock_irqrestore(&mgr->lock, flags);
+}
+
+void emu10k1_dsp_irqhandler(struct emu10k1_card *card)
+{
+	unsigned long flags;
+
+	if (card->pt.state != PT_STATE_INACTIVE) {
+		u32 bc;
+		bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0);
+		if (bc != 0) {
+			DPD(3, "pt interrupt, bc = %d\n", bc);
+			spin_lock_irqsave(&card->pt.lock, flags);
+			card->pt.blocks_played = bc;
+			if (card->pt.blocks_played >= card->pt.blocks_copied) {
+				DPF(1, "buffer underrun in passthrough playback\n");
+				emu10k1_pt_stop(card);
+			}
+			wake_up_interruptible(&card->pt.wait);
+			spin_unlock_irqrestore(&card->pt.lock, flags);
+		}
+	}
+}
+
diff -Nru linux/sound/oss/emu10k1/efxmgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.h
--- linux/sound/oss/emu10k1/efxmgr.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,254 @@
+/*     
+ **********************************************************************
+ *     sblive_fx.h
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#ifndef _EFXMGR_H
+#define _EFXMGR_H
+
+#define WRITE_EFX(a, b, c) sblive_writeptr((a), MICROCODEBASE + (b), 0, (c))
+
+#define OP(op, z, w, x, y) \
+        do { WRITE_EFX(card, (pc) * 2, ((x) << 10) | (y)); \
+        WRITE_EFX(card, (pc) * 2 + 1, ((op) << 20) | ((z) << 10) | (w)); \
+        ++pc; } while (0)
+
+#define NUM_INPUTS 0x20
+#define NUM_OUTPUTS 0x20
+#define NUM_GPRS 0x100
+#define GPR_NAME_SIZE   32
+#define PATCH_NAME_SIZE 32
+
+struct dsp_rpatch {
+	char name[PATCH_NAME_SIZE];
+        u16 code_start;
+        u16 code_size;
+
+        u32 gpr_used[NUM_GPRS / 32];
+        u32 gpr_input[NUM_GPRS / 32];
+        u32 route[NUM_OUTPUTS];
+        u32 route_v[NUM_OUTPUTS];
+};
+
+struct dsp_patch {
+        char name[PATCH_NAME_SIZE];
+        u8 id;
+        u32 input;                      /* bitmap of the lines used as inputs */
+	u32 output;                     /* bitmap of the lines used as outputs */
+        u16 code_start;
+        u16 code_size;
+
+        u32 gpr_used[NUM_GPRS / 32];    /* bitmap of used gprs */
+        u32 gpr_input[NUM_GPRS / 32];
+        u8 traml_istart;  /* starting address of the internal tram lines used */
+        u8 traml_isize;   /* number of internal tram lines used */
+
+        u8 traml_estart;
+        u8 traml_esize;
+
+        u16 tramb_istart;        /* starting address of the internal tram memory used */
+        u16 tramb_isize;         /* amount of internal memory used */
+        u32 tramb_estart;
+        u32 tramb_esize;
+};
+
+struct dsp_gpr {
+        u8 type;                      /* gpr type, STATIC, DYNAMIC, INPUT, OUTPUT, CONTROL */
+        char name[GPR_NAME_SIZE];       /* gpr value, only valid for control gprs */
+        s32 min, max;         /* value range for this gpr, only valid for control gprs */
+        u8 line;                    /* which input/output line is the gpr attached, only valid for input/output gprs */
+        u8 usage;
+};
+
+enum {
+        GPR_TYPE_NULL = 0,
+        GPR_TYPE_IO,
+        GPR_TYPE_STATIC,
+        GPR_TYPE_DYNAMIC,
+        GPR_TYPE_CONTROL,
+        GPR_TYPE_CONSTANT
+};
+
+#define GPR_BASE 0x100
+#define OUTPUT_BASE 0x20
+
+#define MAX_PATCHES_PAGES 32
+
+struct patch_manager {
+        void *patch[MAX_PATCHES_PAGES];
+	int current_pages;
+        struct dsp_rpatch rpatch;
+        struct dsp_gpr gpr[NUM_GPRS];   /* gpr usage table */
+	spinlock_t lock;
+	s16 ctrl_gpr[SOUND_MIXER_NRDEVICES][2];
+};
+
+
+#define PATCHES_PER_PAGE (PAGE_SIZE / sizeof(struct dsp_patch))
+
+#define PATCH(mgr, i) ((struct dsp_patch *) (mgr)->patch[(i) / PATCHES_PER_PAGE] + (i) % PATCHES_PER_PAGE)
+
+/* PCM volume control */
+#define TMP_PCM_L     0x100 //temp PCM L (after the vol control)       
+#define TMP_PCM_R     0x101
+#define VOL_PCM_L     0x102 //vol PCM
+#define VOL_PCM_R     0x103
+
+/* Routing patch */
+#define TMP_AC_L      0x104 //tmp ac97 out
+#define TMP_AC_R      0x105
+#define TMP_REAR_L    0x106 //output - Temp Rear
+#define TMP_REAR_R    0x107
+#define TMP_DIGI_L    0x108 //output - Temp digital
+#define TMP_DIGI_R    0x109
+#define DSP_VOL_L     0x10a // main dsp volume
+#define DSP_VOL_R     0x10b
+
+/* hw inputs */
+#define PCM_IN_L 	0x00
+#define PCM_IN_R 	0x01
+
+#define PCM1_IN_L        0x04
+#define PCM1_IN_R        0x05
+//mutilchannel playback stream appear here:
+
+#define MULTI_FRONT_L	0x08
+#define MULTI_FRONT_R	0x09
+#define MULTI_REAR_L	0x0a
+#define MULTI_REAR_R	0x0b
+#define MULTI_CENTER	0x0c
+#define MULTI_LFE	0x0d
+
+#define AC97_IN_L	0x10
+#define AC97_IN_R	0x11
+#define SPDIF_CD_L	0x12
+#define SPDIF_CD_R	0x13
+
+/* hw outputs */
+#define AC97_FRONT_L	0x20
+#define AC97_FRONT_R	0x21
+#define DIGITAL_OUT_L	0x22
+#define DIGITAL_OUT_R	0x23
+#define DIGITAL_CENTER	0x24
+#define DIGITAL_LFE	0x25
+
+#define ANALOG_REAR_L	0x28
+#define ANALOG_REAR_R	0x29
+#define ADC_REC_L	0x2a
+#define ADC_REC_R	0x2b
+
+#define ANALOG_CENTER	0x31
+#define ANALOG_LFE	0x32
+
+
+#define INPUT_PATCH_START(patch, nm, ln, i)		\
+do {							\
+	patch = PATCH(mgr, patch_n);			\
+	strcpy(patch->name, nm);			\
+	patch->code_start = pc * 2;			\
+	patch->input = (1<<(0x1f&ln));			\
+	patch->output= (1<<(0x1f&ln));			\
+	patch->id = i;					\
+} while(0)
+
+#define INPUT_PATCH_END(patch)				\
+do {							\
+	patch->code_size = pc * 2 - patch->code_start;	\
+	patch_n++;					\
+} while(0)
+
+
+#define ROUTING_PATCH_START(patch, nm)	\
+do {					\
+	patch = &mgr->rpatch;		\
+	strcpy(patch->name, nm);	\
+	patch->code_start = pc * 2;	\
+} while(0)
+
+#define ROUTING_PATCH_END(patch)			\
+do {                                                    \
+	patch->code_size = pc * 2 - patch->code_start;      \
+} while(0)
+
+#define CONNECT(input, output) set_bit(input, &rpatch->route[(output) - OUTPUT_BASE]);
+
+#define CONNECT_V(input, output) set_bit(input, &rpatch->route_v[(output) - OUTPUT_BASE]);
+
+#define OUTPUT_PATCH_START(patch, nm, ln, i)		\
+do {							\
+	patch = PATCH(mgr, patch_n);			\
+	strcpy(patch->name, nm);			\
+	patch->code_start = pc * 2;			\
+	patch->input = (1<<(0x1f&ln));			\
+	patch->output= (1<<(0x1f&ln));			\
+	patch->id = i;					\
+} while(0)
+
+#define OUTPUT_PATCH_END(patch)				\
+do {							\
+	patch->code_size = pc * 2 - patch->code_start;	\
+	patch_n++;					\
+} while(0)
+
+#define GET_OUTPUT_GPR(patch, g, ln)			\
+do {							\
+	mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO;	\
+	mgr->gpr[(g) - GPR_BASE].usage++;		\
+	mgr->gpr[(g) - GPR_BASE].line = ln;		\
+	set_bit((g) - GPR_BASE, patch->gpr_used);	\
+} while(0)
+
+#define GET_INPUT_GPR(patch, g, ln)			\
+do {							\
+	mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO;	\
+	mgr->gpr[(g) - GPR_BASE].usage++;		\
+	mgr->gpr[(g) - GPR_BASE].line = ln;		\
+	set_bit((g) - GPR_BASE, patch->gpr_used);	\
+	set_bit((g) - GPR_BASE, patch->gpr_input);	\
+} while(0)
+
+#define GET_DYNAMIC_GPR(patch, g)				\
+do {								\
+	mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_DYNAMIC;	\
+	mgr->gpr[(g) - GPR_BASE].usage++;			\
+	set_bit((g) - GPR_BASE, patch->gpr_used);          	\
+} while(0)
+
+#define GET_CONTROL_GPR(patch, g, nm, a, b)			\
+do {								\
+	strcpy(mgr->gpr[(g) - GPR_BASE].name, nm);		\
+	mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_CONTROL;	\
+	mgr->gpr[(g) - GPR_BASE].usage++;			\
+	mgr->gpr[(g) - GPR_BASE].min = a;			\
+	mgr->gpr[(g) - GPR_BASE].max = b;			\
+	sblive_writeptr(card, g, 0, b);				\
+	set_bit((g) - GPR_BASE, patch->gpr_used);		\
+} while(0)
+
+#endif /* _EFXMGR_H */
diff -Nru linux/sound/oss/emu10k1/emuadxmg.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/emuadxmg.c
--- linux/sound/oss/emu10k1/emuadxmg.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/emuadxmg.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,104 @@
+
+/*     
+ **********************************************************************
+ *     emuadxmg.c - Address space manager for emu10k1 driver 
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#include "hwaccess.h"
+
+/* Allocates emu address space */
+
+int emu10k1_addxmgr_alloc(u32 size, struct emu10k1_card *card)
+{
+	u16 *pagetable = card->emupagetable;
+	u16 index = 0;
+	u16 numpages;
+	unsigned long flags;
+
+	/* Convert bytes to pages */
+	numpages = (size / EMUPAGESIZE) + ((size % EMUPAGESIZE) ? 1 : 0);
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	while (index < (MAXPAGES - 1)) {
+		if (pagetable[index] & 0x8000) {
+			/* This block of pages is in use, jump to the start of the next block. */
+			index += (pagetable[index] & 0x7fff);
+		} else {
+			/* Found free block */
+			if (pagetable[index] >= numpages) {
+
+				/* Block is large enough */
+
+				/* If free block is larger than the block requested
+				 * then adjust the size of the block remaining */
+				if (pagetable[index] > numpages)
+					pagetable[index + numpages] = pagetable[index] - numpages;
+
+				pagetable[index] = (numpages | 0x8000);	/* Mark block as used */
+
+				spin_unlock_irqrestore(&card->lock, flags);
+
+				return index;
+			} else {
+				/* Block too small, jump to the start of the next block */
+				index += pagetable[index];
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return -1;
+}
+
+/* Frees a previously allocated emu address space. */
+
+void emu10k1_addxmgr_free(struct emu10k1_card *card, int index)
+{
+	u16 *pagetable = card->emupagetable;
+	u16 origsize = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	if (pagetable[index] & 0x8000) {
+		/* Block is allocated - mark block as free */
+		origsize = pagetable[index] & 0x7fff;
+		pagetable[index] = origsize;
+
+		/* If next block is free, we concat both blocks */
+		if (!(pagetable[index + origsize] & 0x8000))
+			pagetable[index] += pagetable[index + origsize] & 0x7fff;
+	}
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return;
+}
diff -Nru linux/sound/oss/emu10k1/hwaccess.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.c
--- linux/sound/oss/emu10k1/hwaccess.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,480 @@
+/*
+ **********************************************************************
+ *     hwaccess.c -- Hardware access layer
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     December 9, 1999     Jon Taylor      rewrote the I/O subsystem
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include <asm/io.h>
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "icardmid.h"
+
+/*************************************************************************
+* Function : srToPitch                                                   *
+* Input    : sampleRate - sampling rate                                  *
+* Return   : pitch value                                                 *
+* About    : convert sampling rate to pitch                              *
+* Note     : for 8010, sampling rate is at 48kHz, this function should   *
+*            be changed.                                                 *
+*************************************************************************/
+u32 srToPitch(u32 sampleRate)
+{
+	int i;
+
+	/* FIXME: These tables should be defined in a headerfile */
+	static u32 logMagTable[128] = {
+		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+		0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+		0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+		0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+		0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
+		0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+		0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
+		0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+		0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+		0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+	};
+
+	static char logSlopeTable[128] = {
+		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+	};
+
+	if (sampleRate == 0)
+		return 0;	/* Bail out if no leading "1" */
+
+	sampleRate *= 11185;	/* Scale 48000 to 0x20002380 */
+
+	for (i = 31; i > 0; i--) {
+		if (sampleRate & 0x80000000) {	/* Detect leading "1" */
+			return (u32) (((s32) (i - 15) << 20) +
+				      logMagTable[0x7f & (sampleRate >> 24)] +
+				      (0x7f & (sampleRate >> 17)) * logSlopeTable[0x7f & (sampleRate >> 24)]);
+		}
+		sampleRate = sampleRate << 1;
+	}
+
+	DPF(2, "srToPitch: BUG!\n");
+	return 0;		/* Should never reach this point */
+}
+
+/* Returns an attenuation based upon a cumulative volume value */
+
+/* Algorithm calculates 0x200 - 0x10 log2 (input) */
+u8 sumVolumeToAttenuation(u32 value)
+{
+	u16 count = 16;
+	s16 ans;
+
+	if (value == 0)
+		return 0xFF;
+
+	/* Find first SET bit. This is the integer part of the value */
+	while ((value & 0x10000) == 0) {
+		value <<= 1;
+		count--;
+	}
+
+	/* The REST of the data is the fractional part. */
+	ans = (s16) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12)));
+	if (ans > 0xFF)
+		ans = 0xFF;
+
+	return (u8) ans;
+}
+
+/*******************************************
+* write/read PCI function 0 registers      *
+********************************************/
+void emu10k1_writefn0(struct emu10k1_card *card, u32 reg, u32 data)
+{
+	unsigned long flags;
+
+	if (reg & 0xff000000) {
+		u32 mask;
+		u8 size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		data = (data << offset) & mask;
+		reg &= 0x7f;
+
+		spin_lock_irqsave(&card->lock, flags);
+		data |= inl(card->iobase + reg) & ~mask;
+		outl(data, card->iobase + reg);
+		spin_unlock_irqrestore(&card->lock, flags);
+	} else {
+		spin_lock_irqsave(&card->lock, flags);
+		outl(data, card->iobase + reg);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+
+	return;
+}
+
+u32 emu10k1_readfn0(struct emu10k1_card * card, u32 reg)
+{
+	u32 val;
+	unsigned long flags;
+
+	if (reg & 0xff000000) {
+		u32 mask;
+		u8 size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		reg &= 0x7f;
+
+		spin_lock_irqsave(&card->lock, flags);
+		val = inl(card->iobase + reg);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		return (val & mask) >> offset;
+        } else {
+		spin_lock_irqsave(&card->lock, flags);
+		val = inl(card->iobase + reg);
+		spin_unlock_irqrestore(&card->lock, flags);
+		return val;
+	}
+}
+
+/************************************************************************
+* write/read Emu10k1 pointer-offset register set, accessed through      *
+*  the PTR and DATA registers                                           *
+*************************************************************************/
+void sblive_writeptr(struct emu10k1_card *card, u32 reg, u32 channel, u32 data)
+{
+	u32 regptr;
+	unsigned long flags;
+
+	regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		u32 mask;
+		u8 size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		data = (data << offset) & mask;
+
+		spin_lock_irqsave(&card->lock, flags);
+		outl(regptr, card->iobase + PTR);
+		data |= inl(card->iobase + DATA) & ~mask;
+		outl(data, card->iobase + DATA);
+		spin_unlock_irqrestore(&card->lock, flags);
+	} else {
+		spin_lock_irqsave(&card->lock, flags);
+		outl(regptr, card->iobase + PTR);
+		outl(data, card->iobase + DATA);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+}
+
+/* ... :  data, reg, ... , TAGLIST_END */
+void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...)
+{
+	va_list args;
+
+	unsigned long flags;
+        u32 reg;
+
+	va_start(args, channel);
+
+	spin_lock_irqsave(&card->lock, flags);
+	while ((reg = va_arg(args, u32)) != TAGLIST_END) {
+		u32 data = va_arg(args, u32);
+		u32 regptr = (((reg << 16) & PTR_ADDRESS_MASK)
+			      | (channel & PTR_CHANNELNUM_MASK));
+		outl(regptr, card->iobase + PTR);
+		if (reg & 0xff000000) {
+			int size = (reg >> 24) & 0x3f;
+                        int offset = (reg >> 16) & 0x1f;
+			u32 mask = ((1 << size) - 1) << offset;
+			data = (data << offset) & mask;
+
+			data |= inl(card->iobase + DATA) & ~mask;
+		}
+		outl(data, card->iobase + DATA);
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	va_end(args);
+
+	return;
+}
+
+u32 sblive_readptr(struct emu10k1_card * card, u32 reg, u32 channel)
+{
+	u32 regptr, val;
+	unsigned long flags;
+
+	regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		u32 mask;
+		u8 size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+
+		spin_lock_irqsave(&card->lock, flags);
+		outl(regptr, card->iobase + PTR);
+		val = inl(card->iobase + DATA);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		return (val & mask) >> offset;
+	} else {
+		spin_lock_irqsave(&card->lock, flags);
+		outl(regptr, card->iobase + PTR);
+		val = inl(card->iobase + DATA);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		return val;
+	}
+}
+
+void emu10k1_irq_enable(struct emu10k1_card *card, u32 irq_mask)
+{
+	u32 val;
+	unsigned long flags;
+
+	DPF(2,"emu10k1_irq_enable()\n");
+
+	spin_lock_irqsave(&card->lock, flags);
+        val = inl(card->iobase + INTE) | irq_mask;
+        outl(val, card->iobase + INTE);
+	spin_unlock_irqrestore(&card->lock, flags);
+	return;
+}
+
+void emu10k1_irq_disable(struct emu10k1_card *card, u32 irq_mask)
+{
+        u32 val;
+        unsigned long flags;
+
+        DPF(2,"emu10k1_irq_disable()\n");
+
+        spin_lock_irqsave(&card->lock, flags);
+        val = inl(card->iobase + INTE) & ~irq_mask;
+        outl(val, card->iobase + INTE);
+        spin_unlock_irqrestore(&card->lock, flags);
+        return;
+}
+
+void emu10k1_set_stop_on_loop(struct emu10k1_card *card, u32 voicenum)
+{
+	/* Voice interrupt */
+	if (voicenum >= 32)
+		sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 1);
+	else
+		sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 1);
+
+	return;
+}
+
+void emu10k1_clear_stop_on_loop(struct emu10k1_card *card, u32 voicenum)
+{
+	/* Voice interrupt */
+	if (voicenum >= 32)
+		sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 0);
+	else
+		sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 0);
+
+	return;
+}
+
+static void sblive_wcwait(struct emu10k1_card *card, u32 wait)
+{
+	volatile unsigned uCount;
+	u32 newtime = 0, curtime;
+
+	curtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER);
+	while (wait--) {
+		uCount = 0;
+		while (uCount++ < TIMEOUT) {
+			newtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER);
+			if (newtime != curtime)
+				break;
+		}
+
+		if (uCount >= TIMEOUT)
+			break;
+
+		curtime = newtime;
+	}
+}
+
+u16 emu10k1_ac97_read(struct ac97_codec *codec, u8 reg)
+{
+	struct emu10k1_card *card = codec->private_data;
+	u16 data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	outb(reg, card->iobase + AC97ADDRESS);
+	data = inw(card->iobase + AC97DATA);
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return data;
+}
+
+void emu10k1_ac97_write(struct ac97_codec *codec, u8 reg, u16 value)
+{
+	struct emu10k1_card *card = codec->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	outb(reg, card->iobase + AC97ADDRESS);
+	outw(value, card->iobase + AC97DATA);
+
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/*********************************************************
+*            MPU access functions                        *
+**********************************************************/
+
+int emu10k1_mpu_write_data(struct emu10k1_card *card, u8 data)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	if ((inb(card->iobase + MUSTAT) & MUSTAT_ORDYN) == 0) {
+		outb(data, card->iobase + MUDATA);
+		ret = 0;
+	} else
+		ret = -1;
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return ret;
+}
+
+int emu10k1_mpu_read_data(struct emu10k1_card *card, u8 * data)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	if ((inb(card->iobase + MUSTAT) & MUSTAT_IRDYN) == 0) {
+		*data = inb(card->iobase + MUDATA);
+		ret = 0;
+	} else
+		ret = -1;
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return ret;
+}
+
+int emu10k1_mpu_reset(struct emu10k1_card *card)
+{
+	u8 status;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_mpu_reset()\n");
+
+	if (card->mpuacqcount == 0) {
+		spin_lock_irqsave(&card->lock, flags);
+		outb(MUCMD_RESET, card->iobase + MUCMD);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		sblive_wcwait(card, 8);
+
+		spin_lock_irqsave(&card->lock, flags);
+		outb(MUCMD_RESET, card->iobase + MUCMD);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		sblive_wcwait(card, 8);
+
+		spin_lock_irqsave(&card->lock, flags);
+		outb(MUCMD_ENTERUARTMODE, card->iobase + MUCMD);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		sblive_wcwait(card, 8);
+
+		spin_lock_irqsave(&card->lock, flags);
+		status = inb(card->iobase + MUDATA);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		if (status == 0xfe)
+			return 0;
+		else
+			return -1;
+	}
+
+	return 0;
+}
+
+int emu10k1_mpu_acquire(struct emu10k1_card *card)
+{
+	/* FIXME: This should be a macro */
+	++card->mpuacqcount;
+
+	return 0;
+}
+
+int emu10k1_mpu_release(struct emu10k1_card *card)
+{
+	/* FIXME: this should be a macro */
+	--card->mpuacqcount;
+
+	return 0;
+}
diff -Nru linux/sound/oss/emu10k1/hwaccess.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.h
--- linux/sound/oss/emu10k1/hwaccess.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,239 @@
+/*
+ **********************************************************************
+ *     hwaccess.h
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date		    Author	    Summary of changes
+ *     ----		    ------	    ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _HWACCESS_H
+#define _HWACCESS_H
+
+#include <linux/fs.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/ac97_codec.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+
+#include "efxmgr.h"
+#include "passthrough.h"
+#include "midi.h"
+
+#define EMUPAGESIZE     4096            /* don't change */
+#define NUM_G           64              /* use all channels */
+#define NUM_FXSENDS     4               /* don't change */
+/* setting this to other than a power of two may break some applications */
+#define MAXBUFSIZE	65536
+#define MAXPAGES	8192 
+#define BUFMAXPAGES     (MAXBUFSIZE / PAGE_SIZE)
+
+#define FLAGS_AVAILABLE     0x0001
+#define FLAGS_READY         0x0002
+
+struct memhandle
+{
+	dma_addr_t dma_handle;
+	void *addr;
+	u32 size;
+};
+
+#define DEBUG_LEVEL 2
+
+#ifdef EMU10K1_DEBUG
+# define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0)
+# define DPF(level,x)   do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0)
+#else
+# define DPD(level,x,y...) do { } while (0) /* not debugging: nothing */
+# define DPF(level,x) do { } while (0)
+#endif /* EMU10K1_DEBUG */
+
+#define ERROR() DPF(1,"error\n")
+
+/* DATA STRUCTURES */
+
+struct emu10k1_waveout
+{
+	u16 send_routing[3];
+
+	u8 send_a[3];
+	u8 send_b[3];
+	u8 send_c[3];
+	u8 send_d[3];
+};
+
+struct emu10k1_wavein
+{
+        struct wiinst *ac97;
+        struct wiinst *mic;
+        struct wiinst *fx;
+
+        u8 recsrc;
+        u32 fxwc;
+};
+
+#define CMD_READ 1
+#define CMD_WRITE 2
+
+struct mixer_private_ioctl {
+        u32 cmd;
+        u32 val[90];
+};
+
+/* bogus ioctls numbers to escape from OSS mixer limitations */
+#define CMD_WRITEFN0            _IOW('D', 0, struct mixer_private_ioctl)
+#define CMD_READFN0		_IOR('D', 1, struct mixer_private_ioctl) 
+#define CMD_WRITEPTR		_IOW('D', 2, struct mixer_private_ioctl) 
+#define CMD_READPTR		_IOR('D', 3, struct mixer_private_ioctl) 
+#define CMD_SETRECSRC		_IOW('D', 4, struct mixer_private_ioctl) 
+#define CMD_GETRECSRC		_IOR('D', 5, struct mixer_private_ioctl) 
+#define CMD_GETVOICEPARAM	_IOR('D', 6, struct mixer_private_ioctl) 
+#define CMD_SETVOICEPARAM	_IOW('D', 7, struct mixer_private_ioctl) 
+#define CMD_GETPATCH		_IOR('D', 8, struct mixer_private_ioctl) 
+#define CMD_GETGPR		_IOR('D', 9, struct mixer_private_ioctl) 
+#define CMD_GETCTLGPR           _IOR('D', 10, struct mixer_private_ioctl)
+#define CMD_SETPATCH		_IOW('D', 11, struct mixer_private_ioctl) 
+#define CMD_SETGPR		_IOW('D', 12, struct mixer_private_ioctl) 
+#define CMD_SETCTLGPR		_IOW('D', 13, struct mixer_private_ioctl)
+#define CMD_SETGPOUT		_IOW('D', 14, struct mixer_private_ioctl)
+#define CMD_GETGPR2OSS		_IOR('D', 15, struct mixer_private_ioctl)
+#define CMD_SETGPR2OSS		_IOW('D', 16, struct mixer_private_ioctl)
+#define CMD_SETMCH_FX		_IOW('D', 17, struct mixer_private_ioctl)
+#define CMD_SETPASSTHROUGH	_IOW('D', 18, struct mixer_private_ioctl)
+#define CMD_PRIVATE3_VERSION	_IOW('D', 19, struct mixer_private_ioctl)
+
+//up this number when breaking compatibility
+#define PRIVATE3_VERSION 1
+
+struct emu10k1_card 
+{
+	struct list_head list;
+
+	struct memhandle	virtualpagetable;
+	struct memhandle	tankmem;
+	struct memhandle	silentpage;
+
+	spinlock_t		lock;
+
+	u8			voicetable[NUM_G];
+	u16			emupagetable[MAXPAGES];
+
+	struct list_head	timers;
+	unsigned		timer_delay;
+	spinlock_t		timer_lock;
+
+	struct pci_dev		*pci_dev;
+	unsigned long           iobase;
+	unsigned long		length;
+	unsigned short		model;
+	unsigned int irq; 
+
+	int	audio_dev;
+	int	audio_dev1;
+	int	midi_dev;
+#ifdef EMU10K1_SEQUENCER
+	int seq_dev;
+	struct emu10k1_mididevice *seq_mididev;
+#endif
+
+	struct ac97_codec ac97;
+	int ac97_supported_mixers;
+	int ac97_stereo_mixers;
+
+	/* Number of first fx voice for multichannel output */
+	u8 mchannel_fx;
+	struct emu10k1_waveout	waveout;
+	struct emu10k1_wavein	wavein;
+	struct emu10k1_mpuout	*mpuout;
+	struct emu10k1_mpuin	*mpuin;
+
+	struct semaphore	open_sem;
+	mode_t			open_mode;
+	wait_queue_head_t	open_wait;
+
+	u32	    mpuacqcount;	  // Mpu acquire count
+	u32	    has_toslink;	       // TOSLink detection
+
+	u8 chiprev;                    /* Chip revision                */
+
+	int isaps;
+
+	struct patch_manager mgr;
+	struct pt_data pt;
+};
+
+int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *);
+void emu10k1_addxmgr_free(struct emu10k1_card *, int);
+
+
+
+int emu10k1_find_control_gpr(struct patch_manager *, const char *, const char *);
+void emu10k1_set_control_gpr(struct emu10k1_card *, int , s32, int );
+
+void emu10k1_set_volume_gpr(struct emu10k1_card *, int, s32, int);
+
+
+#define VOL_6BIT 0x40
+#define VOL_5BIT 0x20
+#define VOL_4BIT 0x10
+
+#define TIMEOUT 		    16384
+
+u32 srToPitch(u32);
+u8 sumVolumeToAttenuation(u32);
+
+extern struct list_head emu10k1_devs;
+
+/* Hardware Abstraction Layer access functions */
+
+void emu10k1_writefn0(struct emu10k1_card *, u32 , u32 );
+u32 emu10k1_readfn0(struct emu10k1_card *, u32 );
+
+void sblive_writeptr(struct emu10k1_card *, u32 , u32 , u32 );
+void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...);
+#define TAGLIST_END 0
+
+u32 sblive_readptr(struct emu10k1_card *, u32 , u32 );
+
+void emu10k1_irq_enable(struct emu10k1_card *, u32);
+void emu10k1_irq_disable(struct emu10k1_card *, u32);
+void emu10k1_set_stop_on_loop(struct emu10k1_card *, u32);
+void emu10k1_clear_stop_on_loop(struct emu10k1_card *, u32);
+
+/* AC97 Codec register access function */
+u16 emu10k1_ac97_read(struct ac97_codec *, u8);
+void emu10k1_ac97_write(struct ac97_codec *, u8, u16);
+
+/* MPU access function*/
+int emu10k1_mpu_write_data(struct emu10k1_card *, u8);
+int emu10k1_mpu_read_data(struct emu10k1_card *, u8 *);
+int emu10k1_mpu_reset(struct emu10k1_card *);
+int emu10k1_mpu_acquire(struct emu10k1_card *);
+int emu10k1_mpu_release(struct emu10k1_card *);
+
+#endif  /* _HWACCESS_H */
diff -Nru linux/sound/oss/emu10k1/icardmid.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardmid.h
--- linux/sound/oss/emu10k1/icardmid.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardmid.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,163 @@
+/*
+ **********************************************************************
+ *     isblive_mid.h
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _ICARDMIDI_H
+#define _ICARDMIDI_H
+
+/* MIDI defines */
+#define MIDI_DATA_FIRST                 0x00
+#define MIDI_DATA_LAST                  0x7F
+#define MIDI_STATUS_FIRST               0x80
+#define MIDI_STATUS_LAST                0xFF
+
+/* Channel status bytes */
+#define MIDI_STATUS_CHANNEL_FIRST       0x80
+#define MIDI_STATUS_CHANNEL_LAST        0xE0
+#define MIDI_STATUS_CHANNEL_MASK        0xF0
+
+/* Channel voice messages */
+#define MIDI_VOICE_NOTE_OFF             0x80
+#define MIDI_VOICE_NOTE_ON              0x90
+#define MIDI_VOICE_POLY_PRESSURE        0xA0
+#define MIDI_VOICE_CONTROL_CHANGE       0xB0
+#define MIDI_VOICE_PROGRAM_CHANGE       0xC0
+#define MIDI_VOICE_CHANNEL_PRESSURE     0xD0
+#define MIDI_VOICE_PITCH_BEND           0xE0
+
+/* Channel mode messages */
+#define MIDI_MODE_CHANNEL               MIDI_VOICE_CONTROL_CHANGE
+
+/* System status bytes */
+#define MIDI_STATUS_SYSTEM_FIRST        0xF0
+#define MIDI_STATUS_SYSTEM_LAST         0xFF
+
+/* System exclusive messages */
+#define MIDI_SYSEX_BEGIN                0xF0
+#define MIDI_SYSEX_EOX                  0xF7
+
+/* System common messages */
+#define MIDI_COMMON_TCQF                0xF1	/* Time code quarter frame  */
+#define MIDI_COMMON_SONG_POSITION       0xF2
+#define MIDI_COMMON_SONG_SELECT         0xF3
+#define MIDI_COMMON_UNDEFINED_F4        0xF4
+#define MIDI_COMMON_UNDEFINED_F5        0xF5
+#define MIDI_COMMON_TUNE_REQUEST        0xF6
+
+/* System real-time messages */
+#define MIDI_RTIME_TIMING_CLOCK         0xF8
+#define MIDI_RTIME_UNDEFINED_F9         0xF9
+#define MIDI_RTIME_START                0xFA
+#define MIDI_RTIME_CONTINUE             0xFB
+#define MIDI_RTIME_STOP                 0xFC
+#define MIDI_RTIME_UNDEFINED_FD         0xFD
+#define MIDI_RTIME_ACTIVE_SENSING       0xFE
+#define MIDI_RTIME_SYSTEM_RESET         0xFF
+
+/* Flags for flags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */
+#define MIDI_CACHE_ALL                  1
+#define MIDI_CACHE_BESTFIT              2
+#define MIDI_CACHE_QUERY                3
+#define MIDI_UNCACHE                    4
+
+/* Event declarations for MPU IRQ Callbacks */
+#define ICARDMIDI_INLONGDATA            0x00000001 /* MIM_LONGDATA */
+#define ICARDMIDI_INLONGERROR           0x00000002 /* MIM_LONGERROR */
+#define ICARDMIDI_OUTLONGDATA           0x00000004 /* MOM_DONE for MPU OUT buffer */
+#define ICARDMIDI_INDATA                0x00000010 /* MIM_DATA */
+#define ICARDMIDI_INDATAERROR           0x00000020 /* MIM_ERROR */
+
+/* Declaration for flags in CARDMIDIBUFFERHDR */
+/* Make it the same as MHDR_DONE, MHDR_INQUEUE in mmsystem.h */
+#define MIDIBUF_DONE                    0x00000001
+#define MIDIBUF_INQUEUE                 0x00000004
+
+/* Declaration for msg parameter in midiCallbackFn */
+#define ICARDMIDI_OUTBUFFEROK           0x00000001
+#define ICARDMIDI_INMIDIOK              0x00000002
+
+/* Declaration for technology in struct midi_caps */
+#define MT_MIDIPORT                     0x00000001	/* In original MIDIOUTCAPS structure */
+#define MT_FMSYNTH                      0x00000004	/* In original MIDIOUTCAPS structure */
+#define MT_AWESYNTH                     0x00001000
+#define MT_PCISYNTH                     0x00002000
+#define MT_PCISYNTH64                   0x00004000
+#define CARDMIDI_AWEMASK                0x0000F000
+
+enum LocalErrorCode
+{
+        CTSTATUS_NOTENABLED = 0x7000,
+        CTSTATUS_READY,
+        CTSTATUS_BUSY,
+        CTSTATUS_DATAAVAIL,
+        CTSTATUS_NODATA,
+        CTSTATUS_NEXT_BYTE
+};
+
+/* MIDI data block header */
+struct midi_hdr
+{
+	u8 *reserved;		/* Pointer to original locked data block */
+	u32 bufferlength;	/* Length of data in data block */
+	u32 bytesrecorded;	/* Used for input only */
+	u32 user;		/* For client's use */
+	u32 flags;		/* Assorted flags (see defines) */
+	struct list_head list;	/* Reserved for driver */
+	u8 *data;		/* Second copy of first pointer */
+};
+
+/* Enumeration for SetControl */
+enum
+{
+	MIDIOBJVOLUME = 0x1,
+	MIDIQUERYACTIVEINST
+};
+
+struct midi_queue
+{
+	struct midi_queue  *next;
+	u32 qtype;            /* 0 = short message, 1 = long data */
+	u32 length;
+	u32 sizeLeft;
+	u8 *midibyte;
+	unsigned long refdata;
+};
+
+struct midi_openinfo
+{
+	u32     cbsize;
+	u32     flags;
+	unsigned long  refdata;
+	u32     streamid;
+};
+
+int emu10k1_midi_callback(unsigned long , unsigned long, unsigned long *);
+
+#endif /* _ICARDMIDI_H */
diff -Nru linux/sound/oss/emu10k1/icardwav.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardwav.h
--- linux/sound/oss/emu10k1/icardwav.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardwav.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,53 @@
+/*     
+ **********************************************************************
+ *     icardwav.h
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#ifndef _ICARDWAV_H
+#define _ICARDWAV_H
+
+struct wave_format 
+{
+	int id;
+	int samplingrate;
+	u8 bitsperchannel;
+	u8 channels;		/* 1 = Mono, 2 = Stereo, 3, ... = Multichannel */
+	u8 bytesperchannel;
+	u8 bytespervoicesample;
+	u8 bytespersample;
+	int bytespersec;
+	u8 passthrough;
+};
+
+/* emu10k1_wave states */
+#define WAVE_STATE_OPEN		0x01	
+#define WAVE_STATE_STARTED	0x02
+#define WAVE_STATE_CLOSED	0x04
+
+#endif /* _ICARDWAV_H */
diff -Nru linux/sound/oss/emu10k1/irqmgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.c
--- linux/sound/oss/emu10k1/irqmgr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,104 @@
+
+/*
+ **********************************************************************
+ *     irqmgr.c - IRQ manager for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "cardmi.h"
+#include "cardmo.h"
+#include "irqmgr.h"
+
+/* Interrupt handler */
+
+void emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct emu10k1_card *card = (struct emu10k1_card *) dev_id;
+	u32 irqstatus;
+
+	DPD(4, "emu10k1_interrupt called, irq =  %u\n", irq);
+
+	/*
+	 ** NOTE :
+	 ** We do a 'while loop' here cos on certain machines, with both
+	 ** playback and recording going on at the same time, IRQs will
+	 ** stop coming in after a while. Checking IPND indeed shows that
+	 ** there are interrupts pending but the PIC says no IRQs pending.
+	 ** I suspect that some boards need edge-triggered IRQs but are not
+	 ** getting that condition if we don't completely clear the IPND
+	 ** (make sure no more interrupts are pending).
+	 ** - Eric
+	 */
+
+	while ((irqstatus = inl(card->iobase + IPR))) {
+		DPD(4, "irq status %#x\n", irqstatus);
+
+		/* acknowledge interrupt */
+		outl(irqstatus, card->iobase + IPR);
+
+		if (irqstatus & IRQTYPE_TIMER) {
+			emu10k1_timer_irqhandler(card);
+			irqstatus &= ~IRQTYPE_TIMER;
+		}
+
+		if (irqstatus & IRQTYPE_DSP) {
+			emu10k1_dsp_irqhandler(card);
+			irqstatus &= ~IRQTYPE_DSP;
+		}
+
+		if (irqstatus & IRQTYPE_MPUIN) {
+			emu10k1_mpuin_irqhandler(card);
+			irqstatus &= ~IRQTYPE_MPUIN;
+		}
+
+		if (irqstatus & IRQTYPE_MPUOUT) {
+			emu10k1_mpuout_irqhandler(card);
+			irqstatus &= ~IRQTYPE_MPUOUT;
+		}
+
+		if (irqstatus & IPR_MUTE) {
+			emu10k1_mute_irqhandler(card);
+			irqstatus &=~IPR_MUTE;
+		}
+
+		if (irqstatus & IPR_VOLINCR) {
+			emu10k1_volincr_irqhandler(card);
+			irqstatus &=~IPR_VOLINCR;
+		}
+
+		if (irqstatus & IPR_VOLDECR) {
+			emu10k1_voldecr_irqhandler(card);
+			irqstatus &=~IPR_VOLDECR;
+		}
+
+		if (irqstatus)
+			emu10k1_irq_disable(card, irqstatus);
+	}
+}
diff -Nru linux/sound/oss/emu10k1/irqmgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.h
--- linux/sound/oss/emu10k1/irqmgr.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,52 @@
+/*
+ **********************************************************************
+ *     irq.h
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _IRQ_H
+#define _IRQ_H
+
+/* EMU Irq Types */
+#define IRQTYPE_PCIBUSERROR         IPR_PCIERROR
+#define IRQTYPE_MIXERBUTTON         (IPR_VOLINCR | IPR_VOLDECR | IPR_MUTE)
+#define IRQTYPE_VOICE               (IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK)
+#define IRQTYPE_RECORD              (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL | IPR_MICBUFFULL | IPR_MICBUFHALFFULL | IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)
+#define IRQTYPE_MPUOUT              IPR_MIDITRANSBUFEMPTY
+#define IRQTYPE_MPUIN               IPR_MIDIRECVBUFEMPTY
+#define IRQTYPE_TIMER               IPR_INTERVALTIMER
+#define IRQTYPE_SPDIF               (IPR_GPSPDIFSTATUSCHANGE | IPR_CDROMSTATUSCHANGE)
+#define IRQTYPE_DSP                 IPR_FXDSP
+
+void emu10k1_timer_irqhandler(struct emu10k1_card *);
+void emu10k1_dsp_irqhandler(struct emu10k1_card *);
+void emu10k1_mute_irqhandler(struct emu10k1_card *);
+void emu10k1_volincr_irqhandler(struct emu10k1_card *);
+void emu10k1_voldecr_irqhandler(struct emu10k1_card *);
+
+#endif /* _IRQ_H */
diff -Nru linux/sound/oss/emu10k1/main.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/main.c
--- linux/sound/oss/emu10k1/main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/main.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1181 @@
+/*
+ **********************************************************************
+ *     main.c - Creative EMU10K1 audio driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999     Alan Cox        cleaned up stuff
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ *
+ *      Supported devices:
+ *      /dev/dsp:        Standard /dev/dsp device, OSS-compatible
+ *      /dev/dsp1:       Routes to rear speakers only	 
+ *      /dev/mixer:      Standard /dev/mixer device, OSS-compatible
+ *      /dev/midi:       Raw MIDI UART device, mostly OSS-compatible
+ *	/dev/sequencer:  Sequencer Interface (requires sound.o)
+ *
+ *      Revision history:
+ *      0.1 beta Initial release
+ *      0.2 Lowered initial mixer vol. Improved on stuttering wave playback. Added MIDI UART support.
+ *      0.3 Fixed mixer routing bug, added APS, joystick support.
+ *      0.4 Added rear-channel, SPDIF support.
+ *	0.5 Source cleanup, SMP fixes, multiopen support, 64 bit arch fixes,
+ *	    moved bh's to tasklets, moved to the new PCI driver initialization style.
+ *	0.6 Make use of pci_alloc_consistent, improve compatibility layer for 2.2 kernels,
+ *	    code reorganization and cleanup.
+ *	0.7 Support for the Emu-APS. Bug fixes for voice cache setup, mmaped sound + poll().
+ *          Support for setting external TRAM size.
+ *      0.8 Make use of the kernel ac97 interface. Support for a dsp patch manager.
+ *      0.9 Re-enables rear speakers volume controls
+ *     0.10 Initializes rear speaker volume.
+ *	    Dynamic patch storage allocation.
+ *	    New private ioctls to change control gpr values.
+ *	    Enable volume control interrupts.
+ *	    By default enable dsp routes to digital out. 
+ *     0.11 Fixed fx / 4 problem.
+ *     0.12 Implemented mmaped for recording.
+ *	    Fixed bug: not unreserving mmaped buffer pages.
+ *	    IRQ handler cleanup.
+ *     0.13 Fixed problem with dsp1
+ *          Simplified dsp patch writing (inside the driver)
+ *	    Fixed several bugs found by the Stanford tools
+ *     0.14 New control gpr to oss mixer mapping feature (Chris Purnell)
+ *          Added AC3 Passthrough Support (Juha Yrjola)
+ *          Added Support for 5.1 cards (digital out and the third analog out)
+ *     0.15 Added Sequencer Support (Daniel Mack)
+ *          Support for multichannel pcm playback (Eduard Hasenleithner)
+ *     0.16 Mixer improvements, added old treble/bass support (Daniel Bertrand)
+ *          Small code format cleanup.
+ *          Deadlock bug fix for emu10k1_volxxx_irqhandler().
+ *
+ *********************************************************************/
+
+/* These are only included once per module */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "efxmgr.h"
+#include "cardwo.h"
+#include "cardwi.h"
+#include "cardmo.h"
+#include "cardmi.h"
+#include "recmgr.h"
+#include "ecard.h"
+
+
+#ifdef EMU10K1_SEQUENCER
+#define MIDI_SYNTH_NAME "EMU10K1 MIDI"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+ 
+#include "../sound_config.h"
+#include "../midi_synth.h"
+
+/* this should be in dev_table.h */
+#define SNDCARD_EMU10K1 46
+#endif
+ 
+#define DRIVER_VERSION "0.16"
+
+/* FIXME: is this right? */
+/* does the card support 32 bit bus master?*/
+#define EMU10K1_DMA_MASK                0xffffffff	/* DMA buffer mask for pci_alloc_consist */
+
+#ifndef PCI_VENDOR_ID_CREATIVE
+#define PCI_VENDOR_ID_CREATIVE 0x1102
+#endif
+
+#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1
+#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
+#endif
+
+#define EMU_APS_SUBID	0x40011102
+ 
+enum {
+	EMU10K1 = 0,
+};
+
+static char *card_names[] __devinitdata = {
+	"EMU10K1",
+};
+
+static struct pci_device_id emu10k1_pci_tbl[] = {
+	{PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, EMU10K1},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, emu10k1_pci_tbl);
+
+/* Global var instantiation */
+
+LIST_HEAD(emu10k1_devs);
+
+extern struct file_operations emu10k1_audio_fops;
+extern struct file_operations emu10k1_mixer_fops;
+extern struct file_operations emu10k1_midi_fops;
+
+#ifdef EMU10K1_SEQUENCER
+static struct midi_operations emu10k1_midi_operations;
+#endif
+
+extern void emu10k1_interrupt(int, void *, struct pt_regs *s);
+
+static int __devinit emu10k1_audio_init(struct emu10k1_card *card)
+{
+	card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1);
+	if (card->audio_dev < 0) {
+		printk(KERN_ERR "emu10k1: cannot register first audio device!\n");
+		goto err_dev;
+	}
+
+	card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1);
+	if (card->audio_dev1 < 0) {
+		printk(KERN_ERR "emu10k1: cannot register second audio device!\n");
+		goto err_dev1;
+	}
+
+	/* Assign default playback voice parameters */
+	card->mchannel_fx = 8;
+	/* mono voice */
+	card->waveout.send_a[0] = 0xff;
+	card->waveout.send_b[0] = 0xff;
+	card->waveout.send_c[0] = 0x00;
+	card->waveout.send_d[0] = 0x00;
+	card->waveout.send_routing[0] = 0x3210;
+
+	/* stereo voice */
+	/* left */
+	card->waveout.send_a[1] = 0xff;
+	card->waveout.send_b[1] = 0x00;
+	card->waveout.send_c[1] = 0x00;
+	card->waveout.send_d[1] = 0x00;
+	card->waveout.send_routing[1] = 0x3210;
+
+	/* right */
+	card->waveout.send_a[2] = 0x00;
+	card->waveout.send_b[2] = 0xff;
+	card->waveout.send_c[2] = 0x00;
+	card->waveout.send_d[2] = 0x00;
+	card->waveout.send_routing[2] = 0x3210;
+
+	/* Assign default recording parameters */
+	/* FIXME */
+	if(card->isaps)
+		card->wavein.recsrc = WAVERECORD_FX;
+	else
+		card->wavein.recsrc = WAVERECORD_AC97;
+
+	card->wavein.fxwc = 0x0003;
+	return 0;
+
+err_dev1:
+	unregister_sound_dsp(card->audio_dev);
+err_dev:
+	return -ENODEV;
+}
+
+static void __devinit emu10k1_audio_cleanup(struct emu10k1_card *card)
+{
+	unregister_sound_dsp(card->audio_dev1);
+	unregister_sound_dsp(card->audio_dev);
+}
+
+static int __devinit emu10k1_mixer_init(struct emu10k1_card *card)
+{
+	char s[32];
+	card->ac97.dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1);
+	if (card->ac97.dev_mixer < 0) {
+		printk(KERN_ERR "emu10k1: cannot register mixer device\n");
+		return -EIO;
+        }
+
+	card->ac97.private_data = card;
+
+	if (!card->isaps) {
+		card->ac97.id = 0;
+		card->ac97.codec_read = emu10k1_ac97_read;
+        	card->ac97.codec_write = emu10k1_ac97_write;
+
+		if (ac97_probe_codec (&card->ac97) == 0) {
+			printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n");
+			goto err_out;
+		}
+		/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
+			does not support this, it shouldn't do any harm */
+		sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE);
+
+		// Force 5bit
+		//card->ac97.bit_resolution=5;
+
+		if (!proc_mkdir ("driver/emu10k1", 0)) {
+			printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n");
+			goto err_out;
+		}
+
+		sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name);
+		if (!proc_mkdir (s, 0)) {
+			printk(KERN_ERR "emu10k1: unable to create proc directory %s\n", s);
+			goto err_emu10k1_proc;
+		}
+	
+		sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name);
+		if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) {
+			printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s);
+			goto err_ac97_proc;
+		}
+
+		/* these will store the original values and never be modified */
+		card->ac97_supported_mixers = card->ac97.supported_mixers;
+		card->ac97_stereo_mixers = card->ac97.stereo_mixers;
+	}
+
+	return 0;
+
+ err_ac97_proc:
+	sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name);
+	remove_proc_entry(s, NULL);
+
+ err_emu10k1_proc:
+	remove_proc_entry("driver/emu10k1", NULL);
+ err_out:
+	unregister_sound_mixer (card->ac97.dev_mixer);
+	return -EIO;
+}
+
+static void __devinit emu10k1_mixer_cleanup(struct emu10k1_card *card)
+{
+	char s[32];
+
+	if (!card->isaps) {
+		sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name);
+		remove_proc_entry(s, NULL);
+
+		sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name);
+		remove_proc_entry(s, NULL);
+
+		remove_proc_entry("driver/emu10k1", NULL);
+	}
+
+	unregister_sound_mixer (card->ac97.dev_mixer);
+}
+
+static int __devinit emu10k1_midi_init(struct emu10k1_card *card)
+{
+	int ret;
+
+	card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1);
+	if (card->midi_dev < 0) {
+                printk(KERN_ERR "emu10k1: cannot register midi device!\n");
+		return -ENODEV;
+        }
+
+
+	card->mpuout = kmalloc(sizeof(struct emu10k1_mpuout), GFP_KERNEL);
+	if (card->mpuout == NULL) {
+		printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuout: out of memory\n");
+		ret = -ENOMEM;
+		goto err_out1;
+	}
+
+	memset(card->mpuout, 0, sizeof(struct emu10k1_mpuout));
+
+	card->mpuout->intr = 1;
+	card->mpuout->status = FLAGS_AVAILABLE;
+	card->mpuout->state = CARDMIDIOUT_STATE_DEFAULT;
+
+	tasklet_init(&card->mpuout->tasklet, emu10k1_mpuout_bh, (unsigned long) card);
+
+	spin_lock_init(&card->mpuout->lock);
+
+	card->mpuin = kmalloc(sizeof(struct emu10k1_mpuin), GFP_KERNEL);
+	if (card->mpuin == NULL) {
+		printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuin: out of memory\n");
+		ret = -ENOMEM;
+                goto err_out2;
+	}
+
+	memset(card->mpuin, 0, sizeof(struct emu10k1_mpuin));
+
+	card->mpuin->status = FLAGS_AVAILABLE;
+
+	tasklet_init(&card->mpuin->tasklet, emu10k1_mpuin_bh, (unsigned long) card->mpuin);
+
+	spin_lock_init(&card->mpuin->lock);
+
+	/* Reset the MPU port */
+	if (emu10k1_mpu_reset(card) < 0) {
+		ERROR();
+		ret = -EIO;
+		goto err_out3;
+	}
+
+#ifdef EMU10K1_SEQUENCER
+	card->seq_dev = sound_alloc_mididev();
+	if (card->seq_dev == -1)
+			printk(KERN_WARNING "emu10k1: unable to register sequencer device!");
+	else {
+			std_midi_synth.midi_dev = card->seq_dev;
+			midi_devs[card->seq_dev] = 
+					(struct midi_operations *)
+					kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
+			
+			if (midi_devs[card->seq_dev] == NULL) {
+				printk(KERN_ERR "emu10k1: unable to allocate memory!");
+				sound_unload_mididev(card->seq_dev);
+				card->seq_dev = -1;
+				return 0;
+			} else {
+				memcpy((char *)midi_devs[card->seq_dev], 
+					(char *)&emu10k1_midi_operations, 
+					sizeof(struct midi_operations));
+				midi_devs[card->seq_dev]->devc = card;
+				sequencer_init();
+			}
+	}
+	card->seq_mididev = 0;
+#endif
+	return 0;
+
+err_out3:
+	kfree(card->mpuin);
+err_out2:
+	kfree(card->mpuout);
+err_out1:
+	unregister_sound_midi(card->midi_dev);
+	return ret;
+}
+
+static void __devinit emu10k1_midi_cleanup(struct emu10k1_card *card)
+{
+	tasklet_kill(&card->mpuout->tasklet);
+	kfree(card->mpuout);
+
+	tasklet_kill(&card->mpuin->tasklet);
+	kfree(card->mpuin);
+
+#ifdef EMU10K1_SEQUENCER
+	if (card->seq_dev > -1) {
+		kfree(midi_devs[card->seq_dev]);
+		midi_devs[card->seq_dev] = NULL;
+		sound_unload_mididev(card->seq_dev);
+		card->seq_dev = -1;
+	}
+#endif
+
+	unregister_sound_midi(card->midi_dev);
+}
+
+static void __devinit voice_init(struct emu10k1_card *card)
+{
+	int i;
+
+	for (i = 0; i < NUM_G; i++)
+		card->voicetable[i] = VOICE_USAGE_FREE;
+}
+
+static void __devinit timer_init(struct emu10k1_card *card)
+{
+	INIT_LIST_HEAD(&card->timers);
+	card->timer_delay = TIMER_STOPPED;
+	card->timer_lock = SPIN_LOCK_UNLOCKED;
+}
+
+static void __devinit addxmgr_init(struct emu10k1_card *card)
+{
+	u32 count;
+
+	for (count = 0; count < MAXPAGES; count++)
+		card->emupagetable[count] = 0;
+
+	/* Mark first page as used */
+	/* This page is reserved by the driver */
+	card->emupagetable[0] = 0x8001;
+	card->emupagetable[1] = MAXPAGES - 1;
+}
+
+static void __devinit fx_cleanup(struct patch_manager *mgr)
+{
+	int i;
+	for(i = 0; i < mgr->current_pages; i++)
+		free_page((unsigned long) mgr->patch[i]);
+}
+
+static int __devinit fx_init(struct emu10k1_card *card)
+{
+	struct patch_manager *mgr = &card->mgr;
+	struct dsp_patch *patch;
+	struct dsp_rpatch *rpatch;
+	s32 left, right;
+	int i;
+	u32 pc = 0;
+	u32 patch_n;
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+		mgr->ctrl_gpr[i][0] = -1;
+		mgr->ctrl_gpr[i][1] = -1;
+	}
+
+	for (i = 0; i < 512; i++)
+		OP(6, 0x40, 0x40, 0x40, 0x40);
+
+	for (i = 0; i < 256; i++)
+		sblive_writeptr_tag(card, 0,
+				    FXGPREGBASE + i, 0,
+				    TANKMEMADDRREGBASE + i, 0,
+				    TAGLIST_END);
+
+	/* !! The number bellow must equal the number of patches, currently 11 !! */
+	mgr->current_pages = (11 + PATCHES_PER_PAGE - 1) / PATCHES_PER_PAGE;
+	for (i = 0; i < mgr->current_pages; i++) {
+		mgr->patch[i] = (void *)__get_free_page(GFP_KERNEL);
+		if (mgr->patch[i] == NULL) {
+			mgr->current_pages = i;
+			fx_cleanup(mgr);
+			return -ENOMEM;
+		}
+		memset(mgr->patch[i], 0, PAGE_SIZE);
+	}
+
+	pc = 0;
+	patch_n = 0;
+	//first free GPR = 0x11b
+
+	/* FX volume correction and Volume control*/
+	INPUT_PATCH_START(patch, "Pcm L vol", 0x0, 0);
+	GET_OUTPUT_GPR(patch, 0x100, 0x0);
+	GET_CONTROL_GPR(patch, 0x106, "Vol", 0, 0x7fffffff);
+	GET_DYNAMIC_GPR(patch, 0x112);
+
+	OP(4, 0x112, 0x40, PCM_IN_L, 0x44); //*4
+	OP(0, 0x100, 0x040, 0x112, 0x106);  //*vol
+	INPUT_PATCH_END(patch);
+
+
+	INPUT_PATCH_START(patch, "Pcm R vol", 0x1, 0);
+	GET_OUTPUT_GPR(patch, 0x101, 0x1);
+	GET_CONTROL_GPR(patch, 0x107, "Vol", 0, 0x7fffffff);
+	GET_DYNAMIC_GPR(patch, 0x112);
+
+	OP(4, 0x112, 0x40, PCM_IN_R, 0x44); 
+	OP(0, 0x101, 0x040, 0x112, 0x107);
+
+	INPUT_PATCH_END(patch);
+
+
+	// CD-Digital In Volume control
+	INPUT_PATCH_START(patch, "CD-Digital Vol L", 0x12, 0);
+	GET_OUTPUT_GPR(patch, 0x10c, 0x12);
+	GET_CONTROL_GPR(patch, 0x10d, "Vol", 0, 0x7fffffff);
+
+	OP(0, 0x10c, 0x040, SPDIF_CD_L, 0x10d);
+	INPUT_PATCH_END(patch);
+
+	INPUT_PATCH_START(patch, "CD-Digital Vol R", 0x13, 0);
+	GET_OUTPUT_GPR(patch, 0x10e, 0x13);
+	GET_CONTROL_GPR(patch, 0x10f, "Vol", 0, 0x7fffffff);
+
+	OP(0, 0x10e, 0x040, SPDIF_CD_R, 0x10f);
+	INPUT_PATCH_END(patch);
+
+	//Volume Correction for Multi-channel Inputs
+	INPUT_PATCH_START(patch, "Multi-Channel Gain", 0x08, 0);
+	patch->input=patch->output=0x3F00;
+
+	GET_OUTPUT_GPR(patch, 0x113, MULTI_FRONT_L);
+	GET_OUTPUT_GPR(patch, 0x114, MULTI_FRONT_R);
+	GET_OUTPUT_GPR(patch, 0x115, MULTI_REAR_L);
+	GET_OUTPUT_GPR(patch, 0x116, MULTI_REAR_R);
+	GET_OUTPUT_GPR(patch, 0x117, MULTI_CENTER);
+	GET_OUTPUT_GPR(patch, 0x118, MULTI_LFE);
+
+	OP(4, 0x113, 0x40, MULTI_FRONT_L, 0x44);
+	OP(4, 0x114, 0x40, MULTI_FRONT_R, 0x44);
+	OP(4, 0x115, 0x40, MULTI_REAR_L, 0x44);
+	OP(4, 0x116, 0x40, MULTI_REAR_R, 0x44);
+	OP(4, 0x117, 0x40, MULTI_CENTER, 0x44);
+	OP(4, 0x118, 0x40, MULTI_LFE, 0x44);
+	
+	INPUT_PATCH_END(patch);
+
+
+	//Routing patch start
+	ROUTING_PATCH_START(rpatch, "Routing");
+	GET_INPUT_GPR(rpatch, 0x100, 0x0);
+	GET_INPUT_GPR(rpatch, 0x101, 0x1);
+	GET_INPUT_GPR(rpatch, 0x10c, 0x12);
+	GET_INPUT_GPR(rpatch, 0x10e, 0x13);
+	GET_INPUT_GPR(rpatch, 0x113, MULTI_FRONT_L);
+	GET_INPUT_GPR(rpatch, 0x114, MULTI_FRONT_R);
+	GET_INPUT_GPR(rpatch, 0x115, MULTI_REAR_L);
+	GET_INPUT_GPR(rpatch, 0x116, MULTI_REAR_R);
+	GET_INPUT_GPR(rpatch, 0x117, MULTI_CENTER);
+	GET_INPUT_GPR(rpatch, 0x118, MULTI_LFE);
+
+	GET_DYNAMIC_GPR(rpatch, 0x102);
+	GET_DYNAMIC_GPR(rpatch, 0x103);
+
+	GET_OUTPUT_GPR(rpatch, 0x104, 0x8);
+	GET_OUTPUT_GPR(rpatch, 0x105, 0x9);
+	GET_OUTPUT_GPR(rpatch, 0x10a, 0x2);
+	GET_OUTPUT_GPR(rpatch, 0x10b, 0x3);
+
+
+	/* input buffer */
+	OP(6, 0x102, AC97_IN_L, 0x40, 0x40);
+	OP(6, 0x103, AC97_IN_R, 0x40, 0x40);
+
+
+	/* Digital In + PCM + MULTI_FRONT-> AC97 out (front speakers)*/
+	OP(6, AC97_FRONT_L, 0x100, 0x10c, 0x113);
+
+	CONNECT(MULTI_FRONT_L, AC97_FRONT_L);
+	CONNECT(PCM_IN_L, AC97_FRONT_L);
+	CONNECT(SPDIF_CD_L, AC97_FRONT_L);
+
+	OP(6, AC97_FRONT_R, 0x101, 0x10e, 0x114);
+
+	CONNECT(MULTI_FRONT_R, AC97_FRONT_R);
+	CONNECT(PCM_IN_R, AC97_FRONT_R);
+	CONNECT(SPDIF_CD_R, AC97_FRONT_R);
+
+	/* Digital In + PCM + AC97 In + PCM1 + MULTI_REAR --> Rear Channel */ 
+	OP(6, 0x104, PCM1_IN_L, 0x100, 0x115);
+	OP(6, 0x104, 0x104, 0x10c, 0x102);
+
+	CONNECT(MULTI_REAR_L, ANALOG_REAR_L);
+	CONNECT(AC97_IN_L, ANALOG_REAR_L);
+	CONNECT(PCM_IN_L, ANALOG_REAR_L);
+	CONNECT(SPDIF_CD_L, ANALOG_REAR_L);
+	CONNECT(PCM1_IN_L, ANALOG_REAR_L);
+
+	OP(6, 0x105, PCM1_IN_R, 0x101, 0x116);
+	OP(6, 0x105, 0x105, 0x10e, 0x103);
+
+	CONNECT(MULTI_REAR_R, ANALOG_REAR_R);
+	CONNECT(AC97_IN_R, ANALOG_REAR_R);
+	CONNECT(PCM_IN_R, ANALOG_REAR_R);
+	CONNECT(SPDIF_CD_R, ANALOG_REAR_R);
+	CONNECT(PCM1_IN_R, ANALOG_REAR_R);
+
+	/* Digital In + PCM + AC97 In + MULTI_FRONT --> Digital out */
+	OP(6, 0x10b, 0x100, 0x102, 0x10c);
+	OP(6, 0x10b, 0x10b, 0x113, 0x40);
+
+	CONNECT(MULTI_FRONT_L, DIGITAL_OUT_L);
+	CONNECT(PCM_IN_L, DIGITAL_OUT_L);
+	CONNECT(AC97_IN_L, DIGITAL_OUT_L);
+	CONNECT(SPDIF_CD_L, DIGITAL_OUT_L);
+
+	OP(6, 0x10a, 0x101, 0x103, 0x10e);
+	OP(6, 0x10b, 0x10b, 0x114, 0x40);
+
+	CONNECT(MULTI_FRONT_R, DIGITAL_OUT_R);
+	CONNECT(PCM_IN_R, DIGITAL_OUT_R);
+	CONNECT(AC97_IN_R, DIGITAL_OUT_R);
+	CONNECT(SPDIF_CD_R, DIGITAL_OUT_R);
+
+	/* AC97 In --> ADC Recording Buffer */
+	OP(6, ADC_REC_L, 0x102, 0x40, 0x40);
+
+	CONNECT(AC97_IN_L, ADC_REC_L);
+
+	OP(6, ADC_REC_R, 0x103, 0x40, 0x40);
+
+	CONNECT(AC97_IN_R, ADC_REC_R);
+
+
+	/* fx12:Analog-Center */
+	OP(6, ANALOG_CENTER, 0x117, 0x40, 0x40);
+	CONNECT(MULTI_CENTER, ANALOG_CENTER);
+
+	/* fx11:Analog-LFE */
+	OP(6, ANALOG_LFE, 0x118, 0x40, 0x40);
+	CONNECT(MULTI_LFE, ANALOG_LFE);
+
+	/* fx12:Digital-Center */
+	OP(6, DIGITAL_CENTER, 0x117, 0x40, 0x40);
+	CONNECT(MULTI_CENTER, DIGITAL_CENTER);
+
+	/* fx11:Analog-LFE */
+	OP(6, DIGITAL_LFE, 0x118, 0x40, 0x40);
+	CONNECT(MULTI_LFE, DIGITAL_LFE);
+	
+	ROUTING_PATCH_END(rpatch);
+
+
+	// Rear volume control
+	OUTPUT_PATCH_START(patch, "Vol Rear L", 0x8, 0);
+	GET_INPUT_GPR(patch, 0x104, 0x8);
+	GET_CONTROL_GPR(patch, 0x119, "Vol", 0, 0x7fffffff);
+
+	OP(0, ANALOG_REAR_L, 0x040, 0x104, 0x119);
+	OUTPUT_PATCH_END(patch);
+
+
+	OUTPUT_PATCH_START(patch, "Vol Rear R", 0x9, 0);
+	GET_INPUT_GPR(patch, 0x105, 0x9);
+	GET_CONTROL_GPR(patch, 0x11a, "Vol", 0, 0x7fffffff);
+
+	OP(0, ANALOG_REAR_R, 0x040, 0x105, 0x11a);
+	OUTPUT_PATCH_END(patch);
+
+
+	//Master volume control on front-digital
+	OUTPUT_PATCH_START(patch, "Vol Master L", 0x2, 1);
+	GET_INPUT_GPR(patch, 0x10a, 0x2);
+	GET_CONTROL_GPR(patch, 0x108, "Vol", 0, 0x7fffffff);
+
+	OP(0, DIGITAL_OUT_L, 0x040, 0x10a, 0x108);
+	OUTPUT_PATCH_END(patch);
+
+
+	OUTPUT_PATCH_START(patch, "Vol Master R", 0x3, 1);
+	GET_INPUT_GPR(patch, 0x10b, 0x3);
+	GET_CONTROL_GPR(patch, 0x109, "Vol", 0, 0x7fffffff);
+
+	OP(0, DIGITAL_OUT_R, 0x040, 0x10b, 0x109);
+	OUTPUT_PATCH_END(patch);
+
+
+	/* delimiter patch */
+	patch = PATCH(mgr, patch_n);
+	patch->code_size = 0;
+
+	sblive_writeptr(card, DBG, 0, 0);
+
+	mgr->lock = SPIN_LOCK_UNLOCKED;
+
+
+	//Master volume
+	mgr->ctrl_gpr[SOUND_MIXER_VOLUME][0] = 8;
+	mgr->ctrl_gpr[SOUND_MIXER_VOLUME][1] = 9;
+
+	left = card->ac97.mixer_state[SOUND_MIXER_VOLUME] & 0xff;
+	right = (card->ac97.mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff;
+
+	emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97.bit_resolution);
+	emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97.bit_resolution);
+
+	//Rear volume
+	mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][0] = 0x19;
+	mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][1] = 0x1a;
+
+	left = right = 67;
+	card->ac97.mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left;
+
+	card->ac97.supported_mixers |= SOUND_MASK_OGAIN;
+	card->ac97.stereo_mixers |= SOUND_MASK_OGAIN;
+
+	emu10k1_set_volume_gpr(card, 0x19, left, VOL_5BIT);
+	emu10k1_set_volume_gpr(card, 0x1a, right, VOL_5BIT);
+
+	//PCM Volume
+	mgr->ctrl_gpr[SOUND_MIXER_PCM][0] = 6;
+	mgr->ctrl_gpr[SOUND_MIXER_PCM][1] = 7;
+
+	left = card->ac97.mixer_state[SOUND_MIXER_PCM] & 0xff;
+	right = (card->ac97.mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff;
+
+	emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT);
+	emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT);
+
+	//CD-Digital Volume
+	mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][0] = 0xd;
+	mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf;
+
+	left = right = 67;
+	card->ac97.mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; 
+
+	card->ac97.supported_mixers |= SOUND_MASK_DIGITAL1;
+	card->ac97.stereo_mixers |= SOUND_MASK_DIGITAL1;
+
+	emu10k1_set_volume_gpr(card, 0xd, left, VOL_5BIT);
+	emu10k1_set_volume_gpr(card, 0xf, right, VOL_5BIT);
+
+	//hard wire the ac97's pcm, we'll do that in dsp code instead.
+	emu10k1_ac97_write(&card->ac97, 0x18, 0x0);
+	card->ac97_supported_mixers &= ~SOUND_MASK_PCM;
+	card->ac97_stereo_mixers &= ~SOUND_MASK_PCM;
+
+	//set Igain to 0dB by default, maybe consider hardwiring it here.
+	emu10k1_ac97_write(&card->ac97, AC97_RECORD_GAIN, 0x0000);
+	card->ac97.mixer_state[SOUND_MIXER_IGAIN] = 0x101; 
+
+	return 0;
+}
+
+static int __devinit hw_init(struct emu10k1_card *card)
+{
+	int nCh;
+	u32 pagecount; /* tmp */
+	int ret;
+
+	/* Disable audio and lock cache */
+	emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE);
+
+	/* Reset recording buffers */
+	sblive_writeptr_tag(card, 0,
+			    MICBS, ADCBS_BUFSIZE_NONE,
+			    MICBA, 0,
+			    FXBS, ADCBS_BUFSIZE_NONE,
+			    FXBA, 0,
+			    ADCBS, ADCBS_BUFSIZE_NONE,
+			    ADCBA, 0,
+			    TAGLIST_END);
+
+	/* Disable channel interrupt */
+	emu10k1_writefn0(card, INTE, 0);
+	sblive_writeptr_tag(card, 0,
+			    CLIEL, 0,
+			    CLIEH, 0,
+			    SOLEL, 0,
+			    SOLEH, 0,
+			    TAGLIST_END);
+
+	/* Init envelope engine */
+	for (nCh = 0; nCh < NUM_G; nCh++) {
+		sblive_writeptr_tag(card, nCh,
+				    DCYSUSV, 0,
+				    IP, 0,
+				    VTFT, 0xffff,
+				    CVCF, 0xffff,
+				    PTRX, 0,
+				    CPF, 0,
+				    CCR, 0,
+
+				    PSST, 0,
+				    DSL, 0x10,
+				    CCCA, 0,
+				    Z1, 0,
+				    Z2, 0,
+				    FXRT, 0xd01c0000,
+
+				    ATKHLDM, 0,
+				    DCYSUSM, 0,
+				    IFATN, 0xffff,
+				    PEFE, 0,
+				    FMMOD, 0,
+				    TREMFRQ, 24,	/* 1 Hz */
+				    FM2FRQ2, 24,	/* 1 Hz */
+				    TEMPENV, 0,
+
+				    /*** These are last so OFF prevents writing ***/
+				    LFOVAL2, 0,
+				    LFOVAL1, 0,
+				    ATKHLDV, 0,
+				    ENVVOL, 0,
+				    ENVVAL, 0,
+                                    TAGLIST_END);
+	}
+
+	/*
+	 ** Init to 0x02109204 :
+	 ** Clock accuracy    = 0     (1000ppm)
+	 ** Sample Rate       = 2     (48kHz)
+	 ** Audio Channel     = 1     (Left of 2)
+	 ** Source Number     = 0     (Unspecified)
+	 ** Generation Status = 1     (Original for Cat Code 12)
+	 ** Cat Code          = 12    (Digital Signal Mixer)
+	 ** Mode              = 0     (Mode 0)
+	 ** Emphasis          = 0     (None)
+	 ** CP                = 1     (Copyright unasserted)
+	 ** AN                = 0     (Digital audio)
+	 ** P                 = 0     (Consumer)
+	 */
+
+	sblive_writeptr_tag(card, 0,
+
+			    /* SPDIF0 */
+			    SPCS0, (SPCS_CLKACCY_1000PPM | 0x002000000 |
+				    SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT),
+
+			    /* SPDIF1 */
+			    SPCS1, (SPCS_CLKACCY_1000PPM | 0x002000000 |
+				    SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT),
+
+			    /* SPDIF2 & SPDIF3 */
+			    SPCS2, (SPCS_CLKACCY_1000PPM | 0x002000000 |
+				    SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT),
+
+			    TAGLIST_END);
+
+	ret = fx_init(card);		/* initialize effects engine */
+	if (ret < 0)
+		return ret;
+
+	card->tankmem.size = 0;
+
+	card->virtualpagetable.size = MAXPAGES * sizeof(u32);
+
+	card->virtualpagetable.addr = pci_alloc_consistent(card->pci_dev, card->virtualpagetable.size, &card->virtualpagetable.dma_handle);
+	if (card->virtualpagetable.addr == NULL) {
+		ERROR();
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	card->silentpage.size = EMUPAGESIZE;
+
+	card->silentpage.addr = pci_alloc_consistent(card->pci_dev, card->silentpage.size, &card->silentpage.dma_handle);
+	if (card->silentpage.addr == NULL) {
+		ERROR();
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	for (pagecount = 0; pagecount < MAXPAGES; pagecount++)
+		((u32 *) card->virtualpagetable.addr)[pagecount] = cpu_to_le32((card->silentpage.dma_handle * 2) | pagecount);
+
+	/* Init page table & tank memory base register */
+	sblive_writeptr_tag(card, 0,
+			    PTB, card->virtualpagetable.dma_handle,
+			    TCB, 0,
+			    TCBS, 0,
+			    TAGLIST_END);
+
+	for (nCh = 0; nCh < NUM_G; nCh++) {
+		sblive_writeptr_tag(card, nCh,
+				    MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2),
+				    MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2),
+				    TAGLIST_END);
+	}
+
+	/* Hokay, now enable the AUD bit */
+	/* Enable Audio = 1 */
+	/* Mute Disable Audio = 0 */
+	/* Lock Tank Memory = 1 */
+	/* Lock Sound Memory = 0 */
+	/* Auto Mute = 1 */
+
+	if (card->model == 0x20 || card->model == 0xc400 ||
+	  (card->model == 0x21 && card->chiprev < 6))
+	        emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE  | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE);
+	else
+		emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE  | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE);
+
+	/* Enable Vol_Ctrl irqs */
+	emu10k1_irq_enable(card, INTE_VOLINCRENABLE | INTE_VOLDECRENABLE | INTE_MUTEENABLE | INTE_FXDSPENABLE);
+
+	/* FIXME: TOSLink detection */
+	card->has_toslink = 0;
+
+	/* Initialize digital passthrough variables */
+	card->pt.pos_gpr = card->pt.intr_gpr = card->pt.enable_gpr = -1;
+	card->pt.selected = 0;
+	card->pt.state = PT_STATE_INACTIVE;
+	card->pt.spcs_to_use = 0x01;
+	card->pt.patch_name = "AC3pass";
+	card->pt.intr_gpr_name = "count";
+	card->pt.enable_gpr_name = "enable";
+	card->pt.pos_gpr_name = "ptr";
+	spin_lock_init(&card->pt.lock);
+	init_waitqueue_head(&card->pt.wait);
+
+/*	tmp = sblive_readfn0(card, HCFG);
+	if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
+		sblive_writefn0(card, HCFG, tmp | 0x800);
+
+		udelay(512);
+
+		if (tmp != (sblive_readfn0(card, HCFG) & ~0x800)) {
+			card->has_toslink = 1;
+			sblive_writefn0(card, HCFG, tmp);
+		}
+	}
+*/
+	return 0;
+
+  err1:
+	pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle);
+  err0:
+	fx_cleanup(&card->mgr);
+
+	return ret;
+}
+
+static int __devinit emu10k1_init(struct emu10k1_card *card)
+{
+	/* Init Card */
+	if (hw_init(card) < 0)
+		return -1;
+
+	voice_init(card);
+	timer_init(card);
+	addxmgr_init(card);
+
+	DPD(2, "  hw control register -> %#x\n", emu10k1_readfn0(card, HCFG));
+
+	return 0;
+}
+
+static void __devinit emu10k1_cleanup(struct emu10k1_card *card)
+{
+	int ch;
+
+	emu10k1_writefn0(card, INTE, 0);
+
+	/** Shutdown the chip **/
+	for (ch = 0; ch < NUM_G; ch++)
+		sblive_writeptr(card, DCYSUSV, ch, 0);
+
+	for (ch = 0; ch < NUM_G; ch++) {
+		sblive_writeptr_tag(card, ch,
+				    VTFT, 0,
+				    CVCF, 0,
+				    PTRX, 0,
+				    CPF, 0,
+				    TAGLIST_END);
+	}
+
+	/* Disable audio and lock cache */
+	emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE);
+
+	sblive_writeptr_tag(card, 0,
+                            PTB, 0,
+
+			    /* Reset recording buffers */
+			    MICBS, ADCBS_BUFSIZE_NONE,
+			    MICBA, 0,
+			    FXBS, ADCBS_BUFSIZE_NONE,
+			    FXBA, 0,
+			    FXWC, 0,
+			    ADCBS, ADCBS_BUFSIZE_NONE,
+			    ADCBA, 0,
+			    TCBS, 0,
+			    TCB, 0,
+			    DBG, 0x8000,
+
+			    /* Disable channel interrupt */
+			    CLIEL, 0,
+			    CLIEH, 0,
+			    SOLEL, 0,
+			    SOLEH, 0,
+			    TAGLIST_END);
+
+
+	pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle);
+	pci_free_consistent(card->pci_dev, card->silentpage.size, card->silentpage.addr, card->silentpage.dma_handle);
+	
+	if(card->tankmem.size != 0)
+		pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle);
+
+	/* release patch storage memory */
+	fx_cleanup(&card->mgr);
+}
+
+/* Driver initialization routine */
+static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
+{
+	struct emu10k1_card *card;
+	u32 subsysvid;
+	int ret;
+
+	if (pci_set_dma_mask(pci_dev, EMU10K1_DMA_MASK)) {
+		printk(KERN_ERR "emu10k1: architecture does not support 32bit PCI busmaster DMA\n");
+		return -ENODEV;
+	}
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	pci_set_master(pci_dev);
+
+	if ((card = kmalloc(sizeof(struct emu10k1_card), GFP_KERNEL)) == NULL) {
+                printk(KERN_ERR "emu10k1: out of memory\n");
+                return -ENOMEM;
+        }
+        memset(card, 0, sizeof(struct emu10k1_card));
+
+	card->iobase = pci_resource_start(pci_dev, 0);
+	card->length = pci_resource_len(pci_dev, 0); 
+
+	if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) {
+		printk(KERN_ERR "emu10k1: IO space in use\n");
+		ret = -EBUSY;
+		goto err_region;
+	}
+
+	pci_set_drvdata(pci_dev, card);
+
+	card->irq = pci_dev->irq;
+	card->pci_dev = pci_dev;
+
+	/* Reserve IRQ Line */
+	if (request_irq(card->irq, emu10k1_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) {
+		printk(KERN_ERR "emu10k1: IRQ in use\n");
+		ret = -EBUSY;
+		goto err_irq;
+	}
+
+	pci_read_config_byte(pci_dev, PCI_REVISION_ID, &card->chiprev);
+	pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &card->model);
+
+	printk(KERN_INFO "emu10k1: %s rev %d model 0x%x found, IO at 0x%04lx-0x%04lx, IRQ %d\n",
+		card_names[pci_id->driver_data], card->chiprev, card->model, card->iobase,
+		card->iobase + card->length - 1, card->irq);
+
+	pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid);
+	card->isaps = (subsysvid == EMU_APS_SUBID);
+
+	spin_lock_init(&card->lock);
+	init_MUTEX(&card->open_sem);
+	card->open_mode = 0;
+	init_waitqueue_head(&card->open_wait);
+
+	ret = emu10k1_audio_init(card);
+	if(ret < 0) {
+                printk(KERN_ERR "emu10k1: cannot initialize audio devices\n");
+                goto err_audio;
+        }
+
+	ret = emu10k1_mixer_init(card);
+	if(ret < 0) {
+		printk(KERN_ERR "emu10k1: cannot initialize AC97 codec\n");
+                goto err_mixer;
+	}
+
+	ret = emu10k1_midi_init(card);
+	if (ret < 0) {
+		printk(KERN_ERR "emu10k1: cannot register midi device\n");
+		goto err_midi;
+	}
+
+	ret = emu10k1_init(card);
+	if (ret < 0) {
+		printk(KERN_ERR "emu10k1: cannot initialize device\n");
+		goto err_emu10k1_init;
+	}
+
+	if (card->isaps)
+		emu10k1_ecard_init(card);
+
+	list_add(&card->list, &emu10k1_devs);
+
+	return 0;
+
+err_emu10k1_init:
+	emu10k1_midi_cleanup(card);
+
+err_midi:
+	emu10k1_mixer_cleanup(card);
+
+err_mixer:
+	emu10k1_audio_cleanup(card);
+
+err_audio:
+	free_irq(card->irq, card);
+
+err_irq:
+	release_region(card->iobase, card->length);
+	pci_set_drvdata(pci_dev, NULL);
+
+err_region:
+	kfree(card);
+
+	return ret;
+}
+
+static void __devexit emu10k1_remove(struct pci_dev *pci_dev)
+{
+	struct emu10k1_card *card = pci_get_drvdata(pci_dev);
+
+	list_del(&card->list);
+
+	emu10k1_cleanup(card);
+	emu10k1_midi_cleanup(card);
+	emu10k1_mixer_cleanup(card);
+	emu10k1_audio_cleanup(card);	
+	free_irq(card->irq, card);
+	release_region(card->iobase, card->length);
+	kfree(card);
+	pci_set_drvdata(pci_dev, NULL);
+}
+
+MODULE_AUTHOR("Bertrand Lee, Cai Ying. (Email to: emu10k1-devel@opensource.creative.com)");
+MODULE_DESCRIPTION("Creative EMU10K1 PCI Audio Driver v" DRIVER_VERSION "\nCopyright (C) 1999 Creative Technology Ltd.");
+MODULE_LICENSE("GPL");
+
+static struct pci_driver emu10k1_pci_driver = {
+	name:		"emu10k1",
+	id_table:	emu10k1_pci_tbl,
+	probe:		emu10k1_probe,
+	remove:		__devexit_p(emu10k1_remove),
+};
+
+static int __init emu10k1_init_module(void)
+{
+	printk(KERN_INFO "Creative EMU10K1 PCI Audio Driver, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
+
+	return pci_module_init(&emu10k1_pci_driver);
+}
+
+static void __exit emu10k1_cleanup_module(void)
+{
+	pci_unregister_driver(&emu10k1_pci_driver);
+
+	return;
+}
+
+module_init(emu10k1_init_module);
+module_exit(emu10k1_cleanup_module);
+
+#ifdef EMU10K1_SEQUENCER
+
+/* in midi.c */
+extern int emu10k1_seq_midi_open(int dev, int mode, 
+				void (*input)(int dev, unsigned char midi_byte),
+				void (*output)(int dev));
+extern void emu10k1_seq_midi_close(int dev);
+extern int emu10k1_seq_midi_out(int dev, unsigned char midi_byte);
+extern int emu10k1_seq_midi_start_read(int dev);
+extern int emu10k1_seq_midi_end_read(int dev);
+extern void emu10k1_seq_midi_kick(int dev);
+extern int emu10k1_seq_midi_buffer_status(int dev);
+
+static struct midi_operations emu10k1_midi_operations =
+{
+	THIS_MODULE,
+	{"EMU10K1 MIDI", 0, 0, SNDCARD_EMU10K1},
+	&std_midi_synth,
+	{0},
+	emu10k1_seq_midi_open,
+	emu10k1_seq_midi_close,
+	NULL,
+	emu10k1_seq_midi_out,
+	emu10k1_seq_midi_start_read,
+	emu10k1_seq_midi_end_read,
+	emu10k1_seq_midi_kick,
+	NULL,
+	emu10k1_seq_midi_buffer_status,
+	NULL
+};
+
+#endif
diff -Nru linux/sound/oss/emu10k1/midi.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.c
--- linux/sound/oss/emu10k1/midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,597 @@
+/*
+ **********************************************************************
+ *     midi.c - /dev/midi interface for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+#include "hwaccess.h"
+#include "cardmo.h"
+#include "cardmi.h"
+#include "midi.h"
+
+#ifdef EMU10K1_SEQUENCER
+#include "../sound_config.h"
+#endif
+
+static spinlock_t midi_spinlock __attribute((unused)) = SPIN_LOCK_UNLOCKED;
+
+static void init_midi_hdr(struct midi_hdr *midihdr)
+{
+	midihdr->bufferlength = MIDIIN_BUFLEN;
+	midihdr->bytesrecorded = 0;
+	midihdr->flags = 0;
+}
+
+static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hdr **midihdrptr)
+{
+	struct midi_hdr *midihdr;
+
+	if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) {
+		ERROR();
+		return -EINVAL;
+	}
+
+	init_midi_hdr(midihdr);
+
+	if ((midihdr->data = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL)) == NULL) {
+		ERROR();
+		kfree(midihdr);
+		return -1;
+	}
+
+	if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) {
+		ERROR();
+		kfree(midihdr->data);
+		kfree(midihdr);
+		return -1;
+	}
+
+	*midihdrptr = midihdr;
+	list_add_tail(&midihdr->list, &midi_dev->mid_hdrs);
+
+	return 0;
+}
+
+static int emu10k1_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct emu10k1_card *card = NULL;
+	struct emu10k1_mididevice *midi_dev;
+	struct list_head *entry;
+
+	DPF(2, "emu10k1_midi_open()\n");
+
+	/* Check for correct device to open */
+	list_for_each(entry, &emu10k1_devs) {
+		card = list_entry(entry, struct emu10k1_card, list);
+
+		if (card->midi_dev == minor)
+			goto match;
+	}
+
+	return -ENODEV;
+
+match:
+#ifdef EMU10K1_SEQUENCER
+	if (card->seq_mididev)	/* card is opened by sequencer */
+		return -EBUSY;
+#endif
+
+	/* Wait for device to become free */
+	down(&card->open_sem);
+	while (card->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&card->open_sem);
+			return -EBUSY;
+		}
+
+		up(&card->open_sem);
+		interruptible_sleep_on(&card->open_wait);
+
+		if (signal_pending(current)) {
+			return -ERESTARTSYS;
+		}
+
+		down(&card->open_sem);
+	}
+
+	if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL)
+		return -EINVAL;
+
+	midi_dev->card = card;
+	midi_dev->mistate = MIDIIN_STATE_STOPPED;
+	init_waitqueue_head(&midi_dev->oWait);
+	init_waitqueue_head(&midi_dev->iWait);
+	midi_dev->ird = 0;
+	midi_dev->iwr = 0;
+	midi_dev->icnt = 0;
+	INIT_LIST_HEAD(&midi_dev->mid_hdrs);
+
+	if (file->f_mode & FMODE_READ) {
+		struct midi_openinfo dsCardMidiOpenInfo;
+		struct midi_hdr *midihdr1;
+		struct midi_hdr *midihdr2;
+
+		dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
+
+		if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) < 0) {
+			ERROR();
+			kfree(midi_dev);
+			return -ENODEV;
+		}
+
+		/* Add two buffers to receive sysex buffer */
+		if (midiin_add_buffer(midi_dev, &midihdr1) < 0) {
+			kfree(midi_dev);
+			return -ENODEV;
+		}
+
+		if (midiin_add_buffer(midi_dev, &midihdr2) < 0) {
+			list_del(&midihdr1->list);
+			kfree(midihdr1->data);
+			kfree(midihdr1);
+			kfree(midi_dev);
+			return -ENODEV;
+		}
+	}
+
+	if (file->f_mode & FMODE_WRITE) {
+		struct midi_openinfo dsCardMidiOpenInfo;
+
+		dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
+
+		if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
+			ERROR();
+			kfree(midi_dev);
+			return -ENODEV;
+		}
+	}
+
+	file->private_data = (void *) midi_dev;
+
+	card->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+
+	up(&card->open_sem);
+
+	return 0;
+}
+
+static int emu10k1_midi_release(struct inode *inode, struct file *file)
+{
+	struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
+	struct emu10k1_card *card;
+
+	lock_kernel();
+
+	card = midi_dev->card;
+	DPF(2, "emu10k1_midi_release()\n");
+
+	if (file->f_mode & FMODE_WRITE) {
+		if (!(file->f_flags & O_NONBLOCK)) {
+
+			while (!signal_pending(current) && (card->mpuout->firstmidiq != NULL)) {
+				DPF(4, "Cannot close - buffers not empty\n");
+
+				interruptible_sleep_on(&midi_dev->oWait);
+
+			}
+		}
+
+		emu10k1_mpuout_close(card);
+	}
+
+	if (file->f_mode & FMODE_READ) {
+		struct midi_hdr *midihdr;
+
+		if (midi_dev->mistate == MIDIIN_STATE_STARTED) {
+			emu10k1_mpuin_stop(card);
+			midi_dev->mistate = MIDIIN_STATE_STOPPED;
+		}
+
+		emu10k1_mpuin_reset(card);
+		emu10k1_mpuin_close(card);
+
+		while (!list_empty(&midi_dev->mid_hdrs)) {
+			midihdr = list_entry(midi_dev->mid_hdrs.next, struct midi_hdr, list);
+
+			list_del(midi_dev->mid_hdrs.next);
+			kfree(midihdr->data);
+			kfree(midihdr);
+		}
+	}
+
+	kfree(midi_dev);
+
+	down(&card->open_sem);
+	card->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE));
+	up(&card->open_sem);
+	wake_up_interruptible(&card->open_wait);
+
+	unlock_kernel();
+
+	return 0;
+}
+
+static ssize_t emu10k1_midi_read(struct file *file, char *buffer, size_t count, loff_t * pos)
+{
+	struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
+	ssize_t ret = 0;
+	u16 cnt;
+	unsigned long flags;
+
+	DPD(4, "emu10k1_midi_read(), count %#x\n", (u32) count);
+
+	if (pos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+
+	if (midi_dev->mistate == MIDIIN_STATE_STOPPED) {
+		if (emu10k1_mpuin_start(midi_dev->card) < 0) {
+			ERROR();
+			return -EINVAL;
+		}
+
+		midi_dev->mistate = MIDIIN_STATE_STARTED;
+	}
+
+	while (count > 0) {
+		cnt = MIDIIN_BUFLEN - midi_dev->ird;
+
+		spin_lock_irqsave(&midi_spinlock, flags);
+
+		if (midi_dev->icnt < cnt)
+			cnt = midi_dev->icnt;
+
+		spin_unlock_irqrestore(&midi_spinlock, flags);
+
+		if (cnt > count)
+			cnt = count;
+
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			DPF(2, " Go to sleep...\n");
+
+			interruptible_sleep_on(&midi_dev->iWait);
+
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+
+			continue;
+		}
+
+		if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) {
+			ERROR();
+			return ret ? ret : -EFAULT;
+		}
+
+		midi_dev->ird += cnt;
+		midi_dev->ird %= MIDIIN_BUFLEN;
+
+		spin_lock_irqsave(&midi_spinlock, flags);
+
+		midi_dev->icnt -= cnt;
+
+		spin_unlock_irqrestore(&midi_spinlock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+
+		if (midi_dev->icnt == 0)
+			break;
+	}
+
+	return ret;
+}
+
+static ssize_t emu10k1_midi_write(struct file *file, const char *buffer, size_t count, loff_t * pos)
+{
+	struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
+	struct midi_hdr *midihdr;
+	ssize_t ret = 0;
+	unsigned long flags;
+
+	DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count);
+
+	if (pos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+
+	if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL)
+		return -EINVAL;
+
+	midihdr->bufferlength = count;
+	midihdr->bytesrecorded = 0;
+	midihdr->flags = 0;
+
+	if ((midihdr->data = (u8 *) kmalloc(count, GFP_KERNEL)) == NULL) {
+		ERROR();
+		kfree(midihdr);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(midihdr->data, buffer, count)) {
+		kfree(midihdr->data);
+		kfree(midihdr);
+		return ret ? ret : -EFAULT;
+	}
+
+	spin_lock_irqsave(&midi_spinlock, flags);
+
+	if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) < 0) {
+		ERROR();
+		kfree(midihdr->data);
+		kfree(midihdr);
+		spin_unlock_irqrestore(&midi_spinlock, flags);
+		return -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&midi_spinlock, flags);
+
+	return count;
+}
+
+static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	DPF(4, "emu10k1_midi_poll() called\n");
+	return 0;
+}
+
+int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg)
+{
+	struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) refdata;
+	struct midi_hdr *midihdr = NULL;
+	unsigned long flags;
+	int i;
+
+	DPF(4, "emu10k1_midi_callback()\n");
+
+	spin_lock_irqsave(&midi_spinlock, flags);
+
+	switch (msg) {
+	case ICARDMIDI_OUTLONGDATA:
+		midihdr = (struct midi_hdr *) pmsg[2];
+
+		kfree(midihdr->data);
+		kfree(midihdr);
+		wake_up_interruptible(&midi_dev->oWait);
+
+		break;
+
+	case ICARDMIDI_INLONGDATA:
+		midihdr = (struct midi_hdr *) pmsg[2];
+
+		for (i = 0; i < midihdr->bytesrecorded; i++) {
+			midi_dev->iBuf[midi_dev->iwr++] = midihdr->data[i];
+			midi_dev->iwr %= MIDIIN_BUFLEN;
+		}
+
+		midi_dev->icnt += midihdr->bytesrecorded;
+
+		if (midi_dev->mistate == MIDIIN_STATE_STARTED) {
+			init_midi_hdr(midihdr);
+			emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr);
+			wake_up_interruptible(&midi_dev->iWait);
+		}
+		break;
+
+	case ICARDMIDI_INDATA:
+		{
+			u8 *pBuf = (u8 *) & pmsg[1];
+			u16 bytesvalid = pmsg[2];
+
+			for (i = 0; i < bytesvalid; i++) {
+				midi_dev->iBuf[midi_dev->iwr++] = pBuf[i];
+				midi_dev->iwr %= MIDIIN_BUFLEN;
+			}
+
+			midi_dev->icnt += bytesvalid;
+		}
+
+		wake_up_interruptible(&midi_dev->iWait);
+		break;
+
+	default:		/* Unknown message */
+		spin_unlock_irqrestore(&midi_spinlock, flags);
+		return -1;
+	}
+
+	spin_unlock_irqrestore(&midi_spinlock, flags);
+
+	return 0;
+}
+
+/* MIDI file operations */
+struct file_operations emu10k1_midi_fops = {
+	owner:		THIS_MODULE,
+	read:		emu10k1_midi_read,
+	write:		emu10k1_midi_write,
+	poll:		emu10k1_midi_poll,
+	open:		emu10k1_midi_open,
+	release:	emu10k1_midi_release,
+};
+
+
+#ifdef EMU10K1_SEQUENCER
+
+/* functions used for sequencer access */
+
+int emu10k1_seq_midi_open(int dev, int mode,
+				void (*input) (int dev, unsigned char data),
+				void (*output) (int dev))
+{
+	struct emu10k1_card *card;
+	struct midi_openinfo dsCardMidiOpenInfo;
+	struct emu10k1_mididevice *midi_dev;
+
+	if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
+		return -EINVAL;
+
+	card = midi_devs[dev]->devc;
+
+	if (card->open_mode)		/* card is opened native */
+		return -EBUSY;
+			
+	DPF(2, "emu10k1_seq_midi_open()\n");
+	
+	if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL)
+		return -EINVAL;
+
+	midi_dev->card = card;
+	midi_dev->mistate = MIDIIN_STATE_STOPPED;
+	init_waitqueue_head(&midi_dev->oWait);
+	init_waitqueue_head(&midi_dev->iWait);
+	midi_dev->ird = 0;
+	midi_dev->iwr = 0;
+	midi_dev->icnt = 0;
+	INIT_LIST_HEAD(&midi_dev->mid_hdrs);
+
+	dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
+
+	if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
+		ERROR();
+		return -ENODEV;
+	}
+
+	card->seq_mididev = midi_dev;
+		
+	return 0;
+}
+
+void emu10k1_seq_midi_close(int dev)
+{
+	struct emu10k1_card *card;
+
+	DPF(2, "emu10k1_seq_midi_close()\n");
+	if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
+		return;
+
+	card = midi_devs[dev]->devc;
+	emu10k1_mpuout_close(card);
+
+	if (card->seq_mididev) {
+		kfree(card->seq_mididev);
+		card->seq_mididev = 0;
+	}
+}
+
+int emu10k1_seq_midi_out(int dev, unsigned char midi_byte)
+{
+	struct emu10k1_card *card;
+	struct midi_hdr *midihdr;
+	unsigned long flags;
+
+	if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
+		return -EINVAL;
+
+	card = midi_devs[dev]->devc;
+
+	if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL)
+		return -EINVAL;
+
+	midihdr->bufferlength = 1;
+	midihdr->bytesrecorded = 0;
+	midihdr->flags = 0;
+
+	if ((midihdr->data = (u8 *) kmalloc(1, GFP_KERNEL)) == NULL) {
+		ERROR();
+		kfree(midihdr);
+		return -EINVAL;
+	}
+
+	*(midihdr->data) = midi_byte;
+	
+	spin_lock_irqsave(&midi_spinlock, flags);
+
+	if (emu10k1_mpuout_add_buffer(card, midihdr) < 0) {
+		ERROR();
+		kfree(midihdr->data);
+		kfree(midihdr);
+		spin_unlock_irqrestore(&midi_spinlock, flags);
+		return -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&midi_spinlock, flags);
+
+	return 1;
+}
+
+int emu10k1_seq_midi_start_read(int dev)
+{
+	return 0;
+}
+
+int emu10k1_seq_midi_end_read(int dev)
+{
+	return 0;
+}
+
+void emu10k1_seq_midi_kick(int dev)
+{
+}
+
+int emu10k1_seq_midi_buffer_status(int dev)
+{
+	int count;
+	struct midi_queue *queue;
+	struct emu10k1_card *card;
+
+	if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
+		return -EINVAL;
+
+	count = 0;
+
+	card = midi_devs[dev]->devc;
+	queue = card->mpuout->firstmidiq;
+
+	while (queue != NULL) {
+		count++;
+		if (queue == card->mpuout->lastmidiq)
+			break;
+
+		queue = queue->next;
+	}
+
+	return count;
+}
+
+#endif
+
diff -Nru linux/sound/oss/emu10k1/midi.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.h
--- linux/sound/oss/emu10k1/midi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,55 @@
+/*     
+ **********************************************************************
+ *     midi.h
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */ 
+
+#ifndef _MIDI_H
+#define _MIDI_H
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define MIDIIN_STATE_STARTED 0x00000001
+#define MIDIIN_STATE_STOPPED 0x00000002
+
+#define MIDIIN_BUFLEN 1024
+
+struct emu10k1_mididevice
+{
+	struct emu10k1_card *card;
+	u32 mistate;
+	wait_queue_head_t oWait;
+	wait_queue_head_t iWait;
+	s8 iBuf[MIDIIN_BUFLEN];
+	u16 ird, iwr, icnt;
+	struct list_head mid_hdrs;
+};
+
+#endif /* _MIDI_H */
diff -Nru linux/sound/oss/emu10k1/mixer.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/mixer.c
--- linux/sound/oss/emu10k1/mixer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/mixer.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,675 @@
+/*
+ **********************************************************************
+ *     mixer.c - /dev/mixer interface for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *     November 2, 1999     Alan Cox        cleaned up stuff
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#define __NO_VERSION__		/* Kernel version only defined once */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "recmgr.h"
+
+
+static const u32 bass_table[41][5] = {
+	{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
+	{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
+	{ 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
+	{ 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
+	{ 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
+	{ 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
+	{ 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
+	{ 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
+	{ 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
+	{ 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
+	{ 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
+	{ 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
+	{ 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
+	{ 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
+	{ 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
+	{ 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
+	{ 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
+	{ 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
+	{ 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
+	{ 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
+	{ 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee },
+	{ 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
+	{ 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
+	{ 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
+	{ 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
+	{ 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
+	{ 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
+	{ 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
+	{ 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
+	{ 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
+	{ 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
+	{ 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
+	{ 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
+	{ 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
+	{ 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
+	{ 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
+	{ 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
+	{ 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
+	{ 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
+	{ 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
+	{ 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
+};
+
+static const u32 treble_table[41][5] = {
+	{ 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
+	{ 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
+	{ 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
+	{ 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
+	{ 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
+	{ 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
+	{ 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
+	{ 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
+	{ 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
+	{ 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
+	{ 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
+	{ 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
+	{ 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
+	{ 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
+	{ 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
+	{ 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
+	{ 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
+	{ 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
+	{ 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
+	{ 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
+	{ 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
+	{ 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
+	{ 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
+	{ 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
+	{ 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
+	{ 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
+	{ 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
+	{ 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
+	{ 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
+	{ 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
+	{ 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
+	{ 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
+	{ 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
+	{ 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
+	{ 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
+	{ 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
+	{ 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
+	{ 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
+	{ 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
+	{ 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
+	{ 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
+};
+
+
+static void set_bass(struct emu10k1_card *card, int l, int r)
+{
+	int i;
+
+	l = (l * 40 + 50) / 100;
+	r = (r * 40 + 50) / 100;
+
+	for (i = 0; i < 5; i++)
+		sblive_writeptr(card, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_BASS][0] + i, 0, bass_table[l][i]);
+}
+
+static void set_treble(struct emu10k1_card *card, int l, int r)
+{
+	int i;
+
+	l = (l * 40 + 50) / 100;
+	r = (r * 40 + 50) / 100;
+
+	for (i = 0; i < 5; i++)
+		sblive_writeptr(card, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_TREBLE][0] + i , 0, treble_table[l][i]);
+}
+
+const char volume_params[SOUND_MIXER_NRDEVICES]= {
+/* Used by the ac97 driver */
+	[SOUND_MIXER_VOLUME]	=	VOL_6BIT,
+	[SOUND_MIXER_BASS]	=	VOL_4BIT,
+	[SOUND_MIXER_TREBLE]	=	VOL_4BIT,
+	[SOUND_MIXER_PCM]	=	VOL_5BIT,
+	[SOUND_MIXER_SPEAKER]	=	VOL_4BIT,
+	[SOUND_MIXER_LINE]	=	VOL_5BIT,
+	[SOUND_MIXER_MIC]	=	VOL_5BIT,
+	[SOUND_MIXER_CD]	=	VOL_5BIT,
+	[SOUND_MIXER_ALTPCM]	=	VOL_6BIT,
+	[SOUND_MIXER_IGAIN]	=	VOL_4BIT,
+	[SOUND_MIXER_LINE1]	=	VOL_5BIT,
+	[SOUND_MIXER_PHONEIN]	= 	VOL_5BIT,
+	[SOUND_MIXER_PHONEOUT]	= 	VOL_6BIT,
+	[SOUND_MIXER_VIDEO]	=	VOL_5BIT,
+/* Not used by the ac97 driver */
+	[SOUND_MIXER_SYNTH]	=	VOL_5BIT,
+	[SOUND_MIXER_IMIX]	=	VOL_5BIT,
+	[SOUND_MIXER_RECLEV]	=	VOL_5BIT,
+	[SOUND_MIXER_OGAIN]	=	VOL_5BIT,
+	[SOUND_MIXER_LINE2]	=	VOL_5BIT,
+	[SOUND_MIXER_LINE3]	=	VOL_5BIT,
+	[SOUND_MIXER_DIGITAL1]	=	VOL_5BIT,
+	[SOUND_MIXER_DIGITAL2]	=	VOL_5BIT,
+	[SOUND_MIXER_DIGITAL3]	=	VOL_5BIT,
+	[SOUND_MIXER_RADIO]	=	VOL_5BIT,
+	[SOUND_MIXER_MONITOR]	=	VOL_5BIT
+};
+
+/* Mixer file operations */
+static int emu10k1_private_mixer(struct emu10k1_card *card, unsigned int cmd, unsigned long arg)
+{
+	struct mixer_private_ioctl *ctl;
+	struct dsp_patch *patch;
+	u32 size, page;
+	int addr, size_reg, i, ret;
+	unsigned int id, ch;
+
+	switch (cmd) {
+
+	case SOUND_MIXER_PRIVATE3:
+
+		ctl = (struct mixer_private_ioctl *) kmalloc(sizeof(struct mixer_private_ioctl), GFP_KERNEL);
+		if (ctl == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(ctl, (void *) arg, sizeof(struct mixer_private_ioctl))) {
+			kfree(ctl);
+			return -EFAULT;
+		}
+
+		ret = 0;
+		switch (ctl->cmd) {
+#ifdef DBGEMU
+		case CMD_WRITEFN0:
+			emu10k1_writefn0(card, ctl->val[0], ctl->val[1]);
+			break;
+
+		case CMD_WRITEPTR:
+			if (ctl->val[1] >= 0x40 || ctl->val[0] > 0xff) {
+				ret = -EINVAL;
+				break;
+			}
+
+			if ((ctl->val[0] & 0x7ff) > 0x3f)
+				ctl->val[1] = 0x00;
+
+			sblive_writeptr(card, ctl->val[0], ctl->val[1], ctl->val[2]);
+
+			break;
+#endif
+		case CMD_READFN0:
+			ctl->val[2] = emu10k1_readfn0(card, ctl->val[0]);
+
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_READPTR:
+			if (ctl->val[1] >= 0x40 || (ctl->val[0] & 0x7ff) > 0xff) {
+				ret = -EINVAL;
+				break;
+			}
+
+			if ((ctl->val[0] & 0x7ff) > 0x3f)
+				ctl->val[1] = 0x00;
+
+			ctl->val[2] = sblive_readptr(card, ctl->val[0], ctl->val[1]);
+
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_SETRECSRC:
+			switch (ctl->val[0]) {
+			case WAVERECORD_AC97:
+				if (card->isaps) {
+					ret = -EINVAL;
+					break;
+				}
+
+				card->wavein.recsrc = WAVERECORD_AC97;
+				break;
+
+			case WAVERECORD_MIC:
+				card->wavein.recsrc = WAVERECORD_MIC;
+				break;
+
+			case WAVERECORD_FX:
+				card->wavein.recsrc = WAVERECORD_FX;
+				card->wavein.fxwc = ctl->val[1] & 0xffff;
+
+				if (!card->wavein.fxwc)
+					ret = -EINVAL;
+
+				break;
+
+			default:
+				ret = -EINVAL;
+				break;
+			}
+			break;
+
+		case CMD_GETRECSRC:
+			ctl->val[0] = card->wavein.recsrc;
+			ctl->val[1] = card->wavein.fxwc;
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_GETVOICEPARAM:
+			ctl->val[0] = card->waveout.send_routing[0];
+			ctl->val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 |
+			    	      card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24;
+
+			ctl->val[2] = card->waveout.send_routing[1];
+			ctl->val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 |
+				      card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24;
+
+			ctl->val[4] = card->waveout.send_routing[2];
+			ctl->val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 |
+				     card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24;
+
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_SETVOICEPARAM:
+			card->waveout.send_routing[0] = ctl->val[0] & 0xffff;
+			card->waveout.send_a[0] = ctl->val[1] & 0xff;
+			card->waveout.send_b[0] = (ctl->val[1] >> 8) & 0xff;
+			card->waveout.send_c[0] = (ctl->val[1] >> 16) & 0xff;
+			card->waveout.send_d[0] = (ctl->val[1] >> 24) & 0xff;
+
+			card->waveout.send_routing[1] = ctl->val[2] & 0xffff;
+			card->waveout.send_a[1] = ctl->val[3] & 0xff;
+			card->waveout.send_b[1] = (ctl->val[3] >> 8) & 0xff;
+			card->waveout.send_c[1] = (ctl->val[3] >> 16) & 0xff;
+			card->waveout.send_d[1] = (ctl->val[3] >> 24) & 0xff;
+
+			card->waveout.send_routing[2] = ctl->val[4] & 0xffff;
+			card->waveout.send_a[2] = ctl->val[5] & 0xff;
+			card->waveout.send_b[2] = (ctl->val[5] >> 8) & 0xff;
+			card->waveout.send_c[2] = (ctl->val[5] >> 16) & 0xff;
+			card->waveout.send_d[2] = (ctl->val[5] >> 24) & 0xff;
+
+			break;
+		
+		case CMD_SETMCH_FX:
+			card->mchannel_fx = ctl->val[0] & 0x000f;
+			break;
+		
+		case CMD_GETPATCH:
+			if (ctl->val[0] == 0) {
+				if (copy_to_user((void *) arg, &card->mgr.rpatch, sizeof(struct dsp_rpatch)))
+                                	ret = -EFAULT;
+			} else {
+				if ((ctl->val[0] - 1) / PATCHES_PER_PAGE >= card->mgr.current_pages) {
+					ret = -EINVAL;
+					break;
+				}
+
+				if (copy_to_user((void *) arg, PATCH(&card->mgr, ctl->val[0] - 1), sizeof(struct dsp_patch)))
+					ret = -EFAULT;
+			}
+
+			break;
+
+		case CMD_GETGPR:
+			id = ctl->val[0];
+
+			if (id > NUM_GPRS) {
+				ret = -EINVAL;
+				break;
+			}
+
+			if (copy_to_user((void *) arg, &card->mgr.gpr[id], sizeof(struct dsp_gpr)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_GETCTLGPR:
+			addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, &((char *) ctl->val)[PATCH_NAME_SIZE]);
+			ctl->val[0] = sblive_readptr(card, addr, 0);
+
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_SETPATCH:
+			if (ctl->val[0] == 0)
+				memcpy(&card->mgr.rpatch, &ctl->val[1], sizeof(struct dsp_rpatch));
+			else {
+				page = (ctl->val[0] - 1) / PATCHES_PER_PAGE;
+				if (page > MAX_PATCHES_PAGES) {
+					ret = -EINVAL;
+					break;
+				}
+
+				if (page >= card->mgr.current_pages) {
+					for (i = card->mgr.current_pages; i < page + 1; i++) {
+				                card->mgr.patch[i] = (void *)__get_free_page(GFP_KERNEL);
+						if(card->mgr.patch[i] == NULL) {
+							card->mgr.current_pages = i;
+							ret = -ENOMEM;
+							break;
+						}
+						memset(card->mgr.patch[i], 0, PAGE_SIZE);
+					}
+					card->mgr.current_pages = page + 1;
+				}
+
+				patch = PATCH(&card->mgr, ctl->val[0] - 1);
+
+				memcpy(patch, &ctl->val[1], sizeof(struct dsp_patch));
+
+				if (patch->code_size == 0) {
+					for(i = page + 1; i < card->mgr.current_pages; i++)
+                                                free_page((unsigned long) card->mgr.patch[i]);
+
+					card->mgr.current_pages = page + 1;
+				}
+			}
+			break;
+
+		case CMD_SETGPR:
+			if (ctl->val[0] > NUM_GPRS) {
+				ret = -EINVAL;
+				break;
+			}
+
+			memcpy(&card->mgr.gpr[ctl->val[0]], &ctl->val[1], sizeof(struct dsp_gpr));
+			break;
+
+		case CMD_SETCTLGPR:
+			addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, (char *) ctl->val + PATCH_NAME_SIZE);
+			emu10k1_set_control_gpr(card, addr, *((s32 *)((char *) ctl->val + 2 * PATCH_NAME_SIZE)), 0);
+			break;
+
+		case CMD_SETGPOUT:
+			if (ctl->val[0] > 2 || ctl->val[1] > 1) {
+				ret= -EINVAL;
+				break;
+			}
+
+			emu10k1_writefn0(card, (1 << 24) | (((ctl->val[0]) + 10) << 16) | HCFG, ctl->val[1]);
+			break;
+
+		case CMD_GETGPR2OSS:
+			id = ctl->val[0];
+			ch = ctl->val[1];
+
+			if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) {
+				ret = -EINVAL;
+				break;
+			}
+
+			ctl->val[2] = card->mgr.ctrl_gpr[id][ch];
+
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+
+			break;
+
+		case CMD_SETGPR2OSS:
+			id = ctl->val[0];
+			ch = ctl->val[1];
+			addr = ctl->val[2];
+
+			if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) {
+				ret = -EINVAL;
+				break;
+			}
+
+			card->mgr.ctrl_gpr[id][ch] = addr;
+
+			if (card->isaps)
+				break;
+
+			if (addr >= 0) {
+				unsigned int state = card->ac97.mixer_state[id];
+
+				if (ch) {
+					state >>= 8;
+					card->ac97.stereo_mixers |= (1 << id);
+				} else {
+					card->ac97.supported_mixers |= (1 << id);
+				}
+
+				if (id == SOUND_MIXER_TREBLE) {
+					set_treble(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff);
+				} else if (id == SOUND_MIXER_BASS) {
+					set_bass(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff);
+				} else
+					emu10k1_set_volume_gpr(card, addr, state & 0xff,
+							       volume_params[id]);
+			} else {
+				if (ch) {
+					card->ac97.stereo_mixers &= ~(1 << id);
+					card->ac97.stereo_mixers |= card->ac97_stereo_mixers;
+				} else {
+					card->ac97.supported_mixers &= ~(1 << id);
+					card->ac97.supported_mixers |= card->ac97_supported_mixers;
+				}
+			}
+			break;
+
+		case CMD_SETPASSTHROUGH:
+			card->pt.selected = ctl->val[0] ? 1 : 0;
+			if (card->pt.state != PT_STATE_INACTIVE)
+				break;
+
+			card->pt.spcs_to_use = ctl->val[0] & 0x07;
+			break;
+
+		case CMD_PRIVATE3_VERSION:
+			ctl->val[0]=PRIVATE3_VERSION;
+			if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl)))
+				ret = -EFAULT;
+			break;
+
+		default:
+			ret = -EINVAL;
+			break;
+		}
+
+		kfree(ctl);
+		return ret;
+		break;
+
+	case SOUND_MIXER_PRIVATE4:
+
+		if (copy_from_user(&size, (void *) arg, sizeof(size)))
+			return -EFAULT;
+
+		DPD(2, "External tram size %#x\n", size);
+
+		if (size > 0x1fffff)
+			return -EINVAL;
+
+		size_reg = 0;
+
+		if (size != 0) {
+			size = (size - 1) >> 14;
+
+			while (size) {
+				size >>= 1;
+				size_reg++;
+			}
+
+			size = 0x4000 << size_reg;
+		}
+
+		DPD(2, "External tram size %#x %#x\n", size, size_reg);
+
+		if (size != card->tankmem.size) {
+			if (card->tankmem.size > 0) {
+				emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1);
+
+				sblive_writeptr_tag(card, 0, TCB, 0, TCBS, 0, TAGLIST_END);
+
+				pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle);
+
+				card->tankmem.size = 0;
+			}
+
+			if (size != 0) {
+				card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size, &card->tankmem.dma_handle);
+				if (card->tankmem.addr == NULL)
+					return -ENOMEM;
+
+				card->tankmem.size = size;
+
+				sblive_writeptr_tag(card, 0, TCB, card->tankmem.dma_handle, TCBS, size_reg, TAGLIST_END);
+
+				emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0);
+			}
+		}
+		return 0;
+		break;
+
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static int emu10k1_dsp_mixer(struct emu10k1_card *card, unsigned int oss_mixer, unsigned long arg)
+{
+	unsigned int left, right;
+	int val;
+	int scale;
+
+	if (get_user(val, (int *)arg))
+		return -EFAULT;
+
+	/* cleanse input a little */
+	right = ((val >> 8)  & 0xff);
+	left = (val  & 0xff);
+
+	if (right > 100) right = 100;
+	if (left > 100) left = 100;
+
+	card->ac97.mixer_state[oss_mixer] = (right << 8) | left;
+	if (oss_mixer == SOUND_MIXER_TREBLE) {
+		set_treble(card, left, right);
+		return 0;
+	} if (oss_mixer == SOUND_MIXER_BASS) {
+		set_bass(card, left, right);
+		return 0;
+	}
+
+	if (oss_mixer == SOUND_MIXER_VOLUME)
+		scale = 1 << card->ac97.bit_resolution;
+	else
+		scale = volume_params[oss_mixer];
+
+	emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, scale);
+	emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, scale);
+
+	if (card->ac97_supported_mixers & (1 << oss_mixer))
+		card->ac97.write_mixer(&card->ac97, oss_mixer, left, right);
+
+	return 0;
+}
+
+static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int ret;
+	struct emu10k1_card *card = file->private_data;
+	unsigned int oss_mixer = _IOC_NR(cmd);
+	
+	ret = -EINVAL;
+	if (!card->isaps) {
+		if (cmd == SOUND_MIXER_INFO) {
+			mixer_info info;
+
+			strncpy(info.id, card->ac97.name, sizeof(info.id));
+			strncpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name));
+			info.modify_counter = card->ac97.modcnt;
+
+			if (copy_to_user((void *)arg, &info, sizeof(info)))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		if ((_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES)
+			ret = emu10k1_dsp_mixer(card, oss_mixer, arg);
+		else
+			ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg);
+	}
+	
+	if (ret < 0)
+		ret = emu10k1_private_mixer(card, cmd, arg);
+
+	return ret;
+}
+
+static int emu10k1_mixer_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct emu10k1_card *card = NULL;
+	struct list_head *entry;
+
+	DPF(4, "emu10k1_mixer_open()\n");
+
+	list_for_each(entry, &emu10k1_devs) {
+		card = list_entry(entry, struct emu10k1_card, list);
+
+		if (card->ac97.dev_mixer == minor)
+			goto match;
+	}
+
+	return -ENODEV;
+
+      match:
+	file->private_data = card;
+	return 0;
+}
+
+static int emu10k1_mixer_release(struct inode *inode, struct file *file)
+{
+	DPF(4, "emu10k1_mixer_release()\n");
+	return 0;
+}
+
+struct file_operations emu10k1_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		emu10k1_mixer_ioctl,
+	open:		emu10k1_mixer_open,
+	release:	emu10k1_mixer_release,
+};
diff -Nru linux/sound/oss/emu10k1/passthrough.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.c
--- linux/sound/oss/emu10k1/passthrough.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,239 @@
+/*
+ **********************************************************************
+ *     passthrough.c -- Emu10k1 digital passthrough
+ *     Copyright (C) 2001  Juha Yrjl <jyrjola@cc.hut.fi>
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     May 15, 2001	    Juha Yrjl	    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+                       
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+
+#include "hwaccess.h"
+#include "cardwo.h"
+#include "cardwi.h"
+#include "recmgr.h"
+#include "irqmgr.h"
+#include "audio.h"
+#include "8010.h"
+
+static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right)
+{
+	unsigned int idx;
+
+	ptr[pt->copyptr] = left;
+	idx = pt->copyptr + PT_SAMPLES/2;
+	idx %= PT_SAMPLES;
+	ptr[idx] = right;
+}
+
+static inline int pt_can_write(struct pt_data *pt)
+{
+	return pt->blocks_copied < pt->blocks_played + 8;
+}
+
+static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock)
+{
+	struct emu10k1_card *card = wavedev->card;
+	struct pt_data *pt = &card->pt;
+
+	if (nonblock && !pt_can_write(pt))
+		return -EAGAIN;
+	while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) {
+		interruptible_sleep_on(&pt->wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+	if (pt->state == PT_STATE_INACTIVE)
+		return -EAGAIN;
+	
+	return 0;
+}
+
+static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock)
+{
+	struct woinst *woinst = wave_dev->woinst;
+	struct emu10k1_card *card = wave_dev->card;
+	struct pt_data *pt = &card->pt;
+	u16 *ptr = (u16 *) card->tankmem.addr;
+	int i = 0, r;
+	unsigned long flags;
+
+	r = pt_wait_for_write(wave_dev, nonblock);
+	if (r < 0)
+		return r;
+	spin_lock_irqsave(&card->pt.lock, flags);
+	while (i < PT_BLOCKSAMPLES) {
+		pt_putsamples(pt, ptr, block[2*i], block[2*i+1]);
+		if (pt->copyptr == 0)
+			pt->copyptr = PT_SAMPLES;
+		pt->copyptr--;
+		i++;
+	}
+	woinst->total_copied += PT_BLOCKSIZE;
+	pt->blocks_copied++;
+	if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) {
+		DPF(2, "activating digital pass-through playback\n");
+		sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1);
+		pt->state = PT_STATE_PLAYING;
+	}
+	spin_unlock_irqrestore(&card->pt.lock, flags);
+	return 0;
+}
+
+static int pt_setup(struct emu10k1_wavedevice *wave_dev)
+{
+	u32 bits;
+	struct emu10k1_card *card = wave_dev->card;
+	struct pt_data *pt = &card->pt;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0);
+		if (pt->spcs_to_use & (1 << i)) {
+			DPD(2, "using S/PDIF port %d\n", i);
+			bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
+				0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
+			if (pt->ac3data)
+				bits |= SPCS_NOTAUDIODATA;
+			sblive_writeptr(card, SPCS0 + i, 0, bits);
+		}
+	}
+	return 0;
+}
+
+ssize_t emu10k1_pt_write(struct file *file, const char *buffer, size_t count)
+{
+	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	struct emu10k1_card *card = wave_dev->card;
+	struct pt_data *pt = &card->pt;
+	int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0;
+
+	DPD(3, "emu10k1_pt_write(): %d bytes\n", count);
+	
+	nonblock = file->f_flags & O_NONBLOCK;
+	
+	if (card->tankmem.size < PT_SAMPLES*2)
+		return -EFAULT;
+	if (pt->state == PT_STATE_INACTIVE) {
+		DPF(2, "bufptr init\n");
+		pt->playptr = PT_SAMPLES-1;
+		pt->copyptr = PT_INITPTR;
+		pt->blocks_played = pt->blocks_copied = 0;
+		memset(card->tankmem.addr, 0, card->tankmem.size);
+		pt->state = PT_STATE_ACTIVATED;
+		pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL);
+		pt->prepend_size = 0;
+		if (pt->buf == NULL)
+			return -ENOMEM;
+		pt_setup(wave_dev);
+	}
+	if (pt->prepend_size) {
+		int needed = PT_BLOCKSIZE - pt->prepend_size;
+
+		DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed);
+		if (count < needed) {
+			copy_from_user(pt->buf + pt->prepend_size, buffer, count);
+			pt->prepend_size += count;
+			DPD(3, "prepend size now %d\n", pt->prepend_size);
+			return count;
+		}
+		copy_from_user(pt->buf + pt->prepend_size, buffer, needed);
+		r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock);
+		if (r)
+			return r;
+		bytes_copied += needed;
+		pt->prepend_size = 0;
+	}
+	blocks = (count-bytes_copied)/PT_BLOCKSIZE;
+	blocks_copied = 0;
+	while (blocks > 0) {
+		u16 *bufptr = (u16 *) buffer + (bytes_copied/2);
+		copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE);
+		bufptr = (u16 *) pt->buf;
+		r = pt_putblock(wave_dev, bufptr, nonblock);
+		if (r) {
+			if (bytes_copied)
+				return bytes_copied;
+			else
+				return r;
+		}
+		bytes_copied += PT_BLOCKSIZE;
+		blocks--;
+		blocks_copied++;
+	}
+	i = count - bytes_copied;
+	if (i) {
+		pt->prepend_size = i;
+		copy_from_user(pt->buf, buffer + bytes_copied, i);
+		bytes_copied += i;
+		DPD(3, "filling prepend buffer with %d bytes", i);
+	}
+	return bytes_copied;
+}
+
+void emu10k1_pt_stop(struct emu10k1_card *card)
+{
+	struct pt_data *pt = &card->pt;
+	int i;
+
+	if (pt->state != PT_STATE_INACTIVE) {
+		DPF(2, "digital pass-through stopped\n");
+		sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 0);
+		for (i = 0; i < 3; i++) {
+                        if (pt->spcs_to_use & (1 << i))
+				sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]);
+		}
+		pt->state = PT_STATE_INACTIVE;
+		kfree(pt->buf);
+	}
+}
+
+void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev)
+{
+	struct woinst *woinst = wave_dev->woinst;
+	struct pt_data *pt = &wave_dev->card->pt;
+	u32 pos;
+
+	if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) {
+		pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0);
+		if (pos > PT_BLOCKSAMPLES)
+			pos = PT_BLOCKSAMPLES;
+		pos = 4 * (PT_BLOCKSAMPLES - pos);
+	} else
+		pos = 0;
+	woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos;
+	woinst->buffer.hw_pos = pos;
+}
diff -Nru linux/sound/oss/emu10k1/passthrough.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.h
--- linux/sound/oss/emu10k1/passthrough.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,70 @@
+/*
+ **********************************************************************
+ *     passthrough.h -- Emu10k1 digital passthrough header file
+ *     Copyright (C) 2001  Juha Yrjl <jyrjola@cc.hut.fi>
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     May 15, 2001	    Juha Yrjl     base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _PASSTHROUGH_H
+#define _PASSTHROUGH_H
+
+#include "audio.h"
+
+/* number of 16-bit stereo samples in XTRAM buffer */
+#define PT_SAMPLES 0x8000
+#define PT_BLOCKSAMPLES 0x400
+#define PT_BLOCKSIZE (PT_BLOCKSAMPLES*4)
+#define PT_BLOCKSIZE_LOG2 12
+#define PT_BLOCKCOUNT (PT_SAMPLES/PT_BLOCKSAMPLES)
+#define PT_INITPTR (PT_SAMPLES/2-1)
+
+#define PT_STATE_INACTIVE 0
+#define PT_STATE_ACTIVATED 1
+#define PT_STATE_PLAYING 2
+
+/* passthrough struct */
+struct pt_data
+{
+	u8	selected, state, spcs_to_use;
+	int	intr_gpr, enable_gpr, pos_gpr;
+	u32	blocks_played, blocks_copied, old_spcs[3];
+	u32	playptr, copyptr;
+	u32	prepend_size;
+	u8	*buf;
+	u8	ac3data;
+
+	char	*patch_name, *intr_gpr_name, *enable_gpr_name, *pos_gpr_name;
+
+	wait_queue_head_t wait;
+	spinlock_t lock;
+};
+
+ssize_t emu10k1_pt_write(struct file *file, const char *buf, size_t count);
+void emu10k1_pt_stop(struct emu10k1_card *card);
+void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev);
+
+#endif /* _PASSTHROUGH_H */
diff -Nru linux/sound/oss/emu10k1/recmgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.c
--- linux/sound/oss/emu10k1/recmgr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,138 @@
+/*
+ **********************************************************************
+ *     recmgr.c -- Recording manager for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include "8010.h"
+#include "recmgr.h"
+
+void emu10k1_start_record(struct emu10k1_card *card, struct wavein_buffer *buffer)
+{
+	DPF(2, "emu10k1_start_record()\n");
+
+	sblive_writeptr(card, buffer->sizereg, 0, buffer->sizeregval);
+
+	if (buffer->adcctl)
+		sblive_writeptr(card, ADCCR, 0, buffer->adcctl);
+
+	return;
+}
+
+void emu10k1_stop_record(struct emu10k1_card *card, struct wavein_buffer *buffer)
+{
+	DPF(2, "emu10k1_stop_record()\n");
+
+	/* Disable record transfer */
+	if (buffer->adcctl)
+		sblive_writeptr(card, ADCCR, 0, 0);
+
+	sblive_writeptr(card, buffer->sizereg, 0, ADCBS_BUFSIZE_NONE);
+
+	return;
+}
+
+void emu10k1_set_record_src(struct emu10k1_card *card, struct wiinst *wiinst)
+{
+	struct wavein_buffer *buffer = &wiinst->buffer;
+
+	DPF(2, "emu10k1_set_record_src()\n");
+
+	switch (wiinst->recsrc) {
+
+	case WAVERECORD_AC97:
+		DPF(2, "recording source: AC97\n");
+		buffer->sizereg = ADCBS;
+		buffer->addrreg = ADCBA;
+		buffer->idxreg = ADCIDX_IDX;
+
+		switch (wiinst->format.samplingrate) {
+		case 0xBB80:
+			buffer->adcctl = ADCCR_SAMPLERATE_48;
+			break;
+		case 0xAC44:
+			buffer->adcctl = ADCCR_SAMPLERATE_44;
+			break;
+		case 0x7D00:
+			buffer->adcctl = ADCCR_SAMPLERATE_32;
+			break;
+		case 0x5DC0:
+			buffer->adcctl = ADCCR_SAMPLERATE_24;
+			break;
+		case 0x5622:
+			buffer->adcctl = ADCCR_SAMPLERATE_22;
+			break;
+		case 0x3E80:
+			buffer->adcctl = ADCCR_SAMPLERATE_16;
+			break;
+		case 0x2B11:
+			buffer->adcctl = ADCCR_SAMPLERATE_11;
+			break;
+		case 0x1F40:
+			buffer->adcctl = ADCCR_SAMPLERATE_8;
+			break;
+		default:
+			BUG();
+			break;
+		}
+
+		buffer->adcctl |= ADCCR_LCHANENABLE;
+
+		if (wiinst->format.channels == 2)
+			buffer->adcctl |= ADCCR_RCHANENABLE;
+
+		break;
+
+	case WAVERECORD_MIC:
+		DPF(2, "recording source: MIC\n");
+		buffer->sizereg = MICBS;
+		buffer->addrreg = MICBA;
+		buffer->idxreg = MICIDX_IDX;
+		buffer->adcctl = 0;
+		break;
+
+	case WAVERECORD_FX:
+		DPF(2, "recording source: FX\n");
+		buffer->sizereg = FXBS;
+		buffer->addrreg = FXBA;
+		buffer->idxreg = FXIDX_IDX;
+		buffer->adcctl = 0;
+
+		sblive_writeptr(card, FXWC, 0, wiinst->fxwc);
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	DPD(2, "bus addx: %#x\n", buffer->dma_handle);
+
+	sblive_writeptr(card, buffer->addrreg, 0, buffer->dma_handle);
+
+	return;
+}
diff -Nru linux/sound/oss/emu10k1/recmgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.h
--- linux/sound/oss/emu10k1/recmgr.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,48 @@
+/*     
+ **********************************************************************
+ *     recmgr.h
+ *     Copyright 1999, 2000 Creative Labs, Inc. 
+ * 
+ ********************************************************************** 
+ * 
+ *     Date                 Author          Summary of changes 
+ *     ----                 ------          ------------------ 
+ *     October 20, 1999     Bertrand Lee    base code release 
+ * 
+ ********************************************************************** 
+ * 
+ *     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., 675 Mass Ave, Cambridge, MA 02139, 
+ *     USA. 
+ * 
+ ********************************************************************** 
+ */
+
+#ifndef _RECORDMGR_H
+#define _RECORDMGR_H
+
+#include "hwaccess.h"
+#include "cardwi.h"
+
+/* Recording resources */
+#define WAVERECORD_AC97		0x01
+#define WAVERECORD_MIC		0x02
+#define WAVERECORD_FX		0x03
+
+void emu10k1_start_record(struct emu10k1_card *, struct wavein_buffer *);
+void emu10k1_stop_record(struct emu10k1_card *, struct wavein_buffer *);
+void emu10k1_set_record_src(struct emu10k1_card *, struct wiinst *wiinst);
+
+
+#endif /* _RECORDMGR_H */
diff -Nru linux/sound/oss/emu10k1/timer.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.c
--- linux/sound/oss/emu10k1/timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,176 @@
+
+/*
+ **********************************************************************
+ *     timer.c
+ *     Copyright (C) 1999, 2000 Creative Labs, inc.
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+/* 3/6/2000	Improved support for different timer delays  Rui Sousa */
+
+/* 4/3/2000	Implemented timer list using list.h 	     Rui Sousa */
+
+#include "hwaccess.h"
+#include "8010.h"
+#include "irqmgr.h"
+#include "timer.h"
+
+/* Try to schedule only once per fragment */
+
+void emu10k1_timer_irqhandler(struct emu10k1_card *card)
+{
+	struct emu_timer *t;
+	struct list_head *entry;
+
+	spin_lock(&card->timer_lock);
+
+	list_for_each(entry, &card->timers) {
+		t = list_entry(entry, struct emu_timer, list);
+
+		if (t->state & TIMER_STATE_ACTIVE) {
+			t->count++;
+			if (t->count == t->count_max) {
+				t->count = 0;
+				tasklet_hi_schedule(&t->tasklet);
+			}
+		}
+	}
+
+	spin_unlock(&card->timer_lock);
+
+	return;
+}
+
+void emu10k1_timer_install(struct emu10k1_card *card, struct emu_timer *timer, u32 delay)
+{
+	struct emu_timer *t;
+	struct list_head *entry;
+	unsigned long flags;
+
+	if (delay < 5)
+		delay = 5;
+
+	timer->delay = delay;
+	timer->state = TIMER_STATE_INSTALLED;
+
+	spin_lock_irqsave(&card->timer_lock, flags);
+
+	timer->count_max = timer->delay / (card->timer_delay < 1024 ? card->timer_delay : 1024);
+	timer->count = timer->count_max - 1;
+
+	list_add(&timer->list, &card->timers);
+
+	if (card->timer_delay > delay) {
+		if (card->timer_delay == TIMER_STOPPED)
+			emu10k1_irq_enable(card, INTE_INTERVALTIMERENB);
+
+		card->timer_delay = delay;
+		delay = (delay < 1024 ? delay : 1024);
+
+		emu10k1_writefn0(card, TIMER_RATE, delay);
+
+		list_for_each(entry, &card->timers) {
+			t = list_entry(entry, struct emu_timer, list);
+
+			t->count_max = t->delay / delay;
+			/* don't want to think much, just force scheduling 
+			   on the next interrupt */
+			t->count = t->count_max - 1;
+		}
+
+		DPD(2, "timer rate --> %u\n", delay);
+	}
+
+	spin_unlock_irqrestore(&card->timer_lock, flags);
+
+	return;
+}
+
+void emu10k1_timer_uninstall(struct emu10k1_card *card, struct emu_timer *timer)
+{
+	struct emu_timer *t;
+	struct list_head *entry;
+	u32 delay = TIMER_STOPPED;
+	unsigned long flags;
+
+	if (timer->state == TIMER_STATE_UNINSTALLED)
+		return;
+
+	spin_lock_irqsave(&card->timer_lock, flags);
+
+	list_del(&timer->list);
+
+	list_for_each(entry, &card->timers) {
+		t = list_entry(entry, struct emu_timer, list);
+
+		if (t->delay < delay)
+			delay = t->delay;
+	}
+
+	if (card->timer_delay != delay) {
+		card->timer_delay = delay;
+
+		if (delay == TIMER_STOPPED)
+			emu10k1_irq_disable(card, INTE_INTERVALTIMERENB);
+		else {
+			delay = (delay < 1024 ? delay : 1024);
+
+			emu10k1_writefn0(card, TIMER_RATE, delay);
+
+			list_for_each(entry, &card->timers) {
+				t = list_entry(entry, struct emu_timer, list);
+
+				t->count_max = t->delay / delay;
+				t->count = t->count_max - 1;
+			}
+		}
+
+		DPD(2, "timer rate --> %u\n", delay);
+	}
+
+	spin_unlock_irqrestore(&card->timer_lock, flags);
+
+	timer->state = TIMER_STATE_UNINSTALLED;
+
+	return;
+}
+
+void emu10k1_timer_enable(struct emu10k1_card *card, struct emu_timer *timer)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->timer_lock, flags);
+	timer->state |= TIMER_STATE_ACTIVE;
+	spin_unlock_irqrestore(&card->timer_lock, flags);
+
+	return;
+}
+
+void emu10k1_timer_disable(struct emu10k1_card *card, struct emu_timer *timer)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->timer_lock, flags);
+	timer->state &= ~TIMER_STATE_ACTIVE;
+	spin_unlock_irqrestore(&card->timer_lock, flags);
+
+	return;
+}
diff -Nru linux/sound/oss/emu10k1/timer.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.h
--- linux/sound/oss/emu10k1/timer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,54 @@
+/*
+ **********************************************************************
+ *     timer.h
+ *     Copyright (C) 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+
+#ifndef _TIMER_H
+#define _TIMER_H
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include "hwaccess.h"
+
+struct emu_timer 
+{
+	struct list_head list;
+	struct tasklet_struct tasklet;
+	u8 state; 
+	u32 count;				/* current number of interrupts */
+	u32 count_max;				/* number of interrupts needed to schedule the bh */
+	u32 delay;                              /* timer delay */
+};
+
+void emu10k1_timer_install(struct emu10k1_card *, struct emu_timer *, u32);
+void emu10k1_timer_uninstall(struct emu10k1_card *, struct emu_timer *);
+void emu10k1_timer_enable(struct emu10k1_card *, struct emu_timer *);
+void emu10k1_timer_disable(struct emu10k1_card *, struct emu_timer *);
+
+#define TIMER_STOPPED 			0xffffffff 
+#define TIMER_STATE_INSTALLED 		0x01
+#define TIMER_STATE_ACTIVE		0x02
+#define TIMER_STATE_UNINSTALLED 	0x04
+
+#endif /* _TIMER_H */
diff -Nru linux/sound/oss/emu10k1/voicemgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.c
--- linux/sound/oss/emu10k1/voicemgr.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,281 @@
+/*
+ **********************************************************************
+ *     voicemgr.c - Voice manager for emu10k1 driver
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#include "voicemgr.h"
+#include "8010.h"
+
+int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice)
+{
+	u8 *voicetable = card->voicetable;
+	int i;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_voice_alloc()\n");
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	if (voice->flags & VOICE_FLAGS_STEREO) {
+		for (i = 0; i < NUM_G; i += 2)
+			if ((voicetable[i] == VOICE_USAGE_FREE) && (voicetable[i + 1] == VOICE_USAGE_FREE)) {
+				voicetable[i] = voice->usage;
+				voicetable[i + 1] = voice->usage;
+				break;
+			}
+	} else {
+		for (i = 0; i < NUM_G; i++)
+			if (voicetable[i] == VOICE_USAGE_FREE) {
+				voicetable[i] = voice->usage;
+				break;
+			}
+	}
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	if (i >= NUM_G)
+		return -1;
+
+	voice->card = card;
+	voice->num = i;
+
+	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
+		DPD(2, " voice allocated -> %d\n", voice->num + i);
+
+		sblive_writeptr_tag(card, voice->num + i, IFATN, 0xffff,
+							DCYSUSV, 0,
+							VTFT, 0x0000ffff,
+							PTRX, 0,
+							TAGLIST_END);
+	}
+
+	return 0;
+}
+
+void emu10k1_voice_free(struct emu_voice *voice)
+{
+	struct emu10k1_card *card = voice->card;
+	int i;
+	unsigned long flags;
+
+	DPF(2, "emu10k1_voice_free()\n");
+
+	if (voice->usage == VOICE_USAGE_FREE)
+		return;
+
+	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
+		DPD(2, " voice released -> %d\n", voice->num + i);
+
+		sblive_writeptr_tag(card, voice->num + i, DCYSUSV, 0, 
+							VTFT, 0x0000ffff,
+							PTRX_PITCHTARGET, 0,
+							CVCF, 0x0000ffff,
+							CPF, 0,
+							TAGLIST_END);
+	}
+
+	voice->usage = VOICE_USAGE_FREE;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	card->voicetable[voice->num] = VOICE_USAGE_FREE;
+
+	if (voice->flags & VOICE_FLAGS_STEREO)
+		card->voicetable[voice->num + 1] = VOICE_USAGE_FREE;
+
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+void emu10k1_voice_playback_setup(struct emu_voice *voice)
+{
+	struct emu10k1_card *card = voice->card;
+	u32 start;
+	int i;
+
+	DPF(2, "emu10k1_voice_playback_setup()\n");
+
+	if (voice->flags & VOICE_FLAGS_STEREO) {
+		/* Set stereo bit */
+		start = 28;
+		sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK);
+		sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK);
+	} else {
+		start = 30;
+		sblive_writeptr(card, CPF, voice->num, 0);
+	}
+
+	if(!(voice->flags & VOICE_FLAGS_16BIT))
+		start *= 2;
+
+	voice->start += start;
+
+	for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) {
+		sblive_writeptr(card, FXRT, voice->num + i, voice->params[i].send_routing << 16);
+
+		/* Stop CA */
+		/* Assumption that PT is already 0 so no harm overwriting */
+		sblive_writeptr(card, PTRX, voice->num + i, (voice->params[i].send_a << 8) | voice->params[i].send_b);
+
+		sblive_writeptr_tag(card, voice->num + i,
+				/* CSL, ST, CA */
+				    DSL, voice->endloop | (voice->params[i].send_d << 24),
+				    PSST, voice->startloop | (voice->params[i].send_c << 24),
+				    CCCA, (voice->start) | CCCA_INTERPROM_0 | ((voice->flags & VOICE_FLAGS_16BIT) ? 0 : CCCA_8BITSELECT),
+				    /* Clear filter delay memory */
+				    Z1, 0,
+				    Z2, 0,
+				    /* Invalidate maps */
+				    MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2),
+				    MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2),
+				/* modulation envelope */
+				    CVCF, 0x0000ffff,
+				    VTFT, 0x0000ffff,
+				    ATKHLDM, 0,
+				    DCYSUSM, 0x007f,
+				    LFOVAL1, 0x8000,
+				    LFOVAL2, 0x8000,
+				    FMMOD, 0,
+				    TREMFRQ, 0,
+				    FM2FRQ2, 0,
+				    ENVVAL, 0x8000,
+				/* volume envelope */
+				    ATKHLDV, 0x7f7f,
+				    ENVVOL, 0x8000,
+				/* filter envelope */
+				    PEFE_FILTERAMOUNT, 0x7f,
+				/* pitch envelope */
+				    PEFE_PITCHAMOUNT, 0, TAGLIST_END);
+
+		voice->params[i].fc_target = 0xffff;
+	}
+}
+
+void emu10k1_voices_start(struct emu_voice *first_voice, unsigned int num_voices, int set)
+{
+	struct emu10k1_card *card = first_voice->card;
+	struct emu_voice *voice;
+	unsigned int voicenum;
+	int j;
+
+	DPF(2, "emu10k1_voices_start()\n");
+
+	for (voicenum = 0; voicenum < num_voices; voicenum++)
+	{
+		voice = first_voice + voicenum;
+
+		if (!set) {
+			u32 cra, ccis, cs, sample;
+			if (voice->flags & VOICE_FLAGS_STEREO) {
+				cra = 64;
+				ccis = 28;
+				cs = 4;
+			} else {
+				cra = 64;
+				ccis = 30;
+				cs = 2;
+			}
+
+			if(voice->flags & VOICE_FLAGS_16BIT) {
+				sample = 0x00000000;
+			} else {
+				sample = 0x80808080;		
+				ccis *= 2;
+			}
+
+			for(j = 0; j < cs; j++)
+	        	        sblive_writeptr(card, CD0 + j, voice->num, sample);
+
+			/* Reset cache */
+			sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, 0);
+			if (voice->flags & VOICE_FLAGS_STEREO)
+				sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num + 1, 0);
+
+			sblive_writeptr(card, CCR_READADDRESS, voice->num, cra);
+
+			if (voice->flags & VOICE_FLAGS_STEREO)
+				sblive_writeptr(card, CCR_READADDRESS, voice->num + 1, cra);
+
+			/* Fill cache */
+			sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, ccis);
+		}
+
+		for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) {
+			sblive_writeptr_tag(card, voice->num + j,
+				    IFATN, (voice->params[j].initial_fc << 8) | voice->params[j].initial_attn,
+				    VTFT, (voice->params[j].volume_target << 16) | voice->params[j].fc_target,
+				    CVCF, (voice->params[j].volume_target << 16) | voice->params[j].fc_target,
+				    DCYSUSV, (voice->params[j].byampl_env_sustain << 8) | voice->params[j].byampl_env_decay,
+				    TAGLIST_END);
+	
+			emu10k1_clear_stop_on_loop(card, voice->num + j);
+		}
+	}
+
+
+        for (voicenum = 0; voicenum < num_voices; voicenum++)
+	{
+		voice = first_voice + voicenum;
+
+		for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) {
+			sblive_writeptr(card, PTRX_PITCHTARGET, voice->num + j, voice->pitch_target);
+
+			if (j == 0)
+				sblive_writeptr(card, CPF_CURRENTPITCH, voice->num, voice->pitch_target);
+
+			sblive_writeptr(card, IP, voice->num + j, voice->initial_pitch);
+		}
+	}
+}
+
+void emu10k1_voices_stop(struct emu_voice *first_voice, int num_voices)
+{
+	struct emu10k1_card *card = first_voice->card;
+	struct emu_voice *voice;
+	unsigned int voice_num;
+	int j;
+
+	DPF(2, "emu10k1_voice_stop()\n");
+
+        for (voice_num = 0; voice_num < num_voices; voice_num++)
+	{
+		voice = first_voice + voice_num;
+
+		for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) {
+			sblive_writeptr_tag(card, voice->num + j,
+						PTRX_PITCHTARGET, 0,
+						CPF_CURRENTPITCH, 0,
+						IFATN, 0xffff,
+						VTFT, 0x0000ffff,
+						CVCF, 0x0000ffff,
+						IP, 0,
+						TAGLIST_END);
+		}
+	}
+}
+
diff -Nru linux/sound/oss/emu10k1/voicemgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.h
--- linux/sound/oss/emu10k1/voicemgr.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,91 @@
+/*
+ **********************************************************************
+ *     sblive_voice.h -- EMU Voice Resource Manager header file
+ *     Copyright 1999, 2000 Creative Labs, Inc.
+ *
+ **********************************************************************
+ *
+ *     Date                 Author          Summary of changes
+ *     ----                 ------          ------------------
+ *     October 20, 1999     Bertrand Lee    base code release
+ *
+ **********************************************************************
+ *
+ *     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., 675 Mass Ave, Cambridge, MA 02139,
+ *     USA.
+ *
+ **********************************************************************
+ */
+
+#ifndef _VOICEMGR_H
+#define _VOICEMGR_H
+
+#include "hwaccess.h"
+
+/* struct emu_voice.usage flags */
+#define VOICE_USAGE_FREE		0x01
+#define VOICE_USAGE_MIDI		0x02
+#define VOICE_USAGE_PLAYBACK		0x04
+
+/* struct emu_voice.flags flags */
+#define VOICE_FLAGS_STEREO		0x02
+#define VOICE_FLAGS_16BIT		0x04
+
+struct voice_param
+{
+	/* FX bus amount send */
+
+	u32 send_routing;
+
+	u32 send_a;
+	u32 send_b;
+	u32 send_c;
+	u32 send_d;
+
+	u32 initial_fc;
+	u32 fc_target;
+
+	u32 initial_attn;
+	u32 volume_target;
+
+	u32 byampl_env_sustain;
+	u32 byampl_env_decay;
+};
+
+
+struct emu_voice
+{
+	struct emu10k1_card *card;
+	u8 usage;		/* Free, MIDI, playback */
+	u8 num;			/* Voice ID */
+	u8 flags;		/* Stereo/mono, 8/16 bit */
+
+        u32 startloop;
+        u32 endloop;
+	u32 start;
+
+	u32 initial_pitch;
+	u32 pitch_target;
+
+	struct voice_param params[2];
+};
+
+int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *);
+void emu10k1_voice_free(struct emu_voice *);
+void emu10k1_voice_playback_setup(struct emu_voice *);
+void emu10k1_voices_start(struct emu_voice *, unsigned int, int);
+void emu10k1_voices_stop(struct emu_voice *, int);
+
+#endif /* _VOICEMGR_H */
diff -Nru linux/sound/oss/es1370.c linux-2.4.19-pre5-mjc/sound/oss/es1370.c
--- linux/sound/oss/es1370.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/es1370.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2784 @@
+/*****************************************************************************/
+
+/*
+ *      es1370.c  --  Ensoniq ES1370/Asahi Kasei AK4531 audio driver.
+ *
+ *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to David C. Niemi
+ *
+ *
+ * Module command line parameters:
+ *   joystick if 1 enables the joystick interface on the card; but it still
+ *            needs a driver for joysticks connected to a standard IBM-PC
+ *	      joyport. It is tested with the joy-analog driver. This 
+ *	      module must be loaded before the joystick driver. Kmod will
+ *	      not ensure that.
+ *   lineout  if 1 the LINE jack is used as an output instead of an input.
+ *            LINE then contains the unmixed dsp output. This can be used
+ *            to make the card a four channel one: use dsp to output two
+ *            channels to LINE and dac to output the other two channels to
+ *            SPKR. Set the mixer to only output synth to SPKR.
+ *   micbias  sets the +5V bias to the mic if using an electretmic.
+ *            
+ *
+ *  Note: sync mode is not yet supported (i.e. running dsp and dac from the same
+ *  clock source)
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/dsp1   additional DAC, like /dev/dsp, but output only,
+ *              only 5512, 11025, 22050 and 44100 samples/s,
+ *              outputs to mixer "SYNTH" setting
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed
+ *  to be done in software. That is what /dev/dac is for. By now (Q2 1998)
+ *  there are several MIDI to PCM (WAV) packages, one of them is timidity.
+ *
+ *  Revision history
+ *    26.03.1998   0.1   Initial release
+ *    31.03.1998   0.2   Fix bug in GETOSPACE
+ *    04.04.1998   0.3   Make it work (again) under 2.0.33
+ *                       Fix mixer write operation not returning the actual
+ *                       settings
+ *    05.04.1998   0.4   First attempt at using the new PCI stuff
+ *    29.04.1998   0.5   Fix hang when ^C is pressed on amp
+ *    07.05.1998   0.6   Don't double lock around stop_*() in *_release()
+ *    10.05.1998   0.7   First stab at a simple midi interface (no bells&whistles)
+ *    14.05.1998   0.8   Don't allow excessive interrupt rates
+ *    08.06.1998   0.9   First release using Alan Cox' soundcore instead of
+ *                       miscdevice
+ *    05.07.1998   0.10  Fixed the driver to correctly maintin OSS style volume
+ *                       settings (not sure if this should be standard)
+ *                       Fixed many references: f_flags should be f_mode
+ *                       -- Gerald Britton <gbritton@mit.edu>
+ *    03.08.1998   0.11  Now mixer behaviour can basically be selected between
+ *                       "OSS documented" and "OSS actual" behaviour
+ *                       Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se
+ *                       On module startup, set DAC2 to 11kSPS instead of 5.5kSPS,
+ *                       as it produces an annoying ssssh in the lower sampling rate
+ *                       Do not include modversions.h
+ *    22.08.1998   0.12  Mixer registers actually have 5 instead of 4 bits
+ *                       pointed out by Itai Nahshon
+ *    31.08.1998   0.13  Fix realplayer problems - dac.count issues
+ *    08.10.1998   0.14  Joystick support fixed
+ *		         -- Oliver Neukum <c188@org.chemie.uni-muenchen.de>
+ *    10.12.1998   0.15  Fix drain_dac trying to wait on not yet initialized DMA
+ *    16.12.1998   0.16  Don't wake up app until there are fragsize bytes to read/write
+ *    06.01.1999   0.17  remove the silly SA_INTERRUPT flag.
+ *                       hopefully killed the egcs section type conflict
+ *    12.03.1999   0.18  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *    22.03.1999   0.19  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    07.04.1999   0.20  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *                       Note: joystick address handling might still be wrong on archs
+ *                       other than i386
+ *    10.05.1999   0.21  Added support for an electret mic for SB PCI64
+ *                       to the Linux kernel sound driver. This mod also straighten
+ *                       out the question marks around the mic impedance setting
+ *                       (micz). From Kim.Berts@fisub.mail.abb.com
+ *    11.05.1999   0.22  Implemented the IMIX call to mute recording monitor.
+ *                       Guenter Geiger <geiger@epy.co.at>
+ *    15.06.1999   0.23  Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.24  Add pci_set_master
+ *    02.08.1999   0.25  Added workaround for the "phantom write" bug first
+ *                       documented by Dave Sharpless from Anchor Games
+ *    03.08.1999   0.26  adapt to Linus' new __setup/__initcall
+ *                       added kernel command line option "es1370=joystick[,lineout[,micbias]]"
+ *                       removed CONFIG_SOUND_ALSA_ES1370_JOYPORT_BOOT kludge
+ *    12.08.1999   0.27  module_init/__setup fixes
+ *    19.08.1999   0.28  SOUND_MIXER_IMIX fixes, reported by Gianluca <gialluca@mail.tiscalinet.it>
+ *    31.08.1999   0.29  add spin_lock_init
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.30  change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    28.10.1999   0.31  More waitqueue races fixed
+ *    08.01.2000   0.32  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *    07.02.2000   0.33  Use pci_alloc_consistent and pci_register_driver
+ *    21.11.2000   0.34  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.35  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
+ *    07.01.2001   0.36  Timeout change in wrcodec as requested by Frank Klemm <pfk@fuchs.offl.uni-jena.de>
+ *    31.01.2001   0.37  Register/Unregister gameport
+ *                       Fix SETTRIGGER non OSS API conformity
+ *
+ * some important things missing in Ensoniq documentation:
+ *
+ * Experimental PCLKDIV results:  play the same waveforms on both DAC1 and DAC2
+ * and vary PCLKDIV to obtain zero beat.
+ *  5512sps:  254
+ * 44100sps:   30
+ * seems to be fs = 1411200/(PCLKDIV+2)
+ *
+ * should find out when curr_sample_ct is cleared and
+ * where exactly the CCB fetches data
+ *
+ * The card uses a 22.5792 MHz crystal.
+ * The LINEIN jack may be converted to an AOUT jack by
+ * setting pin 47 (XCTL0) of the ES1370 to high.
+ * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic
+ * 
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/gameport.h>
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#define DBG(x) {}
+/*#define DBG(x) {x}*/
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_ENSONIQ
+#define PCI_VENDOR_ID_ENSONIQ        0x1274    
+#endif
+
+#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370
+#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000
+#endif
+
+#define ES1370_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370)
+
+#define ES1370_EXTENT             0x40
+#define JOY_EXTENT                8
+
+#define ES1370_REG_CONTROL        0x00
+#define ES1370_REG_STATUS         0x04
+#define ES1370_REG_UART_DATA      0x08
+#define ES1370_REG_UART_STATUS    0x09
+#define ES1370_REG_UART_CONTROL   0x09
+#define ES1370_REG_UART_TEST      0x0a
+#define ES1370_REG_MEMPAGE        0x0c
+#define ES1370_REG_CODEC          0x10
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT    0x24
+#define ES1370_REG_DAC2_SCOUNT    0x28
+#define ES1370_REG_ADC_SCOUNT     0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR    0xc30
+#define ES1370_REG_DAC1_FRAMECNT    0xc34
+#define ES1370_REG_DAC2_FRAMEADR    0xc38
+#define ES1370_REG_DAC2_FRAMECNT    0xc3c
+#define ES1370_REG_ADC_FRAMEADR     0xd30
+#define ES1370_REG_ADC_FRAMECNT     0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
+
+#define ES1370_FMT_U8_MONO     0
+#define ES1370_FMT_U8_STEREO   1
+#define ES1370_FMT_S16_MONO    2
+#define ES1370_FMT_S16_STEREO  3
+#define ES1370_FMT_STEREO      1
+#define ES1370_FMT_S16         2
+#define ES1370_FMT_MASK        3
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
+
+#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
+#define DAC2_DIVTOSR(x) (1411200/((x)+2))
+
+#define CTRL_ADC_STOP   0x80000000  /* 1 = ADC stopped */
+#define CTRL_XCTL1      0x40000000  /* electret mic bias */
+#define CTRL_OPEN       0x20000000  /* no function, can be read and written */
+#define CTRL_PCLKDIV    0x1fff0000  /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL   0x00008000  /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_M_SBB      0x00004000  /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL    0x00003000  /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC   0x00000800  /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM  0x00000400  /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB       0x00000200  /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_XCTL0      0x00000100  /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ       0x00000080  /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN    0x00000040  /* enable DAC1 */
+#define CTRL_DAC2_EN    0x00000020  /* enable DAC2 */
+#define CTRL_ADC_EN     0x00000010  /* enable ADC */
+#define CTRL_UART_EN    0x00000008  /* enable MIDI uart */
+#define CTRL_JYSTK_EN   0x00000004  /* enable Joystick port (presumably at address 0x200) */
+#define CTRL_CDC_EN     0x00000002  /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS   0x00000001  /* 1 = disable PCI SERR signal */
+
+#define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
+#define STAT_CSTAT      0x00000400  /* 1 = codec busy or codec write in progress */
+#define STAT_CBUSY      0x00000200  /* 1 = codec busy */
+#define STAT_CWRIP      0x00000100  /* 1 = codec write in progress */
+#define STAT_VC         0x00000060  /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC      5
+#define STAT_MCCB       0x00000010  /* CCB int pending */
+#define STAT_UART       0x00000008  /* UART int pending */
+#define STAT_DAC1       0x00000004  /* DAC1 int pending */
+#define STAT_DAC2       0x00000002  /* DAC2 int pending */
+#define STAT_ADC        0x00000001  /* ADC int pending */
+
+#define USTAT_RXINT     0x80        /* UART rx int pending */
+#define USTAT_TXINT     0x04        /* UART tx int pending */
+#define USTAT_TXRDY     0x02        /* UART tx ready */
+#define USTAT_RXRDY     0x01        /* UART rx ready */
+
+#define UCTRL_RXINTEN   0x80        /* 1 = enable RX ints */
+#define UCTRL_TXINTEN   0x60        /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20        /* enable TX int */
+#define UCTRL_CNTRL     0x03        /* control field */
+#define UCTRL_CNTRL_SWR 0x03        /* software reset command */
+
+#define SCTRL_P2ENDINC    0x00380000  /*  */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC     0x00070000  /*  */
+#define SCTRL_SH_P2STINC  16
+#define SCTRL_R1LOOPSEL   0x00008000  /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL   0x00004000  /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL   0x00002000  /* 0 = loop mode */
+#define SCTRL_P2PAUSE     0x00001000  /* 1 = pause mode */
+#define SCTRL_P1PAUSE     0x00000800  /* 1 = pause mode */
+#define SCTRL_R1INTEN     0x00000400  /* enable interrupt */
+#define SCTRL_P2INTEN     0x00000200  /* enable interrupt */
+#define SCTRL_P1INTEN     0x00000100  /* enable interrupt */
+#define SCTRL_P1SCTRLD    0x00000080  /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN    0x00000040  /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB       0x00000020  /* 1 = 16bit */
+#define SCTRL_R1SMB       0x00000010  /* 1 = stereo */
+#define SCTRL_R1FMT       0x00000030  /* format mask */
+#define SCTRL_SH_R1FMT    4
+#define SCTRL_P2SEB       0x00000008  /* 1 = 16bit */
+#define SCTRL_P2SMB       0x00000004  /* 1 = stereo */
+#define SCTRL_P2FMT       0x0000000c  /* format mask */
+#define SCTRL_SH_P2FMT    2
+#define SCTRL_P1SEB       0x00000002  /* 1 = 16bit */
+#define SCTRL_P1SMB       0x00000001  /* 1 = stereo */
+#define SCTRL_P1FMT       0x00000003  /* format mask */
+#define SCTRL_SH_P1FMT    0
+
+/* misc stuff */
+
+#define FMODE_DAC         4           /* slight misuse of mode_t */
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+/* --------------------------------------------------------------------- */
+
+struct es1370_state {
+	/* magic */
+	unsigned int magic;
+
+	/* list of es1370 devices */
+	struct list_head devs;
+
+	/* the corresponding pci_dev structure */
+	struct pci_dev *dev;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_dac;
+	int dev_midi;
+	
+	/* hardware resources */
+	unsigned long io; /* long for SPARC */
+	unsigned int irq;
+
+	/* mixer registers; there is no HW readback */
+	struct {
+		unsigned short vol[10];
+		unsigned int recsrc;
+		unsigned int modcnt;
+		unsigned short micpreamp;
+	        unsigned int imix;
+	} mix;
+
+	/* wave stuff */
+	unsigned ctrl;
+	unsigned sctrl;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		dma_addr_t dmaaddr;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned enabled:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac1, dma_dac2, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+
+	struct gameport gameport;
+	struct semaphore sem;
+};
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(devs);
+
+/*
+ * The following buffer is used to point the phantom write channel to,
+ * so that it cannot wreak havoc. The attribute makes sure it doesn't
+ * cross a page boundary and ensures dword alignment for the DMA engine
+ */
+static unsigned char bugbuf[16] __attribute__ ((aligned (16)));
+
+/* --------------------------------------------------------------------- */
+
+static inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data)
+{
+	unsigned long tmo = jiffies + HZ/10, j;
+	
+	do {
+		j = jiffies;
+		if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) {
+			outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC);
+			return;
+		}
+		schedule();
+	} while ((signed)(tmo-j) > 0);
+	printk(KERN_ERR "es1370: write to codec register timeout\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void stop_adc(struct es1370_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_ADC_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static inline void stop_dac1(struct es1370_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC1_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static inline void stop_dac2(struct es1370_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC2_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac1(struct es1370_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0)
+	    && s->dma_dac1.ready) {
+		s->ctrl |= CTRL_DAC1_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN;
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac1.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT);
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac2(struct es1370_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0)
+	    && s->dma_dac2.ready) {
+		s->ctrl |= CTRL_DAC2_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | 
+					 SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN |
+			(((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | 
+			(0 << SCTRL_SH_P2STINC);
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac2.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT);
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct es1370_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
+	    && s->dma_adc.ready) {
+		s->ctrl |= CTRL_ADC_EN;
+		s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_adc.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT);
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+static inline void dealloc_dmabuf(struct es1370_state *s, struct dmabuf *db)
+{
+	struct page *page, *pend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_unreserve(page);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg)
+{
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	struct page *page, *pend;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
+				break;
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_reserve(page);
+	}
+	fmt &= ES1370_FMT_MASK;
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize);
+	outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+	outl(db->dmaaddr, s->io+(reg & 0xff));
+	outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff));
+	db->enabled = 1;
+	db->ready = 1;
+	return 0;
+}
+
+static inline int prog_dmabuf_adc(struct es1370_state *s)
+{
+	stop_adc(s);
+	return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+			   (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR);
+}
+
+static inline int prog_dmabuf_dac2(struct es1370_state *s)
+{
+	stop_dac2(s);
+	return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+			   (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR);
+}
+
+static inline int prog_dmabuf_dac1(struct es1370_state *s)
+{
+	stop_dac1(s);
+	return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
+			   (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR);
+}
+
+static inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg)
+{
+	unsigned hwptr, diff;
+
+	outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+	hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc;
+	diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize;
+	db->hwptr = hwptr;
+	return diff;
+}
+
+static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *)buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(((char *)buf) + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void es1370_update_ptr(struct es1370_state *s)
+{
+	int diff;
+
+	/* update ADC pointer */
+	if (s->ctrl & CTRL_ADC_EN) {
+		diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT);
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+			wake_up(&s->dma_adc.wait);
+		if (!s->dma_adc.mapped) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				s->ctrl &= ~CTRL_ADC_EN;
+				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+				s->dma_adc.error++;
+			}
+		}
+	}
+	/* update DAC1 pointer */
+	if (s->ctrl & CTRL_DAC1_EN) {
+		diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT);
+		s->dma_dac1.total_bytes += diff;
+		if (s->dma_dac1.mapped) {
+			s->dma_dac1.count += diff;
+			if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
+				wake_up(&s->dma_dac1.wait);
+		} else {
+			s->dma_dac1.count -= diff;
+			if (s->dma_dac1.count <= 0) {
+				s->ctrl &= ~CTRL_DAC1_EN;
+				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+				s->dma_dac1.error++;
+			} else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
+				clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, 
+					      s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
+				s->dma_dac1.endcleared = 1;
+			}
+			if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize)
+				wake_up(&s->dma_dac1.wait);
+		}
+	}
+	/* update DAC2 pointer */
+	if (s->ctrl & CTRL_DAC2_EN) {
+		diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT);
+		s->dma_dac2.total_bytes += diff;
+		if (s->dma_dac2.mapped) {
+			s->dma_dac2.count += diff;
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize)
+				wake_up(&s->dma_dac2.wait);
+		} else {
+			s->dma_dac2.count -= diff;
+			if (s->dma_dac2.count <= 0) {
+				s->ctrl &= ~CTRL_DAC2_EN;
+				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+				s->dma_dac2.error++;
+			} else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
+				clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, 
+					      s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
+				s->dma_dac2.endcleared = 1;
+			}
+			if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize)
+				wake_up(&s->dma_dac2.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void es1370_handle_midi(struct es1370_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	if (!(s->ctrl & CTRL_UART_EN))
+		return;
+	wake = 0;
+	while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) {
+		ch = inb(s->io+ES1370_REG_UART_DATA);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+	outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL);
+}
+
+static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct es1370_state *s = (struct es1370_state *)dev_id;
+	unsigned int intsrc, sctl;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inl(s->io+ES1370_REG_STATUS);
+	if (!(intsrc & 0x80000000))
+		return;
+	spin_lock(&s->lock);
+	/* clear audio interrupts first */
+	sctl = s->sctrl;
+	if (intsrc & STAT_ADC)
+		sctl &= ~SCTRL_R1INTEN;
+	if (intsrc & STAT_DAC1)
+		sctl &= ~SCTRL_P1INTEN;
+	if (intsrc & STAT_DAC2)
+		sctl &= ~SCTRL_P2INTEN;
+	outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL);
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	es1370_update_ptr(s);
+	es1370_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != ES1370_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+static const struct {
+	unsigned volidx:4;
+	unsigned left:4;
+	unsigned right:4;
+	unsigned stereo:1;
+	unsigned recmask:13;
+	unsigned avail:1;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },   /* master */
+	[SOUND_MIXER_PCM]    = { 1, 0x2, 0x3, 1, 0x0400, 1 },   /* voice */
+	[SOUND_MIXER_SYNTH]  = { 2, 0x4, 0x5, 1, 0x0060, 1 },   /* FM */
+	[SOUND_MIXER_CD]     = { 3, 0x6, 0x7, 1, 0x0006, 1 },   /* CD */
+	[SOUND_MIXER_LINE]   = { 4, 0x8, 0x9, 1, 0x0018, 1 },   /* Line */
+	[SOUND_MIXER_LINE1]  = { 5, 0xa, 0xb, 1, 0x1800, 1 },   /* AUX */
+	[SOUND_MIXER_LINE2]  = { 6, 0xc, 0x0, 0, 0x0100, 1 },   /* Mono1 */
+	[SOUND_MIXER_LINE3]  = { 7, 0xd, 0x0, 0, 0x0200, 1 },   /* Mono2 */
+	[SOUND_MIXER_MIC]    = { 8, 0xe, 0x0, 0, 0x0001, 1 },   /* Mic */
+	[SOUND_MIXER_OGAIN]  = { 9, 0xf, 0x0, 0, 0x0000, 1 }    /* mono out */
+};
+
+static void set_recsrc(struct es1370_state *s, unsigned int val)
+{
+	unsigned int i, j;
+
+	for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+		if (!(val & (1 << i)))
+			continue;
+		if (!mixtable[i].recmask) {
+			val &= ~(1 << i);
+			continue;
+		}
+		j |= mixtable[i].recmask;
+	}
+	s->mix.recsrc = val;
+	wrcodec(s, 0x12, j & 0xd5);
+	wrcodec(s, 0x13, j & 0xaa);
+	wrcodec(s, 0x14, (j >> 8) & 0x17);
+	wrcodec(s, 0x15, (j >> 8) & 0x0f);
+	i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60;
+	if (!s->mix.imix) {
+		i &= 0xff60;  /* mute record and line monitor */
+	}
+	wrcodec(s, 0x10, i);
+	wrcodec(s, 0x11, i >> 8);
+}
+
+static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg)
+{
+	unsigned long flags;
+	int i, val;
+	unsigned char l, r, rl, rr;
+
+	VALIDATE_STATE(s);
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		/* enable/disable/query mixer preamp */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != -1) {
+			s->mix.micpreamp = !!val;
+			wrcodec(s, 0x19, s->mix.micpreamp);
+		}
+		return put_user(s->mix.micpreamp, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+		/* enable/disable/query use of linein as second lineout */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != -1) {
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->ctrl |= CTRL_XCTL0;
+			else
+				s->ctrl &= ~CTRL_XCTL0;
+			outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->ctrl & CTRL_XCTL0) ? 1 : 0, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE3) {
+		/* enable/disable/query microphone impedance setting */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != -1) {
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->ctrl |= CTRL_XCTL1;
+			else
+				s->ctrl &= ~CTRL_XCTL1;
+			outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->ctrl & CTRL_XCTL1) ? 1 : 0, (int *)arg);
+	}
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "ES1370", sizeof(info.id));
+		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "ES1370", sizeof(info.id));
+		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_SIOC_DIR(cmd) == _SIOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(s->mix.recsrc, (int *)arg);
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			val = SOUND_MASK_IMIX;
+			for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].avail)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].recmask)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].stereo)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(0, (int *)arg);
+		
+		case SOUND_MIXER_IMIX:
+			return put_user(s->mix.imix, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
+                                return -EINVAL;
+			return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg);
+		}
+	}
+        if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+
+	case SOUND_MIXER_IMIX:
+		if (get_user(s->mix.imix, (int *)arg))
+			return -EFAULT;
+		set_recsrc(s, s->mix.recsrc);
+		return 0;
+
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		set_recsrc(s, val);
+		return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
+			return -EINVAL;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		if (mixtable[i].stereo) {
+			r = (val >> 8) & 0xff;
+			if (r > 100)
+				r = 100;
+			if (l < 7) {
+				rl = 0x80;
+				l = 0;
+			} else {
+				rl = 31 - ((l - 7) / 3);
+				l = (31 - rl) * 3 + 7;
+			}
+			if (r < 7) {
+				rr = 0x80;
+				r = 0;
+			} else {
+				rr =  31 - ((r - 7) / 3);
+				r = (31 - rr) * 3 + 7;
+			}
+			wrcodec(s, mixtable[i].right, rr);
+		} else { 
+			if (mixtable[i].left == 15) {
+				if (l < 2) {
+					rr = rl = 0x80;
+					r = l = 0;
+				} else {
+					rl = 7 - ((l - 2) / 14);
+					r = l = (7 - rl) * 14 + 2;
+				}
+			} else {
+				if (l < 7) {
+					rl = 0x80;
+					r = l = 0;
+				} else {
+					rl = 31 - ((l - 7) / 3);
+					r = l = (31 - rl) * 3 + 7;
+				}
+			}
+		}
+		wrcodec(s, mixtable[i].left, rl);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+		s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l;
+#else
+		s->mix.vol[mixtable[i].volidx] = val;
+#endif
+                return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int es1370_open_mixdev(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct list_head *list;
+	struct es1370_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1370_state, devs);
+		if (s->dev_mixer == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	return 0;
+}
+
+static int es1370_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	return 0;
+}
+
+static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations es1370_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		es1370_ioctl_mixdev,
+	open:		es1370_open_mixdev,
+	release:	es1370_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac1(struct es1370_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+	
+	if (s->dma_dac1.mapped || !s->dma_dac1.ready)
+		return 0;
+        add_wait_queue(&s->dma_dac1.wait, &wait);
+        for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac1.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac1.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2
+			/ dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		if (!schedule_timeout(tmo + 1))
+			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
+        }
+        remove_wait_queue(&s->dma_dac1.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+static int drain_dac2(struct es1370_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac2.mapped || !s->dma_dac2.ready)
+		return 0;
+        add_wait_queue(&s->dma_dac2.wait, &wait);
+        for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac2.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac2.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2
+			/ DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		if (!schedule_timeout(tmo + 1))
+			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
+        }
+        remove_wait_queue(&s->dma_dac2.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	down(&s->sem);	
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		goto out;
+        
+	add_wait_queue(&s->dma_adc.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_adc.enabled)
+				start_adc(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				goto out;
+			}
+			up(&s->sem);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				goto out;
+			}
+			down(&s->sem);
+			if (s->dma_adc.mapped)
+			{
+				ret = -ENXIO;
+				goto out;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			goto out;
+		}
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_adc.enabled)
+			start_adc(s);
+	}
+out:
+	up(&s->sem);
+        remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac2.mapped)
+		return -ENXIO;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	down(&s->sem);	
+	if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+		goto out;
+	ret = 0;
+        add_wait_queue(&s->dma_dac2.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac2.count < 0) {
+			s->dma_dac2.count = 0;
+			s->dma_dac2.swptr = s->dma_dac2.hwptr;
+		}
+		swptr = s->dma_dac2.swptr;
+		cnt = s->dma_dac2.dmasize-swptr;
+		if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
+			cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_dac2.enabled)
+				start_dac2(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				goto out;
+			}
+			up(&s->sem);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				goto out;	
+			}
+			down(&s->sem);
+			if (s->dma_dac2.mapped)
+			{
+			ret = -ENXIO;
+			goto out;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			goto out;
+		}
+		swptr = (swptr + cnt) % s->dma_dac2.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac2.swptr = swptr;
+		s->dma_dac2.count += cnt;
+		s->dma_dac2.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_dac2.enabled)
+			start_dac2(s);
+	}
+out:
+	up(&s->sem);
+        remove_wait_queue(&s->dma_dac2.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		if (!s->dma_dac2.ready && prog_dmabuf_dac2(s))
+			return 0;
+		poll_wait(file, &s->dma_dac2.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!s->dma_adc.ready && prog_dmabuf_adc(s))
+			return 0;
+		poll_wait(file, &s->dma_adc.wait, wait);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	es1370_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac2.mapped) {
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1370_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	struct dmabuf *db;
+	int ret = 0;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	down(&s->sem);
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_dac2(s)) != 0) {
+			goto out;
+		}
+		db = &s->dma_dac2;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_adc(s)) != 0) {
+			goto out;
+		}
+		db = &s->dma_adc;
+	} else  {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (vma->vm_pgoff != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	db->mapped = 1;
+out:
+	up(&s->sem);
+	unlock_kernel();
+	return ret;
+}
+
+static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int count;
+	int val, mapped, ret;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			synchronize_irq();
+			s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE))
+				return -EINVAL;
+			if (val < 4000)
+				val = 4000;
+			if (val > 50000)
+				val = 50000;
+			stop_adc(s);
+			stop_dac2(s);
+			s->dma_adc.ready = s->dma_dac2.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV);
+			outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_R1SMB;
+			else
+				s->sctrl &= ~SCTRL_R1SMB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			s->dma_dac2.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_P2SMB;
+			else
+				s->sctrl &= ~SCTRL_P2SMB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+                }
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_R1SMB;
+				else
+					s->sctrl &= ~SCTRL_R1SMB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_P2SMB;
+				else
+					s->sctrl &= ~SCTRL_P2SMB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_R1SEB;
+				else
+					s->sctrl &= ~SCTRL_R1SEB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_P2SEB;
+				else
+					s->sctrl &= ~SCTRL_P2SEB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 
+				AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				s->dma_adc.enabled = 1;
+				start_adc(s);
+			} else {
+				s->dma_adc.enabled = 0;
+				stop_adc(s);
+			}
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+					return ret;
+				s->dma_dac2.enabled = 1;
+				start_dac2(s);
+			} else {
+				s->dma_dac2.enabled = 0;
+				stop_dac2(s);
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+		abinfo.fragsize = s->dma_dac2.fragsize;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac2.dmasize - count;
+                abinfo.fragstotal = s->dma_dac2.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                count = s->dma_dac2.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                cinfo.bytes = s->dma_dac2.total_bytes;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac2.fragshift;
+                cinfo.ptr = s->dma_dac2.hwptr;
+		if (s->dma_dac2.mapped)
+			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac2(s)))
+				return val;
+			return put_user(s->dma_dac2.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac2.ossfragshift = val & 0xffff;
+			s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac2.ossfragshift < 4)
+				s->dma_dac2.ossfragshift = 4;
+			if (s->dma_dac2.ossfragshift > 15)
+				s->dma_dac2.ossfragshift = 15;
+			if (s->dma_dac2.ossmaxfrags < 4)
+				s->dma_dac2.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac2.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ?
+				2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 
+				16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int es1370_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct es1370_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1370_state, devs);
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_READ|FMODE_WRITE)))
+		s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV);
+	if (file->f_mode & FMODE_READ) {
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		s->dma_adc.enabled = 1;
+		s->sctrl &= ~SCTRL_R1FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT;
+		else
+			s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0;
+		s->dma_dac2.enabled = 1;
+		s->sctrl &= ~SCTRL_P2FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT;
+		else
+			s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT;
+	}
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	init_MUTEX(&s->sem);
+	return 0;
+}
+
+static int es1370_release(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac2(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac2(s);
+		synchronize_irq();
+		dealloc_dmabuf(s, &s->dma_dac2);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(s, &s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations es1370_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		es1370_read,
+	write:		es1370_write,
+	poll:		es1370_poll,
+	ioctl:		es1370_ioctl,
+	mmap:		es1370_mmap,
+	open:		es1370_open,
+	release:	es1370_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac1.mapped)
+		return -ENXIO;
+	if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+        add_wait_queue(&s->dma_dac1.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac1.count < 0) {
+			s->dma_dac1.count = 0;
+			s->dma_dac1.swptr = s->dma_dac1.hwptr;
+		}
+		swptr = s->dma_dac1.swptr;
+		cnt = s->dma_dac1.dmasize-swptr;
+		if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
+			cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_dac1.enabled)
+				start_dac1(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		swptr = (swptr + cnt) % s->dma_dac1.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac1.swptr = swptr;
+		s->dma_dac1.count += cnt;
+		s->dma_dac1.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_dac1.enabled)
+			start_dac1(s);
+	}
+        remove_wait_queue(&s->dma_dac1.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (!s->dma_dac1.ready && prog_dmabuf_dac1(s))
+		return 0;
+	poll_wait(file, &s->dma_dac1.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	es1370_update_ptr(s);
+	if (s->dma_dac1.mapped) {
+		if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	} else {
+		if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (!(vma->vm_flags & VM_WRITE))
+		return -EINVAL;
+	lock_kernel();
+	if ((ret = prog_dmabuf_dac1(s)) != 0)
+		goto out;
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << s->dma_dac1.buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot))
+		goto out;
+	s->dma_dac1.mapped = 1;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int count;
+	unsigned ctrl;
+	int val, ret;
+
+	VALIDATE_STATE(s);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/);
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		stop_dac1(s);
+		synchronize_irq();
+		s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0;
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			for (ctrl = 0; ctrl <= 2; ctrl++)
+				if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2)
+					break;
+			spin_lock_irqsave(&s->lock, flags);
+			s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL);
+			outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		stop_dac1(s);
+		s->dma_dac1.ready = 0;
+		spin_lock_irqsave(&s->lock, flags);
+		if (val)
+			s->sctrl |= SCTRL_P1SMB;
+		else
+			s->sctrl &= ~SCTRL_P1SMB;
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			if (s->dma_dac1.mapped)
+				return -EINVAL;
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val >= 2)
+				s->sctrl |= SCTRL_P1SMB;
+			else
+				s->sctrl &= ~SCTRL_P1SMB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg);
+		
+        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val == AFMT_S16_LE)
+				s->sctrl |= SCTRL_P1SEB;
+			else
+				s->sctrl &= ~SCTRL_P1SEB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+        case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg);
+						
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val & PCM_ENABLE_OUTPUT) {
+			if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+				return ret;
+			s->dma_dac1.enabled = 1;
+			start_dac1(s);
+		} else {
+			s->dma_dac1.enabled = 0;
+			stop_dac1(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+		abinfo.fragsize = s->dma_dac1.fragsize;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac1.dmasize - count;
+                abinfo.fragstotal = s->dma_dac1.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                count = s->dma_dac1.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                cinfo.bytes = s->dma_dac1.total_bytes;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac1.fragshift;
+                cinfo.ptr = s->dma_dac1.hwptr;
+		if (s->dma_dac1.mapped)
+			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if ((val = prog_dmabuf_dac1(s)))
+			return val;
+                return put_user(s->dma_dac1.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		s->dma_dac1.ossfragshift = val & 0xffff;
+		s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
+		if (s->dma_dac1.ossfragshift < 4)
+			s->dma_dac1.ossfragshift = 4;
+		if (s->dma_dac1.ossfragshift > 15)
+			s->dma_dac1.ossfragshift = 15;
+		if (s->dma_dac1.ossmaxfrags < 4)
+			s->dma_dac1.ossmaxfrags = 4;
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if (s->dma_dac1.subdivision)
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		s->dma_dac1.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int es1370_open_dac(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct es1370_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1370_state, devs);
+		if (!((s->dev_dac ^ minor) & ~0xf))
+			break;
+	}
+       	VALIDATE_STATE(s);
+       	/* we allow opening with O_RDWR, most programs do it although they will only write */
+#if 0
+	if (file->f_mode & FMODE_READ)
+		return -EPERM;
+#endif
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EINVAL;
+       	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DAC) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0;
+	s->dma_dac1.enabled = 1;
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL);
+      	s->sctrl &= ~SCTRL_P1FMT;
+	if ((minor & 0xf) == SND_DEV_DSP16)
+		s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT;
+	else
+		s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT;
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= FMODE_DAC;
+	up(&s->open_sem);
+	return 0;
+}
+
+static int es1370_release_dac(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	drain_dac1(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	stop_dac1(s);
+	dealloc_dmabuf(s, &s->dma_dac1);
+	s->open_mode &= ~FMODE_DAC;
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations es1370_dac_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	write:		es1370_write_dac,
+	poll:		es1370_poll_dac,
+	ioctl:		es1370_ioctl_dac,
+	mmap:		es1370_mmap_dac,
+	open:		es1370_open_dac,
+	release:	es1370_release_dac,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+        add_wait_queue(&s->midi.iwait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		break;
+	}
+	__set_current_state(TASK_RUNNING);
+        remove_wait_queue(&s->midi.iwait, &wait);
+	return ret;
+}
+
+static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			es1370_handle_midi(s);
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	__set_current_state(TASK_RUNNING);
+        remove_wait_queue(&s->midi.owait, &wait);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1370_midi_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct es1370_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1370_state, devs);
+		if (s->dev_midi == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL);
+		outb(0, s->io+ES1370_REG_UART_CONTROL);
+		outb(0, s->io+ES1370_REG_UART_TEST);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	s->ctrl |= CTRL_UART_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	es1370_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int es1370_midi_release(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE) {
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				set_current_state(TASK_RUNNING);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");)
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		set_current_state(TASK_RUNNING);
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->ctrl &= ~CTRL_UART_EN;
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations es1370_midi_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		es1370_midi_read,
+	write:		es1370_midi_write,
+	poll:		es1370_midi_poll,
+	open:		es1370_midi_open,
+	release:	es1370_midi_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices; only used for command line params */
+#define NR_DEVICE 5
+
+static int joystick[NR_DEVICE] = { 0, };
+static int lineout[NR_DEVICE] = { 0, };
+static int micbias[NR_DEVICE] = { 0, };
+
+static unsigned int devindex = 0;
+
+MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)");
+MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out");
+MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("ES1370 AudioPCI Driver");
+MODULE_LICENSE("GPL");
+
+
+/* --------------------------------------------------------------------- */
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE2, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE3, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_OGAIN, 0x4040 }
+};
+
+static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+{
+	struct es1370_state *s;
+	mm_segment_t fs;
+	int i, val, ret;
+
+	if ((ret=pci_enable_device(pcidev)))
+		return ret;
+
+	if ( !(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) ||
+	     !pci_resource_start(pcidev, 0)
+	)
+		return -ENODEV;
+	if (pcidev->irq == 0) 
+		return -ENODEV;
+	i = pci_set_dma_mask(pcidev, 0xffffffff);
+	if (i) {
+		printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n");
+		return i;
+	}
+	if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) {
+		printk(KERN_WARNING "es1370: out of memory\n");
+		return -ENOMEM;
+	}
+	memset(s, 0, sizeof(struct es1370_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac1.wait);
+	init_waitqueue_head(&s->dma_dac2.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+	s->magic = ES1370_MAGIC;
+	s->dev = pcidev;
+	s->io = pci_resource_start(pcidev, 0);
+	s->irq = pcidev->irq;
+	if (!request_region(s->io, ES1370_EXTENT, "es1370")) {
+		printk(KERN_ERR "es1370: io ports %#lx-%#lx in use\n", s->io, s->io+ES1370_EXTENT-1);
+		ret = -EBUSY;
+		goto err_region;
+	}
+	if ((ret=request_irq(s->irq, es1370_interrupt, SA_SHIRQ, "es1370",s))) {
+		printk(KERN_ERR "es1370: irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+
+	/* initialize codec registers */
+	/* note: setting CTRL_SERR_DIS is reported to break
+	 * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */
+	s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL);
+	s->gameport.io = 0;
+	if (joystick[devindex]) {
+		if (!request_region(0x200, JOY_EXTENT, "es1370"))
+			printk(KERN_ERR "es1370: joystick io port 0x200 in use\n");
+		else {
+			s->ctrl |= CTRL_JYSTK_EN;
+			s->gameport.io = 0x200;
+		}
+	}
+	if (lineout[devindex])
+		s->ctrl |= CTRL_XCTL0;
+	if (micbias[devindex])
+		s->ctrl |= CTRL_XCTL1;
+	s->sctrl = 0;
+	printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n"
+	       KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n",
+	       s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off",
+	       (s->ctrl & CTRL_XCTL0) ? "out" : "in",
+		       (s->ctrl & CTRL_XCTL1) ? "1" : "0");
+	/* register devices */
+	if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) {
+		ret = s->dev_audio;
+		goto err_dev1;
+	}
+	if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) {
+		ret = s->dev_mixer;
+		goto err_dev2;
+	}
+	if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) {
+		ret = s->dev_dac;
+		goto err_dev3;
+	}
+	if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) {
+		ret = s->dev_midi;
+		goto err_dev4;
+	}
+	/* initialize the chips */
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	/* point phantom write channel to "bugbuf" */
+	outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+	outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff));
+	outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff));
+	pci_set_master(pcidev);  /* enable bus mastering */
+	wrcodec(s, 0x16, 3); /* no RST, PD */
+	wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!!  */
+	wrcodec(s, 0x18, 0); /* recording source is mixer */
+	wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */
+	s->mix.imix = 1;
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD;
+	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+	for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+	}
+	set_fs(fs);
+	/* register gameport */
+	gameport_register_port(&s->gameport);
+
+	/* store it in the driver field */
+	pci_set_drvdata(pcidev, s);
+	/* put it into driver list */
+	list_add_tail(&s->devs, &devs);
+	/* increment devindex */
+	if (devindex < NR_DEVICE-1)
+		devindex++;
+	return 0;
+
+ err_dev4:
+	unregister_sound_dsp(s->dev_dac);
+ err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR "es1370: cannot register misc device\n");
+	free_irq(s->irq, s);
+	if (s->gameport.io)
+		release_region(s->gameport.io, JOY_EXTENT);
+ err_irq:
+	release_region(s->io, ES1370_EXTENT);
+ err_region:
+	kfree(s);
+	return ret;
+}
+
+static void __devinit es1370_remove(struct pci_dev *dev)
+{
+	struct es1370_state *s = pci_get_drvdata(dev);
+
+	if (!s)
+		return;
+	list_del(&s->devs);
+	outl(CTRL_SERR_DIS | (1 << CTRL_SH_WTSRSEL), s->io+ES1370_REG_CONTROL); /* switch everything off */
+	outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */
+	synchronize_irq();
+	free_irq(s->irq, s);
+	if (s->gameport.io) {
+		gameport_unregister_port(&s->gameport);
+		release_region(s->gameport.io, JOY_EXTENT);
+	}
+	release_region(s->io, ES1370_EXTENT);
+	unregister_sound_dsp(s->dev_audio);
+	unregister_sound_mixer(s->dev_mixer);
+	unregister_sound_dsp(s->dev_dac);
+	unregister_sound_midi(s->dev_midi);
+	kfree(s);
+	pci_set_drvdata(dev, NULL);
+}
+
+static struct pci_device_id id_table[] __devinitdata = {
+	{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver es1370_driver = {
+	name: "es1370",
+	id_table: id_table,
+	probe: es1370_probe,
+	remove: es1370_remove
+};
+
+static int __init init_es1370(void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "es1370: version v0.37 time " __TIME__ " " __DATE__ "\n");
+	return pci_module_init(&es1370_driver);
+}
+
+static void __exit cleanup_es1370(void)
+{
+	printk(KERN_INFO "es1370: unloading\n");
+	pci_unregister_driver(&es1370_driver);
+}
+
+module_init(init_es1370);
+module_exit(cleanup_es1370);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/* format is: es1370=[joystick[,lineout[,micbias]]] */
+
+static int __init es1370_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= NR_DEVICE)
+		return 0;
+
+	(void)
+	(   (get_option(&str,&joystick[nr_dev]) == 2)
+	 && (get_option(&str,&lineout [nr_dev]) == 2)
+	 &&  get_option(&str,&micbias [nr_dev])
+	);
+
+	nr_dev++;
+	return 1;
+}
+
+__setup("es1370=", es1370_setup);
+
+#endif /* MODULE */
diff -Nru linux/sound/oss/es1371.c linux-2.4.19-pre5-mjc/sound/oss/es1371.c
--- linux/sound/oss/es1371.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/es1371.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3084 @@
+/*****************************************************************************/
+
+/*
+ *      es1371.c  --  Creative Ensoniq ES1371.
+ *
+ *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to Ensoniq
+ *
+ *
+ * Module command line parameters:
+ *   joystick must be set to the base I/O-Port to be used for
+ *   the gameport. Legal values are 0x200, 0x208, 0x210 and 0x218.         
+ *   The gameport is mirrored eight times.
+ *        
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/dsp1   additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed
+ *  to be done in software. That is what /dev/dac is for. By now (Q2 1998)
+ *  there are several MIDI to PCM (WAV) packages, one of them is timidity.
+ *
+ *  Revision history
+ *    04.06.1998   0.1   Initial release
+ *                       Mixer stuff should be overhauled; especially optional AC97 mixer bits
+ *                       should be detected. This results in strange behaviour of some mixer
+ *                       settings, like master volume and mic.
+ *    08.06.1998   0.2   First release using Alan Cox' soundcore instead of miscdevice
+ *    03.08.1998   0.3   Do not include modversions.h
+ *                       Now mixer behaviour can basically be selected between
+ *                       "OSS documented" and "OSS actual" behaviour
+ *    31.08.1998   0.4   Fix realplayer problems - dac.count issues
+ *    27.10.1998   0.5   Fix joystick support
+ *                       -- Oliver Neukum (c188@org.chemie.uni-muenchen.de)
+ *    10.12.1998   0.6   Fix drain_dac trying to wait on not yet initialized DMA
+ *    23.12.1998   0.7   Fix a few f_file & FMODE_ bugs
+ *                       Don't wake up app until there are fragsize bytes to read/write
+ *    06.01.1999   0.8   remove the silly SA_INTERRUPT flag.
+ *                       hopefully killed the egcs section type conflict
+ *    12.03.1999   0.9   cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *    22.03.1999   0.10  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    07.04.1999   0.11  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *                       Another Alpha fix (wait_src_ready in init routine)
+ *                       reported by "Ivan N. Kokshaysky" <ink@jurassic.park.msu.ru>
+ *                       Note: joystick address handling might still be wrong on archs
+ *                       other than i386
+ *    15.06.1999   0.12  Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.13  Add pci_set_master
+ *    03.08.1999   0.14  adapt to Linus' new __setup/__initcall
+ *                       added kernel command line option "es1371=joystickaddr"
+ *                       removed CONFIG_SOUND_ALSA_ES1371_JOYPORT_BOOT kludge
+ *    10.08.1999   0.15  (Re)added S/PDIF module option for cards revision >= 4.
+ *                       Initial version by Dave Platt <dplatt@snulbug.mtview.ca.us>.
+ *                       module_init/__setup fixes
+ *    08.16.1999   0.16  Joe Cotellese <joec@ensoniq.com>
+ *                       Added detection for ES1371 revision ID so that we can
+ *                       detect the ES1373 and later parts.
+ *                       added AC97 #defines for readability
+ *                       added a /proc file system for dumping hardware state
+ *                       updated SRC and CODEC w/r functions to accomodate bugs
+ *                       in some versions of the ES137x chips.
+ *    31.08.1999   0.17  add spin_lock_init
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.18  change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    21.10.1999   0.19  Round sampling rates, requested by
+ *                       Kasamatsu Kenichi <t29w0267@ip.media.kyoto-u.ac.jp>
+ *    27.10.1999   0.20  Added SigmaTel 3D enhancement string
+ *                       Codec ID printing changes
+ *    28.10.1999   0.21  More waitqueue races fixed
+ *                       Joe Cotellese <joec@ensoniq.com>
+ *                       Changed PCI detection routine so we can more easily
+ *                       detect ES137x chip and derivatives.
+ *    05.01.2000   0.22  Should now work with rev7 boards; patch by
+ *                       Eric Lemar, elemar@cs.washington.edu
+ *    08.01.2000   0.23  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *    07.02.2000   0.24  Use pci_alloc_consistent and pci_register_driver
+ *    07.02.2000   0.25  Use ac97_codec
+ *    01.03.2000   0.26  SPDIF patch by Mikael Bouillot <mikael.bouillot@bigfoot.com>
+ *                       Use pci_module_init
+ *    21.11.2000   0.27  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.28  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
+ *    05.01.2001   0.29  Hopefully updates will not be required anymore when Creative bumps
+ *                       the CT5880 revision.
+ *                       suggested by Stephan Mller <smueller@chronox.de>
+ *    31.01.2001   0.30  Register/Unregister gameport
+ *                       Fix SETTRIGGER non OSS API conformity
+ *    14.07.2001   0.31  Add list of laptops needing amplifier control
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wrapper.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/gameport.h>
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#define ES1371_DEBUG
+#define DBG(x) {}
+/*#define DBG(x) {x}*/
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_ENSONIQ
+#define PCI_VENDOR_ID_ENSONIQ        0x1274    
+#endif
+
+#ifndef PCI_VENDOR_ID_ECTIVA
+#define PCI_VENDOR_ID_ECTIVA         0x1102
+#endif
+
+#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+#endif
+
+#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880
+#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880
+#endif
+
+#ifndef PCI_DEVICE_ID_ECTIVA_EV1938
+#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938
+#endif
+
+/* ES1371 chip ID */
+/* This is a little confusing because all ES1371 compatible chips have the
+   same DEVICE_ID, the only thing differentiating them is the REV_ID field.
+   This is only significant if you want to enable features on the later parts.
+   Yes, I know it's stupid and why didn't we use the sub IDs?
+*/
+#define ES1371REV_ES1373_A  0x04
+#define ES1371REV_ES1373_B  0x06
+#define ES1371REV_CT5880_A  0x07
+#define CT5880REV_CT5880_C  0x02
+#define CT5880REV_CT5880_D  0x03
+#define ES1371REV_ES1371_B  0x09
+#define EV1938REV_EV1938_A  0x00
+#define ES1371REV_ES1373_8  0x08
+
+#define ES1371_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371)
+
+#define ES1371_EXTENT             0x40
+#define JOY_EXTENT                8
+
+#define ES1371_REG_CONTROL        0x00
+#define ES1371_REG_STATUS         0x04 /* on the 5880 it is control/status */
+#define ES1371_REG_UART_DATA      0x08
+#define ES1371_REG_UART_STATUS    0x09
+#define ES1371_REG_UART_CONTROL   0x09
+#define ES1371_REG_UART_TEST      0x0a
+#define ES1371_REG_MEMPAGE        0x0c
+#define ES1371_REG_SRCONV         0x10
+#define ES1371_REG_CODEC          0x14
+#define ES1371_REG_LEGACY         0x18
+#define ES1371_REG_SERIAL_CONTROL 0x20
+#define ES1371_REG_DAC1_SCOUNT    0x24
+#define ES1371_REG_DAC2_SCOUNT    0x28
+#define ES1371_REG_ADC_SCOUNT     0x2c
+
+#define ES1371_REG_DAC1_FRAMEADR  0xc30
+#define ES1371_REG_DAC1_FRAMECNT  0xc34
+#define ES1371_REG_DAC2_FRAMEADR  0xc38
+#define ES1371_REG_DAC2_FRAMECNT  0xc3c
+#define ES1371_REG_ADC_FRAMEADR   0xd30
+#define ES1371_REG_ADC_FRAMECNT   0xd34
+
+#define ES1371_FMT_U8_MONO     0
+#define ES1371_FMT_U8_STEREO   1
+#define ES1371_FMT_S16_MONO    2
+#define ES1371_FMT_S16_STEREO  3
+#define ES1371_FMT_STEREO      1
+#define ES1371_FMT_S16         2
+#define ES1371_FMT_MASK        3
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define CTRL_RECEN_B    0x08000000  /* 1 = don't mix analog in to digital out */
+#define CTRL_SPDIFEN_B  0x04000000
+#define CTRL_JOY_SHIFT  24
+#define CTRL_JOY_MASK   3
+#define CTRL_JOY_200    0x00000000  /* joystick base address */
+#define CTRL_JOY_208    0x01000000
+#define CTRL_JOY_210    0x02000000
+#define CTRL_JOY_218    0x03000000
+#define CTRL_GPIO_IN0   0x00100000  /* general purpose inputs/outputs */
+#define CTRL_GPIO_IN1   0x00200000
+#define CTRL_GPIO_IN2   0x00400000
+#define CTRL_GPIO_IN3   0x00800000
+#define CTRL_GPIO_OUT0  0x00010000
+#define CTRL_GPIO_OUT1  0x00020000
+#define CTRL_GPIO_OUT2  0x00040000
+#define CTRL_GPIO_OUT3  0x00080000
+#define CTRL_MSFMTSEL   0x00008000  /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_SYNCRES    0x00004000  /* AC97 warm reset */
+#define CTRL_ADCSTOP    0x00002000  /* stop ADC transfers */
+#define CTRL_PWR_INTRM  0x00001000  /* 1 = power level ints enabled */
+#define CTRL_M_CB       0x00000800  /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_CCB_INTRM  0x00000400  /* 1 = CCB "voice" ints enabled */
+#define CTRL_PDLEV0     0x00000000  /* power down level */
+#define CTRL_PDLEV1     0x00000100
+#define CTRL_PDLEV2     0x00000200
+#define CTRL_PDLEV3     0x00000300
+#define CTRL_BREQ       0x00000080  /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN    0x00000040  /* enable DAC1 */
+#define CTRL_DAC2_EN    0x00000020  /* enable DAC2 */
+#define CTRL_ADC_EN     0x00000010  /* enable ADC */
+#define CTRL_UART_EN    0x00000008  /* enable MIDI uart */
+#define CTRL_JYSTK_EN   0x00000004  /* enable Joystick port */
+#define CTRL_XTALCLKDIS 0x00000002  /* 1 = disable crystal clock input */
+#define CTRL_PCICLKDIS  0x00000001  /* 1 = disable PCI clock distribution */
+
+
+#define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
+#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */
+#define STAT_EN_SPDIF   0x00040000  /* enable S/PDIF circuitry */
+#define STAT_TS_SPDIF   0x00020000  /* test S/PDIF circuitry */
+#define STAT_TESTMODE   0x00010000  /* test ASIC */
+#define STAT_SYNC_ERR   0x00000100  /* 1 = codec sync error */
+#define STAT_VC         0x000000c0  /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC      6
+#define STAT_MPWR       0x00000020  /* power level interrupt */
+#define STAT_MCCB       0x00000010  /* CCB int pending */
+#define STAT_UART       0x00000008  /* UART int pending */
+#define STAT_DAC1       0x00000004  /* DAC1 int pending */
+#define STAT_DAC2       0x00000002  /* DAC2 int pending */
+#define STAT_ADC        0x00000001  /* ADC int pending */
+
+#define USTAT_RXINT     0x80        /* UART rx int pending */
+#define USTAT_TXINT     0x04        /* UART tx int pending */
+#define USTAT_TXRDY     0x02        /* UART tx ready */
+#define USTAT_RXRDY     0x01        /* UART rx ready */
+
+#define UCTRL_RXINTEN   0x80        /* 1 = enable RX ints */
+#define UCTRL_TXINTEN   0x60        /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20        /* enable TX int */
+#define UCTRL_CNTRL     0x03        /* control field */
+#define UCTRL_CNTRL_SWR 0x03        /* software reset command */
+
+/* sample rate converter */
+#define SRC_OKSTATE        1
+
+#define SRC_RAMADDR_MASK   0xfe000000
+#define SRC_RAMADDR_SHIFT  25
+#define SRC_DAC1FREEZE     (1UL << 21)
+#define SRC_DAC2FREEZE      (1UL << 20)
+#define SRC_ADCFREEZE      (1UL << 19)
+
+
+#define SRC_WE             0x01000000  /* read/write control for SRC RAM */
+#define SRC_BUSY           0x00800000  /* SRC busy */
+#define SRC_DIS            0x00400000  /* 1 = disable SRC */
+#define SRC_DDAC1          0x00200000  /* 1 = disable accum update for DAC1 */
+#define SRC_DDAC2          0x00100000  /* 1 = disable accum update for DAC2 */
+#define SRC_DADC           0x00080000  /* 1 = disable accum update for ADC2 */
+#define SRC_CTLMASK        0x00780000
+#define SRC_RAMDATA_MASK   0x0000ffff
+#define SRC_RAMDATA_SHIFT  0
+
+#define SRCREG_ADC      0x78
+#define SRCREG_DAC1     0x70
+#define SRCREG_DAC2     0x74
+#define SRCREG_VOL_ADC  0x6c
+#define SRCREG_VOL_DAC1 0x7c
+#define SRCREG_VOL_DAC2 0x7e
+
+#define SRCREG_TRUNC_N     0x00
+#define SRCREG_INT_REGS    0x01
+#define SRCREG_ACCUM_FRAC  0x02
+#define SRCREG_VFREQ_FRAC  0x03
+
+#define CODEC_PIRD        0x00800000  /* 0 = write AC97 register */
+#define CODEC_PIADD_MASK  0x007f0000
+#define CODEC_PIADD_SHIFT 16
+#define CODEC_PIDAT_MASK  0x0000ffff
+#define CODEC_PIDAT_SHIFT 0
+
+#define CODEC_RDY         0x80000000  /* AC97 read data valid */
+#define CODEC_WIP         0x40000000  /* AC97 write in progress */
+#define CODEC_PORD        0x00800000  /* 0 = write AC97 register */
+#define CODEC_POADD_MASK  0x007f0000
+#define CODEC_POADD_SHIFT 16
+#define CODEC_PODAT_MASK  0x0000ffff
+#define CODEC_PODAT_SHIFT 0
+
+
+#define LEGACY_JFAST      0x80000000  /* fast joystick timing */
+#define LEGACY_FIRQ       0x01000000  /* force IRQ */
+
+#define SCTRL_DACTEST     0x00400000  /* 1 = DAC test, test vector generation purposes */
+#define SCTRL_P2ENDINC    0x00380000  /*  */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC     0x00070000  /*  */
+#define SCTRL_SH_P2STINC  16
+#define SCTRL_R1LOOPSEL   0x00008000  /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL   0x00004000  /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL   0x00002000  /* 0 = loop mode */
+#define SCTRL_P2PAUSE     0x00001000  /* 1 = pause mode */
+#define SCTRL_P1PAUSE     0x00000800  /* 1 = pause mode */
+#define SCTRL_R1INTEN     0x00000400  /* enable interrupt */
+#define SCTRL_P2INTEN     0x00000200  /* enable interrupt */
+#define SCTRL_P1INTEN     0x00000100  /* enable interrupt */
+#define SCTRL_P1SCTRLD    0x00000080  /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN    0x00000040  /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB       0x00000020  /* 1 = 16bit */
+#define SCTRL_R1SMB       0x00000010  /* 1 = stereo */
+#define SCTRL_R1FMT       0x00000030  /* format mask */
+#define SCTRL_SH_R1FMT    4
+#define SCTRL_P2SEB       0x00000008  /* 1 = 16bit */
+#define SCTRL_P2SMB       0x00000004  /* 1 = stereo */
+#define SCTRL_P2FMT       0x0000000c  /* format mask */
+#define SCTRL_SH_P2FMT    2
+#define SCTRL_P1SEB       0x00000002  /* 1 = 16bit */
+#define SCTRL_P1SMB       0x00000001  /* 1 = stereo */
+#define SCTRL_P1FMT       0x00000003  /* format mask */
+#define SCTRL_SH_P1FMT    0
+
+
+/* misc stuff */
+#define POLL_COUNT   0x1000
+#define FMODE_DAC         4           /* slight misuse of mode_t */
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define ES1371_MODULE_NAME "es1371"
+#define PFX ES1371_MODULE_NAME ": "
+
+/* --------------------------------------------------------------------- */
+
+struct es1371_state {
+	/* magic */
+	unsigned int magic;
+
+	/* list of es1371 devices */
+	struct list_head devs;
+
+	/* the corresponding pci_dev structure */
+	struct pci_dev *dev;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_dac;
+	int dev_midi;
+	
+	/* hardware resources */
+	unsigned long io; /* long for SPARC */
+	unsigned int irq;
+
+	/* PCI ID's */
+	u16 vendor;
+	u16 device;
+        u8 rev; /* the chip revision */
+
+	/* options */
+	int spdif_volume; /* S/PDIF output is enabled if != -1 */
+
+#ifdef ES1371_DEBUG
+        /* debug /proc entry */
+	struct proc_dir_entry *ps;
+#endif /* ES1371_DEBUG */
+
+	struct ac97_codec codec;
+
+	/* wave stuff */
+	unsigned ctrl;
+	unsigned sctrl;
+	unsigned dac1rate, dac2rate, adcrate;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		dma_addr_t dmaaddr;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned enabled:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac1, dma_dac2, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+
+	struct gameport gameport;
+	struct semaphore sem;
+};
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(devs);
+
+/* --------------------------------------------------------------------- */
+
+static inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned wait_src_ready(struct es1371_state *s)
+{
+	unsigned int t, r;
+
+	for (t = 0; t < POLL_COUNT; t++) {
+		if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY))
+			return r;
+		udelay(1);
+	}
+	printk(KERN_DEBUG PFX "sample rate converter timeout r = 0x%08x\n", r);
+	return r;
+}
+
+static unsigned src_read(struct es1371_state *s, unsigned reg)
+{
+        unsigned int temp,i,orig;
+
+        /* wait for ready */
+        temp = wait_src_ready (s);
+
+        /* we can only access the SRC at certain times, make sure
+           we're allowed to before we read */
+           
+        orig = temp;
+        /* expose the SRC state bits */
+        outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL,
+               s->io + ES1371_REG_SRCONV);
+
+        /* now, wait for busy and the correct time to read */
+        temp = wait_src_ready (s);
+
+        if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){
+                /* wait for the right state */
+                for (i=0; i<POLL_COUNT; i++){
+                        temp = inl (s->io + ES1371_REG_SRCONV);
+                        if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 ))
+                                break;
+                }
+        }
+
+        /* hide the state bits */
+        outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV);
+        return temp;
+                        
+                
+}
+
+static void src_write(struct es1371_state *s, unsigned reg, unsigned data)
+{
+      
+	unsigned int r;
+
+	r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC);
+	r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK;
+	r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK;
+	outl(r | SRC_WE, s->io + ES1371_REG_SRCONV);
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/* most of the following here is black magic */
+static void set_adc_rate(struct es1371_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned int n, truncm, freq;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+	n = rate / 3000;
+	if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+		n--;
+	truncm = (21 * n - 1) | 1;
+        freq = ((48000UL << 15) / rate) * n;
+	s->adcrate = (48000UL << 15) / (freq / n);
+	spin_lock_irqsave(&s->lock, flags);
+	if (rate >= 24000) {
+		if (truncm > 239)
+			truncm = 239;
+		src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, 
+			  (((239 - truncm) >> 1) << 9) | (n << 4));
+	} else {
+		if (truncm > 119)
+			truncm = 119;
+		src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, 
+			  0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+	}		
+	src_write(s, SRCREG_ADC+SRCREG_INT_REGS, 
+		  (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) |
+		  ((freq >> 5) & 0xfc00));
+	src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff);
+	src_write(s, SRCREG_VOL_ADC, n << 8);
+	src_write(s, SRCREG_VOL_ADC+1, n << 8);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void set_dac1_rate(struct es1371_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned int freq, r;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+        freq = ((rate << 15) + 1500) / 3000;
+	s->dac1rate = (freq * 3000 + 16384) >> 15;
+	spin_lock_irqsave(&s->lock, flags);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1;
+	outl(r, s->io + ES1371_REG_SRCONV);
+	src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 
+		  (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) |
+		  ((freq >> 5) & 0xfc00));
+	src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC));
+	outl(r, s->io + ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void set_dac2_rate(struct es1371_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned int freq, r;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+        freq = ((rate << 15) + 1500) / 3000;
+	s->dac2rate = (freq * 3000 + 16384) >> 15;
+	spin_lock_irqsave(&s->lock, flags);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2;
+	outl(r, s->io + ES1371_REG_SRCONV);
+	src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 
+		  (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) |
+		  ((freq >> 5) & 0xfc00));
+	src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC));
+	outl(r, s->io + ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void __init src_init(struct es1371_state *s)
+{
+        unsigned int i;
+
+        /* before we enable or disable the SRC we need
+           to wait for it to become ready */
+        wait_src_ready(s);
+
+        outl(SRC_DIS, s->io + ES1371_REG_SRCONV);
+
+        for (i = 0; i < 0x80; i++)
+                src_write(s, i, 0);
+
+        src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4);
+        src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10);
+        src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4);
+        src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10);
+        src_write(s, SRCREG_VOL_ADC, 1 << 12);
+        src_write(s, SRCREG_VOL_ADC+1, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC1, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC1+1, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC2, 1 << 12);
+        src_write(s, SRCREG_VOL_DAC2+1, 1 << 12);
+        set_adc_rate(s, 22050);
+        set_dac1_rate(s, 22050);
+        set_dac2_rate(s, 22050);
+
+        /* WARNING:
+         * enabling the sample rate converter without properly programming
+         * its parameters causes the chip to lock up (the SRC busy bit will
+         * be stuck high, and I've found no way to rectify this other than
+         * power cycle)
+         */
+        wait_src_ready(s);
+        outl(0, s->io+ES1371_REG_SRCONV);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
+{
+	struct es1371_state *s = (struct es1371_state *)codec->private_data;
+	unsigned long flags;
+	unsigned t, x;
+        
+	for (t = 0; t < POLL_COUNT; t++)
+		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
+			break;
+	spin_lock_irqsave(&s->lock, flags);
+
+        /* save the current state for later */
+        x = wait_src_ready(s);
+
+        /* enable SRC state data in SRC mux */
+	outl((x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
+	     s->io+ES1371_REG_SRCONV);
+
+        /* wait for not busy (state 0) first to avoid
+           transition states */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0 )
+                    break;
+                udelay(1);
+        }
+        
+        /* wait for a SAFE time to write addr/data and then do it, dammit */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000)
+                    break;
+                udelay(1);
+        }
+
+	outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
+	     ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC);
+
+	/* restore SRC reg */
+	wait_src_ready(s);
+	outl(x, s->io+ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static u16 rdcodec(struct ac97_codec *codec, u8 addr)
+{
+	struct es1371_state *s = (struct es1371_state *)codec->private_data;
+	unsigned long flags;
+	unsigned t, x;
+
+        /* wait for WIP to go away */
+	for (t = 0; t < 0x1000; t++)
+		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
+			break;
+	spin_lock_irqsave(&s->lock, flags);
+
+	/* save the current state for later */
+	x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC));
+
+	/* enable SRC state data in SRC mux */
+	outl( x | 0x00010000,
+              s->io+ES1371_REG_SRCONV);
+
+        /* wait for not busy (state 0) first to avoid
+           transition states */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0 )
+                    break;
+                udelay(1);
+        }
+        
+        /* wait for a SAFE time to write addr/data and then do it, dammit */
+        for (t=0; t<POLL_COUNT; t++){
+                if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000)
+                    break;
+                udelay(1);
+        }
+
+	outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC);
+	/* restore SRC reg */
+	wait_src_ready(s);
+	outl(x, s->io+ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+
+        /* wait for WIP again */
+	for (t = 0; t < 0x1000; t++)
+		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
+			break;
+        
+	/* now wait for the stinkin' data (RDY) */
+	for (t = 0; t < POLL_COUNT; t++)
+		if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY)
+			break;
+        
+	return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void stop_adc(struct es1371_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_ADC_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static inline void stop_dac1(struct es1371_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC1_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static inline void stop_dac2(struct es1371_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC2_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac1(struct es1371_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0)
+	    && s->dma_dac1.ready) {
+		s->ctrl |= CTRL_DAC1_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN;
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac1.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac2(struct es1371_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0)
+	    && s->dma_dac2.ready) {
+		s->ctrl |= CTRL_DAC2_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | 
+					 SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN |
+			(((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | 
+			(0 << SCTRL_SH_P2STINC);
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac2.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct es1371_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
+	    && s->dma_adc.ready) {
+		s->ctrl |= CTRL_ADC_EN;
+		s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_adc.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+
+static inline void dealloc_dmabuf(struct es1371_state *s, struct dmabuf *db)
+{
+	struct page *page, *pend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_unreserve(page);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg)
+{
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	struct page *page, *pend;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
+				break;
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_reserve(page);
+	}
+	fmt &= ES1371_FMT_MASK;
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize);
+	outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE);
+	outl(db->dmaaddr, s->io+(reg & 0xff));
+	outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff));
+	db->enabled = 1;
+	db->ready = 1;
+	return 0;
+}
+
+static inline int prog_dmabuf_adc(struct es1371_state *s)
+{
+	stop_adc(s);
+	return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, 
+			   ES1371_REG_ADC_FRAMEADR);
+}
+
+static inline int prog_dmabuf_dac2(struct es1371_state *s)
+{
+	stop_dac2(s);
+	return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, 
+			   ES1371_REG_DAC2_FRAMEADR);
+}
+
+static inline int prog_dmabuf_dac1(struct es1371_state *s)
+{
+	stop_dac1(s);
+	return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK,
+			   ES1371_REG_DAC1_FRAMEADR);
+}
+
+static inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg)
+{
+	unsigned hwptr, diff;
+
+	outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE);
+	hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc;
+	diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize;
+	db->hwptr = hwptr;
+	return diff;
+}
+
+static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *)buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(((char *)buf) + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void es1371_update_ptr(struct es1371_state *s)
+{
+	int diff;
+
+	/* update ADC pointer */
+	if (s->ctrl & CTRL_ADC_EN) {
+		diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT);
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+			wake_up(&s->dma_adc.wait);
+		if (!s->dma_adc.mapped) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				s->ctrl &= ~CTRL_ADC_EN;
+				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+				s->dma_adc.error++;
+			}
+		}
+	}
+	/* update DAC1 pointer */
+	if (s->ctrl & CTRL_DAC1_EN) {
+		diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT);
+		s->dma_dac1.total_bytes += diff;
+		if (s->dma_dac1.mapped) {
+			s->dma_dac1.count += diff;
+			if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
+				wake_up(&s->dma_dac1.wait);
+		} else {
+			s->dma_dac1.count -= diff;
+			if (s->dma_dac1.count <= 0) {
+				s->ctrl &= ~CTRL_DAC1_EN;
+				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+				s->dma_dac1.error++;
+			} else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
+				clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, 
+					      s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
+				s->dma_dac1.endcleared = 1;
+			}
+			if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize)
+				wake_up(&s->dma_dac1.wait);
+		}
+	}
+	/* update DAC2 pointer */
+	if (s->ctrl & CTRL_DAC2_EN) {
+		diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT);
+		s->dma_dac2.total_bytes += diff;
+		if (s->dma_dac2.mapped) {
+			s->dma_dac2.count += diff;
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize)
+				wake_up(&s->dma_dac2.wait);
+		} else {
+			s->dma_dac2.count -= diff;
+			if (s->dma_dac2.count <= 0) {
+				s->ctrl &= ~CTRL_DAC2_EN;
+				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+				s->dma_dac2.error++;
+			} else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
+				clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, 
+					      s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
+				s->dma_dac2.endcleared = 1;
+			}
+			if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize)
+				wake_up(&s->dma_dac2.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void es1371_handle_midi(struct es1371_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	if (!(s->ctrl & CTRL_UART_EN))
+		return;
+	wake = 0;
+	while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) {
+		ch = inb(s->io+ES1371_REG_UART_DATA);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+	outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL);
+}
+
+static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct es1371_state *s = (struct es1371_state *)dev_id;
+	unsigned int intsrc, sctl;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inl(s->io+ES1371_REG_STATUS);
+	if (!(intsrc & 0x80000000))
+		return;
+	spin_lock(&s->lock);
+	/* clear audio interrupts first */
+	sctl = s->sctrl;
+	if (intsrc & STAT_ADC)
+		sctl &= ~SCTRL_R1INTEN;
+	if (intsrc & STAT_DAC1)
+		sctl &= ~SCTRL_P1INTEN;
+	if (intsrc & STAT_DAC2)
+		sctl &= ~SCTRL_P2INTEN;
+	outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL);
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	es1371_update_ptr(s);
+	es1371_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != ES1371_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+/* Conversion table for S/PDIF PCM volume emulation through the SRC */
+/* dB-linear table of DAC vol values; -0dB to -46.5dB with mute */
+static const unsigned short DACVolTable[101] =
+{
+	0x1000, 0x0f2a, 0x0e60, 0x0da0, 0x0cea, 0x0c3e, 0x0b9a, 0x0aff,
+	0x0a6d, 0x09e1, 0x095e, 0x08e1, 0x086a, 0x07fa, 0x078f, 0x072a,
+	0x06cb, 0x0670, 0x061a, 0x05c9, 0x057b, 0x0532, 0x04ed, 0x04ab,
+	0x046d, 0x0432, 0x03fa, 0x03c5, 0x0392, 0x0363, 0x0335, 0x030b,
+	0x02e2, 0x02bc, 0x0297, 0x0275, 0x0254, 0x0235, 0x0217, 0x01fb,
+	0x01e1, 0x01c8, 0x01b0, 0x0199, 0x0184, 0x0170, 0x015d, 0x014b,
+	0x0139, 0x0129, 0x0119, 0x010b, 0x00fd, 0x00f0, 0x00e3, 0x00d7,
+	0x00cc, 0x00c1, 0x00b7, 0x00ae, 0x00a5, 0x009c, 0x0094, 0x008c,
+	0x0085, 0x007e, 0x0077, 0x0071, 0x006b, 0x0066, 0x0060, 0x005b,
+	0x0057, 0x0052, 0x004e, 0x004a, 0x0046, 0x0042, 0x003f, 0x003c,
+	0x0038, 0x0036, 0x0033, 0x0030, 0x002e, 0x002b, 0x0029, 0x0027,
+	0x0025, 0x0023, 0x0021, 0x001f, 0x001e, 0x001c, 0x001b, 0x0019,
+	0x0018, 0x0017, 0x0016, 0x0014, 0x0000
+};
+
+/*
+ * when we are in S/PDIF mode, we want to disable any analog output so
+ * we filter the mixer ioctls 
+ */
+static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
+{
+	struct es1371_state *s = (struct es1371_state *)codec->private_data;
+	int val;
+	unsigned long flags;
+	unsigned int left, right;
+
+	VALIDATE_STATE(s);
+	/* filter mixer ioctls to catch PCM and MASTER volume when in S/PDIF mode */
+	if (s->spdif_volume == -1)
+		return codec->mixer_ioctl(codec, cmd, arg);
+	switch (cmd) {
+	case SOUND_MIXER_WRITE_VOLUME:
+		return 0;
+
+	case SOUND_MIXER_WRITE_PCM:   /* use SRC for PCM volume */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		right = ((val >> 8)  & 0xff);
+		left = (val  & 0xff);
+		if (right > 100)
+			right = 100;
+		if (left > 100)
+			left = 100;
+		s->spdif_volume = (right << 8) | left;
+		spin_lock_irqsave(&s->lock, flags);
+		src_write(s, SRCREG_VOL_DAC2, DACVolTable[100 - left]);
+		src_write(s, SRCREG_VOL_DAC2+1, DACVolTable[100 - right]);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+	
+	case SOUND_MIXER_READ_PCM:
+		return put_user(s->spdif_volume, (int *)arg);
+	}
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * AC97 Mixer Register to Connections mapping of the Concert 97 board
+ *
+ * AC97_MASTER_VOL_STEREO   Line Out
+ * AC97_MASTER_VOL_MONO     TAD Output
+ * AC97_PCBEEP_VOL          none
+ * AC97_PHONE_VOL           TAD Input (mono)
+ * AC97_MIC_VOL             MIC Input (mono)
+ * AC97_LINEIN_VOL          Line Input (stereo)
+ * AC97_CD_VOL              CD Input (stereo)
+ * AC97_VIDEO_VOL           none
+ * AC97_AUX_VOL             Aux Input (stereo)
+ * AC97_PCMOUT_VOL          Wave Output (stereo)
+ */
+
+static int es1371_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct list_head *list;
+	struct es1371_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1371_state, devs);
+		if (s->codec.dev_mixer == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	return 0;
+}
+
+static int es1371_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	return 0;
+}
+
+static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	struct ac97_codec *codec = &s->codec;
+
+	return mixdev_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations es1371_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		es1371_ioctl_mixdev,
+	open:		es1371_open_mixdev,
+	release:	es1371_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac1(struct es1371_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+	
+	if (s->dma_dac1.mapped || !s->dma_dac1.ready)
+		return 0;
+        add_wait_queue(&s->dma_dac1.wait, &wait);
+        for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac1.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac1.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate;
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		if (!schedule_timeout(tmo + 1))
+			DBG(printk(KERN_DEBUG PFX "dac1 dma timed out??\n");)
+        }
+        remove_wait_queue(&s->dma_dac1.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+static int drain_dac2(struct es1371_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac2.mapped || !s->dma_dac2.ready)
+		return 0;
+        add_wait_queue(&s->dma_dac2.wait, &wait);
+        for (;;) {
+		__set_current_state(TASK_UNINTERRUPTIBLE);
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac2.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac2.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate;
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		if (!schedule_timeout(tmo + 1))
+			DBG(printk(KERN_DEBUG PFX "dac2 dma timed out??\n");)
+        }
+        remove_wait_queue(&s->dma_dac2.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	down(&s->sem);
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		goto out2;
+	
+	add_wait_queue(&s->dma_adc.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_adc.enabled)
+				start_adc(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				goto out;
+			}
+			up(&s->sem);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				goto out2;
+			}
+			down(&s->sem);
+			if (s->dma_adc.mapped)
+			{
+				ret = -ENXIO;
+				goto out;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			goto out;
+		}
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_adc.enabled)
+			start_adc(s);
+	}
+out:
+	up(&s->sem);
+out2:
+	remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac2.mapped)
+		return -ENXIO;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	down(&s->sem);	
+	if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+		goto out3;
+	ret = 0;
+	add_wait_queue(&s->dma_dac2.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac2.count < 0) {
+			s->dma_dac2.count = 0;
+			s->dma_dac2.swptr = s->dma_dac2.hwptr;
+		}
+		swptr = s->dma_dac2.swptr;
+		cnt = s->dma_dac2.dmasize-swptr;
+		if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
+			cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_dac2.enabled)
+				start_dac2(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				goto out;
+			}	
+			up(&s->sem);
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				goto out2;
+			}
+			down(&s->sem);
+			if (s->dma_dac2.mapped)
+			{
+				ret = -ENXIO;
+				goto out;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			goto out;
+		}
+		swptr = (swptr + cnt) % s->dma_dac2.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac2.swptr = swptr;
+		s->dma_dac2.count += cnt;
+		s->dma_dac2.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_dac2.enabled)
+			start_dac2(s);
+	}
+out:
+	up(&s->sem);
+out2:
+	remove_wait_queue(&s->dma_dac2.wait, &wait);
+out3:	
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		if (!s->dma_dac2.ready && prog_dmabuf_dac2(s))
+			return 0;
+		poll_wait(file, &s->dma_dac2.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!s->dma_adc.ready && prog_dmabuf_adc(s))
+			return 0;
+		poll_wait(file, &s->dma_adc.wait, wait);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	es1371_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+				mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac2.mapped) {
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1371_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	struct dmabuf *db;
+	int ret = 0;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	down(&s->sem);
+	
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_dac2(s)) != 0) {
+			goto out;
+		}
+		db = &s->dma_dac2;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_adc(s)) != 0) {
+			goto out;
+		}
+		db = &s->dma_adc;
+	} else {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (vma->vm_pgoff != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	db->mapped = 1;
+out:
+	up(&s->sem);
+	unlock_kernel();
+	return ret;
+}
+
+static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int count;
+	int val, mapped, ret;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			synchronize_irq();
+			s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate(s, val);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				set_dac2_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg);
+
+        case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_R1SMB;
+			else
+				s->sctrl &= ~SCTRL_R1SMB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			s->dma_dac2.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_P2SMB;
+			else
+				s->sctrl &= ~SCTRL_P2SMB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+                }
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_R1SMB;
+				else
+					s->sctrl &= ~SCTRL_R1SMB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_P2SMB;
+				else
+					s->sctrl &= ~SCTRL_P2SMB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_R1SEB;
+				else
+					s->sctrl &= ~SCTRL_R1SEB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_P2SEB;
+				else
+					s->sctrl &= ~SCTRL_P2SEB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 
+				AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				s->dma_adc.enabled = 1;
+				start_adc(s);
+			} else {
+				s->dma_adc.enabled = 0;
+				stop_adc(s);
+			}
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+					return ret;
+				s->dma_dac2.enabled = 1;
+				start_dac2(s);
+			} else {
+				s->dma_dac2.enabled = 0;
+				stop_dac2(s);
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+		abinfo.fragsize = s->dma_dac2.fragsize;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac2.dmasize - count;
+                abinfo.fragstotal = s->dma_dac2.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                count = s->dma_dac2.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                cinfo.bytes = s->dma_dac2.total_bytes;
+		count = s->dma_dac2.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac2.fragshift;
+                cinfo.ptr = s->dma_dac2.hwptr;
+		if (s->dma_dac2.mapped)
+			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac2(s)))
+				return val;
+			return put_user(s->dma_dac2.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac2.ossfragshift = val & 0xffff;
+			s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac2.ossfragshift < 4)
+				s->dma_dac2.ossfragshift = 4;
+			if (s->dma_dac2.ossfragshift > 15)
+				s->dma_dac2.ossfragshift = 15;
+			if (s->dma_dac2.ossmaxfrags < 4)
+				s->dma_dac2.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac2.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg);
+		
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixdev_ioctl(&s->codec, cmd, arg);
+}
+
+static int es1371_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct es1371_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1371_state, devs);
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		s->dma_adc.enabled = 1;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0;
+		s->dma_dac2.enabled = 1;
+		set_dac2_rate(s, 8000);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		s->sctrl &= ~SCTRL_R1FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT;
+		else
+			s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->sctrl &= ~SCTRL_P2FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT;
+		else
+			s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT;
+	}
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	init_MUTEX(&s->sem);
+	return 0;
+}
+
+static int es1371_release(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac2(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac2(s);
+		dealloc_dmabuf(s, &s->dma_dac2);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(s, &s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations es1371_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		es1371_read,
+	write:		es1371_write,
+	poll:		es1371_poll,
+	ioctl:		es1371_ioctl,
+	mmap:		es1371_mmap,
+	open:		es1371_open,
+	release:	es1371_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac1.mapped)
+		return -ENXIO;
+	if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	add_wait_queue(&s->dma_dac1.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac1.count < 0) {
+			s->dma_dac1.count = 0;
+			s->dma_dac1.swptr = s->dma_dac1.hwptr;
+		}
+		swptr = s->dma_dac1.swptr;
+		cnt = s->dma_dac1.dmasize-swptr;
+		if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
+			cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_dac1.enabled)
+				start_dac1(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		swptr = (swptr + cnt) % s->dma_dac1.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac1.swptr = swptr;
+		s->dma_dac1.count += cnt;
+		s->dma_dac1.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_dac1.enabled)
+			start_dac1(s);
+	}
+	remove_wait_queue(&s->dma_dac1.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (!s->dma_dac1.ready && prog_dmabuf_dac1(s))
+		return 0;
+	poll_wait(file, &s->dma_dac1.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	es1371_update_ptr(s);
+	if (s->dma_dac1.mapped) {
+		if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	} else {
+		if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (!(vma->vm_flags & VM_WRITE))
+		return -EINVAL;
+	lock_kernel();
+	if ((ret = prog_dmabuf_dac1(s)) != 0)
+		goto out;
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << s->dma_dac1.buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot))
+		goto out;
+	s->dma_dac1.mapped = 1;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int count;
+	int val, ret;
+
+	VALIDATE_STATE(s);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/);
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		stop_dac1(s);
+		synchronize_irq();
+		s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0;
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			set_dac1_rate(s, val);
+		}
+		return put_user(s->dac1rate, (int *)arg);
+
+        case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		stop_dac1(s);
+		s->dma_dac1.ready = 0;
+		spin_lock_irqsave(&s->lock, flags);
+		if (val)
+			s->sctrl |= SCTRL_P1SMB;
+		else
+			s->sctrl &= ~SCTRL_P1SMB;
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val >= 2)
+				s->sctrl |= SCTRL_P1SMB;
+			else
+				s->sctrl &= ~SCTRL_P1SMB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg);
+		
+        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val == AFMT_S16_LE)
+				s->sctrl |= SCTRL_P1SEB;
+			else
+				s->sctrl &= ~SCTRL_P1SEB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+        case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg);
+						
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val & PCM_ENABLE_OUTPUT) {
+			if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+				return ret;
+			s->dma_dac1.enabled = 1;
+			start_dac1(s);
+		} else {
+			s->dma_dac1.enabled = 0;
+			stop_dac1(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+		abinfo.fragsize = s->dma_dac1.fragsize;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac1.dmasize - count;
+                abinfo.fragstotal = s->dma_dac1.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                count = s->dma_dac1.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                cinfo.bytes = s->dma_dac1.total_bytes;
+		count = s->dma_dac1.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac1.fragshift;
+                cinfo.ptr = s->dma_dac1.hwptr;
+		if (s->dma_dac1.mapped)
+			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if ((val = prog_dmabuf_dac1(s)))
+			return val;
+                return put_user(s->dma_dac1.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		s->dma_dac1.ossfragshift = val & 0xffff;
+		s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
+		if (s->dma_dac1.ossfragshift < 4)
+			s->dma_dac1.ossfragshift = 4;
+		if (s->dma_dac1.ossfragshift > 15)
+			s->dma_dac1.ossfragshift = 15;
+		if (s->dma_dac1.ossmaxfrags < 4)
+			s->dma_dac1.ossmaxfrags = 4;
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if (s->dma_dac1.subdivision)
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		s->dma_dac1.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user(s->dac1rate, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixdev_ioctl(&s->codec, cmd, arg);
+}
+
+static int es1371_open_dac(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct es1371_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1371_state, devs);
+		if (!((s->dev_dac ^ minor) & ~0xf))
+			break;
+	}
+       	VALIDATE_STATE(s);
+       	/* we allow opening with O_RDWR, most programs do it although they will only write */
+#if 0
+	if (file->f_mode & FMODE_READ)
+		return -EPERM;
+#endif
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EINVAL;
+       	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DAC) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0;
+	s->dma_dac1.enabled = 1;
+	set_dac1_rate(s, 8000);
+	spin_lock_irqsave(&s->lock, flags);
+	s->sctrl &= ~SCTRL_P1FMT;
+	if ((minor & 0xf) == SND_DEV_DSP16)
+		s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT;
+	else
+		s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT;
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= FMODE_DAC;
+	up(&s->open_sem);
+	return 0;
+}
+
+static int es1371_release_dac(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	drain_dac1(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	stop_dac1(s);
+	dealloc_dmabuf(s, &s->dma_dac1);
+	s->open_mode &= ~FMODE_DAC;
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations es1371_dac_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	write:		es1371_write_dac,
+	poll:		es1371_poll_dac,
+	ioctl:		es1371_ioctl_dac,
+	mmap:		es1371_mmap_dac,
+	open:		es1371_open_dac,
+	release:	es1371_release_dac,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+        add_wait_queue(&s->midi.iwait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		break;
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.iwait, &wait);
+	return ret;
+}
+
+static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			es1371_handle_midi(s);
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.owait, &wait);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1371_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct es1371_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct es1371_state, devs);
+		if (s->dev_midi == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL);
+		outb(0, s->io+ES1371_REG_UART_CONTROL);
+		outb(0, s->io+ES1371_REG_UART_TEST);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	s->ctrl |= CTRL_UART_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	es1371_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int es1371_midi_release(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE) {
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				set_current_state(TASK_RUNNING);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				printk(KERN_DEBUG PFX "midi timed out??\n");
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		set_current_state(TASK_RUNNING);
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->ctrl &= ~CTRL_UART_EN;
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations es1371_midi_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		es1371_midi_read,
+	write:		es1371_midi_write,
+	poll:		es1371_midi_poll,
+	open:		es1371_midi_open,
+	release:	es1371_midi_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * for debugging purposes, we'll create a proc device that dumps the
+ * CODEC chipstate
+ */
+
+#ifdef ES1371_DEBUG
+static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data)
+{
+	struct es1371_state *s;
+        int cnt, len = 0;
+
+	if (list_empty(&devs))
+		return 0;
+	s = list_entry(devs.next, struct es1371_state, devs);
+        /* print out header */
+        len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n");
+
+        /* print out CODEC state */
+        len += sprintf (buf + len, "AC97 CODEC state\n");
+	for (cnt=0; cnt <= 0x7e; cnt = cnt +2)
+                len+= sprintf (buf + len, "reg:0x%02x  val:0x%04x\n", cnt, rdcodec(&s->codec, cnt));
+
+        if (fpos >=len){
+                *start = buf;
+                *eof =1;
+                return 0;
+        }
+        *start = buf + fpos;
+        if ((len -= fpos) > length)
+                return length;
+        *eof =1;
+        return len;
+
+}
+#endif /* ES1371_DEBUG */
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices; only used for command line params */
+#define NR_DEVICE 5
+
+static int joystick[NR_DEVICE] = { 0, };
+static int spdif[NR_DEVICE] = { 0, };
+static int nomix[NR_DEVICE] = { 0, };
+
+static unsigned int devindex = 0;
+static int amplifier = 0;
+
+MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)");
+MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(spdif, "if 1 the output is in S/PDIF digital mode");
+MODULE_PARM(nomix, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(nomix, "if 1 no analog audio is mixed to the digital output");
+MODULE_PARM(amplifier, "i");
+MODULE_PARM_DESC(amplifier, "Set to 1 if the machine needs the amp control enabling (many laptops)");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver");
+MODULE_LICENSE("GPL");
+
+
+/* --------------------------------------------------------------------- */
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 },
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 },
+	{ SOUND_MIXER_WRITE_OGAIN, 0x4040 },
+	{ MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 },
+	{ SOUND_MIXER_WRITE_SPEAKER, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_RECLEV, 0x4040 },
+	{ SOUND_MIXER_WRITE_IGAIN, 0x4040 }
+};
+
+static struct
+{
+	short svid, sdid;
+} amplifier_needed[] = 
+{
+	{ 0x107B, 0x2150 },		/* Gateway Solo 2150 */
+	{ 0x13BD, 0x100C },		/* Mebius PC-MJ100V */
+	{ 0x1102, 0x5938 },		/* Targa Xtender 300 */
+	{ 0x1102, 0x8938 },		/* IPC notebook */
+	{ PCI_ANY_ID, PCI_ANY_ID }
+};
+
+
+static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+{
+	struct es1371_state *s;
+	mm_segment_t fs;
+	int i, val, res = -1;
+	int idx;
+	unsigned long tmo;
+	signed long tmo2;
+	unsigned int cssr;
+
+	if ((res=pci_enable_device(pcidev)))
+		return res;
+
+	if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO))
+		return -ENODEV;
+	if (pcidev->irq == 0) 
+		return -ENODEV;
+	i = pci_set_dma_mask(pcidev, 0xffffffff);
+	if (i) {
+		printk(KERN_WARNING "es1371: architecture does not support 32bit PCI busmaster DMA\n");
+		return i;
+	}
+	if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) {
+		printk(KERN_WARNING PFX "out of memory\n");
+		return -ENOMEM;
+	}
+	memset(s, 0, sizeof(struct es1371_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac1.wait);
+	init_waitqueue_head(&s->dma_dac2.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+	s->magic = ES1371_MAGIC;
+	s->dev = pcidev;
+	s->io = pci_resource_start(pcidev, 0);
+	s->irq = pcidev->irq;
+	s->vendor = pcidev->vendor;
+	s->device = pcidev->device;
+	pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev);
+	s->codec.private_data = s;
+	s->codec.id = 0;
+	s->codec.codec_read = rdcodec;
+	s->codec.codec_write = wrcodec;
+	printk(KERN_INFO PFX "found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n",
+	       s->vendor, s->device, s->rev);
+	if (!request_region(s->io, ES1371_EXTENT, "es1371")) {
+		printk(KERN_ERR PFX "io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1);
+		res = -EBUSY;
+		goto err_region;
+	}
+	if ((res=request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371",s))) {
+		printk(KERN_ERR PFX "irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+	printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u\n"
+	       KERN_INFO PFX "features: joystick 0x%x\n", s->rev, s->io, s->irq, joystick[devindex]);
+	/* register devices */
+	if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1))<0))
+		goto err_dev1;
+	if ((res=(s->codec.dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1)) < 0))
+		goto err_dev2;
+	if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0))
+		goto err_dev3;
+	if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1))<0 ))
+		goto err_dev4;
+#ifdef ES1371_DEBUG
+	/* intialize the debug proc device */
+	s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL);
+#endif /* ES1371_DEBUG */
+	
+	/* initialize codec registers */
+	s->ctrl = 0;
+
+	/* Check amplifier requirements */
+	
+	if(amplifier)
+		s->ctrl |= CTRL_GPIO_OUT0;
+	else for(idx = 0; amplifier_needed[idx].svid != PCI_ANY_ID; idx++)
+	{
+		if(pcidev->subsystem_vendor == amplifier_needed[idx].svid &&
+		   pcidev->subsystem_device == amplifier_needed[idx].sdid)
+		{
+                    	s->ctrl |= CTRL_GPIO_OUT0;   /* turn internal amplifier on */
+                    	printk(KERN_INFO PFX "Enabling internal amplifier.\n");
+		}
+	}
+	s->gameport.io = 0;
+	if ((joystick[devindex] & ~0x18) == 0x200) {
+		if (!request_region(joystick[devindex], JOY_EXTENT, "es1371"))
+			printk(KERN_ERR PFX "joystick address 0x%x already in use\n", joystick[devindex]);
+		else {
+			s->ctrl |= CTRL_JYSTK_EN | (((joystick[devindex] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT);
+			s->gameport.io = joystick[devindex];
+		}
+	} else if (joystick[devindex] == 1) {
+		for (i = 0x218; i >= 0x200; i -= 0x08) {
+			if (request_region(i, JOY_EXTENT, "es1371")) {
+				s->ctrl |= CTRL_JYSTK_EN | (((i >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT);
+				s->gameport.io = i;
+				break;
+			}
+		}
+		if (!s->gameport.io)
+			printk(KERN_ERR PFX "no free joystick address found\n");
+	}
+	s->sctrl = 0;
+	cssr = 0;
+	s->spdif_volume = -1;
+	/* check to see if s/pdif mode is being requested */
+	if (spdif[devindex]) {
+		if (s->rev >= 4) {
+			printk(KERN_INFO PFX "enabling S/PDIF output\n");
+			s->spdif_volume = 0;
+			cssr |= STAT_EN_SPDIF;
+			s->ctrl |= CTRL_SPDIFEN_B;
+			if (nomix[devindex]) /* don't mix analog inputs to s/pdif output */
+				s->ctrl |= CTRL_RECEN_B;
+		} else {
+			printk(KERN_ERR PFX "revision %d does not support S/PDIF\n", s->rev);
+		}
+	}
+	/* initialize the chips */
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	outl(0, s->io+ES1371_REG_LEGACY);
+	pci_set_master(pcidev);  /* enable bus mastering */
+	/* if we are a 5880 turn on the AC97 */
+	if (s->vendor == PCI_VENDOR_ID_ENSONIQ &&
+	    ((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev >= CT5880REV_CT5880_C) || 
+	     (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A) || 
+	     (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_ES1373_8))) { 
+		cssr |= CSTAT_5880_AC97_RST;
+		outl(cssr, s->io+ES1371_REG_STATUS);
+		/* need to delay around 20ms(bleech) to give
+		   some CODECs enough time to wakeup */
+		tmo = jiffies + (HZ / 50) + 1;
+		for (;;) {
+			tmo2 = tmo - jiffies;
+			if (tmo2 <= 0)
+				break;
+			schedule_timeout(tmo2);
+		}
+	}
+	/* AC97 warm reset to start the bitclk */
+	outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL);
+	udelay(2);
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	/* init the sample rate converter */
+	src_init(s);
+	/* codec init */
+	if (!ac97_probe_codec(&s->codec)) {
+		res = -ENODEV;
+		goto err_gp;
+	}
+	/* set default values */
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE;
+	mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+	for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixdev_ioctl(&s->codec, initvol[i].mixch, (unsigned long)&val);
+	}
+	/* mute master and PCM when in S/PDIF mode */
+	if (s->spdif_volume != -1) {
+		val = 0x0000;
+		s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val);
+		s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val);
+	}
+	set_fs(fs);
+	/* turn on S/PDIF output driver if requested */
+	outl(cssr, s->io+ES1371_REG_STATUS);
+	/* register gameport */
+	gameport_register_port(&s->gameport);
+	/* store it in the driver field */
+	pci_set_drvdata(pcidev, s);
+	/* put it into driver list */
+	list_add_tail(&s->devs, &devs);
+	/* increment devindex */
+	if (devindex < NR_DEVICE-1)
+		devindex++;
+       	return 0;
+
+ err_gp:
+	if (s->gameport.io)
+		release_region(s->gameport.io, JOY_EXTENT);
+ err_dev4:
+	unregister_sound_dsp(s->dev_dac);
+ err_dev3:
+	unregister_sound_mixer(s->codec.dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR PFX "cannot register misc device\n");
+	free_irq(s->irq, s);
+ err_irq:
+	release_region(s->io, ES1371_EXTENT);
+ err_region:
+	kfree(s);
+	return res;
+}
+
+static void __devinit es1371_remove(struct pci_dev *dev)
+{
+	struct es1371_state *s = pci_get_drvdata(dev);
+
+	if (!s)
+		return;
+	list_del(&s->devs);
+#ifdef ES1371_DEBUG
+	if (s->ps)
+		remove_proc_entry("es1371", NULL);
+#endif /* ES1371_DEBUG */
+	outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */
+	outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */
+	synchronize_irq();
+	free_irq(s->irq, s);
+	if (s->gameport.io) {
+		gameport_unregister_port(&s->gameport);
+		release_region(s->gameport.io, JOY_EXTENT);
+	}
+	release_region(s->io, ES1371_EXTENT);
+	unregister_sound_dsp(s->dev_audio);
+	unregister_sound_mixer(s->codec.dev_mixer);
+	unregister_sound_dsp(s->dev_dac);
+	unregister_sound_midi(s->dev_midi);
+	kfree(s);
+	pci_set_drvdata(dev, NULL);
+}
+
+static struct pci_device_id id_table[] __devinitdata = {
+	{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+	{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+	{ PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver es1371_driver = {
+	name: "es1371",
+	id_table: id_table,
+	probe: es1371_probe,
+	remove: es1371_remove
+};
+
+static int __init init_es1371(void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO PFX "version v0.30 time " __TIME__ " " __DATE__ "\n");
+	return pci_module_init(&es1371_driver);
+}
+
+static void __exit cleanup_es1371(void)
+{
+	printk(KERN_INFO PFX "unloading\n");
+	pci_unregister_driver(&es1371_driver);
+}
+
+module_init(init_es1371);
+module_exit(cleanup_es1371);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/* format is: es1371=[joystick] */
+
+static int __init es1371_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= NR_DEVICE)
+		return 0;
+	if (get_option(&str, &joystick[nr_dev]) == 2)
+		(void)get_option(&str, &spdif[nr_dev]);
+	nr_dev++;
+	return 1;
+}
+
+__setup("es1371=", es1371_setup);
+
+#endif /* MODULE */
diff -Nru linux/sound/oss/esssolo1.c linux-2.4.19-pre5-mjc/sound/oss/esssolo1.c
--- linux/sound/oss/esssolo1.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/esssolo1.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2484 @@
+/****************************************************************************/
+
+/*
+ *      esssolo1.c  --  ESS Technology Solo1 (ES1946) audio driver.
+ *
+ *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module command line parameters:
+ *   none so far
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  Revision history
+ *    10.11.1998   0.1   Initial release (without any hardware)
+ *    22.03.1999   0.2   cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *                       return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    07.04.1999   0.3   implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *    15.06.1999   0.4   Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.5   Add pci_set_master
+ *    12.08.1999   0.6   Fix MIDI UART crashing the driver
+ *                       Changed mixer semantics from OSS documented
+ *                       behaviour to OSS "code behaviour".
+ *                       Recording might actually work now.
+ *                       The real DDMA controller address register is at PCI config
+ *                       0x60, while the register at 0x18 is used as a placeholder
+ *                       register for BIOS address allocation. This register
+ *                       is supposed to be copied into 0x60, according
+ *                       to the Solo1 datasheet. When I do that, I can access
+ *                       the DDMA registers except the mask bit, which
+ *                       is stuck at 1. When I copy the contents of 0x18 +0x10
+ *                       to the DDMA base register, everything seems to work.
+ *                       The fun part is that the Windows Solo1 driver doesn't
+ *                       seem to do these tricks.
+ *                       Bugs remaining: plops and clicks when starting/stopping playback
+ *    31.08.1999   0.7   add spin_lock_init
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.8   change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    07.10.1999   0.9   Fix initialization; complain if sequencer writes time out
+ *                       Revised resource grabbing for the FM synthesizer
+ *    28.10.1999   0.10  More waitqueue races fixed
+ *    09.12.1999   0.11  Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M)
+ *                       Disabling recording on Alpha
+ *    12.01.2000   0.12  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *                       Integrated (aka redid 8-)) APM support patch by Zach Brown
+ *    07.02.2000   0.13  Use pci_alloc_consistent and pci_register_driver
+ *    19.02.2000   0.14  Use pci_dma_supported to determine if recording should be disabled
+ *    13.03.2000   0.15  Reintroduce initialization of a couple of PCI config space registers
+ *    21.11.2000   0.16  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.17  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
+ *    31.01.2001   0.18  Register/Unregister gameport, original patch from
+ *                       Nathaniel Daw <daw@cs.cmu.edu>
+ *                       Fix SETTRIGGER non OSS API conformity
+ *    10.03.2001         provide abs function, prevent picking up a bogus kernel macro
+ *                       for abs. Bug report by Andrew Morton <andrewm@uow.edu.au>
+ *    15.05.2001         pci_enable_device moved, return values in probe cleaned
+ *                       up. Marcus Meissner <mm@caldera.de>
+ *    22.05.2001   0.19  more cleanups, changed PM to PCI 2.4 style, got rid
+ *                       of global list of devices, using pci device data.
+ *                       Marcus Meissner <mm@caldera.de>
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/gameport.h>
+
+#include "dm.h"
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS         0x125d
+#endif
+#ifndef PCI_DEVICE_ID_ESS_SOLO1
+#define PCI_DEVICE_ID_ESS_SOLO1   0x1969
+#endif
+
+#define SOLO1_MAGIC  ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1)
+
+#define DDMABASE_OFFSET           0    /* chip bug workaround kludge */
+#define DDMABASE_EXTENT           16
+
+#define IOBASE_EXTENT             16
+#define SBBASE_EXTENT             16
+#define VCBASE_EXTENT             (DDMABASE_EXTENT+DDMABASE_OFFSET)
+#define MPUBASE_EXTENT            4
+#define GPBASE_EXTENT             4
+#define GAMEPORT_EXTENT		  4
+
+#define FMSYNTH_EXTENT            4
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define FMODE_DMFM 0x10
+
+static struct pci_driver solo1_driver;
+
+/* --------------------------------------------------------------------- */
+
+struct solo1_state {
+	/* magic */
+	unsigned int magic;
+
+	/* the corresponding pci_dev structure */
+	struct pci_dev *dev;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_midi;
+	int dev_dmfm;
+
+	/* hardware resources */
+	unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */
+	unsigned int irq;
+
+	/* mixer registers */
+	struct {
+		unsigned short vol[10];
+		unsigned int recsrc;
+		unsigned int modcnt;
+		unsigned short micpreamp;
+	} mix;
+
+	/* wave stuff */
+	unsigned fmt;
+	unsigned channels;
+	unsigned rate;
+	unsigned char clkdiv;
+	unsigned ena;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		dma_addr_t dmaaddr;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned enabled:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		struct timer_list timer;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+
+	struct gameport gameport;
+};
+
+/* --------------------------------------------------------------------- */
+
+static inline void write_seq(struct solo1_state *s, unsigned char data)
+{
+        int i;
+	unsigned long flags;
+
+	/* the __cli stunt is to send the data within the command window */
+        for (i = 0; i < 0xffff; i++) {
+		__save_flags(flags);
+		__cli();
+                if (!(inb(s->sbbase+0xc) & 0x80)) {
+                        outb(data, s->sbbase+0xc);
+			__restore_flags(flags);
+                        return;
+                }
+		__restore_flags(flags);
+	}
+	printk(KERN_ERR "esssolo1: write_seq timeout\n");
+	outb(data, s->sbbase+0xc);
+}
+
+static inline int read_seq(struct solo1_state *s, unsigned char *data)
+{
+        int i;
+
+        if (!data)
+                return 0;
+        for (i = 0; i < 0xffff; i++)
+                if (inb(s->sbbase+0xe) & 0x80) {
+                        *data = inb(s->sbbase+0xa);
+                        return 1;
+                }
+	printk(KERN_ERR "esssolo1: read_seq timeout\n");
+        return 0;
+}
+
+static int inline reset_ctrl(struct solo1_state *s)
+{
+        int i;
+
+        outb(3, s->sbbase+6); /* clear sequencer and FIFO */
+        udelay(10);
+        outb(0, s->sbbase+6);
+        for (i = 0; i < 0xffff; i++)
+                if (inb(s->sbbase+0xe) & 0x80)
+                        if (inb(s->sbbase+0xa) == 0xaa) {
+				write_seq(s, 0xc6); /* enter enhanced mode */
+                                return 1;
+			}
+        return 0;
+}
+
+static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data)
+{
+	write_seq(s, reg);
+	write_seq(s, data);
+}
+
+#if 0 /* unused */
+static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg)
+{
+        unsigned char r;
+
+	write_seq(s, 0xc0);
+	write_seq(s, reg);
+	read_seq(s, &r);
+	return r;
+}
+#endif /* unused */
+
+static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data)
+{
+	outb(reg, s->sbbase+4);
+	outb(data, s->sbbase+5);
+}
+
+static unsigned char read_mixer(struct solo1_state *s, unsigned char reg)
+{
+	outb(reg, s->sbbase+4);
+	return inb(s->sbbase+5);
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void stop_dac(struct solo1_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ena &= ~FMODE_WRITE;
+	write_mixer(s, 0x78, 0x10);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_dac(struct solo1_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		s->ena |= FMODE_WRITE;
+		write_mixer(s, 0x78, 0x12);
+		udelay(10);
+		write_mixer(s, 0x78, 0x13);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static inline void stop_adc(struct solo1_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ena &= ~FMODE_READ;
+	write_ctrl(s, 0xb8, 0xe);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_adc(struct solo1_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
+	    && s->dma_adc.ready) {
+		s->ena |= FMODE_READ;
+		write_ctrl(s, 0xb8, 0xf);
+#if 0
+		printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf);
+		printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x  stat: 0x%02x\n", 
+		       inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8));
+#endif
+                outb(0, s->ddmabase+0xd); /* master reset */
+		outb(1, s->ddmabase+0xf);  /* mask */
+		outb(0x54/*0x14*/, s->ddmabase+0xb);  /* DMA_MODE_READ | DMA_MODE_AUTOINIT */
+		outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase);
+		outw(s->dma_adc.dmasize-1, s->ddmabase+4);
+		outb(0, s->ddmabase+0xf);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+#if 0
+	printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x  SBstat: 0x%02x\n"
+	       KERN_DEBUG "solo1: DMA: stat: 0x%02x  cnt: 0x%04x  mask: 0x%02x\n", 
+	       read_ctrl(s, 0xb8), inb(s->sbbase+0xc), 
+	       inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf));
+	printk(KERN_DEBUG "solo1: A1: 0x%02x  A2: 0x%02x  A4: 0x%02x  A5: 0x%02x  A8: 0x%02x\n"  
+	       KERN_DEBUG "solo1: B1: 0x%02x  B2: 0x%02x  B4: 0x%02x  B7: 0x%02x  B8: 0x%02x  B9: 0x%02x\n",
+	       read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), 
+	       read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), 
+	       read_ctrl(s, 0xb9));
+#endif
+}
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+static inline void dealloc_dmabuf(struct solo1_state *s, struct dmabuf *db)
+{
+	struct page *page, *pend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_unreserve(page);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db)
+{
+	int order;
+	unsigned bytespersec;
+	unsigned bufs, sample_shift = 0;
+	struct page *page, *pend;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+                for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
+				break;
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_reserve(page);
+	}
+	if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE))
+		sample_shift++;
+	if (s->channels > 1)
+		sample_shift++;
+	bytespersec = s->rate << sample_shift;
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytespersec)
+			db->fragshift = ld2(bytespersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift;
+	db->dmasize = db->numfrag << db->fragshift;
+	db->enabled = 1;
+	return 0;
+}
+
+static inline int prog_dmabuf_adc(struct solo1_state *s)
+{
+	unsigned long va;
+	int c;
+
+	stop_adc(s);
+	/* check if PCI implementation supports 24bit busmaster DMA */
+	if (s->dev->dma_mask > 0xffffff)
+		return -EIO;
+	if ((c = prog_dmabuf(s, &s->dma_adc)))
+		return c;
+	va = s->dma_adc.dmaaddr;
+	if ((va & ~((1<<24)-1)))
+		panic("solo1: buffer above 16M boundary");
+	outb(0, s->ddmabase+0xd);  /* clear */
+	outb(1, s->ddmabase+0xf); /* mask */
+	/*outb(0, s->ddmabase+8);*/  /* enable (enable is active low!) */
+	outb(0x54, s->ddmabase+0xb);  /* DMA_MODE_READ | DMA_MODE_AUTOINIT */
+	outl(va, s->ddmabase);
+	outw(s->dma_adc.dmasize-1, s->ddmabase+4);
+	c = - s->dma_adc.fragsamples;
+	write_ctrl(s, 0xa4, c);
+	write_ctrl(s, 0xa5, c >> 8);
+	outb(0, s->ddmabase+0xf);
+	s->dma_adc.ready = 1;
+	return 0;
+}
+
+static inline int prog_dmabuf_dac(struct solo1_state *s)
+{
+	unsigned long va;
+	int c;
+
+	stop_dac(s);
+	if ((c = prog_dmabuf(s, &s->dma_dac)))
+		return c;
+	memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */
+	va = s->dma_dac.dmaaddr;
+	if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1))
+		panic("solo1: buffer crosses 1M boundary");
+	outl(va, s->iobase);
+	/* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */
+	outw(s->dma_dac.dmasize, s->iobase+4);
+	c = - s->dma_dac.fragsamples;
+	write_mixer(s, 0x74, c);
+	write_mixer(s, 0x76, c >> 8);
+	outb(0xa, s->iobase+6);
+	s->dma_dac.ready = 1;
+	return 0;
+}
+
+static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *)buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(((char *)buf) + bptr, c, len);
+}
+
+/* call with spinlock held! */
+
+static void solo1_update_ptr(struct solo1_state *s)
+{
+	int diff;
+	unsigned hwptr;
+
+	/* update ADC pointer */
+	if (s->ena & FMODE_READ) {
+		hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize;
+                diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+                s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+#if 0
+		printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n",
+		       s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count);
+#endif
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+				wake_up(&s->dma_adc.wait);
+		} else {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				s->ena &= ~FMODE_READ;
+				write_ctrl(s, 0xb8, 0xe);
+				s->dma_adc.error++;
+			}
+			if (s->dma_adc.count > 0)
+				wake_up(&s->dma_adc.wait);
+		}
+	}
+	/* update DAC pointer */
+	if (s->ena & FMODE_WRITE) {
+                hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize;
+                diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+                s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+#if 0
+		printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n",
+		       s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count);
+#endif
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+				wake_up(&s->dma_dac.wait);
+		} else {
+			s->dma_dac.count -= diff;
+			if (s->dma_dac.count <= 0) {
+				s->ena &= ~FMODE_WRITE;
+				write_mixer(s, 0x78, 0x12);
+				s->dma_dac.error++;
+			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+				clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr,
+					      s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80);
+				s->dma_dac.endcleared = 1;
+			}
+			if (s->dma_dac.count < (signed)s->dma_dac.dmasize)
+				wake_up(&s->dma_dac.wait);
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void prog_codec(struct solo1_state *s)
+{
+	unsigned long flags;
+	int fdiv, filter;
+	unsigned char c;
+
+	reset_ctrl(s);
+	write_seq(s, 0xd3);
+	/* program sampling rates */
+	filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */
+	fdiv = 256 - 7160000 / (filter * 82);
+	spin_lock_irqsave(&s->lock, flags);
+	write_ctrl(s, 0xa1, s->clkdiv);
+	write_ctrl(s, 0xa2, fdiv);
+	write_mixer(s, 0x70, s->clkdiv);
+	write_mixer(s, 0x72, fdiv);
+	/* program ADC parameters */
+	write_ctrl(s, 0xb8, 0xe);
+	write_ctrl(s, 0xb9, /*0x1*/0);
+	write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12);
+	c = 0xd0;
+	if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE))
+		c |= 0x04;
+	if (s->fmt & (AFMT_S16_LE | AFMT_S8))
+		c |= 0x20;
+	if (s->channels > 1)
+		c ^= 0x48;
+	write_ctrl(s, 0xb7, (c & 0x70) | 1);
+	write_ctrl(s, 0xb7, c);
+	write_ctrl(s, 0xb1, 0x50);
+	write_ctrl(s, 0xb2, 0x50);
+	/* program DAC parameters */
+	c = 0x40;
+	if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE))
+		c |= 1;
+	if (s->fmt & (AFMT_S16_LE | AFMT_S8))
+		c |= 4;
+	if (s->channels > 1)
+		c |= 2;
+	write_mixer(s, 0x7a, c);
+	write_mixer(s, 0x78, 0x10);
+	s->ena = 0;
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != SOLO1_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg)
+{
+	static const unsigned int mixer_src[8] = {
+		SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME,
+		SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0
+	};
+	static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
+		[SOUND_MIXER_PCM]     = 1,   /* voice */
+		[SOUND_MIXER_SYNTH]   = 2,   /* FM */
+		[SOUND_MIXER_CD]      = 3,   /* CD */
+		[SOUND_MIXER_LINE]    = 4,   /* Line */
+		[SOUND_MIXER_LINE1]   = 5,   /* AUX */
+		[SOUND_MIXER_MIC]     = 6,   /* Mic */
+		[SOUND_MIXER_LINE2]   = 7,   /* Mono in */
+		[SOUND_MIXER_SPEAKER] = 8,   /* Speaker */
+		[SOUND_MIXER_RECLEV]  = 9,   /* Recording level */
+		[SOUND_MIXER_VOLUME]  = 10   /* Master Volume */
+	};
+	static const unsigned char mixreg[] = {
+		0x7c,   /* voice */
+		0x36,   /* FM */
+		0x38,   /* CD */
+		0x3e,   /* Line */
+		0x3a,   /* AUX */
+		0x1a,   /* Mic */
+		0x6d    /* Mono in */
+	};
+	unsigned char l, r, rl, rr, vidx;
+	int i, val;
+
+	VALIDATE_STATE(s);
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		/* enable/disable/query mixer preamp */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != -1) {
+			val = val ? 0xff : 0xf7;
+			write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val);
+		}
+		val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0;
+		return put_user(val, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+		/* enable/disable/query spatializer */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != -1) {
+			val &= 0x3f;
+			write_mixer(s, 0x52, val);
+			write_mixer(s, 0x50, val ? 0x08 : 0);
+		}
+		return put_user(read_mixer(s, 0x52), (int *)arg);
+	}
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "Solo1", sizeof(info.id));
+		strncpy(info.name, "ESS Solo1", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "Solo1", sizeof(info.id));
+		strncpy(info.name, "ESS Solo1", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_SIOC_DIR(cmd) == _SIOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_src[read_mixer(s, 0x1c) & 7], (int *)arg);
+
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD |
+					SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC |
+					SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV |
+					SOUND_MASK_SPEAKER, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, (int *)arg);
+
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD |
+					SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC |
+					SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
+                                return -EINVAL;
+			return put_user(s->mix.vol[vidx-1], (int *)arg);
+		}
+	}
+        if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+#if 0
+	        {
+			static const unsigned char regs[] = {
+				0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c
+			};
+			int i;
+			
+			for (i = 0; i < sizeof(regs); i++)
+				printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n",
+				       regs[i], read_mixer(s, regs[i]));
+			printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n",
+			       0xb4, read_ctrl(s, 0xb4));
+		}
+#endif
+	        if (get_user(val, (int *)arg))
+			return -EFAULT;
+                i = hweight32(val);
+                if (i == 0)
+                        return 0;
+                else if (i > 1) 
+                        val &= ~mixer_src[read_mixer(s, 0x1c) & 7];
+		for (i = 0; i < 8; i++) {
+			if (mixer_src[i] & val)
+				break;
+		}
+		if (i > 7)
+			return 0;
+		write_mixer(s, 0x1c, i);
+		return 0;
+
+	case SOUND_MIXER_VOLUME:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		r = (val >> 8) & 0xff;
+		if (r > 100)
+			r = 100;
+		if (l < 6) {
+			rl = 0x40;
+			l = 0;
+		} else {
+			rl = (l * 2 - 11) / 3;
+			l = (rl * 3 + 11) / 2;
+		}
+		if (r < 6) {
+			rr = 0x40;
+			r = 0;
+		} else {
+			rr = (r * 2 - 11) / 3;
+			r = (rr * 3 + 11) / 2;
+		}
+		write_mixer(s, 0x60, rl);
+		write_mixer(s, 0x62, rr);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                s->mix.vol[9] = ((unsigned int)r << 8) | l;
+#else
+                s->mix.vol[9] = val;
+#endif
+		return put_user(s->mix.vol[9], (int *)arg);
+
+	case SOUND_MIXER_SPEAKER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		else if (l < 2)
+			l = 2;
+		rl = (l - 2) / 14;
+		l = rl * 14 + 2;
+		write_mixer(s, 0x3c, rl);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                s->mix.vol[7] = l * 0x101;
+#else
+                s->mix.vol[7] = val;
+#endif
+		return put_user(s->mix.vol[7], (int *)arg);
+
+	case SOUND_MIXER_RECLEV:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = (val << 1) & 0x1fe;
+		if (l > 200)
+			l = 200;
+		else if (l < 5)
+			l = 5;
+		r = (val >> 7) & 0x1fe;
+		if (r > 200)
+			r = 200;
+		else if (r < 5)
+			r = 5;
+		rl = (l - 5) / 13;
+		rr = (r - 5) / 13;
+		r = (rl * 13 + 5) / 2;
+		l = (rr * 13 + 5) / 2;
+		write_ctrl(s, 0xb4, (rl << 4) | rr);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                s->mix.vol[8] = ((unsigned int)r << 8) | l;
+#else
+                s->mix.vol[8] = val;
+#endif
+		return put_user(s->mix.vol[8], (int *)arg);
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
+			return -EINVAL;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = (val << 1) & 0x1fe;
+		if (l > 200)
+			l = 200;
+		else if (l < 5)
+			l = 5;
+		r = (val >> 7) & 0x1fe;
+		if (r > 200)
+			r = 200;
+		else if (r < 5)
+			r = 5;
+		rl = (l - 5) / 13;
+		rr = (r - 5) / 13;
+		r = (rl * 13 + 5) / 2;
+		l = (rr * 13 + 5) / 2;
+		write_mixer(s, mixreg[vidx-1], (rl << 4) | rr);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                s->mix.vol[vidx-1] = ((unsigned int)r << 8) | l;
+#else
+                s->mix.vol[vidx-1] = val;
+#endif
+		return put_user(s->mix.vol[vidx-1], (int *)arg);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int solo1_open_mixdev(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct solo1_state *s = NULL;
+	struct pci_dev *pci_dev;
+
+	pci_for_each_dev(pci_dev) {
+		struct pci_driver *drvr;
+		drvr = pci_dev_driver (pci_dev);
+		if (drvr != &solo1_driver)
+			continue;
+		s = (struct solo1_state*)pci_get_drvdata(pci_dev);
+		if (!s)
+			continue;
+		if (s->dev_mixer == minor)
+			break;
+	}
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	return 0;
+}
+
+static int solo1_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	return 0;
+}
+
+static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations solo1_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		solo1_ioctl_mixdev,
+	open:		solo1_open_mixdev,
+	release:	solo1_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct solo1_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count;
+	unsigned tmo;
+	
+	if (s->dma_dac.mapped)
+		return 0;
+        add_wait_queue(&s->dma_dac.wait, &wait);
+        for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate;
+		if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE))
+			tmo >>= 1;
+		if (s->channels > 1)
+			tmo >>= 1;
+                if (!schedule_timeout(tmo + 1))
+                        printk(KERN_DEBUG "solo1: dma timed out??\n");
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	add_wait_queue(&s->dma_adc.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+#ifdef DEBUGREC
+		printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x  DMAstat: 0x%02x  DMAcnt: 0x%04x  SBstat: 0x%02x  cnt: %u\n", 
+		       read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt);
+#endif
+		if (cnt <= 0) {
+			if (s->dma_adc.enabled)
+				start_adc(s);
+#ifdef DEBUGREC
+			printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x  A2: 0x%02x  A4: 0x%02x  A5: 0x%02x  A8: 0x%02x\n"
+			       KERN_DEBUG "solo1_read: regs: B1: 0x%02x  B2: 0x%02x  B7: 0x%02x  B8: 0x%02x  B9: 0x%02x\n"
+			       KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n"  
+			       KERN_DEBUG "solo1_read: SBstat: 0x%02x  cnt: %u\n",
+			       read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), 
+			       read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), 
+			       inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt);
+#endif
+			if (inb(s->ddmabase+15) & 1)
+				printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n");
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+#ifdef DEBUGREC
+			printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x  A2: 0x%02x  A4: 0x%02x  A5: 0x%02x  A8: 0x%02x\n"
+			       KERN_DEBUG "solo1_read: regs: B1: 0x%02x  B2: 0x%02x  B7: 0x%02x  B8: 0x%02x  B9: 0x%02x\n"
+			       KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n"  
+			       KERN_DEBUG "solo1_read: SBstat: 0x%02x  cnt: %u\n",
+			       read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), 
+			       read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), 
+			       inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt);
+#endif
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_adc.enabled)
+			start_adc(s);
+#ifdef DEBUGREC
+		printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x  DMAstat: 0x%02x  DMAcnt: 0x%04x  SBstat: 0x%02x\n", 
+		       read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc));
+#endif
+	}
+	remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+#if 0
+	printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x  71: 0x%02x  72: 0x%02x  74: 0x%02x  76: 0x%02x  78: 0x%02x  7A: 0x%02x\n"
+	       KERN_DEBUG "solo1_write: DMA: addr: 0x%08x  cnt: 0x%04x  stat: 0x%02x  SBstat: 0x%02x\n", 
+	       read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76),
+	       read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc));
+	printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x  reg 7A: 0x%02x  DMAcnt: 0x%04x  DMAstat: 0x%02x  SBstat: 0x%02x\n", 
+	       read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc));
+#endif
+	ret = 0;
+	add_wait_queue(&s->dma_dac.wait, &wait);	
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize-swptr;
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_dac.enabled)
+				start_dac(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_dac.enabled)
+			start_dac(s);
+	}
+	remove_wait_queue(&s->dma_dac.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		if (!s->dma_dac.ready && prog_dmabuf_dac(s))
+			return 0;
+		poll_wait(file, &s->dma_dac.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!s->dma_adc.ready && prog_dmabuf_adc(s))
+			return 0;
+		poll_wait(file, &s->dma_adc.wait, wait);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	solo1_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+				mask |= POLLIN | POLLRDNORM;
+		} else {
+			if (s->dma_adc.count > 0)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac.dmasize > s->dma_dac.count)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+
+static int solo1_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	struct dmabuf *db;
+	int ret = -EINVAL;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_dac(s)) != 0)
+			goto out;
+		db = &s->dma_dac;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_adc(s)) != 0)
+			goto out;
+		db = &s->dma_adc;
+	} else 
+		goto out;
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		goto out;
+	db->mapped = 1;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret, count;
+        int div1, div2;
+        unsigned rate1, rate2;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		prog_codec(s);
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			stop_adc(s);
+			stop_dac(s);
+			s->dma_adc.ready = s->dma_dac.ready = 0;
+			/* program sampling rates */
+			if (val > 48000)
+				val = 48000;
+			if (val < 6300)
+				val = 6300;
+			div1 = (768000 + val / 2) / val;
+			rate1 = (768000 + div1 / 2) / div1;
+			div1 = -div1;
+			div2 = (793800 + val / 2) / val;
+			rate2 = (793800 + div2 / 2) / div2;
+			div2 = (-div2) & 0x7f;
+			if (abs(val - rate2) < abs(val - rate1)) {
+				rate1 = rate2;
+				div1 = div2;
+			}
+			s->rate = rate1;
+			s->clkdiv = div1;
+			prog_codec(s);
+		}
+		return put_user(s->rate, (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		stop_adc(s);
+		stop_dac(s);
+		s->dma_adc.ready = s->dma_dac.ready = 0;
+		/* program channels */
+		s->channels = val ? 2 : 1;
+		prog_codec(s);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			stop_adc(s);
+			stop_dac(s);
+			s->dma_adc.ready = s->dma_dac.ready = 0;
+			/* program channels */
+			s->channels = (val >= 2) ? 2 : 1;
+			prog_codec(s);
+		}
+		return put_user(s->channels, (int *)arg);
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			stop_adc(s);
+			stop_dac(s);
+			s->dma_adc.ready = s->dma_dac.ready = 0;
+			/* program format */
+			if (val != AFMT_S16_LE && val != AFMT_U16_LE && 
+			    val != AFMT_S8 && val != AFMT_U8)
+				val = AFMT_U8;
+			s->fmt = val;
+			prog_codec(s);
+		}
+		return put_user(s->fmt, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & s->ena & FMODE_READ)
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & s->ena & FMODE_WRITE)
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				s->dma_dac.enabled = 1;
+				start_adc(s);
+				if (inb(s->ddmabase+15) & 1)
+					printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n");
+			} else {
+				s->dma_dac.enabled = 0;
+				stop_adc(s);
+			}
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
+					return ret;
+				s->dma_dac.enabled = 1;
+				start_dac(s);
+			} else {
+				s->dma_dac.enabled = 0;
+				stop_dac(s);
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		solo1_update_ptr(s);
+		abinfo.fragsize = s->dma_dac.fragsize;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac.dmasize - count;
+                abinfo.fragstotal = s->dma_dac.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		solo1_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		solo1_update_ptr(s);
+                count = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		solo1_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		solo1_update_ptr(s);
+                cinfo.bytes = s->dma_dac.total_bytes;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac.fragshift;
+                cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+#if 0
+		printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n"
+		       KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n",
+		       cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift,
+		       s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples);
+#endif
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac(s)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.ossfragshift = val & 0xffff;
+			s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac.ossfragshift < 4)
+				s->dma_dac.ossfragshift = 4;
+			if (s->dma_dac.ossfragshift > 15)
+				s->dma_dac.ossfragshift = 15;
+			if (s->dma_dac.ossmaxfrags < 4)
+				s->dma_dac.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user(s->rate, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user(s->channels, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->fmt & (AFMT_S8|AFMT_U8)) ? 8 : 16, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int solo1_release(struct inode *inode, struct file *file)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		outb(0, s->iobase+6);  /* disable DMA */
+		dealloc_dmabuf(s, &s->dma_dac);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		outb(1, s->ddmabase+0xf); /* mask DMA channel */
+		outb(0, s->ddmabase+0xd); /* DMA master clear */
+		dealloc_dmabuf(s, &s->dma_adc);
+	}
+	s->open_mode &= ~(FMODE_READ | FMODE_WRITE);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static int solo1_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	struct solo1_state *s = NULL;
+	struct pci_dev *pci_dev;
+	
+	pci_for_each_dev(pci_dev) {
+		struct pci_driver *drvr;
+
+		drvr = pci_dev_driver(pci_dev);
+		if (drvr != &solo1_driver)
+			continue;
+		s = (struct solo1_state*)pci_get_drvdata(pci_dev);
+		if (!s)
+			continue;
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (FMODE_READ | FMODE_WRITE)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	s->fmt = AFMT_U8;
+	s->channels = 1;
+	s->rate = 8000;
+	s->clkdiv = 96 | 0x80;
+	s->ena = 0;
+	s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+	s->dma_adc.enabled = 1;
+	s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+	s->dma_dac.enabled = 1;
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	prog_codec(s);
+	return 0;
+}
+
+static /*const*/ struct file_operations solo1_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		solo1_read,
+	write:		solo1_write,
+	poll:		solo1_poll,
+	ioctl:		solo1_ioctl,
+	mmap:		solo1_mmap,
+	open:		solo1_open,
+	release:	solo1_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+/* hold spinlock for the following! */
+static void solo1_handle_midi(struct solo1_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	if (!(s->mpubase))
+		return;
+	wake = 0;
+	while (!(inb(s->mpubase+1) & 0x80)) {
+		ch = inb(s->mpubase);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->mpubase);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+}
+
+static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct solo1_state *s = (struct solo1_state *)dev_id;
+	unsigned int intsrc;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inb(s->iobase+7); /* get interrupt source(s) */
+	if (!intsrc)
+		return;
+	(void)inb(s->sbbase+0xe);  /* clear interrupt */
+	spin_lock(&s->lock);
+	/* clear audio interrupts first */
+	if (intsrc & 0x20)
+		write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f);
+	solo1_update_ptr(s);
+	solo1_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+static void solo1_midi_timer(unsigned long data)
+{
+	struct solo1_state *s = (struct solo1_state *)data;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&s->lock, flags);
+	solo1_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->midi.timer.expires = jiffies+1;
+	add_timer(&s->midi.timer);
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+	add_wait_queue(&s->midi.iwait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		break;
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.iwait, &wait);
+	return ret;
+}
+
+static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			solo1_handle_midi(s);
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		solo1_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.owait, &wait);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_flags & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_flags & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_flags & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_flags & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int solo1_midi_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct solo1_state *s = NULL;
+	struct pci_dev *pci_dev;
+
+	pci_for_each_dev(pci_dev) {
+		struct pci_driver *drvr;
+
+		drvr = pci_dev_driver(pci_dev);
+		if (drvr != &solo1_driver)
+			continue;
+		s = (struct solo1_state*)pci_get_drvdata(pci_dev);
+		if (!s)
+			continue;
+		if (s->dev_midi == minor)
+			break;
+	}
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		outb(0xff, s->mpubase+1); /* reset command */
+		outb(0x3f, s->mpubase+1); /* uart command */
+		if (!(inb(s->mpubase+1) & 0x80))
+			inb(s->mpubase);
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */
+		init_timer(&s->midi.timer);
+		s->midi.timer.expires = jiffies+1;
+		s->midi.timer.data = (unsigned long)s;
+		s->midi.timer.function = solo1_midi_timer;
+		add_timer(&s->midi.timer);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int solo1_midi_release(struct inode *inode, struct file *file)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE) {
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				set_current_state(TASK_RUNNING);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				printk(KERN_DEBUG "solo1: midi timed out??\n");
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		set_current_state(TASK_RUNNING);
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */
+		del_timer(&s->midi.timer);		
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations solo1_midi_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		solo1_midi_read,
+	write:		solo1_midi_write,
+	poll:		solo1_midi_poll,
+	open:		solo1_midi_open,
+	release:	solo1_midi_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	static const unsigned char op_offset[18] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+	};
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	struct dm_fm_voice v;
+	struct dm_fm_note n;
+	struct dm_fm_params p;
+	unsigned int io;
+	unsigned int regb;
+
+	switch (cmd) {		
+	case FM_IOCTL_RESET:
+		for (regb = 0xb0; regb < 0xb9; regb++) {
+			outb(regb, s->sbbase);
+			outb(0, s->sbbase+1);
+			outb(regb, s->sbbase+2);
+			outb(0, s->sbbase+3);
+		}
+		return 0;
+
+	case FM_IOCTL_PLAY_NOTE:
+		if (copy_from_user(&n, (void *)arg, sizeof(n)))
+			return -EFAULT;
+		if (n.voice >= 18)
+			return -EINVAL;
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->sbbase+2;
+		} else {
+			regb = n.voice;
+			io = s->sbbase;
+		}
+		outb(0xa0 + regb, io);
+		outb(n.fnum & 0xff, io+1);
+		outb(0xb0 + regb, io);
+		outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+		return 0;
+
+	case FM_IOCTL_SET_VOICE:
+		if (copy_from_user(&v, (void *)arg, sizeof(v)))
+			return -EFAULT;
+		if (v.voice >= 18)
+			return -EINVAL;
+		regb = op_offset[v.voice];
+		io = s->sbbase + ((v.op & 1) << 1);
+		outb(0x20 + regb, io);
+		outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | 
+		     ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+		outb(0x40 + regb, io);
+		outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+		outb(0x60 + regb, io);
+		outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+		outb(0x80 + regb, io);
+		outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+		outb(0xe0 + regb, io);
+		outb(v.waveform & 0x7, io+1);
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->sbbase+2;
+		} else {
+			regb = n.voice;
+			io = s->sbbase;
+		}
+		outb(0xc0 + regb, io);
+		outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+		     (v.connection & 1), io+1);
+		return 0;
+		
+	case FM_IOCTL_SET_PARAMS:
+		if (copy_from_user(&p, (void *)arg, sizeof(p)))
+			return -EFAULT;
+		outb(0x08, s->sbbase);
+		outb((p.kbd_split & 1) << 6, s->sbbase+1);
+		outb(0xbd, s->sbbase);
+		outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+		     ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1);
+		return 0;
+
+	case FM_IOCTL_SET_OPL:
+		outb(4, s->sbbase+2);
+		outb(arg, s->sbbase+3);
+		return 0;
+
+	case FM_IOCTL_SET_MODE:
+		outb(5, s->sbbase+2);
+		outb(arg & 1, s->sbbase+3);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int solo1_dmfm_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	struct solo1_state *s = NULL;
+	struct pci_dev *pci_dev;
+
+	pci_for_each_dev(pci_dev) {
+		struct pci_driver *drvr;
+
+		drvr = pci_dev_driver(pci_dev);
+		if (drvr != &solo1_driver)
+			continue;
+		s = (struct solo1_state*)pci_get_drvdata(pci_dev);
+		if (!s)
+			continue;
+		if (s->dev_dmfm == minor)
+			break;
+	}
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DMFM) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) {
+		up(&s->open_sem);
+		printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n");
+		return -EBUSY;
+	}
+	/* init the stuff */
+	outb(1, s->sbbase);
+	outb(0x20, s->sbbase+1); /* enable waveforms */
+	outb(4, s->sbbase+2);
+	outb(0, s->sbbase+3);  /* no 4op enabled */
+	outb(5, s->sbbase+2);
+	outb(1, s->sbbase+3);  /* enable OPL3 */
+	s->open_mode |= FMODE_DMFM;
+	up(&s->open_sem);
+	return 0;
+}
+
+static int solo1_dmfm_release(struct inode *inode, struct file *file)
+{
+	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	unsigned int regb;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	down(&s->open_sem);
+	s->open_mode &= ~FMODE_DMFM;
+	for (regb = 0xb0; regb < 0xb9; regb++) {
+		outb(regb, s->sbbase);
+		outb(0, s->sbbase+1);
+		outb(regb, s->sbbase+2);
+		outb(0, s->sbbase+3);
+	}
+	release_region(s->sbbase, FMSYNTH_EXTENT);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations solo1_dmfm_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		solo1_dmfm_ioctl,
+	open:		solo1_dmfm_open,
+	release:	solo1_dmfm_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE2, 0x4040 },
+	{ SOUND_MIXER_WRITE_RECLEV, 0x4040 },
+	{ SOUND_MIXER_WRITE_SPEAKER, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 }
+};
+
+static int setup_solo1(struct solo1_state *s)
+{
+	struct pci_dev *pcidev = s->dev;
+	mm_segment_t fs;
+	int i, val;
+
+	/* initialize DDMA base address */
+	printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase);
+	pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1);
+	/* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */
+	pci_write_config_dword(pcidev, 0x50, 0);
+	/* disable legacy audio address decode */
+	pci_write_config_word(pcidev, 0x40, 0x907f);
+
+	/* initialize the chips */
+	if (!reset_ctrl(s)) {
+		printk(KERN_ERR "esssolo1: cannot reset controller\n");
+		return -1;
+	}
+	outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */
+	
+	/* initialize mixer regs */
+	write_mixer(s, 0x7f, 0); /* disable music digital recording */
+	write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */
+	write_mixer(s, 0x64, 0x45); /* volume control */
+	write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */
+	write_mixer(s, 0x50, 0);  /* disable spatializer */
+	write_mixer(s, 0x52, 0);
+	write_mixer(s, 0x14, 0);  /* DAC1 minimum volume */
+	write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */
+	outb(0, s->ddmabase+0xd); /* DMA master clear */
+	outb(1, s->ddmabase+0xf); /* mask channel */
+	/*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */
+
+	pci_set_master(pcidev);  /* enable bus mastering */
+	
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE;
+	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+	for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+	}
+	val = 1; /* enable mic preamp */
+	mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val);
+	set_fs(fs);
+	return 0;
+}
+
+static int
+solo1_suspend(struct pci_dev *pci_dev, u32 state) {
+	struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev);
+	if (!s)
+		return 1;
+	outb(0, s->iobase+6);
+	/* DMA master clear */
+	outb(0, s->ddmabase+0xd); 
+	/* reset sequencer and FIFO */
+	outb(3, s->sbbase+6); 
+	/* turn off DDMA controller address space */
+	pci_write_config_word(s->dev, 0x60, 0); 
+	return 0;
+}
+
+static int
+solo1_resume(struct pci_dev *pci_dev) {
+	struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev);
+	if (!s)
+		return 1;
+	setup_solo1(s);
+	return 0;
+}
+
+static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+{
+	struct solo1_state *s;
+	int ret;
+
+ 	if ((ret=pci_enable_device(pcidev)))
+		return ret;
+	if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) ||
+	    !(pci_resource_flags(pcidev, 1) & IORESOURCE_IO) ||
+	    !(pci_resource_flags(pcidev, 2) & IORESOURCE_IO) ||
+	    !(pci_resource_flags(pcidev, 3) & IORESOURCE_IO))
+		return -ENODEV;
+	if (pcidev->irq == 0)
+		return -ENODEV;
+
+	/* Recording requires 24-bit DMA, so attempt to set dma mask
+	 * to 24 bits first, then 32 bits (playback only) if that fails.
+	 */
+	if (pci_set_dma_mask(pcidev, 0x00ffffff) &&
+	    pci_set_dma_mask(pcidev, 0xffffffff)) {
+		printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n");
+		return -ENODEV;
+	}
+
+	if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) {
+		printk(KERN_WARNING "solo1: out of memory\n");
+		return -ENOMEM;
+	}
+	memset(s, 0, sizeof(struct solo1_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+	s->magic = SOLO1_MAGIC;
+	s->dev = pcidev;
+	s->iobase = pci_resource_start(pcidev, 0);
+	s->sbbase = pci_resource_start(pcidev, 1);
+	s->vcbase = pci_resource_start(pcidev, 2);
+	s->ddmabase = s->vcbase + DDMABASE_OFFSET;
+	s->mpubase = pci_resource_start(pcidev, 3);
+	s->gameport.io = pci_resource_start(pcidev, 4);
+	s->irq = pcidev->irq;
+	ret = -EBUSY;
+	if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) {
+		printk(KERN_ERR "solo1: io ports in use\n");
+		goto err_region1;
+	}
+	if (!request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1")) {
+		printk(KERN_ERR "solo1: io ports in use\n");
+		goto err_region2;
+	}
+	if (!request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1")) {
+		printk(KERN_ERR "solo1: io ports in use\n");
+		goto err_region3;
+	}
+	if (!request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1")) {
+		printk(KERN_ERR "solo1: io ports in use\n");
+		goto err_region4;
+	}
+	if (s->gameport.io && !request_region(s->gameport.io, GAMEPORT_EXTENT, "ESS Solo1")) {
+		printk(KERN_ERR "solo1: gameport io ports in use\n");
+		s->gameport.io = 0;
+	}
+	if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) {
+		printk(KERN_ERR "solo1: irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+	printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1);
+	/* register devices */
+	if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) {
+		ret = s->dev_audio;
+		goto err_dev1;
+	}
+	if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) {
+		ret = s->dev_mixer;
+		goto err_dev2;
+	}
+	if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) {
+		ret = s->dev_midi;
+		goto err_dev3;
+	}
+	if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) {
+		ret = s->dev_dmfm;
+		goto err_dev4;
+	}
+	if (setup_solo1(s)) {
+		ret = -EIO;
+		goto err;
+	}
+	/* register gameport */
+	gameport_register_port(&s->gameport);
+	/* store it in the driver field */
+	pci_set_drvdata(pcidev, s);
+	return 0;
+
+ err:
+	unregister_sound_dsp(s->dev_dmfm);
+ err_dev4:
+	unregister_sound_dsp(s->dev_midi);
+ err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR "solo1: initialisation error\n");
+	free_irq(s->irq, s);
+ err_irq:
+	if (s->gameport.io)
+		release_region(s->gameport.io, GAMEPORT_EXTENT);
+	release_region(s->iobase, IOBASE_EXTENT);
+ err_region4:
+	release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
+ err_region3:
+	release_region(s->ddmabase, DDMABASE_EXTENT);
+ err_region2:
+	release_region(s->mpubase, MPUBASE_EXTENT);
+ err_region1:
+	kfree(s);
+	return ret;
+}
+
+static void __devinit solo1_remove(struct pci_dev *dev)
+{
+	struct solo1_state *s = pci_get_drvdata(dev);
+	
+	if (!s)
+		return;
+	/* stop DMA controller */
+	outb(0, s->iobase+6);
+	outb(0, s->ddmabase+0xd); /* DMA master clear */
+	outb(3, s->sbbase+6); /* reset sequencer and FIFO */
+	synchronize_irq();
+	pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */
+	free_irq(s->irq, s);
+	if (s->gameport.io) {
+		gameport_unregister_port(&s->gameport);
+		release_region(s->gameport.io, GAMEPORT_EXTENT);
+	}
+	release_region(s->iobase, IOBASE_EXTENT);
+	release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
+	release_region(s->ddmabase, DDMABASE_EXTENT);
+	release_region(s->mpubase, MPUBASE_EXTENT);
+	unregister_sound_dsp(s->dev_audio);
+	unregister_sound_mixer(s->dev_mixer);
+	unregister_sound_midi(s->dev_midi);
+	unregister_sound_special(s->dev_dmfm);
+	kfree(s);
+	pci_set_drvdata(dev, NULL);
+}
+
+static struct pci_device_id id_table[] __devinitdata = {
+	{ PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver solo1_driver = {
+	name: "ESS Solo1",
+	id_table: id_table,
+	probe: solo1_probe,
+	remove: solo1_remove,
+	suspend: solo1_suspend,
+	resume: solo1_resume
+};
+
+
+static int __init init_solo1(void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "solo1: version v0.19 time " __TIME__ " " __DATE__ "\n");
+	if (!pci_register_driver(&solo1_driver)) {
+		pci_unregister_driver(&solo1_driver);
+                return -ENODEV;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("ESS Solo1 Driver");
+MODULE_LICENSE("GPL");
+
+
+static void __exit cleanup_solo1(void)
+{
+	printk(KERN_INFO "solo1: unloading\n");
+	pci_unregister_driver(&solo1_driver);
+}
+
+/* --------------------------------------------------------------------- */
+
+module_init(init_solo1);
+module_exit(cleanup_solo1);
+
diff -Nru linux/sound/oss/gus.h linux-2.4.19-pre5-mjc/sound/oss/gus.h
--- linux/sound/oss/gus.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,30 @@
+/*
+ * gus.h
+ *
+ * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
+ *
+ */
+
+#include "ad1848.h"
+
+/*	From gus_card.c */
+int gus_set_midi_irq(int num);
+void gusintr(int irq, void *dev_id, struct pt_regs * dummy);
+
+/*	From gus_wave.c */
+int gus_wave_detect(int baseaddr);
+void gus_wave_init(struct address_info *hw_config);
+void gus_wave_unload (struct address_info *hw_config);
+void gus_voice_irq(void);
+void gus_write8(int reg, unsigned int data);
+void guswave_dma_irq(void);
+void gus_delay(void);
+int gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg);
+void gus_timer_command (unsigned int addr, unsigned int val);
+
+/*	From gus_midi.c */
+void gus_midi_init(struct address_info *hw_config);
+void gus_midi_interrupt(int dummy);
+
+/*	From ics2101.c */
+int ics2101_mixer_init(void);
diff -Nru linux/sound/oss/gus_card.c linux-2.4.19-pre5-mjc/sound/oss/gus_card.c
--- linux/sound/oss/gus_card.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus_card.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,298 @@
+/*
+ * sound/gus_card.c
+ *
+ * Detection routine for the Gravis Ultrasound.
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ *
+ * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious
+ *                    usage of CS4231A codec, GUS wave and MIDI for GUS MAX.
+ * Christoph Hellwig: Adapted to module_init/module_exit, simple cleanups.
+ *
+ * Status:
+ *              Tested... 
+ */
+      
+ 
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+
+#include "gus.h"
+#include "gus_hw.h"
+
+void            gusintr(int irq, void *dev_id, struct pt_regs *dummy);
+
+int             gus_base = 0, gus_irq = 0, gus_dma = 0;
+int             gus_no_wave_dma = 0; 
+extern int      gus_wave_volume;
+extern int      gus_pcm_volume;
+extern int      have_gus_max;
+int             gus_pnp_flag = 0;
+#ifdef CONFIG_SOUND_ALSA_GUS16
+static int      db16 = 0;	/* Has a Gus16 AD1848 on it */
+#endif
+
+static void __init attach_gus(struct address_info *hw_config)
+{
+	gus_wave_init(hw_config);
+
+	request_region(hw_config->io_base, 16, "GUS");
+	request_region(hw_config->io_base + 0x100, 12, "GUS");	/* 0x10c-> is MAX */
+
+	if (sound_alloc_dma(hw_config->dma, "GUS"))
+		printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma);
+	if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
+		if (sound_alloc_dma(hw_config->dma2, "GUS(2)"))
+			printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2);
+	gus_midi_init(hw_config);
+	if(request_irq(hw_config->irq, gusintr, 0,  "Gravis Ultrasound", hw_config)<0)
+		printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq);
+
+	return;
+}
+
+static int __init probe_gus(struct address_info *hw_config)
+{
+	int             irq;
+	int             io_addr;
+
+	if (hw_config->card_subtype == 1)
+		gus_pnp_flag = 1;
+
+	irq = hw_config->irq;
+
+	if (hw_config->card_subtype == 0)	/* GUS/MAX/ACE */
+		if (irq != 3 && irq != 5 && irq != 7 && irq != 9 &&
+		    irq != 11 && irq != 12 && irq != 15)
+		  {
+			  printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq);
+			  return 0;
+		  }
+	if (check_region(hw_config->io_base, 16))
+		printk(KERN_ERR "GUS: I/O range conflict (1)\n");
+	else if (check_region(hw_config->io_base + 0x100, 16))
+		printk(KERN_ERR "GUS: I/O range conflict (2)\n");
+	else if (gus_wave_detect(hw_config->io_base))
+		return 1;
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+	/*
+	 * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+	 */
+
+	for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+		if (io_addr != hw_config->io_base)	/*
+							 * Already tested
+							 */
+			if (!check_region(io_addr, 16))
+				if (!check_region(io_addr + 0x100, 16))
+					if (gus_wave_detect(io_addr))
+					  {
+						  hw_config->io_base = io_addr;
+						  return 1;
+					  }
+#endif
+
+	printk("NO GUS card found !\n");
+	return 0;
+}
+
+static void __exit unload_gus(struct address_info *hw_config)
+{
+	DDB(printk("unload_gus(%x)\n", hw_config->io_base));
+
+	gus_wave_unload(hw_config);
+
+	release_region(hw_config->io_base, 16);
+	release_region(hw_config->io_base + 0x100, 12);		/* 0x10c-> is MAX */
+	free_irq(hw_config->irq, hw_config);
+
+	sound_free_dma(hw_config->dma);
+
+	if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
+		sound_free_dma(hw_config->dma2);
+}
+
+void gusintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	unsigned char src;
+	extern int gus_timer_enabled;
+
+	sti();
+
+#ifdef CONFIG_SOUND_ALSA_GUSMAX
+	if (have_gus_max) {
+		struct address_info *hw_config = dev_id;
+		adintr(irq, (void *)hw_config->slots[1], NULL);
+	}
+#endif
+#ifdef CONFIG_SOUND_ALSA_GUS16
+	if (db16) {
+		struct address_info *hw_config = dev_id;
+		adintr(irq, (void *)hw_config->slots[3], NULL);
+	}
+#endif
+
+	while (1)
+	{
+		if (!(src = inb(u_IrqStatus)))
+			return;
+
+		if (src & DMA_TC_IRQ)
+		{
+			guswave_dma_irq();
+		}
+		if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
+		{
+			gus_midi_interrupt(0);
+		}
+		if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
+		{
+			if (gus_timer_enabled)
+				sound_timer_interrupt();
+			gus_write8(0x45, 0);	/* Ack IRQ */
+			gus_timer_command(4, 0x80);		/* Reset IRQ flags */
+		}
+		if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+			gus_voice_irq();
+	}
+}
+
+/*
+ *	Some extra code for the 16 bit sampling option
+ */
+
+#ifdef CONFIG_SOUND_ALSA_GUS16
+
+static int __init probe_gus_db16(struct address_info *hw_config)
+{
+	return ad1848_detect(hw_config->io_base, NULL, hw_config->osp);
+}
+
+static void __init attach_gus_db16(struct address_info *hw_config)
+{
+	gus_pcm_volume = 100;
+	gus_wave_volume = 90;
+
+	hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base,
+					  hw_config->irq,
+					  hw_config->dma,
+					  hw_config->dma, 0,
+					  hw_config->osp,
+					  THIS_MODULE);
+}
+
+static void __exit unload_gus_db16(struct address_info *hw_config)
+{
+
+	ad1848_unload(hw_config->io_base,
+		      hw_config->irq,
+		      hw_config->dma,
+		      hw_config->dma, 0);
+	sound_unload_audiodev(hw_config->slots[3]);
+}
+#endif
+
+#ifdef CONFIG_SOUND_ALSA_GUS16
+static int gus16 = 0;
+#endif
+#ifdef CONFIG_SOUND_ALSA_GUSMAX
+static int no_wave_dma = 0;/* Set if no dma is to be used for the
+                                   wave table (GF1 chip) */
+#endif
+
+
+/*
+ *    Note DMA2 of -1 has the right meaning in the GUS driver as well
+ *      as here. 
+ */
+
+static struct address_info cfg;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma16 = -1;	/* Set this for modules that need it */
+static int __initdata type = 0;		/* 1 for PnP */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(dma, "i");
+MODULE_PARM(dma16, "i");
+MODULE_PARM(type, "i");
+#ifdef CONFIG_SOUND_ALSA_GUSMAX
+MODULE_PARM(no_wave_dma, "i");
+#endif
+#ifdef CONFIG_SOUND_ALSA_GUS16
+MODULE_PARM(db16, "i");
+MODULE_PARM(gus16, "i");
+#endif
+MODULE_LICENSE("GPL");
+
+static int __init init_gus(void)
+{
+	printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma16;
+	cfg.card_subtype = type;
+#ifdef CONFIG_SOUND_ALSA_GUSMAX
+	gus_no_wave_dma = no_wave_dma;
+#endif
+
+	if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
+		printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n");
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_SOUND_ALSA_GUS16
+	if (probe_gus_db16(&cfg) && gus16) {
+		/* FIXME: This can't work, can it ? -- Christoph */
+		attach_gus_db16(&cfg);
+		db16 = 1;
+	}	
+#endif
+	if (!probe_gus(&cfg))
+		return -ENODEV;
+	attach_gus(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_gus(void)
+{
+#ifdef CONFIG_SOUND_ALSA_GUS16
+	if (db16)
+		unload_gus_db16(&cfg);
+#endif
+	unload_gus(&cfg);
+}
+
+module_init(init_gus);
+module_exit(cleanup_gus);
+
+#ifndef MODULE
+static int __init setup_gus(char *str)
+{
+	/* io, irq, dma, dma2 */
+	int ints[5];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma16	= ints[4];
+
+	return 1;
+}
+
+__setup("gus=", setup_gus);
+#endif
diff -Nru linux/sound/oss/gus_hw.h linux-2.4.19-pre5-mjc/sound/oss/gus_hw.h
--- linux/sound/oss/gus_hw.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus_hw.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,50 @@
+
+/*
+ * I/O addresses
+ */
+
+#define u_Base			(gus_base + 0x000)
+#define u_Mixer			u_Base
+#define u_Status		(gus_base + 0x006)
+#define u_TimerControl		(gus_base + 0x008)
+#define u_TimerData		(gus_base + 0x009)
+#define u_IRQDMAControl		(gus_base + 0x00b)
+#define u_MidiControl		(gus_base + 0x100)
+#define 	MIDI_RESET		0x03
+#define		MIDI_ENABLE_XMIT	0x20
+#define		MIDI_ENABLE_RCV		0x80
+#define u_MidiStatus		u_MidiControl
+#define		MIDI_RCV_FULL		0x01
+#define 	MIDI_XMIT_EMPTY		0x02
+#define 	MIDI_FRAME_ERR		0x10
+#define 	MIDI_OVERRUN		0x20
+#define 	MIDI_IRQ_PEND		0x80
+#define u_MidiData		(gus_base + 0x101)
+#define u_Voice			(gus_base + 0x102)
+#define u_Command		(gus_base + 0x103)
+#define u_DataLo		(gus_base + 0x104)
+#define u_DataHi		(gus_base + 0x105)
+#define u_MixData               (gus_base + 0x106)   /* Rev. 3.7+ mixing */
+#define u_MixSelect             (gus_base + 0x506)   /* registers.       */
+#define u_IrqStatus		u_Status
+#	define MIDI_TX_IRQ		0x01	/* pending MIDI xmit IRQ */
+#	define MIDI_RX_IRQ		0x02	/* pending MIDI recv IRQ */
+#	define GF1_TIMER1_IRQ		0x04	/* general purpose timer */
+#	define GF1_TIMER2_IRQ		0x08	/* general purpose timer */
+#	define WAVETABLE_IRQ		0x20	/* pending wavetable IRQ */
+#	define ENVELOPE_IRQ		0x40	/* pending volume envelope IRQ */
+#	define DMA_TC_IRQ		0x80	/* pending dma tc IRQ */
+
+#define ICS2101		1
+#	define ICS_MIXDEVS	6
+#	define DEV_MIC		0
+#	define DEV_LINE		1
+#	define DEV_CD		2
+#	define DEV_GF1		3
+#	define DEV_UNUSED	4
+#	define DEV_VOL		5
+
+#	define CHN_LEFT		0
+#	define CHN_RIGHT	1
+#define CS4231		2
+#define u_DRAMIO		(gus_base + 0x107)
diff -Nru linux/sound/oss/gus_linearvol.h linux-2.4.19-pre5-mjc/sound/oss/gus_linearvol.h
--- linux/sound/oss/gus_linearvol.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus_linearvol.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,18 @@
+static unsigned short gus_linearvol[128] = {
+ 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0,
+ 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0,
+ 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70,
+ 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0,
+ 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38,
+ 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78,
+ 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8,
+ 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8,
+ 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c,
+ 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c,
+ 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c,
+ 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c,
+ 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c,
+ 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc,
+ 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc,
+ 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc
+};
diff -Nru linux/sound/oss/gus_midi.c linux-2.4.19-pre5-mjc/sound/oss/gus_midi.c
--- linux/sound/oss/gus_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus_midi.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,262 @@
+/*
+ * sound/gus2_midi.c
+ *
+ * The low level driver for the GUS Midi Interface.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added __init to gus_midi_init()
+ */
+
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include "gus.h"
+#include "gus_hw.h"
+
+static int      midi_busy = 0, input_opened = 0;
+static int      my_dev;
+static int      output_used = 0;
+static volatile unsigned char gus_midi_control;
+
+static void     (*midi_input_intr) (int dev, unsigned char data);
+
+static unsigned char tmp_queue[256];
+extern int      gus_pnp_flag;
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+extern int      gus_base, gus_irq, gus_dma;
+extern int     *gus_osp;
+
+static int GUS_MIDI_STATUS(void)
+{
+	return inb(u_MidiStatus);
+}
+
+static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev))
+{
+	if (midi_busy)
+	{
+/*		printk("GUS: Midi busy\n");*/
+		return -EBUSY;
+	}
+	outb((MIDI_RESET), u_MidiControl);
+	gus_delay();
+
+	gus_midi_control = 0;
+	input_opened = 0;
+
+	if (mode == OPEN_READ || mode == OPEN_READWRITE)
+		if (!gus_pnp_flag)
+		{
+			gus_midi_control |= MIDI_ENABLE_RCV;
+			input_opened = 1;
+		}
+	outb((gus_midi_control), u_MidiControl);	/* Enable */
+
+	midi_busy = 1;
+	qlen = qhead = qtail = output_used = 0;
+	midi_input_intr = input;
+
+	return 0;
+}
+
+static int dump_to_midi(unsigned char midi_byte)
+{
+	unsigned long   flags;
+	int             ok = 0;
+
+	output_used = 1;
+
+	save_flags(flags);
+	cli();
+
+	if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY)
+	{
+		ok = 1;
+		outb((midi_byte), u_MidiData);
+	}
+	else
+	{
+		/*
+		 * Enable Midi xmit interrupts (again)
+		 */
+		gus_midi_control |= MIDI_ENABLE_XMIT;
+		outb((gus_midi_control), u_MidiControl);
+	}
+
+	restore_flags(flags);
+	return ok;
+}
+
+static void gus_midi_close(int dev)
+{
+	/*
+	 * Reset FIFO pointers, disable intrs
+	 */
+
+	outb((MIDI_RESET), u_MidiControl);
+	midi_busy = 0;
+}
+
+static int gus_midi_out(int dev, unsigned char midi_byte)
+{
+	unsigned long   flags;
+
+	/*
+	 * Drain the local queue first
+	 */
+
+	save_flags(flags);
+	cli();
+
+	while (qlen && dump_to_midi(tmp_queue[qhead]))
+	{
+		qlen--;
+		qhead++;
+	}
+	restore_flags(flags);
+
+	/*
+	 *	Output the byte if the local queue is empty.
+	 */
+
+	if (!qlen)
+		if (dump_to_midi(midi_byte))
+			return 1;	/*
+					 * OK
+					 */
+
+	/*
+	 *	Put to the local queue
+	 */
+
+	if (qlen >= 256)
+		return 0;	/*
+				 * Local queue full
+				 */
+	save_flags(flags);
+	cli();
+
+	tmp_queue[qtail] = midi_byte;
+	qlen++;
+	qtail++;
+
+	restore_flags(flags);
+	return 1;
+}
+
+static int gus_midi_start_read(int dev)
+{
+	return 0;
+}
+
+static int gus_midi_end_read(int dev)
+{
+	return 0;
+}
+
+static void gus_midi_kick(int dev)
+{
+}
+
+static int gus_midi_buffer_status(int dev)
+{
+	unsigned long   flags;
+
+	if (!output_used)
+		return 0;
+
+	save_flags(flags);
+	cli();
+
+	if (qlen && dump_to_midi(tmp_queue[qhead]))
+	{
+		qlen--;
+		qhead++;
+	}
+	restore_flags(flags);
+	return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY);
+}
+
+#define MIDI_SYNTH_NAME	"Gravis Ultrasound Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations gus_midi_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		gus_midi_open,
+	close:		gus_midi_close,
+	outputc:	gus_midi_out,
+	start_read:	gus_midi_start_read,
+	end_read:	gus_midi_end_read,
+	kick:		gus_midi_kick,
+	buffer_status:	gus_midi_buffer_status,
+};
+
+void __init gus_midi_init(struct address_info *hw_config)
+{
+	int dev = sound_alloc_mididev();
+
+	if (dev == -1)
+	{
+		printk(KERN_INFO "gus_midi: Too many midi devices detected\n");
+		return;
+	}
+	outb((MIDI_RESET), u_MidiControl);
+
+	std_midi_synth.midi_dev = my_dev = dev;
+	hw_config->slots[2] = dev;
+	midi_devs[dev] = &gus_midi_operations;
+	sequencer_init();
+	return;
+}
+
+void gus_midi_interrupt(int dummy)
+{
+	volatile unsigned char stat, data;
+	unsigned long flags;
+	int timeout = 10;
+
+	save_flags(flags);
+	cli();
+
+	while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY))
+	{
+		if (stat & MIDI_RCV_FULL)
+		{
+			data = inb(u_MidiData);
+			if (input_opened)
+				midi_input_intr(my_dev, data);
+		}
+		if (stat & MIDI_XMIT_EMPTY)
+		{
+			while (qlen && dump_to_midi(tmp_queue[qhead]))
+			{
+				qlen--;
+				qhead++;
+			}
+			if (!qlen)
+			{
+			      /*
+			       * Disable Midi output interrupts, since no data in the buffer
+			       */
+			      gus_midi_control &= ~MIDI_ENABLE_XMIT;
+			      outb((gus_midi_control), u_MidiControl);
+			      outb((gus_midi_control), u_MidiControl);
+			}
+		}
+	}
+	restore_flags(flags);
+}
diff -Nru linux/sound/oss/gus_vol.c linux-2.4.19-pre5-mjc/sound/oss/gus_vol.c
--- linux/sound/oss/gus_vol.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus_vol.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,153 @@
+
+/*
+ * gus_vol.c - Compute volume for GUS.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include "sound_config.h"
+
+#include "gus.h"
+#include "gus_linearvol.h"
+
+#define GUS_VOLUME	gus_wave_volume
+
+
+extern int      gus_wave_volume;
+
+/*
+ * Calculate gus volume from note velocity, main volume, expression, and
+ * intrinsic patch volume given in patch library.  Expression is multiplied
+ * in, so it emphasizes differences in note velocity, while main volume is
+ * added in -- I don't know whether this is right, but it seems reasonable to
+ * me.  (In the previous stage, main volume controller messages were changed
+ * to expression controller messages, if they were found to be used for
+ * dynamic volume adjustments, so here, main volume can be assumed to be
+ * constant throughout a song.)
+ *
+ * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so
+ * we can give a big boost to very weak voices like nylon guitar and the
+ * basses.  The normal value is 64.  Strings are assigned lower values.
+ */
+
+unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev)
+{
+	int i, m, n, x;
+
+
+	/*
+	 * A voice volume of 64 is considered neutral, so adjust the main volume if
+	 * something other than this neutral value was assigned in the patch
+	 * library.
+	 */
+	x = 256 + 6 * (voicev - 64);
+
+	/*
+	 * Boost expression by voice volume above neutral.
+	 */
+	 
+	if (voicev > 65)
+		xpn += voicev - 64;
+	xpn += (voicev - 64) / 2;
+
+	/*
+	 * Combine multiplicative and level components.
+	 */
+	x = vel * xpn * 6 + (voicev / 4) * x;
+
+#ifdef GUS_VOLUME
+	/*
+	 * Further adjustment by installation-specific master volume control
+	 * (default 60).
+	 */
+	x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+	/*
+	 * Experimental support for the channel main volume
+	 */
+
+	mainv = (mainv / 2) + 64;	/* Scale to 64 to 127 */
+	x = (x * mainv * mainv) / 16384;
+#endif
+
+	if (x < 2)
+		return (0);
+	else if (x >= 65535)
+		return ((15 << 8) | 255);
+
+	/*
+	 * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit
+	 * mantissa m.
+	 */
+	 
+	n = x;
+	i = 7;
+	if (n < 128)
+	{
+		  while (i > 0 && n < (1 << i))
+			  i--;
+	}
+	else
+	{
+		while (n > 255)
+		{
+			  n >>= 1;
+			  i++;
+		}
+	}
+	/*
+	 * Mantissa is part of linear volume not expressed in exponent.  (This is
+	 * not quite like real logs -- I wonder if it's right.)
+	 */
+	m = x - (1 << i);
+
+	/*
+	 * Adjust mantissa to 8 bits.
+	 */
+	if (m > 0)
+	{
+		if (i > 8)
+			m >>= i - 8;
+		else if (i < 8)
+			m <<= 8 - i;
+	}
+	return ((i << 8) + m);
+}
+
+/*
+ * Volume-values are interpreted as linear values. Volume is based on the
+ * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in)
+ * and the volume set by the mixer-device (default 60%).
+ */
+
+unsigned short gus_linear_vol(int vol, int mainvol)
+{
+	int mixer_mainvol;
+
+	if (vol <= 0)
+		vol = 0;
+	else if (vol >= 127)
+		vol = 127;
+
+#ifdef GUS_VOLUME
+	mixer_mainvol = GUS_VOLUME;
+#else
+	mixer_mainvol = 100;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+	if (mainvol <= 0)
+		mainvol = 0;
+	else if (mainvol >= 127)
+		mainvol = 127;
+#else
+	mainvol = 127;
+#endif
+	return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100];
+}
diff -Nru linux/sound/oss/gus_wave.c linux-2.4.19-pre5-mjc/sound/oss/gus_wave.c
--- linux/sound/oss/gus_wave.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/gus_wave.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,3540 @@
+/*
+ * sound/gus_wave.c
+ *
+ * Driver for the Gravis UltraSound wave table synth.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer    : ioctl code reworked (vmalloc/vfree removed)
+ * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious
+ *                    usage of CS4231A codec, GUS wave and MIDI for GUS MAX.
+ * Bartlomiej Zolnierkiewicz : added some __init/__exit
+ */
+ 
+#include <linux/init.h> 
+#include <linux/config.h>
+
+#define GUSPNP_AUTODETECT
+
+#include "sound_config.h"
+#include <linux/ultrasound.h>
+
+#include "gus.h"
+#include "gus_hw.h"
+
+#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))
+
+#define MAX_SAMPLE	150
+#define MAX_PATCH	256
+
+#define NOT_SAMPLE	0xffff
+
+struct voice_info
+{
+	unsigned long   orig_freq;
+	unsigned long   current_freq;
+	unsigned long   mode;
+	int             fixed_pitch;
+	int             bender;
+	int             bender_range;
+	int             panning;
+	int             midi_volume;
+	unsigned int    initial_volume;
+	unsigned int    current_volume;
+	int             loop_irq_mode, loop_irq_parm;
+#define LMODE_FINISH		1
+#define LMODE_PCM		2
+#define LMODE_PCM_STOP		3
+	int             volume_irq_mode, volume_irq_parm;
+#define VMODE_HALT		1
+#define VMODE_ENVELOPE		2
+#define VMODE_START_NOTE	3
+
+	int             env_phase;
+	unsigned char   env_rate[6];
+	unsigned char   env_offset[6];
+
+	/*
+	 * Volume computation parameters for gus_adagio_vol()
+	 */
+	int		main_vol, expression_vol, patch_vol;
+
+	/* Variables for "Ultraclick" removal */
+	int             dev_pending, note_pending, volume_pending,
+	                sample_pending;
+	char            kill_pending;
+	long            offset_pending;
+
+};
+
+static struct voice_alloc_info *voice_alloc;
+static struct address_info *gus_hw_config;
+extern int      gus_base;
+extern int      gus_irq, gus_dma;
+extern int      gus_pnp_flag;
+extern int      gus_no_wave_dma;
+static int      gus_dma2 = -1;
+static int      dual_dma_mode = 0;
+static long     gus_mem_size = 0;
+static long     free_mem_ptr = 0;
+static int      gus_busy = 0;
+static int      gus_no_dma = 0;
+static int      nr_voices = 0;
+static int      gus_devnum = 0;
+static int      volume_base, volume_scale, volume_method;
+static int      gus_recmask = SOUND_MASK_MIC;
+static int      recording_active = 0;
+static int      only_read_access = 0;
+static int      only_8_bits = 0;
+
+int             iw_mode = 0;
+int             gus_wave_volume = 60;
+int             gus_pcm_volume = 80;
+int             have_gus_max = 0;
+static int      gus_line_vol = 100, gus_mic_vol = 0;
+static unsigned char mix_image = 0x00;
+
+int             gus_timer_enabled = 0;
+
+/*
+ * Current version of this driver doesn't allow synth and PCM functions
+ * at the same time. The active_device specifies the active driver
+ */
+
+static int      active_device = 0;
+
+#define GUS_DEV_WAVE		1	/* Wave table synth */
+#define GUS_DEV_PCM_DONE	2	/* PCM device, transfer done */
+#define GUS_DEV_PCM_CONTINUE	3	/* PCM device, transfer done ch. 1/2 */
+
+static int      gus_audio_speed;
+static int      gus_audio_channels;
+static int      gus_audio_bits;
+static int      gus_audio_bsize;
+static char     bounce_buf[8 * 1024];	/* Must match value set to max_fragment */
+
+static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper);
+
+/*
+ * Variables and buffers for PCM output
+ */
+
+#define MAX_PCM_BUFFERS		(128*MAX_REALTIME_FACTOR)	/* Don't change */
+
+static int      pcm_bsize, pcm_nblk, pcm_banksize;
+static int      pcm_datasize[MAX_PCM_BUFFERS];
+static volatile int pcm_head, pcm_tail, pcm_qlen;
+static volatile int pcm_active;
+static volatile int dma_active;
+static int      pcm_opened = 0;
+static int      pcm_current_dev;
+static int      pcm_current_block;
+static unsigned long pcm_current_buf;
+static int      pcm_current_count;
+static int      pcm_current_intrflag;
+
+extern int     *gus_osp;
+
+static struct voice_info voices[32];
+
+static int      freq_div_table[] =
+{
+	44100,			/* 14 */
+	41160,			/* 15 */
+	38587,			/* 16 */
+	36317,			/* 17 */
+	34300,			/* 18 */
+	32494,			/* 19 */
+	30870,			/* 20 */
+	29400,			/* 21 */
+	28063,			/* 22 */
+	26843,			/* 23 */
+	25725,			/* 24 */
+	24696,			/* 25 */
+	23746,			/* 26 */
+	22866,			/* 27 */
+	22050,			/* 28 */
+	21289,			/* 29 */
+	20580,			/* 30 */
+	19916,			/* 31 */
+	19293			/* 32 */
+};
+
+static struct patch_info *samples = NULL;
+static long     sample_ptrs[MAX_SAMPLE + 1];
+static int      sample_map[32];
+static int      free_sample;
+static int      mixer_type = 0;
+
+
+static int      patch_table[MAX_PATCH];
+static int      patch_map[32];
+
+static struct synth_info gus_info = {
+	"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 
+	0, 16, 0, MAX_PATCH
+};
+
+static void     gus_poke(long addr, unsigned char data);
+static void     compute_and_set_volume(int voice, int volume, int ramp_time);
+extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev);
+extern unsigned short gus_linear_vol(int vol, int mainvol);
+static void     compute_volume(int voice, int volume);
+static void     do_volume_irq(int voice);
+static void     set_input_volumes(void);
+static void     gus_tmr_install(int io_base);
+
+#define	INSTANT_RAMP		-1	/* Instant change. No ramping */
+#define FAST_RAMP		0	/* Fastest possible ramp */
+
+static void reset_sample_memory(void)
+{
+	int i;
+
+	for (i = 0; i <= MAX_SAMPLE; i++)
+		sample_ptrs[i] = -1;
+	for (i = 0; i < 32; i++)
+		sample_map[i] = -1;
+	for (i = 0; i < 32; i++)
+		patch_map[i] = -1;
+
+	gus_poke(0, 0);		/* Put a silent sample to the beginning */
+	gus_poke(1, 0);
+	free_mem_ptr = 2;
+
+	free_sample = 0;
+
+	for (i = 0; i < MAX_PATCH; i++)
+		patch_table[i] = NOT_SAMPLE;
+}
+
+void gus_delay(void)
+{
+	int i;
+
+	for (i = 0; i < 7; i++)
+		inb(u_DRAMIO);
+}
+
+static void gus_poke(long addr, unsigned char data)
+{				/* Writes a byte to the DRAM */
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	outb((0x43), u_Command);
+	outb((addr & 0xff), u_DataLo);
+	outb(((addr >> 8) & 0xff), u_DataHi);
+
+	outb((0x44), u_Command);
+	outb(((addr >> 16) & 0xff), u_DataHi);
+	outb((data), u_DRAMIO);
+	restore_flags(flags);
+}
+
+static unsigned char gus_peek(long addr)
+{				/* Reads a byte from the DRAM */
+	unsigned long   flags;
+	unsigned char   tmp;
+
+	save_flags(flags);
+	cli();
+	outb((0x43), u_Command);
+	outb((addr & 0xff), u_DataLo);
+	outb(((addr >> 8) & 0xff), u_DataHi);
+
+	outb((0x44), u_Command);
+	outb(((addr >> 16) & 0xff), u_DataHi);
+	tmp = inb(u_DRAMIO);
+	restore_flags(flags);
+
+	return tmp;
+}
+
+void gus_write8(int reg, unsigned int data)
+{				/* Writes to an indirect register (8 bit) */
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+
+	outb((reg), u_Command);
+	outb(((unsigned char) (data & 0xff)), u_DataHi);
+
+	restore_flags(flags);
+}
+
+static unsigned char gus_read8(int reg)
+{				
+	/* Reads from an indirect register (8 bit). Offset 0x80. */
+	unsigned long   flags;
+	unsigned char   val;
+
+	save_flags(flags);
+	cli();
+	outb((reg | 0x80), u_Command);
+	val = inb(u_DataHi);
+	restore_flags(flags);
+
+	return val;
+}
+
+static unsigned char gus_look8(int reg)
+{
+	/* Reads from an indirect register (8 bit). No additional offset. */
+	unsigned long   flags;
+	unsigned char   val;
+
+	save_flags(flags);
+	cli();
+	outb((reg), u_Command);
+	val = inb(u_DataHi);
+	restore_flags(flags);
+
+	return val;
+}
+
+static void gus_write16(int reg, unsigned int data)
+{
+	/* Writes to an indirect register (16 bit) */
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+
+	outb((reg), u_Command);
+
+	outb(((unsigned char) (data & 0xff)), u_DataLo);
+	outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi);
+
+	restore_flags(flags);
+}
+
+static unsigned short gus_read16(int reg)
+{
+	/* Reads from an indirect register (16 bit). Offset 0x80. */
+	unsigned long   flags;
+	unsigned char   hi, lo;
+
+	save_flags(flags);
+	cli();
+
+	outb((reg | 0x80), u_Command);
+
+	lo = inb(u_DataLo);
+	hi = inb(u_DataHi);
+
+	restore_flags(flags);
+
+	return ((hi << 8) & 0xff00) | lo;
+}
+
+static unsigned short gus_look16(int reg)
+{		
+	/* Reads from an indirect register (16 bit). No additional offset. */
+	unsigned long   flags;
+	unsigned char   hi, lo;
+
+	save_flags(flags);
+	cli();
+
+	outb((reg), u_Command);
+
+	lo = inb(u_DataLo);
+	hi = inb(u_DataHi);
+
+	restore_flags(flags);
+
+	return ((hi << 8) & 0xff00) | lo;
+}
+
+static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit)
+{
+	/* Writes an 24 bit memory address */
+	unsigned long   hold_address;
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	if (is16bit)
+	{
+		if (iw_mode)
+		{
+			/* Interwave spesific address translations */
+			address >>= 1;
+		}
+		else
+		{
+			/*
+			 * Special processing required for 16 bit patches
+			 */
+
+			hold_address = address;
+			address = address >> 1;
+			address &= 0x0001ffffL;
+			address |= (hold_address & 0x000c0000L);
+		}
+	}
+	gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff));
+	gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff)
+		    + (frac << 5));
+	/* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */
+	gus_delay();
+	gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff));
+	gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff)
+		    + (frac << 5));
+	restore_flags(flags);
+}
+
+static void gus_select_voice(int voice)
+{
+	if (voice < 0 || voice > 31)
+		return;
+	outb((voice), u_Voice);
+}
+
+static void gus_select_max_voices(int nvoices)
+{
+	if (iw_mode)
+		nvoices = 32;
+	if (nvoices < 14)
+		nvoices = 14;
+	if (nvoices > 32)
+		nvoices = 32;
+
+	voice_alloc->max_voice = nr_voices = nvoices;
+	gus_write8(0x0e, (nvoices - 1) | 0xc0);
+}
+
+static void gus_voice_on(unsigned int mode)
+{
+	gus_write8(0x00, (unsigned char) (mode & 0xfc));
+	gus_delay();
+	gus_write8(0x00, (unsigned char) (mode & 0xfc));
+}
+
+static void gus_voice_off(void)
+{
+	gus_write8(0x00, gus_read8(0x00) | 0x03);
+}
+
+static void gus_voice_mode(unsigned int m)
+{
+	unsigned char   mode = (unsigned char) (m & 0xff);
+
+	gus_write8(0x00, (gus_read8(0x00) & 0x03) |
+		   (mode & 0xfc));	/* Don't touch last two bits */
+	gus_delay();
+	gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc));
+}
+
+static void gus_voice_freq(unsigned long freq)
+{
+	unsigned long   divisor = freq_div_table[nr_voices - 14];
+	unsigned short  fc;
+
+	/* Interwave plays at 44100 Hz with any number of voices */
+	if (iw_mode)
+		fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100);
+	else
+		fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+	fc = fc << 1;
+
+	gus_write16(0x01, fc);
+}
+
+static void gus_voice_volume(unsigned int vol)
+{
+	gus_write8(0x0d, 0x03);	/* Stop ramp before setting volume */
+	gus_write16(0x09, (unsigned short) (vol << 4));
+}
+
+static void gus_voice_balance(unsigned int balance)
+{
+	gus_write8(0x0c, (unsigned char) (balance & 0xff));
+}
+
+static void gus_ramp_range(unsigned int low, unsigned int high)
+{
+	gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff));
+	gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff));
+}
+
+static void gus_ramp_rate(unsigned int scale, unsigned int rate)
+{
+	gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
+}
+
+static void gus_rampon(unsigned int m)
+{
+	unsigned char   mode = (unsigned char) (m & 0xff);
+
+	gus_write8(0x0d, mode & 0xfc);
+	gus_delay();
+	gus_write8(0x0d, mode & 0xfc);
+}
+
+static void gus_ramp_mode(unsigned int m)
+{
+	unsigned char mode = (unsigned char) (m & 0xff);
+
+	gus_write8(0x0d, (gus_read8(0x0d) & 0x03) |
+		   (mode & 0xfc));	/* Leave the last 2 bits alone */
+	gus_delay();
+	gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc));
+}
+
+static void gus_rampoff(void)
+{
+	gus_write8(0x0d, 0x03);
+}
+
+static void gus_set_voice_pos(int voice, long position)
+{
+	int sample_no;
+
+	if ((sample_no = sample_map[voice]) != -1) {
+		if (position < samples[sample_no].len) {
+			if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+				voices[voice].offset_pending = position;
+			else
+				gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0,
+				 samples[sample_no].mode & WAVE_16_BITS);
+		}
+	}
+}
+
+static void gus_voice_init(int voice)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+	gus_voice_volume(0);
+	gus_voice_off();
+	gus_write_addr(0x0a, 0, 0, 0);	/* Set current position to 0 */
+	gus_write8(0x00, 0x03);	/* Voice off */
+	gus_write8(0x0d, 0x03);	/* Ramping off */
+	voice_alloc->map[voice] = 0;
+	voice_alloc->alloc_times[voice] = 0;
+	restore_flags(flags);
+
+}
+
+static void gus_voice_init2(int voice)
+{
+	voices[voice].panning = 0;
+	voices[voice].mode = 0;
+	voices[voice].orig_freq = 20000;
+	voices[voice].current_freq = 20000;
+	voices[voice].bender = 0;
+	voices[voice].bender_range = 200;
+	voices[voice].initial_volume = 0;
+	voices[voice].current_volume = 0;
+	voices[voice].loop_irq_mode = 0;
+	voices[voice].loop_irq_parm = 0;
+	voices[voice].volume_irq_mode = 0;
+	voices[voice].volume_irq_parm = 0;
+	voices[voice].env_phase = 0;
+	voices[voice].main_vol = 127;
+	voices[voice].patch_vol = 127;
+	voices[voice].expression_vol = 127;
+	voices[voice].sample_pending = -1;
+	voices[voice].fixed_pitch = 0;
+}
+
+static void step_envelope(int voice)
+{
+	unsigned        vol, prev_vol, phase;
+	unsigned char   rate;
+	long int        flags;
+
+	if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+	{
+		save_flags(flags);
+		cli();
+		gus_select_voice(voice);
+		gus_rampoff();
+		restore_flags(flags);
+		return;
+		/*
+		 * Sustain phase begins. Continue envelope after receiving note off.
+		 */
+	}
+	if (voices[voice].env_phase >= 5)
+	{
+		/* Envelope finished. Shoot the voice down */
+		gus_voice_init(voice);
+		return;
+	}
+	prev_vol = voices[voice].current_volume;
+	phase = ++voices[voice].env_phase;
+	compute_volume(voice, voices[voice].midi_volume);
+	vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
+	rate = voices[voice].env_rate[phase];
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+
+	gus_voice_volume(prev_vol);
+
+
+	gus_write8(0x06, rate);	/* Ramping rate */
+
+	voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+	if (((vol - prev_vol) / 64) == 0)	/* No significant volume change */
+	{
+		restore_flags(flags);
+		step_envelope(voice);		/* Continue the envelope on the next step */
+		return;
+	}
+	if (vol > prev_vol)
+	{
+		if (vol >= (4096 - 64))
+			vol = 4096 - 65;
+		gus_ramp_range(0, vol);
+		gus_rampon(0x20);	/* Increasing volume, with IRQ */
+	}
+	else
+	{
+		if (vol <= 64)
+			vol = 65;
+		gus_ramp_range(vol, 4030);
+		gus_rampon(0x60);	/* Decreasing volume, with IRQ */
+	}
+	voices[voice].current_volume = vol;
+	restore_flags(flags);
+}
+
+static void init_envelope(int voice)
+{
+	voices[voice].env_phase = -1;
+	voices[voice].current_volume = 64;
+
+	step_envelope(voice);
+}
+
+static void start_release(int voice, long int flags)
+{
+	if (gus_read8(0x00) & 0x03)
+		return;		/* Voice already stopped */
+
+	voices[voice].env_phase = 2;	/* Will be incremented by step_envelope */
+
+	voices[voice].current_volume = voices[voice].initial_volume =
+						gus_read16(0x09) >> 4;	/* Get current volume */
+
+	voices[voice].mode &= ~WAVE_SUSTAIN_ON;
+	gus_rampoff();
+	restore_flags(flags);
+	step_envelope(voice);
+}
+
+static void gus_voice_fade(int voice)
+{
+	int instr_no = sample_map[voice], is16bits;
+	long int flags;
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+
+	if (instr_no < 0 || instr_no > MAX_SAMPLE)
+	{
+		gus_write8(0x00, 0x03);	/* Hard stop */
+		voice_alloc->map[voice] = 0;
+		restore_flags(flags);
+		return;
+	}
+	is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0;	/* 8 or 16 bits */
+
+	if (voices[voice].mode & WAVE_ENVELOPES)
+	{
+		start_release(voice, flags);
+		restore_flags(flags);
+		return;
+	}
+	/*
+	 * Ramp the volume down but not too quickly.
+	 */
+	if ((int) (gus_read16(0x09) >> 4) < 100)	/* Get current volume */
+	{
+		gus_voice_off();
+		gus_rampoff();
+		gus_voice_init(voice);
+		restore_flags(flags);
+		return;
+	}
+	gus_ramp_range(65, 4030);
+	gus_ramp_rate(2, 4);
+	gus_rampon(0x40 | 0x20);	/* Down, once, with IRQ */
+	voices[voice].volume_irq_mode = VMODE_HALT;
+	restore_flags(flags);
+}
+
+static void gus_reset(void)
+{
+	int i;
+
+	gus_select_max_voices(24);
+	volume_base = 3071;
+	volume_scale = 4;
+	volume_method = VOL_METHOD_ADAGIO;
+
+	for (i = 0; i < 32; i++)
+	{
+		gus_voice_init(i);	/* Turn voice off */
+		gus_voice_init2(i);
+	}
+}
+
+static void gus_initialize(void)
+{
+	unsigned long flags;
+	unsigned char dma_image, irq_image, tmp;
+
+	static unsigned char gus_irq_map[16] = 	{
+		0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7
+	};
+
+	static unsigned char gus_dma_map[8] = {
+		0, 1, 0, 2, 0, 3, 4, 5
+	};
+
+	save_flags(flags);
+	cli();
+	gus_write8(0x4c, 0);	/* Reset GF1 */
+	gus_delay();
+	gus_delay();
+
+	gus_write8(0x4c, 1);	/* Release Reset */
+	gus_delay();
+	gus_delay();
+
+	/*
+	 * Clear all interrupts
+	 */
+
+	gus_write8(0x41, 0);	/* DMA control */
+	gus_write8(0x45, 0);	/* Timer control */
+	gus_write8(0x49, 0);	/* Sample control */
+
+	gus_select_max_voices(24);
+
+	inb(u_Status);		/* Touch the status register */
+
+	gus_look8(0x41);	/* Clear any pending DMA IRQs */
+	gus_look8(0x49);	/* Clear any pending sample IRQs */
+	gus_read8(0x0f);	/* Clear pending IRQs */
+
+	gus_reset();		/* Resets all voices */
+
+	gus_look8(0x41);	/* Clear any pending DMA IRQs */
+	gus_look8(0x49);	/* Clear any pending sample IRQs */
+	gus_read8(0x0f);	/* Clear pending IRQs */
+
+	gus_write8(0x4c, 7);	/* Master reset | DAC enable | IRQ enable */
+
+	/*
+	 * Set up for Digital ASIC
+	 */
+
+	outb((0x05), gus_base + 0x0f);
+
+	mix_image |= 0x02;	/* Disable line out (for a moment) */
+	outb((mix_image), u_Mixer);
+
+	outb((0x00), u_IRQDMAControl);
+
+	outb((0x00), gus_base + 0x0f);
+
+	/*
+	 * Now set up the DMA and IRQ interface
+	 *
+	 * The GUS supports two IRQs and two DMAs.
+	 *
+	 * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
+	 * Adding this support requires significant changes to the dmabuf.c, dsp.c
+	 * and audio.c also.
+	 */
+
+	irq_image = 0;
+	tmp = gus_irq_map[gus_irq];
+	if (!gus_pnp_flag && !tmp)
+		printk(KERN_WARNING "Warning! GUS IRQ not selected\n");
+	irq_image |= tmp;
+	irq_image |= 0x40;	/* Combine IRQ1 (GF1) and IRQ2 (Midi) */
+
+	dual_dma_mode = 1;
+	if (gus_dma2 == gus_dma || gus_dma2 == -1)
+	{
+		dual_dma_mode = 0;
+		dma_image = 0x40;	/* Combine DMA1 (DRAM) and IRQ2 (ADC) */
+
+		tmp = gus_dma_map[gus_dma];
+		if (!tmp)
+			printk(KERN_WARNING "Warning! GUS DMA not selected\n");
+
+		dma_image |= tmp;
+	}
+	else
+	{
+		/* Setup dual DMA channel mode for GUS MAX */
+
+		dma_image = gus_dma_map[gus_dma];
+		if (!dma_image)
+			printk(KERN_WARNING "Warning! GUS DMA not selected\n");
+
+		tmp = gus_dma_map[gus_dma2] << 3;
+		if (!tmp)
+		{
+			printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n");
+			tmp = 0x40;		/* Combine DMA channels */
+			    dual_dma_mode = 0;
+		}
+		dma_image |= tmp;
+	}
+
+	/*
+	 * For some reason the IRQ and DMA addresses must be written twice
+	 */
+
+	/*
+	 * Doing it first time
+	 */
+
+	outb((mix_image), u_Mixer);	/* Select DMA control */
+	outb((dma_image | 0x80), u_IRQDMAControl);	/* Set DMA address */
+
+	outb((mix_image | 0x40), u_Mixer);	/* Select IRQ control */
+	outb((irq_image), u_IRQDMAControl);	/* Set IRQ address */
+
+	/*
+	 * Doing it second time
+	 */
+
+	outb((mix_image), u_Mixer);	/* Select DMA control */
+	outb((dma_image), u_IRQDMAControl);	/* Set DMA address */
+
+	outb((mix_image | 0x40), u_Mixer);	/* Select IRQ control */
+	outb((irq_image), u_IRQDMAControl);	/* Set IRQ address */
+
+	gus_select_voice(0);	/* This disables writes to IRQ/DMA reg */
+
+	mix_image &= ~0x02;	/* Enable line out */
+	mix_image |= 0x08;	/* Enable IRQ */
+	outb((mix_image), u_Mixer);	/*
+					 * Turn mixer channels on
+					 * Note! Mic in is left off.
+					 */
+
+	gus_select_voice(0);	/* This disables writes to IRQ/DMA reg */
+
+	gusintr(gus_irq, (void *)gus_hw_config, NULL);	/* Serve pending interrupts */
+
+	inb(u_Status);		/* Touch the status register */
+
+	gus_look8(0x41);	/* Clear any pending DMA IRQs */
+	gus_look8(0x49);	/* Clear any pending sample IRQs */
+
+	gus_read8(0x0f);	/* Clear pending IRQs */
+
+	if (iw_mode)
+		gus_write8(0x19, gus_read8(0x19) | 0x01);
+	restore_flags(flags);
+}
+
+
+static void __init pnp_mem_init(void)
+{
+#include "iwmem.h"
+#define CHUNK_SIZE (256*1024)
+#define BANK_SIZE (4*1024*1024)
+#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE)
+
+	int bank, chunk, addr, total = 0;
+	int bank_sizes[4];
+	int i, j, bits = -1, testbits = -1, nbanks = 0;
+
+	/*
+	 * This routine determines what kind of RAM is installed in each of the four
+	 * SIMM banks and configures the DRAM address decode logic accordingly.
+	 */
+
+	/*
+	 *    Place the chip into enhanced mode
+	 */
+	gus_write8(0x19, gus_read8(0x19) | 0x01);
+	gus_write8(0x53, gus_look8(0x53) & ~0x02);	/* Select DRAM I/O access */
+
+	/*
+	 * Set memory configuration to 4 DRAM banks of 4M in each (16M total).
+	 */
+
+	gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c);
+
+	/*
+	 * Perform the DRAM size detection for each bank individually.
+	 */
+	for (bank = 0; bank < 4; bank++)
+	{
+		int size = 0;
+
+		addr = bank * BANK_SIZE;
+
+		/* Clean check points of each chunk */
+		for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
+		{
+			gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00);
+			gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00);
+		}
+
+		/* Write a value to each chunk point and verify the result */
+		for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
+		{
+			gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55);
+			gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA);
+
+			if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 &&
+				gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA)
+			{
+				/* OK. There is RAM. Now check for possible shadows */
+				int ok = 1, chunk2;
+
+				for (chunk2 = 0; ok && chunk2 < chunk; chunk2++)
+					if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) ||
+							gus_peek(addr + chunk2 * CHUNK_SIZE + 1L))
+						ok = 0;	/* Addressing wraps */
+
+				if (ok)
+					size = (chunk + 1) * CHUNK_SIZE;
+			}
+			gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00);
+			gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00);
+		}
+		bank_sizes[bank] = size;
+		if (size)
+			nbanks = bank + 1;
+		DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024));
+	}
+
+	if (nbanks == 0)	/* No RAM - Give up */
+	{
+		printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n");
+		printk(KERN_ERR "Sound: Unable to work with this card.\n");
+		gus_write8(0x19, gus_read8(0x19) & ~0x01);
+		gus_mem_size = 0;
+		return;
+	}
+
+	/*
+	 * Now we know how much DRAM there is in each bank. The next step is
+	 * to find a DRAM size encoding (0 to 12) which is best for the combination
+	 * we have.
+	 *
+	 * First try if any of the possible alternatives matches exactly the amount
+	 * of memory we have.
+	 */
+
+	for (i = 0; bits == -1 && i < 13; i++)
+	{
+		bits = i;
+
+		for (j = 0; bits != -1 && j < 4; j++)
+			if (mem_decode[i][j] != bank_sizes[j])
+				bits = -1;	/* No hit */
+	}
+
+	/*
+	 * If necessary, try to find a combination where other than the last
+	 * bank matches our configuration and the last bank is left oversized.
+	 * In this way we don't leave holes in the middle of memory.
+	 */
+
+	if (bits == -1)		/* No luck yet */
+	{
+		for (i = 0; bits == -1 && i < 13; i++)
+		{
+			bits = i;
+
+			for (j = 0; bits != -1 && j < nbanks - 1; j++)
+				if (mem_decode[i][j] != bank_sizes[j])
+					bits = -1;	/* No hit */
+			if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1])
+				bits = -1;	/* The last bank is too small */
+		}
+	}
+	/*
+ 	 * The last resort is to search for a combination where the banks are
+ 	 * smaller than the actual SIMMs. This leaves some memory in the banks
+ 	 * unused but doesn't leave holes in the DRAM address space.
+ 	 */
+ 	if (bits == -1)		/* No luck yet */
+ 	{
+ 		for (i = 0; i < 13; i++)
+ 		{
+ 			testbits = i;
+ 			for (j = 0; testbits != -1 && j < nbanks - 1; j++)
+ 				if (mem_decode[i][j] > bank_sizes[j]) {
+ 					testbits = -1;
+ 				}
+ 			if(testbits > bits) bits = testbits;
+ 		}
+ 		if (bits != -1)
+ 		{
+			printk(KERN_INFO "Interwave: Can't use all installed RAM.\n");
+			printk(KERN_INFO "Interwave: Try reordering SIMMS.\n");
+		}
+		printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n");
+		printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n");
+		bits = 0;
+	}
+	DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits));
+
+	for (bank = 0; bank < 4; bank++)
+	{
+		DDB(printk("  Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024));
+
+		if (bank_sizes[bank] > mem_decode[bits][bank])
+			total += mem_decode[bits][bank];
+		else
+			total += bank_sizes[bank];
+	}
+
+	DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024));
+
+	/*
+	 *    Set the memory addressing mode.
+	 */
+	gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits);
+
+/*      Leave the chip into enhanced mode. Disable LFO  */
+	gus_mem_size = total;
+	iw_mode = 1;
+	gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02);
+}
+
+int __init gus_wave_detect(int baseaddr)
+{
+	unsigned long   i, max_mem = 1024L;
+	unsigned long   loc;
+	unsigned char   val;
+
+	gus_base = baseaddr;
+
+	gus_write8(0x4c, 0);	/* Reset GF1 */
+	gus_delay();
+	gus_delay();
+
+	gus_write8(0x4c, 1);	/* Release Reset */
+	gus_delay();
+	gus_delay();
+
+#ifdef GUSPNP_AUTODETECT
+	val = gus_look8(0x5b);	/* Version number register */
+	gus_write8(0x5b, ~val);	/* Invert all bits */
+
+	if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0))	/* No change */
+	{
+		if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f))	/* Change */
+		{
+			DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4));
+			gus_pnp_flag = 1;
+		}
+		else
+		{
+			DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b)));
+			gus_pnp_flag = 0;
+		}
+	}
+	gus_write8(0x5b, val);	/* Restore all bits */
+#endif
+
+	if (gus_pnp_flag)
+		pnp_mem_init();
+	if (iw_mode)
+		return 1;
+
+	/* See if there is first block there.... */
+	gus_poke(0L, 0xaa);
+	if (gus_peek(0L) != 0xaa)
+		return (0);
+
+	/* Now zero it out so that I can check for mirroring .. */
+	gus_poke(0L, 0x00);
+	for (i = 1L; i < max_mem; i++)
+	{
+		int n, failed;
+
+		/* check for mirroring ... */
+		if (gus_peek(0L) != 0)
+			break;
+		loc = i << 10;
+
+		for (n = loc - 1, failed = 0; n <= loc; n++)
+		{
+			gus_poke(loc, 0xaa);
+			if (gus_peek(loc) != 0xaa)
+				failed = 1;
+			gus_poke(loc, 0x55);
+			if (gus_peek(loc) != 0x55)
+				failed = 1;
+		}
+		if (failed)
+			break;
+	}
+	gus_mem_size = i << 10;
+	return 1;
+}
+
+static int guswave_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+
+	switch (cmd) 
+	{
+		case SNDCTL_SYNTH_INFO:
+			gus_info.nr_voices = nr_voices;
+			if (copy_to_user(arg, &gus_info, sizeof(gus_info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_SEQ_RESETSAMPLES:
+			reset_sample_memory();
+			return 0;
+
+		case SNDCTL_SEQ_PERCMODE:
+			return 0;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static int guswave_set_instr(int dev, int voice, int instr_no)
+{
+	int sample_no;
+
+	if (instr_no < 0 || instr_no > MAX_PATCH)
+		instr_no = 0;	/* Default to acoustic piano */
+
+	if (voice < 0 || voice > 31)
+		return -EINVAL;
+
+	if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+	{
+		voices[voice].sample_pending = instr_no;
+		return 0;
+	}
+	sample_no = patch_table[instr_no];
+	patch_map[voice] = -1;
+
+	if (sample_no == NOT_SAMPLE)
+	{
+/*		printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/
+		return -EINVAL;	/* Patch not defined */
+	}
+	if (sample_ptrs[sample_no] == -1)	/* Sample not loaded */
+	{
+/*		printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/
+		return -EINVAL;
+	}
+	sample_map[voice] = sample_no;
+	patch_map[voice] = instr_no;
+	return 0;
+}
+
+static int guswave_kill_note(int dev, int voice, int note, int velocity)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* voice_alloc->map[voice] = 0xffff; */
+	if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+	{
+		voices[voice].kill_pending = 1;
+		restore_flags(flags);
+	}
+	else
+	{
+		restore_flags(flags);
+		gus_voice_fade(voice);
+	}
+
+	return 0;
+}
+
+static void guswave_aftertouch(int dev, int voice, int pressure)
+{
+}
+
+static void guswave_panning(int dev, int voice, int value)
+{
+	if (voice >= 0 || voice < 32)
+		voices[voice].panning = value;
+}
+
+static void guswave_volume_method(int dev, int mode)
+{
+	if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
+		volume_method = mode;
+}
+
+static void compute_volume(int voice, int volume)
+{
+	if (volume < 128)
+		voices[voice].midi_volume = volume;
+
+	switch (volume_method)
+	{
+		case VOL_METHOD_ADAGIO:
+			voices[voice].initial_volume =
+				gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol,
+					voices[voice].expression_vol,
+					voices[voice].patch_vol);
+			break;
+
+		case VOL_METHOD_LINEAR:	/* Totally ignores patch-volume and expression */
+			voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol);
+			break;
+
+		default:
+			voices[voice].initial_volume = volume_base +
+				(voices[voice].midi_volume * volume_scale);
+	}
+
+	if (voices[voice].initial_volume > 4030)
+		voices[voice].initial_volume = 4030;
+}
+
+static void compute_and_set_volume(int voice, int volume, int ramp_time)
+{
+	int curr, target, rate;
+	unsigned long flags;
+
+	compute_volume(voice, volume);
+	voices[voice].current_volume = voices[voice].initial_volume;
+
+	save_flags(flags);
+	cli();
+	/*
+	 * CAUTION! Interrupts disabled. Enable them before returning
+	 */
+
+	gus_select_voice(voice);
+
+	curr = gus_read16(0x09) >> 4;
+	target = voices[voice].initial_volume;
+
+	if (ramp_time == INSTANT_RAMP)
+	{
+		gus_rampoff();
+		gus_voice_volume(target);
+		restore_flags(flags);
+		return;
+	}
+	if (ramp_time == FAST_RAMP)
+		rate = 63;
+	else
+		rate = 16;
+	gus_ramp_rate(0, rate);
+
+	if ((target - curr) / 64 == 0)	/* Close enough to target. */
+	{
+		gus_rampoff();
+		gus_voice_volume(target);
+		restore_flags(flags);
+		return;
+	}
+	if (target > curr)
+	{
+		if (target > (4095 - 65))
+			target = 4095 - 65;
+		gus_ramp_range(curr, target);
+		gus_rampon(0x00);	/* Ramp up, once, no IRQ */
+	}
+	else
+	{
+		if (target < 65)
+			target = 65;
+
+		gus_ramp_range(target, curr);
+		gus_rampon(0x40);	/* Ramp down, once, no irq */
+	}
+	restore_flags(flags);
+}
+
+static void dynamic_volume_change(int voice)
+{
+	unsigned char status;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+	status = gus_read8(0x00);	/* Get voice status */
+	restore_flags(flags);
+
+	if (status & 0x03)
+		return;		/* Voice was not running */
+
+	if (!(voices[voice].mode & WAVE_ENVELOPES))
+	{
+		compute_and_set_volume(voice, voices[voice].midi_volume, 1);
+		return;
+	}
+	
+	/*
+	 * Voice is running and has envelopes.
+	 */
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+	status = gus_read8(0x0d);	/* Ramping status */
+	restore_flags(flags);
+
+	if (status & 0x03)	/* Sustain phase? */
+	{
+		compute_and_set_volume(voice, voices[voice].midi_volume, 1);
+		return;
+	}
+	if (voices[voice].env_phase < 0)
+		return;
+
+	compute_volume(voice, voices[voice].midi_volume);
+
+}
+
+static void guswave_controller(int dev, int voice, int ctrl_num, int value)
+{
+	unsigned long   flags;
+	unsigned long   freq;
+
+	if (voice < 0 || voice > 31)
+		return;
+
+	switch (ctrl_num)
+	{
+		case CTRL_PITCH_BENDER:
+			voices[voice].bender = value;
+
+			if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+			{
+				freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0);
+				voices[voice].current_freq = freq;
+
+				save_flags(flags);
+				cli();
+				gus_select_voice(voice);
+				gus_voice_freq(freq);
+				restore_flags(flags);
+			}
+			break;
+
+		case CTRL_PITCH_BENDER_RANGE:
+			voices[voice].bender_range = value;
+			break;
+		case CTL_EXPRESSION:
+			value /= 128;
+		case CTRL_EXPRESSION:
+			if (volume_method == VOL_METHOD_ADAGIO)
+			{
+				voices[voice].expression_vol = value;
+				if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+					dynamic_volume_change(voice);
+			}
+			break;
+
+		case CTL_PAN:
+			voices[voice].panning = (value * 2) - 128;
+			break;
+
+		case CTL_MAIN_VOLUME:
+			value = (value * 100) / 16383;
+
+		case CTRL_MAIN_VOLUME:
+			voices[voice].main_vol = value;
+			if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+				dynamic_volume_change(voice);
+			break;
+
+		default:
+			break;
+	}
+}
+
+static int guswave_start_note2(int dev, int voice, int note_num, int volume)
+{
+	int sample, best_sample, best_delta, delta_freq;
+	int is16bits, samplep, patch, pan;
+	unsigned long   note_freq, base_note, freq, flags;
+	unsigned char   mode = 0;
+
+	if (voice < 0 || voice > 31)
+	{
+/*		printk("GUS: Invalid voice\n");*/
+		return -EINVAL;
+	}
+	if (note_num == 255)
+	{
+		if (voices[voice].mode & WAVE_ENVELOPES)
+		{
+			voices[voice].midi_volume = volume;
+			dynamic_volume_change(voice);
+			return 0;
+		}
+		compute_and_set_volume(voice, volume, 1);
+		return 0;
+	}
+	if ((patch = patch_map[voice]) == -1)
+		return -EINVAL;
+	if ((samplep = patch_table[patch]) == NOT_SAMPLE)
+	{
+		return -EINVAL;
+	}
+	note_freq = note_to_freq(note_num);
+
+	/*
+	 * Find a sample within a patch so that the note_freq is between low_note
+	 * and high_note.
+	 */
+	sample = -1;
+
+	best_sample = samplep;
+	best_delta = 1000000;
+	while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1)
+	{
+		delta_freq = note_freq - samples[samplep].base_note;
+		if (delta_freq < 0)
+			delta_freq = -delta_freq;
+		if (delta_freq < best_delta)
+		{
+			best_sample = samplep;
+			best_delta = delta_freq;
+		}
+		if (samples[samplep].low_note <= note_freq &&
+			note_freq <= samples[samplep].high_note)
+		{
+			sample = samplep;
+		}
+		else
+			samplep = samples[samplep].key;	/* Link to next sample */
+	  }
+	if (sample == -1)
+		sample = best_sample;
+
+	if (sample == -1)
+	{
+/*		printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/
+		return 0;	/* Should play default patch ??? */
+	}
+	is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;
+	voices[voice].mode = samples[sample].mode;
+	voices[voice].patch_vol = samples[sample].volume;
+
+	if (iw_mode)
+		gus_write8(0x15, 0x00);		/* RAM, Reset voice deactivate bit of SMSI */
+
+	if (voices[voice].mode & WAVE_ENVELOPES)
+	{
+		int i;
+
+		for (i = 0; i < 6; i++)
+		{
+			voices[voice].env_rate[i] = samples[sample].env_rate[i];
+			voices[voice].env_offset[i] = samples[sample].env_offset[i];
+		}
+	}
+	sample_map[voice] = sample;
+
+	if (voices[voice].fixed_pitch)	/* Fixed pitch */
+	{
+		  freq = samples[sample].base_freq;
+	}
+	else
+	{
+		base_note = samples[sample].base_note / 100;
+		note_freq /= 100;
+
+		freq = samples[sample].base_freq * note_freq / base_note;
+	}
+
+	voices[voice].orig_freq = freq;
+
+	/*
+	 * Since the pitch bender may have been set before playing the note, we
+	 * have to calculate the bending now.
+	 */
+
+	freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender,
+				voices[voice].bender_range, 0);
+	voices[voice].current_freq = freq;
+
+	pan = (samples[sample].panning + voices[voice].panning) / 32;
+	pan += 7;
+	if (pan < 0)
+		pan = 0;
+	if (pan > 15)
+		pan = 15;
+
+	if (samples[sample].mode & WAVE_16_BITS)
+	{
+		mode |= 0x04;	/* 16 bits */
+		if ((sample_ptrs[sample] / GUS_BANK_SIZE) !=
+			((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE))
+				printk(KERN_ERR "GUS: Sample address error\n");
+	}
+	/*************************************************************************
+	 *    CAUTION!        Interrupts disabled. Don't return before enabling
+	 *************************************************************************/
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+	gus_voice_off();
+	gus_rampoff();
+
+	restore_flags(flags);
+
+	if (voices[voice].mode & WAVE_ENVELOPES)
+	{
+		compute_volume(voice, volume);
+		init_envelope(voice);
+	}
+	else
+	{
+		compute_and_set_volume(voice, volume, 0);
+	}
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+
+	if (samples[sample].mode & WAVE_LOOP_BACK)
+		gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len -
+			voices[voice].offset_pending, 0, is16bits);	/* start=end */
+	else
+		gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits);	/* Sample start=begin */
+
+	if (samples[sample].mode & WAVE_LOOPING)
+	{
+		mode |= 0x08;
+
+		if (samples[sample].mode & WAVE_BIDIR_LOOP)
+			mode |= 0x10;
+
+		if (samples[sample].mode & WAVE_LOOP_BACK)
+		{
+			gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end -
+					   voices[voice].offset_pending,
+					   (samples[sample].fractions >> 4) & 0x0f, is16bits);
+			mode |= 0x40;
+		}
+		gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start,
+			samples[sample].fractions & 0x0f, is16bits);	/* Loop start location */
+		gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end,
+			(samples[sample].fractions >> 4) & 0x0f, is16bits);	/* Loop end location */
+	}
+	else
+	{
+		mode |= 0x20;	/* Loop IRQ at the end */
+		voices[voice].loop_irq_mode = LMODE_FINISH;	/* Ramp down at the end */
+		voices[voice].loop_irq_parm = 1;
+		gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits);	/* Loop start location */
+		gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1,
+			(samples[sample].fractions >> 4) & 0x0f, is16bits);	/* Loop end location */
+	}
+	gus_voice_freq(freq);
+	gus_voice_balance(pan);
+	gus_voice_on(mode);
+	restore_flags(flags);
+
+	return 0;
+}
+
+/*
+ * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking
+ * when the note playing on the voice is changed.  It uses volume
+ * ramping.
+ */
+
+static int guswave_start_note(int dev, int voice, int note_num, int volume)
+{
+	long int flags;
+	int mode;
+	int ret_val = 0;
+
+	save_flags(flags);
+	cli();
+	if (note_num == 255)
+	{
+		if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+		{
+			voices[voice].volume_pending = volume;
+		}
+		else
+		{
+			ret_val = guswave_start_note2(dev, voice, note_num, volume);
+		}
+	}
+	else
+	{
+		gus_select_voice(voice);
+		mode = gus_read8(0x00);
+		if (mode & 0x20)
+			gus_write8(0x00, mode & 0xdf);	/* No interrupt! */
+
+		voices[voice].offset_pending = 0;
+		voices[voice].kill_pending = 0;
+		voices[voice].volume_irq_mode = 0;
+		voices[voice].loop_irq_mode = 0;
+
+		if (voices[voice].sample_pending >= 0)
+		{
+			restore_flags(flags);	/* Run temporarily with interrupts enabled */
+			guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending);
+			voices[voice].sample_pending = -1;
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);	/* Reselect the voice (just to be sure) */
+		}
+		if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065))
+		{
+			ret_val = guswave_start_note2(dev, voice, note_num, volume);
+		}
+		else
+		{
+			voices[voice].dev_pending = dev;
+			voices[voice].note_pending = note_num;
+			voices[voice].volume_pending = volume;
+			voices[voice].volume_irq_mode = VMODE_START_NOTE;
+
+			gus_rampoff();
+			gus_ramp_range(2000, 4065);
+			gus_ramp_rate(0, 63);	/* Fastest possible rate */
+			gus_rampon(0x20 | 0x40);	/* Ramp down, once, irq */
+		}
+	}
+	restore_flags(flags);
+	return ret_val;
+}
+
+static void guswave_reset(int dev)
+{
+	int i;
+
+	for (i = 0; i < 32; i++)
+	{
+		gus_voice_init(i);
+		gus_voice_init2(i);
+	}
+}
+
+static int guswave_open(int dev, int mode)
+{
+	int err;
+
+	if (gus_busy)
+		return -EBUSY;
+
+	voice_alloc->timestamp = 0;
+
+	if (gus_no_wave_dma) {
+		gus_no_dma = 1;
+	} else {
+		if ((err = DMAbuf_open_dma(gus_devnum)) < 0)
+		{
+			/* printk( "GUS: Loading samples without DMA\n"); */
+			gus_no_dma = 1;	/* Upload samples using PIO */
+		}
+		else
+			gus_no_dma = 0;
+	}
+
+	init_waitqueue_head(&dram_sleeper);
+	gus_busy = 1;
+	active_device = GUS_DEV_WAVE;
+
+	gusintr(gus_irq, (void *)gus_hw_config, NULL);	/* Serve pending interrupts */
+	gus_initialize();
+	gus_reset();
+	gusintr(gus_irq, (void *)gus_hw_config, NULL);	/* Serve pending interrupts */
+
+	return 0;
+}
+
+static void guswave_close(int dev)
+{
+	gus_busy = 0;
+	active_device = 0;
+	gus_reset();
+
+	if (!gus_no_dma)
+		DMAbuf_close_dma(gus_devnum);
+}
+
+static int guswave_load_patch(int dev, int format, const char *addr,
+		   int offs, int count, int pmgr_flag)
+{
+	struct patch_info patch;
+	int instr;
+	long sizeof_patch;
+
+	unsigned long blk_sz, blk_end, left, src_offs, target;
+
+	sizeof_patch = (long) &patch.data[0] - (long) &patch;	/* Header size */
+
+	if (format != GUS_PATCH)
+	{
+/*		printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/
+		return -EINVAL;
+	}
+	if (count < sizeof_patch)
+	{
+/*		  printk("GUS Error: Patch header too short\n");*/
+		  return -EINVAL;
+	}
+	count -= sizeof_patch;
+
+	if (free_sample >= MAX_SAMPLE)
+	{
+/*		  printk("GUS: Sample table full\n");*/
+		  return -ENOSPC;
+	}
+	/*
+	 * Copy the header from user space but ignore the first bytes which have
+	 * been transferred already.
+	 */
+
+	copy_from_user(&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs);
+
+	if (patch.mode & WAVE_ROM)
+		return -EINVAL;
+	if (gus_mem_size == 0)
+		return -ENOSPC;
+
+	instr = patch.instr_no;
+
+	if (instr < 0 || instr > MAX_PATCH)
+	{
+/*		printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/
+		return -EINVAL;
+	}
+	if (count < patch.len)
+	{
+/*		printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/
+		patch.len = count;
+	}
+	if (patch.len <= 0 || patch.len > gus_mem_size)
+	{
+/*		printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/
+		return -EINVAL;
+	}
+	if (patch.mode & WAVE_LOOPING)
+	{
+		if (patch.loop_start < 0 || patch.loop_start >= patch.len)
+		{
+/*			printk(KERN_ERR "GUS: Invalid loop start\n");*/
+			return -EINVAL;
+		}
+		if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
+		{
+/*			printk(KERN_ERR "GUS: Invalid loop end\n");*/
+			return -EINVAL;
+		}
+	}
+	free_mem_ptr = (free_mem_ptr + 31) & ~31;	/* 32 byte alignment */
+
+	if (patch.mode & WAVE_16_BITS)
+	{
+		/*
+		 * 16 bit samples must fit one 256k bank.
+		 */
+		if (patch.len >= GUS_BANK_SIZE)
+		{
+/*			 printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/
+			return -ENOSPC;
+		}
+		if ((free_mem_ptr / GUS_BANK_SIZE) !=
+			((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
+		{
+			unsigned long   tmp_mem =	
+				/* Align to 256K */
+					((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
+
+			if ((tmp_mem + patch.len) > gus_mem_size)
+				return -ENOSPC;
+
+			free_mem_ptr = tmp_mem;		/* This leaves unusable memory */
+		}
+	}
+	if ((free_mem_ptr + patch.len) > gus_mem_size)
+		return -ENOSPC;
+
+	sample_ptrs[free_sample] = free_mem_ptr;
+
+	/*
+	 * Tremolo is not possible with envelopes
+	 */
+
+	if (patch.mode & WAVE_ENVELOPES)
+		patch.mode &= ~WAVE_TREMOLO;
+
+	if (!(patch.mode & WAVE_FRACTIONS))
+	{
+		  patch.fractions = 0;
+	}
+	memcpy((char *) &samples[free_sample], &patch, sizeof_patch);
+
+	/*
+	 * Link this_one sample to the list of samples for patch 'instr'.
+	 */
+
+	samples[free_sample].key = patch_table[instr];
+	patch_table[instr] = free_sample;
+
+	/*
+	 * Use DMA to transfer the wave data to the DRAM
+	 */
+
+	left = patch.len;
+	src_offs = 0;
+	target = free_mem_ptr;
+
+	while (left)		/* Not completely transferred yet */
+	{
+		blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use;
+		if (blk_sz > left)
+			blk_sz = left;
+
+		/*
+		 * DMA cannot cross bank (256k) boundaries. Check for that.
+		 */
+		 
+		blk_end = target + blk_sz;
+
+		if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE))
+		{
+			/* Split the block */
+			blk_end &= ~(GUS_BANK_SIZE - 1);
+			blk_sz = blk_end - target;
+		}
+		if (gus_no_dma)
+		{
+			/*
+			 * For some reason the DMA is not possible. We have to use PIO.
+			 */
+			long i;
+			unsigned char data;
+
+			for (i = 0; i < blk_sz; i++)
+			{
+				get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i]));
+				if (patch.mode & WAVE_UNSIGNED)
+					if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
+						data ^= 0x80;	/* Convert to signed */
+				gus_poke(target + i, data);
+			}
+		}
+		else
+		{
+			unsigned long address, hold_address;
+			unsigned char dma_command;
+			unsigned long flags;
+
+			if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL)
+			{
+				printk(KERN_ERR "GUS: DMA buffer == NULL\n");
+				return -ENOSPC;
+			}
+			/*
+			 * OK, move now. First in and then out.
+			 */
+
+			copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz);
+
+			save_flags(flags);
+			cli();
+			/******** INTERRUPTS DISABLED NOW ********/
+			gus_write8(0x41, 0);	/* Disable GF1 DMA */
+			DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys,
+				blk_sz, DMA_MODE_WRITE);
+
+			/*
+			 * Set the DRAM address for the wave data
+			 */
+
+			if (iw_mode)
+			{
+				/* Different address translation in enhanced mode */
+
+				unsigned char   hi;
+
+				if (gus_dma > 4)
+					address = target >> 1;	/* Convert to 16 bit word address */
+				else
+					address = target;
+
+				hi = (unsigned char) ((address >> 16) & 0xf0);
+				hi += (unsigned char) (address & 0x0f);
+
+				gus_write16(0x42, (address >> 4) & 0xffff);	/* DMA address (low) */
+				gus_write8(0x50, hi);
+			}
+			else
+			{
+				address = target;
+				if (audio_devs[gus_devnum]->dmap_out->dma > 3)
+				{
+					hold_address = address;
+					address = address >> 1;
+					address &= 0x0001ffffL;
+					address |= (hold_address & 0x000c0000L);
+				}
+				gus_write16(0x42, (address >> 4) & 0xffff);	/* DRAM DMA address */
+			}
+
+			/*
+			 * Start the DMA transfer
+			 */
+
+			dma_command = 0x21;		/* IRQ enable, DMA start */
+			if (patch.mode & WAVE_UNSIGNED)
+				dma_command |= 0x80;	/* Invert MSB */
+			if (patch.mode & WAVE_16_BITS)
+				dma_command |= 0x40;	/* 16 bit _DATA_ */
+			if (audio_devs[gus_devnum]->dmap_out->dma > 3)
+				dma_command |= 0x04;	/* 16 bit DMA _channel_ */
+			
+			gus_write8(0x41, dma_command);	/* Lets go luteet (=bugs) */
+
+			/*
+			 * Sleep here until the DRAM DMA done interrupt is served
+			 */
+			active_device = GUS_DEV_WAVE;
+
+			if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ))
+				printk("GUS: DMA Transfer timed out\n");
+			restore_flags(flags);
+		}
+
+		/*
+		 * Now the next part
+		 */
+
+		left -= blk_sz;
+		src_offs += blk_sz;
+		target += blk_sz;
+
+		gus_write8(0x41, 0);	/* Stop DMA */
+	}
+
+	free_mem_ptr += patch.len;
+	free_sample++;
+	return 0;
+}
+
+static void guswave_hw_control(int dev, unsigned char *event_rec)
+{
+	int voice, cmd;
+	unsigned short p1, p2;
+	unsigned int plong;
+	unsigned long flags;
+
+	cmd = event_rec[2];
+	voice = event_rec[3];
+	p1 = *(unsigned short *) &event_rec[4];
+	p2 = *(unsigned short *) &event_rec[6];
+	plong = *(unsigned int *) &event_rec[4];
+
+	if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
+		(cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
+		do_volume_irq(voice);
+
+	switch (cmd)
+	{
+		case _GUS_NUMVOICES:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_select_max_voices(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICESAMPLE:
+			guswave_set_instr(dev, voice, p1);
+			break;
+
+		case _GUS_VOICEON:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			p1 &= ~0x20;	/* Don't allow interrupts */
+			gus_voice_on(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICEOFF:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_voice_off();
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICEFADE:
+			gus_voice_fade(voice);
+			break;
+
+		case _GUS_VOICEMODE:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			p1 &= ~0x20;	/* Don't allow interrupts */
+			gus_voice_mode(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICEBALA:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_voice_balance(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICEFREQ:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_voice_freq(plong);
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICEVOL:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_voice_volume(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOICEVOL2:	/* Just update the software voice level */
+			voices[voice].initial_volume = voices[voice].current_volume = p1;
+			break;
+
+		case _GUS_RAMPRANGE:
+			if (voices[voice].mode & WAVE_ENVELOPES)
+				break;	/* NO-NO */
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_ramp_range(p1, p2);
+			restore_flags(flags);
+			break;
+
+		case _GUS_RAMPRATE:
+			if (voices[voice].mode & WAVE_ENVELOPES)
+				break;	/* NJET-NJET */
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_ramp_rate(p1, p2);
+			restore_flags(flags);
+			break;
+
+		case _GUS_RAMPMODE:
+			if (voices[voice].mode & WAVE_ENVELOPES)
+				break;	/* NO-NO */
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			p1 &= ~0x20;	/* Don't allow interrupts */
+			gus_ramp_mode(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_RAMPON:
+			if (voices[voice].mode & WAVE_ENVELOPES)
+				break;	/* EI-EI */
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			p1 &= ~0x20;	/* Don't allow interrupts */
+			gus_rampon(p1);
+			restore_flags(flags);
+			break;
+
+		case _GUS_RAMPOFF:
+			if (voices[voice].mode & WAVE_ENVELOPES)
+				break;	/* NEJ-NEJ */
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_rampoff();
+			restore_flags(flags);
+			break;
+
+		case _GUS_VOLUME_SCALE:
+			volume_base = p1;
+			volume_scale = p2;
+			break;
+
+		case _GUS_VOICE_POS:
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_set_voice_pos(voice, plong);
+			restore_flags(flags);
+			break;
+
+		default:
+			break;
+	}
+}
+
+static int gus_audio_set_speed(int speed)
+{
+	if (speed <= 0)
+		speed = gus_audio_speed;
+
+	if (speed < 4000)
+		speed = 4000;
+
+	if (speed > 44100)
+		speed = 44100;
+
+	gus_audio_speed = speed;
+
+	if (only_read_access)
+	{
+		/* Compute nearest valid recording speed  and return it */
+
+		/* speed = (9878400 / (gus_audio_speed + 2)) / 16; */
+		speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
+		speed = (9878400 / (speed * 16)) - 2;
+	}
+	return speed;
+}
+
+static int gus_audio_set_channels(int channels)
+{
+	if (!channels)
+		return gus_audio_channels;
+	if (channels > 2)
+		channels = 2;
+	if (channels < 1)
+		channels = 1;
+	gus_audio_channels = channels;
+	return channels;
+}
+
+static int gus_audio_set_bits(int bits)
+{
+	if (!bits)
+		return gus_audio_bits;
+
+	if (bits != 8 && bits != 16)
+		bits = 8;
+
+	if (only_8_bits)
+		bits = 8;
+
+	gus_audio_bits = bits;
+	return bits;
+}
+
+static int gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int val;
+
+	switch (cmd) 
+	{
+		case SOUND_PCM_WRITE_RATE:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = gus_audio_set_speed(val);
+			break;
+
+		case SOUND_PCM_READ_RATE:
+			val = gus_audio_speed;
+			break;
+
+		case SNDCTL_DSP_STEREO:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = gus_audio_set_channels(val + 1) - 1;
+			break;
+
+		case SOUND_PCM_WRITE_CHANNELS:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = gus_audio_set_channels(val);
+			break;
+
+		case SOUND_PCM_READ_CHANNELS:
+			val = gus_audio_channels;
+			break;
+		
+		case SNDCTL_DSP_SETFMT:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			val = gus_audio_set_bits(val);
+			break;
+		
+		case SOUND_PCM_READ_BITS:
+			val = gus_audio_bits;
+			break;
+		
+		case SOUND_PCM_WRITE_FILTER:		/* NOT POSSIBLE */
+		case SOUND_PCM_READ_FILTER:
+			val = -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+	}
+	return put_user(val, (int *)arg);
+}
+
+static void gus_audio_reset(int dev)
+{
+	if (recording_active)
+	{
+		gus_write8(0x49, 0x00);	/* Halt recording */
+		set_input_volumes();
+	}
+}
+
+static int saved_iw_mode;	/* A hack hack hack */
+
+static int gus_audio_open(int dev, int mode)
+{
+	if (gus_busy)
+		return -EBUSY;
+
+	if (gus_pnp_flag && mode & OPEN_READ)
+	{
+/*		printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/
+		return -EIO;
+	}
+	gus_initialize();
+
+	gus_busy = 1;
+	active_device = 0;
+
+	saved_iw_mode = iw_mode;
+	if (iw_mode)
+	{
+		/* There are some problems with audio in enhanced mode so disable it */
+		gus_write8(0x19, gus_read8(0x19) & ~0x01);	/* Disable enhanced mode */
+		iw_mode = 0;
+	}
+
+	gus_reset();
+	reset_sample_memory();
+	gus_select_max_voices(14);
+
+	pcm_active = 0;
+	dma_active = 0;
+	pcm_opened = 1;
+	if (mode & OPEN_READ)
+	{
+		recording_active = 1;
+		set_input_volumes();
+	}
+	only_read_access = !(mode & OPEN_WRITE);
+	only_8_bits = mode & OPEN_READ;
+	if (only_8_bits)
+		audio_devs[dev]->format_mask = AFMT_U8;
+	else
+		audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE;
+
+	return 0;
+}
+
+static void gus_audio_close(int dev)
+{
+	iw_mode = saved_iw_mode;
+	gus_reset();
+	gus_busy = 0;
+	pcm_opened = 0;
+	active_device = 0;
+
+	if (recording_active)
+	{
+		gus_write8(0x49, 0x00);	/* Halt recording */
+		set_input_volumes();
+	}
+	recording_active = 0;
+}
+
+static void gus_audio_update_volume(void)
+{
+	unsigned long flags;
+	int voice;
+
+	if (pcm_active && pcm_opened)
+		for (voice = 0; voice < gus_audio_channels; voice++)
+		{
+			save_flags(flags);
+			cli();
+			gus_select_voice(voice);
+			gus_rampoff();
+			gus_voice_volume(1530 + (25 * gus_pcm_volume));
+			gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
+			restore_flags(flags);
+		}
+}
+
+static void play_next_pcm_block(void)
+{
+	unsigned long flags;
+	int speed = gus_audio_speed;
+	int this_one, is16bits, chn;
+	unsigned long dram_loc;
+	unsigned char mode[2], ramp_mode[2];
+
+	if (!pcm_qlen)
+		return;
+
+	this_one = pcm_head;
+
+	for (chn = 0; chn < gus_audio_channels; chn++)
+	{
+		mode[chn] = 0x00;
+		ramp_mode[chn] = 0x03;	/* Ramping and rollover off */
+
+		if (chn == 0)
+		{
+			mode[chn] |= 0x20;	/* Loop IRQ */
+			voices[chn].loop_irq_mode = LMODE_PCM;
+		}
+		if (gus_audio_bits != 8)
+		{
+			is16bits = 1;
+			mode[chn] |= 0x04;	/* 16 bit data */
+		}
+		else
+			is16bits = 0;
+
+		dram_loc = this_one * pcm_bsize;
+		dram_loc += chn * pcm_banksize;
+
+		if (this_one == (pcm_nblk - 1))	/* Last fragment of the DRAM buffer */
+		{
+			mode[chn] |= 0x08;	/* Enable loop */
+			ramp_mode[chn] = 0x03;	/* Disable rollover bit */
+		}
+		else
+		{
+			if (chn == 0)
+				ramp_mode[chn] = 0x04;	/* Enable rollover bit */
+		}
+		save_flags(flags);
+		cli();
+		gus_select_voice(chn);
+		gus_voice_freq(speed);
+
+		if (gus_audio_channels == 1)
+			gus_voice_balance(7);		/* mono */
+		else if (chn == 0)
+			gus_voice_balance(0);		/* left */
+		else
+			gus_voice_balance(15);		/* right */
+
+		if (!pcm_active)	/* Playback not already active */
+		{
+			/*
+			 * The playback was not started yet (or there has been a pause).
+			 * Start the voice (again) and ask for a rollover irq at the end of
+			 * this_one block. If this_one one is last of the buffers, use just
+			 * the normal loop with irq.
+			 */
+
+			gus_voice_off();
+			gus_rampoff();
+			gus_voice_volume(1530 + (25 * gus_pcm_volume));
+			gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
+
+			gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits);	/* Starting position */
+			gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits);	/* Loop start */
+
+			if (chn != 0)
+				gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1,
+						   0, is16bits);	/* Loop end location */
+		}
+		if (chn == 0)
+			gus_write_addr(0x04, dram_loc + pcm_bsize - 1,
+					 0, is16bits);	/* Loop end location */
+		else
+			mode[chn] |= 0x08;	/* Enable looping */
+		restore_flags(flags);
+	}
+	for (chn = 0; chn < gus_audio_channels; chn++)
+	{
+		save_flags(flags);
+		cli();
+		gus_select_voice(chn);
+		gus_write8(0x0d, ramp_mode[chn]);
+		if (iw_mode)
+			gus_write8(0x15, 0x00);	/* Reset voice deactivate bit of SMSI */
+		gus_voice_on(mode[chn]);
+		restore_flags(flags);
+	}
+	pcm_active = 1;
+}
+
+static void gus_transfer_output_block(int dev, unsigned long buf,
+			  int total_count, int intrflag, int chn)
+{
+	/*
+	 * This routine transfers one block of audio data to the DRAM. In mono mode
+	 * it's called just once. When in stereo mode, this_one routine is called
+	 * once for both channels.
+	 *
+	 * The left/mono channel data is transferred to the beginning of dram and the
+	 * right data to the area pointed by gus_page_size.
+	 */
+
+	int this_one, count;
+	unsigned long flags;
+	unsigned char dma_command;
+	unsigned long address, hold_address;
+
+	save_flags(flags);
+	cli();
+
+	count = total_count / gus_audio_channels;
+
+	if (chn == 0)
+	{
+		if (pcm_qlen >= pcm_nblk)
+			printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n");
+
+		this_one = pcm_current_block = pcm_tail;
+		pcm_qlen++;
+		pcm_tail = (pcm_tail + 1) % pcm_nblk;
+		pcm_datasize[this_one] = count;
+	}
+	else
+		this_one = pcm_current_block;
+
+	gus_write8(0x41, 0);	/* Disable GF1 DMA */
+	DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE);
+
+	address = this_one * pcm_bsize;
+	address += chn * pcm_banksize;
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+	{
+		hold_address = address;
+		address = address >> 1;
+		address &= 0x0001ffffL;
+		address |= (hold_address & 0x000c0000L);
+	}
+	gus_write16(0x42, (address >> 4) & 0xffff);	/* DRAM DMA address */
+
+	dma_command = 0x21;	/* IRQ enable, DMA start */
+
+	if (gus_audio_bits != 8)
+		dma_command |= 0x40;	/* 16 bit _DATA_ */
+	else
+		dma_command |= 0x80;	/* Invert MSB */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		dma_command |= 0x04;	/* 16 bit DMA channel */
+
+	gus_write8(0x41, dma_command);	/* Kick start */
+
+	if (chn == (gus_audio_channels - 1))	/* Last channel */
+	{
+		/*
+		 * Last (right or mono) channel data
+		 */
+		dma_active = 1;	/* DMA started. There is a unacknowledged buffer */
+		active_device = GUS_DEV_PCM_DONE;
+		if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize))
+		{
+			play_next_pcm_block();
+		}
+	}
+	else
+	{
+		/*
+		 * Left channel data. The right channel
+		 * is transferred after DMA interrupt
+		 */
+		active_device = GUS_DEV_PCM_CONTINUE;
+	}
+
+	restore_flags(flags);
+}
+
+static void gus_uninterleave8(char *buf, int l)
+{
+/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */
+	int i, p = 0, halfsize = l / 2;
+	char *buf2 = buf + halfsize, *src = bounce_buf;
+
+	memcpy(bounce_buf, buf, l);
+
+	for (i = 0; i < halfsize; i++)
+	{
+		buf[i] = src[p++];	/* Left channel */
+		buf2[i] = src[p++];	/* Right channel */
+	}
+}
+
+static void gus_uninterleave16(short *buf, int l)
+{
+/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */
+	int i, p = 0, halfsize = l / 2;
+	short *buf2 = buf + halfsize, *src = (short *) bounce_buf;
+
+	memcpy(bounce_buf, (char *) buf, l * 2);
+
+	for (i = 0; i < halfsize; i++)
+	{
+		buf[i] = src[p++];	/* Left channel */
+		buf2[i] = src[p++];	/* Right channel */
+	}
+}
+
+static void gus_audio_output_block(int dev, unsigned long buf, int total_count,
+		       int intrflag)
+{
+	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+	dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT;
+
+	pcm_current_buf = buf;
+	pcm_current_count = total_count;
+	pcm_current_intrflag = intrflag;
+	pcm_current_dev = dev;
+	if (gus_audio_channels == 2)
+	{
+		char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys);
+
+		if (gus_audio_bits == 8)
+			gus_uninterleave8(b, total_count);
+		else
+			gus_uninterleave16((short *) b, total_count / 2);
+	}
+	gus_transfer_output_block(dev, buf, total_count, intrflag, 0);
+}
+
+static void gus_audio_start_input(int dev, unsigned long buf, int count,
+		      int intrflag)
+{
+	unsigned long flags;
+	unsigned char mode;
+
+	save_flags(flags);
+	cli();
+
+	DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ);
+	mode = 0xa0;		/* DMA IRQ enabled, invert MSB */
+
+	if (audio_devs[dev]->dmap_in->dma > 3)
+		mode |= 0x04;	/* 16 bit DMA channel */
+	if (gus_audio_channels > 1)
+		mode |= 0x02;	/* Stereo */
+	mode |= 0x01;		/* DMA enable */
+
+	gus_write8(0x49, mode);
+	restore_flags(flags);
+}
+
+static int gus_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	unsigned int rate;
+
+	gus_audio_bsize = bsize;
+	audio_devs[dev]->dmap_in->flags |= DMA_NODMA;
+	rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
+
+	gus_write8(0x48, rate & 0xff);	/* Set sampling rate */
+
+	if (gus_audio_bits != 8)
+	{
+/*		printk("GUS Error: 16 bit recording not supported\n");*/
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int gus_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	int i;
+
+	long mem_ptr, mem_size;
+
+	audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT;
+	mem_ptr = 0;
+	mem_size = gus_mem_size / gus_audio_channels;
+
+	if (mem_size > (256 * 1024))
+		mem_size = 256 * 1024;
+
+	pcm_bsize = bsize / gus_audio_channels;
+	pcm_head = pcm_tail = pcm_qlen = 0;
+
+	pcm_nblk = 2;		/* MAX_PCM_BUFFERS; */
+	if ((pcm_bsize * pcm_nblk) > mem_size)
+		pcm_nblk = mem_size / pcm_bsize;
+
+	for (i = 0; i < pcm_nblk; i++)
+		pcm_datasize[i] = 0;
+
+	pcm_banksize = pcm_nblk * pcm_bsize;
+
+	if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024))
+		pcm_nblk--;
+	gus_write8(0x41, 0);	/* Disable GF1 DMA */
+	return 0;
+}
+
+static int gus_local_qlen(int dev)
+{
+	return pcm_qlen;
+}
+
+
+static struct audio_driver gus_audio_driver =
+{
+	owner:		THIS_MODULE,
+	open:		gus_audio_open,
+	close:		gus_audio_close,
+	output_block:	gus_audio_output_block,
+	start_input:	gus_audio_start_input,
+	ioctl:		gus_audio_ioctl,
+	prepare_for_input:	gus_audio_prepare_for_input,
+	prepare_for_output:	gus_audio_prepare_for_output,
+	halt_io:	gus_audio_reset,
+	local_qlen:	gus_local_qlen,
+};
+
+static void guswave_setup_voice(int dev, int voice, int chn)
+{
+	struct channel_info *info = &synth_devs[dev]->chn_info[chn];
+
+	guswave_set_instr(dev, voice, info->pgm_num);
+	voices[voice].expression_vol = info->controllers[CTL_EXPRESSION];	/* Just MSB */
+	voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
+	voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
+	voices[voice].bender = 0;
+	voices[voice].bender_range = info->bender_range;
+
+	if (chn == 9)
+		voices[voice].fixed_pitch = 1;
+}
+
+static void guswave_bender(int dev, int voice, int value)
+{
+	int freq;
+	unsigned long   flags;
+
+	voices[voice].bender = value - 8192;
+	freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0);
+	voices[voice].current_freq = freq;
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+	gus_voice_freq(freq);
+	restore_flags(flags);
+}
+
+static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+	int i, p, best = -1, best_time = 0x7fffffff;
+
+	p = alloc->ptr;
+	/*
+	 * First look for a completely stopped voice
+	 */
+
+	for (i = 0; i < alloc->max_voice; i++)
+	{
+		if (alloc->map[p] == 0)
+		{
+			alloc->ptr = p;
+			return p;
+		}
+		if (alloc->alloc_times[p] < best_time)
+		{
+			best = p;
+			best_time = alloc->alloc_times[p];
+		}
+		p = (p + 1) % alloc->max_voice;
+	}
+
+	/*
+	 * Then look for a releasing voice
+	 */
+
+	for (i = 0; i < alloc->max_voice; i++)
+	{
+		if (alloc->map[p] == 0xffff)
+		{
+			alloc->ptr = p;
+			return p;
+		}
+		p = (p + 1) % alloc->max_voice;
+	}
+	if (best >= 0)
+		p = best;
+
+	alloc->ptr = p;
+	return p;
+}
+
+static struct synth_operations guswave_operations =
+{
+	owner:		THIS_MODULE,
+	id:		"GUS",
+	info:		&gus_info,
+	midi_dev:	0,
+	synth_type:	SYNTH_TYPE_SAMPLE,
+	synth_subtype:	SAMPLE_TYPE_GUS,
+	open:		guswave_open,
+	close:		guswave_close,
+	ioctl:		guswave_ioctl,
+	kill_note:	guswave_kill_note,
+	start_note:	guswave_start_note,
+	set_instr:	guswave_set_instr,
+	reset:		guswave_reset,
+	hw_control:	guswave_hw_control,
+	load_patch:	guswave_load_patch,
+	aftertouch:	guswave_aftertouch,
+	controller:	guswave_controller,
+	panning:	guswave_panning,
+	volume_method:	guswave_volume_method,
+	bender:		guswave_bender,
+	alloc_voice:	guswave_alloc,
+	setup_voice:	guswave_setup_voice
+};
+
+static void set_input_volumes(void)
+{
+	unsigned long flags;
+	unsigned char mask = 0xff & ~0x06;	/* Just line out enabled */
+
+	if (have_gus_max)	/* Don't disturb GUS MAX */
+		return;
+
+	save_flags(flags);
+	cli();
+
+	/*
+	 *    Enable channels having vol > 10%
+	 *      Note! bit 0x01 means the line in DISABLED while 0x04 means
+	 *            the mic in ENABLED.
+	 */
+	if (gus_line_vol > 10)
+		mask &= ~0x01;
+	if (gus_mic_vol > 10)
+		mask |= 0x04;
+
+	if (recording_active)
+	{
+		/*
+		 *    Disable channel, if not selected for recording
+		 */
+		if (!(gus_recmask & SOUND_MASK_LINE))
+			mask |= 0x01;
+		if (!(gus_recmask & SOUND_MASK_MIC))
+			mask &= ~0x04;
+	}
+	mix_image &= ~0x07;
+	mix_image |= mask & 0x07;
+	outb((mix_image), u_Mixer);
+
+	restore_flags(flags);
+}
+
+#define MIX_DEVS	(SOUND_MASK_MIC|SOUND_MASK_LINE| \
+			 SOUND_MASK_SYNTH|SOUND_MASK_PCM)
+
+int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int vol, val;
+
+	if (((cmd >> 8) & 0xff) != 'M')
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int)))
+		return -EFAULT;
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) 
+	{
+		if (__get_user(val, (int *) arg))
+			return -EFAULT;
+
+		switch (cmd & 0xff) 
+		{
+			case SOUND_MIXER_RECSRC:
+				gus_recmask = val & MIX_DEVS;
+				if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+					gus_recmask = SOUND_MASK_MIC;
+				/* Note! Input volumes are updated during next open for recording */
+				val = gus_recmask;
+				break;
+
+			case SOUND_MIXER_MIC:
+				vol = val & 0xff;
+				if (vol < 0)
+					vol = 0;
+				if (vol > 100)
+					vol = 100;
+				gus_mic_vol = vol;
+				set_input_volumes();
+				val = vol | (vol << 8);
+				break;
+				
+			case SOUND_MIXER_LINE:
+				vol = val & 0xff;
+				if (vol < 0)
+					vol = 0;
+				if (vol > 100)
+					vol = 100;
+				gus_line_vol = vol;
+				set_input_volumes();
+				val = vol | (vol << 8);
+				break;
+
+			case SOUND_MIXER_PCM:
+				gus_pcm_volume = val & 0xff;
+				if (gus_pcm_volume < 0)
+					gus_pcm_volume = 0;
+				if (gus_pcm_volume > 100)
+					gus_pcm_volume = 100;
+				gus_audio_update_volume();
+				val = gus_pcm_volume | (gus_pcm_volume << 8);
+				break;
+
+			case SOUND_MIXER_SYNTH:
+				gus_wave_volume = val & 0xff;
+				if (gus_wave_volume < 0)
+					gus_wave_volume = 0;
+				if (gus_wave_volume > 100)
+					gus_wave_volume = 100;
+				if (active_device == GUS_DEV_WAVE) 
+				{
+					int voice;
+					for (voice = 0; voice < nr_voices; voice++)
+					dynamic_volume_change(voice);	/* Apply the new vol */
+				}
+				val = gus_wave_volume | (gus_wave_volume << 8);
+				break;
+
+			default:
+				return -EINVAL;
+		}
+	}
+	else
+	{
+		switch (cmd & 0xff) 
+		{
+			/*
+			 * Return parameters
+			 */
+			case SOUND_MIXER_RECSRC:
+				val = gus_recmask;
+				break;
+					
+			case SOUND_MIXER_DEVMASK:
+				val = MIX_DEVS;
+				break;
+
+			case SOUND_MIXER_STEREODEVS:
+				val = 0;
+				break;
+
+			case SOUND_MIXER_RECMASK:
+				val = SOUND_MASK_MIC | SOUND_MASK_LINE;
+				break;
+
+			case SOUND_MIXER_CAPS:
+				val = 0;
+				break;
+
+			case SOUND_MIXER_MIC:
+				val = gus_mic_vol | (gus_mic_vol << 8);
+				break;
+
+			case SOUND_MIXER_LINE:
+				val = gus_line_vol | (gus_line_vol << 8);
+				break;
+
+			case SOUND_MIXER_PCM:
+				val = gus_pcm_volume | (gus_pcm_volume << 8);
+				break;
+
+			case SOUND_MIXER_SYNTH:
+				val = gus_wave_volume | (gus_wave_volume << 8);
+				break;
+
+			default:
+				return -EINVAL;
+		}
+	}
+	return __put_user(val, (int *)arg);
+}
+
+static struct mixer_operations gus_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"GUS",
+	name:	"Gravis Ultrasound",
+	ioctl:	gus_default_mixer_ioctl
+};
+
+static int __init gus_default_mixer_init(void)
+{
+	int n;
+
+	if ((n = sound_alloc_mixerdev()) != -1)
+	{	
+		/*
+		 * Don't install if there is another
+		 * mixer
+		 */
+		mixer_devs[n] = &gus_mixer_operations;
+	}
+	if (have_gus_max)
+	{
+		/*
+		 *  Enable all mixer channels on the GF1 side. Otherwise recording will
+		 *  not be possible using GUS MAX.
+		 */
+		mix_image &= ~0x07;
+		mix_image |= 0x04;	/* All channels enabled */
+		outb((mix_image), u_Mixer);
+	}
+	return n;
+}
+
+void __init gus_wave_init(struct address_info *hw_config)
+{
+	unsigned long flags;
+	unsigned char val;
+	char *model_num = "2.4";
+	char tmp[64], tmp2[64];
+	int gus_type = 0x24;	/* 2.4 */
+
+	int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2;
+	int sdev;
+
+	hw_config->slots[0] = -1;	/* No wave */
+	hw_config->slots[1] = -1;	/* No ad1848 */
+	hw_config->slots[4] = -1;	/* No audio */
+	hw_config->slots[5] = -1;	/* No mixer */
+
+	if (!gus_pnp_flag)
+	{
+		if (irq < 0 || irq > 15)
+		{
+			printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq);
+			return;
+		}
+	}
+	
+	if (dma < 0 || dma > 7 || dma == 4)
+	{
+		printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma);
+		return;
+	}
+	gus_irq = irq;
+	gus_dma = dma;
+	gus_dma2 = dma2;
+	gus_hw_config = hw_config;
+
+	if (gus_dma2 == -1)
+		gus_dma2 = dma;
+
+	/*
+	 * Try to identify the GUS model.
+	 *
+	 *  Versions < 3.6 don't have the digital ASIC. Try to probe it first.
+	 */
+
+	save_flags(flags);
+	cli();
+	outb((0x20), gus_base + 0x0f);
+	val = inb(gus_base + 0x0f);
+	restore_flags(flags);
+
+	if (gus_pnp_flag || (val != 0xff && (val & 0x06)))	/* Should be 0x02?? */
+	{
+		int             ad_flags = 0;
+
+		if (gus_pnp_flag)
+			ad_flags = 0x12345678;	/* Interwave "magic" */
+		/*
+		 * It has the digital ASIC so the card is at least v3.4.
+		 * Next try to detect the true model.
+		 */
+
+		if (gus_pnp_flag)	/* Hack hack hack */
+			val = 10;
+		else
+			val = inb(u_MixSelect);
+
+		/*
+		 * Value 255 means pre-3.7 which don't have mixer.
+		 * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
+		 * 10 and above is GUS MAX which has the CS4231 codec/mixer.
+		 *
+		 */
+
+		if (val == 255 || val < 5)
+		{
+			model_num = "3.4";
+			gus_type = 0x34;
+		}
+		else if (val < 10)
+		{
+			model_num = "3.7";
+			gus_type = 0x37;
+			mixer_type = ICS2101;
+			request_region(u_MixSelect, 1, "GUS mixer");
+		}
+		else
+		{
+			model_num = "MAX";
+			gus_type = 0x40;
+			mixer_type = CS4231;
+#ifdef CONFIG_SOUND_ALSA_GUSMAX
+			{
+				unsigned char   max_config = 0x40;	/* Codec enable */
+
+				if (gus_dma2 == -1)
+					gus_dma2 = gus_dma;
+
+				if (gus_dma > 3)
+					max_config |= 0x10;		/* 16 bit capture DMA */
+
+				if (gus_dma2 > 3)
+					max_config |= 0x20;		/* 16 bit playback DMA */
+
+				max_config |= (gus_base >> 4) & 0x0f;	/* Extract the X from 2X0 */
+
+				outb((max_config), gus_base + 0x106);	/* UltraMax control */
+			}
+
+			if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp))
+			{
+				char           *name = "GUS MAX";
+				int             old_num_mixers = num_mixers;
+
+				if (gus_pnp_flag)
+					name = "GUS PnP";
+
+				gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+				gus_wave_volume = 90;
+				have_gus_max = 1;
+				if (hw_config->name)
+					name = hw_config->name;
+
+				hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c,
+							-irq, gus_dma2,	/* Playback DMA */
+							gus_dma,	/* Capture DMA */
+							1,		/* Share DMA channels with GF1 */
+							hw_config->osp,
+							THIS_MODULE);
+
+				if (num_mixers > old_num_mixers)
+				{
+					/* GUS has it's own mixer map */
+					AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH);
+					AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+					AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
+				}
+			}
+			else
+				printk(KERN_WARNING "GUS: No CS4231 ??");
+#else
+			printk(KERN_ERR "GUS MAX found, but not compiled in\n");
+#endif
+		}
+	}
+	else
+	{
+		/*
+		 * ASIC not detected so the card must be 2.2 or 2.4.
+		 * There could still be the 16-bit/mixer daughter card.
+		 */
+	}
+
+	if (hw_config->name)
+	{
+		strncpy(tmp, hw_config->name, 45);
+		tmp[45] = 0;
+		sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024);
+		tmp2[sizeof(tmp2) - 1] = 0;
+	}
+	else if (gus_pnp_flag)
+	{
+		sprintf(tmp2, "Gravis UltraSound PnP (%dk)",
+			(int) gus_mem_size / 1024);
+	}
+	else
+		sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
+
+
+	samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples));
+	if (samples == NULL)
+	{
+		printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n");
+		return;
+	}
+	conf_printf(tmp2, hw_config);
+	tmp2[sizeof(gus_info.name) - 1] = 0;
+	strcpy(gus_info.name, tmp2);
+
+	if ((sdev = sound_alloc_synthdev()) == -1)
+		printk(KERN_WARNING "gus_init: Too many synthesizers\n");
+	else
+	{
+		voice_alloc = &guswave_operations.alloc;
+		if (iw_mode)
+			guswave_operations.id = "IWAVE";
+		hw_config->slots[0] = sdev;
+		synth_devs[sdev] = &guswave_operations;
+		sequencer_init();
+		gus_tmr_install(gus_base + 8);
+	}
+
+	reset_sample_memory();
+
+	gus_initialize();
+	
+	if ((gus_mem_size > 0) & !gus_no_wave_dma)
+	{
+		hw_config->slots[4] = -1;
+		if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+					"Ultrasound",
+					&gus_audio_driver,
+					sizeof(struct audio_driver),
+					NEEDS_RESTART |
+		                   	((!iw_mode && dma2 != dma && dma2 != -1) ?
+						DMA_DUPLEX : 0),
+					AFMT_U8 | AFMT_S16_LE,
+					NULL, dma, dma2)) < 0)
+		{
+			return;
+		}
+
+		hw_config->slots[4] = gus_devnum;
+		audio_devs[gus_devnum]->min_fragment = 9;	/* 512k */
+		audio_devs[gus_devnum]->max_fragment = 11;	/* 8k (must match size of bounce_buf */
+		audio_devs[gus_devnum]->mixer_dev = -1;	/* Next mixer# */
+		audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
+	}
+	
+	/*
+	 *  Mixer dependent initialization.
+	 */
+
+	switch (mixer_type)
+	{
+		case ICS2101:
+			gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+			gus_wave_volume = 90;
+			request_region(u_MixSelect, 1, "GUS mixer");
+			hw_config->slots[5] = ics2101_mixer_init();
+			audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5];	/* Next mixer# */
+			return;
+
+		case CS4231:
+			/* Initialized elsewhere (ad1848.c) */
+		default:
+			hw_config->slots[5] = gus_default_mixer_init();
+			audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5];	/* Next mixer# */
+			return;
+	}
+}
+
+void __exit gus_wave_unload(struct address_info *hw_config)
+{
+#ifdef CONFIG_SOUND_ALSA_GUSMAX
+	if (have_gus_max)
+	{
+		ad1848_unload(gus_base + 0x10c,
+				-gus_irq,
+				gus_dma2,	/* Playback DMA */
+				gus_dma,	/* Capture DMA */
+				1);	/* Share DMA channels with GF1 */
+	}
+#endif
+
+	if (mixer_type == ICS2101)
+	{
+		release_region(u_MixSelect, 1);
+	}
+	if (hw_config->slots[0] != -1)
+		sound_unload_synthdev(hw_config->slots[0]);
+	if (hw_config->slots[1] != -1)
+		sound_unload_audiodev(hw_config->slots[1]);
+	if (hw_config->slots[2] != -1)
+		sound_unload_mididev(hw_config->slots[2]);
+	if (hw_config->slots[4] != -1)
+		sound_unload_audiodev(hw_config->slots[4]);
+	if (hw_config->slots[5] != -1)
+		sound_unload_mixerdev(hw_config->slots[5]);
+	
+	if(samples)
+		vfree(samples);
+	samples=NULL;
+}
+
+static void do_loop_irq(int voice)
+{
+	unsigned char   tmp;
+	int             mode, parm;
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	gus_select_voice(voice);
+
+	tmp = gus_read8(0x00);
+	tmp &= ~0x20;		/*
+				 * Disable wave IRQ for this_one voice
+				 */
+	gus_write8(0x00, tmp);
+
+	if (tmp & 0x03)		/* Voice stopped */
+		voice_alloc->map[voice] = 0;
+
+	mode = voices[voice].loop_irq_mode;
+	voices[voice].loop_irq_mode = 0;
+	parm = voices[voice].loop_irq_parm;
+
+	switch (mode)
+	{
+		case LMODE_FINISH:	/*
+					 * Final loop finished, shoot volume down
+					 */
+
+			if ((int) (gus_read16(0x09) >> 4) < 100)	/*
+									 * Get current volume
+									 */
+			{
+				gus_voice_off();
+				gus_rampoff();
+				gus_voice_init(voice);
+				break;
+			}
+			gus_ramp_range(65, 4065);
+			gus_ramp_rate(0, 63);		/*
+							 * Fastest possible rate
+							 */
+			gus_rampon(0x20 | 0x40);	/*
+							 * Ramp down, once, irq
+							 */
+			voices[voice].volume_irq_mode = VMODE_HALT;
+			break;
+
+		case LMODE_PCM_STOP:
+			pcm_active = 0;	/* Signal to the play_next_pcm_block routine */
+		case LMODE_PCM:
+		{
+			pcm_qlen--;
+			pcm_head = (pcm_head + 1) % pcm_nblk;
+			if (pcm_qlen && pcm_active)
+			{
+				play_next_pcm_block();
+			}
+			else
+			{
+				/* Underrun. Just stop the voice */
+				gus_select_voice(0);	/* Left channel */
+				gus_voice_off();
+				gus_rampoff();
+				gus_select_voice(1);	/* Right channel */
+				gus_voice_off();
+				gus_rampoff();
+				pcm_active = 0;
+			}
+
+			/*
+			 * If the queue was full before this interrupt, the DMA transfer was
+			 * suspended. Let it continue now.
+			 */
+			
+			if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
+				DMAbuf_outputintr(gus_devnum, 0);
+		}
+		break;
+
+		default:
+			break;
+	}
+	restore_flags(flags);
+}
+
+static void do_volume_irq(int voice)
+{
+	unsigned char tmp;
+	int mode, parm;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	gus_select_voice(voice);
+	tmp = gus_read8(0x0d);
+	tmp &= ~0x20;		/*
+				 * Disable volume ramp IRQ
+				 */
+	gus_write8(0x0d, tmp);
+
+	mode = voices[voice].volume_irq_mode;
+	voices[voice].volume_irq_mode = 0;
+	parm = voices[voice].volume_irq_parm;
+
+	switch (mode)
+	{
+		case VMODE_HALT:	/* Decay phase finished */
+			if (iw_mode)
+				gus_write8(0x15, 0x02);	/* Set voice deactivate bit of SMSI */
+			restore_flags(flags);
+			gus_voice_init(voice);
+			break;
+
+		case VMODE_ENVELOPE:
+			gus_rampoff();
+			restore_flags(flags);
+			step_envelope(voice);
+			break;
+
+		case VMODE_START_NOTE:
+			restore_flags(flags);
+			guswave_start_note2(voices[voice].dev_pending, voice,
+				      voices[voice].note_pending, voices[voice].volume_pending);
+			if (voices[voice].kill_pending)
+				guswave_kill_note(voices[voice].dev_pending, voice,
+					  voices[voice].note_pending, 0);
+
+			if (voices[voice].sample_pending >= 0)
+			{
+				guswave_set_instr(voices[voice].dev_pending, voice,
+					voices[voice].sample_pending);
+				voices[voice].sample_pending = -1;
+			}
+			break;
+
+		default:
+			restore_flags(flags);
+	}
+	restore_flags(flags);
+}
+
+void gus_voice_irq(void)
+{
+	unsigned long wave_ignore = 0, volume_ignore = 0;
+	unsigned long voice_bit;
+
+	unsigned char src, voice;
+
+	while (1)
+	{
+		src = gus_read8(0x0f);	/*
+					 * Get source info
+					 */
+		voice = src & 0x1f;
+		src &= 0xc0;
+
+		if (src == (0x80 | 0x40))
+			return;	/*
+				 * No interrupt
+				 */
+
+		voice_bit = 1 << voice;
+
+		if (!(src & 0x80))	/*
+					 * Wave IRQ pending
+					 */
+			if (!(wave_ignore & voice_bit) && (int) voice < nr_voices)	/*
+											 * Not done
+											 * yet
+											 */
+			{
+				wave_ignore |= voice_bit;
+				do_loop_irq(voice);
+			}
+		if (!(src & 0x40))	/*
+					 * Volume IRQ pending
+					 */
+			if (!(volume_ignore & voice_bit) && (int) voice < nr_voices)	/*
+											   * Not done
+											   * yet
+											 */
+			{
+				volume_ignore |= voice_bit;
+				do_volume_irq(voice);
+			}
+	}
+}
+
+void guswave_dma_irq(void)
+{
+	unsigned char   status;
+
+	status = gus_look8(0x41);	/* Get DMA IRQ Status */
+	if (status & 0x40)	/* DMA interrupt pending */
+		switch (active_device)
+		{
+			case GUS_DEV_WAVE:
+				wake_up(&dram_sleeper);
+				break;
+
+			case GUS_DEV_PCM_CONTINUE:	/* Left channel data transferred */
+				gus_write8(0x41, 0);	/* Disable GF1 DMA */
+				gus_transfer_output_block(pcm_current_dev, pcm_current_buf,
+						pcm_current_count,
+						pcm_current_intrflag, 1);
+				break;
+
+			case GUS_DEV_PCM_DONE:	/* Right or mono channel data transferred */
+				gus_write8(0x41, 0);	/* Disable GF1 DMA */
+				if (pcm_qlen < pcm_nblk)
+				{
+					dma_active = 0;
+					if (gus_busy)
+					{
+						if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
+							DMAbuf_outputintr(gus_devnum, 0);
+					}
+				}
+				break;
+
+			default:
+				break;
+	}
+	status = gus_look8(0x49);	/*
+					 * Get Sampling IRQ Status
+					 */
+	if (status & 0x40)	/*
+				 * Sampling Irq pending
+				 */
+	{
+		DMAbuf_inputintr(gus_devnum);
+	}
+}
+
+/*
+ * Timer stuff
+ */
+
+static volatile int select_addr, data_addr;
+static volatile int curr_timer = 0;
+
+void gus_timer_command(unsigned int addr, unsigned int val)
+{
+	int i;
+
+	outb(((unsigned char) (addr & 0xff)), select_addr);
+
+	for (i = 0; i < 2; i++)
+		inb(select_addr);
+
+	outb(((unsigned char) (val & 0xff)), data_addr);
+
+	for (i = 0; i < 2; i++)
+		inb(select_addr);
+}
+
+static void arm_timer(int timer, unsigned int interval)
+{
+	curr_timer = timer;
+
+	if (timer == 1)
+	{
+		gus_write8(0x46, 256 - interval);	/* Set counter for timer 1 */
+		gus_write8(0x45, 0x04);			/* Enable timer 1 IRQ */
+		gus_timer_command(0x04, 0x01);		/* Start timer 1 */
+	}
+	else
+	{
+		gus_write8(0x47, 256 - interval);	/* Set counter for timer 2 */
+		gus_write8(0x45, 0x08);			/* Enable timer 2 IRQ */
+		gus_timer_command(0x04, 0x02);		/* Start timer 2 */
+	}
+
+	gus_timer_enabled = 1;
+}
+
+static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick)
+{
+	int timer_no, resolution;
+	int divisor;
+
+	if (usecs_per_tick > (256 * 80))
+	{
+		timer_no = 2;
+		resolution = 320;	/* usec */
+	}
+	else
+	{
+		timer_no = 1;
+		resolution = 80;	/* usec */
+	}
+	divisor = (usecs_per_tick + (resolution / 2)) / resolution;
+	arm_timer(timer_no, divisor);
+
+	return divisor * resolution;
+}
+
+static void gus_tmr_disable(int dev)
+{
+	gus_write8(0x45, 0);	/* Disable both timers */
+	gus_timer_enabled = 0;
+}
+
+static void gus_tmr_restart(int dev)
+{
+	if (curr_timer == 1)
+		gus_write8(0x45, 0x04);		/* Start timer 1 again */
+	else
+		gus_write8(0x45, 0x08);		/* Start timer 2 again */
+	gus_timer_enabled = 1;
+}
+
+static struct sound_lowlev_timer gus_tmr =
+{
+	0,
+	1,
+	gus_tmr_start,
+	gus_tmr_disable,
+	gus_tmr_restart
+};
+
+static void gus_tmr_install(int io_base)
+{
+	struct sound_lowlev_timer *tmr;
+
+	select_addr = io_base;
+	data_addr = io_base + 1;
+
+	tmr = &gus_tmr;
+
+#ifdef THIS_GETS_FIXED
+	sound_timer_init(&gus_tmr, "GUS");
+#endif
+}
diff -Nru linux/sound/oss/hex2hex.c linux-2.4.19-pre5-mjc/sound/oss/hex2hex.c
--- linux/sound/oss/hex2hex.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/hex2hex.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,101 @@
+/*
+ * hex2hex reads stdin in Intel HEX format and produces an
+ * (unsigned char) array which contains the bytes and writes it
+ * to stdout using C syntax
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); }
+#define MAX_SIZE (256*1024)
+unsigned char buf[MAX_SIZE];
+
+int loadhex(FILE *inf, unsigned char *buf)
+{
+	int l=0, c, i;
+
+	while ((c=getc(inf))!=EOF)
+	{
+		if (c == ':')	/* Sync with beginning of line */
+		{
+			int n, check;
+			unsigned char sum;
+			int addr;
+			int linetype;
+
+			if (fscanf(inf, "%02x", &n) != 1)
+			   ABANDON("File format error");
+			sum = n;
+
+			if (fscanf(inf, "%04x", &addr) != 1)
+			   ABANDON("File format error");
+			sum += addr/256;
+			sum += addr%256;
+
+			if (fscanf(inf, "%02x", &linetype) != 1)
+			   ABANDON("File format error");
+			sum += linetype;
+
+			if (linetype != 0)
+			   continue;
+
+			for (i=0;i<n;i++)
+			{
+				if (fscanf(inf, "%02x", &c) != 1)
+			   	   ABANDON("File format error");
+				if (addr >= MAX_SIZE)
+				   ABANDON("File too large");
+				buf[addr++] = c;
+				if (addr > l)
+				   l = addr;
+				sum += c;
+			}
+
+			if (fscanf(inf, "%02x", &check) != 1)
+			   ABANDON("File format error");
+
+			sum = ~sum + 1;
+			if (check != sum)
+			   ABANDON("Line checksum error");
+		}
+	}
+
+	return l;
+}
+
+int main( int argc, const char * argv [] )
+{
+	const char * varline;
+	int i,l;
+	int id=0;
+
+	if(argv[1] && strcmp(argv[1], "-i")==0)
+	{
+		argv++;
+		argc--;
+		id=1;
+	}
+	if(argv[1]==NULL)
+	{
+		fprintf(stderr,"hex2hex: [-i] filename\n");
+		exit(1);
+	}
+	varline = argv[1];
+	l = loadhex(stdin, buf);
+
+	printf("/*\n *\t Computer generated file. Do not edit.\n */\n");
+        printf("static int %s_len = %d;\n", varline, l);
+	printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":"");
+
+	for (i=0;i<l;i++)
+	{
+		if (i) printf(",");
+		if (i && !(i % 16)) printf("\n");
+		printf("0x%02x", buf[i]);
+	}
+
+	printf("\n};\n\n");
+	return 0;
+}
diff -Nru linux/sound/oss/i810_audio.c linux-2.4.19-pre5-mjc/sound/oss/i810_audio.c
--- linux/sound/oss/i810_audio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/i810_audio.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3168 @@
+/*
+ *	Intel i810 and friends ICH driver for Linux
+ *	Alan Cox <alan@redhat.com>
+ *
+ *  Built from:
+ *	Low level code:  Zach Brown (original nonworking i810 OSS driver)
+ *			 Jaroslav Kysela <perex@suse.cz> (working ALSA driver)
+ *
+ *	Framework: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *	Extended by: Zach Brown <zab@redhat.com>  
+ *			and others..
+ *
+ *  Hardware Provided By:
+ *	Analog Devices (A major AC97 codec maker)
+ *	Intel Corp  (you've probably heard of them already)
+ *
+ *  AC97 clues and assistance provided by
+ *	Analog Devices
+ *	Zach 'Fufu' Brown
+ *	Jeff Garzik
+ *
+ *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	Intel 810 theory of operation
+ *
+ *	The chipset provides three DMA channels that talk to an AC97
+ *	CODEC (AC97 is a digital/analog mixer standard). At its simplest
+ *	you get 48Khz audio with basic volume and mixer controls. At the
+ *	best you get rate adaption in the codec. We set the card up so
+ *	that we never take completion interrupts but instead keep the card
+ *	chasing its tail around a ring buffer. This is needed for mmap
+ *	mode audio and happens to work rather well for non-mmap modes too.
+ *
+ *	The board has one output channel for PCM audio (supported) and
+ *	a stereo line in and mono microphone input. Again these are normally
+ *	locked to 48Khz only. Right now recording is not finished.
+ *
+ *	There is no midi support, no synth support. Use timidity. To get
+ *	esd working you need to use esd -r 48000 as it won't probe 48KHz
+ *	by default. mpg123 can't handle 48Khz only audio so use xmms.
+ *
+ *	Fix The Sound On Dell
+ *
+ *	Not everyone uses 48KHz. We know of no way to detect this reliably
+ *	and certainly not to get the right data. If your i810 audio sounds
+ *	stupid you may need to investigate other speeds. According to Analog
+ *	they tend to use a 14.318MHz clock which gives you a base rate of
+ *	41194Hz.
+ *
+ *	This is available via the 'ftsodell=1' option. 
+ *
+ *	If you need to force a specific rate set the clocking= option
+ *
+ *	This driver is cursed. (Ben LaHaise)
+ */
+ 
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wrapper.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+#ifndef PCI_DEVICE_ID_INTEL_82801
+#define PCI_DEVICE_ID_INTEL_82801	0x2415
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82901
+#define PCI_DEVICE_ID_INTEL_82901	0x2425
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH2
+#define PCI_DEVICE_ID_INTEL_ICH2	0x2445
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH3
+#define PCI_DEVICE_ID_INTEL_ICH3	0x2485
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_440MX
+#define PCI_DEVICE_ID_INTEL_440MX	0x7195
+#endif
+#ifndef PCI_DEVICE_ID_SI_7012
+#define PCI_DEVICE_ID_SI_7012		0x7012
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO	0x01b1
+#endif
+
+static int ftsodell=0;
+static int strict_clocking=0;
+static unsigned int clocking=0;
+static int spdif_locked=0;
+
+//#define DEBUG
+//#define DEBUG2
+//#define DEBUG_INTERRUPTS
+//#define DEBUG_MMAP
+
+#define ADC_RUNNING	1
+#define DAC_RUNNING	2
+
+#define I810_FMT_16BIT	1
+#define I810_FMT_STEREO	2
+#define I810_FMT_MASK	3
+
+#define SPDIF_ON	0x0004
+#define SURR_ON		0x0010
+#define CENTER_LFE_ON	0x0020
+#define VOL_MUTED	0x8000
+
+/* the 810's array of pointers to data buffers */
+
+struct sg_item {
+#define BUSADDR_MASK	0xFFFFFFFE
+	u32 busaddr;	
+#define CON_IOC 	0x80000000 /* interrupt on completion */
+#define CON_BUFPAD	0x40000000 /* pad underrun with last sample, else 0 */
+#define CON_BUFLEN_MASK	0x0000ffff /* buffer length in samples */
+	u32 control;
+};
+
+/* an instance of the i810 channel */
+#define SG_LEN 32
+struct i810_channel 
+{
+	/* these sg guys should probably be allocated
+	   seperately as nocache. Must be 8 byte aligned */
+	struct sg_item sg[SG_LEN];	/* 32*8 */
+	u32 offset;			/* 4 */
+	u32 port;			/* 4 */
+	u32 used;
+	u32 num;
+};
+
+/*
+ * we have 3 seperate dma engines.  pcm in, pcm out, and mic.
+ * each dma engine has controlling registers.  These goofy
+ * names are from the datasheet, but make it easy to write
+ * code while leafing through it.
+ */
+
+#define ENUM_ENGINE(PRE,DIG) 									\
+enum {												\
+	PRE##_BDBAR =	0x##DIG##0,		/* Buffer Descriptor list Base Address */	\
+	PRE##_CIV =	0x##DIG##4,		/* Current Index Value */			\
+	PRE##_LVI =	0x##DIG##5,		/* Last Valid Index */				\
+	PRE##_SR =	0x##DIG##6,		/* Status Register */				\
+	PRE##_PICB =	0x##DIG##8,		/* Position In Current Buffer */		\
+	PRE##_PIV =	0x##DIG##a,		/* Prefetched Index Value */			\
+	PRE##_CR =	0x##DIG##b		/* Control Register */				\
+}
+
+ENUM_ENGINE(OFF,0);	/* Offsets */
+ENUM_ENGINE(PI,0);	/* PCM In */
+ENUM_ENGINE(PO,1);	/* PCM Out */
+ENUM_ENGINE(MC,2);	/* Mic In */
+
+enum {
+	GLOB_CNT =	0x2c,			/* Global Control */
+	GLOB_STA = 	0x30,			/* Global Status */
+	CAS	 = 	0x34			/* Codec Write Semaphore Register */
+};
+
+/* interrupts for a dma engine */
+#define DMA_INT_FIFO		(1<<4)  /* fifo under/over flow */
+#define DMA_INT_COMPLETE	(1<<3)  /* buffer read/write complete and ioc set */
+#define DMA_INT_LVI		(1<<2)  /* last valid done */
+#define DMA_INT_CELV		(1<<1)  /* last valid is current */
+#define DMA_INT_DCH		(1)	/* DMA Controller Halted (happens on LVI interrupts) */
+#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI)
+
+/* interrupts for the whole chip */
+#define INT_SEC		(1<<11)
+#define INT_PRI		(1<<10)
+#define INT_MC		(1<<7)
+#define INT_PO		(1<<6)
+#define INT_PI		(1<<5)
+#define INT_MO		(1<<2)
+#define INT_NI		(1<<1)
+#define INT_GPI		(1<<0)
+#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
+
+
+#define DRIVER_VERSION "0.21"
+
+/* magic numbers to protect our data structures */
+#define I810_CARD_MAGIC		0x5072696E /* "Prin" */
+#define I810_STATE_MAGIC	0x63657373 /* "cess" */
+#define I810_DMA_MASK		0xffffffff /* DMA buffer mask for pci_alloc_consist */
+#define NR_HW_CH		3
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define NR_AC97		2
+
+/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */
+/* stream at a minimum for this card to be happy */
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */
+/* values are one less than might be expected */
+static const unsigned sample_shift[] = { -1, 0, 0, 1 };
+
+enum {
+	ICH82801AA = 0,
+	ICH82901AB,
+	INTEL440MX,
+	INTELICH2,
+	INTELICH3,
+	SI7012,
+	NVIDIA_NFORCE
+};
+
+static char * card_names[] = {
+	"Intel ICH 82801AA",
+	"Intel ICH 82901AB",
+	"Intel 440MX",
+	"Intel ICH2",
+	"Intel ICH3",
+	"SiS 7012",
+	"NVIDIA nForce Audio"
+};
+
+static struct pci_device_id i810_pci_tbl [] __initdata = {
+	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA},
+	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB},
+	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX},
+	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH2,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2},
+	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3},
+	{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012},
+	{PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE (pci, i810_pci_tbl);
+
+#ifdef CONFIG_PM
+#define PM_SUSPENDED(card) (card->pm_suspended)
+#else
+#define PM_SUSPENDED(card) (0)
+#endif
+
+/* "software" or virtual channel, an instance of opened /dev/dsp */
+struct i810_state {
+	unsigned int magic;
+	struct i810_card *card;	/* Card info */
+
+	/* single open lock mechanism, only used for recording */
+	struct semaphore open_sem;
+	wait_queue_head_t open_wait;
+
+	/* file mode */
+	mode_t open_mode;
+
+	/* virtual channel number */
+	int virt;
+
+#ifdef CONFIG_PM
+	unsigned int pm_saved_dac_rate,pm_saved_adc_rate;
+#endif
+	struct dmabuf {
+		/* wave sample stuff */
+		unsigned int rate;
+		unsigned char fmt, enable, trigger;
+
+		/* hardware channel */
+		struct i810_channel *read_channel;
+		struct i810_channel *write_channel;
+
+		/* OSS buffer management stuff */
+		void *rawbuf;
+		dma_addr_t dma_handle;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+
+		/* our buffer acts like a circular ring */
+		unsigned hwptr;		/* where dma last started, updated by update_ptr */
+		unsigned swptr;		/* where driver last clear/filled, updated by read/write */
+		int count;		/* bytes to be consumed or been generated by dma machine */
+		unsigned total_bytes;	/* total bytes dmaed by hardware */
+
+		unsigned error;		/* number of over/underruns */
+		wait_queue_head_t wait;	/* put process on wait queue when no more space in buffer */
+
+		/* redundant, but makes calculations easier */
+		/* what the hardware uses */
+		unsigned dmasize;
+		unsigned fragsize;
+		unsigned fragsamples;
+
+		/* what we tell the user to expect */
+		unsigned userfrags;
+		unsigned userfragsize;
+
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned update_flag;
+		unsigned ossfragsize;
+		unsigned ossmaxfrags;
+		unsigned subdivision;
+	} dmabuf;
+};
+
+
+struct i810_card {
+	unsigned int magic;
+
+	/* We keep i810 cards in a linked list */
+	struct i810_card *next;
+
+	/* The i810 has a certain amount of cross channel interaction
+	   so we use a single per card lock */
+	spinlock_t lock;
+
+	/* PCI device stuff */
+	struct pci_dev * pci_dev;
+	u16 pci_id;
+#ifdef CONFIG_PM	
+	u16 pm_suspended;
+	u32 pm_save_state[64/sizeof(u32)];
+	int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97];
+#endif
+	/* soundcore stuff */
+	int dev_audio;
+
+	/* structures for abstraction of hardware facilities, codecs, banks and channels*/
+	struct ac97_codec *ac97_codec[NR_AC97];
+	struct i810_state *states[NR_HW_CH];
+	struct i810_channel *channel;	/* 1:1 to states[] but diff. lifetime */
+	dma_addr_t chandma;
+
+	u16 ac97_features;
+	u16 ac97_status;
+	u16 channels;
+	
+	/* hardware resources */
+	unsigned long iobase;
+	unsigned long ac97base;
+	u32 irq;
+	
+	/* Function support */
+	struct i810_channel *(*alloc_pcm_channel)(struct i810_card *);
+	struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *);
+	struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *);
+	void (*free_pcm_channel)(struct i810_card *, int chan);
+
+	/* We have a *very* long init time possibly, so use this to block */
+	/* attempts to open our devices before we are ready (stops oops'es) */
+	int initializing;
+};
+
+static struct i810_card *devs = NULL;
+
+static int i810_open_mixdev(struct inode *inode, struct file *file);
+static int i810_ioctl_mixdev(struct inode *inode, struct file *file,
+			     unsigned int cmd, unsigned long arg);
+static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg);
+static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
+
+static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card)
+{
+	if(card->channel[1].used==1)
+		return NULL;
+	card->channel[1].used=1;
+	return &card->channel[1];
+}
+
+static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card)
+{
+	if(card->channel[0].used==1)
+		return NULL;
+	card->channel[0].used=1;
+	return &card->channel[0];
+}
+
+static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card)
+{
+	if(card->channel[2].used==1)
+		return NULL;
+	card->channel[2].used=1;
+	return &card->channel[2];
+}
+
+static void i810_free_pcm_channel(struct i810_card *card, int channel)
+{
+	card->channel[channel].used=0;
+}
+
+static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate )
+{
+	unsigned long id = 0L;
+
+	id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16);
+	id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff;
+#ifdef DEBUG
+	printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id);
+#endif
+	switch ( id ) {
+		case 0x41445361: /* AD1886 */
+			if (rate == 48000) {
+				return 1;
+			}
+			break;
+		default: /* all other codecs, until we know otherwiae */
+			if (rate == 48000 || rate == 44100 || rate == 32000) {
+				return 1;
+			}
+			break;
+	}
+	return (0);
+}
+
+/* i810_set_spdif_output
+ * 
+ *  Configure the S/PDIF output transmitter. When we turn on
+ *  S/PDIF, we turn off the analog output. This may not be
+ *  the right thing to do.
+ *
+ *  Assumptions:
+ *     The DSP sample rate must already be set to a supported
+ *     S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort.
+ */
+static void i810_set_spdif_output(struct i810_state *state, int slots, int rate)
+{
+	int	vol;
+	int	aud_reg;
+	struct ac97_codec *codec = state->card->ac97_codec[0];
+
+	if(!(state->card->ac97_features & 4)) {
+#ifdef DEBUG
+		printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n");
+#endif
+		state->card->ac97_status &= ~SPDIF_ON;
+	} else {
+		if ( slots == -1 ) { /* Turn off S/PDIF */
+			aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+			i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+
+			/* If the volume wasn't muted before we turned on S/PDIF, unmute it */
+			if ( !(state->card->ac97_status & VOL_MUTED) ) {
+				aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO);
+				i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED));
+			}
+			state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON);
+			return;
+		}
+
+		vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO);
+		state->card->ac97_status = vol & VOL_MUTED;
+
+		/* Set S/PDIF transmitter sample rate */
+		aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
+		switch ( rate ) {
+			case 32000:
+				aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; 
+				break;
+			 case 44100:
+			 	aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; 
+				break;
+			case 48000:
+			 	aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; 
+				break;
+			default:
+#ifdef DEBUG
+				printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate);
+#endif
+				/* turn off S/PDIF */
+				aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+				i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+				state->card->ac97_status &= ~SPDIF_ON;
+				return;
+		}
+
+		i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg);
+		
+		aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+		aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF;
+		i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);
+		state->card->ac97_status |= SPDIF_ON;
+
+		/* Check to make sure the configuration is valid */
+		aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+		if ( ! (aud_reg & 0x0400) ) {
+#ifdef DEBUG
+			printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg);
+#endif
+
+			/* turn off S/PDIF */
+			i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF));
+			state->card->ac97_status &= ~SPDIF_ON;
+			return;
+		}
+		/* Mute the analog output */
+		/* Should this only mute the PCM volume??? */
+		i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED));
+	}
+}
+
+/* i810_set_dac_channels
+ *
+ *  Configure the codec's multi-channel DACs
+ *
+ *  The logic is backwards. Setting the bit to 1 turns off the DAC. 
+ *
+ *  What about the ICH? We currently configure it using the
+ *  SNDCTL_DSP_CHANNELS ioctl.  If we're turnning on the DAC, 
+ *  does that imply that we want the ICH set to support
+ *  these channels?
+ *  
+ *  TODO:
+ *    vailidate that the codec really supports these DACs
+ *    before turning them on. 
+ */
+static void i810_set_dac_channels(struct i810_state *state, int channel)
+{
+	int	aud_reg;
+	struct ac97_codec *codec = state->card->ac97_codec[0];
+
+	aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
+	aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK;
+	state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON);
+
+	switch ( channel ) {
+		case 2: /* always enabled */
+			break;
+		case 4:
+			aud_reg &= ~AC97_EA_PRJ;
+			state->card->ac97_status |= SURR_ON;
+			break;
+		case 6:
+			aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK);
+			state->card->ac97_status |= SURR_ON | CENTER_LFE_ON;
+			break;
+		default:
+			break;
+	}
+	i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);
+
+}
+
+
+/* set playback sample rate */
+static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate)
+{	
+	struct dmabuf *dmabuf = &state->dmabuf;
+	u32 new_rate;
+	struct ac97_codec *codec=state->card->ac97_codec[0];
+	
+	if(!(state->card->ac97_features&0x0001))
+	{
+		dmabuf->rate = clocking;
+#ifdef DEBUG
+		printk("Asked for %d Hz, but ac97_features says we only do %dHz.  Sorry!\n",
+		       rate,clocking);
+#endif		       
+		return clocking;
+	}
+			
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 8000)
+		rate = 8000;
+	dmabuf->rate = rate;
+		
+	/*
+	 *	Adjust for misclocked crap
+	 */
+	rate = ( rate * clocking)/48000;
+	if(strict_clocking && rate < 8000) {
+		rate = 8000;
+		dmabuf->rate = (rate * 48000)/clocking;
+	}
+
+        new_rate=ac97_set_dac_rate(codec, rate);
+	if(new_rate != rate) {
+		dmabuf->rate = (new_rate * 48000)/clocking;
+	}
+#ifdef DEBUG
+	printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate);
+#endif
+	rate = new_rate;
+	return dmabuf->rate;
+}
+
+/* set recording sample rate */
+static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	u32 new_rate;
+	struct ac97_codec *codec=state->card->ac97_codec[0];
+	
+	if(!(state->card->ac97_features&0x0001))
+	{
+		dmabuf->rate = clocking;
+		return clocking;
+	}
+			
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 8000)
+		rate = 8000;
+	dmabuf->rate = rate;
+
+	/*
+	 *	Adjust for misclocked crap
+	 */
+	 
+	rate = ( rate * clocking)/48000;
+	if(strict_clocking && rate < 8000) {
+		rate = 8000;
+		dmabuf->rate = (rate * 48000)/clocking;
+	}
+
+	new_rate = ac97_set_adc_rate(codec, rate);
+	
+	if(new_rate != rate) {
+		dmabuf->rate = (new_rate * 48000)/clocking;
+		rate = new_rate;
+	}
+#ifdef DEBUG
+	printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);
+#endif
+	return dmabuf->rate;
+}
+
+/* get current playback/recording dma buffer pointer (byte offset from LBA),
+   called with spinlock held! */
+   
+static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned int civ, offset, port, port_picb, bytes = 2;
+	
+	if (!dmabuf->enable)
+		return 0;
+
+	if (rec)
+		port = state->card->iobase + dmabuf->read_channel->port;
+	else
+		port = state->card->iobase + dmabuf->write_channel->port;
+
+	if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) {
+		port_picb = port + OFF_SR;
+		bytes = 1;
+	} else
+		port_picb = port + OFF_PICB;
+
+	do {
+		civ = inb(port+OFF_CIV) & 31;
+		offset = inw(port_picb);
+		/* Must have a delay here! */ 
+		if(offset == 0)
+			udelay(1);
+		/* Reread both registers and make sure that that total
+		 * offset from the first reading to the second is 0.
+		 * There is an issue with SiS hardware where it will count
+		 * picb down to 0, then update civ to the next value,
+		 * then set the new picb to fragsize bytes.  We can catch
+		 * it between the civ update and the picb update, making
+		 * it look as though we are 1 fragsize ahead of where we
+		 * are.  The next to we get the address though, it will
+		 * be back in the right place, and we will suddenly think
+		 * we just went forward dmasize - fragsize bytes, causing
+		 * totally stupid *huge* dma overrun messages.  We are
+		 * assuming that the 1us delay is more than long enough
+		 * that we won't have to worry about the chip still being
+		 * out of sync with reality ;-)
+		 */
+	} while (civ != (inb(port+OFF_CIV) & 31) || offset != inw(port_picb));
+		 
+	return (((civ + 1) * dmabuf->fragsize - (bytes * offset))
+		% dmabuf->dmasize);
+}
+
+/* Stop recording (lock held) */
+static inline void __stop_adc(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct i810_card *card = state->card;
+
+	dmabuf->enable &= ~ADC_RUNNING;
+	outb(0, card->iobase + PI_CR);
+	// wait for the card to acknowledge shutdown
+	while( inb(card->iobase + PI_CR) != 0 ) ;
+	// now clear any latent interrupt bits (like the halt bit)
+	if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+		outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB );
+	else
+		outb( inb(card->iobase + PI_SR), card->iobase + PI_SR );
+	outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA);
+}
+
+static void stop_adc(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__stop_adc(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static inline void __start_adc(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+
+	if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable &&
+	    (dmabuf->trigger & PCM_ENABLE_INPUT)) {
+		dmabuf->enable |= ADC_RUNNING;
+		outb((1<<4) | (1<<2) | 1, state->card->iobase + PI_CR);
+	}
+}
+
+static void start_adc(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__start_adc(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* stop playback (lock held) */
+static inline void __stop_dac(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct i810_card *card = state->card;
+
+	dmabuf->enable &= ~DAC_RUNNING;
+	outb(0, card->iobase + PO_CR);
+	// wait for the card to acknowledge shutdown
+	while( inb(card->iobase + PO_CR) != 0 ) ;
+	// now clear any latent interrupt bits (like the halt bit)
+	if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+		outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB );
+	else
+		outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
+	outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA);
+}
+
+static void stop_dac(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__stop_dac(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}	
+
+static inline void __start_dac(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+
+	if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&
+	    (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+		dmabuf->enable |= DAC_RUNNING;
+		outb((1<<4) | (1<<2) | 1, state->card->iobase + PO_CR);
+	}
+}
+static void start_dac(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__start_dac(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
+static int alloc_dmabuf(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	void *rawbuf= NULL;
+	int order, size;
+	struct page *page, *pend;
+
+	/* If we don't have any oss frag params, then use our default ones */
+	if(dmabuf->ossmaxfrags == 0)
+		dmabuf->ossmaxfrags = 4;
+	if(dmabuf->ossfragsize == 0)
+		dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags;
+	size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
+
+	if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size)
+		return 0;
+	/* alloc enough to satisfy the oss params */
+	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
+		if ( (PAGE_SIZE<<order) > size )
+			continue;
+		if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
+						   PAGE_SIZE << order,
+						   &dmabuf->dma_handle)))
+			break;
+	}
+	if (!rawbuf)
+		return -ENOMEM;
+
+
+#ifdef DEBUG
+	printk("i810_audio: allocated %ld (order = %d) bytes at %p\n",
+	       PAGE_SIZE << order, order, rawbuf);
+#endif
+
+	dmabuf->ready  = dmabuf->mapped = 0;
+	dmabuf->rawbuf = rawbuf;
+	dmabuf->buforder = order;
+	
+	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+	pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
+	for (page = virt_to_page(rawbuf); page <= pend; page++)
+		mem_map_reserve(page);
+
+	return 0;
+}
+
+/* free DMA buffer */
+static void dealloc_dmabuf(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct page *page, *pend;
+
+	if (dmabuf->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
+		for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
+			mem_map_unreserve(page);
+		pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,
+				    dmabuf->rawbuf, dmabuf->dma_handle);
+	}
+	dmabuf->rawbuf = NULL;
+	dmabuf->mapped = dmabuf->ready = 0;
+}
+
+static int prog_dmabuf(struct i810_state *state, unsigned rec)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct i810_channel *c;
+	struct sg_item *sg;
+	unsigned long flags;
+	int ret;
+	unsigned fragint;
+	int i;
+
+	spin_lock_irqsave(&state->card->lock, flags);
+	if(dmabuf->enable & DAC_RUNNING)
+		__stop_dac(state);
+	if(dmabuf->enable & ADC_RUNNING)
+		__stop_adc(state);
+	dmabuf->total_bytes = 0;
+	dmabuf->count = dmabuf->error = 0;
+	dmabuf->swptr = dmabuf->hwptr = 0;
+	spin_unlock_irqrestore(&state->card->lock, flags);
+
+	/* allocate DMA buffer, let alloc_dmabuf determine if we are already
+	 * allocated well enough or if we should replace the current buffer
+	 * (assuming one is already allocated, if it isn't, then allocate it).
+	 */
+	if ((ret = alloc_dmabuf(state)))
+		return ret;
+
+	/* FIXME: figure out all this OSS fragment stuff */
+	/* I did, it now does what it should according to the OSS API.  DL */
+	/* We may not have realloced our dmabuf, but the fragment size to
+	 * fragment number ratio may have changed, so go ahead and reprogram
+	 * things
+	 */
+	dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder;
+	dmabuf->numfrag = SG_LEN;
+	dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;
+	dmabuf->fragsamples = dmabuf->fragsize >> 1;
+	dmabuf->userfragsize = dmabuf->ossfragsize;
+	dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize;
+
+	memset(dmabuf->rawbuf, 0, dmabuf->dmasize);
+
+	if(dmabuf->ossmaxfrags == 4) {
+		fragint = 8;
+		dmabuf->fragshift = 2;
+	} else if (dmabuf->ossmaxfrags == 8) {
+		fragint = 4;
+		dmabuf->fragshift = 3;
+	} else if (dmabuf->ossmaxfrags == 16) {
+		fragint = 2;
+		dmabuf->fragshift = 4;
+	} else {
+		fragint = 1;
+		dmabuf->fragshift = 5;
+	}
+	/*
+	 *	Now set up the ring 
+	 */
+	if(dmabuf->read_channel)
+		c = dmabuf->read_channel;
+	else
+		c = dmabuf->write_channel;
+	while(c != NULL) {
+		sg=&c->sg[0];
+		/*
+		 *	Load up 32 sg entries and take an interrupt at half
+		 *	way (we might want more interrupts later..) 
+		 */
+	  
+		for(i=0;i<dmabuf->numfrag;i++)
+		{
+			sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i;
+			// the card will always be doing 16bit stereo
+			sg->control=dmabuf->fragsamples;
+			if(state->card->pci_id == PCI_DEVICE_ID_SI_7012)
+				sg->control <<= 1;
+			sg->control|=CON_BUFPAD;
+			// set us up to get IOC interrupts as often as needed to
+			// satisfy numfrag requirements, no more
+			if( ((i+1) % fragint) == 0) {
+				sg->control|=CON_IOC;
+			}
+			sg++;
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */
+		outl((u32)state->card->chandma +
+		    c->num*sizeof(struct i810_channel),
+		    state->card->iobase+c->port+OFF_BDBAR);
+		outb(0, state->card->iobase+c->port+OFF_CIV);
+		outb(0, state->card->iobase+c->port+OFF_LVI);
+
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if(c != dmabuf->write_channel)
+			c = dmabuf->write_channel;
+		else
+			c = NULL;
+	}
+	
+	/* set the ready flag for the dma buffer */
+	dmabuf->ready = 1;
+
+#ifdef DEBUG
+	printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, "
+	       "fragsize = %d dmasize = %d\n",
+	       dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
+	       dmabuf->fragsize, dmabuf->dmasize);
+#endif
+
+	return 0;
+}
+
+static void __i810_update_lvi(struct i810_state *state, int rec)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int x, port;
+	
+	port = state->card->iobase;
+	if(rec)
+		port += dmabuf->read_channel->port;
+	else
+		port += dmabuf->write_channel->port;
+
+	/* if we are currently stopped, then our CIV is actually set to our
+	 * *last* sg segment and we are ready to wrap to the next.  However,
+	 * if we set our LVI to the last sg segment, then it won't wrap to
+	 * the next sg segment, it won't even get a start.  So, instead, when
+	 * we are stopped, we set both the LVI value and also we increment
+	 * the CIV value to the next sg segment to be played so that when
+	 * we call start_{dac,adc}, things will operate properly
+	 */
+	if (!dmabuf->enable && dmabuf->ready) {
+		if(rec && dmabuf->count < dmabuf->dmasize &&
+		   (dmabuf->trigger & PCM_ENABLE_INPUT))
+		{
+			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			__start_adc(state);
+			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
+		} else if (!rec && dmabuf->count &&
+			   (dmabuf->trigger & PCM_ENABLE_OUTPUT))
+		{
+			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			__start_dac(state);
+			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
+		}
+	}
+
+	/* swptr - 1 is the tail of our transfer */
+	x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize;
+	x /= dmabuf->fragsize;
+	outb(x, port+OFF_LVI);
+}
+
+static void i810_update_lvi(struct i810_state *state, int rec)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+
+	if(!dmabuf->ready)
+		return;
+	spin_lock_irqsave(&state->card->lock, flags);
+	__i810_update_lvi(state, rec);
+	spin_unlock_irqrestore(&state->card->lock, flags);
+}
+
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
+static void i810_update_ptr(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned hwptr;
+	int diff;
+
+	/* error handling and process wake up for DAC */
+	if (dmabuf->enable == ADC_RUNNING) {
+		/* update hardware pointer */
+		hwptr = i810_get_dma_addr(state, 1);
+		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+		printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
+		dmabuf->hwptr = hwptr;
+		dmabuf->total_bytes += diff;
+		dmabuf->count += diff;
+		if (dmabuf->count > dmabuf->dmasize) {
+			/* buffer underrun or buffer overrun */
+			/* this is normal for the end of a read */
+			/* only give an error if we went past the */
+			/* last valid sg entry */
+			if((inb(state->card->iobase + PI_CIV) & 31) !=
+			   (inb(state->card->iobase + PI_LVI) & 31)) {
+				printk(KERN_WARNING "i810_audio: DMA overrun on read\n");
+				dmabuf->error++;
+			}
+		}
+		if (dmabuf->count > dmabuf->userfragsize)
+			wake_up(&dmabuf->wait);
+	}
+	/* error handling and process wake up for DAC */
+	if (dmabuf->enable == DAC_RUNNING) {
+		/* update hardware pointer */
+		hwptr = i810_get_dma_addr(state, 0);
+		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+		printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
+		dmabuf->hwptr = hwptr;
+		dmabuf->total_bytes += diff;
+		dmabuf->count -= diff;
+		if (dmabuf->count < 0) {
+			/* buffer underrun or buffer overrun */
+			/* this is normal for the end of a write */
+			/* only give an error if we went past the */
+			/* last valid sg entry */
+			if((inb(state->card->iobase + PO_CIV) & 31) !=
+			   (inb(state->card->iobase + PO_LVI) & 31)) {
+				printk(KERN_WARNING "i810_audio: DMA overrun on write\n");
+				printk("i810_audio: CIV %d, LVI %d, hwptr %x, "
+					"count %d\n",
+					inb(state->card->iobase + PO_CIV) & 31,
+					inb(state->card->iobase + PO_LVI) & 31,
+					dmabuf->hwptr, dmabuf->count);
+				dmabuf->error++;
+			}
+		}
+		if (dmabuf->count < (dmabuf->dmasize-dmabuf->userfragsize))
+			wake_up(&dmabuf->wait);
+	}
+}
+
+static inline int i810_get_free_write_space(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int free;
+
+	i810_update_ptr(state);
+	// catch underruns during playback
+	if (dmabuf->count < 0) {
+		dmabuf->count = 0;
+		dmabuf->swptr = dmabuf->hwptr;
+	}
+	free = dmabuf->dmasize - dmabuf->count;
+	free -= (dmabuf->hwptr % dmabuf->fragsize);
+	if(free < 0)
+		return(0);
+	return(free);
+}
+
+static inline int i810_get_available_read_data(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int avail;
+
+	i810_update_ptr(state);
+	// catch overruns during record
+	if (dmabuf->count > dmabuf->dmasize) {
+		dmabuf->count = dmabuf->dmasize;
+		dmabuf->swptr = dmabuf->hwptr;
+	}
+	avail = dmabuf->count;
+	avail -= (dmabuf->hwptr % dmabuf->fragsize);
+	if(avail < 0)
+		return(0);
+	return(avail);
+}
+
+static int drain_dac(struct i810_state *state, int signals_allowed)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	unsigned long tmo;
+	int count;
+
+	if (!dmabuf->ready)
+		return 0;
+	if(dmabuf->mapped) {
+		stop_dac(state);
+		return 0;
+	}
+	add_wait_queue(&dmabuf->wait, &wait);
+	for (;;) {
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		i810_update_ptr(state);
+		count = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (count <= 0)
+			break;
+
+		/* 
+		 * This will make sure that our LVI is correct, that our
+		 * pointer is updated, and that the DAC is running.  We
+		 * have to force the setting of dmabuf->trigger to avoid
+		 * any possible deadlocks.
+		 */
+		if(!dmabuf->enable) {
+			dmabuf->trigger = PCM_ENABLE_OUTPUT;
+			i810_update_lvi(state,0);
+		}
+                if (signal_pending(current) && signals_allowed) {
+                        break;
+                }
+
+		/* It seems that we have to set the current state to
+		 * TASK_INTERRUPTIBLE every time to make the process
+		 * really go to sleep.  This also has to be *after* the
+		 * update_ptr() call because update_ptr is likely to
+		 * do a wake_up() which will unset this before we ever
+		 * try to sleep, resuling in a tight loop in this code
+		 * instead of actually sleeping and waiting for an
+		 * interrupt to wake us up!
+		 */
+		set_current_state(TASK_INTERRUPTIBLE);
+		/*
+		 * set the timeout to significantly longer than it *should*
+		 * take for the DAC to drain the DMA buffer
+		 */
+		tmo = (count * HZ) / (dmabuf->rate);
+		if (!schedule_timeout(tmo >= 2 ? tmo : 2)){
+			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");
+			count = 0;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dmabuf->wait, &wait);
+	if(count > 0 && signal_pending(current) && signals_allowed)
+		return -ERESTARTSYS;
+	stop_dac(state);
+	return 0;
+}
+
+static void i810_channel_interrupt(struct i810_card *card)
+{
+	int i, count;
+	
+#ifdef DEBUG_INTERRUPTS
+	printk("CHANNEL ");
+#endif
+	for(i=0;i<NR_HW_CH;i++)
+	{
+		struct i810_state *state = card->states[i];
+		struct i810_channel *c;
+		struct dmabuf *dmabuf;
+		unsigned long port = card->iobase;
+		u16 status;
+		
+		if(!state)
+			continue;
+		if(!state->dmabuf.ready)
+			continue;
+		dmabuf = &state->dmabuf;
+		if(dmabuf->enable & DAC_RUNNING) {
+			c=dmabuf->write_channel;
+		} else if(dmabuf->enable & ADC_RUNNING) {
+			c=dmabuf->read_channel;
+		} else	/* This can occur going from R/W to close */
+			continue;
+		
+		port+=c->port;
+
+		if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+			status = inw(port + OFF_PICB);
+		else
+			status = inw(port + OFF_SR);
+
+#ifdef DEBUG_INTERRUPTS
+		printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);
+#endif
+		if(status & DMA_INT_COMPLETE)
+		{
+			/* only wake_up() waiters if this interrupt signals
+			 * us being beyond a userfragsize of data open or
+			 * available, and i810_update_ptr() does that for
+			 * us
+			 */
+			i810_update_ptr(state);
+#ifdef DEBUG_INTERRUPTS
+			printk("COMP %d ", dmabuf->hwptr /
+					dmabuf->fragsize);
+#endif
+		}
+		if(status & (DMA_INT_LVI | DMA_INT_DCH))
+		{
+			/* wake_up() unconditionally on LVI and DCH */
+			i810_update_ptr(state);
+			wake_up(&dmabuf->wait);
+#ifdef DEBUG_INTERRUPTS
+			if(status & DMA_INT_LVI)
+				printk("LVI ");
+			if(status & DMA_INT_DCH)
+				printk("DCH -");
+#endif
+			if(dmabuf->enable & DAC_RUNNING)
+				count = dmabuf->count;
+			else
+				count = dmabuf->dmasize - dmabuf->count;
+			if(count > 0) {
+				outb(inb(port+OFF_CR) | 1, port+OFF_CR);
+#ifdef DEBUG_INTERRUPTS
+				printk(" CONTINUE ");
+#endif
+			} else {
+				if (dmabuf->enable & DAC_RUNNING)
+					__stop_dac(state);
+				if (dmabuf->enable & ADC_RUNNING)
+					__stop_adc(state);
+				dmabuf->enable = 0;
+				wake_up(&dmabuf->wait);
+#ifdef DEBUG_INTERRUPTS
+				printk(" STOP ");
+#endif
+			}
+		}
+		if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+			outw(status & DMA_INT_MASK, port + OFF_PICB);
+		else
+			outw(status & DMA_INT_MASK, port + OFF_SR);
+	}
+#ifdef DEBUG_INTERRUPTS
+	printk(")\n");
+#endif
+}
+
+static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct i810_card *card = (struct i810_card *)dev_id;
+	u32 status;
+
+	spin_lock(&card->lock);
+
+	status = inl(card->iobase + GLOB_STA);
+
+	if(!(status & INT_MASK)) 
+	{
+		spin_unlock(&card->lock);
+		return;  /* not for us */
+	}
+
+	if(status & (INT_PO|INT_PI|INT_MC))
+		i810_channel_interrupt(card);
+
+ 	/* clear 'em */
+	outl(status & INT_MASK, card->iobase + GLOB_STA);
+	spin_unlock(&card->lock);
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is
+   waiting to be copied to the user's buffer.  It is filled by the dma
+   machine and drained by this loop. */
+
+static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct i810_card *card=state ? state->card : 0;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned int swptr;
+	int cnt;
+        DECLARE_WAITQUEUE(waita, current);
+
+#ifdef DEBUG2
+	printk("i810_audio: i810_read called, count = %d\n", count);
+#endif
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (dmabuf->enable & DAC_RUNNING)
+		return -ENODEV;
+	if (!dmabuf->read_channel) {
+		dmabuf->ready = 0;
+		dmabuf->read_channel = card->alloc_rec_pcm_channel(card);
+		if (!dmabuf->read_channel) {
+			return -EBUSY;
+		}
+	}
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+
+        add_wait_queue(&dmabuf->wait, &waita);
+	while (count > 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&card->lock, flags);
+                if (PM_SUSPENDED(card)) {
+                        spin_unlock_irqrestore(&card->lock, flags);
+                        schedule();
+                        if (signal_pending(current)) {
+                                if (!ret) ret = -EAGAIN;
+                                break;
+                        }
+                        continue;
+                }
+		swptr = dmabuf->swptr;
+		cnt = i810_get_available_read_data(state);
+		// this is to make the copy_to_user simpler below
+		if(cnt > (dmabuf->dmasize - swptr))
+			cnt = dmabuf->dmasize - swptr;
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		/* Lop off the last two bits to force the code to always
+		 * write in full samples.  This keeps software that sets
+		 * O_NONBLOCK but doesn't check the return value of the
+		 * write call from getting things out of state where they
+		 * think a full 4 byte sample was written when really only
+		 * a portion was, resulting in odd sound and stereo
+		 * hysteresis.
+		 */
+		cnt &= ~0x3;
+		if (cnt <= 0) {
+			unsigned long tmo;
+			/*
+			 * Don't let us deadlock.  The ADC won't start if
+			 * dmabuf->trigger isn't set.  A call to SETTRIGGER
+			 * could have turned it off after we set it to on
+			 * previously.
+			 */
+			dmabuf->trigger = PCM_ENABLE_INPUT;
+			/*
+			 * This does three things.  Updates LVI to be correct,
+			 * makes sure the ADC is running, and updates the
+			 * hwptr.
+			 */
+			i810_update_lvi(state,1);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				goto done;
+			}
+			/* Set the timeout to how long it would take to fill
+			 * two of our buffers.  If we haven't been woke up
+			 * by then, then we know something is wrong.
+			 */
+			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
+			/* There are two situations when sleep_on_timeout returns, one is when
+			   the interrupt is serviced correctly and the process is waked up by
+			   ISR ON TIME. Another is when timeout is expired, which means that
+			   either interrupt is NOT serviced correctly (pending interrupt) or it
+			   is TOO LATE for the process to be scheduled to run (scheduler latency)
+			   which results in a (potential) buffer overrun. And worse, there is
+			   NOTHING we can do to prevent it. */
+			if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
+#ifdef DEBUG
+				printk(KERN_ERR "i810_audio: recording schedule timeout, "
+				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+				       dmabuf->hwptr, dmabuf->swptr);
+#endif
+				/* a buffer overrun, we delay the recovery until next time the
+				   while loop begin and we REALLY have space to record */
+			}
+			if (signal_pending(current)) {
+				ret = ret ? ret : -ERESTARTSYS;
+				goto done;
+			}
+			continue;
+		}
+
+		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
+			if (!ret) ret = -EFAULT;
+			goto done;
+		}
+
+		swptr = (swptr + cnt) % dmabuf->dmasize;
+
+		spin_lock_irqsave(&card->lock, flags);
+
+                if (PM_SUSPENDED(card)) {
+                        spin_unlock_irqrestore(&card->lock, flags);
+                        continue;
+                }
+		dmabuf->swptr = swptr;
+		dmabuf->count -= cnt;
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+ done:
+	i810_update_lvi(state,1);
+        set_current_state(TASK_RUNNING);
+        remove_wait_queue(&dmabuf->wait, &waita);
+
+	return ret;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
+   the soundcard.  it is drained by the dma machine and filled by this loop. */
+static ssize_t i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct i810_card *card=state ? state->card : 0;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned int swptr = 0;
+	int cnt, x;
+        DECLARE_WAITQUEUE(waita, current);
+
+#ifdef DEBUG2
+	printk("i810_audio: i810_write called, count = %d\n", count);
+#endif
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (dmabuf->enable & ADC_RUNNING)
+		return -ENODEV;
+	if (!dmabuf->write_channel) {
+		dmabuf->ready = 0;
+		dmabuf->write_channel = card->alloc_pcm_channel(card);
+		if(!dmabuf->write_channel)
+			return -EBUSY;
+	}
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+
+        add_wait_queue(&dmabuf->wait, &waita);
+	while (count > 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&state->card->lock, flags);
+                if (PM_SUSPENDED(card)) {
+                        spin_unlock_irqrestore(&card->lock, flags);
+                        schedule();
+                        if (signal_pending(current)) {
+                                if (!ret) ret = -EAGAIN;
+                                break;
+                        }
+                        continue;
+                }
+
+		swptr = dmabuf->swptr;
+		cnt = i810_get_free_write_space(state);
+		/* Bound the maximum size to how much we can copy to the
+		 * dma buffer before we hit the end.  If we have more to
+		 * copy then it will get done in a second pass of this
+		 * loop starting from the beginning of the buffer.
+		 */
+		if(cnt > (dmabuf->dmasize - swptr))
+			cnt = dmabuf->dmasize - swptr;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+#ifdef DEBUG2
+		printk(KERN_INFO "i810_audio: i810_write: %d bytes available space\n", cnt);
+#endif
+		if (cnt > count)
+			cnt = count;
+		/* Lop off the last two bits to force the code to always
+		 * write in full samples.  This keeps software that sets
+		 * O_NONBLOCK but doesn't check the return value of the
+		 * write call from getting things out of state where they
+		 * think a full 4 byte sample was written when really only
+		 * a portion was, resulting in odd sound and stereo
+		 * hysteresis.
+		 */
+		cnt &= ~0x3;
+		if (cnt <= 0) {
+			unsigned long tmo;
+			// There is data waiting to be played
+			/*
+			 * Force the trigger setting since we would
+			 * deadlock with it set any other way
+			 */
+			dmabuf->trigger = PCM_ENABLE_OUTPUT;
+			i810_update_lvi(state,0);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				goto ret;
+			}
+			/* Not strictly correct but works */
+			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
+			/* There are two situations when sleep_on_timeout returns, one is when
+			   the interrupt is serviced correctly and the process is waked up by
+			   ISR ON TIME. Another is when timeout is expired, which means that
+			   either interrupt is NOT serviced correctly (pending interrupt) or it
+			   is TOO LATE for the process to be scheduled to run (scheduler latency)
+			   which results in a (potential) buffer underrun. And worse, there is
+			   NOTHING we can do to prevent it. */
+			if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
+#ifdef DEBUG
+				printk(KERN_ERR "i810_audio: playback schedule timeout, "
+				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+				       dmabuf->hwptr, dmabuf->swptr);
+#endif
+				/* a buffer underrun, we delay the recovery until next time the
+				   while loop begin and we REALLY have data to play */
+				//return ret;
+			}
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				goto ret;
+			}
+			continue;
+		}
+		if (copy_from_user(dmabuf->rawbuf+swptr,buffer,cnt)) {
+			if (!ret) ret = -EFAULT;
+			goto ret;
+		}
+
+		swptr = (swptr + cnt) % dmabuf->dmasize;
+
+		spin_lock_irqsave(&state->card->lock, flags);
+                if (PM_SUSPENDED(card)) {
+                        spin_unlock_irqrestore(&card->lock, flags);
+                        continue;
+                }
+
+		dmabuf->swptr = swptr;
+		dmabuf->count += cnt;
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+	}
+	if (swptr % dmabuf->fragsize) {
+		x = dmabuf->fragsize - (swptr % dmabuf->fragsize);
+		memset(dmabuf->rawbuf + swptr, '\0', x);
+	}
+ret:
+	i810_update_lvi(state,0);
+        set_current_state(TASK_RUNNING);
+        remove_wait_queue(&dmabuf->wait, &waita);
+
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int i810_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	if(!dmabuf->ready)
+		return 0;
+	poll_wait(file, &dmabuf->wait, wait);
+	spin_lock_irqsave(&state->card->lock, flags);
+	if (dmabuf->enable & ADC_RUNNING ||
+	    dmabuf->trigger & PCM_ENABLE_INPUT) {
+		if (i810_get_available_read_data(state) >= 
+		    (signed)dmabuf->userfragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (dmabuf->enable & DAC_RUNNING ||
+	    dmabuf->trigger & PCM_ENABLE_OUTPUT) {
+		if (i810_get_free_write_space(state) >=
+		    (signed)dmabuf->userfragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&state->card->lock, flags);
+	return mask;
+}
+
+static int i810_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int ret = -EINVAL;
+	unsigned long size;
+
+	lock_kernel();
+	if (vma->vm_flags & VM_WRITE) {
+		if (!dmabuf->write_channel &&
+		    (dmabuf->write_channel =
+		     state->card->alloc_pcm_channel(state->card)) == NULL) {
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+	if (vma->vm_flags & VM_READ) {
+		if (!dmabuf->read_channel &&
+		    (dmabuf->read_channel = 
+		     state->card->alloc_rec_pcm_channel(state->card)) == NULL) {
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+	if ((ret = prog_dmabuf(state, 0)) != 0)
+		goto out;
+
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << dmabuf->buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+			     size, vma->vm_page_prot))
+		goto out;
+	dmabuf->mapped = 1;
+	dmabuf->trigger = 0;
+	ret = 0;
+#ifdef DEBUG_MMAP
+	printk("i810_audio: mmap'ed %ld bytes of data space\n", size);
+#endif
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct i810_channel *c = NULL;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	unsigned int i_glob_cnt;
+	int val = 0, ret;
+	struct ac97_codec *codec = state->card->ac97_codec[0];
+
+#ifdef DEBUG
+	printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *(int *)arg : 0);
+#endif
+
+	switch (cmd) 
+	{
+	case OSS_GETVERSION:
+#ifdef DEBUG
+		printk("OSS_GETVERSION\n");
+#endif
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_RESET:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_RESET\n");
+#endif
+		spin_lock_irqsave(&state->card->lock, flags);
+		if (dmabuf->enable == DAC_RUNNING) {
+			c = dmabuf->write_channel;
+			__stop_dac(state);
+		}
+		if (dmabuf->enable == ADC_RUNNING) {
+			c = dmabuf->read_channel;
+			__stop_adc(state);
+		}
+		if (c != NULL) {
+			outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */
+			outl((u32)state->card->chandma +
+			    c->num*sizeof(struct i810_channel),
+			    state->card->iobase+c->port+OFF_BDBAR);
+			outb(0, state->card->iobase+c->port+OFF_CIV);
+			outb(0, state->card->iobase+c->port+OFF_LVI);
+		}
+
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		synchronize_irq();
+		dmabuf->ready = 0;
+		dmabuf->swptr = dmabuf->hwptr = 0;
+		dmabuf->count = dmabuf->total_bytes = 0;
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SYNC\n");
+#endif
+		if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK)
+			return 0;
+		if((val = drain_dac(state, 1)))
+			return val;
+		dmabuf->total_bytes = 0;
+		return 0;
+
+	case SNDCTL_DSP_SPEED: /* set smaple rate */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SPEED\n");
+#endif
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_WRITE) {
+				if ( (state->card->ac97_status & SPDIF_ON) ) {  /* S/PDIF Enabled */
+					/* AD1886 only supports 48000, need to check that */
+					if ( i810_valid_spdif_rate ( codec, val ) ) {
+						/* Set DAC rate */
+                                        	i810_set_spdif_output ( state, -1, 0 );
+						stop_dac(state);
+						dmabuf->ready = 0;
+						spin_lock_irqsave(&state->card->lock, flags);
+						i810_set_dac_rate(state, val);
+						spin_unlock_irqrestore(&state->card->lock, flags);
+						/* Set S/PDIF transmitter rate. */
+						i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, val );
+	                                        if ( ! (state->card->ac97_status & SPDIF_ON) ) {
+							val = dmabuf->rate;
+						}
+					} else { /* Not a valid rate for S/PDIF, ignore it */
+						val = dmabuf->rate;
+					}
+				} else {
+					stop_dac(state);
+					dmabuf->ready = 0;
+					spin_lock_irqsave(&state->card->lock, flags);
+					i810_set_dac_rate(state, val);
+					spin_unlock_irqrestore(&state->card->lock, flags);
+				}
+			}
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+				dmabuf->ready = 0;
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_set_adc_rate(state, val);
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			}
+		}
+		return put_user(dmabuf->rate, (int *)arg);
+
+	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_STEREO\n");
+#endif
+		if (dmabuf->enable & DAC_RUNNING) {
+			stop_dac(state);
+		}
+		if (dmabuf->enable & ADC_RUNNING) {
+			stop_adc(state);
+		}
+		return put_user(1, (int *)arg);
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
+				return val;
+		}
+		if (file->f_mode & FMODE_READ) {
+			if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
+				return val;
+		}
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);
+#endif
+		return put_user(dmabuf->userfragsize, (int *)arg);
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETFMTS\n");
+#endif
+		return put_user(AFMT_S16_LE, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT: /* Select sample format */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETFMT\n");
+#endif
+		return put_user(AFMT_S16_LE, (int *)arg);
+
+	case SNDCTL_DSP_CHANNELS:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_CHANNELS\n");
+#endif
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (val > 0) {
+			if (dmabuf->enable & DAC_RUNNING) {
+				stop_dac(state);
+			}
+			if (dmabuf->enable & ADC_RUNNING) {
+				stop_adc(state);
+			}
+		} else {
+			return put_user(state->card->channels, (int *)arg);
+		}
+
+		/* ICH and ICH0 only support 2 channels */
+		if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 ) 
+			return put_user(2, (int *)arg);
+	
+		/* Multi-channel support was added with ICH2. Bits in */
+		/* Global Status and Global Control register are now  */
+		/* used to indicate this.                             */
+
+                i_glob_cnt = inl(state->card->iobase + GLOB_CNT);
+
+		/* Current # of channels enabled */
+		if ( i_glob_cnt & 0x0100000 )
+			ret = 4;
+		else if ( i_glob_cnt & 0x0200000 )
+			ret = 6;
+		else
+			ret = 2;
+
+		switch ( val ) {
+			case 2: /* 2 channels is always supported */
+				outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff));
+				/* Do we need to change mixer settings????  */
+				break;
+			case 4: /* Supported on some chipsets, better check first */
+				if ( state->card->channels >= 4 ) {
+					outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000));
+					/* Do we need to change mixer settings??? */
+				} else {
+					val = ret;
+				}
+				break;
+			case 6: /* Supported on some chipsets, better check first */
+				if ( state->card->channels >= 6 ) {
+					outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000));
+					/* Do we need to change mixer settings??? */
+				} else {
+					val = ret;
+				}
+				break;
+			default: /* nothing else is ever supported by the chipset */
+				val = ret;
+				break;
+		}
+
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */
+		/* we update the swptr to the end of the last sg segment then return */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_POST\n");
+#endif
+		if(!dmabuf->ready || (dmabuf->enable != DAC_RUNNING))
+			return 0;
+		if((dmabuf->swptr % dmabuf->fragsize) != 0) {
+			val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize);
+			dmabuf->swptr += val;
+			dmabuf->count += val;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if (dmabuf->subdivision)
+			return -EINVAL;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);
+#endif
+		dmabuf->subdivision = val;
+		dmabuf->ready = 0;
+		return 0;
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		dmabuf->ossfragsize = 1<<(val & 0xffff);
+		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+		if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
+			return -EINVAL;
+		/*
+		 * Bound the frag size into our allowed range of 256 - 4096
+		 */
+		if (dmabuf->ossfragsize < 256)
+			dmabuf->ossfragsize = 256;
+		else if (dmabuf->ossfragsize > 4096)
+			dmabuf->ossfragsize = 4096;
+		/*
+		 * The numfrags could be something reasonable, or it could
+		 * be 0xffff meaning "Give me as much as possible".  So,
+		 * we check the numfrags * fragsize doesn't exceed our
+		 * 64k buffer limit, nor is it less than our 8k minimum.
+		 * If it fails either one of these checks, then adjust the
+		 * number of fragments, not the size of them.  It's OK if
+		 * our number of fragments doesn't equal 32 or anything
+		 * like our hardware based number now since we are using
+		 * a different frag count for the hardware.  Before we get
+		 * into this though, bound the maxfrags to avoid overflow
+		 * issues.  A reasonable bound would be 64k / 256 since our
+		 * maximum buffer size is 64k and our minimum frag size is
+		 * 256.  On the other end, our minimum buffer size is 8k and
+		 * our maximum frag size is 4k, so the lower bound should
+		 * be 2.
+		 */
+
+		if(dmabuf->ossmaxfrags > 256)
+			dmabuf->ossmaxfrags = 256;
+		else if (dmabuf->ossmaxfrags < 2)
+			dmabuf->ossmaxfrags = 2;
+
+		val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
+		while (val < 8192) {
+		    val <<= 1;
+		    dmabuf->ossmaxfrags <<= 1;
+		}
+		while (val > 65536) {
+		    val >>= 1;
+		    dmabuf->ossmaxfrags >>= 1;
+		}
+		dmabuf->ready = 0;
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
+			dmabuf->ossfragsize, dmabuf->ossmaxfrags);
+#endif
+
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		i810_update_ptr(state);
+		abinfo.fragsize = dmabuf->userfragsize;
+		abinfo.fragstotal = dmabuf->userfrags;
+		if (dmabuf->mapped)
+ 			abinfo.bytes = dmabuf->dmasize;
+  		else
+ 			abinfo.bytes = i810_get_free_write_space(state);
+		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+		printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes,
+			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
+#endif
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		val = i810_get_free_write_space(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.ptr = dmabuf->hwptr;
+		cinfo.blocks = val/dmabuf->userfragsize;
+		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+			dmabuf->count += val;
+			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
+			__i810_update_lvi(state, 0);
+		}
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+		printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
+			cinfo.blocks, cinfo.ptr, dmabuf->count);
+#endif
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		abinfo.bytes = i810_get_available_read_data(state);
+		abinfo.fragsize = dmabuf->userfragsize;
+		abinfo.fragstotal = dmabuf->userfrags;
+		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+		printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes,
+			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
+#endif
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		val = i810_get_available_read_data(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = val/dmabuf->userfragsize;
+		cinfo.ptr = dmabuf->hwptr;
+		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
+			dmabuf->count -= val;
+			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
+			__i810_update_lvi(state, 1);
+		}
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+		printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
+			cinfo.blocks, cinfo.ptr, dmabuf->count);
+#endif
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_NONBLOCK:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_NONBLOCK\n");
+#endif
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETCAPS\n");
+#endif
+	    return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
+			    (int *)arg);
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger);
+#endif
+		return put_user(dmabuf->trigger, (int *)arg);
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+#if defined(DEBUG) || defined(DEBUG_MMAP)
+		printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
+#endif
+		if( !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
+			stop_adc(state);
+		}
+		if( !(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) {
+			stop_dac(state);
+		}
+		dmabuf->trigger = val;
+		if(val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) {
+			if (!dmabuf->write_channel) {
+				dmabuf->ready = 0;
+				dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+				if (!dmabuf->write_channel)
+					return -EBUSY;
+			}
+			if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+				return ret;
+			if (dmabuf->mapped) {
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_update_ptr(state);
+				dmabuf->count = 0;
+				dmabuf->swptr = dmabuf->hwptr;
+				dmabuf->count = i810_get_free_write_space(state);
+				dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
+				__i810_update_lvi(state, 0);
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			} else
+				start_dac(state);
+		}
+		if(val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) {
+			if (!dmabuf->read_channel) {
+				dmabuf->ready = 0;
+				dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
+				if (!dmabuf->read_channel)
+					return -EBUSY;
+			}
+			if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+				return ret;
+			if (dmabuf->mapped) {
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_update_ptr(state);
+				dmabuf->swptr = dmabuf->hwptr;
+				dmabuf->count = 0;
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			}
+			i810_update_lvi(state, 1);
+			start_adc(state);
+		}
+		return 0;
+
+	case SNDCTL_DSP_SETDUPLEX:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETDUPLEX\n");
+#endif
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&state->card->lock, flags);
+		i810_update_ptr(state);
+		val = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count);
+#endif
+		return put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_RATE:
+#ifdef DEBUG
+		printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate);
+#endif
+		return put_user(dmabuf->rate, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+#ifdef DEBUG
+		printk("SOUND_PCM_READ_CHANNELS\n");
+#endif
+		return put_user(2, (int *)arg);
+
+	case SOUND_PCM_READ_BITS:
+#ifdef DEBUG
+		printk("SOUND_PCM_READ_BITS\n");
+#endif
+		return put_user(AFMT_S16_LE, (int *)arg);
+
+	case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETSPDIF\n");
+#endif
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		/* Check to make sure the codec supports S/PDIF transmitter */
+
+		if((state->card->ac97_features & 4)) {
+			/* mask out the transmitter speed bits so the user can't set them */
+			val &= ~0x3000;
+
+			/* Add the current transmitter speed bits to the passed value */
+			ret = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
+			val |= (ret & 0x3000);
+
+			i810_ac97_set(codec, AC97_SPDIF_CONTROL, val);
+			if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) {
+				printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val);
+				return -EFAULT;
+			}
+		}
+#ifdef DEBUG
+		else 
+			printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
+#endif
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETSPDIF\n");
+#endif
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		/* Check to make sure the codec supports S/PDIF transmitter */
+
+		if(!(state->card->ac97_features & 4)) {
+#ifdef DEBUG
+			printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
+#endif
+			val = 0;
+		} else {
+			val = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
+		}
+		//return put_user((val & 0xcfff), (int *)arg);
+		return put_user(val, (int *)arg);
+   			
+	case SNDCTL_DSP_GETCHANNELMASK:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETCHANNELMASK\n");
+#endif
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		
+		/* Based on AC'97 DAC support, not ICH hardware */
+		val = DSP_BIND_FRONT;
+		if ( state->card->ac97_features & 0x0004 )
+			val |= DSP_BIND_SPDIF;
+
+		if ( state->card->ac97_features & 0x0080 )
+			val |= DSP_BIND_SURR;
+		if ( state->card->ac97_features & 0x0140 )
+			val |= DSP_BIND_CENTER_LFE;
+
+		return put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_BIND_CHANNEL:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_BIND_CHANNEL\n");
+#endif
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if ( val == DSP_BIND_QUERY ) {
+			val = DSP_BIND_FRONT; /* Always report this as being enabled */
+			if ( state->card->ac97_status & SPDIF_ON ) 
+				val |= DSP_BIND_SPDIF;
+			else {
+				if ( state->card->ac97_status & SURR_ON )
+					val |= DSP_BIND_SURR;
+				if ( state->card->ac97_status & CENTER_LFE_ON )
+					val |= DSP_BIND_CENTER_LFE;
+			}
+		} else {  /* Not a query, set it */
+			if (!(file->f_mode & FMODE_WRITE))
+				return -EINVAL;
+			if ( dmabuf->enable == DAC_RUNNING ) {
+				stop_dac(state);
+			}
+			if ( val & DSP_BIND_SPDIF ) {  /* Turn on SPDIF */
+				/*  Ok, this should probably define what slots
+				 *  to use. For now, we'll only set it to the
+				 *  defaults:
+				 * 
+				 *   non multichannel codec maps to slots 3&4
+				 *   2 channel codec maps to slots 7&8
+				 *   4 channel codec maps to slots 6&9
+				 *   6 channel codec maps to slots 10&11
+				 *
+				 *  there should be some way for the app to
+				 *  select the slot assignment.
+				 */
+	
+				i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate );
+				if ( !(state->card->ac97_status & SPDIF_ON) )
+					val &= ~DSP_BIND_SPDIF;
+			} else {
+				int mask;
+				int channels;
+
+				/* Turn off S/PDIF if it was on */
+				if ( state->card->ac97_status & SPDIF_ON ) 
+					i810_set_spdif_output ( state, -1, 0 );
+				
+				mask = val & (DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE);
+				switch (mask) {
+					case DSP_BIND_FRONT:
+						channels = 2;
+						break;
+					case DSP_BIND_FRONT|DSP_BIND_SURR:
+						channels = 4;
+						break;
+					case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE:
+						channels = 6;
+						break;
+					default:
+						val = DSP_BIND_FRONT;
+						channels = 2;
+						break;
+				}
+				i810_set_dac_channels ( state, channels );
+
+				/* check that they really got turned on */
+				if ( !state->card->ac97_status & SURR_ON )
+					val &= ~DSP_BIND_SURR;
+				if ( !state->card->ac97_status & CENTER_LFE_ON )
+					val &= ~DSP_BIND_CENTER_LFE;
+			}
+		}
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_WRITE_FILTER:
+	case SOUND_PCM_READ_FILTER:
+#ifdef DEBUG
+		printk("SNDCTL_* -EINVAL\n");
+#endif
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+static int i810_open(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	struct i810_card *card = devs;
+	struct i810_state *state = NULL;
+	struct dmabuf *dmabuf = NULL;
+
+	/* find an avaiable virtual channel (instance of /dev/dsp) */
+	while (card != NULL) {
+		/*
+		 * If we are initializing and then fail, card could go
+		 * away unuexpectedly while we are in the for() loop.
+		 * So, check for card on each iteration before we check
+		 * for card->initializing to avoid a possible oops.
+		 * This usually only matters for times when the driver is
+		 * autoloaded by kmod.
+		 */
+		for (i = 0; i < 50 && card && card->initializing; i++) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/20);
+		}
+		for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) {
+			if (card->states[i] == NULL) {
+				state = card->states[i] = (struct i810_state *)
+					kmalloc(sizeof(struct i810_state), GFP_KERNEL);
+				if (state == NULL)
+					return -ENOMEM;
+				memset(state, 0, sizeof(struct i810_state));
+				dmabuf = &state->dmabuf;
+				goto found_virt;
+			}
+		}
+		card = card->next;
+	}
+	/* no more virtual channel avaiable */
+	if (!state)
+		return -ENODEV;
+
+found_virt:
+	/* initialize the virtual channel */
+	state->virt = i;
+	state->card = card;
+	state->magic = I810_STATE_MAGIC;
+	init_waitqueue_head(&dmabuf->wait);
+	init_MUTEX(&state->open_sem);
+	file->private_data = state;
+	dmabuf->trigger = 0;
+
+	/* allocate hardware channels */
+	if(file->f_mode & FMODE_READ) {
+		if((dmabuf->read_channel = card->alloc_rec_pcm_channel(card)) == NULL) {
+			kfree (card->states[i]);
+			card->states[i] = NULL;;
+			return -EBUSY;
+		}
+		dmabuf->trigger |= PCM_ENABLE_INPUT;
+		i810_set_adc_rate(state, 8000);
+	}
+	if(file->f_mode & FMODE_WRITE) {
+		if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) {
+			kfree (card->states[i]);
+			card->states[i] = NULL;;
+			return -EBUSY;
+		}
+		/* Initialize to 8kHz?  What if we don't support 8kHz? */
+		/*  Let's change this to check for S/PDIF stuff */
+	
+		dmabuf->trigger |= PCM_ENABLE_OUTPUT;
+		if ( spdif_locked ) {
+			i810_set_dac_rate(state, spdif_locked);
+			i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked);
+		} else {
+			i810_set_dac_rate(state, 8000);
+		}
+	}
+		
+	/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
+	   should be default to unsigned 8-bits, mono, with sample rate 8kHz and
+	   /dev/dspW will accept 16-bits sample, but we don't support those so we
+	   set it immediately to stereo and 16bit, which is all we do support */
+	dmabuf->fmt |= I810_FMT_16BIT | I810_FMT_STEREO;
+	dmabuf->ossfragsize = 0;
+	dmabuf->ossmaxfrags  = 0;
+	dmabuf->subdivision  = 0;
+
+	state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+
+	return 0;
+}
+
+static int i810_release(struct inode *inode, struct file *file)
+{
+	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct i810_card *card = state->card;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+
+	lock_kernel();
+
+	/* stop DMA state machine and free DMA buffers/channels */
+	if(dmabuf->trigger & PCM_ENABLE_OUTPUT) {
+		drain_dac(state, 0);
+	}
+	if(dmabuf->trigger & PCM_ENABLE_INPUT) {
+		stop_adc(state);
+	}
+	spin_lock_irqsave(&card->lock, flags);
+	dealloc_dmabuf(state);
+	if (file->f_mode & FMODE_WRITE) {
+		state->card->free_pcm_channel(state->card, dmabuf->write_channel->num);
+	}
+	if (file->f_mode & FMODE_READ) {
+		state->card->free_pcm_channel(state->card, dmabuf->read_channel->num);
+	}
+
+	state->card->states[state->virt] = NULL;
+	kfree(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+	unlock_kernel();
+
+	return 0;
+}
+
+static /*const*/ struct file_operations i810_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		i810_read,
+	write:		i810_write,
+	poll:		i810_poll,
+	ioctl:		i810_ioctl,
+	mmap:		i810_mmap,
+	open:		i810_open,
+	release:	i810_release,
+};
+
+/* Write AC97 codec registers */
+
+static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg)
+{
+	struct i810_card *card = dev->private_data;
+	int count = 100;
+	u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));
+
+	while(count-- && (inb(card->iobase + CAS) & 1)) 
+		udelay(1);
+	
+	return inw(card->ac97base + reg_set);
+}
+
+static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data)
+{
+	struct i810_card *card = dev->private_data;
+	int count = 100;
+	u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));
+
+	while(count-- && (inb(card->iobase + CAS) & 1)) 
+		udelay(1);
+	outw(data, card->ac97base + reg_set);
+}
+
+
+/* OSS /dev/mixer file operation methods */
+
+static int i810_open_mixdev(struct inode *inode, struct file *file)
+{
+	int i;
+	unsigned int minor = minor(inode->i_rdev);
+	struct i810_card *card = devs;
+
+	for (card = devs; card != NULL; card = card->next) {
+		/*
+		 * If we are initializing and then fail, card could go
+		 * away unuexpectedly while we are in the for() loop.
+		 * So, check for card on each iteration before we check
+		 * for card->initializing to avoid a possible oops.
+		 * This usually only matters for times when the driver is
+		 * autoloaded by kmod.
+		 */
+		for (i = 0; i < 50 && card && card->initializing; i++) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/20);
+		}
+		for (i = 0; i < NR_AC97 && card && !card->initializing; i++)
+			if (card->ac97_codec[i] != NULL &&
+			    card->ac97_codec[i]->dev_mixer == minor) {
+				file->private_data = card->ac97_codec[i];
+				return 0;
+			}
+	}
+	return -ENODEV;
+}
+
+static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations i810_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		i810_ioctl_mixdev,
+	open:		i810_open_mixdev,
+};
+
+/* AC97 codec initialisation.  These small functions exist so we don't
+   duplicate code between module init and apm resume */
+
+static inline int i810_ac97_exists(struct i810_card *card,int ac97_number)
+{
+	u32 reg = inl(card->iobase + GLOB_STA);
+	return (reg & (0x100 << ac97_number));
+}
+
+static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec)
+{
+	i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
+	i810_ac97_set(codec,AC97_EXTENDED_STATUS,
+		      i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
+	
+	return (i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1);
+}
+
+
+static int i810_ac97_probe_and_powerup(struct i810_card *card,struct ac97_codec *codec)
+{
+	/* Returns 0 on failure */
+	int i;
+
+	if (ac97_probe_codec(codec) == 0) return 0;
+	
+	/* power it all up */
+	i810_ac97_set(codec, AC97_POWER_CONTROL,
+		      i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
+	/* wait for analog ready */
+	for (i=10;
+	     i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf);
+	     i--)
+	{
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/20);
+	} 
+	return i;
+}
+
+/* if I knew what this did, I'd give it a better name */
+static int i810_ac97_random_init_stuff(struct i810_card *card)
+{	
+	u32 reg = inl(card->iobase + GLOB_CNT);
+	int i;
+
+	if((reg&2)==0)	/* Cold required */
+		reg|=2;
+	else
+		reg|=4;	/* Warm */
+		
+	reg&=~8;	/* ACLink on */
+	outl(reg , card->iobase + GLOB_CNT);
+	
+	for(i=0;i<10;i++)
+	{
+		if((inl(card->iobase+GLOB_CNT)&4)==0)
+			break;
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/20);
+	}
+	if(i==10)
+	{
+		printk(KERN_ERR "i810_audio: AC'97 reset failed.\n");
+		return 0;
+	}
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/2);
+	reg = inl(card->iobase + GLOB_STA);
+	inw(card->ac97base);
+	return 1;
+}
+
+static int __init i810_ac97_init(struct i810_card *card)
+{
+	int num_ac97 = 0;
+	int total_channels = 0;
+	struct ac97_codec *codec;
+	u16 eid;
+	u32 reg;
+
+	if(!i810_ac97_random_init_stuff(card)) return 0;
+
+	/* Number of channels supported */
+	/* What about the codec?  Just because the ICH supports */
+	/* multiple channels doesn't mean the codec does.       */
+	/* we'll have to modify this in the codec section below */
+	/* to reflect what the codec has.                       */
+	/* ICH and ICH0 only support 2 channels so don't bother */
+	/* to check....                                         */
+
+	card->channels = 2;
+	reg = inl(card->iobase + GLOB_STA);
+	if ( reg & 0x0200000 )
+		card->channels = 6;
+	else if ( reg & 0x0100000 )
+		card->channels = 4;
+	printk("i810_audio: Audio Controller supports %d channels.\n", card->channels);
+		
+	inw(card->ac97base);
+
+	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+
+		/* Assume codec isn't available until we go through the
+		 * gauntlet below */
+		card->ac97_codec[num_ac97] = NULL;
+
+		/* The ICH programmer's reference says you should   */
+		/* check the ready status before probing. So we chk */
+		/*   What do we do if it's not ready?  Wait and try */
+		/*   again, or abort?                               */
+		if (!i810_ac97_exists(card,num_ac97)) {
+			if(num_ac97 == 0)
+				printk(KERN_ERR "i810_audio: Primary codec not ready.\n");
+			break; /* I think this works, if not ready stop */
+		}
+
+		if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		memset(codec, 0, sizeof(struct ac97_codec));
+
+		/* initialize some basic codec information, other fields will be filled
+		   in ac97_probe_codec */
+		codec->private_data = card;
+		codec->id = num_ac97;
+
+		codec->codec_read = i810_ac97_get;
+		codec->codec_write = i810_ac97_set;
+	
+		if(!i810_ac97_probe_and_powerup(card,codec)) {
+			printk("i810_audio: timed out waiting for codec %d analog ready", num_ac97);
+			kfree(codec);
+			break;	/* it didn't work */
+		}
+		/* Store state information about S/PDIF transmitter */
+		card->ac97_status = 0;
+		
+		/* Don't attempt to get eid until powerup is complete */
+		eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
+		
+		if(eid==0xFFFFFF)
+		{
+			printk(KERN_WARNING "i810_audio: no codec attached ?\n");
+			kfree(codec);
+			break;
+		}
+		
+		card->ac97_features = eid;
+				
+		/* Now check the codec for useful features to make up for
+		   the dumbness of the 810 hardware engine */
+
+		if(!(eid&0x0001))
+			printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
+		else
+		{
+			if(!i810_ac97_enable_variable_rate(codec)) {
+				printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n");
+				card->ac97_features&=~1;
+			}			
+		}
+   		
+		/* Determine how many channels the codec(s) support   */
+		/*   - The primary codec always supports 2            */
+		/*   - If the codec supports AMAP, surround DACs will */
+		/*     automaticlly get assigned to slots.            */
+		/*     * Check for surround DACs and increment if     */
+		/*       found.                                       */
+		/*   - Else check if the codec is revision 2.2        */
+		/*     * If surround DACs exist, assign them to slots */
+		/*       and increment channel count.                 */
+
+		/* All of this only applies to ICH2 and above. ICH    */
+		/* and ICH0 only support 2 channels.  ICH2 will only  */
+		/* support multiple codecs in a "split audio" config. */
+		/* as described above.                                */
+
+		/* TODO: Remove all the debugging messages!           */
+
+		if((eid & 0xc000) == 0) /* primary codec */
+			total_channels += 2; 
+
+		if(eid & 0x200) { /* GOOD, AMAP support */
+			if (eid & 0x0080) /* L/R Surround channels */
+				total_channels += 2;
+			if (eid & 0x0140) /* LFE and Center channels */
+				total_channels += 2;
+			printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels);
+		} else if (eid & 0x0400) {  /* this only works on 2.2 compliant codecs */
+			eid &= 0xffcf;
+			if((eid & 0xc000) != 0)	{
+				switch ( total_channels ) {
+					case 2:
+						/* Set dsa1, dsa0 to 01 */
+						eid |= 0x0010;
+						break;
+					case 4:
+						/* Set dsa1, dsa0 to 10 */
+						eid |= 0x0020;
+						break;
+					case 6:
+						/* Set dsa1, dsa0 to 11 */
+						eid |= 0x0030;
+						break;
+				}
+				total_channels += 2;
+			}
+			i810_ac97_set(codec, AC97_EXTENDED_ID, eid);
+			eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
+			printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid);
+			if (eid & 0x0080) /* L/R Surround channels */
+				total_channels += 2;
+			if (eid & 0x0140) /* LFE and Center channels */
+				total_channels += 2;
+			printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels);
+		} else {
+			printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels);
+		}
+
+		if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
+			printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
+			kfree(codec);
+			break;
+		}
+
+		card->ac97_codec[num_ac97] = codec;
+	}
+
+	/* pick the minimum of channels supported by ICHx or codec(s) */
+	card->channels = (card->channels > total_channels)?total_channels:card->channels;
+
+	return num_ac97;
+}
+
+static void __init i810_configure_clocking (void)
+{
+	struct i810_card *card;
+	struct i810_state *state;
+	struct dmabuf *dmabuf;
+	unsigned int i, offset, new_offset;
+	unsigned long flags;
+
+	card = devs;
+	/* We could try to set the clocking for multiple cards, but can you even have
+	 * more than one i810 in a machine?  Besides, clocking is global, so unless
+	 * someone actually thinks more than one i810 in a machine is possible and
+	 * decides to rewrite that little bit, setting the rate for more than one card
+	 * is a waste of time.
+	 */
+	if(card != NULL) {
+		state = card->states[0] = (struct i810_state *)
+					kmalloc(sizeof(struct i810_state), GFP_KERNEL);
+		if (state == NULL)
+			return;
+		memset(state, 0, sizeof(struct i810_state));
+		dmabuf = &state->dmabuf;
+
+		dmabuf->write_channel = card->alloc_pcm_channel(card);
+		state->virt = 0;
+		state->card = card;
+		state->magic = I810_STATE_MAGIC;
+		init_waitqueue_head(&dmabuf->wait);
+		init_MUTEX(&state->open_sem);
+		dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
+		dmabuf->trigger = PCM_ENABLE_OUTPUT;
+		i810_set_dac_rate(state, 48000);
+		if(prog_dmabuf(state, 0) != 0) {
+			goto config_out_nodmabuf;
+		}
+		if(dmabuf->dmasize < 16384) {
+			goto config_out;
+		}
+		dmabuf->count = dmabuf->dmasize;
+		outb(31,card->iobase+dmabuf->write_channel->port+OFF_LVI);
+		save_flags(flags);
+		cli();
+		start_dac(state);
+		offset = i810_get_dma_addr(state, 0);
+		mdelay(50);
+		new_offset = i810_get_dma_addr(state, 0);
+		stop_dac(state);
+		outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR);
+		restore_flags(flags);
+		i = new_offset - offset;
+#ifdef DEBUG
+		printk("i810_audio: %d bytes in 50 milliseconds\n", i);
+#endif
+		if(i == 0)
+			goto config_out;
+		i = i / 4 * 20;
+		if (i > 48500 || i < 47500) {
+			clocking = clocking * clocking / i;
+			printk("i810_audio: setting clocking to %d\n", clocking);
+		}
+config_out:
+		dealloc_dmabuf(state);
+config_out_nodmabuf:
+		state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num);
+		kfree(state);
+		card->states[0] = NULL;
+	}
+}
+
+/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered 
+   until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+   
+static int __init i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
+{
+	struct i810_card *card;
+
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (pci_set_dma_mask(pci_dev, I810_DMA_MASK)) {
+		printk(KERN_ERR "intel810: architecture does not support"
+		       " 32bit PCI busmaster DMA\n");
+		return -ENODEV;
+	}
+
+	if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "i810_audio: out of memory\n");
+		return -ENOMEM;
+	}
+	memset(card, 0, sizeof(*card));
+
+	card->initializing = 1;
+	card->iobase = pci_resource_start (pci_dev, 1);
+	card->ac97base = pci_resource_start (pci_dev, 0);
+	card->pci_dev = pci_dev;
+	card->pci_id = pci_id->device;
+	card->irq = pci_dev->irq;
+	card->next = devs;
+	card->magic = I810_CARD_MAGIC;
+#ifdef CONFIG_PM
+	card->pm_suspended=0;
+#endif
+	spin_lock_init(&card->lock);
+	devs = card;
+
+	pci_set_master(pci_dev);
+
+	printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n",
+	       card_names[pci_id->driver_data], card->iobase, card->ac97base, 
+	       card->irq);
+
+	card->alloc_pcm_channel = i810_alloc_pcm_channel;
+	card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel;
+	card->alloc_rec_mic_channel = i810_alloc_rec_mic_channel;
+	card->free_pcm_channel = i810_free_pcm_channel;
+
+	if ((card->channel = pci_alloc_consistent(pci_dev,
+	    sizeof(struct i810_channel)*NR_HW_CH, &card->chandma)) == NULL) {
+		printk(KERN_ERR "i810: cannot allocate channel DMA memory\n");
+		goto out_mem;
+	}
+
+	{ /* We may dispose of this altogether some time soon, so... */
+		struct i810_channel *cp = card->channel;
+
+		cp[0].offset = 0;
+		cp[0].port = 0x00;
+		cp[0].num = 0;
+		cp[1].offset = 0;
+		cp[1].port = 0x10;
+		cp[1].num = 1;
+		cp[2].offset = 0;
+		cp[2].port = 0x20;
+		cp[2].num = 2;
+	}
+
+	/* claim our iospace and irq */
+	request_region(card->iobase, 64, card_names[pci_id->driver_data]);
+	request_region(card->ac97base, 256, card_names[pci_id->driver_data]);
+
+	if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ,
+			card_names[pci_id->driver_data], card)) {
+		printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq);
+		release_region(card->iobase, 64);
+		release_region(card->ac97base, 256);
+		goto out_chan;
+	}
+
+	/* initialize AC97 codec and register /dev/mixer */
+	if (i810_ac97_init(card) <= 0) {
+		release_region(card->iobase, 64);
+		release_region(card->ac97base, 256);
+		free_irq(card->irq, card);
+		goto out_chan;
+	}
+	pci_set_drvdata(pci_dev, card);
+
+	if(clocking == 0) {
+		clocking = 48000;
+		i810_configure_clocking();
+	}
+
+	/* register /dev/dsp */
+	if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) {
+		int i;
+		printk(KERN_ERR "i810_audio: couldn't register DSP device!\n");
+		release_region(card->iobase, 64);
+		release_region(card->ac97base, 256);
+		free_irq(card->irq, card);
+		for (i = 0; i < NR_AC97; i++)
+		if (card->ac97_codec[i] != NULL) {
+			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+			kfree (card->ac97_codec[i]);
+		}
+		goto out_chan;
+	}
+ 	card->initializing = 0;
+	return 0;
+
+ out_chan:
+	pci_free_consistent(pci_dev, sizeof(struct i810_channel)*NR_HW_CH,
+	    card->channel, card->chandma);
+ out_mem:
+	kfree(card);
+	return -ENODEV;
+}
+
+static void __devexit i810_remove(struct pci_dev *pci_dev)
+{
+	int i;
+	struct i810_card *card = pci_get_drvdata(pci_dev);
+	/* free hardware resources */
+	free_irq(card->irq, devs);
+	release_region(card->iobase, 64);
+	release_region(card->ac97base, 256);
+
+	/* unregister audio devices */
+	for (i = 0; i < NR_AC97; i++)
+		if (card->ac97_codec[i] != NULL) {
+			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+			kfree (card->ac97_codec[i]);
+			card->ac97_codec[i] = NULL;
+		}
+	unregister_sound_dsp(card->dev_audio);
+	kfree(card);
+}
+
+#ifdef CONFIG_PM
+static int i810_pm_suspend(struct pci_dev *dev, u32 pm_state)
+{
+        struct i810_card *card = pci_get_drvdata(dev);
+        struct i810_state *state;
+	unsigned long flags;
+	struct dmabuf *dmabuf;
+	int i,num_ac97;
+#ifdef DEBUG
+	printk("i810_audio: i810_pm_suspend called\n");
+#endif
+	if(!card) return 0;
+	spin_lock_irqsave(&card->lock, flags);
+	card->pm_suspended=1;
+	for(i=0;i<NR_HW_CH;i++) {
+		state = card->states[i];
+		if(!state) continue;
+		/* this happens only if there are open files */
+		dmabuf = &state->dmabuf;
+		if(dmabuf->enable & DAC_RUNNING ||
+		   (dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) {
+			state->pm_saved_dac_rate=dmabuf->rate;
+			stop_dac(state);
+		} else {
+			state->pm_saved_dac_rate=0;
+		}
+		if(dmabuf->enable & ADC_RUNNING) {
+			state->pm_saved_adc_rate=dmabuf->rate;	
+			stop_adc(state);
+		} else {
+			state->pm_saved_adc_rate=0;
+		}
+		dmabuf->ready = 0;
+		dmabuf->swptr = dmabuf->hwptr = 0;
+		dmabuf->count = dmabuf->total_bytes = 0;
+	}
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	/* save mixer settings */
+	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+		struct ac97_codec *codec = card->ac97_codec[num_ac97];
+		if(!codec) continue;
+		for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) {
+			if((supported_mixer(codec,i)) &&
+			   (codec->read_mixer)) {
+				card->pm_saved_mixer_settings[i][num_ac97]=
+					codec->read_mixer(codec,i);
+			}
+		}
+	}
+	pci_save_state(dev,card->pm_save_state); /* XXX do we need this? */
+	pci_disable_device(dev); /* disable busmastering */
+	pci_set_power_state(dev,3); /* Zzz. */
+
+	return 0;
+}
+
+
+static int i810_pm_resume(struct pci_dev *dev)
+{
+	int num_ac97,i=0;
+	struct i810_card *card=pci_get_drvdata(dev);
+	pci_enable_device(dev);
+	pci_restore_state (dev,card->pm_save_state);
+
+	/* observation of a toshiba portege 3440ct suggests that the 
+	   hardware has to be more or less completely reinitialized from
+	   scratch after an apm suspend.  Works For Me.   -dan */
+
+	i810_ac97_random_init_stuff(card);
+
+	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+		struct ac97_codec *codec = card->ac97_codec[num_ac97];
+		/* check they haven't stolen the hardware while we were
+		   away */
+		if(!codec || !i810_ac97_exists(card,num_ac97)) {
+			if(num_ac97) continue;
+			else BUG();
+		}
+		if(!i810_ac97_probe_and_powerup(card,codec)) BUG();
+		
+		if((card->ac97_features&0x0001)) {
+			/* at probe time we found we could do variable
+			   rates, but APM suspend has made it forget
+			   its magical powers */
+			if(!i810_ac97_enable_variable_rate(codec)) BUG();
+		}
+		/* we lost our mixer settings, so restore them */
+		for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) {
+			if(supported_mixer(codec,i)){
+				int val=card->
+					pm_saved_mixer_settings[i][num_ac97];
+				codec->mixer_state[i]=val;
+				codec->write_mixer(codec,i,
+						   (val  & 0xff) ,
+						   ((val >> 8)  & 0xff) );
+			}
+		}
+	}
+
+	/* we need to restore the sample rate from whatever it was */
+	for(i=0;i<NR_HW_CH;i++) {
+		struct i810_state * state=card->states[i];
+		if(state) {
+			if(state->pm_saved_adc_rate)
+				i810_set_adc_rate(state,state->pm_saved_adc_rate);
+			if(state->pm_saved_dac_rate)
+				i810_set_dac_rate(state,state->pm_saved_dac_rate);
+		}
+	}
+
+	
+        card->pm_suspended = 0;
+
+	/* any processes that were reading/writing during the suspend
+	   probably ended up here */
+	for(i=0;i<NR_HW_CH;i++) {
+		struct i810_state *state = card->states[i];
+		if(state) wake_up(&state->dmabuf.wait);
+        }
+
+	return 0;
+}	
+#endif /* CONFIG_PM */
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Intel 810 audio support");
+MODULE_LICENSE("GPL");
+MODULE_PARM(ftsodell, "i");
+MODULE_PARM(clocking, "i");
+MODULE_PARM(strict_clocking, "i");
+MODULE_PARM(spdif_locked, "i");
+
+#define I810_MODULE_NAME "intel810_audio"
+
+static struct pci_driver i810_pci_driver = {
+	name:		I810_MODULE_NAME,
+	id_table:	i810_pci_tbl,
+	probe:		i810_probe,
+	remove:		__devexit_p(i810_remove),
+#ifdef CONFIG_PM
+	suspend:	i810_pm_suspend,
+	resume:		i810_pm_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init i810_init_module (void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+
+	printk(KERN_INFO "Intel 810 + AC97 Audio, version "
+	       DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
+
+	if (!pci_register_driver(&i810_pci_driver)) {
+		pci_unregister_driver(&i810_pci_driver);
+                return -ENODEV;
+	}
+	if(ftsodell != 0) {
+		printk("i810_audio: ftsodell is now a deprecated option.\n");
+	}
+	if(spdif_locked > 0 ) {
+		if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) {
+			printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked);
+		} else {
+			printk("i810_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n");
+			spdif_locked = 0;
+		}
+	}
+	
+	return 0;
+}
+
+static void __exit i810_cleanup_module (void)
+{
+	pci_unregister_driver(&i810_pci_driver);
+}
+
+module_init(i810_init_module);
+module_exit(i810_cleanup_module);
+
+/*
+Local Variables:
+c-basic-offset: 8
+End:
+*/
diff -Nru linux/sound/oss/ics2101.c linux-2.4.19-pre5-mjc/sound/oss/ics2101.c
--- linux/sound/oss/ics2101.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ics2101.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,246 @@
+/*
+ * sound/ics2101.c
+ *
+ * Driver for the ICS2101 mixer of GUS v3.7.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init()
+ */
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include <linux/ultrasound.h>
+
+#include "gus.h"
+#include "gus_hw.h"
+
+#define MIX_DEVS	(SOUND_MASK_MIC|SOUND_MASK_LINE| \
+			 SOUND_MASK_SYNTH| \
+  			 SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+extern int     *gus_osp;
+extern int      gus_base;
+static int      volumes[ICS_MIXDEVS];
+static int      left_fix[ICS_MIXDEVS] =
+{1, 1, 1, 2, 1, 2};
+static int      right_fix[ICS_MIXDEVS] =
+{2, 2, 2, 1, 2, 1};
+
+static int scale_vol(int vol)
+{
+	/*
+	 *  Experimental volume scaling by Risto Kankkunen.
+	 *  This should give smoother volume response than just
+	 *  a plain multiplication.
+	 */
+	 
+	int e;
+
+	if (vol < 0)
+		vol = 0;
+	if (vol > 100)
+		vol = 100;
+	vol = (31 * vol + 50) / 100;
+	e = 0;
+	if (vol)
+	{
+		while (vol < 16)
+		{
+			vol <<= 1;
+			e--;
+		}
+		vol -= 16;
+		e += 7;
+	}
+	return ((e << 4) + vol);
+}
+
+static void write_mix(int dev, int chn, int vol)
+{
+	int *selector;
+	unsigned long flags;
+	int ctrl_addr = dev << 3;
+	int attn_addr = dev << 3;
+
+	vol = scale_vol(vol);
+
+	if (chn == CHN_LEFT)
+	{
+		selector = left_fix;
+		ctrl_addr |= 0x00;
+		attn_addr |= 0x02;
+	}
+	else
+	{
+		selector = right_fix;
+		ctrl_addr |= 0x01;
+		attn_addr |= 0x03;
+	}
+
+	save_flags(flags);
+	cli();
+	outb((ctrl_addr), u_MixSelect);
+	outb((selector[dev]), u_MixData);
+	outb((attn_addr), u_MixSelect);
+	outb(((unsigned char) vol), u_MixData);
+	restore_flags(flags);
+}
+
+static int set_volumes(int dev, int vol)
+{
+	int left = vol & 0x00ff;
+	int right = (vol >> 8) & 0x00ff;
+
+	if (left < 0)
+		left = 0;
+	if (left > 100)
+		left = 100;
+	if (right < 0)
+		right = 0;
+	if (right > 100)
+		right = 100;
+
+	write_mix(dev, CHN_LEFT, left);
+	write_mix(dev, CHN_RIGHT, right);
+
+	vol = left + (right << 8);
+	volumes[dev] = vol;
+	return vol;
+}
+
+static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int val;
+	
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				return gus_default_mixer_ioctl(dev, cmd, arg);
+
+			case SOUND_MIXER_MIC:
+				val = set_volumes(DEV_MIC, val);
+				break;
+				
+			case SOUND_MIXER_CD:
+				val = set_volumes(DEV_CD, val);
+				break;
+
+			case SOUND_MIXER_LINE:
+				val = set_volumes(DEV_LINE, val);
+				break;
+
+			case SOUND_MIXER_SYNTH:
+				val = set_volumes(DEV_GF1, val);
+				break;
+
+			case SOUND_MIXER_VOLUME:
+				val = set_volumes(DEV_VOL, val);
+				break;
+
+			default:
+				return -EINVAL;
+			}
+			return put_user(val, (int *)arg);
+		} else {
+			switch (cmd & 0xff) {
+				/*
+				 * Return parameters
+				 */
+			case SOUND_MIXER_RECSRC:
+				return gus_default_mixer_ioctl(dev, cmd, arg);
+
+			case SOUND_MIXER_DEVMASK:
+				val = MIX_DEVS; 
+				break;
+
+			case SOUND_MIXER_STEREODEVS:
+				val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; 
+				break;
+
+			case SOUND_MIXER_RECMASK:
+				val = SOUND_MASK_MIC | SOUND_MASK_LINE; 
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				val = 0; 
+				break;
+
+			case SOUND_MIXER_MIC:
+				val = volumes[DEV_MIC];
+				break;
+				
+			case SOUND_MIXER_LINE:
+				val = volumes[DEV_LINE];
+				break;
+
+			case SOUND_MIXER_CD:
+				val = volumes[DEV_CD];
+				break;
+
+			case SOUND_MIXER_VOLUME:
+				val = volumes[DEV_VOL];
+				break;
+
+			case SOUND_MIXER_SYNTH:
+				val = volumes[DEV_GF1]; 
+				break;
+
+			default:
+				return -EINVAL;
+			}
+			return put_user(val, (int *)arg);
+		}
+	}
+	return -EINVAL;
+}
+
+static struct mixer_operations ics2101_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"ICS2101",
+	name:	"ICS2101 Multimedia Mixer",
+	ioctl:	ics2101_mixer_ioctl
+};
+
+int __init ics2101_mixer_init(void)
+{
+	int i;
+	int n;
+
+	if ((n = sound_alloc_mixerdev()) != -1)
+	{
+		mixer_devs[n] = &ics2101_mixer_operations;
+
+		/*
+		 * Some GUS v3.7 cards had some channels flipped. Disable
+		 * the flipping feature if the model id is other than 5.
+		 */
+
+		if (inb(u_MixSelect) != 5)
+		{
+			for (i = 0; i < ICS_MIXDEVS; i++)
+				left_fix[i] = 1;
+			for (i = 0; i < ICS_MIXDEVS; i++)
+				right_fix[i] = 2;
+		}
+		set_volumes(DEV_GF1, 0x5a5a);
+		set_volumes(DEV_CD, 0x5a5a);
+		set_volumes(DEV_MIC, 0x0000);
+		set_volumes(DEV_LINE, 0x5a5a);
+		set_volumes(DEV_VOL, 0x5a5a);
+		set_volumes(DEV_UNUSED, 0x0000);
+	}
+	return n;
+}
diff -Nru linux/sound/oss/ite8172.c linux-2.4.19-pre5-mjc/sound/oss/ite8172.c
--- linux/sound/oss/ite8172.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ite8172.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1952 @@
+/*
+ *      ite8172.c  --  ITE IT8172G Sound Driver.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@mvista.com or source@mvista.com
+ *
+ *  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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Module command line parameters:
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard OSS /dev/dsp device
+ *  /dev/mixer  standard OSS /dev/mixer device
+ *
+ * Notes:
+ *
+ *  1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are
+ *     taken, slightly modified or not at all, from the ES1371 driver,
+ *     so refer to the credits in es1371.c for those. The rest of the
+ *     code (probe, open, read, write, the ISR, etc.) is new.
+ *  2. The following support is untested:
+ *      * Memory mapping the audio buffers, and the ioctl controls that go
+ *        with it.
+ *      * S/PDIF output.
+ *  3. The following is not supported:
+ *      * I2S input.
+ *      * legacy audio mode.
+ *  4. Support for volume button interrupts is implemented but doesn't
+ *     work yet.
+ *
+ *  Revision history
+ *    02.08.2001  0.1   Initial release
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wrapper.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/it8172/it8172.h>
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#define IT8172_DEBUG
+#undef IT8172_VERBOSE_DEBUG
+#define DBG(x) {}
+
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+
+/*
+ * Audio Controller register bit definitions follow. See
+ * include/asm/it8172/it8172.h for register offsets.
+ */
+
+/* PCM Out Volume Reg */
+#define PCMOV_PCMOM	(1<<15)	/* PCM Out Mute default 1: mute */
+#define	PCMOV_PCMRCG_BIT 8	/* PCM Right channel Gain */
+#define	PCMOV_PCMRCG_MASK (0x1f<<PCMOV_PCMRCG_BIT)
+#define PCMOV_PCMLCG_BIT 0	/* PCM Left channel gain  */
+#define PCMOV_PCMLCG_MASK 0x1f
+
+/* FM Out Volume Reg */
+#define FMOV_FMOM       (1<<15)	/* FM Out Mute default 1: mute */
+#define	FMOV_FMRCG_BIT	8	/* FM Right channel Gain */
+#define	FMOV_FMRCG_MASK (0x1f<<FMOV_FMRCG_BIT)
+#define FMOV_FMLCG_BIT	0	/* FM Left channel gain  */
+#define FMOV_FMLCG_MASK 0x1f
+
+/* I2S Out Volume Reg */
+#define I2SV_I2SOM	 (1<<15) /* I2S Out Mute default 1: mute */
+#define	I2SV_I2SRCG_BIT	 8	 /* I2S Right channel Gain */
+#define	I2SV_I2SRCG_MASK (0x1f<<I2SV_I2SRCG_BIT)
+#define I2SV_I2SLCG_BIT	 0	 /* I2S Left channel gain  */
+#define I2SV_I2SLCG_MASK 0x1f
+
+/* Digital Recording Source Select Reg */
+#define	DRSS_BIT   0
+#define	DRSS_MASK  0x07
+#define   DRSS_AC97_PRIM 0
+#define   DRSS_FM        1
+#define   DRSS_I2S       2
+#define   DRSS_PCM       3
+#define   DRSS_AC97_SEC  4
+
+/* Playback/Capture Channel Control Registers */
+#define	CC_SM	        (1<<15)	/* Stereo, Mone 0: mono 1: stereo */
+#define	CC_DF	        (1<<14)	/* Data Format 0: 8 bit 1: 16 bit */
+#define CC_FMT_BIT      14
+#define CC_FMT_MASK     (0x03<<CC_FMT_BIT)
+#define CC_CF_BIT       12      /* Channel format (Playback only) */
+#define CC_CF_MASK      (0x03<<CC_CF_BIT)
+#define	  CC_CF_2	0
+#define   CC_CF_4	(1<<CC_CF_BIT)
+#define   CC_CF_6	(2<<CC_CF_BIT)
+#define CC_SR_BIT       8       /* sample Rate */
+#define CC_SR_MASK      (0x0f<<CC_SR_BIT)
+#define	  CC_SR_5500	0
+#define	  CC_SR_8000	(1<<CC_SR_BIT)
+#define	  CC_SR_9600	(2<<CC_SR_BIT)
+#define	  CC_SR_11025	(3<<CC_SR_BIT)
+#define	  CC_SR_16000	(4<<CC_SR_BIT)
+#define	  CC_SR_19200	(5<<CC_SR_BIT)
+#define	  CC_SR_22050	(6<<CC_SR_BIT)
+#define	  CC_SR_32000	(7<<CC_SR_BIT)
+#define	  CC_SR_38400	(8<<CC_SR_BIT)
+#define	  CC_SR_44100	(9<<CC_SR_BIT)
+#define	  CC_SR_48000	(10<<CC_SR_BIT)
+#define	CC_CSP	        (1<<7)	/* Channel stop 
+				 * 0: End of Current buffer
+				 * 1: Immediately stop when rec stop */
+#define CC_CP	        (1<<6)	/* Channel pause 0: normal, 1: pause */
+#define	CC_CA	        (1<<5)	/* Channel Action 0: Stop , 1: start */
+#define	CC_CB2L         (1<<2)	/* Cur. buf. 2 xfr is last 0: No, 1: Yes */
+#define CC_CB1L         (1<<1)	/* Cur. buf. 1 xfr is last 0: No, 1: Yes */
+#define CC_DE	        1	/* DFC/DFIFO Data Empty 1: empty, 0: not empty
+				 * (Playback only)
+				 */
+
+/* Codec Control Reg */
+#define CODECC_GME	(1<<9)	/* AC97 GPIO Mode enable */
+#define	CODECC_ATM	(1<<8)	/* AC97 ATE test mode 0: test 1: normal */
+#define	CODECC_WR	(1<<6)	/* AC97 Warn reset 1: warm reset , 0: Normal */
+#define	CODECC_CR	(1<<5)	/* AC97 Cold reset 1: Cold reset , 0: Normal */
+
+
+/* I2S Control Reg	*/
+#define	I2SMC_SR_BIT	 6	/* I2S Sampling rate 
+				 * 00: 48KHz, 01: 44.1 KHz, 10: 32 32 KHz */
+#define	I2SMC_SR_MASK    (0x03<<I2SMC_SR_BIT)
+#define	  I2SMC_SR_48000 0
+#define	  I2SMC_SR_44100 (1<<I2SMC_SR_BIT)
+#define	  I2SMC_SR_32000 (2<<I2SMC_SR_BIT)
+#define	I2SMC_SRSS	 (1<<5)	/* Sample Rate Source Select 1:S/W, 0: H/W */
+#define I2SMC_I2SF_BIT	 0	/* I2S Format */
+#define I2SMC_I2SF_MASK  0x03
+#define   I2SMC_I2SF_DAC 0
+#define   I2SMC_I2SF_ADC 2
+#define   I2SMC_I2SF_I2S 3
+
+
+/* Volume up, Down, Mute */
+#define	VS_VMP	(1<<2)	/* Volume mute 1: pushed, 0: not */
+#define	VS_VDP	(1<<1)	/* Volume Down 1: pushed, 0: not */
+#define VS_VUP	1	/* Volime Up 1: pushed, 0: not */
+
+/* SRC, Mixer test control/DFC status reg */
+#define SRCS_DPUSC      (1<<5)	/* DFC Playback underrun Status/clear */
+#define	SRCS_DCOSC	(1<<4)	/* DFC Capture Overrun Status/clear */
+#define SRCS_SIS	(1<<3)	/* SRC input select 1: Mixer, 0: Codec I/F */
+#define SRCS_CDIS_BIT	0	/* Codec Data Input Select */
+#define SRCS_CDIS_MASK  0x07
+#define   SRCS_CDIS_MIXER 0
+#define   SRCS_CDIS_PCM   1
+#define   SRCS_CDIS_I2S   2
+#define   SRCS_CDIS_FM    3
+#define   SRCS_CDIS_DFC   4
+
+
+/* Codec Index Reg command Port */
+#define CIRCP_CID_BIT   10
+#define CIRCP_CID_MASK  (0x03<<CIRCP_CID_BIT)
+#define CIRCP_CPS	(1<<9)	/* Command Port Status 0: ready, 1: busy */
+#define	CIRCP_DPVF	(1<<8)	/* Data Port Valid Flag 0: invalis, 1: valid */
+#define CIRCP_RWC	(1<<7)	/* Read/write command */
+#define CIRCP_CIA_BIT   0
+#define CIRCP_CIA_MASK  0x007F	/* Codec Index Address */
+
+/* Test Mode Control/Test group Select Control */
+
+/* General Control Reg */
+#define GC_VDC_BIT	6	/* Volume Division Control */
+#define GC_VDC_MASK     (0x03<<GC_VDC_BIT)
+#define   GC_VDC_NONE   0
+#define   GC_VDC_DIV2   (1<<GC_VDC_BIT)
+#define   GC_VDC_DIV4   (2<<GC_VDC_BIT)
+#define	GC_SOE	        (1<<2)	/* S/PDIF Output enable */
+#define	GC_SWR	        1	/* Software warn reset */
+
+/* Interrupt mask Control Reg */
+#define	IMC_VCIM	(1<<6)	/* Volume CNTL interrupt mask */
+#define	IMC_CCIM	(1<<1)	/* Capture Chan. iterrupt mask */
+#define	IMC_PCIM	1	/* Playback Chan. interrupt mask */
+
+/* Interrupt status/clear reg */
+#define	ISC_VCI	        (1<<6)	/* Volume CNTL interrupt 1: clears */
+#define	ISC_CCI	        (1<<1)	/* Capture Chan. interrupt 1: clears  */
+#define	ISC_PCI	        1	/* Playback Chan. interrupt 1: clears */
+
+/* misc stuff */
+#define POLL_COUNT   0x5000
+
+
+#define IT8172_MODULE_NAME "IT8172 audio"
+#define PFX IT8172_MODULE_NAME ": "
+
+
+/* --------------------------------------------------------------------- */
+
+struct it8172_state {
+    /* list of it8172 devices */
+    struct list_head devs;
+
+    /* the corresponding pci_dev structure */
+    struct pci_dev *dev;
+
+    /* soundcore stuff */
+    int dev_audio;
+
+    /* hardware resources */
+    unsigned long io;
+    unsigned int irq;
+
+    /* PCI ID's */
+    u16 vendor;
+    u16 device;
+    u8 rev; /* the chip revision */
+
+    /* options */
+    int spdif_volume; /* S/PDIF output is enabled if != -1 */
+
+#ifdef IT8172_DEBUG
+    /* debug /proc entry */
+    struct proc_dir_entry *ps;
+    struct proc_dir_entry *ac97_ps;
+#endif /* IT8172_DEBUG */
+
+    struct ac97_codec codec;
+
+    unsigned short pcc, capcc;
+    unsigned dacrate, adcrate;
+
+    spinlock_t lock;
+    struct semaphore open_sem;
+    mode_t open_mode;
+    wait_queue_head_t open_wait;
+
+    struct dmabuf {
+	void *rawbuf;
+	dma_addr_t dmaaddr;
+	unsigned buforder;
+	unsigned numfrag;
+	unsigned fragshift;
+	void* nextIn;
+	void* nextOut;
+	int count;
+	int curBufPtr;
+	unsigned total_bytes;
+	unsigned error; /* over/underrun */
+	wait_queue_head_t wait;
+	/* redundant, but makes calculations easier */
+	unsigned fragsize;
+	unsigned dmasize;
+	unsigned fragsamples;
+	/* OSS stuff */
+	unsigned mapped:1;
+	unsigned ready:1;
+	unsigned stopped:1;
+	unsigned ossfragshift;
+	int ossmaxfrags;
+	unsigned subdivision;
+    } dma_dac, dma_adc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(devs);
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned ld2(unsigned int x)
+{
+    unsigned r = 0;
+	
+    if (x >= 0x10000) {
+	x >>= 16;
+	r += 16;
+    }
+    if (x >= 0x100) {
+	x >>= 8;
+	r += 8;
+    }
+    if (x >= 0x10) {
+	x >>= 4;
+	r += 4;
+    }
+    if (x >= 4) {
+	x >>= 2;
+	r += 2;
+    }
+    if (x >= 2)
+	r++;
+    return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void it8172_delay(int msec)
+{
+    unsigned long tmo;
+    signed long tmo2;
+
+    if (in_interrupt())
+	return;
+    
+    tmo = jiffies + (msec*HZ)/1000;
+    for (;;) {
+	tmo2 = tmo - jiffies;
+	if (tmo2 <= 0)
+	    break;
+	schedule_timeout(tmo2);
+    }
+}
+
+
+static unsigned short
+get_compat_rate(unsigned* rate)
+{
+    unsigned rate_out = *rate;
+    unsigned short sr;
+    
+    if (rate_out >= 46050) {
+	sr = CC_SR_48000; rate_out = 48000;
+    } else if (rate_out >= 41250) {
+	sr = CC_SR_44100; rate_out = 44100;
+    } else if (rate_out >= 35200) {
+	sr = CC_SR_38400; rate_out = 38400;
+    } else if (rate_out >= 27025) {
+	sr = CC_SR_32000; rate_out = 32000;
+    } else if (rate_out >= 20625) {
+	sr = CC_SR_22050; rate_out = 22050;
+    } else if (rate_out >= 17600) {
+	sr = CC_SR_19200; rate_out = 19200;
+    } else if (rate_out >= 13513) {
+	sr = CC_SR_16000; rate_out = 16000;
+    } else if (rate_out >= 10313) {
+	sr = CC_SR_11025; rate_out = 11025;
+    } else if (rate_out >= 8800) {
+	sr = CC_SR_9600; rate_out = 9600;
+    } else if (rate_out >= 6750) {
+	sr = CC_SR_8000; rate_out = 8000;
+    } else {
+	sr = CC_SR_5500; rate_out = 5500;
+    }
+
+    *rate = rate_out;
+    return sr;
+}
+
+static void set_adc_rate(struct it8172_state *s, unsigned rate)
+{
+    unsigned long flags;
+    unsigned short sr;
+    
+    sr = get_compat_rate(&rate);
+
+    spin_lock_irqsave(&s->lock, flags);
+    s->capcc &= ~CC_SR_MASK;
+    s->capcc |= sr;
+    outw(s->capcc, s->io+IT_AC_CAPCC);
+    spin_unlock_irqrestore(&s->lock, flags);
+
+    s->adcrate = rate;
+}
+
+
+static void set_dac_rate(struct it8172_state *s, unsigned rate)
+{
+    unsigned long flags;
+    unsigned short sr;
+    
+    sr = get_compat_rate(&rate);
+
+    spin_lock_irqsave(&s->lock, flags);
+    s->pcc &= ~CC_SR_MASK;
+    s->pcc |= sr;
+    outw(s->pcc, s->io+IT_AC_PCC);
+    spin_unlock_irqrestore(&s->lock, flags);
+
+    s->dacrate = rate;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static u16 rdcodec(struct ac97_codec *codec, u8 addr)
+{
+    struct it8172_state *s = (struct it8172_state *)codec->private_data;
+    unsigned long flags;
+    unsigned short circp, data;
+    int i;
+    
+    spin_lock_irqsave(&s->lock, flags);
+
+    for (i = 0; i < POLL_COUNT; i++)
+	if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS))
+	    break;
+    if (i == POLL_COUNT)
+	printk(KERN_INFO PFX "rdcodec: codec ready poll expired!\n");
+
+    circp = addr & CIRCP_CIA_MASK;
+    circp |= (codec->id << CIRCP_CID_BIT);
+    circp |= CIRCP_RWC; // read command
+    outw(circp, s->io+IT_AC_CIRCP);
+
+    /* now wait for the data */
+    for (i = 0; i < POLL_COUNT; i++)
+	if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF)
+	    break;
+    if (i == POLL_COUNT)
+	printk(KERN_INFO PFX "rdcodec: read poll expired!\n");
+
+    data = inw(s->io+IT_AC_CIRDP);
+    spin_unlock_irqrestore(&s->lock, flags);
+
+    return data;
+}
+
+
+static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
+{
+    struct it8172_state *s = (struct it8172_state *)codec->private_data;
+    unsigned long flags;
+    unsigned short circp;
+    int i;
+    
+    spin_lock_irqsave(&s->lock, flags);
+
+    for (i = 0; i < POLL_COUNT; i++)
+	if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS))
+	    break;
+    if (i == POLL_COUNT)
+	printk(KERN_INFO PFX "wrcodec: codec ready poll expired!\n");
+
+    circp = addr & CIRCP_CIA_MASK;
+    circp |= (codec->id << CIRCP_CID_BIT);
+    circp &= ~CIRCP_RWC; // write command
+
+    outw(data,  s->io+IT_AC_CIRDP);  // send data first
+    outw(circp, s->io+IT_AC_CIRCP);
+
+    spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void waitcodec(struct ac97_codec *codec)
+{
+    unsigned short temp;
+
+    /* codec_wait is used to wait for a ready state after
+       an AC97_RESET. */
+    it8172_delay(10);
+
+    temp = rdcodec(codec, 0x26);
+
+    // If power down, power up
+    if (temp & 0x3f00) {
+	// Power on
+	wrcodec(codec, 0x26, 0);
+	it8172_delay(100);
+	// Reread
+	temp = rdcodec(codec, 0x26);
+    }
+    
+    // Check if Codec REF,ANL,DAC,ADC ready***/
+    if ((temp & 0x3f0f) != 0x000f) {
+	printk(KERN_INFO PFX "codec reg 26 status (0x%x) not ready!!\n",
+	       temp);
+	return;
+    }
+}
+
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct it8172_state *s)
+{
+    struct dmabuf* db = &s->dma_adc;
+    unsigned long flags;
+    unsigned char imc;
+    
+    if (db->stopped)
+	return;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L);
+    s->capcc |= CC_CSP;
+    outw(s->capcc, s->io+IT_AC_CAPCC);
+    
+    // disable capture interrupt
+    imc = inb(s->io+IT_AC_IMC);
+    outb(imc | IMC_CCIM, s->io+IT_AC_IMC);
+
+    db->stopped = 1;
+
+    spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac(struct it8172_state *s)
+{
+    struct dmabuf* db = &s->dma_dac;
+    unsigned long flags;
+    unsigned char imc;
+    
+    if (db->stopped)
+	return;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L);
+    s->pcc |= CC_CSP;
+    outw(s->pcc, s->io+IT_AC_PCC);
+    
+    // disable playback interrupt
+    imc = inb(s->io+IT_AC_IMC);
+    outb(imc | IMC_PCIM, s->io+IT_AC_IMC);
+
+    db->stopped = 1;
+    
+    spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac(struct it8172_state *s)
+{
+    struct dmabuf* db = &s->dma_dac;
+    unsigned long flags;
+    unsigned char imc;
+    unsigned long buf1, buf2;
+    
+    if (!db->stopped)
+	return;
+    
+    spin_lock_irqsave(&s->lock, flags);
+
+    // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize
+    buf1 = virt_to_bus(db->nextOut);
+    buf2 = buf1 + db->fragsize;
+    if (buf2 >= db->dmaaddr + db->dmasize)
+	buf2 -= db->dmasize;
+    
+    outl(buf1, s->io+IT_AC_PCB1STA);
+    outl(buf2, s->io+IT_AC_PCB2STA);
+    db->curBufPtr = IT_AC_PCB1STA;
+    
+    // enable playback interrupt
+    imc = inb(s->io+IT_AC_IMC);
+    outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC);
+
+    s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L);
+    s->pcc |= CC_CA;
+    outw(s->pcc, s->io+IT_AC_PCC);
+    
+    db->stopped = 0;
+
+    spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct it8172_state *s)
+{
+    struct dmabuf* db = &s->dma_adc;
+    unsigned long flags;
+    unsigned char imc;
+    unsigned long buf1, buf2;
+    
+    if (!db->stopped)
+	return;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize
+    buf1 = virt_to_bus(db->nextIn);
+    buf2 = buf1 + db->fragsize;
+    if (buf2 >= db->dmaaddr + db->dmasize)
+	buf2 -= db->dmasize;
+    
+    outl(buf1, s->io+IT_AC_CAPB1STA);
+    outl(buf2, s->io+IT_AC_CAPB2STA);
+    db->curBufPtr = IT_AC_CAPB1STA;
+
+    // enable capture interrupt
+    imc = inb(s->io+IT_AC_IMC);
+    outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC);
+
+    s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L);
+    s->capcc |= CC_CA;
+    outw(s->capcc, s->io+IT_AC_CAPCC);
+    
+    db->stopped = 0;
+
+    spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+extern inline void dealloc_dmabuf(struct it8172_state *s, struct dmabuf *db)
+{
+    struct page *page, *pend;
+
+    if (db->rawbuf) {
+	/* undo marking the pages as reserved */
+	pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+	for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+	    mem_map_unreserve(page);
+	pci_free_consistent(s->dev, PAGE_SIZE << db->buforder,
+			    db->rawbuf, db->dmaaddr);
+    }
+    db->rawbuf = db->nextIn = db->nextOut = NULL;
+    db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct it8172_state *s, struct dmabuf *db,
+		       unsigned rate, unsigned fmt, unsigned reg)
+{
+    int order;
+    unsigned bytepersec;
+    unsigned bufs;
+    struct page *page, *pend;
+
+    if (!db->rawbuf) {
+	db->ready = db->mapped = 0;
+	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+	    if ((db->rawbuf = pci_alloc_consistent(s->dev,
+						   PAGE_SIZE << order,
+						   &db->dmaaddr)))
+		break;
+	if (!db->rawbuf)
+	    return -ENOMEM;
+	db->buforder = order;
+	/* now mark the pages as reserved;
+	   otherwise remap_page_range doesn't do what we want */
+	pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+	for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+	    mem_map_reserve(page);
+    }
+
+    db->count = 0;
+    db->nextIn = db->nextOut = db->rawbuf;
+    
+    bytepersec = rate << sample_shift[fmt];
+    bufs = PAGE_SIZE << db->buforder;
+    if (db->ossfragshift) {
+	if ((1000 << db->ossfragshift) < bytepersec)
+	    db->fragshift = ld2(bytepersec/1000);
+	else
+	    db->fragshift = db->ossfragshift;
+    } else {
+	db->fragshift = ld2(bytepersec/100/(db->subdivision ?
+					    db->subdivision : 1));
+	if (db->fragshift < 3)
+	    db->fragshift = 3;
+    }
+    db->numfrag = bufs >> db->fragshift;
+    while (db->numfrag < 4 && db->fragshift > 3) {
+	db->fragshift--;
+	db->numfrag = bufs >> db->fragshift;
+    }
+    db->fragsize = 1 << db->fragshift;
+    if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+	db->numfrag = db->ossmaxfrags;
+    db->fragsamples = db->fragsize >> sample_shift[fmt];
+    db->dmasize = db->numfrag << db->fragshift;
+    memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, db->dmasize);
+    
+    // set data length register
+    outw(db->fragsize, s->io+reg+2);
+    db->ready = 1;
+
+    return 0;
+}
+
+extern inline int prog_dmabuf_adc(struct it8172_state *s)
+{
+    stop_adc(s);
+    return prog_dmabuf(s, &s->dma_adc, s->adcrate,
+		       (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT,
+		       IT_AC_CAPCC);
+}
+
+extern inline int prog_dmabuf_dac(struct it8172_state *s)
+{
+    stop_dac(s);
+    return prog_dmabuf(s, &s->dma_dac, s->dacrate,
+		       (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT,
+		       IT_AC_PCC);
+}
+
+
+/* hold spinlock for the following! */
+
+static void it8172_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct it8172_state *s = (struct it8172_state *)dev_id;
+    struct dmabuf* dac = &s->dma_dac;
+    struct dmabuf* adc = &s->dma_adc;
+    unsigned char isc, vs;
+    unsigned short vol, mute;
+    unsigned long newptr;
+    
+    spin_lock(&s->lock);
+
+    isc = inb(s->io+IT_AC_ISC);
+
+    /* fastpath out, to ease interrupt sharing */
+    if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI)))
+	return;
+
+    /* clear audio interrupts first */
+    outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC);
+    
+    /* handle volume button events */
+    if (isc & ISC_VCI) {
+	vs = inb(s->io+IT_AC_VS);
+	outb(0, s->io+IT_AC_VS);
+	vol = inw(s->io+IT_AC_PCMOV);
+	mute = vol & PCMOV_PCMOM;
+	vol &= PCMOV_PCMLCG_MASK;
+	if ((vs & VS_VUP) && vol > 0)
+	    vol--;
+	if ((vs & VS_VDP) && vol < 0x1f)
+	    vol++;
+	vol |= (vol << PCMOV_PCMRCG_BIT);
+	if (vs & VS_VMP)
+	    vol |= (mute ^ PCMOV_PCMOM);
+	outw(vol, s->io+IT_AC_PCMOV);
+    }
+    
+    /* update capture pointers */
+    if (isc & ISC_CCI) {
+	if (adc->count > adc->dmasize - adc->fragsize) {
+	    // Overrun. Stop ADC and log the error
+	    stop_adc(s);
+	    adc->error++;
+	    printk(KERN_INFO PFX "adc overrun\n");
+	} else {
+	    newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize;
+	    if (newptr >= adc->dmaaddr + adc->dmasize)
+		newptr -= adc->dmasize;
+	    
+	    outl(newptr, s->io+adc->curBufPtr);
+	    adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ?
+		IT_AC_CAPB2STA : IT_AC_CAPB1STA;
+	    
+	    adc->nextIn += adc->fragsize;
+	    if (adc->nextIn >= adc->rawbuf + adc->dmasize)
+		adc->nextIn -= adc->dmasize;
+	    
+	    adc->count += adc->fragsize;
+	    adc->total_bytes += adc->fragsize;
+
+	    /* wake up anybody listening */
+	    if (waitqueue_active(&adc->wait))
+		wake_up_interruptible(&adc->wait);
+	}
+    }
+    
+    /* update playback pointers */
+    if (isc & ISC_PCI) {
+	newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize;
+	if (newptr >= dac->dmaaddr + dac->dmasize)
+	    newptr -= dac->dmasize;
+	
+	outl(newptr, s->io+dac->curBufPtr);
+	dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ?
+	    IT_AC_PCB2STA : IT_AC_PCB1STA;
+	
+	dac->nextOut += dac->fragsize;
+	if (dac->nextOut >= dac->rawbuf + dac->dmasize)
+	    dac->nextOut -= dac->dmasize;
+	
+	dac->count -= dac->fragsize;
+	dac->total_bytes += dac->fragsize;
+
+	/* wake up anybody listening */
+	if (waitqueue_active(&dac->wait))
+	    wake_up_interruptible(&dac->wait);
+	
+	if (dac->count <= 0)
+	    stop_dac(s);
+    }
+    
+    spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int it8172_open_mixdev(struct inode *inode, struct file *file)
+{
+    int minor = MINOR(inode->i_rdev);
+    struct list_head *list;
+    struct it8172_state *s;
+
+    for (list = devs.next; ; list = list->next) {
+	if (list == &devs)
+	    return -ENODEV;
+	s = list_entry(list, struct it8172_state, devs);
+	if (s->codec.dev_mixer == minor)
+	    break;
+    }
+    file->private_data = s;
+    return 0;
+}
+
+static int it8172_release_mixdev(struct inode *inode, struct file *file)
+{
+    return 0;
+}
+
+
+static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
+			unsigned long arg)
+{
+    return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int it8172_ioctl_mixdev(struct inode *inode, struct file *file,
+			       unsigned int cmd, unsigned long arg)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+    struct ac97_codec *codec = &s->codec;
+
+    return mixdev_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations it8172_mixer_fops = {
+    owner:	THIS_MODULE,
+    llseek:	no_llseek,
+    ioctl:	it8172_ioctl_mixdev,
+    open:	it8172_open_mixdev,
+    release:	it8172_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct it8172_state *s, int nonblock)
+{
+    unsigned long flags;
+    int count, tmo;
+	
+    if (s->dma_dac.mapped || !s->dma_dac.ready)
+	return 0;
+
+    for (;;) {
+	spin_lock_irqsave(&s->lock, flags);
+	count = s->dma_dac.count;
+	spin_unlock_irqrestore(&s->lock, flags);
+	if (count <= 0)
+	    break;
+	if (signal_pending(current))
+	    break;
+	if (nonblock)
+	    return -EBUSY;
+	tmo = 1000 * count / s->dacrate;
+	tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT];
+	it8172_delay(tmo);
+    }
+    if (signal_pending(current))
+	return -ERESTARTSYS;
+    return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t it8172_read(struct file *file, char *buffer,
+			   size_t count, loff_t *ppos)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+    struct dmabuf *db = &s->dma_adc;
+    ssize_t ret;
+    unsigned long flags;
+    int cnt, bufcnt, avail;
+
+    if (ppos != &file->f_pos)
+	return -ESPIPE;
+    if (db->mapped)
+	return -ENXIO;
+    if (!access_ok(VERIFY_WRITE, buffer, count))
+	return -EFAULT;
+    ret = 0;
+
+    while (count > 0) {
+	// wait for samples in capture buffer
+	do {
+	    spin_lock_irqsave(&s->lock, flags);
+	    if (db->stopped)
+		start_adc(s);
+	    avail = db->count;
+	    spin_unlock_irqrestore(&s->lock, flags);
+	    if (avail <= 0) {
+		if (file->f_flags & O_NONBLOCK) {
+		    if (!ret)
+			ret = -EAGAIN;
+		    return ret;
+		}
+		interruptible_sleep_on(&db->wait);
+		if (signal_pending(current)) {
+		    if (!ret)
+			ret = -ERESTARTSYS;
+		    return ret;
+		}
+	    }
+	} while (avail <= 0);
+
+	cnt = count > avail ? avail : count;
+	bufcnt = cnt;
+	if (cnt % db->fragsize) {
+	    // round count up to nearest fragment
+	    int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize);
+	    cnt = newcnt;
+	}
+
+	// copy from nextOut to user
+	if (copy_to_user(buffer, db->nextOut, bufcnt)) {
+	    if (!ret)
+		ret = -EFAULT;
+	    return ret;
+	}
+
+	spin_lock_irqsave(&s->lock, flags);
+	db->count -= cnt;
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	db->nextOut += cnt;
+	if (db->nextOut >= db->rawbuf + db->dmasize)
+	    db->nextOut -= db->dmasize;	
+
+	count -= bufcnt;
+	buffer += bufcnt;
+	ret += bufcnt;
+    } // while (count > 0)
+
+    return ret;
+}
+
+static ssize_t it8172_write(struct file *file, const char *buffer,
+			    size_t count, loff_t *ppos)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+    struct dmabuf *db = &s->dma_dac;
+    ssize_t ret;
+    unsigned long flags;
+    int cnt, bufcnt, avail;
+
+    if (ppos != &file->f_pos)
+	return -ESPIPE;
+    if (db->mapped)
+	return -ENXIO;
+    if (!access_ok(VERIFY_READ, buffer, count))
+	return -EFAULT;
+    ret = 0;
+    
+    while (count > 0) {
+	// wait for space in playback buffer
+	do {
+	    spin_lock_irqsave(&s->lock, flags);
+	    avail = db->dmasize - db->count;
+	    spin_unlock_irqrestore(&s->lock, flags);
+	    if (avail <= 0) {
+		if (file->f_flags & O_NONBLOCK) {
+		    if (!ret)
+			ret = -EAGAIN;
+		    return ret;
+		}
+		interruptible_sleep_on(&db->wait);
+		if (signal_pending(current)) {
+		    if (!ret)
+			ret = -ERESTARTSYS;
+		    return ret;
+		}
+	    }
+	} while (avail <= 0);
+	
+	cnt = count > avail ? avail : count;
+	// copy to nextIn
+	if (copy_from_user(db->nextIn, buffer, cnt)) {
+	    if (!ret)
+		ret = -EFAULT;
+	    return ret;
+	}
+
+	bufcnt = cnt;
+	if (cnt % db->fragsize) {
+	    // round count up to nearest fragment, and fill remainder of
+	    // fragment with silence
+	    int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize);
+	    memset(db->nextIn + cnt, (s->pcc & CC_DF) ? 0 : 0x80, newcnt - cnt);
+	    cnt = newcnt;
+	}
+
+	spin_lock_irqsave(&s->lock, flags);
+	db->count += cnt;
+	if (db->stopped)
+	    start_dac(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	
+	db->nextIn += cnt;
+	if (db->nextIn >= db->rawbuf + db->dmasize)
+	    db->nextIn -= db->dmasize;
+	
+	count -= bufcnt;
+	buffer += bufcnt;
+	ret += bufcnt;
+    } // while (count > 0)
+	
+    return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int it8172_poll(struct file *file,
+				struct poll_table_struct *wait)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+    unsigned long flags;
+    unsigned int mask = 0;
+
+    if (file->f_mode & FMODE_WRITE)
+	poll_wait(file, &s->dma_dac.wait, wait);
+    if (file->f_mode & FMODE_READ)
+	poll_wait(file, &s->dma_adc.wait, wait);
+    spin_lock_irqsave(&s->lock, flags);
+    if (file->f_mode & FMODE_READ) {
+	if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+	    mask |= POLLIN | POLLRDNORM;
+    }
+    if (file->f_mode & FMODE_WRITE) {
+	if (s->dma_dac.mapped) {
+	    if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+		mask |= POLLOUT | POLLWRNORM;
+	} else {
+	    if ((signed)s->dma_dac.dmasize >=
+		s->dma_dac.count + (signed)s->dma_dac.fragsize)
+		mask |= POLLOUT | POLLWRNORM;
+	}
+    }
+    spin_unlock_irqrestore(&s->lock, flags);
+    return mask;
+}
+
+static int it8172_mmap(struct file *file, struct vm_area_struct *vma)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+    struct dmabuf *db;
+    unsigned long size;
+
+    lock_kernel();
+    if (vma->vm_flags & VM_WRITE)
+	db = &s->dma_dac;
+    else if (vma->vm_flags & VM_READ)
+	db = &s->dma_adc;
+    else {
+	unlock_kernel();
+	return -EINVAL;
+    }
+    if (vma->vm_pgoff != 0) {
+	unlock_kernel();
+	return -EINVAL;
+    }
+    size = vma->vm_end - vma->vm_start;
+    if (size > (PAGE_SIZE << db->buforder)) {
+	unlock_kernel();
+	return -EINVAL;
+    }
+    if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf),
+			 size, vma->vm_page_prot)) {
+	unlock_kernel();
+	return -EAGAIN;
+    }
+    db->mapped = 1;
+    unlock_kernel();
+    return 0;
+}
+
+
+#ifdef IT8172_VERBOSE_DEBUG
+static struct ioctl_str_t {
+    unsigned int cmd;
+    const char* str;
+} ioctl_str[] = {
+    {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
+    {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
+    {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
+    {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
+    {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
+    {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
+    {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
+    {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
+    {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
+    {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
+    {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
+    {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
+    {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
+    {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
+    {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
+    {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
+    {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
+    {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
+    {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
+    {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
+    {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
+    {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
+    {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
+    {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
+    {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
+    {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
+    {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
+    {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
+    {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
+    {OSS_GETVERSION, "OSS_GETVERSION"},
+    {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
+    {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
+    {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
+    {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
+};
+#endif    
+
+static int it8172_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+    unsigned long flags;
+    audio_buf_info abinfo;
+    count_info cinfo;
+    int count;
+    int val, mapped, ret, diff;
+
+    mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+	((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+
+#ifdef IT8172_VERBOSE_DEBUG
+    for (count=0; count<sizeof(ioctl_str)/sizeof(ioctl_str[0]); count++) {
+	if (ioctl_str[count].cmd == cmd)
+	    break;
+    }
+    if (count < sizeof(ioctl_str)/sizeof(ioctl_str[0]))
+	printk(KERN_INFO PFX "ioctl %s\n", ioctl_str[count].str);
+    else
+	printk(KERN_INFO PFX "ioctl unknown, 0x%x\n", cmd);
+#endif
+    
+    switch (cmd) {
+    case OSS_GETVERSION:
+	return put_user(SOUND_VERSION, (int *)arg);
+
+    case SNDCTL_DSP_SYNC:
+	if (file->f_mode & FMODE_WRITE)
+	    return drain_dac(s, file->f_flags & O_NONBLOCK);
+	return 0;
+		
+    case SNDCTL_DSP_SETDUPLEX:
+	return 0;
+
+    case SNDCTL_DSP_GETCAPS:
+	return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
+			DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+    case SNDCTL_DSP_RESET:
+	if (file->f_mode & FMODE_WRITE) {
+	    stop_dac(s);
+	    synchronize_irq();
+	    s->dma_dac.count = s->dma_dac.total_bytes = 0;
+	    s->dma_dac.nextIn = s->dma_dac.nextOut = s->dma_dac.rawbuf;
+	}
+	if (file->f_mode & FMODE_READ) {
+	    stop_adc(s);
+	    synchronize_irq();
+	    s->dma_adc.count = s->dma_adc.total_bytes = 0;
+	    s->dma_adc.nextIn = s->dma_adc.nextOut = s->dma_adc.rawbuf;
+	}
+	return 0;
+
+    case SNDCTL_DSP_SPEED:
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (val >= 0) {
+	    if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		set_adc_rate(s, val);
+		if ((ret = prog_dmabuf_adc(s)))
+		    return ret;
+	    }
+	    if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		set_dac_rate(s, val);
+		if ((ret = prog_dmabuf_dac(s)))
+		    return ret;
+	    }
+	}
+	return put_user((file->f_mode & FMODE_READ) ?
+			s->adcrate : s->dacrate, (int *)arg);
+
+    case SNDCTL_DSP_STEREO:
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (file->f_mode & FMODE_READ) {
+	    stop_adc(s);
+	    if (val)
+		s->capcc |= CC_SM;
+	    else
+		s->capcc &= ~CC_SM;
+	    outw(s->capcc, s->io+IT_AC_CAPCC);
+	    if ((ret = prog_dmabuf_adc(s)))
+		return ret;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+	    stop_dac(s);
+	    if (val)
+		s->pcc |= CC_SM;
+	    else
+		s->pcc &= ~CC_SM;
+	    outw(s->pcc, s->io+IT_AC_PCC);
+	    if ((ret = prog_dmabuf_dac(s)))
+		return ret;
+	}
+	return 0;
+
+    case SNDCTL_DSP_CHANNELS:
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (val != 0) {
+	    if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		if (val >= 2) {
+		    val = 2;
+		    s->capcc |= CC_SM;
+		}
+		else
+		    s->capcc &= ~CC_SM;
+		outw(s->capcc, s->io+IT_AC_CAPCC);
+		if ((ret = prog_dmabuf_adc(s)))
+		    return ret;
+	    }
+	    if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		switch (val) {
+		case 1:
+		    s->pcc &= ~CC_SM;
+		    break;
+		case 2:
+		    s->pcc |= CC_SM;
+		    break;
+		default:
+		    // FIX! support multichannel???
+		    val = 2;
+		    s->pcc |= CC_SM;
+		    break;
+		}
+		outw(s->pcc, s->io+IT_AC_PCC);
+		if ((ret = prog_dmabuf_dac(s)))
+		    return ret;
+	    }
+	}
+	return put_user(val, (int *)arg);
+		
+    case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+	return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+    case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (val != AFMT_QUERY) {
+	    if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		if (val == AFMT_S16_LE)
+		    s->capcc |= CC_DF;
+		else {
+		    val = AFMT_U8;
+		    s->capcc &= ~CC_DF;
+		}
+		outw(s->capcc, s->io+IT_AC_CAPCC);
+		if ((ret = prog_dmabuf_adc(s)))
+		    return ret;
+	    }
+	    if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		if (val == AFMT_S16_LE)
+		    s->pcc |= CC_DF;
+		else {
+		    val = AFMT_U8;
+		    s->pcc &= ~CC_DF;
+		}
+		outw(s->pcc, s->io+IT_AC_PCC);
+		if ((ret = prog_dmabuf_dac(s)))
+		    return ret;
+	    }
+	} else {
+	    if (file->f_mode & FMODE_READ)
+		val = (s->capcc & CC_DF) ? AFMT_S16_LE : AFMT_U8;
+	    else
+		val = (s->pcc & CC_DF) ? AFMT_S16_LE : AFMT_U8;
+	}
+	return put_user(val, (int *)arg);
+		
+    case SNDCTL_DSP_POST:
+	return 0;
+
+    case SNDCTL_DSP_GETTRIGGER:
+	val = 0;
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
+	    val |= PCM_ENABLE_INPUT;
+	if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
+	    val |= PCM_ENABLE_OUTPUT;
+	spin_unlock_irqrestore(&s->lock, flags);
+	return put_user(val, (int *)arg);
+		
+    case SNDCTL_DSP_SETTRIGGER:
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (file->f_mode & FMODE_READ) {
+	    if (val & PCM_ENABLE_INPUT)
+		start_adc(s);
+	    else
+		stop_adc(s);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+	    if (val & PCM_ENABLE_OUTPUT)
+		start_dac(s);
+	    else
+		stop_dac(s);
+	}
+	return 0;
+
+    case SNDCTL_DSP_GETOSPACE:
+	if (!(file->f_mode & FMODE_WRITE))
+	    return -EINVAL;
+	abinfo.fragsize = s->dma_dac.fragsize;
+	spin_lock_irqsave(&s->lock, flags);
+	count = s->dma_dac.count;
+	if (!s->dma_dac.stopped)
+	    count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL));
+	spin_unlock_irqrestore(&s->lock, flags);
+	if (count < 0)
+	    count = 0;
+	abinfo.bytes = s->dma_dac.dmasize - count;
+	abinfo.fragstotal = s->dma_dac.numfrag;
+	abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+	return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+    case SNDCTL_DSP_GETISPACE:
+	if (!(file->f_mode & FMODE_READ))
+	    return -EINVAL;
+	abinfo.fragsize = s->dma_adc.fragsize;
+	spin_lock_irqsave(&s->lock, flags);
+	count = s->dma_adc.count;
+	if (!s->dma_adc.stopped)
+	    count += (s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL));
+	spin_unlock_irqrestore(&s->lock, flags);
+	if (count < 0)
+	    count = 0;
+	abinfo.bytes = count;
+	abinfo.fragstotal = s->dma_adc.numfrag;
+	abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+	return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+    case SNDCTL_DSP_NONBLOCK:
+	file->f_flags |= O_NONBLOCK;
+	return 0;
+
+    case SNDCTL_DSP_GETODELAY:
+	if (!(file->f_mode & FMODE_WRITE))
+	    return -EINVAL;
+	spin_lock_irqsave(&s->lock, flags);
+	count = s->dma_dac.count;
+	if (!s->dma_dac.stopped)
+	    count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL));
+	spin_unlock_irqrestore(&s->lock, flags);
+	if (count < 0)
+	    count = 0;
+	return put_user(count, (int *)arg);
+
+    case SNDCTL_DSP_GETIPTR:
+	if (!(file->f_mode & FMODE_READ))
+	    return -EINVAL;
+	spin_lock_irqsave(&s->lock, flags);
+	cinfo.bytes = s->dma_adc.total_bytes;
+	count = s->dma_adc.count;
+	if (!s->dma_adc.stopped) {
+	    diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL);
+	    count += diff;
+	    cinfo.bytes += diff;
+	    cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - s->dma_adc.dmaaddr;
+	} else
+	    cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - s->dma_adc.dmaaddr;
+	if (s->dma_adc.mapped)
+	    s->dma_adc.count &= s->dma_adc.fragsize-1;
+	spin_unlock_irqrestore(&s->lock, flags);
+	if (count < 0)
+	    count = 0;
+	cinfo.blocks = count >> s->dma_adc.fragshift;
+	return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+    case SNDCTL_DSP_GETOPTR:
+	if (!(file->f_mode & FMODE_READ))
+	    return -EINVAL;
+	spin_lock_irqsave(&s->lock, flags);
+	cinfo.bytes = s->dma_dac.total_bytes;
+	count = s->dma_dac.count;
+	if (!s->dma_dac.stopped) {
+	    diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL);
+	    count -= diff;
+	    cinfo.bytes += diff;
+	    cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - s->dma_dac.dmaaddr;
+	} else
+	    cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - s->dma_dac.dmaaddr;
+	if (s->dma_dac.mapped)
+	    s->dma_dac.count &= s->dma_dac.fragsize-1;
+	spin_unlock_irqrestore(&s->lock, flags);
+	if (count < 0)
+	    count = 0;
+	cinfo.blocks = count >> s->dma_dac.fragshift;
+	return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+    case SNDCTL_DSP_GETBLKSIZE:
+	if (file->f_mode & FMODE_WRITE)
+	    return put_user(s->dma_dac.fragsize, (int *)arg);
+	else
+	    return put_user(s->dma_adc.fragsize, (int *)arg);
+
+    case SNDCTL_DSP_SETFRAGMENT:
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (file->f_mode & FMODE_READ) {
+	    stop_adc(s);
+	    s->dma_adc.ossfragshift = val & 0xffff;
+	    s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+	    if (s->dma_adc.ossfragshift < 4)
+		s->dma_adc.ossfragshift = 4;
+	    if (s->dma_adc.ossfragshift > 15)
+		s->dma_adc.ossfragshift = 15;
+	    if (s->dma_adc.ossmaxfrags < 4)
+		s->dma_adc.ossmaxfrags = 4;
+	    if ((ret = prog_dmabuf_adc(s)))
+		return ret;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+	    stop_dac(s);
+	    s->dma_dac.ossfragshift = val & 0xffff;
+	    s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+	    if (s->dma_dac.ossfragshift < 4)
+		s->dma_dac.ossfragshift = 4;
+	    if (s->dma_dac.ossfragshift > 15)
+		s->dma_dac.ossfragshift = 15;
+	    if (s->dma_dac.ossmaxfrags < 4)
+		s->dma_dac.ossmaxfrags = 4;
+	    if ((ret = prog_dmabuf_dac(s)))
+		return ret;
+	}
+	return 0;
+
+    case SNDCTL_DSP_SUBDIVIDE:
+	if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+	    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+	    return -EINVAL;
+	if (get_user(val, (int *)arg))
+	    return -EFAULT;
+	if (val != 1 && val != 2 && val != 4)
+	    return -EINVAL;
+	if (file->f_mode & FMODE_READ) {
+	    stop_adc(s);
+	    s->dma_adc.subdivision = val;
+	    if ((ret = prog_dmabuf_adc(s)))
+		return ret;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+	    stop_dac(s);
+	    s->dma_dac.subdivision = val;
+	    if ((ret = prog_dmabuf_dac(s)))
+		return ret;
+	}
+	return 0;
+
+    case SOUND_PCM_READ_RATE:
+	return put_user((file->f_mode & FMODE_READ) ?
+			s->adcrate : s->dacrate, (int *)arg);
+
+    case SOUND_PCM_READ_CHANNELS:
+	if (file->f_mode & FMODE_READ)
+	    return put_user((s->capcc & CC_SM) ? 2 : 1, (int *)arg);
+	else
+	    return put_user((s->pcc & CC_SM) ? 2 : 1, (int *)arg);
+	    
+    case SOUND_PCM_READ_BITS:
+	if (file->f_mode & FMODE_READ)
+	    return put_user((s->capcc & CC_DF) ? 16 : 8, (int *)arg);
+	else
+	    return put_user((s->pcc & CC_DF) ? 16 : 8, (int *)arg);
+
+    case SOUND_PCM_WRITE_FILTER:
+    case SNDCTL_DSP_SETSYNCRO:
+    case SOUND_PCM_READ_FILTER:
+	return -EINVAL;
+    }
+
+    return mixdev_ioctl(&s->codec, cmd, arg);
+}
+
+
+static int it8172_open(struct inode *inode, struct file *file)
+{
+    int minor = MINOR(inode->i_rdev);
+    DECLARE_WAITQUEUE(wait, current);
+    unsigned long flags;
+    struct list_head *list;
+    struct it8172_state *s;
+    int ret;
+    
+    for (list = devs.next; ; list = list->next) {
+	if (list == &devs)
+	    return -ENODEV;
+	s = list_entry(list, struct it8172_state, devs);
+	if (!((s->dev_audio ^ minor) & ~0xf))
+	    break;
+    }
+    file->private_data = s;
+    /* wait for device to become free */
+    down(&s->open_sem);
+    while (s->open_mode & file->f_mode) {
+	if (file->f_flags & O_NONBLOCK) {
+	    up(&s->open_sem);
+	    return -EBUSY;
+	}
+	add_wait_queue(&s->open_wait, &wait);
+	__set_current_state(TASK_INTERRUPTIBLE);
+	up(&s->open_sem);
+	schedule();
+	remove_wait_queue(&s->open_wait, &wait);
+	set_current_state(TASK_RUNNING);
+	if (signal_pending(current))
+	    return -ERESTARTSYS;
+	down(&s->open_sem);
+    }
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    if (file->f_mode & FMODE_READ) {
+	s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
+	    s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
+	s->capcc &= ~(CC_SM | CC_DF);
+	set_adc_rate(s, 8000);
+	if ((minor & 0xf) == SND_DEV_DSP16)
+	    s->capcc |= CC_DF;
+	outw(s->capcc, s->io+IT_AC_CAPCC);
+	if ((ret = prog_dmabuf_adc(s)))
+	    return ret;
+    }
+    if (file->f_mode & FMODE_WRITE) {
+	s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
+	    s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
+	s->pcc &= ~(CC_SM | CC_DF);
+	set_dac_rate(s, 8000);
+	if ((minor & 0xf) == SND_DEV_DSP16)
+	    s->pcc |= CC_DF;
+	outw(s->pcc, s->io+IT_AC_PCC);
+	if ((ret = prog_dmabuf_dac(s)))
+	    return ret;
+    }
+
+    spin_unlock_irqrestore(&s->lock, flags);
+
+    s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+    up(&s->open_sem);
+    return 0;
+}
+
+static int it8172_release(struct inode *inode, struct file *file)
+{
+    struct it8172_state *s = (struct it8172_state *)file->private_data;
+
+    lock_kernel();
+    if (file->f_mode & FMODE_WRITE)
+	drain_dac(s, file->f_flags & O_NONBLOCK);
+    down(&s->open_sem);
+    if (file->f_mode & FMODE_WRITE) {
+	stop_dac(s);
+	dealloc_dmabuf(s, &s->dma_dac);
+    }
+    if (file->f_mode & FMODE_READ) {
+	stop_adc(s);
+	dealloc_dmabuf(s, &s->dma_adc);
+    }
+    s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+    up(&s->open_sem);
+    wake_up(&s->open_wait);
+    unlock_kernel();
+    return 0;
+}
+
+static /*const*/ struct file_operations it8172_audio_fops = {
+    owner:	THIS_MODULE,
+    llseek:	no_llseek,
+    read:	it8172_read,
+    write:	it8172_write,
+    poll:	it8172_poll,
+    ioctl:	it8172_ioctl,
+    mmap:	it8172_mmap,
+    open:	it8172_open,
+    release:	it8172_release,
+};
+
+
+/* --------------------------------------------------------------------- */
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * for debugging purposes, we'll create a proc device that dumps the
+ * CODEC chipstate
+ */
+
+#ifdef IT8172_DEBUG
+static int proc_it8172_dump (char *buf, char **start, off_t fpos,
+			     int length, int *eof, void *data)
+{
+    struct it8172_state *s;
+    int cnt, len = 0;
+
+    if (list_empty(&devs))
+	return 0;
+    s = list_entry(devs.next, struct it8172_state, devs);
+
+    /* print out header */
+    len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n");
+
+    // print out digital controller state
+    len += sprintf (buf + len, "IT8172 Audio Controller registers\n");
+    len += sprintf (buf + len, "---------------------------------\n");
+    cnt=0;
+    while (cnt < 0x72) {
+	if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA ||
+	    cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA ||
+	    cnt == IT_AC_PFDP) {
+	    len+= sprintf (buf + len, "reg %02x = %08x\n",
+			   cnt, inl(s->io+cnt));
+	    cnt += 4;
+	} else {
+	    len+= sprintf (buf + len, "reg %02x = %04x\n",
+			   cnt, inw(s->io+cnt));
+	    cnt += 2;
+	}
+    }
+    
+    /* print out CODEC state */
+    len += sprintf (buf + len, "\nAC97 CODEC registers\n");
+    len += sprintf (buf + len, "----------------------\n");
+    for (cnt=0; cnt <= 0x7e; cnt = cnt +2)
+	len+= sprintf (buf + len, "reg %02x = %04x\n",
+		       cnt, rdcodec(&s->codec, cnt));
+
+    if (fpos >=len){
+	*start = buf;
+	*eof =1;
+	return 0;
+    }
+    *start = buf + fpos;
+    if ((len -= fpos) > length)
+	return length;
+    *eof =1;
+    return len;
+
+}
+#endif /* IT8172_DEBUG */
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices; only used for command line params */
+#define NR_DEVICE 5
+
+static int spdif[NR_DEVICE] = { 0, };
+
+static unsigned int devindex = 0;
+
+MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled");
+
+MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com");
+MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+static int __devinit it8172_probe(struct pci_dev *pcidev,
+				  const struct pci_device_id *pciid)
+{
+    struct it8172_state *s;
+    int i, val;
+    unsigned short pcisr, vol;
+    unsigned char legacy, imc;
+    char proc_str[80];
+    
+    if (pcidev->irq == 0) 
+	return -1;
+
+    if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) {
+	printk(KERN_ERR PFX "alloc of device struct failed\n");
+	return -1;
+    }
+	
+    memset(s, 0, sizeof(struct it8172_state));
+    init_waitqueue_head(&s->dma_adc.wait);
+    init_waitqueue_head(&s->dma_dac.wait);
+    init_waitqueue_head(&s->open_wait);
+    init_MUTEX(&s->open_sem);
+    spin_lock_init(&s->lock);
+    s->dev = pcidev;
+    s->io = pci_resource_start(pcidev, 0);
+    s->irq = pcidev->irq;
+    s->vendor = pcidev->vendor;
+    s->device = pcidev->device;
+    pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev);
+    s->codec.private_data = s;
+    s->codec.id = 0;
+    s->codec.codec_read = rdcodec;
+    s->codec.codec_write = wrcodec;
+    s->codec.codec_wait = waitcodec;
+
+    if (!request_region(s->io, pci_resource_len(pcidev,0),
+			IT8172_MODULE_NAME)) {
+	printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n",
+		s->io, s->io + pci_resource_len(pcidev,0)-1);
+	goto err_region;
+    }
+    if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT,
+		    IT8172_MODULE_NAME, s)) {
+	printk(KERN_ERR PFX "irq %u in use\n", s->irq);
+	goto err_irq;
+    }
+
+    printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq);
+
+    /* register devices */
+    if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0)
+	goto err_dev1;
+    if ((s->codec.dev_mixer =
+	 register_sound_mixer(&it8172_mixer_fops, -1)) < 0)
+	goto err_dev2;
+
+#ifdef IT8172_DEBUG
+    /* intialize the debug proc device */
+    s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL,
+				   proc_it8172_dump, NULL);
+#endif /* IT8172_DEBUG */
+	
+    /*
+     * Reset the Audio device using the IT8172 PCI Reset register. This
+     * creates an audible double click on a speaker connected to Line-out.
+     */
+    IT_IO_READ16(IT_PM_PCISR, pcisr);
+    pcisr |= IT_PM_PCISR_ACSR;
+    IT_IO_WRITE16(IT_PM_PCISR, pcisr);
+    /* wait up to 100msec for reset to complete */
+    for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) {
+	it8172_delay(10);
+	if (i == 10)
+	    break;
+	IT_IO_READ16(IT_PM_PCISR, pcisr);
+    }
+    if (i == 10) {
+	printk(KERN_ERR PFX "chip reset timeout!\n");
+	goto err_dev3;
+    }
+    
+    /* enable pci io and bus mastering */
+    if (pci_enable_device(pcidev))
+	goto err_dev3;
+    pci_set_master(pcidev);
+
+    /* get out of legacy mode */
+    pci_read_config_byte (pcidev, 0x40, &legacy);
+    pci_write_config_byte (pcidev, 0x40, legacy & ~1);
+    
+    s->spdif_volume = -1;
+    /* check to see if s/pdif mode is being requested */
+    if (spdif[devindex]) {
+	printk(KERN_INFO PFX "enabling S/PDIF output\n");
+	s->spdif_volume = 0;
+	outb(GC_SOE, s->io+IT_AC_GC);
+    } else {
+	printk(KERN_INFO PFX "disabling S/PDIF output\n");
+	outb(0, s->io+IT_AC_GC);
+    }
+    
+    /* cold reset the AC97 */
+    outw(CODECC_CR, s->io+IT_AC_CODECC);
+    udelay(1000);
+    outw(0, s->io+IT_AC_CODECC);
+    /* need to delay around 500msec(bleech) to give
+       some CODECs enough time to wakeup */
+    it8172_delay(500);
+    
+    /* AC97 warm reset to start the bitclk */
+    outw(CODECC_WR, s->io+IT_AC_CODECC);
+    udelay(1000);
+    outw(0, s->io+IT_AC_CODECC);
+    
+    /* codec init */
+    if (!ac97_probe_codec(&s->codec))
+	goto err_dev3;
+
+    /* Enable Volume button interrupts */
+    imc = inb(s->io+IT_AC_IMC);
+    outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC);
+
+    /* Un-mute PCM and FM out on the controller */
+    vol = inw(s->io+IT_AC_PCMOV);
+    outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV);
+    vol = inw(s->io+IT_AC_FMOV);
+    outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV);
+
+    /* set channel defaults to 8-bit, mono, 8 Khz */
+    s->pcc = 0;
+    s->capcc = 0;
+    set_dac_rate(s, 8000);
+    set_adc_rate(s, 8000);
+
+    /* set mic to be the recording source */
+    val = SOUND_MASK_MIC;
+    mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+
+    /* mute master and PCM when in S/PDIF mode */
+    if (s->spdif_volume != -1) {
+	val = 0x0000;
+	mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME,
+		     (unsigned long)&val);
+	mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM,
+		     (unsigned long)&val);
+    }
+    
+#ifdef IT8172_DEBUG
+    sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, s->codec.id);
+    s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL,
+					 ac97_read_proc, &s->codec);
+#endif
+    
+    /* store it in the driver field */
+    pci_set_drvdata(pcidev, s);
+    pcidev->dma_mask = 0xffffffff;
+    /* put it into driver list */
+    list_add_tail(&s->devs, &devs);
+    /* increment devindex */
+    if (devindex < NR_DEVICE-1)
+	devindex++;
+    return 0;
+
+ err_dev3:
+    unregister_sound_mixer(s->codec.dev_mixer);
+ err_dev2:
+    unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+    printk(KERN_ERR PFX "cannot register misc device\n");
+    free_irq(s->irq, s);
+ err_irq:
+    release_region(s->io, pci_resource_len(pcidev,0));
+ err_region:
+    kfree(s);
+    return -1;
+}
+
+static void __devinit it8172_remove(struct pci_dev *dev)
+{
+    struct it8172_state *s = pci_get_drvdata(dev);
+
+    if (!s)
+	return;
+    list_del(&s->devs);
+#ifdef IT8172_DEBUG
+    if (s->ps)
+	remove_proc_entry(IT8172_MODULE_NAME, NULL);
+#endif /* IT8172_DEBUG */
+    synchronize_irq();
+    free_irq(s->irq, s);
+    release_region(s->io, pci_resource_len(dev,0));
+    unregister_sound_dsp(s->dev_audio);
+    unregister_sound_mixer(s->codec.dev_mixer);
+    kfree(s);
+    pci_set_drvdata(dev, NULL);
+}
+
+
+
+static struct pci_device_id id_table[] __devinitdata = {
+    { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID,
+      PCI_ANY_ID, 0, 0 },
+    { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver it8172_driver = {
+    name: IT8172_MODULE_NAME,
+    id_table: id_table,
+    probe: it8172_probe,
+    remove: it8172_remove
+};
+
+static int __init init_it8172(void)
+{
+    if (!pci_present())   /* No PCI bus in this machine! */
+	return -ENODEV;
+    printk("version v0.26 time " __TIME__ " " __DATE__ "\n");
+    return pci_module_init(&it8172_driver);
+}
+
+static void __exit cleanup_it8172(void)
+{
+    printk(KERN_INFO PFX "unloading\n");
+    pci_unregister_driver(&it8172_driver);
+}
+
+module_init(init_it8172);
+module_exit(cleanup_it8172);
+
diff -Nru linux/sound/oss/iwmem.h linux-2.4.19-pre5-mjc/sound/oss/iwmem.h
--- linux/sound/oss/iwmem.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/iwmem.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,36 @@
+/*
+ * sound/iwmem.h
+ *
+ * DRAM size encoding table for AMD Interwave chip.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ * Bartlomiej Zolnierkiewicz	: added __initdata to mem_decode
+ */
+
+
+#define K 1024
+#define M (1024*K)
+static int mem_decode[][4] __initdata =
+{
+/*	Bank0	Bank1	Bank2	Bank3	Encoding bits	*/
+	{256*K,	0,	0,	0},		/*  0 */
+	{256*K,	256*K,	0,	0},		/*  1 */
+	{256*K,	256*K,	256*K,	256*K},		/*  2 */
+	{256*K,	1*M,	0,	0},		/*  3 */
+	{256*K,	1*M,	1*M,	1*M},		/*  4 */
+	{256*K,	256*K,	1*M,	0},		/*  5 */
+	{256*K,	256*K,	1*M,	1*M},		/*  6 */
+	{1*M,	0,	0,	0},		/*  7 */
+	{1*M,	1*M,	0,	0},		/*  8 */
+	{1*M,	1*M,	1*M,	1*M},		/*  9 */
+	{4*M,	0,	0,	0},		/* 10 */
+	{4*M,	4*M,	0,	0},		/* 11 */
+	{4*M,	4*M,	4*M,	4*M}		/* 12 */
+};
diff -Nru linux/sound/oss/mad16.c linux-2.4.19-pre5-mjc/sound/oss/mad16.c
--- linux/sound/oss/mad16.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/mad16.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * mad16.c
+ *
+ * Initialization code for OPTi MAD16 compatible audio chips. Including
+ *
+ *      OPTi 82C928     MAD16           (replaced by C929)
+ *      OAK OTI-601D    Mozart
+ *      OAK OTI-605	Mozart		(later version with MPU401 Midi)
+ *      OPTi 82C929     MAD16 Pro
+ *      OPTi 82C930
+ *      OPTi 82C924
+ *
+ * These audio interface chips don't produce sound themselves. They just
+ * connect some other components (OPL-[234] and a WSS compatible codec)
+ * to the PC bus and perform I/O, DMA and IRQ address decoding. There is
+ * also a UART for the MPU-401 mode (not 82C928/Mozart).
+ * The Mozart chip appears to be compatible with the 82C928, although later
+ * issues of the card, using the OTI-605 chip, have an MPU-401 compatable Midi
+ * port. This port is configured differently to that of the OPTi audio chips.
+ *
+ *	Changes
+ *	
+ *	Alan Cox		Clean up, added module selections.
+ *
+ *	A. Wik			Added support for Opti924 PnP.
+ *				Improved debugging support.	16-May-1998
+ *				Fixed bug.			16-Jun-1998
+ *
+ *      Torsten Duwe            Made Opti924 PnP support non-destructive
+ *                                                             	23-Dec-1998
+ *
+ *	Paul Grayson		Added support for Midi on later Mozart cards.
+ *								25-Nov-1999
+ *	Christoph Hellwig	Adapted to module_init/module_exit.
+ *	Arnaldo C. de Melo	got rid of attach_uart401       21-Sep-2000
+ *
+ *	Pavel Rabel		Clean up                           Nov-2000
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+
+#include "ad1848.h"
+#include "sb.h"
+#include "mpu401.h"
+
+static int      mad16_conf;
+static int      mad16_cdsel;
+
+static int      already_initialized = 0;
+
+#define C928	1
+#define MOZART	2
+#define C929	3
+#define C930	4
+#define C924    5
+
+/*
+ *    Registers
+ *
+ *      The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations).
+ *      All ports are inactive by default. They can be activated by
+ *      writing 0xE2 or 0xE3 to the password register. The password is valid
+ *      only until the next I/O read or write.
+ *
+ *      82C930 uses 0xE4 as the password and indirect addressing to access
+ *      the config registers.
+ */
+
+#define MC0_PORT	0xf8c	/* Dummy port */
+#define MC1_PORT	0xf8d	/* SB address, CD-ROM interface type, joystick */
+#define MC2_PORT	0xf8e	/* CD-ROM address, IRQ, DMA, plus OPL4 bit */
+#define MC3_PORT	0xf8f
+#define PASSWD_REG	0xf8f
+#define MC4_PORT	0xf90
+#define MC5_PORT	0xf91
+#define MC6_PORT	0xf92
+#define MC7_PORT	0xf93
+#define MC8_PORT	0xf94
+#define MC9_PORT	0xf95
+#define MC10_PORT	0xf96
+#define MC11_PORT	0xf97
+#define MC12_PORT	0xf98
+
+static int      board_type = C928;
+
+static int     *mad16_osp;
+static int	c931_detected;	/* minor differences from C930 */
+static char	c924pnp = 0;	/* "     "           "    C924 */
+static int	debug = 0;	/* debugging output */
+
+#ifdef DDB
+#undef DDB
+#endif
+#define DDB(x) {if (debug) x;}
+
+static unsigned char mad_read(int port)
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+	save_flags(flags);
+	cli();
+
+	switch (board_type)	/* Output password */
+	{
+		case C928:
+		case MOZART:
+			outb((0xE2), PASSWD_REG);
+			break;
+
+		case C929:
+			outb((0xE3), PASSWD_REG);
+			break;
+
+		case C930:
+			/* outb(( 0xE4),  PASSWD_REG); */
+			break;
+
+		case C924:
+			/* the c924 has its ports relocated by -128 if
+			   PnP is enabled  -aw */
+			if (!c924pnp)
+				outb((0xE5), PASSWD_REG); else
+				outb((0xE5), PASSWD_REG - 0x80);
+			break;
+	}
+
+	if (board_type == C930)
+	{
+		outb((port - MC0_PORT), 0xe0e);	/* Write to index reg */
+		tmp = inb(0xe0f);	/* Read from data reg */
+	}
+	else
+		if (!c924pnp)
+			tmp = inb(port); else
+			tmp = inb(port-0x80);
+	restore_flags(flags);
+
+	return tmp;
+}
+
+static void mad_write(int port, int value)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+
+	switch (board_type)	/* Output password */
+	{
+		case C928:
+		case MOZART:
+			outb((0xE2), PASSWD_REG);
+			break;
+
+		case C929:
+			outb((0xE3), PASSWD_REG);
+			break;
+
+		case C930:
+			/* outb(( 0xE4),  PASSWD_REG); */
+			break;
+
+		case C924:
+			if (!c924pnp)
+				outb((0xE5), PASSWD_REG); else
+				outb((0xE5), PASSWD_REG - 0x80);
+			break;
+	}
+
+	if (board_type == C930)
+	{
+		outb((port - MC0_PORT), 0xe0e);	/* Write to index reg */
+		outb(((unsigned char) (value & 0xff)), 0xe0f);
+	}
+	else
+		if (!c924pnp)
+			outb(((unsigned char) (value & 0xff)), port); else
+			outb(((unsigned char) (value & 0xff)), port-0x80);
+	restore_flags(flags);
+}
+
+static int __init detect_c930(void)
+{
+	unsigned char   tmp = mad_read(MC1_PORT);
+
+	if ((tmp & 0x06) != 0x06)
+	{
+		DDB(printk("Wrong C930 signature (%x)\n", tmp));
+		/* return 0; */
+	}
+	mad_write(MC1_PORT, 0);
+
+	if (mad_read(MC1_PORT) != 0x06)
+	{
+		DDB(printk("Wrong C930 signature2 (%x)\n", tmp));
+		/* return 0; */
+	}
+	mad_write(MC1_PORT, tmp);	/* Restore bits */
+
+	mad_write(MC7_PORT, 0);
+	if ((tmp = mad_read(MC7_PORT)) != 0)
+	{
+		DDB(printk("MC7 not writable (%x)\n", tmp));
+		return 0;
+	}
+	mad_write(MC7_PORT, 0xcb);
+	if ((tmp = mad_read(MC7_PORT)) != 0xcb)
+	{
+		DDB(printk("MC7 not writable2 (%x)\n", tmp));
+		return 0;
+	}
+
+	tmp = mad_read(MC0_PORT+18);
+	if (tmp == 0xff || tmp == 0x00)
+		return 1;
+	/* We probably have a C931 */
+	DDB(printk("Detected C931 config=0x%02x\n", tmp));
+	c931_detected = 1;
+
+	/*
+         * We cannot configure the chip if it is in PnP mode.
+         * If we have a CSN assigned (bit 8 in MC13) we first try
+         * a software reset, then a software power off, finally
+         * Clearing PnP mode. The last option is not
+	 * Bit 8 in MC13 
+         */
+	if ((mad_read(MC0_PORT+13) & 0x80) == 0)
+		return 1;
+
+	/* Software reset */
+	mad_write(MC9_PORT, 0x02);
+	mad_write(MC9_PORT, 0x00);
+
+	if ((mad_read(MC0_PORT+13) & 0x80) == 0)
+		return 1;
+	
+	/* Power off, and on again */
+	mad_write(MC9_PORT, 0xc2);
+	mad_write(MC9_PORT, 0xc0);
+
+	if ((mad_read(MC0_PORT+13) & 0x80) == 0)
+		return 1;
+	
+#if 0	
+	/* Force off PnP mode. This is not recommended because
+	 * the PnP bios will not recognize the chip on the next
+	 * warm boot and may assignd different resources to other
+	 * PnP/PCI cards.
+	 */
+	mad_write(MC0_PORT+17, 0x04);
+#endif
+	return 1;
+}
+
+static int __init detect_mad16(void)
+{
+	unsigned char tmp, tmp2, bit;
+	int i, port;
+
+	/*
+	 * Check that reading a register doesn't return bus float (0xff)
+	 * when the card is accessed using password. This may fail in case
+	 * the card is in low power mode. Normally at least the power saving
+	 * mode bit should be 0.
+	 */
+
+	if ((tmp = mad_read(MC1_PORT)) == 0xff)
+	{
+		DDB(printk("MC1_PORT returned 0xff\n"));
+		return 0;
+	}
+	for (i = 0xf8d; i <= 0xf98; i++)
+		if (!c924pnp)
+			DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))) else
+			DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i)));
+
+	if (board_type == C930)
+		return detect_c930();
+
+	/*
+	 * Now check that the gate is closed on first I/O after writing
+	 * the password. (This is how a MAD16 compatible card works).
+	 */
+
+	if ((tmp2 = inb(MC1_PORT)) == tmp)	/* It didn't close */
+	{
+		DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2));
+		return 0;
+	}
+
+	bit  = (c924pnp) ?     0x20 : 0x80;
+	port = (c924pnp) ? MC2_PORT : MC1_PORT;
+
+	tmp = mad_read(port);
+	mad_write(port, tmp ^ bit);	/* Toggle a bit */
+	if ((tmp2 = mad_read(port)) != (tmp ^ bit))	/* Compare the bit */
+	{
+		mad_write(port, tmp);	/* Restore */
+		DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2));
+		return 0;
+	}
+	mad_write(port, tmp);	/* Restore */
+	return 1;		/* Bingo */
+}
+
+static int __init wss_init(struct address_info *hw_config)
+{
+	int ad_flags = 0;
+
+	/*
+	 *    Verify the WSS parameters
+	 */
+
+	if (check_region(hw_config->io_base, 8))
+	{
+		printk(KERN_ERR "MSS: I/O port conflict\n");
+		return 0;
+	}
+	if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp))
+		return 0;
+	/*
+	 * Check if the IO port returns valid signature. The original MS Sound
+	 * system returns 0x04 while some cards (AudioTrix Pro for example)
+	 * return 0x00.
+	 */
+
+	if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 &&
+	    (inb(hw_config->io_base + 3) & 0x3f) != 0x00)
+	{
+		DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3)));
+		return 0;
+	}
+	if (hw_config->irq > 11)
+	{
+		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+	{
+		printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma);
+		return 0;
+	}
+	/*
+	 * Check that DMA0 is not in use with a 8 bit board.
+	 */
+
+	if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+	{
+		printk("MSS: Can't use DMA0 with a 8 bit card/slot\n");
+		return 0;
+	}
+	if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+		printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+	return 1;
+}
+
+static int __init init_c930(struct address_info *hw_config)
+{
+	unsigned char cfg = 0;
+
+	if(c931_detected)
+	{
+		/* Bit 0 has reversd meaning. Bits 1 and 2 sese
+		   reversed on write.
+		   Support only IDE cdrom. IDE port programmed
+		   somewhere else. */
+		cfg =  (cfg & 0x09) ^ 0x07;
+	}
+
+	switch (hw_config->io_base)
+	{
+		case 0x530:
+			cfg |= 0x00;
+			break;
+		case 0xe80:
+			cfg |= 0x10;
+			break;
+		case 0xf40:
+			cfg |= 0x20;
+			break;
+		case 0x604:
+			cfg |= 0x30;
+			break;
+		default:
+			printk(KERN_ERR "MAD16: Invalid codec port %x\n", hw_config->io_base);
+			return 0;
+	}
+	mad_write(MC1_PORT, cfg);
+
+	/* MC2 is CD configuration. Don't touch it. */
+
+	mad_write(MC3_PORT, 0);	/* Disable SB mode IRQ and DMA */
+
+	/* bit 2 of MC4 reverses it's meaning between the C930
+	   and the C931. */
+	cfg = c931_detected ? 0x04 : 0x00;
+
+	mad_write(MC4_PORT, 0x52|cfg);
+
+	mad_write(MC5_PORT, 0x3C);	/* Init it into mode2 */
+	mad_write(MC6_PORT, 0x02);	/* Enable WSS, Disable MPU and SB */
+	mad_write(MC7_PORT, 0xCB);
+	mad_write(MC10_PORT, 0x11);
+
+	return wss_init(hw_config);
+}
+
+static int __init chip_detect(void)
+{
+	int i;
+
+	/*
+	 *    Then try to detect with the old password
+	 */
+	board_type = C924;
+
+	DDB(printk("Detect using password = 0xE5\n"));
+	
+	if (detect_mad16()) {
+		return 1;
+	}
+	
+	board_type = C928;
+
+	DDB(printk("Detect using password = 0xE2\n"));
+
+	if (detect_mad16())
+	{
+		unsigned char model;
+
+		if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) {
+			DDB(printk("mad16.c: Mozart detected\n"));
+			board_type = MOZART;
+		} else {
+			DDB(printk("mad16.c: 82C928 detected???\n"));
+			board_type = C928;
+		}
+		return 1;
+	}
+
+	board_type = C929;
+
+	DDB(printk("Detect using password = 0xE3\n"));
+
+	if (detect_mad16())
+	{
+		DDB(printk("mad16.c: 82C929 detected\n"));
+		return 1;
+	}
+
+	if (inb(PASSWD_REG) != 0xff)
+		return 0;
+
+	/*
+	 * First relocate MC# registers to 0xe0e/0xe0f, disable password 
+	 */
+
+	outb((0xE4), PASSWD_REG);
+	outb((0x80), PASSWD_REG);
+
+	board_type = C930;
+
+	DDB(printk("Detect using password = 0xE4\n"));
+
+	for (i = 0xf8d; i <= 0xf93; i++)
+		DDB(printk("port %03x = %02x\n", i, mad_read(i)));
+
+        if(detect_mad16()) {
+		DDB(printk("mad16.c: 82C930 detected\n"));
+		return 1;
+	}
+
+	/* The C931 has the password reg at F8D */
+	outb((0xE4), 0xF8D);
+	outb((0x80), 0xF8D);
+	DDB(printk("Detect using password = 0xE4 for C931\n"));
+
+	if (detect_mad16()) {
+		return 1;
+	}
+
+	board_type = C924;
+	c924pnp++;
+	DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n"));
+	if (detect_mad16()) {
+		DDB(printk("mad16.c: 82C924 PnP detected\n"));
+		return 1;
+	}
+	
+	c924pnp=0;
+
+	return 0;
+}
+
+static int __init probe_mad16(struct address_info *hw_config)
+{
+	int i;
+	static int valid_ports[] = 
+	{
+		0x530, 0xe80, 0xf40, 0x604
+	};
+	unsigned char tmp;
+	unsigned char cs4231_mode = 0;
+
+	int ad_flags = 0;
+
+	if (already_initialized)
+		return 0;
+
+	mad16_osp = hw_config->osp;
+
+	/*
+	 *    Check that all ports return 0xff (bus float) when no password
+	 *      is written to the password register.
+	 */
+
+	DDB(printk("--- Detecting MAD16 / Mozart ---\n"));
+	if (!chip_detect())
+		return 0;
+
+	if (board_type == C930)
+		return init_c930(hw_config);
+
+
+	for (i = 0xf8d; i <= 0xf93; i++)
+		if (!c924pnp)
+			DDB(printk("port %03x = %02x\n", i, mad_read(i))) else
+			DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i)));
+
+/*
+ * Set the WSS address
+ */
+
+	tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80;	/* Enable WSS, Disable SB */
+
+	for (i = 0; i < 5; i++)
+	{
+		if (i > 3)	/* Not a valid port */
+		{
+			printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base);
+			return 0;
+		}
+		if (valid_ports[i] == hw_config->io_base)
+		{
+			tmp |= i << 4;	/* WSS port select bits */
+			break;
+		}
+	}
+
+	/*
+	 * Set optional CD-ROM and joystick settings.
+	 */
+
+	tmp &= ~0x0f;
+	mad_write(MC1_PORT, tmp);
+
+	tmp = mad_read(MC2_PORT);
+
+	mad_write(MC2_PORT, tmp);
+	mad_write(MC3_PORT, 0xf0);	/* Disable SB */
+
+	if (board_type == C924)	/* Specific C924 init values */
+	{
+		mad_write(MC4_PORT, 0xA0);
+		mad_write(MC5_PORT, 0x05);
+		mad_write(MC6_PORT, 0x03);
+	}
+	if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp))
+		return 0;
+
+	if (ad_flags & (AD_F_CS4231 | AD_F_CS4248))
+		cs4231_mode = 0x02;	/* CS4248/CS4231 sync delay switch */
+
+	if (board_type == C929)
+	{
+		mad_write(MC4_PORT, 0xa2);
+		mad_write(MC5_PORT, 0xA5 | cs4231_mode);
+		mad_write(MC6_PORT, 0x03);	/* Disable MPU401 */
+	}
+	else
+	{
+		mad_write(MC4_PORT, 0x02);
+		mad_write(MC5_PORT, 0x30 | cs4231_mode);
+	}
+
+	for (i = 0xf8d; i <= 0xf93; i++) if (!c924pnp)
+		DDB(printk("port %03x after init = %02x\n", i, mad_read(i))) else
+		DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i)));
+	wss_init(hw_config);
+
+	return 1;
+}
+
+static void __init attach_mad16(struct address_info *hw_config)
+{
+
+	static signed char     interrupt_bits[12] = {
+		-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
+	};
+	signed char bits;
+
+	static char     dma_bits[4] = {
+		1, 2, 0, 3
+	};
+
+	int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
+	int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2;
+	unsigned char dma2_bit = 0;
+
+	already_initialized = 1;
+
+	if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp))
+		return;
+
+	/*
+	 * Set the IRQ and DMA addresses.
+	 */
+	
+	if (board_type == C930 || c924pnp)
+		interrupt_bits[5] = 0x28;	/* Also IRQ5 is possible on C930 */
+
+	bits = interrupt_bits[hw_config->irq];
+	if (bits == -1)
+		return;
+
+	outb((bits | 0x40), config_port);
+	if ((inb(version_port) & 0x40) == 0)
+		printk(KERN_ERR "[IRQ Conflict?]\n");
+
+	/*
+	 * Handle the capture DMA channel
+	 */
+
+	if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma)
+	{
+		if (!((dma == 0 && dma2 == 1) ||
+			(dma == 1 && dma2 == 0) ||
+			(dma == 3 && dma2 == 0)))
+		{		/* Unsupported combination. Try to swap channels */
+			int tmp = dma;
+
+			dma = dma2;
+			dma2 = tmp;
+		}
+		if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) ||
+			(dma == 3 && dma2 == 0))
+		{
+			dma2_bit = 0x04;	/* Enable capture DMA */
+		}
+		else
+		{
+			printk("MAD16: Invalid capture DMA\n");
+			dma2 = dma;
+		}
+	}
+	else dma2 = dma;
+
+	outb((bits | dma_bits[dma] | dma2_bit), config_port);	/* Write IRQ+DMA setup */
+
+	hw_config->slots[0] = ad1848_init("MAD16 WSS", hw_config->io_base + 4,
+					  hw_config->irq,
+					  dma,
+					  dma2, 0,
+					  hw_config->osp,
+					  THIS_MODULE);
+	request_region(hw_config->io_base, 4, "MAD16 WSS config");
+}
+
+static int __init probe_mad16_mpu(struct address_info *hw_config)
+{
+	static int mpu_attached = 0;
+	unsigned char tmp;
+
+	if (!already_initialized)	/* The MSS port must be initialized first */
+		return 0;
+
+	if (mpu_attached)		/* Don't let them call this twice */
+		return 0;
+	mpu_attached = 1;
+
+	if (board_type < C929)	/* Early chip. No MPU support. Just SB MIDI */
+	{
+
+#ifdef CONFIG_MAD16_OLDCARD
+
+		tmp = mad_read(MC3_PORT);
+
+		/* 
+		 * MAD16 SB base is defined by the WSS base. It cannot be changed 
+		 * alone.
+		 * Ignore configured I/O base. Use the active setting. 
+		 */
+
+		if (mad_read(MC1_PORT) & 0x20)
+			hw_config->io_base = 0x240;
+		else
+			hw_config->io_base = 0x220;
+
+		switch (hw_config->irq)
+		{
+			case 5:
+				tmp = (tmp & 0x3f) | 0x80;
+				break;
+			case 7:
+				tmp = (tmp & 0x3f);
+				break;
+			case 11:
+				tmp = (tmp & 0x3f) | 0x40;
+				break;
+			default:
+				printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n");
+				return 0;
+		}
+
+		mad_write(MC3_PORT, tmp | 0x04);
+		hw_config->driver_use_1 = SB_MIDI_ONLY;
+		if (!sb_dsp_detect(hw_config, 0, 0, NULL))
+			return 0;
+
+		if (mad_read(MC1_PORT) & 0x20)
+			hw_config->io_base = 0x240;
+		else
+			hw_config->io_base = 0x220;
+
+		hw_config->name = "Mad16/Mozart";
+		sb_dsp_init(hw_config, THIS_MODULE);
+		return 1;
+#else
+		/* assuming all later Mozart cards are identified as
+		 * either 82C928 or Mozart. If so, following code attempts
+		 * to set MPU register. TODO - add probing
+		 */
+
+		tmp = mad_read(MC8_PORT);
+
+		switch (hw_config->irq)
+		{
+			case 5:
+				tmp |= 0x08;
+				break;
+			case 7:
+				tmp |= 0x10;
+				break;
+			case 9:
+				tmp |= 0x18;
+				break;
+			case 10:
+				tmp |= 0x20;
+				break;
+			case 11:
+				tmp |= 0x28;
+				break;
+			default:
+				printk(KERN_ERR "mad16/MOZART: invalid mpu_irq\n");
+				return 0;
+		}
+
+		switch (hw_config->io_base)
+		{
+			case 0x300:
+				tmp |= 0x01;
+				break;
+			case 0x310:
+				tmp |= 0x03;
+				break;
+			case 0x320:
+				tmp |= 0x05;
+				break;
+			case 0x330:
+				tmp |= 0x07;
+				break;
+			default:
+				printk(KERN_ERR "mad16/MOZART: invalid mpu_io\n");
+				return 0;
+		}
+
+		mad_write(MC8_PORT, tmp);	/* write MPU port parameters */
+		goto probe_401;
+#endif
+	}
+	tmp = mad_read(MC6_PORT) & 0x83;
+	tmp |= 0x80;		/* MPU-401 enable */
+
+	/* Set the MPU base bits */
+
+	switch (hw_config->io_base)
+	{
+		case 0x300:
+			tmp |= 0x60;
+			break;
+		case 0x310:
+			tmp |= 0x40;
+			break;
+		case 0x320:
+			tmp |= 0x20;
+			break;
+		case 0x330:
+			tmp |= 0x00;
+			break;
+		default:
+			printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base);
+			return 0;
+	}
+
+	/* Set the MPU IRQ bits */
+
+	switch (hw_config->irq)
+	{
+		case 5:
+			tmp |= 0x10;
+			break;
+		case 7:
+			tmp |= 0x18;
+			break;
+		case 9:
+			tmp |= 0x00;
+			break;
+		case 10:
+			tmp |= 0x08;
+			break;
+		default:
+			printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq);
+			break;
+	}
+			
+	mad_write(MC6_PORT, tmp);	/* Write MPU401 config */
+
+#ifndef CONFIG_MAD16_OLDCARD
+probe_401:
+#endif
+	hw_config->driver_use_1 = SB_MIDI_ONLY;
+	hw_config->name = "Mad16/Mozart";
+	return probe_uart401(hw_config, THIS_MODULE);
+}
+
+static void __exit unload_mad16(struct address_info *hw_config)
+{
+	ad1848_unload(hw_config->io_base + 4,
+			hw_config->irq,
+			hw_config->dma,
+			hw_config->dma2, 0);
+	release_region(hw_config->io_base, 4);
+	sound_unload_audiodev(hw_config->slots[0]);
+}
+
+static void __exit unload_mad16_mpu(struct address_info *hw_config)
+{
+#ifdef CONFIG_MAD16_OLDCARD
+	if (board_type < C929)	/* Early chip. No MPU support. Just SB MIDI */
+	{
+		sb_dsp_unload(hw_config, 0);
+		return;
+	}
+#endif
+
+	unload_uart401(hw_config);
+}
+
+static struct address_info cfg;
+static struct address_info cfg_mpu;
+
+static int found_mpu;
+
+static int __initdata mpu_io = 0;
+static int __initdata mpu_irq = 0;
+static int __initdata io = -1;
+static int __initdata dma = -1;
+static int __initdata dma16 = -1; /* Set this for modules that need it */
+static int __initdata irq = -1;
+static int __initdata cdtype = 0;
+static int __initdata cdirq = 0;
+static int __initdata cdport = 0x340;
+static int __initdata cddma = -1;
+static int __initdata opl4 = 0;
+static int __initdata joystick = 0;
+
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(mpu_irq, "i");
+MODULE_PARM(io,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma16,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(cdtype,"i");
+MODULE_PARM(cdirq,"i");
+MODULE_PARM(cdport,"i");
+MODULE_PARM(cddma,"i");
+MODULE_PARM(opl4,"i");
+MODULE_PARM(joystick,"i");
+MODULE_PARM(debug,"i");
+
+static int __initdata dma_map[2][8] =
+{
+	{0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02},
+	{0x03, -1, 0x01, 0x00, -1, -1, -1, -1}
+};
+
+static int __initdata irq_map[16] =
+{
+	0x00, -1, -1, 0x0A,
+	-1, 0x04, -1, 0x08,
+	-1, 0x10, 0x14, 0x18,
+	-1, -1, -1, -1
+};
+
+static int __init init_mad16(void)
+{
+	int dmatype = 0;
+
+	printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	printk(KERN_INFO "CDROM ");
+	switch (cdtype)
+	{
+		case 0x00:
+			printk("Disabled");
+			cdirq = 0;
+			break;
+		case 0x02:
+			printk("Sony CDU31A");
+			dmatype = 1;
+			if(cddma == -1) cddma = 3;
+			break;
+		case 0x04:
+			printk("Mitsumi");
+			dmatype = 0;
+			if(cddma == -1) cddma = 5;
+			break;
+		case 0x06:
+			printk("Panasonic Lasermate");
+			dmatype = 1;
+			if(cddma == -1) cddma = 3;
+			break;
+		case 0x08:
+			printk("Secondary IDE");
+			dmatype = 0;
+			if(cddma == -1) cddma = 5;
+			break;
+		case 0x0A:
+			printk("Primary IDE");
+			dmatype = 0;
+			if(cddma == -1) cddma = 5;
+			break;
+		default:
+			printk("\n");
+			printk(KERN_ERR "Invalid CDROM type\n");
+			return -EINVAL;
+	}
+
+ 	/*
+         *    Build the config words
+         */
+
+        mad16_conf = (joystick ^ 1) | cdtype;
+	mad16_cdsel = 0;
+        if (opl4)
+                mad16_cdsel |= 0x20;
+
+	if(cdtype){
+		if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1)
+		{
+			printk("\n");
+			printk(KERN_ERR "Invalid CDROM DMA\n");
+			return -EINVAL;
+		}
+		if (cddma)
+			printk(", DMA %d", cddma);
+		else
+			printk(", no DMA");
+
+		if (!cdirq)
+			printk(", no IRQ");
+		else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1)
+		{
+		  	printk(", invalid IRQ (disabling)");
+		  	cdirq = 0;
+		}
+		else printk(", IRQ %d", cdirq);
+
+		mad16_cdsel |= dma_map[dmatype][cddma];
+
+		if (cdtype < 0x08)
+		{
+			switch (cdport)
+			{
+				case 0x340:
+					mad16_cdsel |= 0x00;
+					break;
+				case 0x330:
+					mad16_cdsel |= 0x40;
+					break;
+				case 0x360:
+					mad16_cdsel |= 0x80;
+					break;
+				case 0x320:
+					mad16_cdsel |= 0xC0;
+					break;
+				default:
+					printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport);
+					return -EINVAL;
+			}
+		}
+		mad16_cdsel |= irq_map[cdirq];
+	}
+
+	printk(".\n");
+        printk(KERN_INFO "Joystick port ");
+        if (joystick == 1)
+                printk("enabled.\n");
+        else
+        {
+                joystick = 0;
+                printk("disabled.\n");
+        }
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma16;
+
+	if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
+		printk(KERN_ERR "I/O, DMA and irq are mandatory\n");
+		return -EINVAL;
+	}
+	
+	if (!probe_mad16(&cfg))
+		return -ENODEV;
+
+	cfg_mpu.io_base = mpu_io;
+	cfg_mpu.irq = mpu_irq;
+
+	attach_mad16(&cfg);
+
+	found_mpu = probe_mad16_mpu(&cfg_mpu);
+	return 0;
+}
+
+static void __exit cleanup_mad16(void)
+{
+	if (found_mpu)
+		unload_mad16_mpu(&cfg_mpu);
+	unload_mad16(&cfg);
+}
+
+module_init(init_mad16);
+module_exit(cleanup_mad16);
+
+#ifndef MODULE
+static int __init setup_mad16(char *str)
+{
+        /* io, irq */
+	int ints[7];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma16	= ints[4];
+	mpu_io	= ints[5];
+	mpu_irq = ints[6];
+
+	return 1;
+}
+
+__setup("mad16=", setup_mad16);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/maestro.c linux-2.4.19-pre5-mjc/sound/oss/maestro.c
--- linux/sound/oss/maestro.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/maestro.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3807 @@
+/*****************************************************************************
+ *
+ *      ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *	(c) Copyright 1999	 Alan Cox <alan.cox@linux.org>
+ *
+ *	Based heavily on SonicVibes.c:
+ *      Copyright (C) 1998-1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	Heavily modified by Zach Brown <zab@zabbo.net> based on lunch
+ *	with ESS engineers.  Many thanks to Howard Kim for providing 
+ *	contacts and hardware.  Honorable mention goes to Eric 
+ *	Brombaugh for all sorts of things.  Best regards to the 
+ *	proprietors of Hack Central for fine lodging.
+ *
+ *  Supported devices:
+ *  /dev/dsp0-3    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *
+ *  Hardware Description
+ *
+ *	A working Maestro setup contains the Maestro chip wired to a 
+ *	codec or 2.  In the Maestro we have the APUs, the ASSP, and the
+ *	Wavecache.  The APUs can be though of as virtual audio routing
+ *	channels.  They can take data from a number of sources and perform
+ *	basic encodings of the data.  The wavecache is a storehouse for
+ *	PCM data.  Typically it deals with PCI and interracts with the
+ *	APUs.  The ASSP is a wacky DSP like device that ESS is loth
+ *	to release docs on.  Thankfully it isn't required on the Maestro
+ *	until you start doing insane things like FM emulation and surround
+ *	encoding.  The codecs are almost always AC-97 compliant codecs, 
+ *	but it appears that early Maestros may have had PT101 (an ESS
+ *	part?) wired to them.  The only real difference in the Maestro
+ *	families is external goop like docking capability, memory for
+ *	the ASSP, and initialization differences.
+ *
+ *  Driver Operation
+ *
+ *	We only drive the APU/Wavecache as typical DACs and drive the
+ *	mixers in the codecs.  There are 64 APUs.  We assign 6 to each
+ *	/dev/dsp? device.  2 channels for output, and 4 channels for
+ *	input.
+ *
+ *	Each APU can do a number of things, but we only really use
+ *	3 basic functions.  For playback we use them to convert PCM
+ *	data fetched over PCI by the wavecahche into analog data that
+ *	is handed to the codec.  One APU for mono, and a pair for stereo.
+ *	When in stereo, the combination of smarts in the APU and Wavecache
+ *	decide which wavecache gets the left or right channel.
+ *
+ *	For record we still use the old overly mono system.  For each in
+ *	coming channel the data comes in from the codec, through a 'input'
+ *	APU, through another rate converter APU, and then into memory via
+ *	the wavecache and PCI.  If its stereo, we mash it back into LRLR in
+ *	software.  The pass between the 2 APUs is supposedly what requires us
+ *	to have a 512 byte buffer sitting around in wavecache/memory.
+ *
+ *	The wavecache makes our life even more fun.  First off, it can
+ *	only address the first 28 bits of PCI address space, making it
+ *	useless on quite a few architectures.  Secondly, its insane.
+ *	It claims to fetch from 4 regions of PCI space, each 4 meg in length.
+ *	But that doesn't really work.  You can only use 1 region.  So all our
+ *	allocations have to be in 4meg of each other.  Booo.  Hiss.
+ *	So we have a module parameter, dsps_order, that is the order of
+ *	the number of dsps to provide.  All their buffer space is allocated
+ *	on open time.  The sonicvibes OSS routines we inherited really want
+ *	power of 2 buffers, so we have all those next to each other, then
+ *	512 byte regions for the recording wavecaches.  This ends up
+ *	wasting quite a bit of memory.  The only fixes I can see would be 
+ *	getting a kernel allocator that could work in zones, or figuring out
+ *	just how to coerce the WP into doing what we want.
+ *
+ *	The indirection of the various registers means we have to spinlock
+ *	nearly all register accesses.  We have the main register indirection
+ *	like the wave cache, maestro registers, etc.  Then we have beasts
+ *	like the APU interface that is indirect registers gotten at through
+ *	the main maestro indirection.  Ouch.  We spinlock around the actual
+ *	ports on a per card basis.  This means spinlock activity at each IO
+ *	operation, but the only IO operation clusters are in non critical 
+ *	paths and it makes the code far easier to follow.  Interrupts are
+ *	blocked while holding the locks because the int handler has to
+ *	get at some of them :(.  The mixer interface doesn't, however.
+ *	We also have an OSS state lock that is thrown around in a few
+ *	places.
+ *
+ *	This driver has brute force APM suspend support.  We catch suspend
+ *	notifications and stop all work being done on the chip.  Any people
+ *	that try between this shutdown and the real suspend operation will
+ *	be put to sleep.  When we resume we restore our software state on
+ *	the chip and wake up the people that were using it.  The code thats
+ *	being used now is quite dirty and assumes we're on a uni-processor
+ *	machine.  Much of it will need to be cleaned up for SMP ACPI or 
+ *	similar.
+ *
+ *	We also pay attention to PCI power management now.  The driver
+ *	will power down units of the chip that it knows aren't needed.
+ *	The WaveProcessor and company are only powered on when people
+ *	have /dev/dsp*s open.  On removal the driver will
+ *	power down the maestro entirely.  There could still be
+ *	trouble with BIOSen that magically change power states 
+ *	themselves, but we'll see.  
+ *	
+ * History
+ *  v0.15 - May 21 2001 - Marcus Meissner <mm@caldera.de>
+ *      Ported to Linux 2.4 PCI API. Some clean ups, global devs list
+ *      removed (now using pci device driver data).
+ *      PM needs to be polished still. Bumped version.
+ *  (still kind of v0.14) May 13 2001 - Ben Pfaff <pfaffben@msu.edu>
+ *      Add support for 978 docking and basic hardware volume control
+ *  (still kind of v0.14) Nov 23 - Alan Cox <alan@redhat.com>
+ *	Add clocking= for people with seriously warped hardware
+ *  (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *	add __init to maestro_ac97_init() and maestro_install()
+ *  (still based on v0.14) Mar 29 2000 - Zach Brown <zab@redhat.com>
+ *	move to 2.3 power management interface, which
+ *		required hacking some suspend/resume/check paths 
+ *	make static compilation work
+ *  v0.14 - Jan 28 2000 - Zach Brown <zab@redhat.com>
+ *	add PCI power management through ACPI regs.
+ *	we now shut down on machine reboot/halt
+ *	leave scary PCI config items alone (isa stuff, mostly)
+ *	enable 1921s, it seems only mine was broke.
+ *	fix swapped left/right pcm dac.  har har.
+ *	up bob freq, increase buffers, fix pointers at underflow
+ *	silly compilation problems
+ *  v0.13 - Nov 18 1999 - Zach Brown <zab@redhat.com>
+ *	fix nec Versas?  man would that be cool.
+ *  v0.12 - Nov 12 1999 - Zach Brown <zab@redhat.com>
+ *	brown bag volume max fix..
+ *  v0.11 - Nov 11 1999 - Zach Brown <zab@redhat.com>
+ *	use proper stereo apu decoding, mmap/write should work.
+ *	make volume sliders more useful, tweak rate calculation.
+ *	fix lame 8bit format reporting bug.  duh. apm apu saving buglet also
+ *	fix maestro 1 clock freq "bug", remove pt101 support
+ *  v0.10 - Oct 28 1999 - Zach Brown <zab@redhat.com>
+ *	aha, so, sometimes the WP writes a status word to offset 0
+ *	  from one of the PCMBARs.  rearrange allocation accordingly..
+ *	  cheers again to Eric for being a good hacker in investigating this.
+ *	Jeroen Hoogervorst submits 7500 fix out of nowhere.  yay.  :)
+ *  v0.09 - Oct 23 1999 - Zach Brown <zab@redhat.com>
+ *	added APM support.
+ *	re-order something such that some 2Es now work.  Magic!
+ *	new codec reset routine.  made some codecs come to life.
+ *	fix clear_advance, sync some control with ESS.
+ *	now write to all base regs to be paranoid.
+ *  v0.08 - Oct 20 1999 - Zach Brown <zab@redhat.com>
+ *	Fix initial buflen bug.  I am so smart.  also smp compiling..
+ *	I owe Eric yet another beer: fixed recmask, igain, 
+ *	  muting, and adc sync consistency.  Go Team.
+ *  v0.07 - Oct 4 1999 - Zach Brown <zab@redhat.com>
+ *	tweak adc/dac, formating, and stuff to allow full duplex
+ *	allocate dsps memory at open() so we can fit in the wavecache window
+ *	fix wavecache braindamage.  again.  no more scribbling?
+ *	fix ess 1921 codec bug on some laptops.
+ *	fix dumb pci scanning bug
+ *	started 2.3 cleanup, redid spinlocks, little cleanups
+ *  v0.06 - Sep 20 1999 - Zach Brown <zab@redhat.com>
+ *	fix wavecache thinkos.  limit to 1 /dev/dsp.
+ *	eric is wearing his thinking toque this week.
+ *		spotted apu mode bugs and gain ramping problem
+ *	don't touch weird mixer regs, make recmask optional
+ *	fixed igain inversion, defaults for mixers, clean up rec_start
+ *	make mono recording work.
+ *	report subsystem stuff, please send reports.
+ *	littles: parallel out, amp now
+ *  v0.05 - Sep 17 1999 - Zach Brown <zab@redhat.com>
+ *	merged and fixed up Eric's initial recording code
+ *	munged format handling to catch misuse, needs rewrite.
+ *	revert ring bus init, fixup shared int, add pci busmaster setting
+ *	fix mixer oss interface, fix mic mute and recmask
+ *	mask off unsupported mixers, reset with all 1s, modularize defaults
+ *	make sure bob is running while we need it
+ *	got rid of device limit, initial minimal apm hooks
+ *	pull out dead code/includes, only allow multimedia/audio maestros
+ *  v0.04 - Sep 01 1999 - Zach Brown <zab@redhat.com>
+ *	copied memory leak fix from sonicvibes driver
+ *	different ac97 reset, play with 2.0 ac97, simplify ring bus setup
+ *	bob freq code, region sanity, jitter sync fix; all from Eric 
+ *
+ * TODO
+ *	fix bob frequency
+ *	endianness
+ *	do smart things with ac97 2.0 bits.
+ *	dual codecs
+ *	leave 54->61 open
+ *
+ *	it also would be fun to have a mode that would not use pci dma at all
+ *	but would copy into the wavecache on board memory and use that 
+ *	on architectures that don't like the maestro's pci dma ickiness.
+ */
+
+/*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/bitops.h>
+
+#include <linux/pm.h>
+static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d);
+
+#include "maestro.h"
+
+static struct pci_driver maestro_pci_driver;
+
+/* --------------------------------------------------------------------- */
+
+#define M_DEBUG 1
+
+#ifdef M_DEBUG
+static int debug=0;
+#define M_printk(args...) {if (debug) printk(args);}
+#else
+#define M_printk(x)
+#endif
+
+/* we try to setup 2^(dsps_order) /dev/dsp devices */
+static int dsps_order=0;
+/* wether or not we mess around with power management */
+static int use_pm=2; /* set to 1 for force */
+/* clocking for broken hardware - a few laptops seem to use a 50Khz clock
+	ie insmod with clocking=50000 or so */
+	
+static int clocking=48000;
+
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Alan Cox <alan@redhat.com>");
+MODULE_DESCRIPTION("ESS Maestro Driver");
+MODULE_LICENSE("GPL");
+
+#ifdef M_DEBUG
+MODULE_PARM(debug,"i");
+#endif
+MODULE_PARM(dsps_order,"i");
+MODULE_PARM(use_pm,"i");
+MODULE_PARM(clocking, "i");
+
+/* --------------------------------------------------------------------- */
+#define DRIVER_VERSION "0.15"
+
+#ifndef PCI_VENDOR_ESS
+#define PCI_VENDOR_ESS			0x125D
+#define PCI_DEVICE_ID_ESS_ESS1968	0x1968		/* Maestro 2	*/
+#define PCI_DEVICE_ID_ESS_ESS1978      	0x1978		/* Maestro 2E	*/
+
+#define PCI_VENDOR_ESS_OLD		0x1285		/* Platform Tech, 
+						the people the maestro 
+						was bought from */
+#define PCI_DEVICE_ID_ESS_ESS0100	0x0100		/* maestro 1 */
+#endif /* PCI_VENDOR_ESS */
+
+#define ESS_CHAN_HARD		0x100
+
+/* NEC Versas ? */
+#define NEC_VERSA_SUBID1	0x80581033
+#define NEC_VERSA_SUBID2	0x803c1033
+
+
+/* changed so that I could actually find all the
+	references and fix them up.  its a little more readable now. */
+#define ESS_FMT_STEREO	0x01
+#define ESS_FMT_16BIT	0x02
+#define ESS_FMT_MASK	0x03
+#define ESS_DAC_SHIFT	0   
+#define ESS_ADC_SHIFT	4
+
+#define ESS_STATE_MAGIC		0x125D1968
+#define ESS_CARD_MAGIC		0x19283746
+
+#define DAC_RUNNING		1
+#define ADC_RUNNING		2
+
+#define MAX_DSP_ORDER	2
+#define MAX_DSPS	(1<<MAX_DSP_ORDER)
+#define NR_DSPS		(1<<dsps_order)
+#define NR_IDRS		32
+
+#define NR_APUS		64
+#define NR_APU_REGS	16
+
+/* acpi states */
+enum {
+	ACPI_D0=0,
+	ACPI_D1,
+	ACPI_D2,
+	ACPI_D3
+};
+
+/* bits in the acpi masks */
+#define ACPI_12MHZ	( 1 << 15)
+#define ACPI_24MHZ	( 1 << 14)
+#define ACPI_978	( 1 << 13)
+#define ACPI_SPDIF	( 1 << 12)
+#define ACPI_GLUE	( 1 << 11)
+#define ACPI__10	( 1 << 10) /* reserved */
+#define ACPI_PCIINT	( 1 << 9)
+#define ACPI_HV		( 1 << 8) /* hardware volume */
+#define ACPI_GPIO	( 1 << 7)
+#define ACPI_ASSP	( 1 << 6)
+#define ACPI_SB		( 1 << 5) /* sb emul */
+#define ACPI_FM		( 1 << 4) /* fm emul */
+#define ACPI_RB		( 1 << 3) /* ringbus / aclink */
+#define ACPI_MIDI	( 1 << 2) 
+#define ACPI_GP		( 1 << 1) /* game port */
+#define ACPI_WP		( 1 << 0) /* wave processor */
+
+#define ACPI_ALL	(0xffff)
+#define ACPI_SLEEP	(~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \
+			ACPI_MIDI|ACPI_GP|ACPI_WP))
+#define ACPI_NONE	(ACPI__10)
+
+/* these masks indicate which units we care about at
+	which states */
+u16 acpi_state_mask[] = {
+	[ACPI_D0] = ACPI_ALL,
+	[ACPI_D1] = ACPI_SLEEP,
+	[ACPI_D2] = ACPI_SLEEP,
+	[ACPI_D3] = ACPI_NONE
+};
+
+static char version[] __devinitdata =
+KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n";
+
+
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+enum card_types_t {
+	TYPE_MAESTRO,
+	TYPE_MAESTRO2,
+	TYPE_MAESTRO2E
+};
+
+static const char *card_names[]={
+	[TYPE_MAESTRO] = "ESS Maestro",
+	[TYPE_MAESTRO2] = "ESS Maestro 2",
+	[TYPE_MAESTRO2E] = "ESS Maestro 2E"
+};
+
+static int clock_freq[]={
+	[TYPE_MAESTRO] = (49152000L / 1024L),
+	[TYPE_MAESTRO2] = (50000000L / 1024L),
+	[TYPE_MAESTRO2E] = (50000000L / 1024L)
+};
+
+static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf);
+
+static struct notifier_block maestro_nb = {maestro_notifier, NULL, 0};
+
+/* --------------------------------------------------------------------- */
+
+struct ess_state {
+	unsigned int magic;
+	/* FIXME: we probably want submixers in here, but only one record pair */
+	u8 apu[6];		/* l/r output, l/r intput converters, l/r input apus */
+	u8 apu_mode[6];		/* Running mode for this APU */
+	u8 apu_pan[6];		/* Panning setup for this APU */
+	u32 apu_base[6];	/* base address for this apu */
+	struct ess_card *card;	/* Card info */
+	/* wave stuff */
+	unsigned int rateadc, ratedac;
+	unsigned char fmt, enable;
+
+	int index;
+
+	/* this locks around the oss state in the driver */
+	spinlock_t lock;
+	/* only let 1 be opening at a time */
+	struct semaphore open_sem;
+	wait_queue_head_t open_wait;
+	mode_t open_mode;
+
+	/* soundcore stuff */
+	int dev_audio;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		/* XXX zab - swptr only in here so that it can be referenced by
+			clear_advance, as far as I can tell :( */
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;	/* our oss buffers are ready to go */
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+		u16 base;		/* Offset for ptr */
+	} dma_dac, dma_adc;
+
+	/* pointer to each dsp?s piece of the apu->src buffer page */
+	void *mixbuf;
+
+};
+	
+struct ess_card {
+	unsigned int magic;
+
+	/* We keep maestro cards in a linked list */
+	struct ess_card *next;
+
+	int dev_mixer;
+
+	int card_type;
+
+	/* as most of this is static,
+		perhaps it should be a pointer to a global struct */
+	struct mixer_goo {
+		int modcnt;
+		int supported_mixers;
+		int stereo_mixers;
+		int record_sources;
+		/* the caller must guarantee arg sanity before calling these */
+/*		int (*read_mixer)(struct ess_card *card, int index);*/
+		void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right);
+		int (*recmask_io)(struct ess_card *card,int rw,int mask);
+		unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
+	} mix;
+	
+	int power_regs;
+		
+	int in_suspend;
+	wait_queue_head_t suspend_queue;
+
+	struct ess_state channels[MAX_DSPS];
+	u16 maestro_map[NR_IDRS];	/* Register map */
+	/* we have to store this junk so that we can come back from a
+		suspend */
+	u16 apu_map[NR_APUS][NR_APU_REGS];	/* contents of apu regs */
+
+	/* this locks around the physical registers on the card */
+	spinlock_t lock;
+
+	/* memory for this card.. wavecache limited :(*/
+	void *dmapages;
+	int dmaorder;
+
+	/* hardware resources */
+	struct pci_dev *pcidev;
+	u32 iobase;
+	u32 irq;
+
+	int bob_freq;
+	char dsps_open;
+
+	int dock_mute_vol;
+};
+
+static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val );
+
+static unsigned 
+ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static void check_suspend(struct ess_card *card);
+
+/* --------------------------------------------------------------------- */
+
+
+/*
+ *	ESS Maestro AC97 codec programming interface.
+ */
+	 
+static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val)
+{
+	int io = card->iobase;
+	int i;
+	/*
+	 *	Wait for the codec bus to be free 
+	 */
+
+	check_suspend(card);
+	 
+	for(i=0;i<10000;i++)
+	{
+		if(!(inb(io+ESS_AC97_INDEX)&1)) 
+			break;
+	}
+	/*
+	 *	Write the bus
+	 */ 
+	outw(val, io+ESS_AC97_DATA);
+	mdelay(1);
+	outb(cmd, io+ESS_AC97_INDEX);
+	mdelay(1);
+}
+
+static u16 maestro_ac97_get(struct ess_card *card, u8 cmd)
+{
+	int io = card->iobase;
+	int sanity=10000;
+	u16 data;
+	int i;
+	
+	check_suspend(card);
+	/*
+	 *	Wait for the codec bus to be free 
+	 */
+	 
+	for(i=0;i<10000;i++)
+	{
+		if(!(inb(io+ESS_AC97_INDEX)&1))
+			break;
+	}
+
+	outb(cmd|0x80, io+ESS_AC97_INDEX);
+	mdelay(1);
+	
+	while(inb(io+ESS_AC97_INDEX)&1)
+	{
+		sanity--;
+		if(!sanity)
+		{
+			printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd);
+			return 0;
+		}
+	}
+	data=inw(io+ESS_AC97_DATA);
+	mdelay(1);
+	return data;
+}
+
+/* OSS interface to the ac97s.. */
+
+#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\
+	SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\
+	SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN)
+
+#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \
+	SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\
+	SOUND_MASK_SPEAKER)
+
+#define AC97_RECORD_MASK (SOUND_MASK_MIC|\
+	SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\
+	SOUND_MASK_PHONEIN)
+
+#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<<FOO) )
+
+/* this table has default mixer values for all OSS mixers.
+	be sure to fill it in if you add oss mixers
+	to anyone's supported mixer defines */
+
+ unsigned int mixer_defaults[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_VOLUME] =          0x3232,
+	[SOUND_MIXER_BASS] =            0x3232,
+	[SOUND_MIXER_TREBLE] =          0x3232,
+	[SOUND_MIXER_SPEAKER] =         0x3232,
+	[SOUND_MIXER_MIC] =     0x8000, /* annoying */
+	[SOUND_MIXER_LINE] =    0x3232,
+	[SOUND_MIXER_CD] =      0x3232,
+	[SOUND_MIXER_VIDEO] =   0x3232,
+	[SOUND_MIXER_LINE1] =   0x3232,
+	[SOUND_MIXER_PCM] =             0x3232,
+	[SOUND_MIXER_IGAIN] =           0x3232
+};
+	
+static struct ac97_mixer_hw {
+	unsigned char offset;
+	int scale;
+} ac97_hw[SOUND_MIXER_NRDEVICES]= {
+	[SOUND_MIXER_VOLUME]	=	{0x02,63},
+	[SOUND_MIXER_BASS]	=	{0x08,15},
+	[SOUND_MIXER_TREBLE]	=	{0x08,15},
+	[SOUND_MIXER_SPEAKER]	=	{0x0a,15},
+	[SOUND_MIXER_MIC]	=	{0x0e,31},
+	[SOUND_MIXER_LINE]	=	{0x10,31},
+	[SOUND_MIXER_CD]	=	{0x12,31},
+	[SOUND_MIXER_VIDEO]	=	{0x14,31},
+	[SOUND_MIXER_LINE1]	=	{0x16,31},
+	[SOUND_MIXER_PCM]	=	{0x18,31},
+	[SOUND_MIXER_IGAIN]	=	{0x1c,15}
+};
+
+#if 0 /* *shrug* removed simply because we never used it.
+		feel free to implement again if needed */
+
+/* reads the given OSS mixer from the ac97
+	the caller must have insured that the ac97 knows
+	about that given mixer, and should be holding a
+	spinlock for the card */
+static int ac97_read_mixer(struct ess_card *card, int mixer) 
+{
+	u16 val;
+	int ret=0;
+	struct ac97_mixer_hw *mh = &ac97_hw[mixer];
+
+	val = maestro_ac97_get(card, mh->offset);
+
+	if(AC97_STEREO_MASK & (1<<mixer)) {
+		/* nice stereo mixers .. */
+		int left,right;
+
+		left = (val >> 8)  & 0x7f;
+		right = val  & 0x7f;
+
+		if (mixer == SOUND_MIXER_IGAIN) {
+			right = (right * 100) / mh->scale;
+			left = (left * 100) / mh->scale;
+		else {
+			right = 100 - ((right * 100) / mh->scale);
+			left = 100 - ((left * 100) / mh->scale);
+		}
+
+		ret = left | (right << 8);
+	} else if (mixer == SOUND_MIXER_SPEAKER) {
+		ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
+	} else if (mixer == SOUND_MIXER_MIC) {
+		ret = 100 - (((val & 0x1f) * 100) / mh->scale);
+	/*  the low bit is optional in the tone sliders and masking
+		it lets is avoid the 0xf 'bypass'.. */
+	} else if (mixer == SOUND_MIXER_BASS) {
+		ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
+	} else if (mixer == SOUND_MIXER_TREBLE) {
+		ret = 100 - (((val & 0xe) * 100) / mh->scale);
+	}
+
+	M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret);
+
+	return ret;
+}
+#endif
+
+/* write the OSS encoded volume to the given OSS encoded mixer,
+	again caller's job to make sure all is well in arg land,
+	call with spinlock held */
+	
+/* linear scale -> log */
+static unsigned char lin2log[101] = 
+{
+0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 ,
+50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 ,
+63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 ,
+72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 ,
+78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 ,
+83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 ,
+87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 ,
+90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 ,
+93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 ,
+95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 ,
+97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 
+};
+
+static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right)
+{
+	u16 val=0;
+	struct ac97_mixer_hw *mh = &ac97_hw[mixer];
+
+	M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right);
+
+	if(AC97_STEREO_MASK & (1<<mixer)) {
+		/* stereo mixers, mute them if we can */
+
+		if (mixer == SOUND_MIXER_IGAIN) {
+			/* igain's slider is reversed.. */
+			right = (right * mh->scale) / 100;
+			left = (left * mh->scale) / 100;
+			if ((left == 0) && (right == 0))
+				val |= 0x8000;
+		} else {
+			/* log conversion for the stereo controls */
+			if((left == 0) && (right == 0))
+				val = 0x8000;
+			right = ((100 - lin2log[right]) * mh->scale) / 100;
+			left = ((100 - lin2log[left]) * mh->scale) / 100;
+		}
+
+		val |= (left << 8) | right;
+
+	} else if (mixer == SOUND_MIXER_SPEAKER) {
+		val = (((100 - left) * mh->scale) / 100) << 1;
+	} else if (mixer == SOUND_MIXER_MIC) {
+		val = maestro_ac97_get(card, mh->offset) & ~0x801f;
+		val |= (((100 - left) * mh->scale) / 100);
+	/*  the low bit is optional in the tone sliders and masking
+		it lets is avoid the 0xf 'bypass'.. */
+	} else if (mixer == SOUND_MIXER_BASS) {
+		val = maestro_ac97_get(card , mh->offset) & ~0x0f00;
+		val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
+	} else if (mixer == SOUND_MIXER_TREBLE)  {
+		val = maestro_ac97_get(card , mh->offset) & ~0x000f;
+		val |= (((100 - left) * mh->scale) / 100) & 0x000e;
+	}
+
+	maestro_ac97_set(card , mh->offset, val);
+	
+	M_printk(" -> %x\n",val);
+}
+
+/* the following tables allow us to go from 
+	OSS <-> ac97 quickly. */
+
+enum ac97_recsettings {
+	AC97_REC_MIC=0,
+	AC97_REC_CD,
+	AC97_REC_VIDEO,
+	AC97_REC_AUX,
+	AC97_REC_LINE,
+	AC97_REC_STEREO, /* combination of all enabled outputs..  */
+	AC97_REC_MONO,        /*.. or the mono equivalent */
+	AC97_REC_PHONE        
+};
+
+static unsigned int ac97_oss_mask[] = {
+	[AC97_REC_MIC] = SOUND_MASK_MIC, 
+	[AC97_REC_CD] = SOUND_MASK_CD, 
+	[AC97_REC_VIDEO] = SOUND_MASK_VIDEO, 
+	[AC97_REC_AUX] = SOUND_MASK_LINE1, 
+	[AC97_REC_LINE] = SOUND_MASK_LINE, 
+	[AC97_REC_PHONE] = SOUND_MASK_PHONEIN
+};
+
+/* indexed by bit position */
+static unsigned int ac97_oss_rm[] = {
+	[SOUND_MIXER_MIC] = AC97_REC_MIC,
+	[SOUND_MIXER_CD] = AC97_REC_CD,
+	[SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
+	[SOUND_MIXER_LINE1] = AC97_REC_AUX,
+	[SOUND_MIXER_LINE] = AC97_REC_LINE,
+	[SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
+};
+	
+/* read or write the recmask 
+	the ac97 can really have left and right recording
+	inputs independantly set, but OSS doesn't seem to 
+	want us to express that to the user. 
+	the caller guarantees that we have a supported bit set,
+	and they must be holding the card's spinlock */
+static int 
+ac97_recmask_io(struct ess_card *card, int read, int mask) 
+{
+	unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ];
+
+	if (read) return val;
+
+	/* oss can have many inputs, maestro cant.  try
+		to pick the 'new' one */
+
+	if (mask != val) mask &= ~val;
+
+	val = ffs(mask) - 1; 
+	val = ac97_oss_rm[val];
+	val |= val << 8;  /* set both channels */
+
+	M_printk("maestro: setting ac97 recmask to 0x%x\n",val);
+
+	maestro_ac97_set(card,0x1a,val);
+
+	return 0;
+};
+
+/*
+ *	The Maestro can be wired to a standard AC97 compliant codec
+ *	(see www.intel.com for the pdf's on this), or to a PT101 codec
+ *	which appears to be the ES1918 (data sheet on the esstech.com.tw site)
+ *
+ *	The PT101 setup is untested.
+ */
+ 
+static u16 __init maestro_ac97_init(struct ess_card *card)
+{
+	u16 vend1, vend2, caps;
+
+	card->mix.supported_mixers = AC97_SUPPORTED_MASK;
+	card->mix.stereo_mixers = AC97_STEREO_MASK;
+	card->mix.record_sources = AC97_RECORD_MASK;
+/*	card->mix.read_mixer = ac97_read_mixer;*/
+	card->mix.write_mixer = ac97_write_mixer;
+	card->mix.recmask_io = ac97_recmask_io;
+
+	vend1 = maestro_ac97_get(card, 0x7c);
+	vend2 = maestro_ac97_get(card, 0x7e);
+
+	caps = maestro_ac97_get(card, 0x00);
+
+	printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n",
+		vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf);
+
+	if (! (caps & 0x4) ) {
+		/* no bass/treble nobs */
+		card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
+	}
+
+	/* XXX endianness, dork head. */
+	/* vendor specifc bits.. */
+	switch ((long)(vend1 << 16) | vend2) {
+	case 0x545200ff:	/* TriTech */
+		/* no idea what this does */
+		maestro_ac97_set(card,0x2a,0x0001);
+		maestro_ac97_set(card,0x2c,0x0000);
+		maestro_ac97_set(card,0x2c,0xffff);
+		break;
+#if 0	/* i thought the problems I was seeing were with
+	the 1921, but apparently they were with the pci board
+	it was on, so this code is commented out.
+	 lets see if this holds true. */
+	case 0x83847609:	/* ESS 1921 */
+		/* writing to 0xe (mic) or 0x1a (recmask) seems
+			to hang this codec */
+		card->mix.supported_mixers &= ~(SOUND_MASK_MIC);
+		card->mix.record_sources = 0;
+		card->mix.recmask_io = NULL;
+#if 0	/* don't ask.  I have yet to see what these actually do. */
+		maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */
+		udelay(20);
+		maestro_ac97_set(card,0x78,0x3002);
+		udelay(20);
+		maestro_ac97_set(card,0x78,0x3802);
+		udelay(20);
+#endif
+		break;
+#endif
+	default: break;
+	}
+
+	maestro_ac97_set(card, 0x1E, 0x0404);
+	/* null misc stuff */
+	maestro_ac97_set(card, 0x20, 0x0000);
+
+	return 0;
+}
+
+#if 0  /* there has been 1 person on the planet with a pt101 that we
+	know of.  If they care, they can put this back in :) */
+static u16 maestro_pt101_init(struct ess_card *card,int iobase)
+{
+	printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
+	/* who knows.. */
+	maestro_ac97_set(iobase, 0x2A, 0x0001);
+	maestro_ac97_set(iobase, 0x2C, 0x0000);
+	maestro_ac97_set(iobase, 0x2C, 0xFFFF);
+	maestro_ac97_set(iobase, 0x10, 0x9F1F);
+	maestro_ac97_set(iobase, 0x12, 0x0808);
+	maestro_ac97_set(iobase, 0x14, 0x9F1F);
+	maestro_ac97_set(iobase, 0x16, 0x9F1F);
+	maestro_ac97_set(iobase, 0x18, 0x0404);
+	maestro_ac97_set(iobase, 0x1A, 0x0000);
+	maestro_ac97_set(iobase, 0x1C, 0x0000);
+	maestro_ac97_set(iobase, 0x02, 0x0404);
+	maestro_ac97_set(iobase, 0x04, 0x0808);
+	maestro_ac97_set(iobase, 0x0C, 0x801F);
+	maestro_ac97_set(iobase, 0x0E, 0x801F);
+	return 0;
+}
+#endif
+
+/* this is very magic, and very slow.. */
+static void 
+maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev)
+{
+	u16 save_68;
+	u16 w;
+	u32 vend;
+
+	outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+	outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+	outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+	/* reset the first codec */
+	outw(0x0000,  ioaddr+0x36);
+	save_68 = inw(ioaddr+0x68);
+	pci_read_config_word(pcidev, 0x58, &w);	/* something magical with gpio and bus arb. */
+	pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+	if( w & 0x1)
+		save_68 |= 0x10;
+	outw(0xfffe, ioaddr + 0x64);	/* tickly gpio 0.. */
+	outw(0x0001, ioaddr + 0x68);
+	outw(0x0000, ioaddr + 0x60);
+	udelay(20);
+	outw(0x0001, ioaddr + 0x60);
+	mdelay(20);
+
+	outw(save_68 | 0x1, ioaddr + 0x68);	/* now restore .. */
+	outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38);
+	outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a);
+	outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c);
+
+	/* now the second codec */
+	outw(0x0000,  ioaddr+0x36);
+	outw(0xfff7, ioaddr + 0x64);
+	save_68 = inw(ioaddr+0x68);
+	outw(0x0009, ioaddr + 0x68);
+	outw(0x0001, ioaddr + 0x60);
+	udelay(20);
+	outw(0x0009, ioaddr + 0x60);
+	mdelay(500);	/* .. ouch.. */
+	outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+	outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+	outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+#if 0 /* the loop here needs to be much better if we want it.. */
+	M_printk("trying software reset\n");
+	/* try and do a software reset */
+	outb(0x80|0x7c, ioaddr + 0x30);
+	for (w=0; ; w++) {
+		if ((inw(ioaddr+ 0x30) & 1) == 0) {
+			if(inb(ioaddr + 0x32) !=0) break;
+
+			outb(0x80|0x7d, ioaddr + 0x30);
+			if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break;
+			outb(0x80|0x7f, ioaddr + 0x30);
+			if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break;
+		}
+
+		if( w > 10000) {
+			outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37);  /* do a software reset */
+			mdelay(500); /* oh my.. */
+			outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37);  
+			udelay(1);
+			outw( 0x80, ioaddr+0x30);
+			for(w = 0 ; w < 10000; w++) {
+				if((inw(ioaddr + 0x30) & 1) ==0) break;
+			}
+		}
+	}
+#endif
+	if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) {
+		/* turn on external amp? */
+		outw(0xf9ff, ioaddr + 0x64);
+		outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68);
+		outw(0x0209, ioaddr + 0x60);
+	}
+
+	/* Turn on the 978 docking chip.
+	   First frob the "master output enable" bit,
+	   then set most of the playback volume control registers to max. */
+	outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0);
+	outb(0xff, ioaddr+0xc3);
+	outb(0xff, ioaddr+0xc4);
+	outb(0xff, ioaddr+0xc6);
+	outb(0xff, ioaddr+0xc8);
+	outb(0x3f, ioaddr+0xcf);
+	outb(0x3f, ioaddr+0xd0);
+}
+/*
+ *	Indirect register access. Not all registers are readable so we
+ *	need to keep register state ourselves
+ */
+ 
+#define WRITEABLE_MAP	0xEFFFFF
+#define READABLE_MAP	0x64003F
+
+/*
+ *	The Maestro engineers were a little indirection happy. These indirected
+ *	registers themselves include indirect registers at another layer
+ */
+
+static void __maestro_write(struct ess_card *card, u16 reg, u16 data)
+{
+	long ioaddr = card->iobase;
+
+	outw(reg, ioaddr+0x02);
+	outw(data, ioaddr+0x00);
+	if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg);
+	else card->maestro_map[reg]=data;
+
+}
+ 
+static void maestro_write(struct ess_state *s, u16 reg, u16 data)
+{
+	unsigned long flags;
+
+	check_suspend(s->card);
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	__maestro_write(s->card,reg,data);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+}
+
+static u16 __maestro_read(struct ess_card *card, u16 reg)
+{
+	long ioaddr = card->iobase;
+
+	outw(reg, ioaddr+0x02);
+	return card->maestro_map[reg]=inw(ioaddr+0x00);
+}
+
+static u16 maestro_read(struct ess_state *s, u16 reg)
+{
+	if(READABLE_MAP & (1<<reg))
+	{
+		unsigned long flags;
+		check_suspend(s->card);
+		spin_lock_irqsave(&s->card->lock,flags);
+
+		__maestro_read(s->card,reg);
+
+		spin_unlock_irqrestore(&s->card->lock,flags);
+	}
+	return s->card->maestro_map[reg];
+}
+
+/*
+ *	These routines handle accessing the second level indirections to the
+ *	wave ram.
+ */
+
+/*
+ *	The register names are the ones ESS uses (see 104T31.ZIP)
+ */
+ 
+#define IDR0_DATA_PORT		0x00
+#define IDR1_CRAM_POINTER	0x01
+#define IDR2_CRAM_DATA		0x02
+#define IDR3_WAVE_DATA		0x03
+#define IDR4_WAVE_PTR_LOW	0x04
+#define IDR5_WAVE_PTR_HI	0x05
+#define IDR6_TIMER_CTRL		0x06
+#define IDR7_WAVE_ROMRAM	0x07
+
+static void apu_index_set(struct ess_card *card, u16 index)
+{
+	int i;
+	__maestro_write(card, IDR1_CRAM_POINTER, index);
+	for(i=0;i<1000;i++)
+		if(__maestro_read(card, IDR1_CRAM_POINTER)==index)
+			return;
+	printk(KERN_WARNING "maestro: APU register select failed.\n");
+}
+
+static void apu_data_set(struct ess_card *card, u16 data)
+{
+	int i;
+	for(i=0;i<1000;i++)
+	{
+		if(__maestro_read(card, IDR0_DATA_PORT)==data)
+			return;
+		__maestro_write(card, IDR0_DATA_PORT, data);
+	}
+}
+
+/*
+ *	This is the public interface for APU manipulation. It handles the
+ *	interlock to avoid two APU writes in parallel etc. Don't diddle
+ *	directly with the stuff above.
+ */
+
+static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data)
+{
+	unsigned long flags;
+	
+	check_suspend(s->card);
+
+	if(channel&ESS_CHAN_HARD)
+		channel&=~ESS_CHAN_HARD;
+	else
+	{
+		if(channel>5)
+			printk("BAD CHANNEL %d.\n",channel);
+		else
+			channel = s->apu[channel];
+		/* store based on real hardware apu/reg */
+		s->card->apu_map[channel][reg]=data;
+	}
+	reg|=(channel<<4);
+	
+	/* hooray for double indirection!! */
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	apu_index_set(s->card, reg);
+	apu_data_set(s->card, data);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+}
+
+static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg)
+{
+	unsigned long flags;
+	u16 v;
+	
+	check_suspend(s->card);
+
+	if(channel&ESS_CHAN_HARD)
+		channel&=~ESS_CHAN_HARD;
+	else
+		channel = s->apu[channel];
+
+	reg|=(channel<<4);
+	
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	apu_index_set(s->card, reg);
+	v=__maestro_read(s->card, IDR0_DATA_PORT);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+	return v;
+}
+
+
+/*
+ *	The wavecache buffers between the APUs and
+ *	pci bus mastering
+ */
+ 
+static void wave_set_register(struct ess_state *s, u16 reg, u16 value)
+{
+	long ioaddr = s->card->iobase;
+	unsigned long flags;
+	check_suspend(s->card);
+	
+	spin_lock_irqsave(&s->card->lock,flags);
+
+	outw(reg, ioaddr+0x10);
+	outw(value, ioaddr+0x12);
+
+	spin_unlock_irqrestore(&s->card->lock,flags);
+}
+
+static u16 wave_get_register(struct ess_state *s, u16 reg)
+{
+	long ioaddr = s->card->iobase;
+	unsigned long flags;
+	u16 value;
+	check_suspend(s->card);
+	
+	spin_lock_irqsave(&s->card->lock,flags);
+	outw(reg, ioaddr+0x10);
+	value=inw(ioaddr+0x12);
+	spin_unlock_irqrestore(&s->card->lock,flags);
+	
+	return value;
+}
+
+static void sound_reset(int ioaddr)
+{
+	outw(0x2000, 0x18+ioaddr);
+	udelay(1);
+	outw(0x0000, 0x18+ioaddr);
+	udelay(1);
+}
+
+/* sets the play formats of these apus, should be passed the already shifted format */
+static void set_apu_fmt(struct ess_state *s, int apu, int mode)
+{
+	int apu_fmt = 0x10;
+
+	if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; 
+	if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; 
+	s->apu_mode[apu]   = apu_fmt;
+	s->apu_mode[apu+1] = apu_fmt;
+}
+
+/* this only fixes the output apu mode to be later set by start_dac and
+	company.  output apu modes are set in ess_rec_setup */
+static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data)
+{
+	s->fmt = (s->fmt & mask) | data;
+	set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK);
+}
+
+/* this is off by a little bit.. */
+static u32 compute_rate(struct ess_state *s, u32 freq)
+{
+	u32 clock = clock_freq[s->card->card_type];     
+
+	freq = (freq * clocking)/48000;
+	
+	if (freq == 48000) 
+		return 0x10000;
+
+	return ((freq / clock) <<16 )+  
+		(((freq % clock) << 16) / clock);
+}
+
+static void set_dac_rate(struct ess_state *s, unsigned int rate)
+{
+	u32 freq;
+	int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+
+	s->ratedac = rate;
+
+	if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO))
+		rate >>= 1;
+
+/*	M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/
+
+	freq = compute_rate(s, rate);
+	
+	/* Load the frequency, turn on 6dB */
+	apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 0, 3, freq>>8);
+	apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 1, 3, freq>>8);
+}
+
+static void set_adc_rate(struct ess_state *s, unsigned rate)
+{
+	u32 freq;
+
+	/* Sample Rate conversion APUs don't like 0x10000 for their rate */
+	if (rate > 47999)
+		rate = 47999;
+	if (rate < 4000)
+		rate = 4000;
+
+	s->rateadc = rate;
+
+	freq = compute_rate(s, rate);
+	
+	/* Load the frequency, turn on 6dB */
+	apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 2, 3, freq>>8);
+	apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 3, 3, freq>>8);
+
+	/* fix mixer rate at 48khz.  and its _must_ be 0x10000. */
+	freq = 0x10000;
+
+	apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 4, 3, freq>>8);
+	apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)|
+		( ((freq&0xFF)<<8)|0x10 ));
+	apu_set_register(s, 5, 3, freq>>8);
+}
+
+/* Stop our host of recording apus */
+static inline void stop_adc(struct ess_state *s)
+{
+	/* XXX lets hope we don't have to lock around this */
+	if (! (s->enable & ADC_RUNNING)) return;
+
+	s->enable &= ~ADC_RUNNING;
+	apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F);
+	apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F);
+	apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F);
+	apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F);
+}	
+
+/* stop output apus */
+static void stop_dac(struct ess_state *s)
+{
+	/* XXX have to lock around this? */
+	if (! (s->enable & DAC_RUNNING)) return;
+
+	s->enable &= ~DAC_RUNNING;
+	apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F);
+	apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F);
+}	
+
+static void start_dac(struct ess_state *s)
+{
+	/* XXX locks? */
+	if (	(s->dma_dac.mapped || s->dma_dac.count > 0) && 
+		s->dma_dac.ready &&
+		(! (s->enable & DAC_RUNNING)) ) {
+
+		s->enable |= DAC_RUNNING;
+
+		apu_set_register(s, 0, 0, 
+			(apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]);
+
+		if((s->fmt >> ESS_DAC_SHIFT)  & ESS_FMT_STEREO) 
+			apu_set_register(s, 1, 0, 
+				(apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]);
+	}
+}	
+
+static void start_adc(struct ess_state *s)
+{
+	/* XXX locks? */
+	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
+	    && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) {
+
+		s->enable |= ADC_RUNNING;
+		apu_set_register(s, 2, 0, 
+			(apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]);
+		apu_set_register(s, 4, 0, 
+			(apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]);
+
+		if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
+			apu_set_register(s, 3, 0, 
+				(apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]);
+			apu_set_register(s, 5, 0, 
+				(apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]);
+		}
+			
+	}
+}	
+
+
+/*
+ *	Native play back driver 
+ */
+
+/* the mode passed should be already shifted and masked */
+static void 
+ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size)
+{
+	u32 pa;
+	u32 tmpval;
+	int high_apu = 0;
+	int channel;
+
+	M_printk("mode=%d rate=%d buf=%p len=%d.\n",
+		mode, rate, buffer, size);
+		
+	/* all maestro sizes are in 16bit words */
+	size >>=1;
+
+	if(mode&ESS_FMT_STEREO) {
+		high_apu++;
+		/* only 16/stereo gets size divided */
+		if(mode&ESS_FMT_16BIT)
+			size>>=1;
+	}
+	
+	for(channel=0; channel <= high_apu; channel++)
+	{
+		pa = virt_to_bus(buffer);
+
+		/* set the wavecache control reg */
+		tmpval = (pa - 0x10) & 0xFFF8;
+		if(!(mode & ESS_FMT_16BIT)) tmpval |= 4;
+		if(mode & ESS_FMT_STEREO) tmpval |= 2;
+		ess->apu_base[channel]=tmpval;
+		wave_set_register(ess, ess->apu[channel]<<3, tmpval);
+		
+		pa -= virt_to_bus(ess->card->dmapages);
+		pa>>=1; /* words */
+		
+		/* base offset of dma calcs when reading the pointer
+			on the left one */
+		if(!channel) ess->dma_dac.base = pa&0xFFFF;
+		
+		pa|=0x00400000;			/* System RAM */
+
+		/* XXX the 16bit here might not be needed.. */
+		if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) {
+			if(channel) 
+				pa|=0x00800000;			/* Stereo */
+			pa>>=1;
+		}
+			
+/* XXX think about endianess when writing these registers */
+		M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa);
+		/* start of sample */
+		apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8);
+		apu_set_register(ess, channel, 5, pa&0xFFFF);
+		/* sample end */
+		apu_set_register(ess, channel, 6, (pa+size)&0xFFFF);
+		/* setting loop len == sample len */
+		apu_set_register(ess, channel, 7, size);
+		
+		/* clear effects/env.. */
+		apu_set_register(ess, channel, 8, 0x0000);
+		/* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */
+		apu_set_register(ess, channel, 9, 0xD000);
+
+		/* clear routing stuff */
+		apu_set_register(ess, channel, 11, 0x0000);
+		/* dma on, no envelopes, filter to all 1s) */
+		apu_set_register(ess, channel, 0, 0x400F);
+		
+		if(mode&ESS_FMT_16BIT)
+			ess->apu_mode[channel]=0x10;
+		else
+			ess->apu_mode[channel]=0x30;
+
+		if(mode&ESS_FMT_STEREO) {
+			/* set panning: left or right */
+			apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10));
+			ess->apu_mode[channel] += 0x10;
+		} else
+			apu_set_register(ess, channel, 10, 0x8F08);
+	}
+	
+	/* clear WP interrupts */
+	outw(1, ess->card->iobase+0x04);
+	/* enable WP ints */
+	outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18);
+
+	/* go team! */
+	set_dac_rate(ess,rate);
+	start_dac(ess);
+}
+
+/*
+ *	Native record driver 
+ */
+
+/* again, passed mode is alrady shifted/masked */
+static void 
+ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size)
+{
+	int apu_step = 2;
+	int channel;
+
+	M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n",
+		mode, rate, buffer, size);
+		
+	/* all maestro sizes are in 16bit words */
+	size >>=1;
+
+	/* we're given the full size of the buffer, but
+	in stereo each channel will only use its half */
+	if(mode&ESS_FMT_STEREO) {
+		size >>=1; 
+		apu_step = 1;
+	}
+	
+	/* APU assignments: 2 = mono/left SRC
+	                    3 = right SRC
+	                    4 = mono/left Input Mixer
+	                    5 = right Input Mixer */
+	for(channel=2;channel<6;channel+=apu_step)
+	{
+		int i;
+		int bsize, route;
+		u32 pa;
+		u32 tmpval;
+
+		/* data seems to flow from the codec, through an apu into
+			the 'mixbuf' bit of page, then through the SRC apu
+			and out to the real 'buffer'.  ok.  sure.  */
+		
+		if(channel & 0x04) {
+			/* ok, we're an input mixer going from adc
+				through the mixbuf to the other apus */
+
+			if(!(channel & 0x01)) { 
+				pa = virt_to_bus(ess->mixbuf);
+			} else {
+				pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4));
+			}
+
+			/* we source from a 'magic' apu */
+			bsize = PAGE_SIZE >> 5;	/* half of this channels alloc, in words */
+			route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */
+			ess->apu_mode[channel] = 0x90;  /* Input Mixer */
+
+		} else {  
+			/* we're a rate converter taking
+				input from the input apus and outputing it to
+				system memory */
+			if(!(channel & 0x01))  {
+				pa = virt_to_bus(buffer);
+			} else {
+				/* right channel records its split half.
+				*2 accomodates for rampant shifting earlier */
+				pa = virt_to_bus(buffer + size*2);
+			}
+
+			ess->apu_mode[channel] = 0xB0;  /* Sample Rate Converter */
+
+			bsize = size; 
+			/* get input from inputing apu */
+			route = channel + 2;
+		}
+
+		M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel);
+		
+		/* set the wavecache control reg */
+		tmpval = (pa - 0x10) & 0xFFF8;
+		ess->apu_base[channel]=tmpval;
+		wave_set_register(ess, ess->apu[channel]<<3, tmpval);
+		
+		pa -= virt_to_bus(ess->card->dmapages);
+		pa>>=1; /* words */
+		
+		/* base offset of dma calcs when reading the pointer
+			on this left one */
+		if(channel==2) ess->dma_adc.base = pa&0xFFFF;
+
+		pa|=0x00400000;			/* bit 22 -> System RAM */
+
+		M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", 
+			ess->apu[channel], pa, bsize, route);
+		
+		/* Begin loading the APU */		
+		for(i=0;i<15;i++)		/* clear all PBRs */
+			apu_set_register(ess, channel, i, 0x0000);
+			
+		apu_set_register(ess, channel, 0, 0x400F);
+
+		/* need to enable subgroups.. and we should probably
+			have different groups for different /dev/dsps..  */
+ 		apu_set_register(ess, channel, 2, 0x8);
+				
+		/* Load the buffer into the wave engine */
+		apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8);
+		/* XXX reg is little endian.. */
+		apu_set_register(ess, channel, 5, pa&0xFFFF);
+		apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF);
+		apu_set_register(ess, channel, 7, bsize);
+				
+		/* clear effects/env.. */
+		apu_set_register(ess, channel, 8, 0x00F0);
+		
+		/* amplitude now?  sure.  why not.  */
+		apu_set_register(ess, channel, 9, 0x0000);
+
+		/* set filter tune, radius, polar pan */
+		apu_set_register(ess, channel, 10, 0x8F08);
+
+		/* route input */
+		apu_set_register(ess, channel, 11, route);
+	}
+	
+	/* clear WP interrupts */
+	outw(1, ess->card->iobase+0x04);
+	/* enable WP ints */
+	outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18);
+
+	/* let 'er rip */
+	set_adc_rate(ess,rate);
+	start_adc(ess);
+}
+/* --------------------------------------------------------------------- */
+
+static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count)
+{
+	M_printk("set_dmaa??\n");
+}
+
+static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count)
+{
+	M_printk("set_dmac??\n");
+}
+
+/* Playback pointer */
+static inline unsigned get_dmaa(struct ess_state *s)
+{
+	int offset;
+
+	offset = apu_get_register(s,0,5);
+
+/*	M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */
+	
+	offset-=s->dma_dac.base;
+
+	return (offset&0xFFFE)<<1; /* hardware is in words */
+}
+
+/* Record pointer */
+static inline unsigned get_dmac(struct ess_state *s)
+{
+	int offset;
+
+	offset = apu_get_register(s,2,5);
+
+/*	M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */
+	
+	/* The offset is an address not a position relative to base */
+	offset-=s->dma_adc.base;
+	
+	return (offset&0xFFFE)<<1; /* hardware is in words */
+}
+
+/*
+ *	Meet Bob, the timer...
+ */
+
+static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void stop_bob(struct ess_state *s)
+{
+	/* Mask IDR 11,17 */
+	maestro_write(s,  0x11, maestro_read(s, 0x11)&~1);
+	maestro_write(s,  0x17, maestro_read(s, 0x17)&~1);
+}
+
+/* eventually we could be clever and limit bob ints
+	to the frequency at which our smallest duration
+	chunks may expire */
+#define ESS_SYSCLK	50000000
+static void start_bob(struct ess_state *s)
+{
+	int prescale;
+	int divide;
+	
+	/* XXX make freq selector much smarter, see calc_bob_rate */
+	int freq = 200; 
+	
+	/* compute ideal interrupt frequency for buffer size & play rate */
+	/* first, find best prescaler value to match freq */
+	for(prescale=5;prescale<12;prescale++)
+		if(freq > (ESS_SYSCLK>>(prescale+9)))
+			break;
+			
+	/* next, back off prescaler whilst getting divider into optimum range */
+	divide=1;
+	while((prescale > 5) && (divide<32))
+	{
+		prescale--;
+		divide <<=1;
+	}
+	divide>>=1;
+	
+	/* now fine-tune the divider for best match */
+	for(;divide<31;divide++)
+		if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1)))
+			break;
+	
+	/* divide = 0 is illegal, but don't let prescale = 4! */
+	if(divide == 0)
+	{
+		divide++;
+		if(prescale>5)
+			prescale--;
+	}
+
+	maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */
+	
+	/* Now set IDR 11/17 */
+	maestro_write(s, 0x11, maestro_read(s, 0x11)|1);
+	maestro_write(s, 0x17, maestro_read(s, 0x17)|1);
+}
+/* --------------------------------------------------------------------- */
+
+/* this quickly calculates the frequency needed for bob
+	and sets it if its different than what bob is
+	currently running at.  its called often so 
+	needs to be fairly quick. */
+#define BOB_MIN 50
+#define BOB_MAX 400
+static void calc_bob_rate(struct ess_state *s) {
+#if 0 /* this thing tries to set the frequency of bob such that
+	there are 2 interrupts / buffer walked by the dac/adc.  That
+	is probably very wrong for people who actually care about 
+	mid buffer positioning.  it should be calculated as bytes/interrupt
+	and that needs to be decided :)  so for now just use the static 150
+	in start_bob.*/
+
+	unsigned int dac_rate=2,adc_rate=1,newrate;
+	static int israte=-1;
+
+	if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN;
+	else  {
+		dac_rate =	(2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) /
+				(s->dma_dac.fragsize) ;
+	}
+		
+	if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN;
+	else {
+		adc_rate =	(2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) /
+				(s->dma_adc.fragsize) ;
+	}
+
+	if(dac_rate > adc_rate) newrate = adc_rate;
+	else newrate=dac_rate;
+
+	if(newrate > BOB_MAX) newrate = BOB_MAX;
+	else {
+		if(newrate < BOB_MIN) 
+			newrate = BOB_MIN;
+	}
+
+	if( israte != newrate) {
+		printk("dac: %d  adc: %d rate: %d\n",dac_rate,adc_rate,israte);
+		israte=newrate;
+	}
+#endif
+
+}
+
+static int 
+prog_dmabuf(struct ess_state *s, unsigned rec)
+{
+	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+	unsigned rate = rec ? s->rateadc : s->ratedac;
+	unsigned bytepersec;
+	unsigned bufs;
+	unsigned char fmt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	fmt = s->fmt;
+	if (rec) {
+		stop_adc(s);
+		fmt >>= ESS_ADC_SHIFT;
+	} else {
+		stop_dac(s);
+		fmt >>= ESS_DAC_SHIFT;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	fmt &= ESS_FMT_MASK;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+
+	/* this algorithm is a little nuts.. where did /1000 come from? */
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3; 
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+
+	M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize);
+
+	memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize);
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (rec) 
+		ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize);
+	else 
+		ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize);
+
+	spin_unlock_irqrestore(&s->lock, flags);
+	db->ready = 1;
+
+	return 0;
+}
+
+static __inline__ void 
+clear_advance(struct ess_state *s)
+{
+	unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80;
+	
+	unsigned char *buf = s->dma_dac.rawbuf;
+	unsigned bsize = s->dma_dac.dmasize;
+	unsigned bptr = s->dma_dac.swptr;
+	unsigned len = s->dma_dac.fragsize;
+	
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(buf + bptr, c, x);
+		/* account for wrapping? */
+		bptr = 0;
+		len -= x;
+	}
+	memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void 
+ess_update_ptr(struct ess_state *s)
+{
+	unsigned hwptr;
+	int diff;
+
+	/* update ADC pointer */
+	if (s->dma_adc.ready) {
+		/* oh boy should this all be re-written.  everything in the current code paths think
+		that the various counters/pointers are expressed in bytes to the user but we have
+		two apus doing stereo stuff so we fix it up here.. it propogates to all the various
+		counters from here.  */
+		if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
+			hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize;
+		} else {
+			hwptr = get_dmac(s) % s->dma_adc.dmasize;
+		}
+		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+			wake_up(&s->dma_adc.wait);
+		if (!s->dma_adc.mapped) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				/* FILL ME 
+				wrindir(s, SV_CIENABLE, s->enable); */
+				stop_adc(s); 
+				/* brute force everyone back in sync, sigh */
+				s->dma_adc.count = 0;
+				s->dma_adc.swptr = 0;
+				s->dma_adc.hwptr = 0;
+				s->dma_adc.error++;
+			}
+		}
+	}
+	/* update DAC pointer */
+	if (s->dma_dac.ready) {
+		hwptr = get_dmaa(s) % s->dma_dac.dmasize; 
+		/* the apu only reports the length it has seen, not the
+			length of the memory that has been used (the WP
+			knows that) */
+		if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT))
+			hwptr<<=1;
+
+		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+/*		M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/
+		s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) {
+				wake_up(&s->dma_dac.wait);
+			}
+		} else {
+			s->dma_dac.count -= diff;
+/*			M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */
+			if (s->dma_dac.count <= 0) {
+				M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, 
+					hwptr, s->dma_dac.swptr);
+				/* FILL ME 
+				wrindir(s, SV_CIENABLE, s->enable); */
+				/* XXX how on earth can calling this with the lock held work.. */
+				stop_dac(s);
+				/* brute force everyone back in sync, sigh */
+				s->dma_dac.count = 0; 
+				s->dma_dac.swptr = hwptr; 
+				s->dma_dac.error++;
+			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+				clear_advance(s);
+				s->dma_dac.endcleared = 1;
+			}
+			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) {
+				wake_up(&s->dma_dac.wait);
+/*				printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, 
+					hwptr);*/
+			}
+		}
+	}
+}
+
+static void 
+ess_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct ess_state *s;
+        struct ess_card *c = (struct ess_card *)dev_id;
+	int i;
+	u32 event;
+
+	if ( ! (event = inb(c->iobase+0x1A)) ) return;
+
+	outw(inw(c->iobase+4)&1, c->iobase+4);
+
+/*	M_printk("maestro int: %x\n",event);*/
+	if(event&(1<<6))
+	{
+		int x;
+		enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt;
+		int volume;
+
+		/* Figure out which volume control button was pushed,
+		   based on differences from the default register
+		   values. */
+		x = inb(c->iobase+0x1c);
+		if (x&1) vol_evt = MUTE_EVT;
+		else if (((x>>1)&7) > 4) vol_evt = UP_EVT;
+		else vol_evt = DOWN_EVT;
+
+		/* Reset the volume control registers. */
+		outb(0x88, c->iobase+0x1c);
+		outb(0x88, c->iobase+0x1d);
+		outb(0x88, c->iobase+0x1e);
+		outb(0x88, c->iobase+0x1f);
+
+		/* Deal with the button press in a hammer-handed
+		   manner by adjusting the master mixer volume. */
+		volume = c->mix.mixer_state[0] & 0xff;
+		if (vol_evt == UP_EVT) {
+			volume += 10;
+			if (volume > 100)
+				volume = 100;
+		}
+		else if (vol_evt == DOWN_EVT) {
+			volume -= 10;
+			if (volume < 0)
+				volume = 0;
+		} else {
+			/* vol_evt == MUTE_EVT */
+			if (volume == 0)
+				volume = c->dock_mute_vol;
+			else {
+				c->dock_mute_vol = volume;
+				volume = 0;
+			}
+		}
+		set_mixer (c, 0, (volume << 8) | volume);
+	}
+
+	/* Ack all the interrupts. */
+	outb(0xFF, c->iobase+0x1A);
+		
+	/*
+	 *	Update the pointers for all APU's we are running.
+	 */
+	for(i=0;i<NR_DSPS;i++)
+	{
+		s=&c->channels[i];
+		if(s->dev_audio == -1)
+			break;
+		spin_lock(&s->lock);
+		ess_update_ptr(s);
+		spin_unlock(&s->lock);
+	}
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n";
+
+#define VALIDATE_MAGIC(FOO,MAG)                         \
+({                                                \
+	if (!(FOO) || (FOO)->magic != MAG) { \
+		printk(invalid_magic,__FUNCTION__);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC)
+#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC)
+
+static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) 
+{
+	unsigned int left,right;
+	/* cleanse input a little */
+	right = ((val >> 8)  & 0xff) ;
+	left = (val  & 0xff) ;
+
+	if(right > 100) right = 100;
+	if(left > 100) left = 100;
+
+	card->mix.mixer_state[mixer]=(right << 8) | left;
+	card->mix.write_mixer(card,mixer,left,right);
+}
+
+static void
+mixer_push_state(struct ess_card *card)
+{
+	int i;
+	for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) {
+		if( ! supported_mixer(card,i)) continue;
+
+		set_mixer(card,i,card->mix.mixer_state[i]);
+	}
+}
+
+static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg)
+{
+	int i, val=0;
+       unsigned long flags;
+
+	VALIDATE_CARD(card);
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, card_names[card->card_type], sizeof(info.id));
+		strncpy(info.name,card_names[card->card_type],sizeof(info.name));
+		info.modify_counter = card->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, card_names[card->card_type], sizeof(info.id));
+		strncpy(info.name,card_names[card->card_type],sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+
+        if (_IOC_DIR(cmd) == _IOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* give them the current record source */
+
+			if(!card->mix.recmask_io) {
+				val = 0;
+			} else {
+                               spin_lock_irqsave(&card->lock, flags);
+				val = card->mix.recmask_io(card,1,0);
+                               spin_unlock_irqrestore(&card->lock, flags);
+			}
+			break;
+			
+                case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
+			val = card->mix.supported_mixers;
+			break;
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			val = card->mix.record_sources;
+			break;
+			
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			val = card->mix.stereo_mixers;
+			break;
+			
+                case SOUND_MIXER_CAPS:
+			val = SOUND_CAP_EXCL_INPUT;
+			break;
+
+		default: /* read a specific mixer */
+			i = _IOC_NR(cmd);
+
+			if ( ! supported_mixer(card,i)) 
+				return -EINVAL;
+
+			/* do we ever want to touch the hardware? */
+/*                     spin_lock_irqsave(&card->lock, flags);
+			val = card->mix.read_mixer(card,i);
+                       spin_unlock_irqrestore(&card->lock, flags);*/
+
+			val = card->mix.mixer_state[i];
+/*			M_printk("returned 0x%x for mixer %d\n",val,i);*/
+
+			break;
+		}
+		return put_user(val,(int *)arg);
+	}
+	
+        if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ))
+		return -EINVAL;
+	
+	card->mix.modcnt++;
+
+	if (get_user(val, (int *)arg))
+		return -EFAULT;
+
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+
+		if (!card->mix.recmask_io) return -EINVAL;
+		if(!val) return 0;
+		if(! (val &= card->mix.record_sources)) return -EINVAL;
+
+               spin_lock_irqsave(&card->lock, flags);
+		card->mix.recmask_io(card,0,val);
+               spin_unlock_irqrestore(&card->lock, flags);
+		return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+
+		if ( ! supported_mixer(card,i)) 
+			return -EINVAL;
+
+               spin_lock_irqsave(&card->lock, flags);
+		set_mixer(card,i,val);
+               spin_unlock_irqrestore(&card->lock, flags);
+
+		return 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+static int ess_open_mixdev(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct ess_card *card = NULL;
+	struct pci_dev *pdev;
+	struct pci_driver *drvr;
+
+	pci_for_each_dev(pdev) {
+		drvr = pci_dev_driver (pdev);
+		if (drvr == &maestro_pci_driver) {
+			card = (struct ess_card*)pci_get_drvdata (pdev);
+			if (!card)
+				continue;
+			if (card->dev_mixer == minor)
+				break;
+		}
+	}
+	if (!card)
+		return -ENODEV;
+	file->private_data = card;
+	return 0;
+}
+
+static int ess_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct ess_card *card = (struct ess_card *)file->private_data;
+
+	VALIDATE_CARD(card);
+	
+	return 0;
+}
+
+static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ess_card *card = (struct ess_card *)file->private_data;
+
+	VALIDATE_CARD(card);
+
+	return mixer_ioctl(card, cmd, arg);
+}
+
+static /*const*/ struct file_operations ess_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:         no_llseek,
+	ioctl:          ess_ioctl_mixdev,
+	open:           ess_open_mixdev,
+	release:        ess_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct ess_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait,current);
+	unsigned long flags;
+	int count;
+	signed long tmo;
+
+	if (s->dma_dac.mapped || !s->dma_dac.ready)
+		return 0;
+	current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac.wait, &wait);
+        for (;;) {
+		/* XXX uhm.. questionable locking*/
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac.wait, &wait);
+			current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / s->ratedac;
+		tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK];
+		/* XXX this is just broken.  someone is waking us up alot, or schedule_timeout is broken.
+			or something.  who cares. - zach */
+		if (!schedule_timeout(tmo ? tmo : 1) && tmo)
+			M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies);
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+	current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* Zach sez: "god this is gross.." */
+static int 
+comb_stereo(unsigned char *real_buffer,unsigned char  *tmp_buffer, int offset, 
+	int count, int bufsize)
+{  
+	/* No such thing as stereo recording, so we
+	use dual input mixers.  which means we have to 
+	combine mono to stereo buffer.  yuck. 
+
+	but we don't have to be able to work a byte at a time..*/
+
+	unsigned char *so,*left,*right;
+	int i;
+
+	so = tmp_buffer;
+	left = real_buffer + offset;
+	right = real_buffer + bufsize/2 + offset;
+
+/*	M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/
+
+	for(i=count/4; i ; i--) {
+		(*(so+2)) = *(right++);
+		(*(so+3)) = *(right++);
+		(*so) = *(left++);
+		(*(so+1)) = *(left++);
+		so+=4;
+	}
+
+	return 0;
+}
+
+/* in this loop, dma_adc.count signifies the amount of data thats waiting
+	to be copied to the user's buffer.  it is filled by the interrupt
+	handler and drained by this loop. */
+static ssize_t 
+ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+	unsigned char *combbuf = NULL;
+	
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	if(!(combbuf = kmalloc(count,GFP_KERNEL)))
+		return -ENOMEM;
+	ret = 0;
+
+	calc_bob_rate(s);
+
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		/* remember, all these things are expressed in bytes to be
+			sent to the user.. hence the evil / 2 down below */
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+
+		if ( cnt > 0 ) cnt &= ~3;
+
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK) 
+			{
+				ret = ret ? ret : -EAGAIN;
+				goto rec_return_free;
+			}
+			if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
+				if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, 
+				       s->dma_adc.hwptr, s->dma_adc.swptr);
+				stop_adc(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
+				/* program enhanced mode registers */
+				/* FILL ME */
+/*				wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8);
+				wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */
+				s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current)) 
+			{
+				ret = ret ? ret : -ERESTARTSYS;
+				goto rec_return_free;
+			}
+			continue;
+		}
+	
+		if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
+			/* swptr/2 so that we know the real offset in each apu's buffer */
+			comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize);
+			if (copy_to_user(buffer, combbuf, cnt)) {
+				ret = ret ? ret : -EFAULT;
+				goto rec_return_free;
+			}
+		} else  {
+			if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+				ret = ret ? ret : -EFAULT;
+				goto rec_return_free;
+			}
+		}
+
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(s);
+	}
+
+rec_return_free:
+	if(combbuf) kfree(combbuf);
+	return ret;
+}
+
+static ssize_t 
+ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+	
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+
+	calc_bob_rate(s);
+
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+
+		cnt = s->dma_dac.dmasize-swptr;
+
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+
+		if (cnt <= 0) {
+			start_dac(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if(!ret) ret = -EAGAIN;
+				goto return_free;
+			}
+			if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
+				if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, 
+				       s->dma_dac.hwptr, s->dma_dac.swptr);
+				stop_dac(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
+				/* program enhanced mode registers */
+/*				wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8);
+				wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */
+				/* FILL ME */
+				s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				goto return_free;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+			if (!ret) ret = -EFAULT;
+			goto return_free;
+		}
+/*		printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/
+
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(s);
+	}
+return_free:
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+
+/* In 0.14 prog_dmabuf always returns success anyway ... */
+	if (file->f_mode & FMODE_WRITE) {
+		if (!s->dma_dac.ready && prog_dmabuf(s, 0)) 
+			return 0;
+	}
+	if (file->f_mode & FMODE_READ) {
+	  	if (!s->dma_adc.ready && prog_dmabuf(s, 1))
+			return 0;
+	}
+
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac.wait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	ess_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int ess_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	struct dmabuf *db;
+	int ret = -EINVAL;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(s, 1)) != 0)
+			goto out;
+		db = &s->dma_dac;
+	} else 
+#if 0
+	/* if we can have the wp/wc do the combining
+		we can turn this back on.  */
+	      if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			goto out;
+		db = &s->dma_adc;
+	} else  
+#endif
+		goto out;
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		goto out;
+	db->mapped = 1;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+	unsigned char fmtm, fmtd;
+
+/*	printk("maestro: ess_ioctl: cmd %d\n", cmd);*/
+	
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, file->f_flags & O_NONBLOCK);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		/* XXX fix */
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate(s, val);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				set_dac_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		fmtd = 0;
+		fmtm = ~0;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			if (val)
+				fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+			else
+				fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			if (val)
+				fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+			else
+				fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+		}
+		set_fmt(s, fmtm, fmtd);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+					   : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+	/* fixed at 16bit for now */
+				fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT;
+#if 0
+				if (val == AFMT_S16_LE)
+					fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT);
+#endif
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+				else
+					fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+ 		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? 
+			(ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+			: (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 
+				AFMT_S16_LE : 
+				AFMT_U8, 
+			(int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING))
+			val |= PCM_ENABLE_INPUT;
+		if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+					return ret;
+				start_dac(s);
+			} else
+				stop_dac(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+			return ret;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_update_ptr(s);
+		abinfo.fragsize = s->dma_dac.fragsize;
+                abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+                abinfo.fragstotal = s->dma_dac.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+			return ret;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+			return ret;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_update_ptr(s);
+                val = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+			return ret;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+			return ret;
+		spin_lock_irqsave(&s->lock, flags);
+		ess_update_ptr(s);
+                cinfo.bytes = s->dma_dac.total_bytes;
+                cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+                cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(s, 0)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf(s, 1)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		M_printk("maestro: SETFRAGMENT: %0x\n",val);
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.ossfragshift = val & 0xffff;
+			s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac.ossfragshift < 4)
+				s->dma_dac.ossfragshift = 4;
+			if (s->dma_dac.ossfragshift > 15)
+				s->dma_dac.ossfragshift = 15;
+			if (s->dma_dac.ossmaxfrags < 4)
+				s->dma_dac.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+					   : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+					   : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return -EINVAL;
+}
+
+static void
+set_base_registers(struct ess_state *s,void *vaddr)
+{
+	unsigned long packed_phys = virt_to_bus(vaddr)>>12;
+	wave_set_register(s, 0x01FC , packed_phys);
+	wave_set_register(s, 0x01FD , packed_phys);
+	wave_set_register(s, 0x01FE , packed_phys);
+	wave_set_register(s, 0x01FF , packed_phys);
+}
+
+/* 
+ * this guy makes sure we're in the right power
+ * state for what we want to be doing 
+ */
+static void maestro_power(struct ess_card *card, int tostate)
+{
+	u16 active_mask = acpi_state_mask[tostate];
+	u8 state;
+
+	if(!use_pm) return;
+
+	pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state);
+	state&=3;
+
+	/* make sure we're in the right state */
+	if(state != tostate) {
+		M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n",
+			card->pcidev->bus->number, 
+			PCI_SLOT(card->pcidev->devfn),
+			PCI_FUNC(card->pcidev->devfn),
+			state,tostate);
+		pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate);
+	}
+
+	/* and make sure the units we care about are on 
+		XXX we might want to do this before state flipping? */
+	pci_write_config_word(card->pcidev, 0x54, ~ active_mask);
+	pci_write_config_word(card->pcidev, 0x56, ~ active_mask);
+}
+
+/* we allocate a large power of two for all our memory.
+	this is cut up into (not to scale :):
+	|silly fifo word	| 512byte mixbuf per adc	| dac/adc * channels |
+*/
+static int
+allocate_buffers(struct ess_state *s)
+{
+	void *rawbuf=NULL;
+	int order,i;
+	struct page *page, *pend;
+
+	/* alloc as big a chunk as we can */
+	for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--)
+		if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
+			break;
+
+	if (!rawbuf)
+		return 1;
+
+	M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<<order,order, rawbuf);
+
+	if ((virt_to_bus(rawbuf) + (PAGE_SIZE << order) - 1) & ~((1<<28)-1))  {
+		printk(KERN_ERR "maestro: DMA buffer beyond 256MB! busaddr 0x%lx  size %ld\n",
+			virt_to_bus(rawbuf), PAGE_SIZE << order);
+		kfree(rawbuf);
+		return 1;
+	}
+
+	s->card->dmapages = rawbuf;
+	s->card->dmaorder = order;
+
+	for(i=0;i<NR_DSPS;i++) {
+		struct ess_state *ess = &s->card->channels[i];
+
+		if(ess->dev_audio == -1)
+			continue;
+
+		ess->dma_dac.ready = s->dma_dac.mapped = 0;
+		ess->dma_adc.ready = s->dma_adc.mapped = 0;
+		ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1;
+
+		/* offset dac and adc buffers starting half way through and then at each [da][ad]c's
+			order's intervals.. */
+		ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 )));
+		ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder);
+		/* offset mixbuf by a mixbuf so that the lame status fifo can
+			happily scribble away.. */ 
+		ess->mixbuf = rawbuf + (512 * (i+1));
+
+		M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf,
+			ess->dma_adc.rawbuf, ess->mixbuf);
+
+	}
+
+	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+	pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
+	for (page = virt_to_page(rawbuf); page <= pend; page++)
+		mem_map_reserve(page);
+
+	return 0;
+} 
+static void
+free_buffers(struct ess_state *s)
+{
+	struct page *page, *pend;
+
+	s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL;
+	s->dma_dac.mapped = s->dma_adc.mapped = 0;
+	s->dma_dac.ready = s->dma_adc.ready = 0;
+
+	M_printk("maestro: freeing %p\n",s->card->dmapages);
+	/* undo marking the pages as reserved */
+
+	pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1);
+	for (page = virt_to_page(s->card->dmapages); page <= pend; page++)
+		mem_map_unreserve(page);
+
+	free_pages((unsigned long)s->card->dmapages,s->card->dmaorder);
+	s->card->dmapages = NULL;
+}
+
+static int 
+ess_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = minor(inode->i_rdev);
+	struct ess_state *s = NULL;
+	unsigned char fmtm = ~0, fmts = 0;
+	struct pci_dev *pdev;
+	/*
+	 *	Scan the cards and find the channel. We only
+	 *	do this at open time so it is ok
+	 */
+
+	pci_for_each_dev(pdev) {
+		struct ess_card *c;
+		struct pci_driver *drvr;
+
+		drvr = pci_dev_driver (pdev);
+		if (drvr == &maestro_pci_driver) {
+			int i;
+			struct ess_state *sp;
+
+			c = (struct ess_card*)pci_get_drvdata (pdev);
+			if (!c)
+				continue;
+			for(i=0;i<NR_DSPS;i++)
+			{
+				sp=&c->channels[i];
+				if(sp->dev_audio < 0)
+					continue;
+				if((sp->dev_audio ^ minor) & ~0xf)
+					continue;
+				s=sp;
+			}
+		}
+	}
+	if (!s)
+		return -ENODEV;
+
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EWOULDBLOCK;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+
+	/* under semaphore.. */
+	if ((s->card->dmapages==NULL) && allocate_buffers(s)) {
+		up(&s->open_sem);
+		return -ENOMEM;
+	}
+
+	/* we're covered by the open_sem */
+	if( ! s->card->dsps_open )  {
+		maestro_power(s->card,ACPI_D0);
+		start_bob(s);
+	}
+	s->card->dsps_open++;
+	M_printk("maestro: open, %d bobs now\n",s->card->dsps_open);
+
+	/* ok, lets write WC base regs now that we've 
+		powered up the chip */
+	M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages),
+		((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12);
+	set_base_registers(s,s->card->dmapages);
+
+	if (file->f_mode & FMODE_READ) {
+/*
+		fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */
+
+		fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT);
+		fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT;
+
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+		set_dac_rate(s, 8000);
+	}
+	set_fmt(s, fmtm, fmts);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+
+	up(&s->open_sem);
+	return 0;
+}
+
+static int 
+ess_release(struct inode *inode, struct file *file)
+{
+	struct ess_state *s = (struct ess_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+	}
+		
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	/* we're covered by the open_sem */
+	M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1);
+	if( --s->card->dsps_open <= 0) {
+		s->card->dsps_open = 0;
+		stop_bob(s);
+		free_buffers(s);
+		maestro_power(s->card,ACPI_D2);
+	}
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static struct file_operations ess_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:         no_llseek,
+	read:           ess_read,
+	write:          ess_write,
+	poll:           ess_poll,
+	ioctl:          ess_ioctl,
+	mmap:           ess_mmap,
+	open:           ess_open,
+	release:        ess_release,
+};
+
+static int
+maestro_config(struct ess_card *card) 
+{
+	struct pci_dev *pcidev = card->pcidev;
+	struct ess_state *ess = &card->channels[0];
+	int apu,iobase  = card->iobase;
+	u16 w;
+	u32 n;
+
+	/* We used to muck around with pci config space that
+	 * we had no business messing with.  We don't know enough
+	 * about the machine to know which DMA mode is appropriate, 
+	 * etc.  We were guessing wrong on some machines and making
+	 * them unhappy.  We now trust in the BIOS to do things right,
+	 * which almost certainly means a new host of problems will
+	 * arise with broken BIOS implementations.  screw 'em. 
+	 * We're already intolerant of machines that don't assign
+	 * IRQs.
+	 */
+	
+	/* do config work at full power */
+	maestro_power(card,ACPI_D0);
+	 
+	pci_read_config_word(pcidev, 0x50, &w);
+
+	w&=~(1<<5);			/* Don't swap left/right (undoc)*/
+	
+	pci_write_config_word(pcidev, 0x50, w);
+	
+	pci_read_config_word(pcidev, 0x52, &w);
+	w&=~(1<<15);		/* Turn off internal clock multiplier */
+	/* XXX how do we know which to use? */
+	w&=~(1<<14);		/* External clock */
+	
+	w|= (1<<7);		/* Hardware volume control on */
+	w|= (1<<6);		/* Debounce off: easier to push the HWV buttons. */
+	w&=~(1<<5);		/* GPIO 4:5 */
+	w|= (1<<4);             /* Disconnect from the CHI.  Enabling this made a dell 7500 work. */
+	w&=~(1<<2);		/* MIDI fix off (undoc) */
+	w&=~(1<<1);		/* reserved, always write 0 */
+	pci_write_config_word(pcidev, 0x52, w);
+	
+	/*
+	 *	Legacy mode
+	 */
+
+	pci_read_config_word(pcidev, 0x40, &w);
+	w|=(1<<15);	/* legacy decode off */
+	w&=~(1<<14);	/* Disable SIRQ */
+	w&=~(0x1f);	/* disable mpu irq/io, game port, fm, SB */
+	 
+	pci_write_config_word(pcidev, 0x40, w);
+
+	/* Set up 978 docking control chip. */
+	pci_read_config_word(pcidev, 0x58, &w);
+	w|=1<<2;	/* Enable 978. */
+	w|=1<<3;	/* Turn on 978 hardware volume control. */
+	w&=~(1<<11);	/* Turn on 978 mixer volume control. */
+	pci_write_config_word(pcidev, 0x58, w);
+	
+	sound_reset(iobase);
+
+	/*
+	 *	Ring Bus Setup
+	 */
+
+	/* setup usual 0x34 stuff.. 0x36 may be chip specific */
+        outw(0xC090, iobase+0x34); /* direct sound, stereo */
+        udelay(20);
+        outw(0x3000, iobase+0x36); /* direct sound, stereo */
+        udelay(20);
+
+
+	/*
+	 *	Reset the CODEC
+	 */
+	 
+	maestro_ac97_reset(iobase,pcidev);
+	
+	/*
+	 *	Ring Bus Setup
+	 */
+	 	 
+	n=inl(iobase+0x34);
+	n&=~0xF000;
+	n|=12<<12;		/* Direct Sound, Stereo */
+	outl(n, iobase+0x34);
+
+	n=inl(iobase+0x34);
+	n&=~0x0F00;		/* Modem off */
+	outl(n, iobase+0x34);
+
+	n=inl(iobase+0x34);
+	n&=~0x00F0;
+	n|=9<<4;		/* DAC, Stereo */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n&=~0x000F;		/* ASSP off */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n|=(1<<29);		/* Enable ring bus */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n|=(1<<28);		/* Enable serial bus */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n&=~0x00F00000;		/* MIC off */
+	outl(n, iobase+0x34);
+	
+	n=inl(iobase+0x34);
+	n&=~0x000F0000;		/* I2S off */
+	outl(n, iobase+0x34);
+	
+
+	w=inw(iobase+0x18);
+	w&=~(1<<7);		/* ClkRun off */
+	outw(w, iobase+0x18);
+
+	w=inw(iobase+0x18);
+	w&=~(1<<6);		/* Hardware volume control interrupt off... for now. */
+	outw(w, iobase+0x18);
+	
+	w=inw(iobase+0x18);
+	w&=~(1<<4);		/* ASSP irq off */
+	outw(w, iobase+0x18);
+	
+	w=inw(iobase+0x18);
+	w&=~(1<<3);		/* ISDN irq off */
+	outw(w, iobase+0x18);
+	
+	w=inw(iobase+0x18);
+	w|=(1<<2);		/* Direct Sound IRQ on */
+	outw(w, iobase+0x18);
+
+	w=inw(iobase+0x18);
+	w&=~(1<<1);		/* MPU401 IRQ off */
+	outw(w, iobase+0x18);
+
+	w=inw(iobase+0x18);
+	w|=(1<<0);		/* SB IRQ on */
+	outw(w, iobase+0x18);
+
+	/* Set hardware volume control registers to midpoints.
+	   We can tell which button was pushed based on how they change. */
+	outb(0x88, iobase+0x1c);
+	outb(0x88, iobase+0x1d);
+	outb(0x88, iobase+0x1e);
+	outb(0x88, iobase+0x1f);
+
+	/* it appears some maestros (dell 7500) only work if these are set,
+		regardless of wether we use the assp or not. */
+
+	outb(0, iobase+0xA4); 
+	outb(3, iobase+0xA2); 
+	outb(0, iobase+0xA6);
+	
+	for(apu=0;apu<16;apu++)
+	{
+		/* Write 0 into the buffer area 0x1E0->1EF */
+		outw(0x01E0+apu, 0x10+iobase);
+		outw(0x0000, 0x12+iobase);
+	
+		/*
+		 * The 1.10 test program seem to write 0 into the buffer area
+		 * 0x1D0-0x1DF too.
+		 */
+		outw(0x01D0+apu, 0x10+iobase);
+		outw(0x0000, 0x12+iobase);
+	}
+
+#if 1
+	wave_set_register(ess, IDR7_WAVE_ROMRAM, 
+		(wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00));
+	wave_set_register(ess, IDR7_WAVE_ROMRAM,
+		wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100);
+	wave_set_register(ess, IDR7_WAVE_ROMRAM,
+		wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200);
+	wave_set_register(ess, IDR7_WAVE_ROMRAM,
+		wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400);
+#else		
+	maestro_write(ess, IDR7_WAVE_ROMRAM, 
+		(maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00));
+	maestro_write(ess, IDR7_WAVE_ROMRAM,
+		maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100);
+	maestro_write(ess, IDR7_WAVE_ROMRAM,
+		maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200);
+	maestro_write(ess, IDR7_WAVE_ROMRAM,
+		maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400);
+#endif
+	
+	maestro_write(ess, IDR2_CRAM_DATA, 0x0000);
+	maestro_write(ess, 0x08, 0xB004);
+	/* Now back to the DirectSound stuff */
+	maestro_write(ess, 0x09, 0x001B);
+	maestro_write(ess, 0x0A, 0x8000);
+	maestro_write(ess, 0x0B, 0x3F37);
+	maestro_write(ess, 0x0C, 0x0098);
+	
+	/* parallel out ?? */
+	maestro_write(ess, 0x0C, 
+		(maestro_read(ess, 0x0C)&~0xF000)|0x8000); 
+	/* parallel in, has something to do with recording :) */
+	maestro_write(ess, 0x0C, 
+		(maestro_read(ess, 0x0C)&~0x0F00)|0x0500);
+
+	maestro_write(ess, 0x0D, 0x7632);
+			
+	/* Wave cache control on - test off, sg off, 
+		enable, enable extra chans 1Mb */
+
+	outw(inw(0x14+iobase)|(1<<8),0x14+iobase);
+	outw(inw(0x14+iobase)&0xFE03,0x14+iobase);
+	outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase);
+	outw(inw(0x14+iobase)|(1<<7),0x14+iobase);
+
+	outw(0xA1A0, 0x14+iobase);      /* 0300 ? */
+
+	/* Now clear the APU control ram */	
+	for(apu=0;apu<NR_APUS;apu++)
+	{
+		for(w=0;w<NR_APU_REGS;w++)
+			apu_set_register(ess, apu|ESS_CHAN_HARD, w, 0);
+		
+	}
+
+	return 0;
+	
+}
+
+/* this guy tries to find the pci power management
+ * register bank.  this should really be in core
+ * code somewhere.  1 on success. */
+int
+parse_power(struct ess_card *card, struct pci_dev *pcidev)
+{
+	u32 n;
+	u16 w;
+	u8 next;
+	int max = 64;  /* an a 8bit guy pointing to 32bit guys
+				can only express so much. */
+
+	card->power_regs = 0;
+
+	/* check to see if we have a capabilities list in
+		the config register */
+	pci_read_config_word(pcidev, PCI_STATUS, &w);
+	if(! w & PCI_STATUS_CAP_LIST) return 0;
+
+	/* walk the list, starting at the head. */
+	pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next);
+
+	while(next && max--) {
+		pci_read_config_dword(pcidev, next & ~3, &n);
+		if((n & 0xff) == PCI_CAP_ID_PM) {
+			card->power_regs = next;
+			break;
+		}
+		next = ((n>>8) & 0xff);
+	}
+
+	return card->power_regs ? 1 : 0;
+}
+
+static int __init
+maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid)
+{
+	int card_type = pdid->driver_data;
+	u32 n;
+	int iobase;
+	int i, ret;
+	struct ess_card *card;
+	struct ess_state *ess;
+	struct pm_dev *pmdev;
+	int num = 0;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
+	/* don't pick up weird modem maestros */
+	if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+		return -ENODEV;
+
+
+	if ((ret=pci_enable_device(pcidev)))
+		return ret;
+			
+	iobase = pci_resource_start(pcidev,0);
+	if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO))
+		return -ENODEV;
+
+	if(pcidev->irq == 0)
+		return -ENODEV;
+
+	/* stake our claim on the iospace */
+	if( request_region(iobase, 256, card_names[card_type]) == NULL )
+	{
+		printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase);
+		return -EBUSY;
+	}
+
+	/* just to be sure */
+	pci_set_master(pcidev);
+
+	card = kmalloc(sizeof(struct ess_card), GFP_KERNEL);
+	if(card == NULL)
+	{
+		printk(KERN_WARNING "maestro: out of memory\n");
+		release_region(iobase, 256);
+		return -ENOMEM;
+	}
+	
+	memset(card, 0, sizeof(*card));
+	card->pcidev = pcidev;
+
+	pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev),
+			maestro_pm_callback);
+	if (pmdev)
+		pmdev->data = card;
+
+	card->iobase = iobase;
+	card->card_type = card_type;
+	card->irq = pcidev->irq;
+	card->magic = ESS_CARD_MAGIC;
+	spin_lock_init(&card->lock);
+	init_waitqueue_head(&card->suspend_queue);
+
+	card->dock_mute_vol = 50;
+	
+	/* init our groups of 6 apus */
+	for(i=0;i<NR_DSPS;i++)
+	{
+		struct ess_state *s=&card->channels[i];
+
+		s->index = i;
+
+		s->card = card;
+		init_waitqueue_head(&s->dma_adc.wait);
+		init_waitqueue_head(&s->dma_dac.wait);
+		init_waitqueue_head(&s->open_wait);
+		spin_lock_init(&s->lock);
+		init_MUTEX(&s->open_sem);
+		s->magic = ESS_STATE_MAGIC;
+		
+		s->apu[0] = 6*i;
+		s->apu[1] = (6*i)+1;
+		s->apu[2] = (6*i)+2;
+		s->apu[3] = (6*i)+3;
+		s->apu[4] = (6*i)+4;
+		s->apu[5] = (6*i)+5;
+		
+		if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
+			printk("maestro: BOTCH!\n");
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
+			break;
+	}
+	
+	num = i;
+	
+	/* clear the rest if we ran out of slots to register */
+	for(;i<NR_DSPS;i++)
+	{
+		struct ess_state *s=&card->channels[i];
+		s->dev_audio = -1;
+	}
+	
+	ess = &card->channels[0];
+
+	/*
+	 *	Ok card ready. Begin setup proper
+	 */
+
+	printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", 
+		card_names[card_type],iobase,card->irq);
+	pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n);
+	printk(KERN_INFO "maestro:  subvendor id: 0x%08x\n",n); 
+
+	/* turn off power management unless:
+	 *	- the user explicitly asks for it
+	 * 		or
+	 *		- we're not a 2e, lesser chipps seem to have problems.
+	 *		- we're not on our _very_ small whitelist.  some implemenetations
+	 *			really dont' like the pm code, others require it.
+	 *			feel free to expand this as required.
+	 */
+#define SUBSYSTEM_VENDOR(x) (x&0xffff)
+	if(	(use_pm != 1) && 
+		((card_type != TYPE_MAESTRO2E)	|| (SUBSYSTEM_VENDOR(n) != 0x1028)))
+			use_pm = 0;
+
+	if(!use_pm) 
+		printk(KERN_INFO "maestro: not attempting power management.\n");
+	else {
+		if(!parse_power(card,pcidev)) 
+			printk(KERN_INFO "maestro: no PCI power management interface found.\n");
+		else {
+			pci_read_config_dword(pcidev, card->power_regs, &n);
+			printk(KERN_INFO "maestro: PCI power management capability: 0x%x\n",n>>16);
+		}	
+	}
+
+	maestro_config(card);
+
+	if(maestro_ac97_get(card, 0x00)==0x0080) {
+		printk(KERN_ERR "maestro: my goodness!  you seem to have a pt101 codec, which is quite rare.\n"
+				"\tyou should tell someone about this.\n");
+	} else {
+		maestro_ac97_init(card);
+	}
+
+	if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) {
+		printk("maestro: couldn't register mixer!\n");
+	} else {
+		memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state));
+		mixer_push_state(card);
+	}
+	
+	if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card)))
+	{
+		printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq);
+		unregister_sound_mixer(card->dev_mixer);
+		for(i=0;i<NR_DSPS;i++)
+		{
+			struct ess_state *s = &card->channels[i];
+			if(s->dev_audio != -1)
+				unregister_sound_dsp(s->dev_audio);
+		}
+		release_region(card->iobase, 256);		
+		unregister_reboot_notifier(&maestro_nb);
+		kfree(card);
+		return ret;
+	}
+
+	/* Turn on hardware volume control interrupt.
+	   This has to come after we grab the IRQ above,
+	   or a crash will result on installation if a button has been pressed,
+	   because in that case we'll get an immediate interrupt. */
+	n = inw(iobase+0x18);
+	n|=(1<<6);
+	outw(n, iobase+0x18);
+
+	pci_set_drvdata(pcidev,card);
+	/* now go to sleep 'till something interesting happens */
+	maestro_power(card,ACPI_D2);
+
+	printk(KERN_INFO "maestro: %d channels configured.\n", num);
+	return 0;
+}
+
+static void maestro_remove(struct pci_dev *pcidev) {
+	struct ess_card *card = pci_get_drvdata(pcidev);
+	int i;
+	
+	/* XXX maybe should force stop bob, but should be all 
+		stopped by _release by now */
+	free_irq(card->irq, card);
+	unregister_sound_mixer(card->dev_mixer);
+	for(i=0;i<NR_DSPS;i++)
+	{
+		struct ess_state *ess = &card->channels[i];
+		if(ess->dev_audio != -1)
+			unregister_sound_dsp(ess->dev_audio);
+	}
+	/* Goodbye, Mr. Bond. */
+	maestro_power(card,ACPI_D3);
+ 	release_region(card->iobase, 256);
+	kfree(card);
+	pci_set_drvdata(pcidev,NULL);
+}
+
+static struct pci_device_id maestro_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2},
+	{PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E},
+	{PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO},
+	{0,}
+};
+MODULE_DEVICE_TABLE(pci, maestro_pci_tbl);
+
+static struct pci_driver maestro_pci_driver = {
+	name:"maestro",
+	id_table:maestro_pci_tbl,
+	probe:maestro_probe,
+	remove:maestro_remove,
+};
+
+int __init init_maestro(void)
+{
+	int rc;
+
+	rc = pci_module_init(&maestro_pci_driver);
+	if (rc < 0)
+		return rc;
+
+	if (register_reboot_notifier(&maestro_nb))
+		printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n");
+#ifdef MODULE
+	printk(version);
+#endif
+	if (dsps_order < 0)   {
+		dsps_order = 1;
+		printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
+	}
+	else if (dsps_order > MAX_DSP_ORDER)  {
+		dsps_order = MAX_DSP_ORDER;
+		printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
+	}
+	return 0;
+}
+
+static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+{
+	/* this notifier is called when the kernel is really shut down. */
+	M_printk("maestro: shutting down\n");
+	/* this will remove all card instances too */
+	pci_unregister_driver(&maestro_pci_driver);
+	/* XXX dunno about power management */
+	return NOTIFY_OK;
+}
+
+/* --------------------------------------------------------------------- */
+
+
+void cleanup_maestro(void) {
+	M_printk("maestro: unloading\n");
+	pci_unregister_driver(&maestro_pci_driver);
+	pm_unregister_all(maestro_pm_callback);
+	unregister_reboot_notifier(&maestro_nb);
+}
+
+/* --------------------------------------------------------------------- */
+
+void
+check_suspend(struct ess_card *card)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	if(!card->in_suspend) return;
+
+	card->in_suspend++;
+	add_wait_queue(&(card->suspend_queue), &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule();
+	remove_wait_queue(&(card->suspend_queue), &wait);
+	current->state = TASK_RUNNING;
+}
+
+static int 
+maestro_suspend(struct ess_card *card)
+{
+	unsigned long flags;
+	int i,j;
+
+	save_flags(flags); 
+	cli(); /* over-kill */
+
+	M_printk("maestro: apm in dev %p\n",card);
+
+	/* we have to read from the apu regs, need
+		to power it up */
+	maestro_power(card,ACPI_D0);
+
+	for(i=0;i<NR_DSPS;i++) {
+		struct ess_state *s = &card->channels[i];
+
+		if(s->dev_audio == -1)
+			continue;
+
+		M_printk("maestro: stopping apus for device %d\n",i);
+		stop_dac(s);
+		stop_adc(s);
+		for(j=0;j<6;j++) 
+			card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5);
+
+	}
+
+	/* get rid of interrupts? */
+	if( card->dsps_open > 0)
+		stop_bob(&card->channels[0]);
+
+	card->in_suspend++;
+
+	restore_flags(flags);
+
+	/* we trust in the bios to power down the chip on suspend.
+	 * XXX I'm also not sure that in_suspend will protect
+	 * against all reg accesses from here on out. 
+	 */
+	return 0;
+}
+static int 
+maestro_resume(struct ess_card *card)
+{
+	unsigned long flags;
+	int i;
+
+	save_flags(flags);
+	cli(); /* over-kill */
+
+	card->in_suspend = 0;
+
+	M_printk("maestro: resuming card at %p\n",card);
+
+	/* restore all our config */
+	maestro_config(card);
+	/* need to restore the base pointers.. */ 
+	if(card->dmapages) 
+		set_base_registers(&card->channels[0],card->dmapages);
+
+	mixer_push_state(card);
+
+	/* set each channels' apu control registers before
+	 * restoring audio 
+	 */
+	for(i=0;i<NR_DSPS;i++) {
+		struct ess_state *s = &card->channels[i];
+		int chan,reg;
+
+		if(s->dev_audio == -1)
+			continue;
+
+		for(chan = 0 ; chan < 6 ; chan++) {
+			wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]);
+			for(reg = 1 ; reg < NR_APU_REGS ; reg++)  
+				apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]);
+		}
+		for(chan = 0 ; chan < 6 ; chan++)  
+			apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F);
+	}
+
+	/* now we flip on the music */
+
+	if( card->dsps_open <= 0) {
+		/* this card's idle */
+		maestro_power(card,ACPI_D2);
+	} else {
+		/* ok, we're actually playing things on
+			this card */
+		maestro_power(card,ACPI_D0);
+		start_bob(&card->channels[0]);
+		for(i=0;i<NR_DSPS;i++) {
+			struct ess_state *s = &card->channels[i];
+
+			/* these use the apu_mode, and can handle
+				spurious calls */
+			start_dac(s);	
+			start_adc(s);	
+		}
+	}
+
+	restore_flags(flags);
+
+	/* all right, we think things are ready, 
+		wake up people who were using the device
+		when we suspended */
+	wake_up(&(card->suspend_queue));
+
+	return 0;
+}
+
+int 
+maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) 
+{
+	struct ess_card *card = (struct ess_card*) dev->data;
+
+	if ( ! card ) goto out;
+
+	M_printk("maestro: pm event 0x%x received for card %p\n", rqst, card);
+	
+	switch (rqst) {
+		case PM_SUSPEND: 
+			maestro_suspend(card);
+		break;
+		case PM_RESUME: 
+			maestro_resume(card);
+		break;
+		/*
+		 * we'd also like to find out about
+		 * power level changes because some biosen
+		 * do mean things to the maestro when they
+		 * change their power state.
+		 */
+        }
+out:
+	return 0;
+}
+
+module_init(init_maestro);
+module_exit(cleanup_maestro);
diff -Nru linux/sound/oss/maestro.h linux-2.4.19-pre5-mjc/sound/oss/maestro.h
--- linux/sound/oss/maestro.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/maestro.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,60 @@
+/*
+ *	Registers for the ESS PCI cards
+ */
+ 
+/*
+ *	Memory access
+ */
+ 
+#define ESS_MEM_DATA		0x00
+#define	ESS_MEM_INDEX		0x02
+
+/*
+ *	AC-97 Codec port. Delay 1uS after each write. This is used to
+ *	talk AC-97 (see intel.com). Write data then register.
+ */
+ 
+#define ESS_AC97_INDEX		0x30		/* byte wide */
+#define ESS_AC97_DATA		0x32
+
+/* 
+ *	Reading is a bit different. You write register|0x80 to ubdex
+ *	delay 1uS poll the low bit of index, when it clears read the
+ *	data value.
+ */
+
+/*
+ *	Control port. Not yet fully understood
+ *	The value 0xC090 gets loaded to it then 0x0000 and 0x2800
+ *	to the data port. Then after 4uS the value 0x300 is written
+ */
+ 
+#define RING_BUS_CTRL_L		0x34
+#define RING_BUS_CTRL_H		0x36
+
+/*
+ *	This is also used during setup. The value 0x17 is written to it
+ */
+ 
+#define ESS_SETUP_18		0x18
+
+/*
+ *	And this one gets 0x000b
+ */
+ 
+#define ESS_SETUP_A2		0xA2
+
+/*
+ *	And this 0x0000
+ */
+ 
+#define ESS_SETUP_A4		0xA4
+#define ESS_SETUP_A6		0xA6
+
+/*
+ *	Stuff to do with Harpo - the wave stuff
+ */
+ 
+#define ESS_WAVETABLE_SIZE	0x14
+#define 	ESS_WAVETABLE_2M	0xA180
+
diff -Nru linux/sound/oss/maestro3.c linux-2.4.19-pre5-mjc/sound/oss/maestro3.c
--- linux/sound/oss/maestro3.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/maestro3.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2962 @@
+/*****************************************************************************
+ *
+ *      ESS Maestro3/Allegro driver for Linux 2.4.x
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    (c) Copyright 2000 Zach Brown <zab@zabbo.net>
+ *
+ * I need to thank many people for helping make this driver happen.  
+ * As always, Eric Brombaugh was a hacking machine and killed many bugs
+ * that I was too dumb to notice.  Howard Kim at ESS provided reference boards 
+ * and as much docs as he could.  Todd and Mick at Dell tested snapshots on 
+ * an army of laptops.  msw and deviant at Red Hat also humoured me by hanging
+ * their laptops every few hours in the name of science.
+ * 
+ * Shouts go out to Mike "DJ XPCom" Ang.
+ *
+ * History
+ *  v1.22 - Feb 28 2001 - Zach Brown <zab@zabbo.net>
+ *   allocate mem at insmod/setup, rather than open
+ *   limit pci dma addresses to 28bit, thanks guys.
+ *  v1.21 - Feb 04 2001 - Zach Brown <zab@zabbo.net>
+ *   fix up really dumb notifier -> suspend oops
+ *  v1.20 - Jan 30 2001 - Zach Brown <zab@zabbo.net>
+ *   get rid of pm callback and use pci_dev suspend/resume instead
+ *   m3_probe cleanups, including pm oops think-o
+ *  v1.10 - Jan 6 2001 - Zach Brown <zab@zabbo.net>
+ *   revert to lame remap_page_range mmap() just to make it work
+ *   record mmap fixed.
+ *   fix up incredibly broken open/release resource management
+ *   duh.  fix record format setting.
+ *   add SMP locking and cleanup formatting here and there
+ *  v1.00 - Dec 16 2000 - Zach Brown <zab@zabbo.net>
+ *   port to sexy 2.4 interfaces
+ *   properly align instance allocations so recording works
+ *   clean up function namespace a little :/
+ *   update PCI IDs based on mail from ESS
+ *   arbitrarily bump version number to show its 2.4 now, 
+ *      2.2 will stay 0., oss_audio port gets 2.
+ *  v0.03 - Nov 05 2000 - Zach Brown <zab@zabbo.net>
+ *   disable recording but allow dsp to be opened read 
+ *   pull out most silly compat defines
+ *  v0.02 - Nov 04 2000 - Zach Brown <zab@zabbo.net>
+ *   changed clocking setup for m3, slowdown fixed.
+ *   codec reset is hopefully reliable now
+ *   rudimentary apm/power management makes suspend/resume work
+ *  v0.01 - Oct 31 2000 - Zach Brown <zab@zabbo.net>
+ *   first release
+ *  v0.00 - Sep 09 2000 - Zach Brown <zab@zabbo.net>
+ *   first pass derivation from maestro.c
+ *
+ * TODO
+ *  in/out allocated contiguously so fullduplex mmap will work?
+ *  no beep on init (mute)
+ *  resetup msrc data memory if freq changes?
+ *
+ *  --
+ *
+ *  Allow me to ramble a bit about the m3 architecture.  The core of the
+ *  chip is the 'assp', the custom ESS dsp that runs the show.  It has
+ *  a small amount of code and data ram.  ESS drops binary dsp code images
+ *  on our heads, but we don't get to see specs on the dsp.  
+ *
+ *  The constant piece of code on the dsp is the 'kernel'.  It also has a 
+ *  chunk of the dsp memory that is statically set aside for its control
+ *  info.  This is the KDATA defines in maestro3.h.  Part of its core
+ *  data is a list of code addresses that point to the pieces of DSP code
+ *  that it should walk through in its loop.  These other pieces of code
+ *  do the real work.  The kernel presumably jumps into each of them in turn.
+ *  These code images tend to have their own data area, and one can have
+ *  multiple data areas representing different states for each of the 'client
+ *  instance' code portions.  There is generally a list in the kernel data
+ *  that points to the data instances for a given piece of code.
+ *
+ *  We've only been given the binary image for the 'minisrc', mini sample 
+ *  rate converter.  This is rather annoying because it limits the work
+ *  we can do on the dsp, but it also greatly simplifies the job of managing
+ *  dsp data memory for the code and data for our playing streams :).  We
+ *  statically allocate the minisrc code into a region we 'know' to be free
+ *  based on the map of the binary kernel image we're loading.  We also 
+ *  statically allocate the data areas for the maximum number of pcm streams
+ *  we can be dealing with.  This max is set by the length of the static list
+ *  in the kernel data that records the number of minisrc data regions we
+ *  can have.  Thats right, all software dsp mixing with static code list
+ *  limits.  Rock.
+ *
+ *  How sound goes in and out is still a relative mystery.  It appears
+ *  that the dsp has the ability to get input and output through various
+ *  'connections'.  To do IO from or to a connection, you put the address
+ *  of the minisrc client area in the static kernel data lists for that 
+ *  input or output.  so for pcm -> dsp -> mixer, we put the minisrc data
+ *  instance in the DMA list and also in the list for the mixer.  I guess
+ *  it Just Knows which is in/out, and we give some dma control info that
+ *  helps.  There are all sorts of cool inputs/outputs that it seems we can't
+ *  use without dsp code images that know how to use them.
+ *
+ *  So at init time we preload all the memory allocation stuff and set some
+ *  system wide parameters.  When we really get a sound to play we build
+ *  up its minisrc header (stream parameters, buffer addresses, input/output
+ *  settings).  Then we throw its header on the various lists.  We also
+ *  tickle some KDATA settings that ask the assp to raise clock interrupts
+ *  and do some amount of software mixing before handing data to the ac97.
+ *
+ *  Sorry for the vague details.  Feel free to ask Eric or myself if you
+ *  happen to be trying to use this driver elsewhere.  Please accept my
+ *  apologies for the quality of the OSS support code, its passed through
+ *  too many hands now and desperately wants to be rethought.
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/spinlock.h>
+#include <linux/ac97_codec.h>
+
+ /*
+  * for crizappy mmap()
+  */
+#include <linux/wrapper.h>
+
+#include "maestro3.h"
+
+#define M_DEBUG 1
+
+#define DRIVER_VERSION      "1.22"
+#define M3_MODULE_NAME      "maestro3"
+#define PFX                 M3_MODULE_NAME ": "
+
+#define M3_STATE_MAGIC      0x734d724d
+#define M3_CARD_MAGIC       0x646e6f50
+
+#define ESS_FMT_STEREO      0x01
+#define ESS_FMT_16BIT       0x02
+#define ESS_FMT_MASK        0x03
+#define ESS_DAC_SHIFT       0   
+#define ESS_ADC_SHIFT       4
+
+#define DAC_RUNNING         1
+#define ADC_RUNNING         2
+
+#define SND_DEV_DSP16       5 
+   
+#ifdef M_DEBUG
+static int debug;
+#define DPMOD   1   /* per module load */
+#define DPSTR   2   /* per 'stream' */
+#define DPSYS   3   /* per syscall */
+#define DPCRAP  4   /* stuff the user shouldn't see unless they're really debuggin */
+#define DPINT   5   /* per interrupt, LOTS */
+#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);}
+#else
+#define DPRINTK(x)
+#endif
+
+struct m3_list {
+    int curlen;
+    u16 mem_addr;
+    int max;
+};
+
+int external_amp = 1;
+
+struct m3_state {
+    unsigned int magic;
+    struct m3_card *card;
+    unsigned char fmt, enable;
+
+    int index;
+
+    /* this locks around the oss state in the driver */
+    spinlock_t lock;
+
+    struct semaphore open_sem;
+    wait_queue_head_t open_wait;
+    mode_t open_mode;
+
+    int dev_audio;
+
+    struct assp_instance {
+        u16 code, data;
+    } dac_inst, adc_inst;
+
+    /* should be in dmabuf */
+    unsigned int rateadc, ratedac;
+
+    struct dmabuf {
+        void *rawbuf;
+        unsigned buforder;
+        unsigned numfrag;
+        unsigned fragshift;
+        unsigned hwptr, swptr;
+        unsigned total_bytes;
+        int count;
+        unsigned error; /* over/underrun */
+        wait_queue_head_t wait;
+        /* redundant, but makes calculations easier */
+        unsigned fragsize;
+        unsigned dmasize;
+        unsigned fragsamples;
+        /* OSS stuff */
+        unsigned mapped:1;
+        unsigned ready:1;    
+        unsigned endcleared:1;
+        unsigned ossfragshift;
+        int ossmaxfrags;
+        unsigned subdivision;
+        /* new in m3 */
+        int mixer_index, dma_index, msrc_index, adc1_index;
+        int in_lists;
+        /* 2.4.. */
+        dma_addr_t handle;
+
+    } dma_dac, dma_adc;
+};
+    
+struct m3_card {
+    unsigned int magic;
+
+    struct m3_card *next;
+
+    struct ac97_codec *ac97;
+    spinlock_t ac97_lock;
+
+    int card_type;
+
+#define NR_DSPS 1
+#define MAX_DSPS NR_DSPS
+    struct m3_state channels[MAX_DSPS];
+
+    /* this locks around the physical registers on the card */
+    spinlock_t lock;
+
+    /* hardware resources */
+    struct pci_dev *pcidev;
+    u32 iobase;
+    u32 irq;
+
+    int dacs_active;
+
+    int timer_users;
+
+    struct m3_list  msrc_list,
+                    mixer_list,
+                    adc1_list,
+                    dma_list;
+
+    /* for storing reset state..*/
+    u8 reset_state;
+
+    u16 *suspend_mem;
+    int in_suspend;
+    wait_queue_head_t suspend_queue;
+};
+
+/*
+ * an arbitrary volume we set the internal
+ * volume settings to so that the ac97 volume
+ * range is a little less insane.  0x7fff is 
+ * max.
+ */
+#define ARB_VOLUME ( 0x6800 )
+
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+enum {
+    ESS_ALLEGRO,
+    ESS_MAESTRO3,
+    /*
+     * a maestro3 with 'hardware strapping', only
+     * found inside ESS?
+     */
+    ESS_MAESTRO3HW,
+};
+
+static char *card_names[] = {
+    [ESS_ALLEGRO] = "Allegro",
+    [ESS_MAESTRO3] = "Maestro3(i)",
+    [ESS_MAESTRO3HW] = "Maestro3(i)hw"
+};
+
+#ifndef PCI_VENDOR_ESS
+#define PCI_VENDOR_ESS      0x125D
+#endif
+
+#define M3_DEVICE(DEV, TYPE)                \
+{                                           \
+vendor: PCI_VENDOR_ESS,                     \
+device: DEV,                                \
+subvendor: PCI_ANY_ID,                      \
+subdevice: PCI_ANY_ID,                      \
+class:  PCI_CLASS_MULTIMEDIA_AUDIO << 8,    \
+class_mask: 0xffff << 8,                    \
+driver_data: TYPE,                          \
+}
+
+static struct pci_device_id m3_id_table[] __initdata = {
+    M3_DEVICE(0x1988, ESS_ALLEGRO),
+    M3_DEVICE(0x1998, ESS_MAESTRO3),
+    M3_DEVICE(0x199a, ESS_MAESTRO3HW),
+    {0,}
+};
+
+MODULE_DEVICE_TABLE (pci, m3_id_table);
+
+/*
+ * reports seem to indicate that the m3 is limited
+ * to 28bit bus addresses.  aaaargggh...
+ */
+#define M3_PCI_DMA_MASK 0x0fffffff
+
+static unsigned 
+ld2(unsigned int x)
+{
+    unsigned r = 0;
+    
+    if (x >= 0x10000) {
+        x >>= 16;
+        r += 16;
+    }
+    if (x >= 0x100) {
+        x >>= 8;
+        r += 8;
+    }
+    if (x >= 0x10) {
+        x >>= 4;
+        r += 4;
+    }
+    if (x >= 4) {
+        x >>= 2;
+        r += 2;
+    }
+    if (x >= 2)
+        r++;
+    return r;
+}
+
+static struct m3_card *devs;
+
+/*
+ * I'm not very good at laying out functions in a file :)
+ */
+static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf);
+static int m3_suspend(struct pci_dev *pci_dev, u32 state);
+static void check_suspend(struct m3_card *card);
+
+struct notifier_block m3_reboot_nb = {m3_notifier, NULL, 0};
+
+static void m3_outw(struct m3_card *card,
+        u16 value, unsigned long reg)
+{
+    check_suspend(card);
+    outw(value, card->iobase + reg);
+}
+
+static u16 m3_inw(struct m3_card *card, unsigned long reg)
+{
+    check_suspend(card);
+    return inw(card->iobase + reg);
+}
+static void m3_outb(struct m3_card *card, 
+        u8 value, unsigned long reg)
+{
+    check_suspend(card);
+    outb(value, card->iobase + reg);
+}
+static u8 m3_inb(struct m3_card *card, unsigned long reg)
+{
+    check_suspend(card);
+    return inb(card->iobase + reg);
+}
+
+/*
+ * access 16bit words to the code or data regions of the dsp's memory.
+ * index addresses 16bit words.
+ */
+static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index)
+{
+    m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+    m3_outw(card, index, DSP_PORT_MEMORY_INDEX);
+    return m3_inw(card, DSP_PORT_MEMORY_DATA);
+}
+static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index)
+{
+    unsigned long flags;
+    u16 ret;
+
+    spin_lock_irqsave(&(card->lock), flags);
+    ret = __m3_assp_read(card, region, index);
+    spin_unlock_irqrestore(&(card->lock), flags);
+
+    return ret;
+}
+
+static void __m3_assp_write(struct m3_card *card, 
+        u16 region, u16 index, u16 data)
+{
+    m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+    m3_outw(card, index, DSP_PORT_MEMORY_INDEX);
+    m3_outw(card, data, DSP_PORT_MEMORY_DATA);
+}
+static void m3_assp_write(struct m3_card *card, 
+        u16 region, u16 index, u16 data)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&(card->lock), flags);
+    __m3_assp_write(card, region, index, data);
+    spin_unlock_irqrestore(&(card->lock), flags);
+}
+
+static void m3_assp_halt(struct m3_card *card)
+{
+    card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
+    mdelay(10);
+    m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+static void m3_assp_continue(struct m3_card *card)
+{
+    m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+/*
+ * This makes me sad. the maestro3 has lists
+ * internally that must be packed.. 0 terminates,
+ * apparently, or maybe all unused entries have
+ * to be 0, the lists have static lengths set
+ * by the binary code images.
+ */
+
+static int m3_add_list(struct m3_card *card,
+        struct m3_list *list, u16 val)
+{
+    DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n",
+            val, list, list->curlen);
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+            list->mem_addr + list->curlen,
+            val);
+
+    return list->curlen++;
+
+}
+
+static void m3_remove_list(struct m3_card *card,
+        struct m3_list *list, int index)
+{
+    u16  val;
+    int lastindex = list->curlen - 1;
+
+    DPRINTK(DPSTR, "removing ind %d from list 0x%p\n",
+            index, list);
+
+    if(index != lastindex) {
+        val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                list->mem_addr + lastindex);
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+                list->mem_addr + index,
+                val);
+    }
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+            list->mem_addr + lastindex,
+            0);
+
+    list->curlen--;
+}
+
+static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data)
+{
+    int tmp;
+
+    s->fmt = (s->fmt & mask) | data;
+
+    tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
+
+    /* write to 'mono' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, 
+            (tmp & ESS_FMT_STEREO) ? 0 : 1);
+    /* write to '8bit' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, 
+            (tmp & ESS_FMT_16BIT) ? 0 : 1);
+
+    tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK;
+
+    /* write to 'mono' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, 
+            (tmp & ESS_FMT_STEREO) ? 0 : 1);
+    /* write to '8bit' word */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, 
+            (tmp & ESS_FMT_16BIT) ? 0 : 1);
+}
+
+static void set_dac_rate(struct m3_state *s, unsigned int rate)
+{
+    u32 freq;
+
+    if (rate > 48000)
+        rate = 48000;
+    if (rate < 8000)
+        rate = 8000;
+
+    s->ratedac = rate;
+
+    freq = ((rate << 15) + 24000 ) / 48000;
+    if(freq) 
+        freq--;
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + CDATA_FREQUENCY,
+            freq);
+}
+
+static void set_adc_rate(struct m3_state *s, unsigned int rate)
+{
+    u32 freq;
+
+    if (rate > 48000)
+        rate = 48000;
+    if (rate < 8000)
+        rate = 8000;
+
+    s->rateadc = rate;
+
+    freq = ((rate << 15) + 24000 ) / 48000;
+    if(freq) 
+        freq--;
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + CDATA_FREQUENCY,
+            freq);
+}
+
+static void inc_timer_users(struct m3_card *card)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&card->lock, flags);
+    
+    card->timer_users++;
+    DPRINTK(DPSYS, "inc timer users now %d\n",
+            card->timer_users);
+    if(card->timer_users != 1) 
+        goto out;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_RELOAD,
+         240 ) ;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_CURRENT,
+         240 ) ;
+
+    m3_outw(card,  
+            m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE,
+            HOST_INT_CTRL);
+out:
+    spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void dec_timer_users(struct m3_card *card)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&card->lock, flags);
+
+    card->timer_users--;
+    DPRINTK(DPSYS, "dec timer users now %d\n",
+            card->timer_users);
+    if(card->timer_users > 0 ) 
+        goto out;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_RELOAD,
+         0 ) ;
+
+    __m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_TIMER_COUNT_CURRENT,
+         0 ) ;
+
+    m3_outw(card,  m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE,
+            HOST_INT_CTRL);
+out:
+    spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/*
+ * {start,stop}_{adc,dac} should be called
+ * while holding the 'state' lock and they
+ * will try to grab the 'card' lock..
+ */
+static void stop_adc(struct m3_state *s)
+{
+    if (! (s->enable & ADC_RUNNING)) 
+        return;
+
+    s->enable &= ~ADC_RUNNING;
+    dec_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + CDATA_INSTANCE_READY, 0);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_ADC1_REQUEST, 0);
+}    
+
+static void stop_dac(struct m3_state *s)
+{
+    if (! (s->enable & DAC_RUNNING)) 
+        return;
+
+    DPRINTK(DPSYS, "stop_dac()\n");
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + CDATA_INSTANCE_READY, 0);
+
+    s->enable &= ~DAC_RUNNING;
+    s->card->dacs_active--;
+    dec_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_MIXER_TASK_NUMBER, 
+            s->card->dacs_active ) ;
+}    
+
+static void start_dac(struct m3_state *s)
+{
+    if( (!s->dma_dac.mapped && s->dma_dac.count < 1) ||
+            !s->dma_dac.ready ||
+            (s->enable & DAC_RUNNING)) 
+        return;
+
+    DPRINTK(DPSYS, "start_dac()\n");
+
+    s->enable |= DAC_RUNNING;
+    s->card->dacs_active++;
+    inc_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + CDATA_INSTANCE_READY, 1);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_MIXER_TASK_NUMBER, 
+            s->card->dacs_active ) ;
+}    
+
+static void start_adc(struct m3_state *s)
+{
+    if ((! s->dma_adc.mapped &&
+                s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
+        || !s->dma_adc.ready 
+        || (s->enable & ADC_RUNNING) ) 
+            return;
+
+    DPRINTK(DPSYS, "start_adc()\n");
+
+    s->enable |= ADC_RUNNING;
+    inc_timer_users(s->card);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            KDATA_ADC1_REQUEST, 1);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + CDATA_INSTANCE_READY, 1);
+}    
+
+static struct play_vals {
+    u16 addr, val;
+} pv[] = {
+    {CDATA_LEFT_VOLUME, ARB_VOLUME},
+    {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+    {SRC3_DIRECTION_OFFSET, 0} ,
+    /* +1, +2 are stereo/16 bit */
+    {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+    {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+    {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+    {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+    {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+    {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+    {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+    {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+    {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+    {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+    {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+    {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+    {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */
+    {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */
+    {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */
+    {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+    {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */
+};
+
+
+/* the mode passed should be already shifted and masked */
+static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size)
+{
+    int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2);
+    int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2);
+    int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+    int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+    struct dmabuf *db = &s->dma_dac;
+    int i;
+
+    DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n",
+        mode, rate, buffer, size);
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+    /* host dma buffer pointers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_ADDRL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_ADDRH,
+        HI(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+        LO(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+        HI(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_CURRENTL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_HOST_SRC_CURRENTH,
+        HI(virt_to_bus(buffer)));
+#undef LO
+#undef HI
+
+    /* dsp buffers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_BEGIN,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1,
+        dsp_in_buffer + (dsp_in_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_HEAD,
+        dsp_in_buffer);
+    
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_IN_BUF_TAIL,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_BEGIN,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1,
+        dsp_out_buffer + (dsp_out_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_HEAD,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_OUT_BUF_TAIL,
+        dsp_out_buffer);
+
+    /*
+     * some per client initializers
+     */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12,
+        s->dac_inst.data + 40 + 8);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19,
+        s->dac_inst.code + MINISRC_COEF_LOC);
+
+    /* enable or disable low pass filter? */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22,
+        s->ratedac > 45000 ? 0xff : 0 );
+    
+    /* tell it which way dma is going? */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->dac_inst.data + CDATA_DMA_CONTROL,
+        DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+    /*
+     * set an armload of static initializers
+     */
+    for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) 
+        m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->dac_inst.data + pv[i].addr, pv[i].val);
+
+    /* 
+     * put us in the lists if we're not already there
+     */
+
+    if(db->in_lists == 0) {
+
+        db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, 
+                s->dac_inst.data >> DP_SHIFT_COUNT);
+
+        db->dma_index = m3_add_list(s->card, &s->card->dma_list, 
+                s->dac_inst.data >> DP_SHIFT_COUNT);
+
+        db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, 
+                s->dac_inst.data >> DP_SHIFT_COUNT);
+
+        db->in_lists = 1;
+    }
+
+    set_dac_rate(s,rate);
+    start_dac(s);
+}
+
+/*
+ *    Native record driver 
+ */
+static struct rec_vals {
+    u16 addr, val;
+} rv[] = {
+    {CDATA_LEFT_VOLUME, ARB_VOLUME},
+    {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+    {SRC3_DIRECTION_OFFSET, 1} ,
+    /* +1, +2 are stereo/16 bit */
+    {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+    {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+    {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+    {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+    {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+    {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+    {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+    {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+    {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+    {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+    {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+    {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+    {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */
+    {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */
+    {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */
+    {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */
+    {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+    {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */
+    {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */
+};
+
+/* again, passed mode is alrady shifted/masked */
+static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size)
+{
+    int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2);
+    int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2);
+    int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+    int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+    struct dmabuf *db = &s->dma_adc;
+    int i;
+
+    DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n",
+        mode, rate, buffer, size);
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+    /* host dma buffer pointers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_ADDRL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_ADDRH,
+        HI(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+        LO(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+        HI(virt_to_bus(buffer) + size));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_CURRENTL,
+        LO(virt_to_bus(buffer)));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_HOST_SRC_CURRENTH,
+        HI(virt_to_bus(buffer)));
+#undef LO
+#undef HI
+
+    /* dsp buffers */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_BEGIN,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1,
+        dsp_in_buffer + (dsp_in_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_HEAD,
+        dsp_in_buffer);
+    
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_IN_BUF_TAIL,
+        dsp_in_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_BEGIN,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1,
+        dsp_out_buffer + (dsp_out_size / 2));
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_HEAD,
+        dsp_out_buffer);
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_OUT_BUF_TAIL,
+        dsp_out_buffer);
+
+    /*
+     * some per client initializers
+     */
+
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12,
+        s->adc_inst.data + 40 + 8);
+
+    /* tell it which way dma is going? */
+    m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+        s->adc_inst.data + CDATA_DMA_CONTROL,
+        DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + 
+        DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+    /*
+     * set an armload of static initializers
+     */
+    for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) 
+        m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA,
+            s->adc_inst.data + rv[i].addr, rv[i].val);
+
+    /* 
+     * put us in the lists if we're not already there
+     */
+
+    if(db->in_lists == 0) {
+
+        db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, 
+                s->adc_inst.data >> DP_SHIFT_COUNT);
+
+        db->dma_index = m3_add_list(s->card, &s->card->dma_list, 
+                s->adc_inst.data >> DP_SHIFT_COUNT);
+
+        db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, 
+                s->adc_inst.data >> DP_SHIFT_COUNT);
+
+        db->in_lists = 1;
+    }
+
+    set_adc_rate(s,rate);
+    start_adc(s);
+}
+/* --------------------------------------------------------------------- */
+
+static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count)
+{
+    DPRINTK(DPINT,"set_dmaa??\n");
+}
+
+static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count)
+{
+    DPRINTK(DPINT,"set_dmac??\n");
+}
+
+u32 get_dma_pos(struct m3_card *card,
+        int instance_addr)
+{
+    u16 hi = 0, lo = 0;
+    int retry = 10;
+
+    /*
+     * try and get a valid answer
+     */
+    while(retry--) {
+        hi =  m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                instance_addr + CDATA_HOST_SRC_CURRENTH);
+
+        lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                instance_addr + CDATA_HOST_SRC_CURRENTL);
+
+        if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA,
+                instance_addr + CDATA_HOST_SRC_CURRENTH))
+            break;
+    }
+    return lo | (hi<<16);
+}
+
+u32 get_dmaa(struct m3_state *s)
+{
+    u32 offset;
+
+    offset = get_dma_pos(s->card, s->dac_inst.data) - 
+        virt_to_bus(s->dma_dac.rawbuf);
+
+    DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset);
+
+    return offset;
+}
+
+u32 get_dmac(struct m3_state *s)
+{
+    u32 offset;
+
+    offset = get_dma_pos(s->card, s->adc_inst.data) -
+        virt_to_bus(s->dma_adc.rawbuf);
+
+    DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset);
+
+    return offset;
+
+}
+
+static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int 
+prog_dmabuf(struct m3_state *s, unsigned rec)
+{
+    struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+    unsigned rate = rec ? s->rateadc : s->ratedac;
+    unsigned bytepersec;
+    unsigned bufs;
+    unsigned char fmt;
+    unsigned long flags;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    fmt = s->fmt;
+    if (rec) {
+        stop_adc(s);
+        fmt >>= ESS_ADC_SHIFT;
+    } else {
+        stop_dac(s);
+        fmt >>= ESS_DAC_SHIFT;
+    }
+    fmt &= ESS_FMT_MASK;
+
+    db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+
+    bytepersec = rate << sample_shift[fmt];
+    bufs = PAGE_SIZE << db->buforder;
+    if (db->ossfragshift) {
+        if ((1000 << db->ossfragshift) < bytepersec)
+            db->fragshift = ld2(bytepersec/1000);
+        else
+            db->fragshift = db->ossfragshift;
+    } else {
+        db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+        if (db->fragshift < 3)
+            db->fragshift = 3; 
+    }
+    db->numfrag = bufs >> db->fragshift;
+    while (db->numfrag < 4 && db->fragshift > 3) {
+        db->fragshift--;
+        db->numfrag = bufs >> db->fragshift;
+    }
+    db->fragsize = 1 << db->fragshift;
+    if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+        db->numfrag = db->ossmaxfrags;
+    db->fragsamples = db->fragsize >> sample_shift[fmt];
+    db->dmasize = db->numfrag << db->fragshift;
+
+    DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize);
+
+    memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize);
+
+    if (rec) 
+        m3_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize);
+    else 
+        m3_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize);
+
+    db->ready = 1;
+
+    spin_unlock_irqrestore(&s->lock, flags);
+
+    return 0;
+}
+
+static void clear_advance(struct m3_state *s)
+{
+    unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80;
+    
+    unsigned char *buf = s->dma_dac.rawbuf;
+    unsigned bsize = s->dma_dac.dmasize;
+    unsigned bptr = s->dma_dac.swptr;
+    unsigned len = s->dma_dac.fragsize;
+    
+    if (bptr + len > bsize) {
+        unsigned x = bsize - bptr;
+        memset(buf + bptr, c, x);
+        /* account for wrapping? */
+        bptr = 0;
+        len -= x;
+    }
+    memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void m3_update_ptr(struct m3_state *s)
+{
+    unsigned hwptr;
+    int diff;
+
+    /* update ADC pointer */
+    if (s->dma_adc.ready) {
+        hwptr = get_dmac(s) % s->dma_adc.dmasize;
+        diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+        s->dma_adc.hwptr = hwptr;
+        s->dma_adc.total_bytes += diff;
+        s->dma_adc.count += diff;
+        if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+            wake_up(&s->dma_adc.wait);
+        if (!s->dma_adc.mapped) {
+            if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+                stop_adc(s); 
+                /* brute force everyone back in sync, sigh */
+                s->dma_adc.count = 0;
+                s->dma_adc.swptr = 0;
+                s->dma_adc.hwptr = 0;
+                s->dma_adc.error++;
+            }
+        }
+    }
+    /* update DAC pointer */
+    if (s->dma_dac.ready) {
+        hwptr = get_dmaa(s) % s->dma_dac.dmasize; 
+        diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+
+        DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n",
+                hwptr,diff,s->dma_dac.count);
+
+        s->dma_dac.hwptr = hwptr;
+        s->dma_dac.total_bytes += diff;
+
+        if (s->dma_dac.mapped) {
+            
+            s->dma_dac.count += diff;
+            if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) {
+                wake_up(&s->dma_dac.wait);
+            }
+        } else {
+
+            s->dma_dac.count -= diff;
+            
+            if (s->dma_dac.count <= 0) {
+                DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", 
+                        diff, diff, 
+                        s->dma_dac.count, 
+                        s->dma_dac.count, 
+                    hwptr, hwptr,
+                    s->dma_dac.swptr,
+                    s->dma_dac.swptr);
+                stop_dac(s);
+                /* brute force everyone back in sync, sigh */
+                s->dma_dac.count = 0; 
+                s->dma_dac.swptr = hwptr; 
+                s->dma_dac.error++;
+            } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+                clear_advance(s);
+                s->dma_dac.endcleared = 1;
+            }
+            if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) {
+                wake_up(&s->dma_dac.wait);
+                DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n",
+                        s->dma_dac.count, s->dma_dac.swptr, hwptr);
+            }
+        }
+    }
+}
+
+static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct m3_card *c = (struct m3_card *)dev_id;
+    struct m3_state *s = &c->channels[0];
+    u8 status;
+
+    status = inb(c->iobase+0x1A);
+
+    if(status == 0xff) return;
+   
+    /* presumably acking the ints? */
+    outw(status, c->iobase+0x1A); 
+
+    if(c->in_suspend)
+        return;
+
+    /*
+     * ack an assp int if its running
+     * and has an int pending
+     */
+    if( status & ASSP_INT_PENDING) {
+        u8 ctl = inb(c->iobase + ASSP_CONTROL_B);
+        if( !(ctl & STOP_ASSP_CLOCK)) {
+            ctl = inb(c->iobase + ASSP_HOST_INT_STATUS );
+            if(ctl & DSP2HOST_REQ_TIMER) {
+                outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS);
+                /* update adc/dac info if it was a timer int */
+                spin_lock(&s->lock);
+                m3_update_ptr(s);
+                spin_unlock(&s->lock);
+            }
+        }
+    }
+
+    /* XXX is this needed? */
+    if(status & 0x40) 
+        outb(0x40, c->iobase+0x1A);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n";
+
+#define VALIDATE_MAGIC(FOO,MAG)                         \
+({                                                \
+    if (!(FOO) || (FOO)->magic != MAG) { \
+        printk(invalid_magic,__FUNCTION__);            \
+        return -ENXIO;                    \
+    }                                         \
+})
+
+#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC)
+#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC)
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct m3_state *s, int nonblock)
+{
+    DECLARE_WAITQUEUE(wait,current);
+    unsigned long flags;
+    int count;
+    signed long tmo;
+
+    if (s->dma_dac.mapped || !s->dma_dac.ready)
+        return 0;
+    set_current_state(TASK_INTERRUPTIBLE);
+    add_wait_queue(&s->dma_dac.wait, &wait);
+    for (;;) {
+        spin_lock_irqsave(&s->lock, flags);
+        count = s->dma_dac.count;
+        spin_unlock_irqrestore(&s->lock, flags);
+        if (count <= 0)
+            break;
+        if (signal_pending(current))
+            break;
+        if (nonblock) {
+            remove_wait_queue(&s->dma_dac.wait, &wait);
+            set_current_state(TASK_RUNNING);
+            return -EBUSY;
+        }
+        tmo = (count * HZ) / s->ratedac;
+        tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK];
+        /* XXX this is just broken.  someone is waking us up alot, or schedule_timeout is broken.
+            or something.  who cares. - zach */
+        if (!schedule_timeout(tmo ? tmo : 1) && tmo)
+            DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies);
+    }
+    remove_wait_queue(&s->dma_dac.wait, &wait);
+    set_current_state(TASK_RUNNING);
+    if (signal_pending(current))
+            return -ERESTARTSYS;
+    return 0;
+}
+
+static ssize_t m3_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+    struct m3_state *s = (struct m3_state *)file->private_data;
+    ssize_t ret;
+    unsigned long flags;
+    unsigned swptr;
+    int cnt;
+    
+    VALIDATE_STATE(s);
+    if (ppos != &file->f_pos)
+        return -ESPIPE;
+    if (s->dma_adc.mapped)
+        return -ENXIO;
+    if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+        return ret;
+    if (!access_ok(VERIFY_WRITE, buffer, count))
+        return -EFAULT;
+    ret = 0;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    while (count > 0) {
+        int timed_out;
+
+        swptr = s->dma_adc.swptr;
+        cnt = s->dma_adc.dmasize-swptr;
+        if (s->dma_adc.count < cnt)
+            cnt = s->dma_adc.count;
+
+        if (cnt > count)
+            cnt = count;
+
+        if (cnt <= 0) {
+            start_adc(s);
+            if (file->f_flags & O_NONBLOCK) 
+            {
+                ret = ret ? ret : -EAGAIN;
+                goto out;
+            }
+
+            spin_unlock_irqrestore(&s->lock, flags);
+            timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0;
+            spin_lock_irqsave(&s->lock, flags);
+
+            if(timed_out) {
+                printk("read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n",
+                       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, 
+                       s->dma_adc.hwptr, s->dma_adc.swptr);
+                stop_adc(s);
+                set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
+                s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+            }
+            if (signal_pending(current)) 
+            {
+                ret = ret ? ret : -ERESTARTSYS;
+                goto out;
+            }
+            continue;
+        }
+    
+        spin_unlock_irqrestore(&s->lock, flags);
+        if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+            ret = ret ? ret : -EFAULT;
+            return ret;
+        }
+        spin_lock_irqsave(&s->lock, flags);
+
+        swptr = (swptr + cnt) % s->dma_adc.dmasize;
+        s->dma_adc.swptr = swptr;
+        s->dma_adc.count -= cnt;
+        count -= cnt;
+        buffer += cnt;
+        ret += cnt;
+        start_adc(s);
+    }
+
+out:
+    spin_unlock_irqrestore(&s->lock, flags);
+    return ret;
+}
+
+static ssize_t m3_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+    struct m3_state *s = (struct m3_state *)file->private_data;
+    ssize_t ret;
+    unsigned long flags;
+    unsigned swptr;
+    int cnt;
+    
+    VALIDATE_STATE(s);
+    if (ppos != &file->f_pos)
+        return -ESPIPE;
+    if (s->dma_dac.mapped)
+        return -ENXIO;
+    if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+        return ret;
+    if (!access_ok(VERIFY_READ, buffer, count))
+        return -EFAULT;
+    ret = 0;
+
+    spin_lock_irqsave(&s->lock, flags);
+
+    while (count > 0) {
+        int timed_out;
+
+        if (s->dma_dac.count < 0) {
+            s->dma_dac.count = 0;
+            s->dma_dac.swptr = s->dma_dac.hwptr;
+        }
+        swptr = s->dma_dac.swptr;
+
+        cnt = s->dma_dac.dmasize-swptr;
+
+        if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+            cnt = s->dma_dac.dmasize - s->dma_dac.count;
+
+
+        if (cnt > count)
+            cnt = count;
+
+        if (cnt <= 0) {
+            start_dac(s);
+            if (file->f_flags & O_NONBLOCK) {
+                if(!ret) ret = -EAGAIN;
+                goto out;
+            }
+            spin_unlock_irqrestore(&s->lock, flags);
+            timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0;
+            spin_lock_irqsave(&s->lock, flags);
+            if(timed_out) {
+                DPRINTK(DPCRAP,"write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n",
+                       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, 
+                       s->dma_dac.hwptr, s->dma_dac.swptr);
+                stop_dac(s);
+                set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
+                s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+            }
+            if (signal_pending(current)) {
+                if (!ret) ret = -ERESTARTSYS;
+                goto out;
+            }
+            continue;
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+            if (!ret) ret = -EFAULT;
+            return ret;
+        }
+        spin_lock_irqsave(&s->lock, flags);
+
+        DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n",
+                cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);
+        
+        swptr = (swptr + cnt) % s->dma_dac.dmasize;
+
+        s->dma_dac.swptr = swptr;
+        s->dma_dac.count += cnt;
+        s->dma_dac.endcleared = 0;
+        count -= cnt;
+        buffer += cnt;
+        ret += cnt;
+        start_dac(s);
+    }
+out:
+    spin_unlock_irqrestore(&s->lock, flags);
+    return ret;
+}
+
+static unsigned int m3_poll(struct file *file, struct poll_table_struct *wait)
+{
+    struct m3_state *s = (struct m3_state *)file->private_data;
+    unsigned long flags;
+    unsigned int mask = 0;
+
+    VALIDATE_STATE(s);
+    if (file->f_mode & FMODE_WRITE)
+        poll_wait(file, &s->dma_dac.wait, wait);
+    if (file->f_mode & FMODE_READ)
+        poll_wait(file, &s->dma_adc.wait, wait);
+
+    spin_lock_irqsave(&s->lock, flags);
+    m3_update_ptr(s);
+
+    if (file->f_mode & FMODE_READ) {
+        if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+            mask |= POLLIN | POLLRDNORM;
+    }
+    if (file->f_mode & FMODE_WRITE) {
+        if (s->dma_dac.mapped) {
+            if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+                mask |= POLLOUT | POLLWRNORM;
+        } else {
+            if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+                mask |= POLLOUT | POLLWRNORM;
+        }
+    }
+
+    spin_unlock_irqrestore(&s->lock, flags);
+    return mask;
+}
+
+static int m3_mmap(struct file *file, struct vm_area_struct *vma)
+{
+    struct m3_state *s = (struct m3_state *)file->private_data;
+    unsigned long max_size, size, start, offset;
+    struct dmabuf *db;
+    int ret = -EINVAL;
+
+    VALIDATE_STATE(s);
+    if (vma->vm_flags & VM_WRITE) {
+        if ((ret = prog_dmabuf(s, 0)) != 0)
+            return ret;
+        db = &s->dma_dac;
+    } else 
+    if (vma->vm_flags & VM_READ) {
+        if ((ret = prog_dmabuf(s, 1)) != 0)
+            return ret;
+        db = &s->dma_adc;
+    } else  
+        return -EINVAL;
+
+    max_size = db->dmasize;
+
+    start = vma->vm_start;
+    offset = (vma->vm_pgoff << PAGE_SHIFT);
+    size = vma->vm_end - vma->vm_start;
+
+    if(size > max_size)
+        goto out;
+    if(offset > max_size - size)
+        goto out;
+
+    /*
+     * this will be ->nopage() once I can 
+     * ask Jeff what the hell I'm doing wrong.
+     */
+    ret = -EAGAIN;
+    if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+        goto out;
+
+    db->mapped = 1;
+    ret = 0;
+
+out:
+    return ret;
+}
+
+/*
+ * this function is a disaster..
+ */
+#define get_user_ret(x, ptr,  ret) ({ if(get_user(x, ptr)) return ret; })
+static int m3_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+    struct m3_state *s = (struct m3_state *)file->private_data;
+    unsigned long flags;
+    audio_buf_info abinfo;
+    count_info cinfo;
+    int val, mapped, ret;
+    unsigned char fmtm, fmtd;
+
+    VALIDATE_STATE(s);
+
+    mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+        ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+
+    DPRINTK(DPSYS,"m3_ioctl: cmd %d\n", cmd);
+
+    switch (cmd) {
+    case OSS_GETVERSION:
+        return put_user(SOUND_VERSION, (int *)arg);
+
+    case SNDCTL_DSP_SYNC:
+        if (file->f_mode & FMODE_WRITE)
+            return drain_dac(s, file->f_flags & O_NONBLOCK);
+        return 0;
+        
+    case SNDCTL_DSP_SETDUPLEX:
+        /* XXX fix */
+        return 0;
+
+    case SNDCTL_DSP_GETCAPS:
+        return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+        
+    case SNDCTL_DSP_RESET:
+        spin_lock_irqsave(&s->lock, flags);
+        if (file->f_mode & FMODE_WRITE) {
+            stop_dac(s);
+            synchronize_irq();
+            s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+        }
+        if (file->f_mode & FMODE_READ) {
+            stop_adc(s);
+            synchronize_irq();
+            s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return 0;
+
+    case SNDCTL_DSP_SPEED:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (val >= 0) {
+            if (file->f_mode & FMODE_READ) {
+                stop_adc(s);
+                s->dma_adc.ready = 0;
+                set_adc_rate(s, val);
+            }
+            if (file->f_mode & FMODE_WRITE) {
+                stop_dac(s);
+                s->dma_dac.ready = 0;
+                set_dac_rate(s, val);
+            }
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+        
+    case SNDCTL_DSP_STEREO:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        fmtd = 0;
+        fmtm = ~0;
+        if (file->f_mode & FMODE_READ) {
+            stop_adc(s);
+            s->dma_adc.ready = 0;
+            if (val)
+                fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+            else
+                fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+        }
+        if (file->f_mode & FMODE_WRITE) {
+            stop_dac(s);
+            s->dma_dac.ready = 0;
+            if (val)
+                fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+            else
+                fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+        }
+        set_fmt(s, fmtm, fmtd);
+        spin_unlock_irqrestore(&s->lock, flags);
+        return 0;
+
+    case SNDCTL_DSP_CHANNELS:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (val != 0) {
+            fmtd = 0;
+            fmtm = ~0;
+            if (file->f_mode & FMODE_READ) {
+                stop_adc(s);
+                s->dma_adc.ready = 0;
+                if (val >= 2)
+                    fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT);
+            }
+            if (file->f_mode & FMODE_WRITE) {
+                stop_dac(s);
+                s->dma_dac.ready = 0;
+                if (val >= 2)
+                    fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT);
+            }
+            set_fmt(s, fmtm, fmtd);
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+                       : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+        
+    case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+        return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg);
+        
+    case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (val != AFMT_QUERY) {
+            fmtd = 0;
+            fmtm = ~0;
+            if (file->f_mode & FMODE_READ) {
+                stop_adc(s);
+                s->dma_adc.ready = 0;
+                if (val == AFMT_S16_LE)
+                    fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT);
+            }
+            if (file->f_mode & FMODE_WRITE) {
+                stop_dac(s);
+                s->dma_dac.ready = 0;
+                if (val == AFMT_S16_LE)
+                    fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+                else
+                    fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT);
+            }
+            set_fmt(s, fmtm, fmtd);
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? 
+            (ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+            : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 
+                AFMT_S16_LE : 
+                AFMT_U8, 
+            (int *)arg);
+        
+    case SNDCTL_DSP_POST:
+        return 0;
+
+    case SNDCTL_DSP_GETTRIGGER:
+        val = 0;
+        if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING))
+            val |= PCM_ENABLE_INPUT;
+        if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) 
+            val |= PCM_ENABLE_OUTPUT;
+        return put_user(val, (int *)arg);
+        
+    case SNDCTL_DSP_SETTRIGGER:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        if (file->f_mode & FMODE_READ) {
+            if (val & PCM_ENABLE_INPUT) {
+                if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+                    return ret;
+                start_adc(s);
+            } else
+                stop_adc(s);
+        }
+        if (file->f_mode & FMODE_WRITE) {
+            if (val & PCM_ENABLE_OUTPUT) {
+                if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+                    return ret;
+                start_dac(s);
+            } else
+                stop_dac(s);
+        }
+        return 0;
+
+    case SNDCTL_DSP_GETOSPACE:
+        if (!(file->f_mode & FMODE_WRITE))
+            return -EINVAL;
+        if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0)
+            return val;
+        spin_lock_irqsave(&s->lock, flags);
+        m3_update_ptr(s);
+        abinfo.fragsize = s->dma_dac.fragsize;
+        abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+        abinfo.fragstotal = s->dma_dac.numfrag;
+        abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+    case SNDCTL_DSP_GETISPACE:
+        if (!(file->f_mode & FMODE_READ))
+            return -EINVAL;
+        if (!(s->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0)
+            return val;
+        spin_lock_irqsave(&s->lock, flags);
+        m3_update_ptr(s);
+        abinfo.fragsize = s->dma_adc.fragsize;
+        abinfo.bytes = s->dma_adc.count;
+        abinfo.fragstotal = s->dma_adc.numfrag;
+        abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+        
+    case SNDCTL_DSP_NONBLOCK:
+        file->f_flags |= O_NONBLOCK;
+        return 0;
+
+    case SNDCTL_DSP_GETODELAY:
+        if (!(file->f_mode & FMODE_WRITE))
+            return -EINVAL;
+        spin_lock_irqsave(&s->lock, flags);
+        m3_update_ptr(s);
+        val = s->dma_dac.count;
+        spin_unlock_irqrestore(&s->lock, flags);
+        return put_user(val, (int *)arg);
+
+    case SNDCTL_DSP_GETIPTR:
+        if (!(file->f_mode & FMODE_READ))
+            return -EINVAL;
+        spin_lock_irqsave(&s->lock, flags);
+        m3_update_ptr(s);
+        cinfo.bytes = s->dma_adc.total_bytes;
+        cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+        cinfo.ptr = s->dma_adc.hwptr;
+        if (s->dma_adc.mapped)
+            s->dma_adc.count &= s->dma_adc.fragsize-1;
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+    case SNDCTL_DSP_GETOPTR:
+        if (!(file->f_mode & FMODE_WRITE))
+            return -EINVAL;
+        spin_lock_irqsave(&s->lock, flags);
+        m3_update_ptr(s);
+        cinfo.bytes = s->dma_dac.total_bytes;
+        cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+        cinfo.ptr = s->dma_dac.hwptr;
+        if (s->dma_dac.mapped)
+            s->dma_dac.count &= s->dma_dac.fragsize-1;
+        spin_unlock_irqrestore(&s->lock, flags);
+        return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+    case SNDCTL_DSP_GETBLKSIZE:
+        if (file->f_mode & FMODE_WRITE) {
+            if ((val = prog_dmabuf(s, 0)))
+                return val;
+            return put_user(s->dma_dac.fragsize, (int *)arg);
+        }
+        if ((val = prog_dmabuf(s, 1)))
+            return val;
+        return put_user(s->dma_adc.fragsize, (int *)arg);
+
+    case SNDCTL_DSP_SETFRAGMENT:
+        get_user_ret(val, (int *)arg, -EFAULT);
+        spin_lock_irqsave(&s->lock, flags);
+        if (file->f_mode & FMODE_READ) {
+            s->dma_adc.ossfragshift = val & 0xffff;
+            s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+            if (s->dma_adc.ossfragshift < 4)
+                s->dma_adc.ossfragshift = 4;
+            if (s->dma_adc.ossfragshift > 15)
+                s->dma_adc.ossfragshift = 15;
+            if (s->dma_adc.ossmaxfrags < 4)
+                s->dma_adc.ossmaxfrags = 4;
+        }
+        if (file->f_mode & FMODE_WRITE) {
+            s->dma_dac.ossfragshift = val & 0xffff;
+            s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+            if (s->dma_dac.ossfragshift < 4)
+                s->dma_dac.ossfragshift = 4;
+            if (s->dma_dac.ossfragshift > 15)
+                s->dma_dac.ossfragshift = 15;
+            if (s->dma_dac.ossmaxfrags < 4)
+                s->dma_dac.ossmaxfrags = 4;
+        }
+        spin_unlock_irqrestore(&s->lock, flags);
+        return 0;
+
+    case SNDCTL_DSP_SUBDIVIDE:
+        if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+            (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+            return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+        if (val != 1 && val != 2 && val != 4)
+            return -EINVAL;
+        if (file->f_mode & FMODE_READ)
+            s->dma_adc.subdivision = val;
+        if (file->f_mode & FMODE_WRITE)
+            s->dma_dac.subdivision = val;
+        return 0;
+
+    case SOUND_PCM_READ_RATE:
+        return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+    case SOUND_PCM_READ_CHANNELS:
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) 
+                       : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
+
+    case SOUND_PCM_READ_BITS:
+        return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) 
+                       : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg);
+
+    case SOUND_PCM_WRITE_FILTER:
+    case SNDCTL_DSP_SETSYNCRO:
+    case SOUND_PCM_READ_FILTER:
+        return -EINVAL;
+        
+    }
+    return -EINVAL;
+}
+
+static int
+allocate_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db)
+{
+    int order;
+
+    DPRINTK(DPSTR,"allocating for dmabuf %p\n", db);
+
+    /* 
+     * alloc as big a chunk as we can, start with 
+     * 64k 'cause we're insane.  based on order cause
+     * the amazingly complicated prog_dmabuf wants it.
+     *
+     * pci_alloc_sonsistent guarantees that it won't cross a natural
+     * boundry; the m3 hardware can't have dma cross a 64k bus
+     * address boundry.
+     */
+    for (order = 16-PAGE_SHIFT; order >= 1; order--) {
+        db->rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order,
+                        &(db->handle));
+        if(db->rawbuf)
+            break;
+    }
+
+    if (!db->rawbuf)
+        return 1;
+
+    DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n",
+            PAGE_SIZE<<order, order, db->rawbuf);
+
+    {
+        struct page *page, *pend;
+
+        pend = virt_to_page(db->rawbuf + (PAGE_SIZE << order) - 1);
+        for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+            mem_map_reserve(page);
+    }
+
+
+    db->buforder = order;
+    db->ready = 0;
+    db->mapped = 0;
+
+    return 0;
+}
+
+static void
+nuke_lists(struct m3_card *card, struct dmabuf *db)
+{
+    m3_remove_list(card, &(card->dma_list), db->dma_index);
+    m3_remove_list(card, &(card->msrc_list), db->msrc_index);
+    db->in_lists = 0;
+}
+
+static void
+free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db)
+{
+    if(db->rawbuf == NULL)
+        return;
+
+    DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db);
+
+    {
+        struct page *page, *pend;
+        pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+        for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+            mem_map_unreserve(page);
+    }
+
+
+    pci_free_consistent(pci_dev, PAGE_SIZE << db->buforder,
+            db->rawbuf, db->handle);
+
+    db->rawbuf = NULL;
+    db->buforder = 0;
+    db->mapped = 0;
+    db->ready = 0;
+}
+
+static int m3_open(struct inode *inode, struct file *file)
+{
+    unsigned int minor = minor(inode->i_rdev);
+    struct m3_card *c;
+    struct m3_state *s = NULL;
+    int i;
+    unsigned char fmtm = ~0, fmts = 0;
+    unsigned long flags;
+
+    /*
+     *    Scan the cards and find the channel. We only
+     *    do this at open time so it is ok
+     */
+    for(c = devs ; c != NULL ; c = c->next) {
+
+        for(i=0;i<NR_DSPS;i++) {
+
+            if(c->channels[i].dev_audio < 0)
+                continue;
+            if((c->channels[i].dev_audio ^ minor) & ~0xf)
+                continue;
+
+            s = &c->channels[i];
+            break;
+        }
+    }
+        
+    if (!s)
+        return -ENODEV;
+        
+    VALIDATE_STATE(s);
+
+    file->private_data = s;
+
+    /* wait for device to become free */
+    down(&s->open_sem);
+    while (s->open_mode & file->f_mode) {
+        if (file->f_flags & O_NONBLOCK) {
+            up(&s->open_sem);
+            return -EWOULDBLOCK;
+        }
+        up(&s->open_sem);
+        interruptible_sleep_on(&s->open_wait);
+        if (signal_pending(current))
+            return -ERESTARTSYS;
+        down(&s->open_sem);
+    }
+    
+    spin_lock_irqsave(&s->lock, flags);
+
+    if (file->f_mode & FMODE_READ) {
+        fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
+        if ((minor & 0xf) == SND_DEV_DSP16)
+            fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; 
+
+        s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+        set_adc_rate(s, 8000);
+    }
+    if (file->f_mode & FMODE_WRITE) {
+        fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT);
+        if ((minor & 0xf) == SND_DEV_DSP16)
+            fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
+
+        s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+        set_dac_rate(s, 8000);
+    }
+    set_fmt(s, fmtm, fmts);
+    s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+
+    up(&s->open_sem);
+    spin_unlock_irqrestore(&s->lock, flags);
+    return 0;
+}
+
+static int m3_release(struct inode *inode, struct file *file)
+{
+    struct m3_state *s = (struct m3_state *)file->private_data;
+    unsigned long flags;
+
+    VALIDATE_STATE(s);
+    if (file->f_mode & FMODE_WRITE)
+        drain_dac(s, file->f_flags & O_NONBLOCK);
+
+    down(&s->open_sem);
+    spin_lock_irqsave(&s->lock, flags);
+
+    if (file->f_mode & FMODE_WRITE) {
+        stop_dac(s);
+        if(s->dma_dac.in_lists) {
+            m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index);
+            nuke_lists(s->card, &(s->dma_dac));
+        }
+    }
+    if (file->f_mode & FMODE_READ) {
+        stop_adc(s);
+        if(s->dma_adc.in_lists) {
+            m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index);
+            nuke_lists(s->card, &(s->dma_adc));
+        }
+    }
+        
+    s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+
+    spin_unlock_irqrestore(&s->lock, flags);
+    up(&s->open_sem);
+    wake_up(&s->open_wait);
+
+    return 0;
+}
+
+/*
+ * Wait for the ac97 serial bus to be free.
+ * return nonzero if the bus is still busy.
+ */
+static int m3_ac97_wait(struct m3_card *card)
+{
+    int i = 10000;
+
+    while( (m3_inb(card, 0x30) & 1) && i--) ;
+
+    return i == 0;
+}
+
+u16 m3_ac97_read(struct ac97_codec *codec, u8 reg)
+{
+    u16 ret = 0;
+    struct m3_card *card = codec->private_data;
+
+    spin_lock(&card->ac97_lock);
+
+    if(m3_ac97_wait(card)) {
+        printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg);
+        goto out;
+    }
+
+    m3_outb(card, 0x80 | (reg & 0x7f), 0x30);
+
+    if(m3_ac97_wait(card)) {
+        printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg);
+        goto out;
+    }
+
+    ret =  m3_inw(card, 0x32);
+    DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg);
+
+out:
+    spin_unlock(&card->ac97_lock);
+    return ret;
+}
+
+void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
+{
+    struct m3_card *card = codec->private_data;
+
+    spin_lock(&card->ac97_lock);
+
+    if(m3_ac97_wait(card)) {
+        printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg);
+        goto out;
+    }
+    DPRINTK(DPCRAP,"writing 0x%04x  to  0x%02x\n", val, reg);
+
+    m3_outw(card, val, 0x32);
+    m3_outb(card, reg & 0x7f, 0x30);
+out:
+    spin_unlock(&card->ac97_lock);
+}
+/* OSS /dev/mixer file operation methods */
+static int m3_open_mixdev(struct inode *inode, struct file *file)
+{
+    unsigned int minor = minor(inode->i_rdev);
+    struct m3_card *card = devs;
+
+    for (card = devs; card != NULL; card = card->next) {
+        if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor))
+                break;
+    }
+
+    if (!card) {
+        return -ENODEV;
+    }
+
+    file->private_data = card->ac97;
+
+    return 0;
+}
+
+static int m3_release_mixdev(struct inode *inode, struct file *file)
+{
+    return 0;
+}
+
+static int m3_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+                                    unsigned long arg)
+{
+    struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+    return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static struct file_operations m3_mixer_fops = {
+    owner:          THIS_MODULE,
+    llseek:         no_llseek,
+    ioctl:          m3_ioctl_mixdev,
+    open:           m3_open_mixdev,
+    release:        m3_release_mixdev,
+};
+
+void remote_codec_config(int io, int isremote)
+{
+    isremote = isremote ? 1 : 0;
+
+    outw(  (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
+            io + RING_BUS_CTRL_B);
+    outw(  (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
+            io + SDO_OUT_DEST_CTRL);
+    outw(  (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
+            io + SDO_IN_DEST_CTRL);
+}
+
+/* 
+ * hack, returns non zero on err 
+ */
+static int try_read_vendor(struct m3_card *card)
+{
+    u16 ret;
+
+    if(m3_ac97_wait(card)) 
+        return 1;
+
+    m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30);
+
+    if(m3_ac97_wait(card)) 
+        return 1;
+
+    ret =  m3_inw(card, 0x32);
+
+    return (ret == 0) || (ret == 0xffff);
+}
+
+static void m3_codec_reset(struct m3_card *card, int busywait)
+{
+    u16 dir;
+    int delay1 = 0, delay2 = 0, i;
+    int io = card->iobase;
+
+    switch (card->card_type) {
+        /*
+         * the onboard codec on the allegro seems 
+         * to want to wait a very long time before
+         * coming back to life 
+         */
+        case ESS_ALLEGRO:
+            delay1 = 50;
+            delay2 = 800;
+        break;
+        case ESS_MAESTRO3:
+        case ESS_MAESTRO3HW:
+            delay1 = 20;
+            delay2 = 500;
+        break;
+    }
+
+    for(i = 0; i < 5; i ++) {
+        dir = inw(io + GPIO_DIRECTION);
+        dir |= 0x10; /* assuming pci bus master? */
+
+        remote_codec_config(io, 0);
+
+        outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
+        udelay(20);
+
+        outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION);
+        outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK);
+        outw(0, io + GPIO_DATA);
+        outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION);
+
+        if(busywait)  {
+            mdelay(delay1);
+        } else {
+            set_current_state(TASK_UNINTERRUPTIBLE);
+            schedule_timeout((delay1 * HZ) / 1000);
+        }
+
+        outw(GPO_PRIMARY_AC97, io + GPIO_DATA);
+        udelay(5);
+        /* ok, bring back the ac-link */
+        outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A);
+        outw(~0, io + GPIO_MASK);
+
+        if(busywait) {
+            mdelay(delay2);
+        } else {
+            set_current_state(TASK_UNINTERRUPTIBLE);
+            schedule_timeout((delay2 * HZ) / 1000);
+        }
+        if(! try_read_vendor(card))
+            break;
+
+        delay1 += 10;
+        delay2 += 100;
+
+        DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n",
+                delay1, delay2);
+    }
+
+#if 0
+    /* more gung-ho reset that doesn't
+     * seem to work anywhere :)
+     */
+    tmp = inw(io + RING_BUS_CTRL_A);
+    outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A);
+    mdelay(20);
+    outw(tmp, io + RING_BUS_CTRL_A);
+    mdelay(50);
+#endif
+}
+
+static int __init m3_codec_install(struct m3_card *card)
+{
+    struct ac97_codec *codec;
+
+    if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+        return -ENOMEM;
+    memset(codec, 0, sizeof(struct ac97_codec));
+
+    codec->private_data = card;
+    codec->codec_read = m3_ac97_read;
+    codec->codec_write = m3_ac97_write;
+    /* someday we should support secondary codecs.. */
+    codec->id = 0;
+
+    if (ac97_probe_codec(codec) == 0) {
+        printk(KERN_ERR PFX "codec probe failed\n");
+        kfree(codec);
+        return -1;
+    }
+
+    if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) {
+        printk(KERN_ERR PFX "couldn't register mixer!\n");
+        kfree(codec);
+        return -1;
+    }
+
+    card->ac97 = codec;
+
+    return 0;
+}
+
+
+#define MINISRC_LPF_LEN 10
+static u16 minisrc_lpf[MINISRC_LPF_LEN] = {
+    0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
+    0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
+};
+static void m3_assp_init(struct m3_card *card)
+{
+    int i;
+
+    /* zero kernel data */
+    for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+                KDATA_BASE_ADDR + i, 0);
+
+    /* zero mixer data? */
+    for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+                KDATA_BASE_ADDR2 + i, 0);
+
+    /* init dma pointer */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+            KDATA_CURRENT_DMA, 
+            KDATA_DMA_XFER0);
+
+    /* write kernel into code memory.. */
+    for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) {
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE, 
+                REV_B_CODE_MEMORY_BEGIN + i, 
+                assp_kernel_image[i]);
+    }
+
+    /*
+     * We only have this one client and we know that 0x400
+     * is free in our kernel's mem map, so lets just
+     * drop it there.  It seems that the minisrc doesn't
+     * need vectors, so we won't bother with them..
+     */
+    for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) {
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE, 
+                0x400 + i, 
+                assp_minisrc_image[i]);
+    }
+
+    /*
+     * write the coefficients for the low pass filter?
+     */
+    for(i = 0; i < MINISRC_LPF_LEN ; i++) {
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE,
+            0x400 + MINISRC_COEF_LOC + i,
+            minisrc_lpf[i]);
+    }
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_CODE,
+        0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN,
+        0x8000);
+
+    /*
+     * the minisrc is the only thing on
+     * our task list..
+     */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+            KDATA_TASK0, 
+            0x400);
+
+    /*
+     * init the mixer number..
+     */
+
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+            KDATA_MIXER_TASK_NUMBER,0);
+
+    /*
+     * EXTREME KERNEL MASTER VOLUME
+     */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_DAC_LEFT_VOLUME, ARB_VOLUME);
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+        KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME);
+
+    card->mixer_list.mem_addr = KDATA_MIXER_XFER0;
+    card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS;
+    card->adc1_list.mem_addr = KDATA_ADC1_XFER0;
+    card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS;
+    card->dma_list.mem_addr = KDATA_DMA_XFER0;
+    card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS;
+    card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC;
+    card->msrc_list.max = MAX_INSTANCE_MINISRC;
+}
+
+static int setup_msrc(struct m3_card *card,
+        struct assp_instance *inst, int index)
+{
+    int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + 
+            MINISRC_IN_BUFFER_SIZE / 2 +
+            1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 );
+    int address, i;
+
+    /*
+     * the revb memory map has 0x1100 through 0x1c00
+     * free.  
+     */
+
+    /*
+     * align instance address to 256 bytes so that it's
+     * shifted list address is aligned.  
+     * list address = (mem address >> 1) >> 7;
+     */
+    data_bytes = (data_bytes + 255) & ~255;
+    address = 0x1100 + ((data_bytes/2) * index);
+
+    if((address + (data_bytes/2)) >= 0x1c00) {
+        printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n",
+                data_bytes, index, address);
+        return -1;
+    }
+
+    for(i = 0; i < data_bytes/2 ; i++) 
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA,
+                address + i, 0);
+
+    inst->code = 0x400;
+    inst->data = address;
+
+    return 0;
+}
+
+static int m3_assp_client_init(struct m3_state *s)
+{
+    setup_msrc(s->card, &(s->dac_inst), s->index * 2);
+    setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1);
+
+    return 0;
+}
+
+static void m3_amp_enable(struct m3_card *card, int enable)
+{
+    /* 
+     * this works for the reference board, have to find
+     * out about others
+     *
+     * this needs more magic for 4 speaker, but..
+     */
+    int io = card->iobase;
+    u16 gpo, polarity_port, polarity;
+
+    if(!external_amp)
+        return;
+
+    switch (card->card_type) {
+        case ESS_ALLEGRO:
+            polarity_port = 0x1800;
+            break;
+        default:
+            /* presumably this is for all 'maestro3's.. */
+            polarity_port = 0x1100;
+            break;
+    }
+
+    gpo = (polarity_port >> 8) & 0x0F;
+    polarity = polarity_port >> 12;
+    if ( enable )
+        polarity = !polarity;
+    polarity = polarity << gpo;
+    gpo = 1 << gpo;
+
+    outw(~gpo , io + GPIO_MASK);
+
+    outw( inw(io + GPIO_DIRECTION) | gpo ,
+            io + GPIO_DIRECTION);
+
+    outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) ,
+            io + GPIO_DATA);
+
+    outw(0xffff , io + GPIO_MASK);
+}
+
+static int
+maestro_config(struct m3_card *card) 
+{
+    struct pci_dev *pcidev = card->pcidev;
+    u32 n;
+    u8  t; /* makes as much sense as 'n', no? */
+
+    pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+    n &= REDUCED_DEBOUNCE;
+    n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
+    pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+    outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B);
+    pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+    n &= ~INT_CLK_SELECT;
+    if(card->card_type >= ESS_MAESTRO3)  {
+        n &= ~INT_CLK_MULT_ENABLE; 
+        n |= INT_CLK_SRC_NOT_PCI;
+    }
+    n &=  ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 );
+    pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+    if(card->card_type <= ESS_ALLEGRO) {
+        pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n);
+        n |= IN_CLK_12MHZ_SELECT;
+        pci_write_config_dword(pcidev, PCI_USER_CONFIG, n);
+    }
+
+    t = inb(card->iobase + ASSP_CONTROL_A);
+    t &= ~( DSP_CLK_36MHZ_SELECT  | ASSP_CLK_49MHZ_SELECT);
+    t |= ASSP_CLK_49MHZ_SELECT;
+    t |= ASSP_0_WS_ENABLE; 
+    outb(t, card->iobase + ASSP_CONTROL_A);
+
+    outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); 
+
+    return 0;
+} 
+
+static void m3_enable_ints(struct m3_card *card)
+{
+    unsigned long io = card->iobase;
+
+    outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL);
+    outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
+            io + ASSP_CONTROL_C);
+}
+
+static struct file_operations m3_audio_fops = {
+    owner:      THIS_MODULE,
+    llseek:     &no_llseek,
+    read:       &m3_read,
+    write:      &m3_write,
+    poll:       &m3_poll,
+    ioctl:      &m3_ioctl,
+    mmap:       &m3_mmap,
+    open:       &m3_open,
+    release:    &m3_release,
+};
+
+#ifdef CONFIG_PM
+int alloc_dsp_suspendmem(struct m3_card *card)
+{
+    int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH);
+
+    if( (card->suspend_mem = vmalloc(len)) == NULL)
+        return 1;
+
+    return 0;
+}
+void free_dsp_suspendmem(struct m3_card *card)
+{
+   if(card->suspend_mem)
+       vfree(card->suspend_mem);
+}
+
+#else
+#define alloc_dsp_suspendmem(args...) 0
+#define free_dsp_suspendmem(args...) 
+#endif
+
+/*
+ * great day!  this function is ugly as hell.
+ */
+static int __init m3_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
+{
+    u32 n;
+    int i;
+    struct m3_card *card = NULL;
+    int ret = 0;
+    int card_type = pci_id->driver_data;
+
+    DPRINTK(DPMOD, "in maestro_install\n");
+
+    if (pci_enable_device(pci_dev))
+        return -EIO;
+
+    if (pci_set_dma_mask(pci_dev, M3_PCI_DMA_MASK)) {
+        printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n");
+        return -ENODEV;
+    }
+        
+    pci_set_master(pci_dev);
+
+    if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) {
+        printk(KERN_WARNING PFX "out of memory\n");
+        return -ENOMEM;
+    }
+    memset(card, 0, sizeof(struct m3_card));
+    card->pcidev = pci_dev;
+    init_waitqueue_head(&card->suspend_queue);
+
+    if ( ! request_region(pci_resource_start(pci_dev, 0),
+                pci_resource_len (pci_dev, 0), M3_MODULE_NAME)) {
+
+        printk(KERN_WARNING PFX "unable to reserve I/O space.\n");
+        ret = -EBUSY;
+        goto out;
+    }
+
+    card->iobase = pci_resource_start(pci_dev, 0);
+
+    if(alloc_dsp_suspendmem(card)) {
+        printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n",
+                REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH);
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    card->card_type = card_type;
+    card->irq = pci_dev->irq;
+    card->next = devs;
+    card->magic = M3_CARD_MAGIC;
+    spin_lock_init(&card->lock);
+    spin_lock_init(&card->ac97_lock);
+    devs = card;
+    for(i = 0; i<NR_DSPS; i++) {
+        struct m3_state *s = &(card->channels[i]);
+        s->dev_audio = -1;
+    }
+
+    printk(KERN_INFO PFX "Configuring ESS %s found at IO 0x%04X IRQ %d\n", 
+        card_names[card->card_type], card->iobase, card->irq);
+
+    pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &n);
+    printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); 
+
+    maestro_config(card);
+    m3_assp_halt(card);
+
+    m3_codec_reset(card, 0);
+
+    if(m3_codec_install(card))  {
+        ret = -EIO; 
+        goto out;
+    }
+
+    m3_assp_init(card);
+    m3_amp_enable(card, 1);
+    
+    for(i=0;i<NR_DSPS;i++) {
+        struct m3_state *s=&card->channels[i];
+
+        s->index = i;
+
+        s->card = card;
+        init_waitqueue_head(&s->dma_adc.wait);
+        init_waitqueue_head(&s->dma_dac.wait);
+        init_waitqueue_head(&s->open_wait);
+        spin_lock_init(&s->lock);
+        init_MUTEX(&(s->open_sem));
+        s->magic = M3_STATE_MAGIC;
+
+        m3_assp_client_init(s);
+        
+        if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
+            printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n");
+        /* register devices */
+        if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) {
+            break;
+        }
+
+        if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) ||
+                allocate_dmabuf(card->pcidev, &(s->dma_dac)))  { 
+            ret = -ENOMEM;
+            goto out;
+        }
+    }
+    
+    if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) {
+
+        printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq);
+
+        ret = -EIO;
+        goto out;
+    }
+
+    pci_set_drvdata(pci_dev, card);
+    
+    m3_enable_ints(card);
+    m3_assp_continue(card);
+
+out:
+    if(ret) {
+        if(card->iobase)
+            release_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0));
+        free_dsp_suspendmem(card);
+        if(card->ac97) {
+            unregister_sound_mixer(card->ac97->dev_mixer);
+            kfree(card->ac97);
+        }
+        for(i=0;i<NR_DSPS;i++)
+        {
+            struct m3_state *s = &card->channels[i];
+            if(s->dev_audio != -1)
+                unregister_sound_dsp(s->dev_audio);
+        }
+        kfree(card);
+    }
+
+    return ret; 
+}
+
+static void m3_remove(struct pci_dev *pci_dev)
+{
+    struct m3_card *card;
+
+    unregister_reboot_notifier(&m3_reboot_nb);
+
+    while ((card = devs)) {
+        int i;
+        devs = devs->next;
+    
+        free_irq(card->irq, card);
+        unregister_sound_mixer(card->ac97->dev_mixer);
+        kfree(card->ac97);
+
+        for(i=0;i<NR_DSPS;i++)
+        {
+            struct m3_state *s = &card->channels[i];
+            if(s->dev_audio < 0)
+                continue;
+
+            unregister_sound_dsp(s->dev_audio);
+            free_dmabuf(card->pcidev, &s->dma_adc);
+            free_dmabuf(card->pcidev, &s->dma_dac);
+        }
+
+        release_region(card->iobase, 256);
+        free_dsp_suspendmem(card);
+        kfree(card);
+    }
+    devs = NULL;
+}
+
+/*
+ * some bioses like the sound chip to be powered down
+ * at shutdown.  We're just calling _suspend to
+ * achieve that..
+ */
+static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+{
+    struct m3_card *card;
+
+    DPRINTK(DPMOD, "notifier suspending all cards\n");
+
+    for(card = devs; card != NULL; card = card->next) {
+        if(!card->in_suspend)
+            m3_suspend(card->pcidev, 3); /* XXX legal? */
+    }
+    return 0;
+}
+
+static int m3_suspend(struct pci_dev *pci_dev, u32 state)
+{
+    unsigned long flags;
+    int i;
+    struct m3_card *card = pci_get_drvdata(pci_dev);
+
+    /* must be a better way.. */
+    save_flags(flags);
+    cli();
+
+    DPRINTK(DPMOD, "pm in dev %p\n",card);
+
+    for(i=0;i<NR_DSPS;i++) {
+        struct m3_state *s = &card->channels[i];
+
+        if(s->dev_audio == -1)
+            continue;
+
+        DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i);
+        stop_dac(s);
+        stop_adc(s);
+    }
+
+    mdelay(10); /* give the assp a chance to idle.. */
+
+    m3_assp_halt(card);
+
+    if(card->suspend_mem) {
+        int index = 0;
+
+        DPRINTK(DPMOD, "saving code\n");
+        for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++)
+            card->suspend_mem[index++] = 
+                m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i);
+        DPRINTK(DPMOD, "saving data\n");
+        for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+            card->suspend_mem[index++] = 
+                m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i);
+    }
+
+    DPRINTK(DPMOD, "powering down apci regs\n");
+    m3_outw(card, 0xffff, 0x54);
+    m3_outw(card, 0xffff, 0x56);
+
+    card->in_suspend = 1;
+
+    restore_flags(flags);
+
+    return 0;
+}
+
+static int m3_resume(struct pci_dev *pci_dev)
+{
+    unsigned long flags;
+    int index;
+    int i;
+    struct m3_card *card = pci_get_drvdata(pci_dev);
+
+    save_flags(flags); /* paranoia */
+    cli();
+    card->in_suspend = 0;
+
+    DPRINTK(DPMOD, "resuming\n");
+
+    /* first lets just bring everything back. .*/
+
+    DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card);
+    m3_outw(card, 0, 0x54);
+    m3_outw(card, 0, 0x56);
+
+    DPRINTK(DPMOD, "restoring pci configs and reseting codec\n");
+    maestro_config(card);
+    m3_assp_halt(card);
+    m3_codec_reset(card, 1);
+
+    DPRINTK(DPMOD, "restoring dsp code card\n");
+    index = 0;
+    for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++)
+        m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, 
+            card->suspend_mem[index++]);
+    for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+        m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, 
+            card->suspend_mem[index++]);
+
+     /* tell the dma engine to restart itself */
+    m3_assp_write(card, MEMTYPE_INTERNAL_DATA, 
+        KDATA_DMA_ACTIVE, 0);
+
+    DPRINTK(DPMOD, "resuming dsp\n");
+    m3_assp_continue(card);
+
+    DPRINTK(DPMOD, "enabling ints\n");
+    m3_enable_ints(card);
+
+    /* bring back the old school flavor */
+    for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) {
+        int state = card->ac97->mixer_state[i];
+        if (!supported_mixer(card->ac97, i)) 
+                continue;
+
+        card->ac97->write_mixer(card->ac97, i, 
+                state & 0xff, (state >> 8) & 0xff);
+    }
+
+    m3_amp_enable(card, 1);
+
+    /* 
+     * now we flip on the music 
+     */
+    for(i=0;i<NR_DSPS;i++) {
+        struct m3_state *s = &card->channels[i];
+        if(s->dev_audio == -1)
+            continue;
+        /*
+         * db->ready makes it so these guys can be
+         * called unconditionally..
+         */
+        DPRINTK(DPMOD, "turning on dacs ind %d\n",i);
+        start_dac(s);    
+        start_adc(s);    
+    }
+
+    restore_flags(flags);
+
+    /* 
+     * all right, we think things are ready, 
+     * wake up people who were using the device 
+     * when we suspended
+     */
+    wake_up(&card->suspend_queue);
+
+    return 0;
+}
+
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>");
+MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver");
+MODULE_LICENSE("GPL");
+
+#ifdef M_DEBUG
+MODULE_PARM(debug,"i");
+#endif
+MODULE_PARM(external_amp,"i");
+
+static struct pci_driver m3_pci_driver = {
+    name:       "ess_m3_audio",
+    id_table:   m3_id_table,
+    probe:      m3_probe,
+    remove:     m3_remove,
+    suspend:    m3_suspend,
+    resume:     m3_resume,
+};
+
+static int __init m3_init_module(void)
+{
+    if (!pci_present())   /* No PCI bus in this machine! */
+        return -ENODEV;
+
+    printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n");
+
+    if (register_reboot_notifier(&m3_reboot_nb)) {
+        printk(KERN_WARNING PFX "reboot notifier registration failed\n");
+        return -ENODEV; /* ? */
+    }
+
+    if (!pci_register_driver(&m3_pci_driver)) {
+        pci_unregister_driver(&m3_pci_driver);
+        unregister_reboot_notifier(&m3_reboot_nb);
+        return -ENODEV;
+    }
+    return 0;
+}
+
+static void __exit m3_cleanup_module(void)
+{
+    pci_unregister_driver(&m3_pci_driver);
+}
+
+module_init(m3_init_module);
+module_exit(m3_cleanup_module);
+
+void check_suspend(struct m3_card *card)
+{
+    DECLARE_WAITQUEUE(wait, current);
+
+    if(!card->in_suspend) 
+        return;
+
+    card->in_suspend++;
+    add_wait_queue(&card->suspend_queue, &wait);
+    set_current_state(TASK_UNINTERRUPTIBLE);
+    schedule();
+    remove_wait_queue(&card->suspend_queue, &wait);
+    set_current_state(TASK_RUNNING);
+}
diff -Nru linux/sound/oss/maestro3.h linux-2.4.19-pre5-mjc/sound/oss/maestro3.h
--- linux/sound/oss/maestro3.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/maestro3.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,821 @@
+/*
+ *      ESS Technology allegro audio driver.
+ *
+ *      Copyright (C) 1992-2000  Don Kim (don.kim@esstech.com)
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *      Hacked for the maestro3 driver by zab
+ */
+
+// Allegro PCI configuration registers
+#define PCI_LEGACY_AUDIO_CTRL   0x40
+#define SOUND_BLASTER_ENABLE    0x00000001
+#define FM_SYNTHESIS_ENABLE     0x00000002
+#define GAME_PORT_ENABLE        0x00000004
+#define MPU401_IO_ENABLE        0x00000008
+#define MPU401_IRQ_ENABLE       0x00000010
+#define ALIAS_10BIT_IO          0x00000020
+#define SB_DMA_MASK             0x000000C0
+#define SB_DMA_0                0x00000040
+#define SB_DMA_1                0x00000040
+#define SB_DMA_R                0x00000080
+#define SB_DMA_3                0x000000C0
+#define SB_IRQ_MASK             0x00000700
+#define SB_IRQ_5                0x00000000
+#define SB_IRQ_7                0x00000100
+#define SB_IRQ_9                0x00000200
+#define SB_IRQ_10               0x00000300
+#define MIDI_IRQ_MASK           0x00003800
+#define SERIAL_IRQ_ENABLE       0x00004000
+#define DISABLE_LEGACY          0x00008000
+
+#define PCI_ALLEGRO_CONFIG      0x50
+#define SB_ADDR_240             0x00000004
+#define MPU_ADDR_MASK           0x00000018
+#define MPU_ADDR_330            0x00000000
+#define MPU_ADDR_300            0x00000008
+#define MPU_ADDR_320            0x00000010
+#define MPU_ADDR_340            0x00000018
+#define USE_PCI_TIMING          0x00000040
+#define POSTED_WRITE_ENABLE     0x00000080
+#define DMA_POLICY_MASK         0x00000700
+#define DMA_DDMA                0x00000000
+#define DMA_TDMA                0x00000100
+#define DMA_PCPCI               0x00000200
+#define DMA_WBDMA16             0x00000400
+#define DMA_WBDMA4              0x00000500
+#define DMA_WBDMA2              0x00000600
+#define DMA_WBDMA1              0x00000700
+#define DMA_SAFE_GUARD          0x00000800
+#define HI_PERF_GP_ENABLE       0x00001000
+#define PIC_SNOOP_MODE_0        0x00002000
+#define PIC_SNOOP_MODE_1        0x00004000
+#define SOUNDBLASTER_IRQ_MASK   0x00008000
+#define RING_IN_ENABLE          0x00010000
+#define SPDIF_TEST_MODE         0x00020000
+#define CLK_MULT_MODE_SELECT_2  0x00040000
+#define EEPROM_WRITE_ENABLE     0x00080000
+#define CODEC_DIR_IN            0x00100000
+#define HV_BUTTON_FROM_GD       0x00200000
+#define REDUCED_DEBOUNCE        0x00400000
+#define HV_CTRL_ENABLE          0x00800000
+#define SPDIF_ENABLE            0x01000000
+#define CLK_DIV_SELECT          0x06000000
+#define CLK_DIV_BY_48           0x00000000
+#define CLK_DIV_BY_49           0x02000000
+#define CLK_DIV_BY_50           0x04000000
+#define CLK_DIV_RESERVED        0x06000000
+#define PM_CTRL_ENABLE          0x08000000
+#define CLK_MULT_MODE_SELECT    0x30000000
+#define CLK_MULT_MODE_SHIFT     28
+#define CLK_MULT_MODE_0         0x00000000
+#define CLK_MULT_MODE_1         0x10000000
+#define CLK_MULT_MODE_2         0x20000000
+#define CLK_MULT_MODE_3         0x30000000
+#define INT_CLK_SELECT          0x40000000
+#define INT_CLK_MULT_RESET      0x80000000
+
+// M3
+#define INT_CLK_SRC_NOT_PCI     0x00100000
+#define INT_CLK_MULT_ENABLE     0x80000000
+
+#define PCI_ACPI_CONTROL        0x54
+#define PCI_ACPI_D0             0x00000000
+#define PCI_ACPI_D1             0xB4F70000
+#define PCI_ACPI_D2             0xB4F7B4F7
+
+#define PCI_USER_CONFIG         0x58
+#define EXT_PCI_MASTER_ENABLE   0x00000001
+#define SPDIF_OUT_SELECT        0x00000002
+#define TEST_PIN_DIR_CTRL       0x00000004
+#define AC97_CODEC_TEST         0x00000020
+#define TRI_STATE_BUFFER        0x00000080
+#define IN_CLK_12MHZ_SELECT     0x00000100
+#define MULTI_FUNC_DISABLE      0x00000200
+#define EXT_MASTER_PAIR_SEL     0x00000400
+#define PCI_MASTER_SUPPORT      0x00000800
+#define STOP_CLOCK_ENABLE       0x00001000
+#define EAPD_DRIVE_ENABLE       0x00002000
+#define REQ_TRI_STATE_ENABLE    0x00004000
+#define REQ_LOW_ENABLE          0x00008000
+#define MIDI_1_ENABLE           0x00010000
+#define MIDI_2_ENABLE           0x00020000
+#define SB_AUDIO_SYNC           0x00040000
+#define HV_CTRL_TEST            0x00100000
+#define SOUNDBLASTER_TEST       0x00400000
+
+#define PCI_USER_CONFIG_C       0x5C
+
+#define PCI_DDMA_CTRL           0x60
+#define DDMA_ENABLE             0x00000001
+
+
+// Allegro registers
+#define HOST_INT_CTRL           0x18
+#define SB_INT_ENABLE           0x0001
+#define MPU401_INT_ENABLE       0x0002
+#define ASSP_INT_ENABLE         0x0010
+#define RING_INT_ENABLE         0x0020
+#define HV_INT_ENABLE           0x0040
+#define CLKRUN_GEN_ENABLE       0x0100
+#define HV_CTRL_TO_PME          0x0400
+#define SOFTWARE_RESET_ENABLE   0x8000
+
+/*
+ * should be using the above defines, probably.
+ */
+#define REGB_ENABLE_RESET               0x01
+#define REGB_STOP_CLOCK                 0x10
+
+#define HOST_INT_STATUS         0x1A
+#define SB_INT_PENDING          0x01
+#define MPU401_INT_PENDING      0x02
+#define ASSP_INT_PENDING        0x10
+#define RING_INT_PENDING        0x20
+#define HV_INT_PENDING          0x40
+
+#define HARDWARE_VOL_CTRL       0x1B
+#define SHADOW_MIX_REG_VOICE    0x1C
+#define HW_VOL_COUNTER_VOICE    0x1D
+#define SHADOW_MIX_REG_MASTER   0x1E
+#define HW_VOL_COUNTER_MASTER   0x1F
+
+#define CODEC_COMMAND           0x30
+#define CODEC_READ_B            0x80
+
+#define CODEC_STATUS            0x30
+#define CODEC_BUSY_B            0x01
+
+#define CODEC_DATA              0x32
+
+#define RING_BUS_CTRL_A         0x36
+#define RAC_PME_ENABLE          0x0100
+#define RAC_SDFS_ENABLE         0x0200
+#define LAC_PME_ENABLE          0x0400
+#define LAC_SDFS_ENABLE         0x0800
+#define SERIAL_AC_LINK_ENABLE   0x1000
+#define IO_SRAM_ENABLE          0x2000
+#define IIS_INPUT_ENABLE        0x8000
+
+#define RING_BUS_CTRL_B         0x38
+#define SECOND_CODEC_ID_MASK    0x0003
+#define SPDIF_FUNC_ENABLE       0x0010
+#define SECOND_AC_ENABLE        0x0020
+#define SB_MODULE_INTF_ENABLE   0x0040
+#define SSPE_ENABLE             0x0040
+#define M3I_DOCK_ENABLE         0x0080
+
+#define SDO_OUT_DEST_CTRL       0x3A
+#define COMMAND_ADDR_OUT        0x0003
+#define PCM_LR_OUT_LOCAL        0x0000
+#define PCM_LR_OUT_REMOTE       0x0004
+#define PCM_LR_OUT_MUTE         0x0008
+#define PCM_LR_OUT_BOTH         0x000C
+#define LINE1_DAC_OUT_LOCAL     0x0000
+#define LINE1_DAC_OUT_REMOTE    0x0010
+#define LINE1_DAC_OUT_MUTE      0x0020
+#define LINE1_DAC_OUT_BOTH      0x0030
+#define PCM_CLS_OUT_LOCAL       0x0000
+#define PCM_CLS_OUT_REMOTE      0x0040
+#define PCM_CLS_OUT_MUTE        0x0080
+#define PCM_CLS_OUT_BOTH        0x00C0
+#define PCM_RLF_OUT_LOCAL       0x0000
+#define PCM_RLF_OUT_REMOTE      0x0100
+#define PCM_RLF_OUT_MUTE        0x0200
+#define PCM_RLF_OUT_BOTH        0x0300
+#define LINE2_DAC_OUT_LOCAL     0x0000
+#define LINE2_DAC_OUT_REMOTE    0x0400
+#define LINE2_DAC_OUT_MUTE      0x0800
+#define LINE2_DAC_OUT_BOTH      0x0C00
+#define HANDSET_OUT_LOCAL       0x0000
+#define HANDSET_OUT_REMOTE      0x1000
+#define HANDSET_OUT_MUTE        0x2000
+#define HANDSET_OUT_BOTH        0x3000
+#define IO_CTRL_OUT_LOCAL       0x0000
+#define IO_CTRL_OUT_REMOTE      0x4000
+#define IO_CTRL_OUT_MUTE        0x8000
+#define IO_CTRL_OUT_BOTH        0xC000
+
+#define SDO_IN_DEST_CTRL        0x3C
+#define STATUS_ADDR_IN          0x0003
+#define PCM_LR_IN_LOCAL         0x0000
+#define PCM_LR_IN_REMOTE        0x0004
+#define PCM_LR_RESERVED         0x0008
+#define PCM_LR_IN_BOTH          0x000C
+#define LINE1_ADC_IN_LOCAL      0x0000
+#define LINE1_ADC_IN_REMOTE     0x0010
+#define LINE1_ADC_IN_MUTE       0x0020
+#define MIC_ADC_IN_LOCAL        0x0000
+#define MIC_ADC_IN_REMOTE       0x0040
+#define MIC_ADC_IN_MUTE         0x0080
+#define LINE2_DAC_IN_LOCAL      0x0000
+#define LINE2_DAC_IN_REMOTE     0x0400
+#define LINE2_DAC_IN_MUTE       0x0800
+#define HANDSET_IN_LOCAL        0x0000
+#define HANDSET_IN_REMOTE       0x1000
+#define HANDSET_IN_MUTE         0x2000
+#define IO_STATUS_IN_LOCAL      0x0000
+#define IO_STATUS_IN_REMOTE     0x4000
+
+#define SPDIF_IN_CTRL           0x3E
+#define SPDIF_IN_ENABLE         0x0001
+
+#define GPIO_DATA               0x60
+#define GPIO_DATA_MASK          0x0FFF
+#define GPIO_HV_STATUS          0x3000
+#define GPIO_PME_STATUS         0x4000
+
+#define GPIO_MASK               0x64
+#define GPIO_DIRECTION          0x68
+#define GPO_PRIMARY_AC97        0x0001
+#define GPI_LINEOUT_SENSE       0x0004
+#define GPO_SECONDARY_AC97      0x0008
+#define GPI_VOL_DOWN            0x0010
+#define GPI_VOL_UP              0x0020
+#define GPI_IIS_CLK             0x0040
+#define GPI_IIS_LRCLK           0x0080
+#define GPI_IIS_DATA            0x0100
+#define GPI_DOCKING_STATUS      0x0100
+#define GPI_HEADPHONE_SENSE     0x0200
+#define GPO_EXT_AMP_SHUTDOWN    0x1000
+
+// M3
+#define GPO_M3_EXT_AMP_SHUTDN   0x0002
+
+#define ASSP_INDEX_PORT         0x80
+#define ASSP_MEMORY_PORT        0x82
+#define ASSP_DATA_PORT          0x84
+
+#define MPU401_DATA_PORT        0x98
+#define MPU401_STATUS_PORT      0x99
+
+#define CLK_MULT_DATA_PORT      0x9C
+
+#define ASSP_CONTROL_A          0xA2
+#define ASSP_0_WS_ENABLE        0x01
+#define ASSP_CTRL_A_RESERVED1   0x02
+#define ASSP_CTRL_A_RESERVED2   0x04
+#define ASSP_CLK_49MHZ_SELECT   0x08
+#define FAST_PLU_ENABLE         0x10
+#define ASSP_CTRL_A_RESERVED3   0x20
+#define DSP_CLK_36MHZ_SELECT    0x40
+
+#define ASSP_CONTROL_B          0xA4
+#define RESET_ASSP              0x00
+#define RUN_ASSP                0x01
+#define ENABLE_ASSP_CLOCK       0x00
+#define STOP_ASSP_CLOCK         0x10
+#define RESET_TOGGLE            0x40
+
+#define ASSP_CONTROL_C          0xA6
+#define ASSP_HOST_INT_ENABLE    0x01
+#define FM_ADDR_REMAP_DISABLE   0x02
+#define HOST_WRITE_PORT_ENABLE  0x08
+
+#define ASSP_HOST_INT_STATUS    0xAC
+#define DSP2HOST_REQ_PIORECORD  0x01
+#define DSP2HOST_REQ_I2SRATE    0x02
+#define DSP2HOST_REQ_TIMER      0x04
+
+// AC97 registers
+// XXX fix this crap up
+/*#define AC97_RESET              0x00*/
+
+#define AC97_VOL_MUTE_B         0x8000
+#define AC97_VOL_M              0x1F
+#define AC97_LEFT_VOL_S         8
+
+#define AC97_MASTER_VOL         0x02
+#define AC97_LINE_LEVEL_VOL     0x04
+#define AC97_MASTER_MONO_VOL    0x06
+#define AC97_PC_BEEP_VOL        0x0A
+#define AC97_PC_BEEP_VOL_M      0x0F
+#define AC97_SROUND_MASTER_VOL  0x38
+#define AC97_PC_BEEP_VOL_S      1
+
+/*#define AC97_PHONE_VOL          0x0C
+#define AC97_MIC_VOL            0x0E*/
+#define AC97_MIC_20DB_ENABLE    0x40
+
+/*#define AC97_LINEIN_VOL         0x10
+#define AC97_CD_VOL             0x12
+#define AC97_VIDEO_VOL          0x14
+#define AC97_AUX_VOL            0x16*/
+#define AC97_PCM_OUT_VOL        0x18
+/*#define AC97_RECORD_SELECT      0x1A*/
+#define AC97_RECORD_MIC         0x00
+#define AC97_RECORD_CD          0x01
+#define AC97_RECORD_VIDEO       0x02
+#define AC97_RECORD_AUX         0x03
+#define AC97_RECORD_MONO_MUX    0x02
+#define AC97_RECORD_DIGITAL     0x03
+#define AC97_RECORD_LINE        0x04
+#define AC97_RECORD_STEREO      0x05
+#define AC97_RECORD_MONO        0x06
+#define AC97_RECORD_PHONE       0x07
+
+/*#define AC97_RECORD_GAIN        0x1C*/
+#define AC97_RECORD_VOL_M       0x0F
+
+/*#define AC97_GENERAL_PURPOSE    0x20*/
+#define AC97_POWER_DOWN_CTRL    0x26
+#define AC97_ADC_READY          0x0001
+#define AC97_DAC_READY          0x0002
+#define AC97_ANALOG_READY       0x0004
+#define AC97_VREF_ON            0x0008
+#define AC97_PR0                0x0100
+#define AC97_PR1                0x0200
+#define AC97_PR2                0x0400
+#define AC97_PR3                0x0800
+#define AC97_PR4                0x1000
+
+#define AC97_RESERVED1          0x28
+
+#define AC97_VENDOR_TEST        0x5A
+
+#define AC97_CLOCK_DELAY        0x5C
+#define AC97_LINEOUT_MUX_SEL    0x0001
+#define AC97_MONO_MUX_SEL       0x0002
+#define AC97_CLOCK_DELAY_SEL    0x1F
+#define AC97_DAC_CDS_SHIFT      6
+#define AC97_ADC_CDS_SHIFT      11
+
+#define AC97_MULTI_CHANNEL_SEL  0x74
+
+/*#define AC97_VENDOR_ID1         0x7C
+#define AC97_VENDOR_ID2         0x7E*/
+
+/*
+ * ASSP control regs
+ */
+#define DSP_PORT_TIMER_COUNT    0x06
+
+#define DSP_PORT_MEMORY_INDEX   0x80
+
+#define DSP_PORT_MEMORY_TYPE    0x82
+#define MEMTYPE_INTERNAL_CODE   0x0002
+#define MEMTYPE_INTERNAL_DATA   0x0003
+#define MEMTYPE_MASK            0x0003
+
+#define DSP_PORT_MEMORY_DATA    0x84
+
+#define DSP_PORT_CONTROL_REG_A  0xA2
+#define DSP_PORT_CONTROL_REG_B  0xA4
+#define DSP_PORT_CONTROL_REG_C  0xA6
+
+#define REV_A_CODE_MEMORY_BEGIN         0x0000
+#define REV_A_CODE_MEMORY_END           0x0FFF
+#define REV_A_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_A_CODE_MEMORY_LENGTH        (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1)
+
+#define REV_B_CODE_MEMORY_BEGIN         0x0000
+#define REV_B_CODE_MEMORY_END           0x0BFF
+#define REV_B_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_B_CODE_MEMORY_LENGTH        (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1)
+
+#define REV_A_DATA_MEMORY_BEGIN         0x1000
+#define REV_A_DATA_MEMORY_END           0x2FFF
+#define REV_A_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_A_DATA_MEMORY_LENGTH        (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1)
+
+#define REV_B_DATA_MEMORY_BEGIN         0x1000
+#define REV_B_DATA_MEMORY_END           0x2BFF
+#define REV_B_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_B_DATA_MEMORY_LENGTH        (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1)
+
+
+#define NUM_UNITS_KERNEL_CODE          16
+#define NUM_UNITS_KERNEL_DATA           2
+
+#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16
+#define NUM_UNITS_KERNEL_DATA_WITH_HSP  5
+
+/*
+ * Kernel data layout
+ */
+
+#define DP_SHIFT_COUNT                  7
+
+#define KDATA_BASE_ADDR                 0x1000
+#define KDATA_BASE_ADDR2                0x1080
+
+#define KDATA_TASK0                     (KDATA_BASE_ADDR + 0x0000)
+#define KDATA_TASK1                     (KDATA_BASE_ADDR + 0x0001)
+#define KDATA_TASK2                     (KDATA_BASE_ADDR + 0x0002)
+#define KDATA_TASK3                     (KDATA_BASE_ADDR + 0x0003)
+#define KDATA_TASK4                     (KDATA_BASE_ADDR + 0x0004)
+#define KDATA_TASK5                     (KDATA_BASE_ADDR + 0x0005)
+#define KDATA_TASK6                     (KDATA_BASE_ADDR + 0x0006)
+#define KDATA_TASK7                     (KDATA_BASE_ADDR + 0x0007)
+#define KDATA_TASK_ENDMARK              (KDATA_BASE_ADDR + 0x0008)
+
+#define KDATA_CURRENT_TASK              (KDATA_BASE_ADDR + 0x0009)
+#define KDATA_TASK_SWITCH               (KDATA_BASE_ADDR + 0x000A)
+
+#define KDATA_INSTANCE0_POS3D           (KDATA_BASE_ADDR + 0x000B)
+#define KDATA_INSTANCE1_POS3D           (KDATA_BASE_ADDR + 0x000C)
+#define KDATA_INSTANCE2_POS3D           (KDATA_BASE_ADDR + 0x000D)
+#define KDATA_INSTANCE3_POS3D           (KDATA_BASE_ADDR + 0x000E)
+#define KDATA_INSTANCE4_POS3D           (KDATA_BASE_ADDR + 0x000F)
+#define KDATA_INSTANCE5_POS3D           (KDATA_BASE_ADDR + 0x0010)
+#define KDATA_INSTANCE6_POS3D           (KDATA_BASE_ADDR + 0x0011)
+#define KDATA_INSTANCE7_POS3D           (KDATA_BASE_ADDR + 0x0012)
+#define KDATA_INSTANCE8_POS3D           (KDATA_BASE_ADDR + 0x0013)
+#define KDATA_INSTANCE_POS3D_ENDMARK    (KDATA_BASE_ADDR + 0x0014)
+
+#define KDATA_INSTANCE0_SPKVIRT         (KDATA_BASE_ADDR + 0x0015)
+#define KDATA_INSTANCE_SPKVIRT_ENDMARK  (KDATA_BASE_ADDR + 0x0016)
+
+#define KDATA_INSTANCE0_SPDIF           (KDATA_BASE_ADDR + 0x0017)
+#define KDATA_INSTANCE_SPDIF_ENDMARK    (KDATA_BASE_ADDR + 0x0018)
+
+#define KDATA_INSTANCE0_MODEM           (KDATA_BASE_ADDR + 0x0019)
+#define KDATA_INSTANCE_MODEM_ENDMARK    (KDATA_BASE_ADDR + 0x001A)
+
+#define KDATA_INSTANCE0_SRC             (KDATA_BASE_ADDR + 0x001B)
+#define KDATA_INSTANCE1_SRC             (KDATA_BASE_ADDR + 0x001C)
+#define KDATA_INSTANCE_SRC_ENDMARK      (KDATA_BASE_ADDR + 0x001D)
+
+#define KDATA_INSTANCE0_MINISRC         (KDATA_BASE_ADDR + 0x001E)
+#define KDATA_INSTANCE1_MINISRC         (KDATA_BASE_ADDR + 0x001F)
+#define KDATA_INSTANCE2_MINISRC         (KDATA_BASE_ADDR + 0x0020)
+#define KDATA_INSTANCE3_MINISRC         (KDATA_BASE_ADDR + 0x0021)
+#define KDATA_INSTANCE_MINISRC_ENDMARK  (KDATA_BASE_ADDR + 0x0022)
+
+#define KDATA_INSTANCE0_CPYTHRU         (KDATA_BASE_ADDR + 0x0023)
+#define KDATA_INSTANCE1_CPYTHRU         (KDATA_BASE_ADDR + 0x0024)
+#define KDATA_INSTANCE_CPYTHRU_ENDMARK  (KDATA_BASE_ADDR + 0x0025)
+
+#define KDATA_CURRENT_DMA               (KDATA_BASE_ADDR + 0x0026)
+#define KDATA_DMA_SWITCH                (KDATA_BASE_ADDR + 0x0027)
+#define KDATA_DMA_ACTIVE                (KDATA_BASE_ADDR + 0x0028)
+
+#define KDATA_DMA_XFER0                 (KDATA_BASE_ADDR + 0x0029)
+#define KDATA_DMA_XFER1                 (KDATA_BASE_ADDR + 0x002A)
+#define KDATA_DMA_XFER2                 (KDATA_BASE_ADDR + 0x002B)
+#define KDATA_DMA_XFER3                 (KDATA_BASE_ADDR + 0x002C)
+#define KDATA_DMA_XFER4                 (KDATA_BASE_ADDR + 0x002D)
+#define KDATA_DMA_XFER5                 (KDATA_BASE_ADDR + 0x002E)
+#define KDATA_DMA_XFER6                 (KDATA_BASE_ADDR + 0x002F)
+#define KDATA_DMA_XFER7                 (KDATA_BASE_ADDR + 0x0030)
+#define KDATA_DMA_XFER8                 (KDATA_BASE_ADDR + 0x0031)
+#define KDATA_DMA_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0032)
+
+#define KDATA_I2S_SAMPLE_COUNT          (KDATA_BASE_ADDR + 0x0033)
+#define KDATA_I2S_INT_METER             (KDATA_BASE_ADDR + 0x0034)
+#define KDATA_I2S_ACTIVE                (KDATA_BASE_ADDR + 0x0035)
+
+#define KDATA_TIMER_COUNT_RELOAD        (KDATA_BASE_ADDR + 0x0036)
+#define KDATA_TIMER_COUNT_CURRENT       (KDATA_BASE_ADDR + 0x0037)
+
+#define KDATA_HALT_SYNCH_CLIENT         (KDATA_BASE_ADDR + 0x0038)
+#define KDATA_HALT_SYNCH_DMA            (KDATA_BASE_ADDR + 0x0039)
+#define KDATA_HALT_ACKNOWLEDGE          (KDATA_BASE_ADDR + 0x003A)
+
+#define KDATA_ADC1_XFER0                (KDATA_BASE_ADDR + 0x003B)
+#define KDATA_ADC1_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x003C)
+#define KDATA_ADC1_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x003D)
+#define KDATA_ADC1_RIGHT_VOLUME  		(KDATA_BASE_ADDR + 0x003E)
+#define KDATA_ADC1_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x003F)
+#define KDATA_ADC1_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0040)
+
+#define KDATA_ADC2_XFER0                (KDATA_BASE_ADDR + 0x0041)
+#define KDATA_ADC2_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x0042)
+#define KDATA_ADC2_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0043)
+#define KDATA_ADC2_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x0044)
+#define KDATA_ADC2_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x0045)
+#define KDATA_ADC2_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0046)
+
+#define KDATA_CD_XFER0					(KDATA_BASE_ADDR + 0x0047)					
+#define KDATA_CD_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x0048)
+#define KDATA_CD_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0049)
+#define KDATA_CD_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x004A)
+#define KDATA_CD_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x004B)
+#define KDATA_CD_RIGHT_SUR_VOL			(KDATA_BASE_ADDR + 0x004C)
+
+#define KDATA_MIC_XFER0					(KDATA_BASE_ADDR + 0x004D)
+#define KDATA_MIC_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x004E)
+#define KDATA_MIC_VOLUME				(KDATA_BASE_ADDR + 0x004F)
+#define KDATA_MIC_SUR_VOL				(KDATA_BASE_ADDR + 0x0050)
+
+#define KDATA_I2S_XFER0                 (KDATA_BASE_ADDR + 0x0051)
+#define KDATA_I2S_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0052)
+
+#define KDATA_CHI_XFER0                 (KDATA_BASE_ADDR + 0x0053)
+#define KDATA_CHI_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0054)
+
+#define KDATA_SPDIF_XFER                (KDATA_BASE_ADDR + 0x0055)
+#define KDATA_SPDIF_CURRENT_FRAME       (KDATA_BASE_ADDR + 0x0056)
+#define KDATA_SPDIF_FRAME0              (KDATA_BASE_ADDR + 0x0057)
+#define KDATA_SPDIF_FRAME1              (KDATA_BASE_ADDR + 0x0058)
+#define KDATA_SPDIF_FRAME2              (KDATA_BASE_ADDR + 0x0059)
+
+#define KDATA_SPDIF_REQUEST             (KDATA_BASE_ADDR + 0x005A)
+#define KDATA_SPDIF_TEMP                (KDATA_BASE_ADDR + 0x005B)
+
+#define KDATA_SPDIFIN_XFER0             (KDATA_BASE_ADDR + 0x005C)
+#define KDATA_SPDIFIN_XFER_ENDMARK      (KDATA_BASE_ADDR + 0x005D)
+#define KDATA_SPDIFIN_INT_METER         (KDATA_BASE_ADDR + 0x005E)
+
+#define KDATA_DSP_RESET_COUNT           (KDATA_BASE_ADDR + 0x005F)
+#define KDATA_DEBUG_OUTPUT              (KDATA_BASE_ADDR + 0x0060)
+
+#define KDATA_KERNEL_ISR_LIST           (KDATA_BASE_ADDR + 0x0061)
+
+#define KDATA_KERNEL_ISR_CBSR1          (KDATA_BASE_ADDR + 0x0062)
+#define KDATA_KERNEL_ISR_CBER1          (KDATA_BASE_ADDR + 0x0063)
+#define KDATA_KERNEL_ISR_CBCR           (KDATA_BASE_ADDR + 0x0064)
+#define KDATA_KERNEL_ISR_AR0            (KDATA_BASE_ADDR + 0x0065)
+#define KDATA_KERNEL_ISR_AR1            (KDATA_BASE_ADDR + 0x0066)
+#define KDATA_KERNEL_ISR_AR2            (KDATA_BASE_ADDR + 0x0067)
+#define KDATA_KERNEL_ISR_AR3            (KDATA_BASE_ADDR + 0x0068)
+#define KDATA_KERNEL_ISR_AR4            (KDATA_BASE_ADDR + 0x0069)
+#define KDATA_KERNEL_ISR_AR5            (KDATA_BASE_ADDR + 0x006A)
+#define KDATA_KERNEL_ISR_BRCR           (KDATA_BASE_ADDR + 0x006B)
+#define KDATA_KERNEL_ISR_PASR           (KDATA_BASE_ADDR + 0x006C)
+#define KDATA_KERNEL_ISR_PAER           (KDATA_BASE_ADDR + 0x006D)
+
+#define KDATA_CLIENT_SCRATCH0           (KDATA_BASE_ADDR + 0x006E)
+#define KDATA_CLIENT_SCRATCH1           (KDATA_BASE_ADDR + 0x006F)
+#define KDATA_KERNEL_SCRATCH            (KDATA_BASE_ADDR + 0x0070)
+#define KDATA_KERNEL_ISR_SCRATCH        (KDATA_BASE_ADDR + 0x0071)
+
+#define KDATA_OUEUE_LEFT                (KDATA_BASE_ADDR + 0x0072)
+#define KDATA_QUEUE_RIGHT               (KDATA_BASE_ADDR + 0x0073)
+
+#define KDATA_ADC1_REQUEST              (KDATA_BASE_ADDR + 0x0074)
+#define KDATA_ADC2_REQUEST              (KDATA_BASE_ADDR + 0x0075)
+#define KDATA_CD_REQUEST				(KDATA_BASE_ADDR + 0x0076)
+#define KDATA_MIC_REQUEST				(KDATA_BASE_ADDR + 0x0077)
+
+#define KDATA_ADC1_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0078)
+#define KDATA_ADC2_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0079)
+#define KDATA_CD_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007A)
+#define KDATA_MIC_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007B)
+#define KDATA_MIC_SYNC_COUNTER			(KDATA_BASE_ADDR + 0x007C)
+
+/*
+ * second 'segment' (?) reserved for mixer
+ * buffers..
+ */
+
+#define KDATA_MIXER_WORD0               (KDATA_BASE_ADDR2 + 0x0000)
+#define KDATA_MIXER_WORD1               (KDATA_BASE_ADDR2 + 0x0001)
+#define KDATA_MIXER_WORD2               (KDATA_BASE_ADDR2 + 0x0002)
+#define KDATA_MIXER_WORD3               (KDATA_BASE_ADDR2 + 0x0003)
+#define KDATA_MIXER_WORD4               (KDATA_BASE_ADDR2 + 0x0004)
+#define KDATA_MIXER_WORD5               (KDATA_BASE_ADDR2 + 0x0005)
+#define KDATA_MIXER_WORD6               (KDATA_BASE_ADDR2 + 0x0006)
+#define KDATA_MIXER_WORD7               (KDATA_BASE_ADDR2 + 0x0007)
+#define KDATA_MIXER_WORD8               (KDATA_BASE_ADDR2 + 0x0008)
+#define KDATA_MIXER_WORD9               (KDATA_BASE_ADDR2 + 0x0009)
+#define KDATA_MIXER_WORDA               (KDATA_BASE_ADDR2 + 0x000A)
+#define KDATA_MIXER_WORDB               (KDATA_BASE_ADDR2 + 0x000B)
+#define KDATA_MIXER_WORDC               (KDATA_BASE_ADDR2 + 0x000C)
+#define KDATA_MIXER_WORDD               (KDATA_BASE_ADDR2 + 0x000D)
+#define KDATA_MIXER_WORDE               (KDATA_BASE_ADDR2 + 0x000E)
+#define KDATA_MIXER_WORDF               (KDATA_BASE_ADDR2 + 0x000F)
+
+#define KDATA_MIXER_XFER0               (KDATA_BASE_ADDR2 + 0x0010)
+#define KDATA_MIXER_XFER1               (KDATA_BASE_ADDR2 + 0x0011)
+#define KDATA_MIXER_XFER2               (KDATA_BASE_ADDR2 + 0x0012)
+#define KDATA_MIXER_XFER3               (KDATA_BASE_ADDR2 + 0x0013)
+#define KDATA_MIXER_XFER4               (KDATA_BASE_ADDR2 + 0x0014)
+#define KDATA_MIXER_XFER5               (KDATA_BASE_ADDR2 + 0x0015)
+#define KDATA_MIXER_XFER6               (KDATA_BASE_ADDR2 + 0x0016)
+#define KDATA_MIXER_XFER7               (KDATA_BASE_ADDR2 + 0x0017)
+#define KDATA_MIXER_XFER8               (KDATA_BASE_ADDR2 + 0x0018)
+#define KDATA_MIXER_XFER9               (KDATA_BASE_ADDR2 + 0x0019)
+#define KDATA_MIXER_XFER_ENDMARK        (KDATA_BASE_ADDR2 + 0x001A)
+
+#define KDATA_MIXER_TASK_NUMBER         (KDATA_BASE_ADDR2 + 0x001B)
+#define KDATA_CURRENT_MIXER             (KDATA_BASE_ADDR2 + 0x001C)
+#define KDATA_MIXER_ACTIVE              (KDATA_BASE_ADDR2 + 0x001D)
+#define KDATA_MIXER_BANK_STATUS         (KDATA_BASE_ADDR2 + 0x001E)
+#define KDATA_DAC_LEFT_VOLUME	        (KDATA_BASE_ADDR2 + 0x001F)
+#define KDATA_DAC_RIGHT_VOLUME          (KDATA_BASE_ADDR2 + 0x0020)
+
+#define MAX_INSTANCE_MINISRC            (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC)
+#define MAX_VIRTUAL_DMA_CHANNELS        (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0)
+#define MAX_VIRTUAL_MIXER_CHANNELS      (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0)
+#define MAX_VIRTUAL_ADC1_CHANNELS       (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0)
+
+/*
+ * client data area offsets
+ */
+#define CDATA_INSTANCE_READY            0x00
+
+#define CDATA_HOST_SRC_ADDRL            0x01
+#define CDATA_HOST_SRC_ADDRH            0x02
+#define CDATA_HOST_SRC_END_PLUS_1L      0x03
+#define CDATA_HOST_SRC_END_PLUS_1H      0x04
+#define CDATA_HOST_SRC_CURRENTL         0x05
+#define CDATA_HOST_SRC_CURRENTH         0x06
+
+#define CDATA_IN_BUF_CONNECT            0x07
+#define CDATA_OUT_BUF_CONNECT           0x08
+
+#define CDATA_IN_BUF_BEGIN              0x09
+#define CDATA_IN_BUF_END_PLUS_1         0x0A
+#define CDATA_IN_BUF_HEAD               0x0B
+#define CDATA_IN_BUF_TAIL               0x0C
+#define CDATA_OUT_BUF_BEGIN             0x0D
+#define CDATA_OUT_BUF_END_PLUS_1        0x0E
+#define CDATA_OUT_BUF_HEAD              0x0F
+#define CDATA_OUT_BUF_TAIL              0x10
+
+#define CDATA_DMA_CONTROL               0x11
+#define CDATA_RESERVED                  0x12
+
+#define CDATA_FREQUENCY                 0x13
+#define CDATA_LEFT_VOLUME               0x14
+#define CDATA_RIGHT_VOLUME              0x15
+#define CDATA_LEFT_SUR_VOL              0x16
+#define CDATA_RIGHT_SUR_VOL             0x17
+
+#define CDATA_HEADER_LEN                0x18
+
+#define SRC3_DIRECTION_OFFSET           CDATA_HEADER_LEN
+#define SRC3_MODE_OFFSET                (CDATA_HEADER_LEN + 1)
+#define SRC3_WORD_LENGTH_OFFSET         (CDATA_HEADER_LEN + 2)
+#define SRC3_PARAMETER_OFFSET           (CDATA_HEADER_LEN + 3)
+#define SRC3_COEFF_ADDR_OFFSET          (CDATA_HEADER_LEN + 8)
+#define SRC3_FILTAP_ADDR_OFFSET         (CDATA_HEADER_LEN + 10)
+#define SRC3_TEMP_INBUF_ADDR_OFFSET     (CDATA_HEADER_LEN + 16)
+#define SRC3_TEMP_OUTBUF_ADDR_OFFSET    (CDATA_HEADER_LEN + 17)
+
+#define MINISRC_IN_BUFFER_SIZE   ( 0x50 * 2 )
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_TMP_BUFFER_SIZE  ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
+#define MINISRC_BIQUAD_STAGE    2
+#define MINISRC_COEF_LOC          0X175
+
+#define DMACONTROL_BLOCK_MASK           0x000F
+#define  DMAC_BLOCK0_SELECTOR           0x0000
+#define  DMAC_BLOCK1_SELECTOR           0x0001
+#define  DMAC_BLOCK2_SELECTOR           0x0002
+#define  DMAC_BLOCK3_SELECTOR           0x0003
+#define  DMAC_BLOCK4_SELECTOR           0x0004
+#define  DMAC_BLOCK5_SELECTOR           0x0005
+#define  DMAC_BLOCK6_SELECTOR           0x0006
+#define  DMAC_BLOCK7_SELECTOR           0x0007
+#define  DMAC_BLOCK8_SELECTOR           0x0008
+#define  DMAC_BLOCK9_SELECTOR           0x0009
+#define  DMAC_BLOCKA_SELECTOR           0x000A
+#define  DMAC_BLOCKB_SELECTOR           0x000B
+#define  DMAC_BLOCKC_SELECTOR           0x000C
+#define  DMAC_BLOCKD_SELECTOR           0x000D
+#define  DMAC_BLOCKE_SELECTOR           0x000E
+#define  DMAC_BLOCKF_SELECTOR           0x000F
+#define DMACONTROL_PAGE_MASK            0x00F0
+#define  DMAC_PAGE0_SELECTOR            0x0030
+#define  DMAC_PAGE1_SELECTOR            0x0020
+#define  DMAC_PAGE2_SELECTOR            0x0010
+#define  DMAC_PAGE3_SELECTOR            0x0000
+#define DMACONTROL_AUTOREPEAT           0x1000
+#define DMACONTROL_STOPPED              0x2000
+#define DMACONTROL_DIRECTION            0x0100
+
+
+/*
+ * DSP Code images
+ */
+
+u16 assp_kernel_image[] = {
+    0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, 
+    0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, 
+    0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, 
+    0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, 
+    0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, 
+    0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, 
+    0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, 
+    0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, 
+    0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, 
+    0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, 
+    0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, 
+    0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, 
+    0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, 
+    0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, 
+    0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, 
+    0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, 
+    0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, 
+    0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, 
+    0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, 
+    0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, 
+    0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, 
+    0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, 
+    0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, 
+    0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, 
+    0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, 
+    0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, 
+    0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, 
+    0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, 
+    0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, 
+    0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, 
+    0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, 
+    0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 
+    0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, 
+    0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, 
+    0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, 
+    0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, 
+    0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 
+    0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, 
+    0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, 
+    0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, 
+    0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, 
+    0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, 
+    0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 
+    0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, 
+    0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, 
+    0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, 
+    0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 
+    0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, 
+    0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, 
+    0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, 
+    0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, 
+    0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, 
+    0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, 
+    0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, 
+    0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, 
+    0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 
+    0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, 
+    0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 
+    0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, 
+    0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, 
+    0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, 
+    0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, 
+    0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, 
+    0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, 
+    0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, 
+    0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, 
+    0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, 
+    0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, 
+    0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, 
+    0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, 
+    0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, 
+    0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, 
+    0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, 
+    0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, 
+    0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, 
+    0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, 
+    0xBE3A, 
+};
+
+/*
+ * Mini sample rate converter code image
+ * that is to be loaded at 0x400 on the DSP.
+ */
+u16 assp_minisrc_image[] = {
+
+    0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, 
+    0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, 
+    0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, 
+    0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, 
+    0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, 
+    0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, 
+    0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, 
+    0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, 
+    0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, 
+    0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, 
+    0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, 
+    0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, 
+    0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, 
+    0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, 
+    0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, 
+    0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, 
+    0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, 
+    0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, 
+    0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, 
+    0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, 
+    0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, 
+    0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, 
+    0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, 
+    0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, 
+    0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, 
+    0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, 
+    0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, 
+    0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, 
+    0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, 
+    0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, 
+    0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, 
+    0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+
diff -Nru linux/sound/oss/maestro_tables.h linux-2.4.19-pre5-mjc/sound/oss/maestro_tables.h
--- linux/sound/oss/maestro_tables.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/maestro_tables.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,333 @@
+/* 
+ *	Set up data block for the ESS soundblaster emulation. This is like
+ *	the example code from ftp.esstech.com.tw  (104T31.ZIP)
+ */
+
+u16 asp_block_0[]= {
+	0x7980, 0x003a, 0x7980, 0x007d, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 
+	0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 
+	0x7980, 0x0be6, 0x7980, 0x0069, 0xbe3a, 0x8b00, 0x8048, 0x6945, 
+	0xb801, 0x9045, 0x6941, 0xe388, 0x0031, 0x6944, 0xba50, 0xe38c, 
+	0x0031, 0x8b88, 0x6942, 0x304a, 0xe308, 0x0028, 0x6949, 0x9042, 
+	0x0042, 0xafa0, 0x8000, 0xafa0, 0x8000, 0x8042, 0x6944, 0xb801, 
+	0x9044, 0x0048, 0xbe3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0xbe41, 0xbf80, 0x0101, 0x8806, 0x8804, 0xb912, 
+	0x8807, 0xbc26, 0xbe40, 0xb92f, 0x9001, 0x9002, 0x003b, 0x0122, 
+	0x7a88, 0x0055, 0x003a, 0x013c, 0x7a88, 0x0055, 0x4902, 0xe100, 
+	0x0800, 0x0c02, 0xa000, 0x7980, 0x004e, 0xb908, 0x8809, 0xbec6, 
+	0x0067, 0x4a80, 0xe200, 0x0065, 0x4880, 0xe200, 0x0063, 0xb97f, 
+	0x6e80, 0x7980, 0x0066, 0x1089, 0x9088, 0xb900, 0x90a9, 0x8ba8, 
+	0xef00, 0x803d, 0x0003, 0x1007, 0x881f, 0x8b88, 0xbb0f, 0xada0, 
+	0x0810, 0x9003, 0x4903, 0xe200, 0x007b, 0x9002, 0x6a05, 0x6d04, 
+	0x9803, 0x9804, 0x9005, 0x003d, 0xbe3a, 0xaf06, 0x0000, 0x803d, 
+	0x8b88, 0x6906, 0x8810, 0xaf06, 0x0001, 0xbfb0, 0x00ff, 0xbaf5, 
+	0xe3cc, 0x009a, 0x5e06, 0x003f, 0xbf80, 0x0080, 0x4a06, 0xe200, 
+	0x0095, 0x6e80, 0x6d06, 0x7980, 0x009b, 0x9080, 0x0810, 0xba46, 
+	0x8810, 0x8b00, 0x1006, 0x9080, 0x003d, 0xbe3a, 0x1024, 0x2009, 
+	0x8810, 0x8b88, 0x8b00, 0x1089, 0xbfb0, 0x000e, 0xbe0a, 0x880d, 
+	0x903e, 0x1038, 0x2008, 0x8810, 0x1037, 0x2008, 0x8811, 0x5f3e, 
+	0x0000, 0x8b00, 0xf500, 0x5e80, 0xfffe, 0x1039, 0x2008, 0x8815, 
+	0x8b8d, 0x0231, 0x0333, 0x108a, 0x90ad, 0x1025, 0x200a, 0x8815, 
+	0x0605, 0xb91f, 0x8809, 0x4f16, 0x1080, 0xf600, 0xbfb0, 
+	0x0003, 0xbfb0, 0x0007, 0x9089, 0xbe47, 0xbe43, 0xbec6, 
+	0x00f6, 0x4f8b, 0x14a8, 0xe500, 0x6380, 0x8b89, 0x4e8a, 
+	0xbfe3, 0xe500, 0x2480, 0xbe46, 0x8b9d, 0xbfe7, 0xbfb0, 
+	0x00ff, 0x288c, 0x2026, 0x8814, 0xbe47, 0x8b00, 0x738f, 
+	0x54aa, 0xbe03, 0xbe0a, 0x2fa8, 0x988a, 0x8da9, 0xbe03, 
+	0x4d8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe03, 
+	0x4c8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe42, 
+	0x1039, 0x2008, 0x8815, 0x8b8d, 0x8b00, 0x8d8a, 0x7980, 
+	0x0bcf};
+
+u16 asp_block_1[]={
+	0x0005, 0xb900, 0x9008, 0x9009, 0x900a, 0xbb3f, 0x90a0,
+	0x001a, 0x011b, 0x021c, 0x0323, 0x1089, 0x9015, 0x108a,
+	0x9016, 0x108b, 0x9017, 0x1088, 0x9018, 0x0137, 0x0524, 
+	0x8b8d, 0x4f16, 0xe200, 0x0827, 0x4f15, 0xe200, 0x0827, 
+	0x7a80, 0x08e6, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb900, 
+	0x90a0, 0x908d, 0x7980, 0x0833, 0x7a80, 0x0913, 0x7803, 
+	0x7a80, 0x0913, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb903, 
+	0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, 0x0845, 0x4e15, 
+	0xe200, 0x0845, 0x7a80, 0x08e6, 0x102c, 0xb806, 0x8813, 
+	0x8b8b, 0xb901, 0x90a0, 0x908d, 0x7980, 0x0851, 0x7a80, 
+	0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb806, 0x8813, 
+	0x8b8b, 0xb904, 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, 
+	0x0863, 0x4d15, 0xe200, 0x0863, 0x7a80, 0x08e6, 0x102c, 
+	0xb80a, 0x8813, 0x8b8b, 0xb902, 0x90a0, 0x908d, 0x7980, 
+	0x086f, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 
+	0xb80a, 0x8813, 0x8b8b, 0xb905, 0x90a0, 0x908d, 0x7801, 
+	0x7a80, 0x0913, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, 
+	0x0913, 0x1024, 0xbf90, 0x0100, 0x8815, 0x4f16, 0xe200, 
+	0x088e, 0x4c15, 0xe200, 0x088e, 0x7a80, 0x08e6, 0x102c, 
+	0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0100, 0x90a0, 0x908d, 
+	0x7980, 0x089b, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, 
+	0x102c, 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0103, 0x90a0, 
+	0x908d, 0x7c02, 0x4f16, 0xe200, 0x08ae, 0x4b15, 0xe200, 
+	0x08ae, 0x7a80, 0x08e6, 0x102c, 0xb818, 0x8813, 0x8b8b, 
+	0xbf80, 0x0101, 0x90a0, 0x908d, 0x7980, 0x08bb, 0x7a80, 
+	0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb818, 0x8813, 
+	0x8b8b, 0xbf80, 0x0104, 0x90a0, 0x908d, 0x7c02, 0x4f16, 
+	0xe200, 0x08ce, 0x4a15, 0xe200, 0x08ce, 0x7a80, 0x08e6, 
+	0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0102, 0x90a0, 
+	0x908d, 0x7980, 0x08db, 0x7a80, 0x0913, 0x7803, 0x7a80, 
+	0x0913, 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0105, 
+	0x90a0, 0x908d, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, 
+	0x0913, 0x7801, 0x7a80, 0x0913, 0x7980, 0x0920, 0x4f80, 
+	0x7803, 0xe100, 0x08fe, 0x4f89, 0xe100, 0x08f5, 0xb901, 
+	0x90a0, 0xb902, 0x90a0, 0x90a0, 0xb906, 0x90ad, 0xef00, 
+	0xb901, 0x90a0, 0xb906, 0x90a0, 0xb900, 0x90a0, 0xb906, 
+	0x90ad, 0xef00, 0x4f89, 0xe100, 0x090a, 0xb905, 0x90a0, 
+	0xb900, 0x90a0, 0xb902, 0x90a0, 0xb906, 0x90ad, 0xef00, 
+	0xb905, 0x90a0, 0xb900, 0x90a0, 0xb906, 0x90a0, 0xb904, 
+	0x90ad, 0xef00, 0x4f89, 0xe100, 0x091b, 0xb901, 0x90a0, 
+	0xb906, 0x90ad, 0xef00, 0xb905, 0x90a0, 0xb904, 0x90ad, 
+	0xef00, 0xb91f, 0x8809, 0x0034, 0x8b88, 0xbec6, 0x0932, 
+	0x1313, 0xbe1e, 0x1014, 0xbe1a, 0xbe01, 0xbfe8, 0xbe17, 
+	0x6a13, 0x6214, 0xbe14, 0x9813, 0x9014, 0x98a0, 0xbe47, 
+	0x5f0f, 0x002e, 0xe200, 0x093d, 0xbf80, 0xffd2, 0x900f, 
+	0x7980, 0x0940, 0x100f, 0xb801, 0x900f, 0x400f, 0x8b00, 
+	0xe500, 0xbe01, 0xbe09, 0x9010, 0xbe46, 0x5f11, 0x003f, 
+	0xe200, 0x094f, 0xb900, 0x9011, 0x7980, 0x0952, 0x1011, 
+	0xb801, 0x9011, 0x1001, 0xe388, 0x0bcf, 0x1021, 0x2009, 
+	0x8813, 0x8b8b, 0x8b00, 0x1080, 0xbfe6, 0x7810, 0x8b00, 
+	0x8b00, 0x2180, 0xbfb0, 0x0007, 0x4c11, 0x8b00, 0xe100, 
+	0x096c, 0x4b11, 0x8b00, 0xe600, 0xb900, 0x7980, 0x096d, 
+	0xbe0a, 0x4a11, 0x8b00, 0xe500, 0xbe01, 0x9012, 0x1037, 
+	0x2008, 0x8811, 0x102e, 0x2008, 0x8812, 0x4a89, 0xb901, 
+	0xe500, 0x9019, 0xe100, 0x09c9, 0xb900, 0x9019, 0x4a18, 
+	0xe200, 0x09c9, 0x5f0a, 0x0010, 0xe200, 0x098d, 0x4b18, 
+	0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0013, 
+	0xe200, 0x0997, 0x4b18, 0xb901, 0xe500, 0x9019, 0x7980, 
+	0x09c9, 0x5f0a, 0x0011, 0xe200, 0x09a4, 0x4f18, 0xb905, 
+	0xe500, 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 
+	0x5f0a, 0x0014, 0xe200, 0x09b1, 0x4c18, 0xb904, 0xe500, 
+	0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 
+	0x0012, 0xe200, 0x09be, 0x4d18, 0xb905, 0xe500, 0x9080, 
+	0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0015, 
+	0xe200, 0x09c9, 0x4e18, 0xb904, 0xe500, 0x9080, 0xb901, 
+	0xe500, 0x9019, 0x8b8a, 0x4f19, 0xe200, 0x09d4, 0x4b80, 
+	0xe200, 0x09d6, 0x5d80, 0x0020, 0x7980, 0x09d9, 0x5d80, 
+	0x0010, 0x4a80, 0xe200, 0x0bca, 0x1001, 0xba01, 0x9001, 
+	0x1024, 0x2009, 0x8815, 0x8b89, 0x4d8d, 0xe200, 0x09f8, 
+	0x4f16, 0xe100, 0x09ed, 0x8b89, 0x1080, 0xbfc0, 0x000c, 
+	0x908c, 0x7980, 0x09f8, 0x4b89, 0x108d, 0xf600, 0xbfb0, 
+	0x0003, 0x4a89, 0x8b00, 0xf500, 0xbfc0, 0x0008, 0x908c, 
+	0x1022, 0x2009, 0x8811, 0x102e, 0x2008, 0x8812, 0x102f, 
+	0x2008, 0x8813, 0x1020, 0x200a, 0x8814, 0x101d, 0x200a, 
+	0x8815, 0x8b8a, 0x4f19, 0xe100, 0x0a11, 0x5e80, 0x0020, 
+	0x5d80, 0x0018, 0x7980, 0x0a3e, 0x4f80, 0xe200, 0x0a1e, 
+	0x8b8b, 0x108a, 0xbfa0, 0x7fc0, 0x8b00, 0xf704, 0x5c80, 
+	0x0003, 0x7980, 0x0a3e, 0x4e80, 0xe200, 0x0a36, 0x8b8c, 
+	0x178b, 0xbe01, 0xbfb7, 0x00f0, 0x308a, 0xe344, 0x0a3e, 
+	0x8b8d, 0x4a8a, 0x8b00, 0xe200, 0x0a32, 0x5c80, 0x0006, 
+	0x7980, 0x0a3e, 0x5c80, 0x000a, 0x7980, 0x0a3e, 0x4c80,
+	0xe200, 0x0a3e, 0x4b80, 0x8b00, 0xf500, 0x5c80, 0x0019, 
+	0x4f80, 0xe200, 0x0a4d, 0x101f, 0x200a, 0x8816, 0x8b00, 
+	0x8b00, 0x8b8e, 0x1780, 0xbfb7, 0x00f0, 0x900b, 0x7980, 
+	0x0a64, 0x4e80, 0xe200, 0x0a5c, 0x101f, 0x200a, 0x8816, 
+	0x8b00, 0x8b00, 0x8b8e, 0x1b80, 0xbfbb, 0x000f, 0x900b, 
+	0x7980, 0x0a64, 0x4c80, 0xe200, 0x0a64, 0x8b8c, 0x1b80, 
+	0xbfbb, 0x000f, 0x900b, 0x8b89, 0x1880, 0xbfb8, 0x001c, 
+	0x4917, 0xe100, 0x0a6e, 0x4f80, 0x7980, 0x0a70, 0x4e80, 
+	0x8b00, 0xf500, 0xbf98, 0x0002, 0x8b8d, 0x4b89, 0x8b00, 
+	0xe600, 0xbfe1, 0x903e, 0xbe43, 0x6a0b, 0x613e, 0xbfe8, 
+	0x980c, 0xbe42, 0x7c10, 0x1080, 0xbfe5, 0xbe1e, 0x7810, 
+	0x1280, 0xbfb2, 0x001f, 0xbe11, 0x2027, 0x8811, 0x101e, 
+	0x200a, 0x8816, 0x8b00, 0x128e, 0x4980, 0xe100, 0x0a9b, 
+	0x4880, 0xe100, 0x0a98, 0xb900, 0x7980, 0x0a9f, 0xbe0a, 
+	0x7980, 0x0a9f, 0x4880, 0xe200, 0x0a9f, 0xbe09, 0xbe1e, 
+	0x158d, 0xbfb5, 0x003f, 0xbe11, 0x4889, 0xe200, 0x0aae, 
+	0xbe1e, 0x1010, 0x4818, 0x8b00, 0xe600, 0xbfe1, 0xbe11, 
+	0xbfe1, 0x8811, 0xbe1e, 0xb9ff, 0x8819, 0x8b00, 0x8b00, 
+	0xbf46, 0x8b00, 0xe600, 0xbe1f, 0xbe01, 0xbfb0, 0x00ff, 
+	0x202a, 0x8811, 0x8b00, 0x8b00, 0x108a, 0x900d, 0xb91f, 
+	0x8809, 0x0732, 0x730d, 0x4f80, 0xe200, 0x0ad8, 0x1028, 
+	0x200c, 0x8815, 0xbe43, 0x8b8b, 0x6a8d, 0xbec6, 0x0ad4, 
+	0x618b, 0x9880, 0x548f, 0x8dad, 0xbe42, 0x7980, 0x0b24, 
+	0x4e80, 0xe200, 0x0af9, 0x8b8c, 0x178b, 0xbe01, 0xbfb7, 
+	0x00f0, 0x903e, 0x1029, 0x200c, 0x8815, 0x108d, 0xbec6, 
+	0x0af6, 0x308b, 0xbe1e, 0x303e, 0x903f, 0x403f, 0xbe1f, 
+	0xe500, 0x103e, 0x9080, 0xbfe6, 0x202a, 0x8811, 0x8b00, 
+	0x1089, 0x548f, 0x8dad, 0x7980, 0x0b24, 0x4c8b, 0xe200, 
+	0x0b1b, 0x1029, 0x200c, 0x8815, 0xbf80, 0x007f, 0x903e, 
+	0x108d, 0xbec6, 0x0b14, 0x308b, 0xbe1e, 0x303e, 0x903f, 
+	0x403f, 0xbe1f, 0xe500, 0xb900, 0x9080, 0xbfe6, 0x202a, 
+	0x8811, 0x8b00, 0x1089, 0x548f, 0x8dad, 0x8b8a, 0xf500, 
+	0x5e80, 0xffdf, 0x7980, 0x0b24, 0x1089, 0xbfe6, 0x202a, 
+	0x8811, 0x8b00, 0x8b00, 0x548f, 0xbb1f, 0x8da0, 0x8b8f, 
+	0x0732, 0x1021, 0x2009, 0x8811, 0x8b89, 0x101d, 0x200a, 
+	0x8812, 0x1080, 0x7810, 0x8b00, 0xbe47, 0x288a, 0xbfb0, 
+	0x03ff, 0x4989, 0xe200, 0x0b3e, 0xbe1e, 0x1012, 0x4918, 
+	0x8b00, 0xe600, 0xbe0a, 0xbe11, 0x900e, 0xbe46, 0x108a, 
+	0xbfb0, 0x001c, 0xbfe1, 0x880d, 0x8b00, 0x6b0e, 0xbe0a, 
+	0x880c, 0x108c, 0xbfb0, 0x000f, 0x202d, 0x8814, 0x8b00, 
+	0x8b00, 0x5589, 0xbf03, 0x8c0e, 0xbf00, 0x1030, 0x2008, 
+	0x8811, 0xb91f, 0x8809, 0x108b, 0x0333, 0xbec6, 0x0b5f, 
+	0x200e, 0x90a0, 0x8b00, 0x8b89, 0x9080, 0x4a18, 0xe200, 
+	0x0b7a, 0x5f0a, 0x0011, 0xe200, 0x0b6c, 0x4f18, 0xe900, 
+	0x0b7c, 0x5f0a, 0x0014, 0xe200, 0x0b73, 0x4c18, 0xe900, 
+	0x0b9b, 0x5f0a, 0x0015, 0xe200, 0x0b7a, 0x4e18, 0xe900, 
+	0x0bb2, 0x7980, 0x009e, 0x0034, 0xb91f, 0x8809, 0x0333, 
+	0x8b8b, 0xbec6, 0x0b99, 0x1280, 0x6c80, 0xbfea, 0xbe1e, 
+	0x1580, 0x6c88, 0xbfec, 0xbe13, 0x903e, 0x6cab, 0x903f, 
+	0x4f3e, 0xb900, 0xf500, 0xbf80, 0x8000, 0x4f3f, 0xbf90, 
+	0x0d00, 0xf500, 0xbf90, 0x2700, 0x90a0, 0xef00, 0x0034, 
+	0xb91f, 0x8809, 0x0333, 0x8b88, 0xbec6, 0x0bb0, 0x1eab, 
+	0x6c80, 0xbfed, 0x903e, 0x4180, 0xb900, 0xf500, 0xbf80, 
+	0x8000, 0x4f3e, 0x8b00, 0xf500, 0xbf90, 0x4000, 0x90a8, 
+	0xef00, 0x0034, 0xb91f, 0x8809, 0x0333, 0x8b8b, 0xbec6, 
+	0x0bc8, 0x1280, 0x6c8b, 0xbfea, 0xbe1e, 0x1580, 0x6c80, 
+	0xbfec, 0xbe13, 0x903e, 0x4f3e, 0xbf80, 0x4000, 0xf500, 
+	0xbf90, 0x8000, 0x90a0, 0xef00, 0x0231, 0x8b8a, 0xb900, 
+	0xbb20, 0x90a0, 0x5f08, 0x0023, 0xe100, 0x0043, 0x1008, 
+	0xb801, 0x9008, 0x102b, 0x2008, 0x8812, 0x8b00, 0x8b00, 
+	0x1080, 0x900a, 0x102c, 0x2008, 0x8812, 0x8b00, 0x8b00, 
+	0x1080, 0x9009, 0x7980, 0x0952, 0x8148, 0x6946, 0xb801, 
+	0x9046, 0xb901, 0x9041, 0x6944, 0xba08, 0xe344, 0x0bfe, 
+	0x9044, 0x8b89, 0x0143, 0x694b, 0x881f, 0xbb0f, 0xada0, 
+	0x8143, 0x0811, 0x304a, 0xe308, 0x0bfe, 0x6949, 0x9043,
+	0x0148, 0xbe3a 
+}; 
+	
+u16 asp_block_2[]={ 
+	0x0000, 0x0000, 0x0003, 0x0003, 0x0001, 0x0001, 
+	0x0004, 0x0004, 0x0002, 0x0002, 0x0005, 0x0005, 
+	0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, 
+	0x0100, 0x0100, 0x0103, 0x0103, 0x0101, 0x0101, 
+	0x0104, 0x0104, 0x0102, 0x0102, 0x0105, 0x0105, 
+	0x0106, 0x0106, 0x0107, 0x0107, 0x0108, 0x0108
+};
+
+u16 asp_block_3[]={ 
+	0x0000, 0x0000, 0x0000, 0x1200, 0x1200, 0x1280, 0x0000, 0x05d0, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x1104, 0x1105, 0x1008, 0x1020, 0x1040, 0x1060, 
+	0x1080, 0x10a0, 0x10b0, 0x100d, 0x1010, 0x10e0, 0x2000, 0x2980, 
+	0x2b00, 0x2b40, 0x2a00, 0x2b90, 0x13dc, 0x2b80, 0x11bc, 0x134c, 
+	0x1370, 0x12e0, 0x1240, 0x1260, 0x12c0, 0x009e, 0x0045, 0x10bc, 
+	0x1394, 0x13b8, 0x11f6, 0x10f6, 0x11b0, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0815, 0x0956, 0x09df, 0x0be5, 0x0a19, 0x0a48, 0x0b37,
+	0x0b5d, 0x0a8b, 0x0aae, 0x0ad2 }; 
+
+
+u16 asp_block_4[] = {
+	0x0192, 0x04b6, 0x07d9, 0x0afb, 0x0e1c, 0x113a, 0x1455, 0x176e,
+	0x1a83, 0x1d93, 0x209f, 0x23a7, 0x26a8, 0x29a4, 0x2c99, 0x2f87,
+	0x326e, 0x354e, 0x3825, 0x3af3, 0x3db8, 0x4074, 0x4326, 0x45cd,
+	0x486a, 0x4afb, 0x4d81, 0x4ffb, 0x5269, 0x54ca, 0x571e, 0x5964,
+	0x5b9d, 0x5dc8, 0x5fe4, 0x61f1, 0x63ef, 0x65de, 0x67bd, 0x698c,
+	0x6b4b, 0x6cf9, 0x6e97, 0x7023, 0x719e, 0x7308, 0x7460, 0x75a6,
+	0x76d9, 0x77fb, 0x790a, 0x7a06, 0x7aef, 0x7bc6, 0x7c89, 0x7d3a,
+	0x7dd6, 0x7e60, 0x7ed6, 0x7f38, 0x7f87, 0x7fc2, 0x7fea, 0x7ffe,
+	0x7ffe, 0x7fea, 0x7fc2, 0x7f87, 0x7f38, 0x7ed6, 0x7e60, 0x7dd6,
+	0x7d3a, 0x7c89, 0x7bc6, 0x7aef, 0x7a06, 0x790a, 0x77fb, 0x76d9,
+	0x75a6, 0x7460, 0x7308, 0x719e, 0x7023, 0x6e97, 0x6cf9, 0x6b4b,
+	0x698c, 0x67bd, 0x65de, 0x63ef, 0x61f1, 0x5fe4, 0x5dc8, 0x5b9d,
+	0x5964, 0x571e, 0x54ca, 0x5269, 0x4ffb, 0x4d81, 0x4afb, 0x486a,
+	0x45cd, 0x4326, 0x4074, 0x3db8, 0x3af3, 0x3825, 0x354e, 0x326e,
+	0x2f87, 0x2c99, 0x29a4, 0x26a8, 0x23a7, 0x209f, 0x1d93, 0x1a83,
+	0x176e, 0x1455, 0x113a, 0x0e1c, 0x0afb, 0x07d9, 0x04b6, 0x0192,
+	0xfe6f, 0xfb4b, 0xf828, 0xf506, 0xf1e5, 0xeec7, 0xebac, 0xe893,
+	0xe57e, 0xe26e, 0xdf62, 0xdc5a, 0xd959, 0xd65d, 0xd368, 0xd07a,
+	0xcd93, 0xcab3, 0xc7dc, 0xc50e, 0xc249, 0xbf8d, 0xbcdb, 0xba34,
+	0xb797, 0xb506, 0xb280, 0xb006, 0xad98, 0xab37, 0xa8e3, 0xa69d,
+	0xa464, 0xa239, 0xa01d, 0x9e10, 0x9c12, 0x9a23, 0x9844, 0x9675,
+	0x94b6, 0x9308, 0x916a, 0x8fde, 0x8e63, 0x8cf9, 0x8ba1, 0x8a5b,
+	0x8928, 0x8806, 0x86f7, 0x85fb, 0x8512, 0x843b, 0x8378, 0x82c7,
+	0x822b, 0x81a1, 0x812b, 0x80c9, 0x807a, 0x803f, 0x8017, 0x8003,
+	0x8003, 0x8017, 0x803f, 0x807a, 0x80c9, 0x812b, 0x81a1, 0x822b,
+	0x82c7, 0x8378, 0x843b, 0x8512, 0x85fb, 0x86f7, 0x8806, 0x8928,
+	0x8a5b, 0x8ba1, 0x8cf9, 0x8e63, 0x8fde, 0x916a, 0x9308, 0x94b6,
+	0x9675, 0x9844, 0x9a23, 0x9c12, 0x9e10, 0xa01d, 0xa239, 0xa464,
+	0xa69d, 0xa8e3, 0xab37, 0xad98, 0xb006, 0xb280, 0xb506, 0xb797,
+	0xba34, 0xbcdb, 0xbf8d, 0xc249, 0xc50e, 0xc7dc, 0xcab3, 0xcd93,
+	0xd07a, 0xd368, 0xd65d, 0xd959, 0xdc5a, 0xdf62, 0xe26e, 0xe57e,
+	0xe893, 0xebac, 0xeec7, 0xf1e5, 0xf506, 0xf828, 0xfb4b, 0xfe6f,
+	0x7cc3, 0x725e, 0x68d5, 0x6017, 0x5813, 0x50b9, 0x49fb, 0x43cd,
+	0x3e22, 0x38ef, 0x342b, 0x2fcc, 0x2bc9, 0x281c, 0x24be, 0x21a6,
+	0x1ed1, 0x1c37, 0x19d5, 0x17a6, 0x15a5, 0x13ce, 0x121f, 0x1093,
+	0x0f28, 0x0ddc, 0x0cab, 0x0b93, 0x0a92, 0x09a7, 0x08cf, 0x080a,
+	0x0754, 0x06ae, 0x0615, 0x0589, 0x0509, 0x0494, 0x0428, 0x03c5,
+	0x036a, 0x0317, 0x02cb, 0x0285, 0x0245, 0x020a, 0x01d4, 0x01a2,
+	0x0175, 0x014b, 0x0125, 0x0102, 0x00e2, 0x00c5, 0x00aa, 0x0091,
+	0x007b, 0x0066, 0x0053, 0x0041, 0x0031, 0x0022, 0x0015, 0x0009,
+	0xfffe, 0xfff2, 0xffe5, 0xffd7, 0xffc8, 0xffb7, 0xffa5, 0xff91,
+	0xff7b, 0xff64, 0xff4a, 0xff2e, 0xff0f, 0xfeee, 0xfec9, 0xfea1,
+	0xfe76, 0xfe46, 0xfe13, 0xfdda, 0xfd9d, 0xfd5a, 0xfd11, 0xfcc1,
+	0xfc6b, 0xfc0c, 0xfba5, 0xfb34, 0xfab9, 0xfa33, 0xf9a1, 0xf902,
+	0xf854, 0xf797, 0xf6c8, 0xf5e7, 0xf4f1, 0xf3e5, 0xf2c1, 0xf183,
+	0xf027, 0xeeac, 0xed0f, 0xeb4d, 0xe961, 0xe74a, 0xe501, 0xe284,
+	0xdfcd, 0xdcd8, 0xd99d, 0xd618, 0xd242, 0xce12, 0xc982, 0xc487,
+	0xbf1a, 0xb92e, 0xb2ba, 0xabaf, 0xa402, 0x9ba3, 0x9282, 0x888d,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, 0x0010,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x000a, 0x000e,
+	0x0010, 0x0014, 0x0016, 0x0018, 0x001a, 0x001c, 0x001e, 0x0020,
+	0x0000, 0x0000, 0x0000, 0x000a, 0x0010, 0x0016, 0x001a, 0x001e,
+	0x0020, 0x0024, 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030,
+	0x0000, 0x0000, 0x0010, 0x001a, 0x0020, 0x0026, 0x002a, 0x002e,
+	0x0030, 0x0034, 0x0036, 0x0038, 0x003a, 0x003c, 0x003e, 0x0040,
+	0x0000, 0x0010, 0x0020, 0x002a, 0x0030, 0x0036, 0x003a, 0x003e,
+	0x0040, 0x0044, 0x0046, 0x0048, 0x004a, 0x004c, 0x004e, 0x0050,
+	0x0000, 0x0020, 0x0030, 0x003a, 0x0040, 0x0046, 0x004a, 0x004e,
+	0x0050, 0x0054, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060,
+	0x0000, 0x0030, 0x0040, 0x004a, 0x0050, 0x0056, 0x005a, 0x005e,
+	0x0060, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070,
+	0x0008, 0x0021, 0x0042, 0x0064, 0x0086, 0x00a8, 0x00cb, 0x00ee,
+	0x0111, 0x0135, 0x0158, 0x017d, 0x01a1, 0x01c6, 0x01eb, 0x0211,
+	0x0236, 0x025d, 0x0283, 0x02aa, 0x02d1, 0x02f9, 0x0321, 0x0349,
+	0x0372, 0x039b, 0x03c4, 0x03ee, 0x0418, 0x0442, 0x046d, 0x0499,
+	0x04c4, 0x04f0, 0x051d, 0x054a, 0x0577, 0x05a5, 0x05d3, 0x0601,
+	0x0630, 0x0660, 0x0690, 0x06c0, 0x06f1, 0x0722, 0x0753, 0x0785,
+	0x07b8, 0x07eb, 0x081e, 0x0852, 0x0886, 0x08bb, 0x08f0, 0x0926,
+	0x095c, 0x0993, 0x09ca, 0x0a02, 0x0a3a, 0x0a73, 0x0aac, 0x0ae6,
+	0x0b20, 0x0b5b, 0x0b96, 0x0bd2, 0x0c0e, 0x0c4b, 0x0c89, 0x0cc7,
+	0x0d05, 0x0d44, 0x0d84, 0x0dc5, 0x0e05, 0x0e47, 0x0e89, 0x0ecc,
+	0x0f0f, 0x0f53, 0x0f97, 0x0fdd, 0x1022, 0x1069, 0x10b0, 0x10f7,
+	0x1140, 0x1189, 0x11d2, 0x121c, 0x1267, 0x12b3, 0x12ff, 0x134c,
+	0x139a, 0x13e8, 0x1438, 0x1487, 0x14d8, 0x1529, 0x157b, 0x15ce,
+	0x1621, 0x1676, 0x16cb, 0x1720, 0x1777, 0x17ce, 0x1826, 0x187f,
+	0x18d9, 0x1934, 0x198f, 0x19eb, 0x1a48, 0x1aa6, 0x1b05, 0x1b64,
+	0x1bc5, 0x1c26, 0x1c88, 0x1ceb, 0x1d4f, 0x1db4, 0x1e1a, 0x1e80,
+	0x1ee8, 0x1f51, 0x1fba, 0x2025, 0x2090, 0x20fc, 0x216a, 0x21d8,
+	0x2247, 0x22b8, 0x2329, 0x239b, 0x240f, 0x2483, 0x24f9, 0x256f,
+	0x25e7, 0x2660, 0x26da, 0x2755, 0x27d1, 0x284e, 0x28cc, 0x294b,
+	0x29cc, 0x2a4e, 0x2ad1, 0x2b55, 0x2bda, 0x2c61, 0x2ce8, 0x2d71,
+	0x2dfb, 0x2e87, 0x2f13, 0x2fa1, 0x3031, 0x30c1, 0x3153, 0x31e6,
+	0x327b, 0x3310, 0x33a8, 0x3440, 0x34da, 0x3575, 0x3612, 0x36b0,
+	0x3750, 0x37f1, 0x3893, 0x3937, 0x39dc, 0x3a83, 0x3b2c, 0x3bd6,
+	0x3c81, 0x3d2e, 0x3ddd, 0x3e8d, 0x3f3f, 0x3ff2, 0x40a7, 0x415d,
+	0x4216, 0x42d0, 0x438b, 0x4448, 0x4507, 0x45c8, 0x468b, 0x474f,
+	0x4815, 0x48dd, 0x49a6, 0x4a72, 0x4b3f, 0x4c0e, 0x4cdf, 0x4db2,
+	0x4e87, 0x4f5d, 0x5036, 0x5111, 0x51ed, 0x52cc, 0x53ac, 0x548f,
+	0x5573, 0x565a, 0x5743, 0x582e, 0x591b, 0x5a0a, 0x5afb, 0x5bef,
+	0x5ce4, 0x5ddc, 0x5ed7, 0x5fd3, 0x60d2, 0x61d3, 0x62d6, 0x63dc,
+	0x64e4, 0x65ee, 0x66fb, 0x680a, 0x691c, 0x6a30, 0x6b47, 0x6c60,
+	0x6d7c, 0x6e9a, 0x6fbb, 0x70de, 0x7204, 0x732d, 0x7459, 0x7587,
+	0x76b8, 0x77eb, 0x7922, 0x7a5b, 0x7b97, 0x7cd6, 0x7e18, 0x7f5d,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003,
+	0x0003, 0x0004, 0x0004, 0x0005, 0x0006, 0x0008, 0x0009, 0x000a,
+	0x000c, 0x0010, 0x0012, 0x0015, 0x0019, 0x0022, 0x0024, 0x002a,
+	0x0031, 0x003e, 0x0049, 0x0055, 0x0062, 0x007c, 0x0092, 0x00a9,
+	0x00c4, 0x00fc, 0x0125, 0x0152, 0x0187, 0x01f2, 0x024a, 0x02a4,
+	0x030d, 0x03e3, 0x0492, 0x0547, 0x061b, 0x07c7, 0x0923, 0x0a8d,
+	0x0c19, 0x0eb3, 0x1228, 0x14c1, 0x17fb, 0x1d17, 0x22f2, 0x2835,
+	0x2dd4, 0x3cd0, 0x4cf5, 0x51cc, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002,
+	0x0002, 0x0003, 0x0004, 0x0005, 0x0005, 0x0007, 0x0008, 0x000a,
+	0x000b, 0x000e, 0x0011, 0x0014, 0x0017, 0x001c, 0x0022, 0x0028,
+	0x002e, 0x0039, 0x0045, 0x0050, 0x005c, 0x0073, 0x008a, 0x00a0,
+	0x00b9, 0x00e7, 0x0114, 0x0141, 0x0172, 0x01ce, 0x0228, 0x0283,
+	0x02e3, 0x039b, 0x0454, 0x0501, 0x05bf, 0x072a, 0x0899, 0x0a18,
+	0x0b7e, 0x0e55, 0x10d3, 0x1404, 0x16c3, 0x16c3, 0x16c3, 0x16c3,
+	0x0012, 0x0024, 0x0048, 0x006c, 0x0090, 0x00b4, 0x00d8, 0x00fc,
+	0x0120, 0x0144, 0x0168, 0x0168, 0x01b0, 0x01b0, 0x021c, 0x021c,
+	0x0000, 0x0003, 0x0008, 0x000b, 0x0001, 0x0004, 0x0009, 0x000c,
+	0x0002, 0x0005, 0x000a, 0x000d, 0x0010, 0x0013, 0x0011, 0x0014,
+	0x0012, 0x0015, 0x0100, 0x0103, 0x0108, 0x010b, 0x0101, 0x0104,
+	0x0109, 0x010c, 0x0102, 0x0105, 0x010a, 0x010d, 0x0110, 0x0113,
+	0x0111, 0x0114, 0x0112, 0x0115 };
diff -Nru linux/sound/oss/maui.c linux-2.4.19-pre5-mjc/sound/oss/maui.c
--- linux/sound/oss/maui.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/maui.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,477 @@
+/*
+ * sound/maui.c
+ *
+ * The low level driver for Turtle Beach Maui and Tropez.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *	Changes:
+ *		Alan Cox		General clean up, use kernel IRQ 
+ *					system
+ *		Christoph Hellwig	Adapted to module_init/module_exit
+ *		Bartlomiej Zolnierkiewicz
+ *					Added __init to download_code()
+ *
+ *	Status:
+ *		Andrew J. Kroll		Tested 06/01/1999 with:
+ *					* OSWF.MOT File Version: 1.15
+ *					* OSWF.MOT File Dated: 09/12/94
+ *					* Older versions will cause problems.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+#include "sound_firmware.h"
+
+#include "mpu401.h"
+
+static int      maui_base = 0x330;
+
+static volatile int irq_ok = 0;
+static int     *maui_osp;
+
+#define HOST_DATA_PORT	(maui_base + 2)
+#define HOST_STAT_PORT	(maui_base + 3)
+#define HOST_CTRL_PORT	(maui_base + 3)
+
+#define STAT_TX_INTR	0x40
+#define STAT_TX_AVAIL	0x20
+#define STAT_TX_IENA	0x10
+#define STAT_RX_INTR	0x04
+#define STAT_RX_AVAIL	0x02
+#define STAT_RX_IENA	0x01
+
+static int      (*orig_load_patch) (int dev, int format, const char *addr,
+			      int offs, int count, int pmgr_flag) = NULL;
+
+#include "maui_boot.h"
+
+static int maui_wait(int mask)
+{
+	int i;
+
+	/*
+	 * Perform a short initial wait without sleeping
+	 */
+
+	for (i = 0; i < 100; i++)
+		if (inb(HOST_STAT_PORT) & mask)
+			return 1;
+
+	/*
+	 * Wait up to 15 seconds with sleeping
+	 */
+
+	for (i = 0; i < 150; i++) {
+		if (inb(HOST_STAT_PORT) & mask)
+			return 1;
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ / 10);
+		if (signal_pending(current))
+			return 0;
+	}
+	return 0;
+}
+
+static int maui_read(void)
+{
+	if (maui_wait(STAT_RX_AVAIL))
+		return inb(HOST_DATA_PORT);
+	return -1;
+}
+
+static int maui_write(unsigned char data)
+{
+	if (maui_wait(STAT_TX_AVAIL)) {
+		outb((data), HOST_DATA_PORT);
+		return 1;
+	}
+	printk(KERN_WARNING "Maui: Write timeout\n");
+	return 0;
+}
+
+static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	irq_ok = 1;
+}
+
+static int __init download_code(void)
+{
+	int i, lines = 0;
+	int eol_seen = 0, done = 0;
+	int skip = 1;
+
+	printk(KERN_INFO "Code download (%d bytes): ", maui_osLen);
+
+	for (i = 0; i < maui_osLen; i++) {
+		if (maui_os[i] != '\r') {
+			if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) {
+				skip = 0;
+
+				if (maui_os[i] == '\n')
+					eol_seen = skip = 1;
+				else if (maui_os[i] == 'S') {
+					if (maui_os[i + 1] == '8')
+						done = 1;
+					if (!maui_write(0xF1))
+						goto failure;
+					if (!maui_write('S'))
+						goto failure;
+				} else {
+					if (!maui_write(maui_os[i]))
+						goto failure;
+				}
+
+				if (eol_seen) {
+					int c = 0;
+					int n;
+
+					eol_seen = 0;
+
+					for (n = 0; n < 2; n++) {
+						if (maui_wait(STAT_RX_AVAIL)) {
+							c = inb(HOST_DATA_PORT);
+							break;
+						}
+					}
+					if (c != 0x80) {
+						printk("Download not acknowledged\n");
+						return 0;
+					}
+					else if (!(lines++ % 10))
+						printk(".");
+
+					if (done) {
+						printk("\n");
+						printk(KERN_INFO "Download complete\n");
+						return 1;
+					}
+				}
+			}
+		}
+	}
+
+failure:
+	printk("\n");
+	printk(KERN_ERR "Download failed!!!\n");
+	return 0;
+}
+
+static int __init maui_init(int irq)
+{
+	unsigned char bits;
+
+	switch (irq) {
+		case 9:
+			bits = 0x00;
+			break;
+		case 5:
+			bits = 0x08;
+			break;
+		case 12:
+			bits = 0x10;
+			break;
+		case 15:
+			bits = 0x18;
+			break;
+
+		default:
+			printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq);
+			return 0;
+	}
+	outb((0x00), HOST_CTRL_PORT);	/* Reset */
+	outb((bits), HOST_DATA_PORT);	/* Set the IRQ bits */
+	outb((bits | 0x80), HOST_DATA_PORT);	/* Set the IRQ bits again? */
+	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
+	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
+	outb((0xD0), HOST_CTRL_PORT);	/* Cause interrupt */
+
+#ifdef CONFIG_SMP
+	{
+		int i;
+		for (i = 0; i < 1000000 && !irq_ok; i++)
+			;
+		if (!irq_ok)
+			return 0;
+	}
+#endif
+	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
+
+	printk(KERN_INFO "Turtle Beach Maui initialization\n");
+
+	if (!download_code())
+		return 0;
+
+	outb((0xE0), HOST_CTRL_PORT);	/* Normal operation */
+
+	/* Select mpu401 mode */
+
+	maui_write(0xf0);
+	maui_write(1);
+	if (maui_read() != 0x80) {
+		maui_write(0xf0);
+		maui_write(1);
+		if (maui_read() != 0x80)
+			printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n");
+	}
+	printk(KERN_INFO "Maui initialized OK\n");
+	return 1;
+}
+
+static int maui_short_wait(int mask) {
+	int i;
+
+	for (i = 0; i < 1000; i++) {
+		if (inb(HOST_STAT_PORT) & mask) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int maui_load_patch(int dev, int format, const char *addr,
+		int offs, int count, int pmgr_flag)
+{
+
+	struct sysex_info header;
+	unsigned long left, src_offs;
+	int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header;
+	int i;
+
+	if (format == SYSEX_PATCH)	/* Handled by midi_synth.c */
+		return orig_load_patch(dev, format, addr, offs, count, pmgr_flag);
+
+	if (format != MAUI_PATCH)
+	{
+		  printk(KERN_WARNING "Maui: Unknown patch format\n");
+	}
+	if (count < hdr_size) {
+/*		  printk("Maui error: Patch header too short\n");*/
+		  return -EINVAL;
+	}
+	count -= hdr_size;
+
+	/*
+	 * Copy the header from user space but ignore the first bytes which have
+	 * been transferred already.
+	 */
+
+	if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs))
+		return -EFAULT;
+
+	if (count < header.len) {
+		  printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len);
+		  header.len = count;
+	}
+	left = header.len;
+	src_offs = 0;
+
+	for (i = 0; i < left; i++) {
+		unsigned char   data;
+
+		if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])))
+			return -EFAULT;
+		if (i == 0 && !(data & 0x80))
+			return -EINVAL;
+
+		if (maui_write(data) == -1)
+			return -EIO;
+	}
+
+	if ((i = maui_read()) != 0x80) {
+		if (i != -1)
+			printk("Maui: Error status %02x\n", i);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int __init probe_maui(struct address_info *hw_config)
+{
+	int i;
+	int tmp1, tmp2, ret;
+
+	if (check_region(hw_config->io_base, 8))
+		return 0;
+
+	maui_base = hw_config->io_base;
+	maui_osp = hw_config->osp;
+
+	if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0)
+		return 0;
+
+	/*
+	 * Initialize the processor if necessary
+	 */
+
+	if (maui_osLen > 0) {
+		if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) ||
+			!maui_write(0x9F) ||	/* Report firmware version */
+			!maui_short_wait(STAT_RX_AVAIL) ||
+			maui_read() == -1 || maui_read() == -1)
+			if (!maui_init(hw_config->irq)) {
+				free_irq(hw_config->irq, NULL);
+				return 0;
+			}
+	}
+	if (!maui_write(0xCF))	/* Report hardware version */ {
+		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
+		free_irq(hw_config->irq, NULL);
+		return 0;
+	}
+	if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) {
+		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
+		free_irq(hw_config->irq, NULL);
+		return 0;
+	}
+	if (tmp1 == 0xff || tmp2 == 0xff) {
+		free_irq(hw_config->irq, NULL);
+		return 0;
+	}
+	printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2);
+
+	if (!maui_write(0x9F))	/* Report firmware version */
+		return 0;
+	if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1)
+		return 0;
+
+	printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2);
+
+	if (!maui_write(0x85))	/* Report free DRAM */
+		return 0;
+	tmp1 = 0;
+	for (i = 0; i < 4; i++) {
+		tmp1 |= maui_read() << (7 * i);
+	}
+	printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024);
+
+	for (i = 0; i < 1000; i++)
+		if (probe_mpu401(hw_config))
+			break;
+
+	ret = probe_mpu401(hw_config);
+
+	if (ret)
+		request_region(hw_config->io_base + 2, 6, "Maui");
+
+	return ret;
+}
+
+static void __init attach_maui(struct address_info *hw_config)
+{
+	int this_dev;
+
+	conf_printf("Maui", hw_config);
+
+	hw_config->irq *= -1;
+	hw_config->name = "Maui";
+	attach_mpu401(hw_config, THIS_MODULE);
+
+	if (hw_config->slots[1] != -1)	/* The MPU401 driver installed itself */ {
+		struct synth_operations *synth;
+
+		this_dev = hw_config->slots[1];
+
+		/*
+		 * Intercept patch loading calls so that they can be handled
+		 * by the Maui driver.
+		 */
+
+		synth = midi_devs[this_dev]->converter;
+		synth->id = "MAUI";
+
+		if (synth != NULL) {
+			orig_load_patch = synth->load_patch;
+			synth->load_patch = &maui_load_patch;
+		} else
+			printk(KERN_ERR "Maui: Can't install patch loader\n");
+	}
+}
+
+static void __exit unload_maui(struct address_info *hw_config)
+{
+	int irq = hw_config->irq;
+	release_region(hw_config->io_base + 2, 6);
+	unload_mpu401(hw_config);
+
+	if (irq < 0)
+		irq = -irq;
+	if (irq > 0)
+		free_irq(irq, NULL);
+}
+
+static int fw_load = 0;
+
+static struct address_info cfg;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+
+/*
+ *	Install a Maui card. Needs mpu401 loaded already.
+ */
+
+static int __init init_maui(void)
+{
+	printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+
+	if (cfg.io_base == -1 || cfg.irq == -1) {
+		printk(KERN_INFO "maui: irq and io must be set.\n");
+		return -EINVAL;
+	}
+
+	if (maui_os == NULL) {
+		fw_load = 1;
+		maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os);
+	}
+	if (probe_maui(&cfg) == 0)
+		return -ENODEV;
+	attach_maui(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_maui(void)
+{
+	if (fw_load && maui_os)
+		vfree(maui_os);
+	unload_maui(&cfg);
+}
+
+module_init(init_maui);
+module_exit(cleanup_maui);
+
+#ifndef MODULE
+static int __init setup_maui(char *str)
+{
+        /* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+	irq = ints[2];
+
+	return 1;
+}
+
+__setup("maui=", setup_maui);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/midi_ctrl.h linux-2.4.19-pre5-mjc/sound/oss/midi_ctrl.h
--- linux/sound/oss/midi_ctrl.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/midi_ctrl.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,22 @@
+static unsigned char ctrl_def_values[128] =
+{
+	0x40,0x00,0x40,0x40,  0x40,0x40,0x40,0x7f,	/*   0 to   7 */
+	0x40,0x40,0x40,0x7f,  0x40,0x40,0x40,0x40,	/*   8 to  15 */
+	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*  16 to  23 */
+	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*  24 to  31 */
+
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  32 to  39 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  40 to  47 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  48 to  55 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  56 to  63 */
+	
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  64 to  71 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  72 to  79 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  80 to  87 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  88 to  95 */
+
+	0x00,0x00,0x7f,0x7f,  0x7f,0x7f,0x00,0x00,	/*  96 to 103 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 104 to 111 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 112 to 119 */
+	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 120 to 127 */
+};
diff -Nru linux/sound/oss/midi_syms.c linux-2.4.19-pre5-mjc/sound/oss/midi_syms.c
--- linux/sound/oss/midi_syms.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/midi_syms.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,31 @@
+/*
+ * Exported symbols for midi driver.
+ * __NO_VERSION__ because this is still part of sound.o.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+char midi_syms_symbol;
+
+#include "sound_config.h"
+#define _MIDI_SYNTH_C_
+#include "midi_synth.h"
+
+EXPORT_SYMBOL(do_midi_msg);
+EXPORT_SYMBOL(midi_synth_open);
+EXPORT_SYMBOL(midi_synth_close);
+EXPORT_SYMBOL(midi_synth_ioctl);
+EXPORT_SYMBOL(midi_synth_kill_note);
+EXPORT_SYMBOL(midi_synth_start_note);
+EXPORT_SYMBOL(midi_synth_set_instr);
+EXPORT_SYMBOL(midi_synth_reset);
+EXPORT_SYMBOL(midi_synth_hw_control);
+EXPORT_SYMBOL(midi_synth_aftertouch);
+EXPORT_SYMBOL(midi_synth_controller);
+EXPORT_SYMBOL(midi_synth_panning);
+EXPORT_SYMBOL(midi_synth_setup_voice);
+EXPORT_SYMBOL(midi_synth_send_sysex);
+EXPORT_SYMBOL(midi_synth_bender);
+EXPORT_SYMBOL(midi_synth_load_patch);
+EXPORT_SYMBOL(MIDIbuf_avail);
diff -Nru linux/sound/oss/midi_synth.c linux-2.4.19-pre5-mjc/sound/oss/midi_synth.c
--- linux/sound/oss/midi_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/midi_synth.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,697 @@
+/*
+ * sound/midi_synth.c
+ *
+ * High level midi sequencer manager for dumb MIDI interfaces.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Andrew Veliath  : fixed running status in MIDI input state machine
+ */
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#define _MIDI_SYNTH_C_
+
+#include "midi_synth.h"
+
+static int      midi2synth[MAX_MIDI_DEV];
+static int      sysex_state[MAX_MIDI_DEV] =
+{0};
+static unsigned char prev_out_status[MAX_MIDI_DEV];
+
+#define STORE(cmd) \
+{ \
+  int len; \
+  unsigned char obuf[8]; \
+  cmd; \
+  seq_input_event(obuf, len); \
+}
+
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+void
+do_midi_msg(int synthno, unsigned char *msg, int mlen)
+{
+	switch (msg[0] & 0xf0)
+	  {
+	  case 0x90:
+		  if (msg[2] != 0)
+		    {
+			    STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+			    break;
+		    }
+		  msg[2] = 64;
+
+	  case 0x80:
+		  STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+		  break;
+
+	  case 0xA0:
+		  STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+		  break;
+
+	  case 0xB0:
+		  STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f,
+				    msg[1], msg[2]));
+		  break;
+
+	  case 0xC0:
+		  STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1]));
+		  break;
+
+	  case 0xD0:
+		  STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1]));
+		  break;
+
+	  case 0xE0:
+		  STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
+			      (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7)));
+		  break;
+
+	  default:
+		  /* printk( "MPU: Unknown midi channel message %02x\n",  msg[0]); */
+		  ;
+	  }
+}
+
+static void
+midi_outc(int midi_dev, int data)
+{
+	int             timeout;
+
+	for (timeout = 0; timeout < 3200; timeout++)
+		if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff)))
+		  {
+			  if (data & 0x80)	/*
+						 * Status byte
+						 */
+				  prev_out_status[midi_dev] =
+				      (unsigned char) (data & 0xff);	/*
+									 * Store for running status
+									 */
+			  return;	/*
+					 * Mission complete
+					 */
+		  }
+	/*
+	 * Sorry! No space on buffers.
+	 */
+	printk("Midi send timed out\n");
+}
+
+static int
+prefix_cmd(int midi_dev, unsigned char status)
+{
+	if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
+		return 1;
+
+	return midi_devs[midi_dev]->prefix_cmd(midi_dev, status);
+}
+
+static void
+midi_synth_input(int orig_dev, unsigned char data)
+{
+	int             dev;
+	struct midi_input_info *inc;
+
+	static unsigned char len_tab[] =	/* # of data bytes following a status
+						 */
+	{
+		2,		/* 8x */
+		2,		/* 9x */
+		2,		/* Ax */
+		2,		/* Bx */
+		1,		/* Cx */
+		1,		/* Dx */
+		2,		/* Ex */
+		0		/* Fx */
+	};
+
+	if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+		return;
+
+	if (data == 0xfe)	/* Ignore active sensing */
+		return;
+
+	dev = midi2synth[orig_dev];
+	inc = &midi_devs[orig_dev]->in_info;
+
+	switch (inc->m_state)
+	  {
+	  case MST_INIT:
+		  if (data & 0x80)	/* MIDI status byte */
+		    {
+			    if ((data & 0xf0) == 0xf0)	/* Common message */
+			      {
+				      switch (data)
+					{
+					case 0xf0:	/* Sysex */
+						inc->m_state = MST_SYSEX;
+						break;	/* Sysex */
+
+					case 0xf1:	/* MTC quarter frame */
+					case 0xf3:	/* Song select */
+						inc->m_state = MST_DATA;
+						inc->m_ptr = 1;
+						inc->m_left = 1;
+						inc->m_buf[0] = data;
+						break;
+
+					case 0xf2:	/* Song position pointer */
+						inc->m_state = MST_DATA;
+						inc->m_ptr = 1;
+						inc->m_left = 2;
+						inc->m_buf[0] = data;
+						break;
+
+					default:
+						inc->m_buf[0] = data;
+						inc->m_ptr = 1;
+						do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+						inc->m_ptr = 0;
+						inc->m_left = 0;
+					}
+			    } else
+			      {
+				      inc->m_state = MST_DATA;
+				      inc->m_ptr = 1;
+				      inc->m_left = len_tab[(data >> 4) - 8];
+				      inc->m_buf[0] = inc->m_prev_status = data;
+			      }
+		    } else if (inc->m_prev_status & 0x80) {
+			    /* Data byte (use running status) */
+			    inc->m_ptr = 2;
+			    inc->m_buf[1] = data;
+			    inc->m_buf[0] = inc->m_prev_status;
+			    inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1;
+			    if (inc->m_left > 0)
+				    inc->m_state = MST_DATA; /* Not done yet */
+			    else {
+				    inc->m_state = MST_INIT;
+				    do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+				    inc->m_ptr = 0;
+			    }
+		    }
+		  break;	/* MST_INIT */
+
+	  case MST_DATA:
+		  inc->m_buf[inc->m_ptr++] = data;
+		  if (--inc->m_left <= 0)
+		    {
+			    inc->m_state = MST_INIT;
+			    do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+			    inc->m_ptr = 0;
+		    }
+		  break;	/* MST_DATA */
+
+	  case MST_SYSEX:
+		  if (data == 0xf7)	/* Sysex end */
+		    {
+			    inc->m_state = MST_INIT;
+			    inc->m_left = 0;
+			    inc->m_ptr = 0;
+		    }
+		  break;	/* MST_SYSEX */
+
+	  default:
+		  printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
+		  inc->m_state = MST_INIT;
+	  }
+}
+
+static void
+leave_sysex(int dev)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             timeout = 0;
+
+	if (!sysex_state[dev])
+		return;
+
+	sysex_state[dev] = 0;
+
+	while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) &&
+	       timeout < 1000)
+		timeout++;
+
+	sysex_state[dev] = 0;
+}
+
+static void
+midi_synth_output(int dev)
+{
+	/*
+	 * Currently NOP
+	 */
+}
+
+int midi_synth_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	/*
+	 * int orig_dev = synth_devs[dev]->midi_dev;
+	 */
+
+	switch (cmd) {
+
+	case SNDCTL_SYNTH_INFO:
+		if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info)))
+			return -EFAULT;
+		return 0;
+		
+	case SNDCTL_SYNTH_MEMAVL:
+		return 0x7fffffff;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+int
+midi_synth_kill_note(int dev, int channel, int note, int velocity)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, chn;
+
+	if (note < 0 || note > 127)
+		return 0;
+	if (channel < 0 || channel > 15)
+		return 0;
+	if (velocity < 0)
+		velocity = 0;
+	if (velocity > 127)
+		velocity = 127;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
+	  {			/*
+				 * Use running status
+				 */
+		  if (!prefix_cmd(orig_dev, note))
+			  return 0;
+
+		  midi_outc(orig_dev, note);
+
+		  if (msg == 0x90)	/*
+					 * Running status = Note on
+					 */
+			  midi_outc(orig_dev, 0);	/*
+							   * Note on with velocity 0 == note
+							   * off
+							 */
+		  else
+			  midi_outc(orig_dev, velocity);
+	} else
+	  {
+		  if (velocity == 64)
+		    {
+			    if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
+				    return 0;
+			    midi_outc(orig_dev, 0x90 | (channel & 0x0f));	/*
+										 * Note on
+										 */
+			    midi_outc(orig_dev, note);
+			    midi_outc(orig_dev, 0);	/*
+							 * Zero G
+							 */
+		  } else
+		    {
+			    if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f)))
+				    return 0;
+			    midi_outc(orig_dev, 0x80 | (channel & 0x0f));	/*
+										 * Note off
+										 */
+			    midi_outc(orig_dev, note);
+			    midi_outc(orig_dev, velocity);
+		    }
+	  }
+
+	return 0;
+}
+
+int
+midi_synth_set_instr(int dev, int channel, int instr_no)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+
+	if (instr_no < 0 || instr_no > 127)
+		instr_no = 0;
+	if (channel < 0 || channel > 15)
+		return 0;
+
+	leave_sysex(dev);
+
+	if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f)))
+		return 0;
+	midi_outc(orig_dev, 0xc0 | (channel & 0x0f));	/*
+							 * Program change
+							 */
+	midi_outc(orig_dev, instr_no);
+
+	return 0;
+}
+
+int
+midi_synth_start_note(int dev, int channel, int note, int velocity)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, chn;
+
+	if (note < 0 || note > 127)
+		return 0;
+	if (channel < 0 || channel > 15)
+		return 0;
+	if (velocity < 0)
+		velocity = 0;
+	if (velocity > 127)
+		velocity = 127;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (chn == channel && msg == 0x90)
+	  {			/*
+				 * Use running status
+				 */
+		  if (!prefix_cmd(orig_dev, note))
+			  return 0;
+		  midi_outc(orig_dev, note);
+		  midi_outc(orig_dev, velocity);
+	} else
+	  {
+		  if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
+			  return 0;
+		  midi_outc(orig_dev, 0x90 | (channel & 0x0f));		/*
+									 * Note on
+									 */
+		  midi_outc(orig_dev, note);
+		  midi_outc(orig_dev, velocity);
+	  }
+	return 0;
+}
+
+void
+midi_synth_reset(int dev)
+{
+
+	leave_sysex(dev);
+}
+
+int
+midi_synth_open(int dev, int mode)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             err;
+	unsigned long   flags;
+	struct midi_input_info *inc;
+
+	if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+		return -ENXIO;
+
+	midi2synth[orig_dev] = dev;
+	sysex_state[dev] = 0;
+	prev_out_status[orig_dev] = 0;
+
+	if ((err = midi_devs[orig_dev]->open(orig_dev, mode,
+			       midi_synth_input, midi_synth_output)) < 0)
+		return err;
+	inc = &midi_devs[orig_dev]->in_info;
+
+	save_flags(flags);
+	cli();
+	inc->m_busy = 0;
+	inc->m_state = MST_INIT;
+	inc->m_ptr = 0;
+	inc->m_left = 0;
+	inc->m_prev_status = 0x00;
+	restore_flags(flags);
+
+	return 1;
+}
+
+void
+midi_synth_close(int dev)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+
+	leave_sysex(dev);
+
+	/*
+	 * Shut up the synths by sending just single active sensing message.
+	 */
+	midi_devs[orig_dev]->outputc(orig_dev, 0xfe);
+
+	midi_devs[orig_dev]->close(orig_dev);
+}
+
+void
+midi_synth_hw_control(int dev, unsigned char *event)
+{
+}
+
+int
+midi_synth_load_patch(int dev, int format, const char *addr,
+		      int offs, int count, int pmgr_flag)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+
+	struct sysex_info sysex;
+	int             i;
+	unsigned long   left, src_offs, eox_seen = 0;
+	int             first_byte = 1;
+	int             hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
+
+	leave_sysex(dev);
+
+	if (!prefix_cmd(orig_dev, 0xf0))
+		return 0;
+
+	if (format != SYSEX_PATCH)
+	{
+/*		  printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/
+		  return -EINVAL;
+	}
+	if (count < hdr_size)
+	{
+/*		printk("MIDI Error: Patch header too short\n");*/
+		return -EINVAL;
+	}
+	count -= hdr_size;
+
+	/*
+	 * Copy the header from user space but ignore the first bytes which have
+	 * been transferred already.
+	 */
+
+	if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs))
+		return -EFAULT;
+ 
+ 	if (count < sysex.len)
+	{
+/*		printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/
+		sysex.len = count;
+	}
+  	left = sysex.len;
+  	src_offs = 0;
+
+	for (i = 0; i < left && !signal_pending(current); i++)
+	{
+		unsigned char   data;
+
+		get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
+
+		eox_seen = (i > 0 && data & 0x80);	/* End of sysex */
+
+		if (eox_seen && data != 0xf7)
+			data = 0xf7;
+
+		if (i == 0)
+		{
+			if (data != 0xf0)
+			{
+				printk(KERN_WARNING "midi_synth: Sysex start missing\n");
+				return -EINVAL;
+			}
+		}
+		while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
+			!signal_pending(current))
+			schedule();
+
+		if (!first_byte && data & 0x80)
+			return 0;
+		first_byte = 0;
+	}
+
+	if (!eox_seen)
+		midi_outc(orig_dev, 0xf7);
+	return 0;
+}
+  
+void midi_synth_panning(int dev, int channel, int pressure)
+{
+}
+  
+void midi_synth_aftertouch(int dev, int channel, int pressure)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, chn;
+
+	if (pressure < 0 || pressure > 127)
+		return;
+	if (channel < 0 || channel > 15)
+		return;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (msg != 0xd0 || chn != channel)	/*
+						 * Test for running status
+						 */
+	  {
+		  if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f)))
+			  return;
+		  midi_outc(orig_dev, 0xd0 | (channel & 0x0f));		/*
+									 * Channel pressure
+									 */
+	} else if (!prefix_cmd(orig_dev, pressure))
+		return;
+
+	midi_outc(orig_dev, pressure);
+}
+
+void
+midi_synth_controller(int dev, int channel, int ctrl_num, int value)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             chn, msg;
+
+	if (ctrl_num < 0 || ctrl_num > 127)
+		return;
+	if (channel < 0 || channel > 15)
+		return;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (msg != 0xb0 || chn != channel)
+	  {
+		  if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f)))
+			  return;
+		  midi_outc(orig_dev, 0xb0 | (channel & 0x0f));
+	} else if (!prefix_cmd(orig_dev, ctrl_num))
+		return;
+
+	midi_outc(orig_dev, ctrl_num);
+	midi_outc(orig_dev, value & 0x7f);
+}
+
+void
+midi_synth_bender(int dev, int channel, int value)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             msg, prev_chn;
+
+	if (channel < 0 || channel > 15)
+		return;
+
+	if (value < 0 || value > 16383)
+		return;
+
+	leave_sysex(dev);
+
+	msg = prev_out_status[orig_dev] & 0xf0;
+	prev_chn = prev_out_status[orig_dev] & 0x0f;
+
+	if (msg != 0xd0 || prev_chn != channel)		/*
+							 * Test for running status
+							 */
+	  {
+		  if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f)))
+			  return;
+		  midi_outc(orig_dev, 0xe0 | (channel & 0x0f));
+	} else if (!prefix_cmd(orig_dev, value & 0x7f))
+		return;
+
+	midi_outc(orig_dev, value & 0x7f);
+	midi_outc(orig_dev, (value >> 7) & 0x7f);
+}
+
+void
+midi_synth_setup_voice(int dev, int voice, int channel)
+{
+}
+
+int
+midi_synth_send_sysex(int dev, unsigned char *bytes, int len)
+{
+	int             orig_dev = synth_devs[dev]->midi_dev;
+	int             i;
+
+	for (i = 0; i < len; i++)
+	  {
+		  switch (bytes[i])
+		    {
+		    case 0xf0:	/* Start sysex */
+			    if (!prefix_cmd(orig_dev, 0xf0))
+				    return 0;
+			    sysex_state[dev] = 1;
+			    break;
+
+		    case 0xf7:	/* End sysex */
+			    if (!sysex_state[dev])	/* Orphan sysex end */
+				    return 0;
+			    sysex_state[dev] = 0;
+			    break;
+
+		    default:
+			    if (!sysex_state[dev])
+				    return 0;
+
+			    if (bytes[i] & 0x80)	/* Error. Another message before sysex end */
+			      {
+				      bytes[i] = 0xf7;	/* Sysex end */
+				      sysex_state[dev] = 0;
+			      }
+		    }
+
+		  if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]))
+		    {
+/*
+ * Hardware level buffer is full. Abort the sysex message.
+ */
+
+			    int             timeout = 0;
+
+			    bytes[i] = 0xf7;
+			    sysex_state[dev] = 0;
+
+			    while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) &&
+				   timeout < 1000)
+				    timeout++;
+		    }
+		  if (!sysex_state[dev])
+			  return 0;
+	  }
+
+	return 0;
+}
diff -Nru linux/sound/oss/midi_synth.h linux-2.4.19-pre5-mjc/sound/oss/midi_synth.h
--- linux/sound/oss/midi_synth.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/midi_synth.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,47 @@
+int midi_synth_ioctl (int dev,
+	    unsigned int cmd, caddr_t arg);
+int midi_synth_kill_note (int dev, int channel, int note, int velocity);
+int midi_synth_set_instr (int dev, int channel, int instr_no);
+int midi_synth_start_note (int dev, int channel, int note, int volume);
+void midi_synth_reset (int dev);
+int midi_synth_open (int dev, int mode);
+void midi_synth_close (int dev);
+void midi_synth_hw_control (int dev, unsigned char *event);
+int midi_synth_load_patch (int dev, int format, const char * addr,
+		 int offs, int count, int pmgr_flag);
+void midi_synth_panning (int dev, int channel, int pressure);
+void midi_synth_aftertouch (int dev, int channel, int pressure);
+void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
+void midi_synth_bender (int dev, int chn, int value);
+void midi_synth_setup_voice (int dev, int voice, int chn);
+int midi_synth_send_sysex(int dev, unsigned char *bytes,int len);
+
+#ifndef _MIDI_SYNTH_C_
+static struct synth_info std_synth_info =
+{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
+
+static struct synth_operations std_midi_synth =
+{
+	owner:		THIS_MODULE,
+	id:		"MIDI",
+	info:		&std_synth_info,
+	midi_dev:	0,
+	synth_type:	SYNTH_TYPE_MIDI,
+	synth_subtype:	0,
+	open:		midi_synth_open,
+	close:		midi_synth_close,
+	ioctl:		midi_synth_ioctl,
+	kill_note:	midi_synth_kill_note,
+	start_note:	midi_synth_start_note,
+	set_instr:	midi_synth_set_instr,
+	reset:		midi_synth_reset,
+	hw_control:	midi_synth_hw_control,
+	load_patch:	midi_synth_load_patch,
+	aftertouch:	midi_synth_aftertouch,
+	controller:	midi_synth_controller,
+	panning:	midi_synth_panning,
+	bender:		midi_synth_bender,
+	setup_voice:	midi_synth_setup_voice,
+	send_sysex:	midi_synth_send_sysex
+};
+#endif
diff -Nru linux/sound/oss/midibuf.c linux-2.4.19-pre5-mjc/sound/oss/midibuf.c
--- linux/sound/oss/midibuf.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/midibuf.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,438 @@
+/*
+ * sound/midibuf.c
+ *
+ * Device file manager for /dev/midi#
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
+#include <linux/stddef.h>
+#include <linux/kmod.h>
+
+#define MIDIBUF_C
+
+#include "sound_config.h"
+
+
+/*
+ * Don't make MAX_QUEUE_SIZE larger than 4000
+ */
+
+#define MAX_QUEUE_SIZE	4000
+
+static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV];
+static wait_queue_head_t input_sleeper[MAX_MIDI_DEV];
+
+struct midi_buf
+{
+	int len, head, tail;
+	unsigned char queue[MAX_QUEUE_SIZE];
+};
+
+struct midi_parms
+{
+	long prech_timeout;	/*
+				 * Timeout before the first ch
+				 */
+};
+
+static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
+static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
+static struct midi_parms parms[MAX_MIDI_DEV];
+
+static void midi_poll(unsigned long dummy);
+
+
+static struct timer_list poll_timer = {
+	function: midi_poll
+};
+
+static volatile int open_devs = 0;
+
+#define DATA_AVAIL(q) (q->len)
+#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
+
+#define QUEUE_BYTE(q, data) \
+	if (SPACE_AVAIL(q)) \
+	{ \
+	  unsigned long flags; \
+	  save_flags( flags);cli(); \
+	  q->queue[q->tail] = (data); \
+	  q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
+	  restore_flags(flags); \
+	}
+
+#define REMOVE_BYTE(q, data) \
+	if (DATA_AVAIL(q)) \
+	{ \
+	  unsigned long flags; \
+	  save_flags( flags);cli(); \
+	  data = q->queue[q->head]; \
+	  q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
+	  restore_flags(flags); \
+	}
+
+static void drain_midi_queue(int dev)
+{
+
+	/*
+	 * Give the Midi driver time to drain its output queues
+	 */
+
+	if (midi_devs[dev]->buffer_status != NULL)
+		while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) 
+			interruptible_sleep_on_timeout(&midi_sleeper[dev],
+						       HZ/10);
+}
+
+static void midi_input_intr(int dev, unsigned char data)
+{
+	if (midi_in_buf[dev] == NULL)
+		return;
+
+	if (data == 0xfe)	/*
+				 * Active sensing
+				 */
+		return;		/*
+				 * Ignore
+				 */
+
+	if (SPACE_AVAIL(midi_in_buf[dev])) {
+		QUEUE_BYTE(midi_in_buf[dev], data);
+		wake_up(&input_sleeper[dev]);
+	}
+}
+
+static void midi_output_intr(int dev)
+{
+	/*
+	 * Currently NOP
+	 */
+}
+
+static void midi_poll(unsigned long dummy)
+{
+	unsigned long   flags;
+	int             dev;
+
+	save_flags(flags);
+	cli();
+	if (open_devs)
+	{
+		for (dev = 0; dev < num_midis; dev++)
+			if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
+			{
+				int ok = 1;
+
+				while (DATA_AVAIL(midi_out_buf[dev]) && ok)
+				{
+					int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
+
+					restore_flags(flags);	/* Give some time to others */
+					ok = midi_devs[dev]->outputc(dev, c);
+					cli();
+					midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
+					midi_out_buf[dev]->len--;
+				}
+
+				if (DATA_AVAIL(midi_out_buf[dev]) < 100)
+					wake_up(&midi_sleeper[dev]);
+			}
+		poll_timer.expires = (1) + jiffies;
+		add_timer(&poll_timer);
+		/*
+		 * Come back later
+		 */
+	}
+	restore_flags(flags);
+}
+
+int MIDIbuf_open(int dev, struct file *file)
+{
+	int mode, err;
+
+	dev = dev >> 4;
+	mode = translate_mode(file);
+
+	if (num_midis > MAX_MIDI_DEV)
+	{
+		printk(KERN_ERR "midi: Too many midi interfaces\n");
+		num_midis = MAX_MIDI_DEV;
+	}
+	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+		  return -ENXIO;
+	/*
+	 *    Interrupts disabled. Be careful
+	 */
+
+	if (midi_devs[dev]->owner)
+		__MOD_INC_USE_COUNT (midi_devs[dev]->owner);
+
+	if ((err = midi_devs[dev]->open(dev, mode,
+				 midi_input_intr, midi_output_intr)) < 0)
+		return err;
+
+	parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT;
+	midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
+
+	if (midi_in_buf[dev] == NULL)
+	{
+		printk(KERN_WARNING "midi: Can't allocate buffer\n");
+		midi_devs[dev]->close(dev);
+		return -EIO;
+	}
+	midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
+
+	midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
+
+	if (midi_out_buf[dev] == NULL)
+	{
+		printk(KERN_WARNING "midi: Can't allocate buffer\n");
+		midi_devs[dev]->close(dev);
+		vfree(midi_in_buf[dev]);
+		midi_in_buf[dev] = NULL;
+		return -EIO;
+	}
+	midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
+	open_devs++;
+
+	init_waitqueue_head(&midi_sleeper[dev]);
+	init_waitqueue_head(&input_sleeper[dev]);
+
+	if (open_devs < 2)	/* This was first open */
+	{
+		poll_timer.expires = 1 + jiffies;
+		add_timer(&poll_timer);	/* Start polling */
+	}
+	return err;
+}
+
+void MIDIbuf_release(int dev, struct file *file)
+{
+	int mode;
+	unsigned long flags;
+
+	dev = dev >> 4;
+	mode = translate_mode(file);
+
+	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	/*
+	 * Wait until the queue is empty
+	 */
+
+	if (mode != OPEN_READ)
+	{
+		midi_devs[dev]->outputc(dev, 0xfe);	/*
+							   * Active sensing to shut the
+							   * devices
+							 */
+
+		while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev]))
+			  interruptible_sleep_on(&midi_sleeper[dev]);
+		/*
+		 *	Sync
+		 */
+
+		drain_midi_queue(dev);	/*
+					 * Ensure the output queues are empty
+					 */
+	}
+	restore_flags(flags);
+
+	midi_devs[dev]->close(dev);
+
+	open_devs--;
+	if (open_devs == 0)
+		del_timer_sync(&poll_timer);
+	vfree(midi_in_buf[dev]);
+	vfree(midi_out_buf[dev]);
+	midi_in_buf[dev] = NULL;
+	midi_out_buf[dev] = NULL;
+
+	if (midi_devs[dev]->owner)
+		__MOD_DEC_USE_COUNT (midi_devs[dev]->owner);
+}
+
+int MIDIbuf_write(int dev, struct file *file, const char *buf, int count)
+{
+	unsigned long flags;
+	int c, n, i;
+	unsigned char tmp_data;
+
+	dev = dev >> 4;
+
+	if (!count)
+		return 0;
+
+	save_flags(flags);
+	cli();
+
+	c = 0;
+
+	while (c < count)
+	{
+		n = SPACE_AVAIL(midi_out_buf[dev]);
+
+		if (n == 0) {	/*
+				 * No space just now.
+				 */
+
+			if (file->f_flags & O_NONBLOCK) {
+				restore_flags(flags);
+				return -EAGAIN;
+			}
+
+			interruptible_sleep_on(&midi_sleeper[dev]);
+			if (signal_pending(current)) 
+			{
+				restore_flags(flags);
+				return -EINTR;
+			}
+			n = SPACE_AVAIL(midi_out_buf[dev]);
+		}
+		if (n > (count - c))
+			n = count - c;
+
+		for (i = 0; i < n; i++)
+		{
+			/* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */
+			copy_from_user((char *) &tmp_data, &(buf)[c], 1);
+			QUEUE_BYTE(midi_out_buf[dev], tmp_data);
+			c++;
+		}
+	}
+	restore_flags(flags);
+	return c;
+}
+
+
+int MIDIbuf_read(int dev, struct file *file, char *buf, int count)
+{
+	int n, c = 0;
+	unsigned long flags;
+	unsigned char tmp_data;
+
+	dev = dev >> 4;
+
+	save_flags(flags);
+	cli();
+
+	if (!DATA_AVAIL(midi_in_buf[dev])) {	/*
+						 * No data yet, wait
+						 */
+ 		if (file->f_flags & O_NONBLOCK) {
+ 			restore_flags(flags);
+ 			return -EAGAIN;
+ 		}
+		interruptible_sleep_on_timeout(&input_sleeper[dev],
+					       parms[dev].prech_timeout);
+
+		if (signal_pending(current))
+			c = -EINTR;	/* The user is getting restless */
+	}
+	if (c == 0 && DATA_AVAIL(midi_in_buf[dev]))	/*
+							 * Got some bytes
+							 */
+	{
+		n = DATA_AVAIL(midi_in_buf[dev]);
+		if (n > count)
+			n = count;
+		c = 0;
+
+		while (c < n)
+		{
+			char *fixit;
+			REMOVE_BYTE(midi_in_buf[dev], tmp_data);
+			fixit = (char *) &tmp_data;
+			/* BROKE BROKE BROKE */
+			copy_to_user(&(buf)[c], fixit, 1);
+			c++;
+		}
+	}
+	restore_flags(flags);
+	return c;
+}
+
+int MIDIbuf_ioctl(int dev, struct file *file,
+		  unsigned int cmd, caddr_t arg)
+{
+	int val;
+
+	dev = dev >> 4;
+	
+	if (((cmd >> 8) & 0xff) == 'C') 
+	{
+		if (midi_devs[dev]->coproc)	/* Coprocessor ioctl */
+			return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
+/*		printk("/dev/midi%d: No coprocessor for this device\n", dev);*/
+		return -ENXIO;
+	}
+	else
+	{
+		switch (cmd) 
+		{
+			case SNDCTL_MIDI_PRETIME:
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				if (val < 0)
+					val = 0;
+				val = (HZ * val) / 10;
+				parms[dev].prech_timeout = val;
+				return put_user(val, (int *)arg);
+			
+			default:
+				if (!midi_devs[dev]->ioctl)
+					return -EINVAL;
+				return midi_devs[dev]->ioctl(dev, cmd, arg);
+		}
+	}
+}
+
+/* No kernel lock - fine */
+unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+
+	dev = dev >> 4;
+
+	/* input */
+	poll_wait(file, &input_sleeper[dev], wait);
+	if (DATA_AVAIL(midi_in_buf[dev]))
+		mask |= POLLIN | POLLRDNORM;
+
+	/* output */
+	poll_wait(file, &midi_sleeper[dev], wait);
+	if (!SPACE_AVAIL(midi_out_buf[dev]))
+		mask |= POLLOUT | POLLWRNORM;
+	
+	return mask;
+}
+
+
+void MIDIbuf_init(void)
+{
+	/* drag in midi_syms.o */
+	{
+		extern char midi_syms_symbol;
+		midi_syms_symbol = 0;
+	}
+}
+
+int MIDIbuf_avail(int dev)
+{
+        if (midi_in_buf[dev])
+		return DATA_AVAIL (midi_in_buf[dev]);
+	return 0;
+}
diff -Nru linux/sound/oss/mpu401.c linux-2.4.19-pre5-mjc/sound/oss/mpu401.c
--- linux/sound/oss/mpu401.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/mpu401.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1797 @@
+/*
+ * sound/mpu401.c
+ *
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer	ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox		modularisation, use normal request_irq, use dev_id
+ * Bartlomiej Zolnierkiewicz	removed some __init to allow using many drivers
+ * Chris Rankin		Update the module-usage counter for the coprocessor
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#include "coproc.h"
+#include "mpu401.h"
+
+static int      timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
+
+struct mpu_config
+{
+	int             base;	/*
+				 * I/O base
+				 */
+	int             irq;
+	int             opened;	/*
+				 * Open mode
+				 */
+	int             devno;
+	int             synthno;
+	int             uart_mode;
+	int             initialized;
+	int             mode;
+#define MODE_MIDI	1
+#define MODE_SYNTH	2
+	unsigned char   version, revision;
+	unsigned int    capabilities;
+#define MPU_CAP_INTLG	0x10000000
+#define MPU_CAP_SYNC	0x00000010
+#define MPU_CAP_FSK	0x00000020
+#define MPU_CAP_CLS	0x00000040
+#define MPU_CAP_SMPTE 	0x00000080
+#define MPU_CAP_2PORT	0x00000001
+	int             timer_flag;
+
+#define MBUF_MAX	10
+#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
+	{printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n",  dc->m_ptr,  dc->m_left,  dc->m_state);dc->m_ptr--;}
+	  int             m_busy;
+	  unsigned char   m_buf[MBUF_MAX];
+	  int             m_ptr;
+	  int             m_state;
+	  int             m_left;
+	  unsigned char   last_status;
+	  void            (*inputintr) (int dev, unsigned char data);
+	  int             shared_irq;
+	  int            *osp;
+  };
+
+#define	DATAPORT(base)   (base)
+#define	COMDPORT(base)   (base+1)
+#define	STATPORT(base)   (base+1)
+
+
+static void mpu401_close(int dev);
+
+static int mpu401_status(struct mpu_config *devc)
+{
+	return inb(STATPORT(devc->base));
+}
+
+#define input_avail(devc)		(!(mpu401_status(devc)&INPUT_AVAIL))
+#define output_ready(devc)		(!(mpu401_status(devc)&OUTPUT_READY))
+
+static void write_command(struct mpu_config *devc, unsigned char cmd)
+{
+	outb(cmd, COMDPORT(devc->base));
+}
+
+static int read_data(struct mpu_config *devc)
+{
+	return inb(DATAPORT(devc->base));
+}
+
+static void write_data(struct mpu_config *devc, unsigned char byte)
+{
+	outb(byte, DATAPORT(devc->base));
+}
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	MPU_RESET	0xFF
+#define	UART_MODE_ON	0x3F
+
+static struct mpu_config dev_conf[MAX_MIDI_DEV] =
+{
+	{0}
+};
+
+static int n_mpu_devs = 0;
+
+static int reset_mpu401(struct mpu_config *devc);
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg);
+
+static int mpu_timer_init(int midi_dev);
+static void mpu_timer_interrupt(void);
+static void timer_ext_event(struct mpu_config *devc, int event, int parm);
+
+static struct synth_info mpu_synth_info_proto = {
+	"MPU-401 MIDI interface", 
+	0, 
+	SYNTH_TYPE_MIDI, 
+	MIDI_TYPE_MPU401, 
+	0, 128, 
+	0, 128, 
+	SYNTH_CAP_INPUT
+};
+
+static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
+
+/*
+ * States for the input scanner
+ */
+
+#define ST_INIT			0	/* Ready for timing byte or msg */
+#define ST_TIMED		1	/* Leading timing byte rcvd */
+#define ST_DATABYTE		2	/* Waiting for (nr_left) data bytes */
+
+#define ST_SYSMSG		100	/* System message (sysx etc). */
+#define ST_SYSEX		101	/* System exclusive msg */
+#define ST_MTC			102	/* Midi Time Code (MTC) qframe msg */
+#define ST_SONGSEL		103	/* Song select */
+#define ST_SONGPOS		104	/* Song position pointer */
+
+static unsigned char len_tab[] =	/* # of data bytes following a status
+					 */
+{
+	2,			/* 8x */
+	2,			/* 9x */
+	2,			/* Ax */
+	2,			/* Bx */
+	1,			/* Cx */
+	1,			/* Dx */
+	2,			/* Ex */
+	0			/* Fx */
+};
+
+#define STORE(cmd) \
+{ \
+	int len; \
+	unsigned char obuf[8]; \
+	cmd; \
+	seq_input_event(obuf, len); \
+}
+
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
+{
+
+	switch (devc->m_state)
+	{
+		case ST_INIT:
+			switch (midic)
+			{
+				case 0xf8:
+				/* Timer overflow */
+					break;
+
+				case 0xfc:
+					printk("<all end>");
+			 		break;
+
+				case 0xfd:
+					if (devc->timer_flag)
+						mpu_timer_interrupt();
+					break;
+
+				case 0xfe:
+					return MPU_ACK;
+
+				case 0xf0:
+				case 0xf1:
+				case 0xf2:
+				case 0xf3:
+				case 0xf4:
+				case 0xf5:
+				case 0xf6:
+				case 0xf7:
+					printk("<Trk data rq #%d>", midic & 0x0f);
+					break;
+
+				case 0xf9:
+					printk("<conductor rq>");
+					break;
+
+				case 0xff:
+					devc->m_state = ST_SYSMSG;
+					break;
+
+				default:
+					if (midic <= 0xef)
+					{
+						/* printk( "mpu time: %d ",  midic); */
+						devc->m_state = ST_TIMED;
+					}
+					else
+						printk("<MPU: Unknown event %02x> ", midic);
+			}
+			break;
+
+		case ST_TIMED:
+			{
+				int msg = ((int) (midic & 0xf0) >> 4);
+
+				devc->m_state = ST_DATABYTE;
+
+				if (msg < 8)	/* Data byte */
+				{
+					/* printk( "midi msg (running status) "); */
+					msg = ((int) (devc->last_status & 0xf0) >> 4);
+					msg -= 8;
+					devc->m_left = len_tab[msg] - 1;
+
+					devc->m_ptr = 2;
+					devc->m_buf[0] = devc->last_status;
+					devc->m_buf[1] = midic;
+
+					if (devc->m_left <= 0)
+					{
+						devc->m_state = ST_INIT;
+						do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+						devc->m_ptr = 0;
+					}
+				}
+				else if (msg == 0xf)	/* MPU MARK */
+				{
+					devc->m_state = ST_INIT;
+
+					switch (midic)
+					{
+						case 0xf8:
+							/* printk( "NOP "); */
+							break;
+
+						case 0xf9:
+							/* printk( "meas end "); */
+							break;
+
+						case 0xfc:
+							/* printk( "data end "); */
+							break;
+
+						default:
+							printk("Unknown MPU mark %02x\n", midic);
+					}
+				}
+				else
+				{
+					devc->last_status = midic;
+					/* printk( "midi msg "); */
+					msg -= 8;
+					devc->m_left = len_tab[msg];
+
+					devc->m_ptr = 1;
+					devc->m_buf[0] = midic;
+
+					if (devc->m_left <= 0)
+					{
+						devc->m_state = ST_INIT;
+						do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+						devc->m_ptr = 0;
+					}
+				}
+			}
+			break;
+
+		case ST_SYSMSG:
+			switch (midic)
+			{
+				case 0xf0:
+					printk("<SYX>");
+					devc->m_state = ST_SYSEX;
+					break;
+
+				case 0xf1:
+					devc->m_state = ST_MTC;
+					break;
+
+				case 0xf2:
+					devc->m_state = ST_SONGPOS;
+					devc->m_ptr = 0;
+					break;
+
+				case 0xf3:
+					devc->m_state = ST_SONGSEL;
+					break;
+
+				case 0xf6:
+					/* printk( "tune_request\n"); */
+					devc->m_state = ST_INIT;
+
+					/*
+					 *    Real time messages
+					 */
+				case 0xf8:
+					/* midi clock */
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_CLOCK, 0);
+					break;
+
+				case 0xfA:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_START, 0);
+					break;
+
+				case 0xFB:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_CONTINUE, 0);
+					break;
+
+				case 0xFC:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_STOP, 0);
+					break;
+
+				case 0xFE:
+					/* active sensing */
+					devc->m_state = ST_INIT;
+					break;
+
+				case 0xff:
+					/* printk( "midi hard reset"); */
+					devc->m_state = ST_INIT;
+					break;
+
+				default:
+					printk("unknown MIDI sysmsg %0x\n", midic);
+					devc->m_state = ST_INIT;
+			}
+			break;
+
+		case ST_MTC:
+			devc->m_state = ST_INIT;
+			printk("MTC frame %x02\n", midic);
+			break;
+
+		case ST_SYSEX:
+			if (midic == 0xf7)
+			{
+				printk("<EOX>");
+				devc->m_state = ST_INIT;
+			}
+			else
+				printk("%02x ", midic);
+			break;
+
+		case ST_SONGPOS:
+			BUFTEST(devc);
+			devc->m_buf[devc->m_ptr++] = midic;
+			if (devc->m_ptr == 2)
+			{
+				devc->m_state = ST_INIT;
+				devc->m_ptr = 0;
+				timer_ext_event(devc, TMR_SPP,
+					((devc->m_buf[1] & 0x7f) << 7) |
+					(devc->m_buf[0] & 0x7f));
+			}
+			break;
+
+		case ST_DATABYTE:
+			BUFTEST(devc);
+			devc->m_buf[devc->m_ptr++] = midic;
+			if ((--devc->m_left) <= 0)
+			{
+				devc->m_state = ST_INIT;
+				do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+				devc->m_ptr = 0;
+			}
+			break;
+
+		default:
+			printk("Bad state %d ", devc->m_state);
+			devc->m_state = ST_INIT;
+	}
+	return 1;
+}
+
+static void mpu401_input_loop(struct mpu_config *devc)
+{
+	unsigned long flags;
+	int busy;
+	int n;
+
+	save_flags(flags);
+	cli();
+	busy = devc->m_busy;
+	devc->m_busy = 1;
+	restore_flags(flags);
+
+	if (busy)		/* Already inside the scanner */
+		return;
+
+	n = 50;
+
+	while (input_avail(devc) && n-- > 0)
+	{
+		unsigned char c = read_data(devc);
+
+		if (devc->mode == MODE_SYNTH)
+		{
+			mpu_input_scanner(devc, c);
+		}
+		else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
+			devc->inputintr(devc->devno, c);
+	}
+	devc->m_busy = 0;
+}
+
+int intchk_mpu401(void *dev_id)
+{
+	struct mpu_config *devc;
+	int dev = (int) dev_id;
+
+	devc = &dev_conf[dev];
+	return input_avail(devc);
+}
+
+void mpuintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	struct mpu_config *devc;
+	int dev = (int) dev_id;
+
+	sti();
+	devc = &dev_conf[dev];
+
+	if (input_avail(devc))
+	{
+		if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
+			mpu401_input_loop(devc);
+		else
+		{
+			/* Dummy read (just to acknowledge the interrupt) */
+			read_data(devc);
+		}
+	}
+}
+
+static int mpu401_open(int dev, int mode,
+	    void            (*input) (int dev, unsigned char data),
+	    void            (*output) (int dev)
+)
+{
+	int err;
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+		return -ENXIO;
+
+	devc = &dev_conf[dev];
+
+	if (devc->opened)
+		  return -EBUSY;
+	/*
+	 *  Verify that the device is really running.
+	 *  Some devices (such as Ensoniq SoundScape don't
+	 *  work before the on board processor (OBP) is initialized
+	 *  by downloading its microcode.
+	 */
+
+	if (!devc->initialized)
+	{
+		if (mpu401_status(devc) == 0xff)	/* Bus float */
+		{
+			printk(KERN_ERR "mpu401: Device not initialized properly\n");
+			return -EIO;
+		}
+		reset_mpu401(devc);
+	}
+
+	if ( (coprocessor = midi_devs[dev]->coproc) != NULL )
+	{
+		if (coprocessor->owner)
+			__MOD_INC_USE_COUNT(coprocessor->owner);
+
+		if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
+		{
+			printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n");
+			mpu401_close(dev);
+			return err;
+		}
+	}
+	
+	set_uart_mode(dev, devc, 1);
+	devc->mode = MODE_MIDI;
+	devc->synthno = 0;
+
+	mpu401_input_loop(devc);
+
+	devc->inputintr = input;
+	devc->opened = mode;
+
+	return 0;
+}
+
+static void mpu401_close(int dev)
+{
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	devc = &dev_conf[dev];
+	if (devc->uart_mode)
+		reset_mpu401(devc);	/*
+					 * This disables the UART mode
+					 */
+	devc->mode = 0;
+	devc->inputintr = NULL;
+
+	coprocessor = midi_devs[dev]->coproc;
+	if (coprocessor) {
+		coprocessor->close(coprocessor->devc, COPR_MIDI);
+
+		if (coprocessor->owner)
+			__MOD_DEC_USE_COUNT(coprocessor->owner);
+	}
+	devc->opened = 0;
+}
+
+static int mpu401_out(int dev, unsigned char midi_byte)
+{
+	int timeout;
+	unsigned long flags;
+
+	struct mpu_config *devc;
+
+	devc = &dev_conf[dev];
+
+	/*
+	 * Sometimes it takes about 30000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+	save_flags(flags);
+	cli();
+	if (!output_ready(devc))
+	{
+		printk(KERN_WARNING "mpu401: Send data timeout\n");
+		restore_flags(flags);
+		return 0;
+	}
+	write_data(devc, midi_byte);
+	restore_flags(flags);
+	return 1;
+}
+
+static int mpu401_command(int dev, mpu_command_rec * cmd)
+{
+	int i, timeout, ok;
+	int ret = 0;
+	unsigned long   flags;
+	struct mpu_config *devc;
+
+	devc = &dev_conf[dev];
+
+	if (devc->uart_mode)	/*
+				 * Not possible in UART mode
+				 */
+	{
+		printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n");
+		return -EINVAL;
+	}
+	/*
+	 * Test for input since pending input seems to block the output.
+	 */
+	if (input_avail(devc))
+		mpu401_input_loop(devc);
+
+	/*
+	 * Sometimes it takes about 50000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	timeout = 50000;
+retry:
+	if (timeout-- <= 0)
+	{
+		printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd);
+		return -EIO;
+	}
+	save_flags(flags);
+	cli();
+
+	if (!output_ready(devc))
+	{
+		  restore_flags(flags);
+		  goto retry;
+	}
+	write_command(devc, cmd->cmd);
+
+	ok = 0;
+	for (timeout = 50000; timeout > 0 && !ok; timeout--)
+	{
+		if (input_avail(devc))
+		{
+			if (devc->opened && devc->mode == MODE_SYNTH)
+			{
+				if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
+					ok = 1;
+			}
+			else
+			{
+				/* Device is not currently open. Use simpler method */
+				if (read_data(devc) == MPU_ACK)
+					ok = 1;
+			}
+		}
+	}
+	if (!ok)
+	{
+		restore_flags(flags);
+		return -EIO;
+	}
+	if (cmd->nr_args)
+	{
+		for (i = 0; i < cmd->nr_args; i++)
+		{
+			for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
+
+			if (!mpu401_out(dev, cmd->data[i]))
+			{
+				restore_flags(flags);
+				printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
+				return -EIO;
+			}
+		}
+	}
+	ret = 0;
+	cmd->data[0] = 0;
+
+	if (cmd->nr_returns)
+	{
+		for (i = 0; i < cmd->nr_returns; i++)
+		{
+			ok = 0;
+			for (timeout = 5000; timeout > 0 && !ok; timeout--)
+				if (input_avail(devc))
+				{
+					cmd->data[i] = read_data(devc);
+					ok = 1;
+				}
+			if (!ok)
+			{
+				restore_flags(flags);
+				return -EIO;
+			}
+		}
+	}
+	restore_flags(flags);
+	return ret;
+}
+
+static int mpu_cmd(int dev, int cmd, int data)
+{
+	int ret;
+
+	static mpu_command_rec rec;
+
+	rec.cmd = cmd & 0xff;
+	rec.nr_args = ((cmd & 0xf0) == 0xE0);
+	rec.nr_returns = ((cmd & 0xf0) == 0xA0);
+	rec.data[0] = data & 0xff;
+
+	if ((ret = mpu401_command(dev, &rec)) < 0)
+		return ret;
+	return (unsigned char) rec.data[0];
+}
+
+static int mpu401_prefix_cmd(int dev, unsigned char status)
+{
+	struct mpu_config *devc = &dev_conf[dev];
+
+	if (devc->uart_mode)
+		return 1;
+
+	if (status < 0xf0)
+	{
+		if (mpu_cmd(dev, 0xD0, 0) < 0)
+			return 0;
+		return 1;
+	}
+	switch (status)
+	{
+		case 0xF0:
+			if (mpu_cmd(dev, 0xDF, 0) < 0)
+				return 0;
+			return 1;
+
+		default:
+			return 0;
+	}
+}
+
+static int mpu401_start_read(int dev)
+{
+	return 0;
+}
+
+static int mpu401_end_read(int dev)
+{
+	return 0;
+}
+
+static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg)
+{
+	struct mpu_config *devc;
+	mpu_command_rec rec;
+	int val, ret;
+
+	devc = &dev_conf[dev];
+	switch (cmd) 
+	{
+		case SNDCTL_MIDI_MPUMODE:
+			if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
+				printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n");
+				return -EINVAL;
+			}
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			set_uart_mode(dev, devc, !val);
+			return 0;
+
+		case SNDCTL_MIDI_MPUCMD:
+			if (copy_from_user(&rec, arg, sizeof(rec)))
+				return -EFAULT;
+			if ((ret = mpu401_command(dev, &rec)) < 0)
+				return ret;
+			if (copy_to_user(arg, &rec, sizeof(rec)))
+				return -EFAULT;
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static void mpu401_kick(int dev)
+{
+}
+
+static int mpu401_buffer_status(int dev)
+{
+	return 0;		/*
+				 * No data in buffers
+				 */
+}
+
+static int mpu_synth_ioctl(int dev,
+		unsigned int cmd, caddr_t arg)
+{
+	int midi_dev;
+	struct mpu_config *devc;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+		return -ENXIO;
+
+	devc = &dev_conf[midi_dev];
+
+	switch (cmd)
+	{
+
+		case SNDCTL_SYNTH_INFO:
+			memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info));
+			return 0;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+static int mpu_synth_open(int dev, int mode)
+{
+	int midi_dev, err;
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+		return -ENXIO;
+
+	devc = &dev_conf[midi_dev];
+
+	/*
+	 *  Verify that the device is really running.
+	 *  Some devices (such as Ensoniq SoundScape don't
+	 *  work before the on board processor (OBP) is initialized
+	 *  by downloading its microcode.
+	 */
+
+	if (!devc->initialized)
+	{
+		if (mpu401_status(devc) == 0xff)	/* Bus float */
+		{
+			printk(KERN_ERR "mpu401: Device not initialized properly\n");
+			return -EIO;
+		}
+		reset_mpu401(devc);
+	}
+	if (devc->opened)
+		return -EBUSY;
+	devc->mode = MODE_SYNTH;
+	devc->synthno = dev;
+
+	devc->inputintr = NULL;
+
+	coprocessor = midi_devs[midi_dev]->coproc;
+	if (coprocessor) {
+		if (coprocessor->owner)
+			__MOD_INC_USE_COUNT(coprocessor->owner);
+
+		if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
+		{
+			printk(KERN_WARNING "mpu401: Can't access coprocessor device\n");
+			return err;
+		}
+	}
+	devc->opened = mode;
+	reset_mpu401(devc);
+
+	if (mode & OPEN_READ)
+	{
+		mpu_cmd(midi_dev, 0x8B, 0);	/* Enable data in stop mode */
+		mpu_cmd(midi_dev, 0x34, 0);	/* Return timing bytes in stop mode */
+		mpu_cmd(midi_dev, 0x87, 0);	/* Enable pitch & controller */
+	}
+	return 0;
+}
+
+static void mpu_synth_close(int dev)
+{ 
+	int midi_dev;
+	struct mpu_config *devc;
+	struct coproc_operations *coprocessor;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	devc = &dev_conf[midi_dev];
+	mpu_cmd(midi_dev, 0x15, 0);	/* Stop recording, playback and MIDI */
+	mpu_cmd(midi_dev, 0x8a, 0);	/* Disable data in stopped mode */
+
+	devc->inputintr = NULL;
+
+	coprocessor = midi_devs[midi_dev]->coproc;
+	if (coprocessor) {
+		coprocessor->close(coprocessor->devc, COPR_MIDI);
+
+		if (coprocessor->owner)
+			__MOD_DEC_USE_COUNT(coprocessor->owner);
+	}
+	devc->opened = 0;
+	devc->mode = 0;
+}
+
+#define MIDI_SYNTH_NAME	"MPU-401 UART Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct synth_operations mpu401_synth_proto =
+{
+	owner:		THIS_MODULE,
+	id:		"MPU401",
+	info:		NULL,
+	midi_dev:	0,
+	synth_type:	SYNTH_TYPE_MIDI,
+	synth_subtype:	0,
+	open:		mpu_synth_open,
+	close:		mpu_synth_close,
+	ioctl:		mpu_synth_ioctl,
+	kill_note:	midi_synth_kill_note,
+	start_note:	midi_synth_start_note,
+	set_instr:	midi_synth_set_instr,
+	reset:		midi_synth_reset,
+	hw_control:	midi_synth_hw_control,
+	load_patch:	midi_synth_load_patch,
+	aftertouch:	midi_synth_aftertouch,
+	controller:	midi_synth_controller,
+	panning:	midi_synth_panning,
+	bender:		midi_synth_bender,
+	setup_voice:	midi_synth_setup_voice,
+	send_sysex:	midi_synth_send_sysex
+};
+
+static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV];
+
+static struct midi_operations mpu401_midi_proto =
+{
+	owner:		THIS_MODULE,
+	info:		{"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+	in_info:	{0},
+	open:		mpu401_open,
+	close:		mpu401_close,
+	ioctl:		mpu401_ioctl,
+	outputc:	mpu401_out,
+	start_read:	mpu401_start_read,
+	end_read:	mpu401_end_read,
+	kick:		mpu401_kick,
+	buffer_status:	mpu401_buffer_status,
+	prefix_cmd:	mpu401_prefix_cmd
+};
+
+static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
+
+static void mpu401_chk_version(int n, struct mpu_config *devc)
+{
+	int tmp;
+	unsigned long flags;
+
+	devc->version = devc->revision = 0;
+
+	save_flags(flags);
+	cli();
+	if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0)
+	{
+		restore_flags(flags);
+		return;
+	}
+	if ((tmp & 0xf0) > 0x20)	/* Why it's larger than 2.x ??? */
+	{
+		restore_flags(flags);
+		return;
+	}
+	devc->version = tmp;
+
+	if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0)
+	{
+		devc->version = 0;
+		restore_flags(flags);
+		return;
+	}
+	devc->revision = tmp;
+	restore_flags(flags);
+}
+
+void attach_mpu401(struct address_info *hw_config, struct module *owner)
+{
+	unsigned long flags;
+	char revision_char;
+
+	int m;
+	struct mpu_config *devc;
+
+	hw_config->slots[1] = -1;
+	m = sound_alloc_mididev();
+	if (m == -1)
+	{
+		printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
+		return;
+	}
+	devc = &dev_conf[m];
+	devc->base = hw_config->io_base;
+	devc->osp = hw_config->osp;
+	devc->irq = hw_config->irq;
+	devc->opened = 0;
+	devc->uart_mode = 0;
+	devc->initialized = 0;
+	devc->version = 0;
+	devc->revision = 0;
+	devc->capabilities = 0;
+	devc->timer_flag = 0;
+	devc->m_busy = 0;
+	devc->m_state = ST_INIT;
+	devc->shared_irq = hw_config->always_detect;
+	devc->irq = hw_config->irq;
+
+	if (devc->irq < 0)
+	{
+		devc->irq *= -1;
+		devc->shared_irq = 1;
+	}
+
+	if (!hw_config->always_detect)
+	{
+		/* Verify the hardware again */
+		if (!reset_mpu401(devc))
+		{
+			printk(KERN_WARNING "mpu401: Device didn't respond\n");
+			sound_unload_mididev(m);
+			return;
+		}
+		if (!devc->shared_irq)
+		{
+			if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0)
+			{
+				printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
+				sound_unload_mididev(m);
+				return;
+			}
+		}
+		save_flags(flags);
+		cli();
+		mpu401_chk_version(m, devc);
+		if (devc->version == 0)
+			mpu401_chk_version(m, devc);
+		restore_flags(flags);
+	}
+	request_region(hw_config->io_base, 2, "mpu401");
+
+	if (devc->version != 0)
+		if (mpu_cmd(m, 0xC5, 0) >= 0)	/* Set timebase OK */
+			if (mpu_cmd(m, 0xE0, 120) >= 0)		/* Set tempo OK */
+				devc->capabilities |= MPU_CAP_INTLG;	/* Supports intelligent mode */
+
+
+	mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+
+	if (mpu401_synth_operations[m] == NULL)
+	{
+		sound_unload_mididev(m);
+		printk(KERN_ERR "mpu401: Can't allocate memory\n");
+		return;
+	}
+	if (!(devc->capabilities & MPU_CAP_INTLG))	/* No intelligent mode */
+	{
+		memcpy((char *) mpu401_synth_operations[m],
+			(char *) &std_midi_synth,
+			 sizeof(struct synth_operations));
+	}
+	else
+	{
+		memcpy((char *) mpu401_synth_operations[m],
+			(char *) &mpu401_synth_proto,
+			 sizeof(struct synth_operations));
+	}
+	if (owner)
+		mpu401_synth_operations[m]->owner = owner;
+
+	memcpy((char *) &mpu401_midi_operations[m],
+	       (char *) &mpu401_midi_proto,
+	       sizeof(struct midi_operations));
+
+	mpu401_midi_operations[m].converter = mpu401_synth_operations[m];
+
+	memcpy((char *) &mpu_synth_info[m],
+	       (char *) &mpu_synth_info_proto,
+	       sizeof(struct synth_info));
+
+	n_mpu_devs++;
+
+	if (devc->version == 0x20 && devc->revision >= 0x07)	/* MusicQuest interface */
+	{
+		int ports = (devc->revision & 0x08) ? 32 : 16;
+
+		devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
+				MPU_CAP_CLS | MPU_CAP_2PORT;
+
+		revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
+		sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d",
+				ports,
+				revision_char,
+				n_mpu_devs);
+	}
+	else
+	{
+		revision_char = devc->revision ? devc->revision + '@' : ' ';
+		if ((int) devc->revision > ('Z' - '@'))
+			revision_char = '+';
+
+		devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
+
+		if (hw_config->name)
+			sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
+		else
+			sprintf(mpu_synth_info[m].name,
+				"MPU-401 %d.%d%c Midi interface #%d",
+				(int) (devc->version & 0xf0) >> 4,
+				devc->version & 0x0f,
+				revision_char,
+				n_mpu_devs);
+	}
+
+	strcpy(mpu401_midi_operations[m].info.name,
+	       mpu_synth_info[m].name);
+
+	conf_printf(mpu_synth_info[m].name, hw_config);
+
+	mpu401_synth_operations[m]->midi_dev = devc->devno = m;
+	mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno];
+
+	if (devc->capabilities & MPU_CAP_INTLG)		/* Intelligent mode */
+		hw_config->slots[2] = mpu_timer_init(m);
+
+	midi_devs[m] = &mpu401_midi_operations[devc->devno];
+	
+	if (owner)
+		midi_devs[m]->owner = owner;
+
+	hw_config->slots[1] = m;
+	sequencer_init();
+}
+
+static int reset_mpu401(struct mpu_config *devc)
+{
+	unsigned long flags;
+	int ok, timeout, n;
+	int timeout_limit;
+
+	/*
+	 * Send the RESET command. Try again if no success at the first time.
+	 * (If the device is in the UART mode, it will not ack the reset cmd).
+	 */
+
+	ok = 0;
+
+	timeout_limit = devc->initialized ? 30000 : 100000;
+	devc->initialized = 1;
+
+	for (n = 0; n < 2 && !ok; n++)
+	{
+		for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
+			  ok = output_ready(devc);
+
+		write_command(devc, MPU_RESET);	/*
+							   * Send MPU-401 RESET Command
+							 */
+
+		/*
+		 * Wait at least 25 msec. This method is not accurate so let's make the
+		 * loop bit longer. Cannot sleep since this is called during boot.
+		 */
+
+		for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
+		{
+			save_flags(flags);
+			cli();
+			if (input_avail(devc))
+				if (read_data(devc) == MPU_ACK)
+					ok = 1;
+			restore_flags(flags);
+		}
+
+	}
+
+	devc->m_state = ST_INIT;
+	devc->m_ptr = 0;
+	devc->m_left = 0;
+	devc->last_status = 0;
+	devc->uart_mode = 0;
+
+	return ok;
+}
+
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg)
+{
+	if (!arg && (devc->capabilities & MPU_CAP_INTLG))
+		return;
+	if ((devc->uart_mode == 0) == (arg == 0))
+		return;		/* Already set */
+	reset_mpu401(devc);	/* This exits the uart mode */
+
+	if (arg)
+	{
+		if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
+		{
+			printk(KERN_ERR "mpu401: Can't enter UART mode\n");
+			devc->uart_mode = 0;
+			return;
+		}
+	}
+	devc->uart_mode = arg;
+
+}
+
+int probe_mpu401(struct address_info *hw_config)
+{
+	int ok = 0;
+	struct mpu_config tmp_devc;
+
+	if (check_region(hw_config->io_base, 2))
+	{
+		printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base);
+		return 0;
+	}
+	tmp_devc.base = hw_config->io_base;
+	tmp_devc.irq = hw_config->irq;
+	tmp_devc.initialized = 0;
+	tmp_devc.opened = 0;
+	tmp_devc.osp = hw_config->osp;
+
+	if (hw_config->always_detect)
+		return 1;
+
+	if (inb(hw_config->io_base + 1) == 0xff)
+	{
+		DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
+		return 0;	/* Just bus float? */
+	}
+	ok = reset_mpu401(&tmp_devc);
+
+	if (!ok)
+	{
+		DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
+	}
+	return ok;
+}
+
+void unload_mpu401(struct address_info *hw_config)
+{
+	void *p;
+	int n=hw_config->slots[1];
+	
+	release_region(hw_config->io_base, 2);
+	if (hw_config->always_detect == 0 && hw_config->irq > 0)
+		free_irq(hw_config->irq, (void *)n);
+	p=mpu401_synth_operations[n];
+	sound_unload_mididev(n);
+	sound_unload_timerdev(hw_config->slots[2]);
+	if(p)
+		kfree(p);
+}
+
+/*****************************************************
+ *      Timer stuff
+ ****************************************************/
+
+static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
+static volatile int curr_tempo, curr_timebase, hw_timebase;
+static int      max_timebase = 8;	/* 8*24=192 ppqn */
+static volatile unsigned long next_event_time;
+static volatile unsigned long curr_ticks, curr_clocks;
+static unsigned long prev_event_time;
+static int      metronome_mode;
+
+static unsigned long clocks2ticks(unsigned long clocks)
+{
+	/*
+	 * The MPU-401 supports just a limited set of possible timebase values.
+	 * Since the applications require more choices, the driver has to
+	 * program the HW to do its best and to convert between the HW and
+	 * actual timebases.
+	 */
+	return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
+}
+
+static void set_timebase(int midi_dev, int val)
+{
+	int hw_val;
+
+	if (val < 48)
+		val = 48;
+	if (val > 1000)
+		val = 1000;
+
+	hw_val = val;
+	hw_val = (hw_val + 12) / 24;
+	if (hw_val > max_timebase)
+		hw_val = max_timebase;
+
+	if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
+	{
+		printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24);
+		return;
+	}
+	hw_timebase = hw_val * 24;
+	curr_timebase = val;
+
+}
+
+static void tmr_reset(void)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	next_event_time = (unsigned long) -1;
+	prev_event_time = 0;
+	curr_ticks = curr_clocks = 0;
+	restore_flags(flags);
+}
+
+static void set_timer_mode(int midi_dev)
+{
+	if (timer_mode & TMR_MODE_CLS)
+		mpu_cmd(midi_dev, 0x3c, 0);	/* Use CLS sync */
+	else if (timer_mode & TMR_MODE_SMPTE)
+		mpu_cmd(midi_dev, 0x3d, 0);	/* Use SMPTE sync */
+
+	if (timer_mode & TMR_INTERNAL)
+	{
+		  mpu_cmd(midi_dev, 0x80, 0);	/* Use MIDI sync */
+	}
+	else
+	{
+		if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
+		{
+			mpu_cmd(midi_dev, 0x82, 0);		/* Use MIDI sync */
+			mpu_cmd(midi_dev, 0x91, 0);		/* Enable ext MIDI ctrl */
+		}
+		else if (timer_mode & TMR_MODE_FSK)
+			mpu_cmd(midi_dev, 0x81, 0);	/* Use FSK sync */
+	}
+}
+
+static void stop_metronome(int midi_dev)
+{
+	mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */
+}
+
+static void setup_metronome(int midi_dev)
+{
+	int numerator, denominator;
+	int clks_per_click, num_32nds_per_beat;
+	int beats_per_measure;
+
+	numerator = ((unsigned) metronome_mode >> 24) & 0xff;
+	denominator = ((unsigned) metronome_mode >> 16) & 0xff;
+	clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
+	num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
+	beats_per_measure = (numerator * 4) >> denominator;
+
+	if (!metronome_mode)
+		mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */
+	else
+	{
+		mpu_cmd(midi_dev, 0xE4, clks_per_click);
+		mpu_cmd(midi_dev, 0xE6, beats_per_measure);
+		mpu_cmd(midi_dev, 0x83, 0);	/* Enable metronome without accents */
+	}
+}
+
+static int mpu_start_timer(int midi_dev)
+{
+	tmr_reset();
+	set_timer_mode(midi_dev);
+
+	if (tmr_running)
+		return TIMER_NOT_ARMED;		/* Already running */
+
+	if (timer_mode & TMR_INTERNAL)
+	{
+		mpu_cmd(midi_dev, 0x02, 0);	/* Send MIDI start */
+		tmr_running = 1;
+		return TIMER_NOT_ARMED;
+	}
+	else
+	{
+		mpu_cmd(midi_dev, 0x35, 0);	/* Enable mode messages to PC */
+		mpu_cmd(midi_dev, 0x38, 0);	/* Enable sys common messages to PC */
+		mpu_cmd(midi_dev, 0x39, 0);	/* Enable real time messages to PC */
+		mpu_cmd(midi_dev, 0x97, 0);	/* Enable system exclusive messages to PC */
+	}
+	return TIMER_ARMED;
+}
+
+static int mpu_timer_open(int dev, int mode)
+{
+	int midi_dev = sound_timer_devs[dev]->devlink;
+
+	if (timer_open)
+		return -EBUSY;
+
+	tmr_reset();
+	curr_tempo = 50;
+	mpu_cmd(midi_dev, 0xE0, 50);
+	curr_timebase = hw_timebase = 120;
+	set_timebase(midi_dev, 120);
+	timer_open = 1;
+	metronome_mode = 0;
+	set_timer_mode(midi_dev);
+
+	mpu_cmd(midi_dev, 0xe7, 0x04);	/* Send all clocks to host */
+	mpu_cmd(midi_dev, 0x95, 0);	/* Enable clock to host */
+
+	return 0;
+}
+
+static void mpu_timer_close(int dev)
+{
+	int midi_dev = sound_timer_devs[dev]->devlink;
+
+	timer_open = tmr_running = 0;
+	mpu_cmd(midi_dev, 0x15, 0);	/* Stop all */
+	mpu_cmd(midi_dev, 0x94, 0);	/* Disable clock to host */
+	mpu_cmd(midi_dev, 0x8c, 0);	/* Disable measure end messages to host */
+	stop_metronome(midi_dev);
+}
+
+static int mpu_timer_event(int dev, unsigned char *event)
+{
+	unsigned char command = event[1];
+	unsigned long parm = *(unsigned int *) &event[4];
+	int midi_dev = sound_timer_devs[dev]->devlink;
+
+	switch (command)
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				if (parm <= curr_ticks)	/* It's the time */
+					return TIMER_NOT_ARMED;
+				time = parm;
+				next_event_time = prev_event_time = time;
+
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			if (tmr_running)
+				break;
+			return mpu_start_timer(midi_dev);
+
+		case TMR_STOP:
+			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
+			stop_metronome(midi_dev);
+			tmr_running = 0;
+			break;
+
+		case TMR_CONTINUE:
+			if (tmr_running)
+				break;
+			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
+			setup_metronome(midi_dev);
+			tmr_running = 1;
+			break;
+
+		case TMR_TEMPO:
+			if (parm)
+			{
+				if (parm < 8)
+					parm = 8;
+			 	if (parm > 250)
+					parm = 250;
+				if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
+					printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm);
+				curr_tempo = parm;
+			}
+			break;
+
+		case TMR_ECHO:
+			seq_copy_to_input(event, 8);
+			break;
+
+		case TMR_TIMESIG:
+			if (metronome_mode)	/* Metronome enabled */
+			{
+				metronome_mode = parm;
+				setup_metronome(midi_dev);
+			}
+			break;
+
+		default:;
+	}
+	return TIMER_NOT_ARMED;
+}
+
+static unsigned long mpu_timer_get_time(int dev)
+{
+	if (!timer_open)
+		return 0;
+
+	return curr_ticks;
+}
+
+static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg)
+{
+	int midi_dev = sound_timer_devs[dev]->devlink;
+
+	switch (command)
+	{
+		case SNDCTL_TMR_SOURCE:
+			{
+				int parm;
+	
+				parm = *(int *) arg;
+				parm &= timer_caps;
+
+				if (parm != 0)
+				{
+					timer_mode = parm;
+	
+					if (timer_mode & TMR_MODE_CLS)
+						mpu_cmd(midi_dev, 0x3c, 0);		/* Use CLS sync */
+					else if (timer_mode & TMR_MODE_SMPTE)
+						mpu_cmd(midi_dev, 0x3d, 0);		/* Use SMPTE sync */
+				}
+				return (*(int *) arg = timer_mode);
+			}
+			break;
+
+		case SNDCTL_TMR_START:
+			mpu_start_timer(midi_dev);
+			return 0;
+
+		case SNDCTL_TMR_STOP:
+			tmr_running = 0;
+			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
+			stop_metronome(midi_dev);
+			return 0;
+
+		case SNDCTL_TMR_CONTINUE:
+			if (tmr_running)
+				return 0;
+			tmr_running = 1;
+			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
+			return 0;
+
+		case SNDCTL_TMR_TIMEBASE:
+			{
+				int val;
+
+				val = *(int *) arg;
+				if (val)
+					set_timebase(midi_dev, val);
+				return (*(int *) arg = curr_timebase);
+			}
+			break;
+
+		case SNDCTL_TMR_TEMPO:
+			{
+				int val;
+				int ret;
+
+				val = *(int *) arg;
+
+				if (val)
+				{
+					if (val < 8)
+						val = 8;
+					if (val > 250)
+						val = 250;
+					if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
+					{
+						printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val);
+						return ret;
+					}
+					curr_tempo = val;
+				}
+				return (*(int *) arg = curr_tempo);
+			}
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			{
+				int val;
+
+				val = *(int *) arg;
+				if (val != 0)		/* Can't change */
+					return -EINVAL;
+				return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
+			}
+			break;
+
+		case SNDCTL_SEQ_GETTIME:
+			return (*(int *) arg = curr_ticks);
+
+		case SNDCTL_TMR_METRONOME:
+			metronome_mode = *(int *) arg;
+			setup_metronome(midi_dev);
+			return 0;
+
+		default:;
+	}
+	return -EINVAL;
+}
+
+static void mpu_timer_arm(int dev, long time)
+{
+	if (time < 0)
+		time = curr_ticks + 1;
+	else if (time <= curr_ticks)	/* It's the time */
+		return;
+	next_event_time = prev_event_time = time;
+	return;
+}
+
+static struct sound_timer_operations mpu_timer =
+{
+	owner:		THIS_MODULE,
+	info:		{"MPU-401 Timer", 0},
+	priority:	10,	/* Priority */
+	devlink:	0,	/* Local device link */
+	open:		mpu_timer_open,
+	close:		mpu_timer_close,
+	event:		mpu_timer_event,
+	get_time:	mpu_timer_get_time,
+	ioctl:		mpu_timer_ioctl,
+	arm_timer:	mpu_timer_arm
+};
+
+static void mpu_timer_interrupt(void)
+{
+	if (!timer_open)
+		return;
+
+	if (!tmr_running)
+		return;
+
+	curr_clocks++;
+	curr_ticks = clocks2ticks(curr_clocks);
+
+	if (curr_ticks >= next_event_time)
+	{
+		next_event_time = (unsigned long) -1;
+		sequencer_timer(0);
+	}
+}
+
+static void timer_ext_event(struct mpu_config *devc, int event, int parm)
+{
+	int midi_dev = devc->devno;
+
+	if (!devc->timer_flag)
+		return;
+
+	switch (event)
+	{
+		case TMR_CLOCK:
+			printk("<MIDI clk>");
+			break;
+
+		case TMR_START:
+			printk("Ext MIDI start\n");
+			if (!tmr_running)
+			{
+				if (timer_mode & TMR_EXTERNAL)
+				{
+					tmr_running = 1;
+					setup_metronome(midi_dev);
+					next_event_time = 0;
+					STORE(SEQ_START_TIMER());
+				}
+			}
+			break;
+
+		case TMR_STOP:
+			printk("Ext MIDI stop\n");
+			if (timer_mode & TMR_EXTERNAL)
+			{
+				tmr_running = 0;
+				stop_metronome(midi_dev);
+				STORE(SEQ_STOP_TIMER());
+			}
+			break;
+
+		case TMR_CONTINUE:
+			printk("Ext MIDI continue\n");
+			if (timer_mode & TMR_EXTERNAL)
+			{
+				tmr_running = 1;
+				setup_metronome(midi_dev);
+				STORE(SEQ_CONTINUE_TIMER());
+		  	}
+		  	break;
+
+		case TMR_SPP:
+			printk("Songpos: %d\n", parm);
+			if (timer_mode & TMR_EXTERNAL)
+			{
+				STORE(SEQ_SONGPOS(parm));
+			}
+			break;
+	}
+}
+
+static int mpu_timer_init(int midi_dev)
+{
+	struct mpu_config *devc;
+	int n;
+
+	devc = &dev_conf[midi_dev];
+
+	if (timer_initialized)
+		return -1;	/* There is already a similar timer */
+
+	timer_initialized = 1;
+
+	mpu_timer.devlink = midi_dev;
+	dev_conf[midi_dev].timer_flag = 1;
+
+	n = sound_alloc_timerdev();
+	if (n == -1)
+		n = 0;
+	sound_timer_devs[n] = &mpu_timer;
+
+	if (devc->version < 0x20)	/* Original MPU-401 */
+		timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
+	else
+	{
+		/*
+		 * The version number 2.0 is used (at least) by the
+		 * MusicQuest cards and the Roland Super-MPU.
+		 *
+		 * MusicQuest has given a special meaning to the bits of the
+		 * revision number. The Super-MPU returns 0.
+		 */
+
+		if (devc->revision)
+			timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
+
+		if (devc->revision & 0x02)
+			timer_caps |= TMR_MODE_CLS;
+
+
+		if (devc->revision & 0x40)
+			max_timebase = 10;	/* Has the 216 and 240 ppqn modes */
+	}
+
+	timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
+	return n;
+
+}
+
+EXPORT_SYMBOL(probe_mpu401);
+EXPORT_SYMBOL(attach_mpu401);
+EXPORT_SYMBOL(unload_mpu401);
+EXPORT_SYMBOL(intchk_mpu401);
+EXPORT_SYMBOL(mpuintr);
+
+static struct address_info cfg;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+
+MODULE_PARM(irq, "i");
+MODULE_PARM(io, "i");
+
+int __init init_mpu401(void)
+{
+	/* Can be loaded either for module use or to provide functions
+	   to others */
+	if (io != -1 && irq != -1) {
+	        cfg.irq = irq;
+		cfg.io_base = io;
+		if (probe_mpu401(&cfg) == 0)
+			return -ENODEV;
+		attach_mpu401(&cfg, THIS_MODULE);
+	}
+	
+	return 0;
+}
+
+void __exit cleanup_mpu401(void)
+{
+	if (io != -1 && irq != -1) {
+		/* Check for use by, for example, sscape driver */
+		unload_mpu401(&cfg);
+	}
+}
+
+module_init(init_mpu401);
+module_exit(cleanup_mpu401);
+
+#ifndef MODULE
+static int __init setup_mpu401(char *str)
+{
+        /* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+	irq = ints[2];
+
+	return 1;
+}
+
+__setup("mpu401=", setup_mpu401);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/mpu401.h linux-2.4.19-pre5-mjc/sound/oss/mpu401.h
--- linux/sound/oss/mpu401.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/mpu401.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,20 @@
+/*
+ *	uart401.h 
+ *
+ * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
+ *
+ */
+
+/*	From uart401.c */
+int probe_uart401 (struct address_info *hw_config, struct module *owner);
+void unload_uart401 (struct address_info *hw_config);
+
+void uart401intr (int irq, void *dev_id, struct pt_regs * dummy);
+
+/*	From mpu401.c */
+int probe_mpu401(struct address_info *hw_config);
+void attach_mpu401(struct address_info * hw_config, struct module *owner);
+void unload_mpu401(struct address_info *hw_info);
+
+int intchk_mpu401(void *dev_id);
+void mpuintr(int irq, void *dev_id, struct pt_regs * dummy);
diff -Nru linux/sound/oss/msnd.c linux-2.4.19-pre5-mjc/sound/oss/msnd.c
--- linux/sound/oss/msnd.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/msnd.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,406 @@
+/*********************************************************************
+ *
+ * msnd.c - Driver Base
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $
+ *
+ ********************************************************************/
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 0x020101
+#  define LINUX20
+#endif
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#ifdef LINUX20
+#  include <linux/major.h>
+#  include <linux/fs.h>
+#  include <linux/sound.h>
+#  include <asm/segment.h>
+#  include "sound_config.h"
+#else
+#  include <linux/init.h>
+#  include <asm/io.h>
+#  include <asm/uaccess.h>
+#  include <linux/spinlock.h>
+#endif
+#include <asm/irq.h>
+#include "msnd.h"
+
+#define LOGNAME			"msnd"
+
+#define MSND_MAX_DEVS		4
+
+static multisound_dev_t		*devs[MSND_MAX_DEVS];
+static int			num_devs;
+
+int __init msnd_register(multisound_dev_t *dev)
+{
+	int i;
+
+	for (i = 0; i < MSND_MAX_DEVS; ++i)
+		if (devs[i] == NULL)
+			break;
+
+	if (i == MSND_MAX_DEVS)
+		return -ENOMEM;
+
+	devs[i] = dev;
+	++num_devs;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+void msnd_unregister(multisound_dev_t *dev)
+{
+	int i;
+
+	for (i = 0; i < MSND_MAX_DEVS; ++i)
+		if (devs[i] == dev)
+			break;
+
+	if (i == MSND_MAX_DEVS) {
+		printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
+		return;
+	}
+
+	devs[i] = NULL;
+	--num_devs;
+
+	MOD_DEC_USE_COUNT;
+}
+
+int msnd_get_num_devs(void)
+{
+	return num_devs;
+}
+
+multisound_dev_t *msnd_get_dev(int j)
+{
+	int i;
+
+	for (i = 0; i < MSND_MAX_DEVS && j; ++i)
+		if (devs[i] != NULL)
+			--j;
+
+	if (i == MSND_MAX_DEVS || j != 0)
+		return NULL;
+
+	return devs[i];
+}
+
+void msnd_init_queue(unsigned long base, int start, int size)
+{
+	isa_writew(PCTODSP_BASED(start), base + JQS_wStart);
+	isa_writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
+	isa_writew(0, base + JQS_wHead);
+	isa_writew(0, base + JQS_wTail);
+}
+
+void msnd_fifo_init(msnd_fifo *f)
+{
+	f->data = NULL;
+}
+
+void msnd_fifo_free(msnd_fifo *f)
+{
+	if (f->data) {
+		vfree(f->data);
+		f->data = NULL;
+	}
+}
+
+int msnd_fifo_alloc(msnd_fifo *f, size_t n)
+{
+	msnd_fifo_free(f);
+	f->data = (char *)vmalloc(n);
+	f->n = n;
+	f->tail = 0;
+	f->head = 0;
+	f->len = 0;
+
+	if (!f->data)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void msnd_fifo_make_empty(msnd_fifo *f)
+{
+	f->len = f->tail = f->head = 0;
+}
+
+int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user)
+{
+	int count = 0;
+
+	if (f->len == f->n)
+		return 0;
+
+	while ((count < len) && (f->len != f->n)) {
+
+		int nwritten;
+
+		if (f->head <= f->tail) {
+			nwritten = len - count;
+			if (nwritten > f->n - f->tail)
+				nwritten = f->n - f->tail;
+		}
+		else {
+			nwritten = f->head - f->tail;
+			if (nwritten > len - count)
+				nwritten = len - count;
+		}
+
+		if (user) {
+			if (copy_from_user(f->data + f->tail, buf, nwritten))
+				return -EFAULT;
+		} else
+			isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten);
+
+		count += nwritten;
+		buf += nwritten;
+		f->len += nwritten;
+		f->tail += nwritten;
+		f->tail %= f->n;
+	}
+
+	return count;
+}
+
+int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user)
+{
+	int count = 0;
+
+	if (f->len == 0)
+		return f->len;
+
+	while ((count < len) && (f->len > 0)) {
+
+		int nread;
+
+		if (f->tail <= f->head) {
+			nread = len - count;
+			if (nread > f->n - f->head)
+				nread = f->n - f->head;
+		}
+		else {
+			nread = f->tail - f->head;
+			if (nread > len - count)
+				nread = len - count;
+		}
+
+		if (user) {
+			if (copy_to_user(buf, f->data + f->head, nread))
+				return -EFAULT;
+		} else
+			isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread);
+
+		count += nread;
+		buf += nread;
+		f->len -= nread;
+		f->head += nread;
+		f->head %= f->n;
+	}
+
+	return count;
+}
+
+int msnd_wait_TXDE(multisound_dev_t *dev)
+{
+	register unsigned int io = dev->io;
+	register int timeout = 1000;
+    
+	while(timeout-- > 0)
+		if (inb(io + HP_ISR) & HPISR_TXDE)
+			return 0;
+
+	return -EIO;
+}
+
+int msnd_wait_HC0(multisound_dev_t *dev)
+{
+	register unsigned int io = dev->io;
+	register int timeout = 1000;
+
+	while(timeout-- > 0)
+		if (!(inb(io + HP_CVR) & HPCVR_HC))
+			return 0;
+
+	return -EIO;
+}
+
+int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (msnd_wait_HC0(dev) == 0) {
+		outb(cmd, dev->io + HP_CVR);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
+
+	return -EIO;
+}
+
+int msnd_send_word(multisound_dev_t *dev, unsigned char high,
+		   unsigned char mid, unsigned char low)
+{
+	register unsigned int io = dev->io;
+
+	if (msnd_wait_TXDE(dev) == 0) {
+		outb(high, io + HP_TXH);
+		outb(mid, io + HP_TXM);
+		outb(low, io + HP_TXL);
+		return 0;
+	}
+
+	printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
+
+	return -EIO;
+}
+
+int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
+{
+	int i;
+
+	if (len % 3 != 0) {
+		printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");		
+		return -EINVAL;
+	}
+
+	for (i = 0; i < len; i += 3)
+		if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
+			return -EIO;
+
+	inb(dev->io + HP_RXL);
+	inb(dev->io + HP_CVR);
+
+	return 0;
+}
+
+int msnd_enable_irq(multisound_dev_t *dev)
+{
+	unsigned long flags;
+
+	if (dev->irq_ref++)
+		return 0;
+
+	printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (msnd_wait_TXDE(dev) == 0) {
+		outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
+		if (dev->type == msndClassic)
+			outb(dev->irqid, dev->io + HP_IRQM);
+		outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
+		outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
+		enable_irq(dev->irq);
+		msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
+
+	return -EIO;
+}
+
+int msnd_disable_irq(multisound_dev_t *dev)
+{
+	unsigned long flags;
+
+	if (--dev->irq_ref > 0)
+		return 0;
+
+	if (dev->irq_ref < 0)
+		printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
+
+	printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (msnd_wait_TXDE(dev) == 0) {
+		outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
+		if (dev->type == msndClassic)
+			outb(HPIRQ_NONE, dev->io + HP_IRQM);
+		disable_irq(dev->irq);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
+
+	return -EIO;
+}
+
+#ifndef LINUX20
+EXPORT_SYMBOL(msnd_register);
+EXPORT_SYMBOL(msnd_unregister);
+EXPORT_SYMBOL(msnd_get_num_devs);
+EXPORT_SYMBOL(msnd_get_dev);
+
+EXPORT_SYMBOL(msnd_init_queue);
+
+EXPORT_SYMBOL(msnd_fifo_init);
+EXPORT_SYMBOL(msnd_fifo_free);
+EXPORT_SYMBOL(msnd_fifo_alloc);
+EXPORT_SYMBOL(msnd_fifo_make_empty);
+EXPORT_SYMBOL(msnd_fifo_write);
+EXPORT_SYMBOL(msnd_fifo_read);
+
+EXPORT_SYMBOL(msnd_wait_TXDE);
+EXPORT_SYMBOL(msnd_wait_HC0);
+EXPORT_SYMBOL(msnd_send_dsp_cmd);
+EXPORT_SYMBOL(msnd_send_word);
+EXPORT_SYMBOL(msnd_upload_host);
+
+EXPORT_SYMBOL(msnd_enable_irq);
+EXPORT_SYMBOL(msnd_disable_irq);
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR				("Andrew Veliath <andrewtv@usa.net>");
+MODULE_DESCRIPTION			("Turtle Beach MultiSound Driver Base");
+MODULE_LICENSE("GPL");
+
+
+int init_module(void)
+{
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
diff -Nru linux/sound/oss/msnd.h linux-2.4.19-pre5-mjc/sound/oss/msnd.h
--- linux/sound/oss/msnd.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/msnd.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,281 @@
+/*********************************************************************
+ *
+ * msnd.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $
+ *
+ ********************************************************************/
+#ifndef __MSND_H
+#define __MSND_H
+
+#define VERSION			"0.8.3.1"
+
+#define DEFSAMPLERATE		DSP_DEFAULT_SPEED
+#define DEFSAMPLESIZE		AFMT_U8
+#define DEFCHANNELS		1
+
+#define DEFFIFOSIZE		128
+
+#define SNDCARD_MSND		38
+
+#define SRAM_BANK_SIZE		0x8000
+#define SRAM_CNTL_START		0x7F00
+
+#define DSP_BASE_ADDR		0x4000
+#define DSP_BANK_BASE		0x4000
+
+#define	HP_ICR			0x00
+#define	HP_CVR			0x01
+#define	HP_ISR			0x02
+#define	HP_IVR			0x03
+#define HP_NU			0x04
+#define HP_INFO			0x04
+#define	HP_TXH			0x05
+#define	HP_RXH			0x05
+#define	HP_TXM			0x06
+#define	HP_RXM			0x06
+#define	HP_TXL			0x07
+#define	HP_RXL			0x07
+
+#define HP_ICR_DEF		0x00
+#define HP_CVR_DEF		0x12
+#define HP_ISR_DEF		0x06
+#define HP_IVR_DEF		0x0f
+#define HP_NU_DEF		0x00
+
+#define	HP_IRQM			0x09
+
+#define	HPR_BLRC		0x08
+#define	HPR_SPR1		0x09
+#define	HPR_SPR2		0x0A
+#define	HPR_TCL0		0x0B
+#define	HPR_TCL1		0x0C
+#define	HPR_TCL2		0x0D
+#define	HPR_TCL3		0x0E
+#define	HPR_TCL4		0x0F
+
+#define	HPICR_INIT		0x80
+#define HPICR_HM1		0x40
+#define HPICR_HM0		0x20
+#define HPICR_HF1		0x10
+#define HPICR_HF0		0x08
+#define	HPICR_TREQ		0x02
+#define	HPICR_RREQ		0x01
+
+#define HPCVR_HC		0x80
+
+#define	HPISR_HREQ		0x80
+#define HPISR_DMA		0x40
+#define HPISR_HF3		0x10
+#define HPISR_HF2		0x08
+#define	HPISR_TRDY		0x04
+#define	HPISR_TXDE		0x02
+#define	HPISR_RXDF		0x01
+
+#define	HPIO_290		0
+#define	HPIO_260		1
+#define	HPIO_250		2
+#define	HPIO_240		3
+#define	HPIO_230		4
+#define	HPIO_220		5
+#define	HPIO_210		6
+#define	HPIO_3E0		7
+
+#define	HPMEM_NONE		0
+#define	HPMEM_B000		1
+#define	HPMEM_C800		2
+#define	HPMEM_D000		3
+#define	HPMEM_D400		4
+#define	HPMEM_D800		5
+#define	HPMEM_E000		6
+#define	HPMEM_E800		7
+
+#define	HPIRQ_NONE		0
+#define HPIRQ_5			1
+#define HPIRQ_7			2
+#define HPIRQ_9			3
+#define HPIRQ_10		4
+#define HPIRQ_11		5
+#define HPIRQ_12		6
+#define HPIRQ_15		7
+
+#define	HIMT_PLAY_DONE		0x00
+#define	HIMT_RECORD_DONE	0x01
+#define	HIMT_MIDI_EOS		0x02
+#define	HIMT_MIDI_OUT		0x03
+
+#define	HIMT_MIDI_IN_UCHAR	0x0E
+#define	HIMT_DSP		0x0F
+
+#define	HDEX_BASE	       	0x92
+#define	HDEX_PLAY_START		(0 + HDEX_BASE)
+#define	HDEX_PLAY_STOP		(1 + HDEX_BASE)
+#define	HDEX_PLAY_PAUSE		(2 + HDEX_BASE)
+#define	HDEX_PLAY_RESUME	(3 + HDEX_BASE)
+#define	HDEX_RECORD_START	(4 + HDEX_BASE)
+#define	HDEX_RECORD_STOP	(5 + HDEX_BASE)
+#define	HDEX_MIDI_IN_START 	(6 + HDEX_BASE)
+#define	HDEX_MIDI_IN_STOP	(7 + HDEX_BASE)
+#define	HDEX_MIDI_OUT_START	(8 + HDEX_BASE)
+#define	HDEX_MIDI_OUT_STOP	(9 + HDEX_BASE)
+#define	HDEX_AUX_REQ		(10 + HDEX_BASE)
+
+#define HIWORD(l)		((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
+#define LOWORD(l)		((WORD)(DWORD)(l))
+#define HIBYTE(w)		((BYTE)(((WORD)(w) >> 8) & 0xFF))
+#define LOBYTE(w)		((BYTE)(w))
+#define MAKELONG(low,hi)	((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16)))
+#define MAKEWORD(low,hi)	((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8)))
+
+#define PCTODSP_OFFSET(w)	(USHORT)((w)/2)
+#define PCTODSP_BASED(w)	(USHORT)(((w)/2) + DSP_BASE_ADDR)
+#define DSPTOPC_BASED(w)	(((w) - DSP_BASE_ADDR) * 2)
+
+#ifdef SLOWIO
+#  undef outb
+#  undef inb
+#  define outb			outb_p
+#  define inb			inb_p
+#endif
+
+/* JobQueueStruct */
+#define JQS_wStart		0x00
+#define JQS_wSize		0x02
+#define JQS_wHead		0x04
+#define JQS_wTail		0x06
+#define JQS__size		0x08
+
+/* DAQueueDataStruct */
+#define DAQDS_wStart		0x00
+#define DAQDS_wSize		0x02
+#define DAQDS_wFormat		0x04
+#define DAQDS_wSampleSize	0x06
+#define DAQDS_wChannels		0x08
+#define DAQDS_wSampleRate	0x0A
+#define DAQDS_wIntMsg		0x0C
+#define DAQDS_wFlags		0x0E
+#define DAQDS__size		0x10
+
+typedef u8			BYTE;
+typedef u16			USHORT;
+typedef u16			WORD;
+typedef u32			DWORD;
+typedef unsigned long		LPDAQD;
+
+/* Generic FIFO */
+typedef struct {
+	size_t n, len;
+	char *data;
+	int head, tail;
+} msnd_fifo;
+
+typedef struct multisound_dev {
+	/* Linux device info */
+	char *name;
+	int dsp_minor, mixer_minor;
+	int ext_midi_dev, hdr_midi_dev;
+
+	/* Hardware resources */
+	int io, numio;
+	int memid, irqid;
+	int irq, irq_ref;
+	unsigned char info;
+	unsigned long base;
+
+	/* Motorola 56k DSP SMA */
+	unsigned long SMA;
+	unsigned long DAPQ, DARQ, MODQ, MIDQ, DSPQ;
+	unsigned long pwDSPQData, pwMIDQData, pwMODQData;
+	int dspq_data_buff, dspq_buff_size;
+
+	/* State variables */
+	enum { msndClassic, msndPinnacle } type;
+	mode_t mode;
+	unsigned long flags;
+#define F_RESETTING			0
+#define F_HAVEDIGITAL			1
+#define F_AUDIO_WRITE_INUSE		2
+#define F_WRITING			3
+#define F_WRITEBLOCK			4
+#define F_WRITEFLUSH			5
+#define F_AUDIO_READ_INUSE		6
+#define F_READING			7
+#define F_READBLOCK			8
+#define F_EXT_MIDI_INUSE		9
+#define F_HDR_MIDI_INUSE		10
+#define F_DISABLE_WRITE_NDELAY		11
+	wait_queue_head_t writeblock;
+	wait_queue_head_t readblock;
+	wait_queue_head_t writeflush;
+	spinlock_t lock;
+	int nresets;
+	unsigned long recsrc;
+	int left_levels[16];
+	int right_levels[16];
+	int mixer_mod_count;
+	int calibrate_signal;
+	int play_sample_size, play_sample_rate, play_channels;
+	int play_ndelay;
+	int rec_sample_size, rec_sample_rate, rec_channels;
+	int rec_ndelay;
+	BYTE bCurrentMidiPatch;
+
+	/* Digital audio FIFOs */
+	msnd_fifo DAPF, DARF;
+	int fifosize;
+	int last_playbank, last_recbank;
+
+	/* MIDI in callback */
+	void (*midi_in_interrupt)(struct multisound_dev *);
+} multisound_dev_t;
+
+#ifndef mdelay
+#  define mdelay(a)		udelay((a) * 1000)
+#endif
+
+int				msnd_register(multisound_dev_t *dev);
+void				msnd_unregister(multisound_dev_t *dev);
+int				msnd_get_num_devs(void);
+multisound_dev_t *		msnd_get_dev(int i);
+
+void				msnd_init_queue(unsigned long, int start, int size);
+
+void				msnd_fifo_init(msnd_fifo *f);
+void				msnd_fifo_free(msnd_fifo *f);
+int				msnd_fifo_alloc(msnd_fifo *f, size_t n);
+void				msnd_fifo_make_empty(msnd_fifo *f);
+int				msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user);
+int				msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user);
+
+int				msnd_wait_TXDE(multisound_dev_t *dev);
+int				msnd_wait_HC0(multisound_dev_t *dev);
+int				msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd);
+int				msnd_send_word(multisound_dev_t *dev, unsigned char high,
+					       unsigned char mid, unsigned char low);
+int				msnd_upload_host(multisound_dev_t *dev, char *bin, int len);
+int				msnd_enable_irq(multisound_dev_t *dev);
+int				msnd_disable_irq(multisound_dev_t *dev);
+
+#endif /* __MSND_H */
diff -Nru linux/sound/oss/msnd_classic.c linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.c
--- linux/sound/oss/msnd_classic.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,3 @@
+/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
+#define MSND_CLASSIC
+#include "msnd_pinnacle.c"
diff -Nru linux/sound/oss/msnd_classic.h linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.h
--- linux/sound/oss/msnd_classic.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,188 @@
+/*********************************************************************
+ *
+ * msnd_classic.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 
+ * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $
+ *
+ ********************************************************************/
+#ifndef __MSND_CLASSIC_H
+#define __MSND_CLASSIC_H
+
+#include <linux/config.h>
+
+#define DSP_NUMIO				0x10
+
+#define	HP_MEMM					0x08
+
+#define	HP_BITM					0x0E
+#define	HP_WAIT					0x0D
+#define	HP_DSPR					0x0A
+#define	HP_PROR					0x0B
+#define	HP_BLKS					0x0C
+
+#define	HPPRORESET_OFF				0
+#define HPPRORESET_ON				1
+
+#define HPDSPRESET_OFF				0
+#define HPDSPRESET_ON				1
+
+#define HPBLKSEL_0				0
+#define HPBLKSEL_1				1
+
+#define HPWAITSTATE_0				0
+#define HPWAITSTATE_1				1
+
+#define HPBITMODE_16				0
+#define HPBITMODE_8				1
+
+#define	HIDSP_INT_PLAY_UNDER			0x00
+#define	HIDSP_INT_RECORD_OVER			0x01
+#define	HIDSP_INPUT_CLIPPING			0x02
+#define	HIDSP_MIDI_IN_OVER			0x10
+#define	HIDSP_MIDI_OVERRUN_ERR  0x13
+
+#define	HDEXAR_CLEAR_PEAKS			1
+#define	HDEXAR_IN_SET_POTS			2
+#define	HDEXAR_AUX_SET_POTS			3
+#define	HDEXAR_CAL_A_TO_D			4
+#define	HDEXAR_RD_EXT_DSP_BITS			5
+
+#define TIME_PRO_RESET_DONE			0x028A
+#define TIME_PRO_SYSEX				0x0040
+#define TIME_PRO_RESET				0x0032
+
+#define AGND					0x01
+#define SIGNAL					0x02
+
+#define EXT_DSP_BIT_DCAL			0x0001
+#define EXT_DSP_BIT_MIDI_CON			0x0002
+
+#define BUFFSIZE				0x8000
+#define HOSTQ_SIZE				0x40
+
+#define SRAM_CNTL_START				0x7F00
+#define SMA_STRUCT_START			0x7F40
+
+#define DAP_BUFF_SIZE				0x2400
+#define DAR_BUFF_SIZE				0x2000
+
+#define DAPQ_STRUCT_SIZE			0x10
+#define DARQ_STRUCT_SIZE			0x10
+#define DAPQ_BUFF_SIZE				(3 * 0x10)
+#define DARQ_BUFF_SIZE				(3 * 0x10)
+#define MODQ_BUFF_SIZE				0x400
+#define MIDQ_BUFF_SIZE				0x200
+#define DSPQ_BUFF_SIZE				0x40
+
+#define DAPQ_DATA_BUFF				0x6C00
+#define DARQ_DATA_BUFF				0x6C30
+#define MODQ_DATA_BUFF				0x6C60
+#define MIDQ_DATA_BUFF				0x7060
+#define DSPQ_DATA_BUFF				0x7260
+
+#define DAPQ_OFFSET				SRAM_CNTL_START
+#define DARQ_OFFSET				(SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET				(SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET				(SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET				(SRAM_CNTL_START + 0x20)
+
+#define MOP_SYNTH				0x10
+#define MOP_EXTOUT				0x32
+#define MOP_EXTTHRU				0x02
+#define MOP_OUTMASK				0x01
+
+#define MIP_EXTIN				0x01
+#define MIP_SYNTH				0x00
+#define MIP_INMASK				0x32
+
+/* Classic SMA Common Data */
+#define SMA_wCurrPlayBytes			0x0000
+#define SMA_wCurrRecordBytes			0x0002
+#define SMA_wCurrPlayVolLeft			0x0004
+#define SMA_wCurrPlayVolRight			0x0006
+#define SMA_wCurrInVolLeft			0x0008
+#define SMA_wCurrInVolRight			0x000a
+#define SMA_wUser_3				0x000c
+#define SMA_wUser_4				0x000e
+#define SMA_dwUser_5				0x0010
+#define SMA_dwUser_6				0x0014
+#define SMA_wUser_7				0x0018
+#define SMA_wReserved_A				0x001a
+#define SMA_wReserved_B				0x001c
+#define SMA_wReserved_C				0x001e
+#define SMA_wReserved_D				0x0020
+#define SMA_wReserved_E				0x0022
+#define SMA_wReserved_F				0x0024
+#define SMA_wReserved_G				0x0026
+#define SMA_wReserved_H				0x0028
+#define SMA_wCurrDSPStatusFlags			0x002a
+#define SMA_wCurrHostStatusFlags		0x002c
+#define SMA_wCurrInputTagBits			0x002e
+#define SMA_wCurrLeftPeak			0x0030
+#define SMA_wCurrRightPeak			0x0032
+#define SMA_wExtDSPbits				0x0034
+#define SMA_bExtHostbits			0x0036
+#define SMA_bBoardLevel				0x0037
+#define SMA_bInPotPosRight			0x0038
+#define SMA_bInPotPosLeft			0x0039
+#define SMA_bAuxPotPosRight			0x003a
+#define SMA_bAuxPotPosLeft			0x003b
+#define SMA_wCurrMastVolLeft			0x003c
+#define SMA_wCurrMastVolRight			0x003e
+#define SMA_bUser_12				0x0040
+#define SMA_bUser_13				0x0041
+#define SMA_wUser_14				0x0042
+#define SMA_wUser_15				0x0044
+#define SMA_wCalFreqAtoD			0x0046
+#define SMA_wUser_16				0x0048
+#define SMA_wUser_17				0x004a
+#define SMA__size				0x004c
+
+#ifdef HAVE_DSPCODEH
+#  include "msndperm.c"
+#  include "msndinit.c"
+#  define PERMCODE		msndperm
+#  define INITCODE		msndinit
+#  define PERMCODESIZE		sizeof(msndperm)
+#  define INITCODESIZE		sizeof(msndinit)
+#else
+#  ifndef CONFIG_MSNDCLAS_INIT_FILE
+#    define CONFIG_MSNDCLAS_INIT_FILE				\
+				"/etc/sound/msndinit.bin"
+#  endif
+#  ifndef CONFIG_MSNDCLAS_PERM_FILE
+#    define CONFIG_MSNDCLAS_PERM_FILE				\
+				"/etc/sound/msndperm.bin"
+#  endif
+#  define PERMCODEFILE		CONFIG_MSNDCLAS_PERM_FILE
+#  define INITCODEFILE		CONFIG_MSNDCLAS_INIT_FILE
+#  define PERMCODE		dspini
+#  define INITCODE		permini
+#  define PERMCODESIZE		sizeof_dspini
+#  define INITCODESIZE		sizeof_permini
+#endif
+#define LONGNAME		"MultiSound (Classic/Monterey/Tahiti)"
+
+#endif /* __MSND_CLASSIC_H */
diff -Nru linux/sound/oss/msnd_pinnacle.c linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.c
--- linux/sound/oss/msnd_pinnacle.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1894 @@
+/*********************************************************************
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ * Linux 2.0/2.2 Version
+ *
+ * msnd_pinnacle.c / msnd_classic.c
+ *
+ * -- If MSND_CLASSIC is defined:
+ *
+ *     -> driver for Turtle Beach Classic/Monterey/Tahiti
+ *
+ * -- Else
+ *
+ *     -> driver for Turtle Beach Pinnacle/Fiji
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $
+ *
+ * 12-3-2000  Modified IO port validation  Steve Sycamore
+ *
+ *
+ * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include "sound_config.h"
+#include "sound_firmware.h"
+#ifdef MSND_CLASSIC
+# ifndef __alpha__
+#  define SLOWIO
+# endif
+#endif
+#include "msnd.h"
+#ifdef MSND_CLASSIC
+#  ifdef CONFIG_MSNDCLAS_HAVE_BOOT
+#    define HAVE_DSPCODEH
+#  endif
+#  include "msnd_classic.h"
+#  define LOGNAME			"msnd_classic"
+#else
+#  ifdef CONFIG_MSNDPIN_HAVE_BOOT
+#    define HAVE_DSPCODEH
+#  endif
+#  include "msnd_pinnacle.h"
+#  define LOGNAME			"msnd_pinnacle"
+#endif
+
+#ifndef CONFIG_MSND_WRITE_NDELAY
+#  define CONFIG_MSND_WRITE_NDELAY	1
+#endif
+
+#define get_play_delay_jiffies(size)	((size) * HZ *			\
+					 dev.play_sample_size / 8 /	\
+					 dev.play_sample_rate /		\
+					 dev.play_channels)
+
+#define get_rec_delay_jiffies(size)	((size) * HZ *			\
+					 dev.rec_sample_size / 8 /	\
+					 dev.rec_sample_rate /		\
+					 dev.rec_channels)
+
+static multisound_dev_t			dev;
+
+#ifndef HAVE_DSPCODEH
+static char				*dspini, *permini;
+static int				sizeof_dspini, sizeof_permini;
+#endif
+
+static int				dsp_full_reset(void);
+static void				dsp_write_flush(void);
+
+static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd)
+{
+	if (msnd_send_dsp_cmd(dev, cmd) == 0)
+		return 0;
+	dsp_full_reset();
+	return msnd_send_dsp_cmd(dev, cmd);
+}
+
+static void reset_play_queue(void)
+{
+	int n;
+	LPDAQD lpDAQ;
+
+	dev.last_playbank = -1;
+	isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead);
+	isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail);
+
+	for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
+		isa_writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
+		isa_writew(0, lpDAQ + DAQDS_wSize);
+		isa_writew(1, lpDAQ + DAQDS_wFormat);
+		isa_writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize);
+		isa_writew(dev.play_channels, lpDAQ + DAQDS_wChannels);
+		isa_writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate);
+		isa_writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		isa_writew(n, lpDAQ + DAQDS_wFlags);
+	}
+}
+
+static void reset_record_queue(void)
+{
+	int n;
+	LPDAQD lpDAQ;
+	unsigned long flags;
+
+	dev.last_recbank = 2;
+	isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead);
+	isa_writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail);
+
+	/* Critical section: bank 1 access */
+	spin_lock_irqsave(&dev.lock, flags);
+	outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	isa_memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
+	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	spin_unlock_irqrestore(&dev.lock, flags);
+
+	for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
+		isa_writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
+		isa_writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		isa_writew(1, lpDAQ + DAQDS_wFormat);
+		isa_writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize);
+		isa_writew(dev.rec_channels, lpDAQ + DAQDS_wChannels);
+		isa_writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate);
+		isa_writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		isa_writew(n, lpDAQ + DAQDS_wFlags);
+	}
+}
+
+static void reset_queues(void)
+{
+	if (dev.mode & FMODE_WRITE) {
+		msnd_fifo_make_empty(&dev.DAPF);
+		reset_play_queue();
+	}
+	if (dev.mode & FMODE_READ) {
+		msnd_fifo_make_empty(&dev.DARF);
+		reset_record_queue();
+	}
+}
+
+static int dsp_set_format(struct file *file, int val)
+{
+	int data, i;
+	LPDAQD lpDAQ, lpDARQ;
+
+	lpDAQ = dev.base + DAPQ_DATA_BUFF;
+	lpDARQ = dev.base + DARQ_DATA_BUFF;
+
+	switch (val) {
+	case AFMT_U8:
+	case AFMT_S16_LE:
+		data = val;
+		break;
+	default:
+		data = DEFSAMPLESIZE;
+		break;
+	}
+
+	for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+		if (file->f_mode & FMODE_WRITE)
+			isa_writew(data, lpDAQ + DAQDS_wSampleSize);
+		if (file->f_mode & FMODE_READ)
+			isa_writew(data, lpDARQ + DAQDS_wSampleSize);
+	}
+	if (file->f_mode & FMODE_WRITE)
+		dev.play_sample_size = data;
+	if (file->f_mode & FMODE_READ)
+		dev.rec_sample_size = data;
+
+	return data;
+}
+
+static int dsp_get_frag_size(void)
+{
+	int size;
+	size = dev.fifosize / 4;
+	if (size > 32 * 1024)
+		size = 32 * 1024;
+	return size;
+}
+
+static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int val, i, data, tmp;
+	LPDAQD lpDAQ, lpDARQ;
+        audio_buf_info abinfo;
+	unsigned long flags;
+
+	lpDAQ = dev.base + DAPQ_DATA_BUFF;
+	lpDARQ = dev.base + DARQ_DATA_BUFF;
+
+	switch (cmd) {
+	case SNDCTL_DSP_SUBDIVIDE:
+	case SNDCTL_DSP_SETFRAGMENT:
+	case SNDCTL_DSP_SETDUPLEX:
+	case SNDCTL_DSP_POST:
+		return 0;
+
+	case SNDCTL_DSP_GETIPTR:
+	case SNDCTL_DSP_GETOPTR:
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&dev.lock, flags);
+		abinfo.fragsize = dsp_get_frag_size();
+                abinfo.bytes = dev.DAPF.n - dev.DAPF.len;
+                abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize;
+                abinfo.fragments = abinfo.bytes / abinfo.fragsize;
+		spin_unlock_irqrestore(&dev.lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&dev.lock, flags);
+		abinfo.fragsize = dsp_get_frag_size();
+                abinfo.bytes = dev.DARF.n - dev.DARF.len;
+                abinfo.fragstotal = dev.DARF.n / abinfo.fragsize;
+                abinfo.fragments = abinfo.bytes / abinfo.fragsize;
+		spin_unlock_irqrestore(&dev.lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_RESET:
+		dev.nresets = 0;
+		reset_queues();
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+		dsp_write_flush();
+		return 0;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		tmp = dsp_get_frag_size();
+		if (put_user(tmp, (int *)arg))
+                        return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_GETFMTS:
+		val = AFMT_S16_LE | AFMT_U8;
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (file->f_mode & FMODE_WRITE)
+			data = val == AFMT_QUERY
+				? dev.play_sample_size
+				: dsp_set_format(file, val);
+		else
+			data = val == AFMT_QUERY
+				? dev.rec_sample_size
+				: dsp_set_format(file, val);
+
+		if (put_user(data, (int *)arg))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_NONBLOCK:
+		if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) &&
+		    file->f_mode & FMODE_WRITE)
+			dev.play_ndelay = 1;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_ndelay = 1;
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		val = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (val < 8000)
+			val = 8000;
+
+		if (val > 48000)
+			val = 48000;
+
+		data = val;
+
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+			if (file->f_mode & FMODE_WRITE)
+				isa_writew(data, lpDAQ + DAQDS_wSampleRate);
+			if (file->f_mode & FMODE_READ)
+				isa_writew(data, lpDARQ + DAQDS_wSampleRate);
+		}
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_sample_rate = data;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_sample_rate = data;
+
+		if (put_user(data, (int *)arg))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_CHANNELS:
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (cmd == SNDCTL_DSP_CHANNELS) {
+			switch (val) {
+			case 1:
+			case 2:
+				data = val;
+				break;
+			default:
+				val = data = 2;
+				break;
+			}
+		} else {
+			switch (val) {
+			case 0:
+				data = 1;
+				break;
+			default:
+				val = 1;
+			case 1:
+				data = 2;
+				break;
+			}
+		}
+
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+			if (file->f_mode & FMODE_WRITE)
+				isa_writew(data, lpDAQ + DAQDS_wChannels);
+			if (file->f_mode & FMODE_READ)
+				isa_writew(data, lpDARQ + DAQDS_wChannels);
+		}
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_channels = data;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_channels = data;
+
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int mixer_get(int d)
+{
+	if (d > 31)
+		return -EINVAL;
+
+	switch (d) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_IMIX:
+	case SOUND_MIXER_LINE1:
+#ifndef MSND_CLASSIC
+	case SOUND_MIXER_MIC:
+	case SOUND_MIXER_SYNTH:
+#endif
+		return (dev.left_levels[d] >> 8) * 100 / 0xff | 
+			(((dev.right_levels[d] >> 8) * 100 / 0xff) << 8);
+	default:
+		return 0;
+	}
+}
+
+#define update_volm(a,b)						\
+	isa_writew((dev.left_levels[a] >> 1) *				\
+	       isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff,	\
+	       dev.SMA + SMA_##b##Left);				\
+	isa_writew((dev.right_levels[a] >> 1)  *			\
+	       isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff,	\
+	       dev.SMA + SMA_##b##Right);
+
+#define update_potm(d,s,ar)						\
+	isa_writeb((dev.left_levels[d] >> 8) *				\
+	       isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff,	\
+	       dev.SMA + SMA_##s##Left);				\
+	isa_writeb((dev.right_levels[d] >> 8) *				\
+	       isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff,	\
+	       dev.SMA + SMA_##s##Right);				\
+	if (msnd_send_word(&dev, 0, 0, ar) == 0)			\
+		chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+
+#define update_pot(d,s,ar)				\
+	isa_writeb(dev.left_levels[d] >> 8,		\
+	       dev.SMA + SMA_##s##Left);		\
+	isa_writeb(dev.right_levels[d] >> 8,		\
+	       dev.SMA + SMA_##s##Right);		\
+	if (msnd_send_word(&dev, 0, 0, ar) == 0)	\
+		chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+
+static int mixer_set(int d, int value)
+{
+	int left = value & 0x000000ff;
+	int right = (value & 0x0000ff00) >> 8;
+	int bLeft, bRight;
+	int wLeft, wRight;
+	int updatemaster = 0;
+
+	if (d > 31)
+		return -EINVAL;
+
+	bLeft = left * 0xff / 100;
+	wLeft = left * 0xffff / 100;
+
+	bRight = right * 0xff / 100;
+	wRight = right * 0xffff / 100;
+
+	dev.left_levels[d] = wLeft;
+	dev.right_levels[d] = wRight;
+
+	switch (d) {
+		/* master volume unscaled controls */
+	case SOUND_MIXER_LINE:			/* line pot control */
+		/* scaled by IMIX in digital mix */
+		isa_writeb(bLeft, dev.SMA + SMA_bInPotPosLeft);
+		isa_writeb(bRight, dev.SMA + SMA_bInPotPosRight);
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+		break;
+#ifndef MSND_CLASSIC
+	case SOUND_MIXER_MIC:			/* mic pot control */
+		/* scaled by IMIX in digital mix */
+		isa_writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft);
+		isa_writeb(bRight, dev.SMA + SMA_bMicPotPosRight);
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+		break;
+#endif
+	case SOUND_MIXER_VOLUME:		/* master volume */
+		isa_writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft);
+		isa_writew(wRight, dev.SMA + SMA_wCurrMastVolRight);
+		/* fall through */
+
+	case SOUND_MIXER_LINE1:			/* aux pot control */
+		/* scaled by master volume */
+		/* fall through */
+
+		/* digital controls */
+	case SOUND_MIXER_SYNTH:			/* synth vol (dsp mix) */
+	case SOUND_MIXER_PCM:			/* pcm vol (dsp mix) */
+	case SOUND_MIXER_IMIX:			/* input monitor (dsp mix) */
+		/* scaled by master volume */
+		updatemaster = 1;
+		break;
+
+	default:
+		return 0;
+	}
+
+	if (updatemaster) {
+		/* update master volume scaled controls */
+		update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
+		update_volm(SOUND_MIXER_IMIX, wCurrInVol);
+#ifndef MSND_CLASSIC
+		update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
+#endif
+		update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+	}
+
+	return mixer_get(d);
+}
+
+static void mixer_setup(void)
+{
+	update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
+	update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+	update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
+	update_volm(SOUND_MIXER_IMIX, wCurrInVol);
+#ifndef MSND_CLASSIC
+	update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
+	update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
+#endif
+}
+
+static unsigned long set_recsrc(unsigned long recsrc)
+{
+	if (dev.recsrc == recsrc)
+		return dev.recsrc;
+#ifdef HAVE_NORECSRC
+	else if (recsrc == 0)
+		dev.recsrc = 0;
+#endif
+	else
+		dev.recsrc ^= recsrc;
+
+#ifndef MSND_CLASSIC
+	if (dev.recsrc & SOUND_MASK_IMIX) {
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+	}
+	else if (dev.recsrc & SOUND_MASK_SYNTH) {
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+	}
+	else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) {
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0)
+      			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+	}
+	else {
+#ifdef HAVE_NORECSRC
+		/* Select no input (?) */
+		dev.recsrc = 0;
+#else
+		dev.recsrc = SOUND_MASK_IMIX;
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+#endif
+	}
+#endif /* MSND_CLASSIC */
+
+	return dev.recsrc;
+}
+
+static unsigned long force_recsrc(unsigned long recsrc)
+{
+	dev.recsrc = 0;
+	return set_recsrc(recsrc);
+}
+
+#define set_mixer_info()							\
+		strncpy(info.id, "MSNDMIXER", sizeof(info.id));			\
+		strncpy(info.name, "MultiSound Mixer", sizeof(info.name));
+
+static int mixer_ioctl(unsigned int cmd, unsigned long arg)
+{
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		set_mixer_info();
+		info.modify_counter = dev.mixer_mod_count;
+		return copy_to_user((void *)arg, &info, sizeof(info));
+	} else if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		set_mixer_info();
+		return copy_to_user((void *)arg, &info, sizeof(info));
+	} else if (cmd == SOUND_MIXER_PRIVATE1) {
+		dev.nresets = 0;
+		dsp_full_reset();
+		return 0;
+	} else if (((cmd >> 8) & 0xff) == 'M') {
+		int val = 0;
+
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				val = set_recsrc(val);
+				break;
+
+			default:
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				val = mixer_set(cmd & 0xff, val);
+				break;
+			}
+			++dev.mixer_mod_count;
+			return put_user(val, (int *)arg);
+		} else {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				val = dev.recsrc;
+				break;
+
+			case SOUND_MIXER_DEVMASK:
+			case SOUND_MIXER_STEREODEVS:
+				val =   SOUND_MASK_PCM |
+					SOUND_MASK_LINE |
+					SOUND_MASK_IMIX |
+					SOUND_MASK_LINE1 |
+#ifndef MSND_CLASSIC
+					SOUND_MASK_MIC |
+					SOUND_MASK_SYNTH |
+#endif
+					SOUND_MASK_VOLUME;
+				break;
+				  
+			case SOUND_MIXER_RECMASK:
+#ifdef MSND_CLASSIC
+				val =   0;
+#else
+				val =   SOUND_MASK_IMIX |
+					SOUND_MASK_SYNTH;
+				if (test_bit(F_HAVEDIGITAL, &dev.flags))
+					val |= SOUND_MASK_DIGITAL1;
+#endif
+				break;
+				  
+			case SOUND_MIXER_CAPS:
+				val =   SOUND_CAP_EXCL_INPUT;
+				break;
+
+			default:
+				if ((val = mixer_get(cmd & 0xff)) < 0)
+					return -EINVAL;
+				break;
+			}
+		}
+
+		return put_user(val, (int *)arg); 
+	}
+
+	return -EINVAL;
+}
+
+static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int minor = MINOR(inode->i_rdev);
+
+	if (cmd == OSS_GETVERSION) {
+		int sound_version = SOUND_VERSION;
+		return put_user(sound_version, (int *)arg);
+	}
+
+	if (minor == dev.dsp_minor)
+		return dsp_ioctl(file, cmd, arg);
+	else if (minor == dev.mixer_minor)
+		return mixer_ioctl(cmd, arg);
+
+	return -EINVAL;
+}
+
+static void dsp_write_flush(void)
+{
+	if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags))
+		return;
+	set_bit(F_WRITEFLUSH, &dev.flags);
+	interruptible_sleep_on_timeout(
+		&dev.writeflush,
+		get_play_delay_jiffies(dev.DAPF.len));
+	clear_bit(F_WRITEFLUSH, &dev.flags);
+	if (!signal_pending(current)) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE));
+	}
+	clear_bit(F_WRITING, &dev.flags);
+}
+
+static void dsp_halt(struct file *file)
+{
+	if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
+		clear_bit(F_READING, &dev.flags);
+		chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP);
+		msnd_disable_irq(&dev);
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file);
+			dev.mode &= ~FMODE_READ;
+		}
+		clear_bit(F_AUDIO_READ_INUSE, &dev.flags);
+	}
+	if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
+		if (test_bit(F_WRITING, &dev.flags)) {
+			dsp_write_flush();
+			chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP);
+		}
+		msnd_disable_irq(&dev);
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file);
+			dev.mode &= ~FMODE_WRITE;
+		}
+		clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
+	}
+}
+
+static int dsp_release(struct file *file)
+{
+	dsp_halt(file);
+	return 0;
+}
+
+static int dsp_open(struct file *file)
+{
+	if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
+		set_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
+		clear_bit(F_WRITING, &dev.flags);
+		msnd_fifo_make_empty(&dev.DAPF);
+		reset_play_queue();
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file);
+			dev.mode |= FMODE_WRITE;
+		}
+		msnd_enable_irq(&dev);
+	}
+	if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
+		set_bit(F_AUDIO_READ_INUSE, &dev.flags);
+		clear_bit(F_READING, &dev.flags);
+		msnd_fifo_make_empty(&dev.DARF);
+		reset_record_queue();
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file);
+			dev.mode |= FMODE_READ;
+		}
+		msnd_enable_irq(&dev);
+	}
+	return 0;
+}
+
+static void set_default_play_audio_parameters(void)
+{
+	dev.play_sample_size = DEFSAMPLESIZE;
+	dev.play_sample_rate = DEFSAMPLERATE;
+	dev.play_channels = DEFCHANNELS;
+}
+
+static void set_default_rec_audio_parameters(void)
+{
+	dev.rec_sample_size = DEFSAMPLESIZE;
+	dev.rec_sample_rate = DEFSAMPLERATE;
+	dev.rec_channels = DEFCHANNELS;
+}
+
+static void set_default_audio_parameters(void)
+{
+	set_default_play_audio_parameters();
+	set_default_rec_audio_parameters();
+}
+
+static int dev_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	int err = 0;
+
+	if (minor == dev.dsp_minor) {
+		if ((file->f_mode & FMODE_WRITE &&
+		     test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
+		    (file->f_mode & FMODE_READ &&
+		     test_bit(F_AUDIO_READ_INUSE, &dev.flags)))
+			return -EBUSY;
+
+		if ((err = dsp_open(file)) >= 0) {
+			dev.nresets = 0;
+			if (file->f_mode & FMODE_WRITE) {
+				set_default_play_audio_parameters();
+				if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags))
+					dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
+				else
+					dev.play_ndelay = 0;
+			}
+			if (file->f_mode & FMODE_READ) {
+				set_default_rec_audio_parameters();
+				dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
+			}
+		}
+	}
+	else if (minor == dev.mixer_minor) {
+		/* nothing */
+	} else
+		err = -EINVAL;
+
+	return err;
+}
+
+static int dev_release(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	int err = 0;
+
+	lock_kernel();
+	if (minor == dev.dsp_minor)
+		err = dsp_release(file);
+	else if (minor == dev.mixer_minor) {
+		/* nothing */
+	} else
+		err = -EINVAL;
+	unlock_kernel();
+	return err;
+}
+
+static __inline__ int pack_DARQ_to_DARF(register int bank)
+{
+	register int size, n, timeout = 3;
+	register WORD wTmp;
+	LPDAQD DAQD;
+
+	/* Increment the tail and check for queue wrap */
+	wTmp = isa_readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
+	if (wTmp > isa_readw(dev.DARQ + JQS_wSize))
+		wTmp = 0;
+	while (wTmp == isa_readw(dev.DARQ + JQS_wHead) && timeout--)
+		udelay(1);
+	isa_writew(wTmp, dev.DARQ + JQS_wTail);
+
+	/* Get our digital audio queue struct */
+	DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF;
+
+	/* Get length of data */
+	size = isa_readw(DAQD + DAQDS_wSize);
+
+	/* Read data from the head (unprotected bank 1 access okay
+           since this is only called inside an interrupt) */
+	outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	if ((n = msnd_fifo_write(
+		&dev.DARF,
+		(char *)(dev.base + bank * DAR_BUFF_SIZE),
+		size, 0)) <= 0) {
+		outb(HPBLKSEL_0, dev.io + HP_BLKS);
+		return n;
+	}
+	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+
+	return 1;
+}
+
+static __inline__ int pack_DAPF_to_DAPQ(register int start)
+{
+	register WORD DAPQ_tail;
+	register int protect = start, nbanks = 0;
+	LPDAQD DAQD;
+
+	DAPQ_tail = isa_readw(dev.DAPQ + JQS_wTail);
+	while (DAPQ_tail != isa_readw(dev.DAPQ + JQS_wHead) || start) {
+		register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
+		register int n;
+		unsigned long flags;
+
+		/* Write the data to the new tail */
+		if (protect) {
+			/* Critical section: protect fifo in non-interrupt */
+			spin_lock_irqsave(&dev.lock, flags);
+			if ((n = msnd_fifo_read(
+				&dev.DAPF,
+				(char *)(dev.base + bank_num * DAP_BUFF_SIZE),
+				DAP_BUFF_SIZE, 0)) < 0) {
+				spin_unlock_irqrestore(&dev.lock, flags);
+				return n;
+			}
+			spin_unlock_irqrestore(&dev.lock, flags);
+		} else {
+			if ((n = msnd_fifo_read(
+				&dev.DAPF,
+				(char *)(dev.base + bank_num * DAP_BUFF_SIZE),
+				DAP_BUFF_SIZE, 0)) < 0) {
+				return n;
+			}
+		}
+		if (!n)
+			break;
+
+		if (start)
+			start = 0;
+
+		/* Get our digital audio queue struct */
+		DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF;
+
+		/* Write size of this bank */
+		isa_writew(n, DAQD + DAQDS_wSize);
+		++nbanks;
+
+		/* Then advance the tail */
+		DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
+		isa_writew(DAPQ_tail, dev.DAPQ + JQS_wTail);
+		/* Tell the DSP to play the bank */
+		msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
+	}
+	return nbanks;
+}
+
+static int dsp_read(char *buf, size_t len)
+{
+	int count = len;
+
+	while (count > 0) {
+		int n;
+		unsigned long flags;
+
+		/* Critical section: protect fifo in non-interrupt */
+		spin_lock_irqsave(&dev.lock, flags);
+		if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) {
+			printk(KERN_WARNING LOGNAME ": FIFO read error\n");
+			spin_unlock_irqrestore(&dev.lock, flags);
+			return n;
+		}
+		spin_unlock_irqrestore(&dev.lock, flags);
+		buf += n;
+		count -= n;
+
+		if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) {
+			dev.last_recbank = -1;
+			if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0)
+				set_bit(F_READING, &dev.flags);
+		}
+
+		if (dev.rec_ndelay)
+			return count == len ? -EAGAIN : len - count;
+
+		if (count > 0) {
+			set_bit(F_READBLOCK, &dev.flags);
+			if (!interruptible_sleep_on_timeout(
+				&dev.readblock,
+				get_rec_delay_jiffies(DAR_BUFF_SIZE)))
+				clear_bit(F_READING, &dev.flags);
+			clear_bit(F_READBLOCK, &dev.flags);
+			if (signal_pending(current))
+				return -EINTR;
+		}
+	}
+
+	return len - count;
+}
+
+static int dsp_write(const char *buf, size_t len)
+{
+	int count = len;
+
+	while (count > 0) {
+		int n;
+		unsigned long flags;
+
+		/* Critical section: protect fifo in non-interrupt */
+		spin_lock_irqsave(&dev.lock, flags);
+		if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) {
+			printk(KERN_WARNING LOGNAME ": FIFO write error\n");
+			spin_unlock_irqrestore(&dev.lock, flags);
+			return n;
+		}
+		spin_unlock_irqrestore(&dev.lock, flags);
+		buf += n;
+		count -= n;
+
+		if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
+			dev.last_playbank = -1;
+			if (pack_DAPF_to_DAPQ(1) > 0)
+				set_bit(F_WRITING, &dev.flags);
+		}
+
+		if (dev.play_ndelay)
+			return count == len ? -EAGAIN : len - count;
+
+		if (count > 0) {
+			set_bit(F_WRITEBLOCK, &dev.flags);
+			interruptible_sleep_on_timeout(
+				&dev.writeblock,
+				get_play_delay_jiffies(DAP_BUFF_SIZE));
+			clear_bit(F_WRITEBLOCK, &dev.flags);
+			if (signal_pending(current))
+				return -EINTR;
+		}
+	}
+
+	return len - count;
+}
+
+static ssize_t dev_read(struct file *file, char *buf, size_t count, loff_t *off)
+{
+	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	if (minor == dev.dsp_minor)
+		return dsp_read(buf, count);
+	else
+		return -EINVAL;
+}
+
+static ssize_t dev_write(struct file *file, const char *buf, size_t count, loff_t *off)
+{
+	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	if (minor == dev.dsp_minor)
+		return dsp_write(buf, count);
+	else
+		return -EINVAL;
+}
+
+static __inline__ void eval_dsp_msg(register WORD wMessage)
+{
+	switch (HIBYTE(wMessage)) {
+	case HIMT_PLAY_DONE:
+		if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags))
+			break;
+		dev.last_playbank = LOBYTE(wMessage);
+
+		if (pack_DAPF_to_DAPQ(0) <= 0) {
+			if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
+				if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags))
+					wake_up_interruptible(&dev.writeflush);
+			}
+			clear_bit(F_WRITING, &dev.flags);
+		}
+
+		if (test_bit(F_WRITEBLOCK, &dev.flags))
+			wake_up_interruptible(&dev.writeblock);
+		break;
+
+	case HIMT_RECORD_DONE:
+		if (dev.last_recbank == LOBYTE(wMessage))
+			break;
+		dev.last_recbank = LOBYTE(wMessage);
+
+		pack_DARQ_to_DARF(dev.last_recbank);
+
+		if (test_bit(F_READBLOCK, &dev.flags))
+			wake_up_interruptible(&dev.readblock);
+		break;
+
+	case HIMT_DSP:
+		switch (LOBYTE(wMessage)) {
+#ifndef MSND_CLASSIC
+		case HIDSP_PLAY_UNDER:
+#endif
+		case HIDSP_INT_PLAY_UNDER:
+/*			printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */
+			clear_bit(F_WRITING, &dev.flags);
+			break;
+
+		case HIDSP_INT_RECORD_OVER:
+/*			printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */
+			clear_bit(F_READING, &dev.flags);
+			break;
+
+		default:
+/*			printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n",
+			LOBYTE(wMessage), LOBYTE(wMessage)); */
+			break;
+		}
+		break;
+
+        case HIMT_MIDI_IN_UCHAR:
+		if (dev.midi_in_interrupt)
+			(*dev.midi_in_interrupt)(&dev);
+		break;
+
+	default:
+/*		printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */
+		break;
+	}
+}
+
+static void intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* Send ack to DSP */
+	inb(dev.io + HP_RXL);
+
+	/* Evaluate queued DSP messages */
+	while (isa_readw(dev.DSPQ + JQS_wTail) != isa_readw(dev.DSPQ + JQS_wHead)) {
+		register WORD wTmp;
+
+		eval_dsp_msg(isa_readw(dev.pwDSPQData + 2*isa_readw(dev.DSPQ + JQS_wHead)));
+
+		if ((wTmp = isa_readw(dev.DSPQ + JQS_wHead) + 1) > isa_readw(dev.DSPQ + JQS_wSize))
+			isa_writew(0, dev.DSPQ + JQS_wHead);
+		else
+			isa_writew(wTmp, dev.DSPQ + JQS_wHead);
+	}
+}
+
+static struct file_operations dev_fileops = {
+	owner:		THIS_MODULE,
+	read:		dev_read,
+	write:		dev_write,
+	ioctl:		dev_ioctl,
+	open:		dev_open,
+	release:	dev_release,
+};
+
+static int reset_dsp(void)
+{
+	int timeout = 100;
+
+	outb(HPDSPRESET_ON, dev.io + HP_DSPR);
+	mdelay(1);
+#ifndef MSND_CLASSIC
+	dev.info = inb(dev.io + HP_INFO);
+#endif
+	outb(HPDSPRESET_OFF, dev.io + HP_DSPR);
+	mdelay(1);
+	while (timeout-- > 0) {
+		if (inb(dev.io + HP_CVR) == HP_CVR_DEF)
+			return 0;
+		mdelay(1);
+	}
+	printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
+
+	return -EIO;
+}
+
+static int __init probe_multisound(void)
+{
+#ifndef MSND_CLASSIC
+	char *xv, *rev = NULL;
+	char *pin = "Pinnacle", *fiji = "Fiji";
+	char *pinfiji = "Pinnacle/Fiji";
+#endif
+
+	if (check_region(dev.io, dev.numio)) {
+		printk(KERN_ERR LOGNAME ": I/O port conflict\n");
+		return -ENODEV;
+	}
+	request_region(dev.io, dev.numio, "probing");
+
+	if (reset_dsp() < 0) {
+		release_region(dev.io, dev.numio);
+		return -ENODEV;
+	}
+
+#ifdef MSND_CLASSIC
+	dev.name = "Classic/Tahiti/Monterey";
+	printk(KERN_INFO LOGNAME ": %s, "
+#else
+	switch (dev.info >> 4) {
+	case 0xf: xv = "<= 1.15"; break;
+	case 0x1: xv = "1.18/1.2"; break;
+	case 0x2: xv = "1.3"; break;
+	case 0x3: xv = "1.4"; break;
+	default: xv = "unknown"; break;
+	}
+
+	switch (dev.info & 0x7) {
+	case 0x0: rev = "I"; dev.name = pin; break;
+	case 0x1: rev = "F"; dev.name = pin; break;
+	case 0x2: rev = "G"; dev.name = pin; break;
+	case 0x3: rev = "H"; dev.name = pin; break;
+	case 0x4: rev = "E"; dev.name = fiji; break;
+	case 0x5: rev = "C"; dev.name = fiji; break;
+	case 0x6: rev = "D"; dev.name = fiji; break;
+	case 0x7:
+		rev = "A-B (Fiji) or A-E (Pinnacle)";
+		dev.name = pinfiji;
+		break;
+	}
+	printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
+#endif /* MSND_CLASSIC */
+	       "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
+	       dev.name,
+#ifndef MSND_CLASSIC
+	       rev, xv,
+#endif
+	       dev.io, dev.io + dev.numio - 1,
+	       dev.irq,
+	       dev.base, dev.base + 0x7fff);
+
+	release_region(dev.io, dev.numio);
+	return 0;
+}
+
+static int init_sma(void)
+{
+	static int initted;
+	WORD mastVolLeft, mastVolRight;
+	unsigned long flags;
+
+#ifdef MSND_CLASSIC
+	outb(dev.memid, dev.io + HP_MEMM);
+#endif
+	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	if (initted) {
+		mastVolLeft = isa_readw(dev.SMA + SMA_wCurrMastVolLeft);
+		mastVolRight = isa_readw(dev.SMA + SMA_wCurrMastVolRight);
+	} else
+		mastVolLeft = mastVolRight = 0;
+	isa_memset_io(dev.base, 0, 0x8000);
+
+	/* Critical section: bank 1 access */
+	spin_lock_irqsave(&dev.lock, flags);
+	outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	isa_memset_io(dev.base, 0, 0x8000);
+	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	spin_unlock_irqrestore(&dev.lock, flags);
+
+	dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF);
+	dev.pwMODQData = (dev.base + MODQ_DATA_BUFF);
+	dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF);
+
+	/* Motorola 56k shared memory base */
+	dev.SMA = dev.base + SMA_STRUCT_START;
+
+	/* Digital audio play queue */
+	dev.DAPQ = dev.base + DAPQ_OFFSET;
+	msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
+
+	/* Digital audio record queue */
+	dev.DARQ = dev.base + DARQ_OFFSET;
+	msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
+
+	/* MIDI out queue */
+	dev.MODQ = dev.base + MODQ_OFFSET;
+	msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
+
+	/* MIDI in queue */
+	dev.MIDQ = dev.base + MIDQ_OFFSET;
+	msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
+
+	/* DSP -> host message queue */
+	dev.DSPQ = dev.base + DSPQ_OFFSET;
+	msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
+
+	/* Setup some DSP values */
+#ifndef MSND_CLASSIC
+	isa_writew(1, dev.SMA + SMA_wCurrPlayFormat);
+	isa_writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
+	isa_writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels);
+	isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
+#endif
+	isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD);
+	isa_writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft);
+	isa_writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight);
+#ifndef MSND_CLASSIC
+	isa_writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch);
+	isa_writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate);
+#endif
+	isa_writew(0x303, dev.SMA + SMA_wCurrInputTagBits);
+
+	initted = 1;
+
+	return 0;
+}
+
+static int __init calibrate_adc(WORD srate)
+{
+	isa_writew(srate, dev.SMA + SMA_wCalFreqAtoD);
+	if (dev.calibrate_signal == 0)
+		isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags)
+		       | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
+	else
+		isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags)
+		       & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
+	if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
+	    chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ / 3);
+		return 0;
+	}
+	printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
+
+	return -EIO;
+}
+
+static int upload_dsp_code(void)
+{
+	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+#ifndef HAVE_DSPCODEH
+	INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE);
+	if (!INITCODE) {
+		printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
+		return -EBUSY;
+	}
+
+	PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE);
+	if (!PERMCODE) {
+		printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
+		vfree(INITCODE);
+		return -EBUSY;
+	}
+#endif
+	isa_memcpy_toio(dev.base, PERMCODE, PERMCODESIZE);
+	if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) {
+		printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
+		return -ENODEV;
+	}
+#ifdef HAVE_DSPCODEH
+	printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n");
+#else
+	printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
+#endif
+
+#ifndef HAVE_DSPCODEH
+	vfree(INITCODE);
+	vfree(PERMCODE);
+#endif
+
+	return 0;
+}
+
+#ifdef MSND_CLASSIC
+static void reset_proteus(void)
+{
+	outb(HPPRORESET_ON, dev.io + HP_PROR);
+	mdelay(TIME_PRO_RESET);
+	outb(HPPRORESET_OFF, dev.io + HP_PROR);
+	mdelay(TIME_PRO_RESET_DONE);
+}
+#endif
+
+static int initialize(void)
+{
+	int err, timeout;
+
+#ifdef MSND_CLASSIC
+	outb(HPWAITSTATE_0, dev.io + HP_WAIT);
+	outb(HPBITMODE_16, dev.io + HP_BITM);
+
+	reset_proteus();
+#endif
+	if ((err = init_sma()) < 0) {
+		printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
+		return err;
+	}
+
+	if ((err = reset_dsp()) < 0)
+		return err;
+
+	if ((err = upload_dsp_code()) < 0) {
+		printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
+		return err;
+	}
+
+	timeout = 200;
+	while (isa_readw(dev.base)) {
+		mdelay(1);
+		if (!timeout--) {
+			printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n");
+			return -EIO;
+		}
+	}
+
+	mixer_setup();
+
+	return 0;
+}
+
+static int dsp_full_reset(void)
+{
+	int rv;
+
+	if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10)
+		return 0;
+
+	set_bit(F_RESETTING, &dev.flags);
+	printk(KERN_INFO LOGNAME ": DSP reset\n");
+	dsp_halt(NULL);			/* Unconditionally halt */
+	if ((rv = initialize()))
+		printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
+	force_recsrc(dev.recsrc);
+	dsp_open(NULL);
+	clear_bit(F_RESETTING, &dev.flags);
+
+	return rv;
+}
+
+static int __init attach_multisound(void)
+{
+	int err;
+
+	if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) {
+		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
+		return err;
+	}
+	request_region(dev.io, dev.numio, dev.name);
+
+        if ((err = dsp_full_reset()) < 0) {
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return err;
+	}
+
+	if ((err = msnd_register(&dev)) < 0) {
+		printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n");
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return err;
+	}
+
+	if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
+		printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
+		msnd_unregister(&dev);
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return dev.dsp_minor;
+	}
+
+	if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
+		printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
+		unregister_sound_mixer(dev.mixer_minor);
+		msnd_unregister(&dev);
+		release_region(dev.io, dev.numio);
+		free_irq(dev.irq, &dev);
+		return dev.mixer_minor;
+	}
+
+	dev.ext_midi_dev = dev.hdr_midi_dev = -1;
+
+	disable_irq(dev.irq);
+	calibrate_adc(dev.play_sample_rate);
+#ifndef MSND_CLASSIC
+	force_recsrc(SOUND_MASK_IMIX);
+#endif
+
+	return 0;
+}
+
+static void __exit unload_multisound(void)
+{
+	release_region(dev.io, dev.numio);
+	free_irq(dev.irq, &dev);
+	unregister_sound_mixer(dev.mixer_minor);
+	unregister_sound_dsp(dev.dsp_minor);
+	msnd_unregister(&dev);
+}
+
+#ifndef MSND_CLASSIC
+
+/* Pinnacle/Fiji Logical Device Configuration */
+
+static int __init msnd_write_cfg(int cfg, int reg, int value)
+{
+	outb(reg, cfg);
+	outb(value, cfg + 1);
+	if (value != inb(cfg + 1)) {
+		printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int __init msnd_write_cfg_io0(int cfg, int num, WORD io)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_io1(int cfg, int num, WORD io)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_mem(int cfg, int num, int mem)
+{
+	WORD wmem;
+
+	mem >>= 8;
+	mem &= 0xfff;
+	wmem = (WORD)mem;
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
+		return -EIO;
+	if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT)))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_activate_logical(int cfg, int num)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
+		return -EIO;
+	return 0;
+}
+
+static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem)
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg_io0(cfg, num, io0))
+		return -EIO;
+	if (msnd_write_cfg_io1(cfg, num, io1))
+		return -EIO;
+	if (msnd_write_cfg_irq(cfg, num, irq))
+		return -EIO;
+	if (msnd_write_cfg_mem(cfg, num, mem))
+		return -EIO;
+	if (msnd_activate_logical(cfg, num))
+		return -EIO;
+	return 0;
+}
+
+typedef struct msnd_pinnacle_cfg_device {
+	WORD io0, io1, irq;
+	int mem;
+} msnd_pinnacle_cfg_t[4];
+
+static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device)
+{
+	int i;
+
+	/* Reset devices if told to */
+	if (reset) {
+		printk(KERN_INFO LOGNAME ": Resetting all devices\n");
+		for (i = 0; i < 4; ++i)
+			if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
+				return -EIO;
+	}
+
+	/* Configure specified devices */
+	for (i = 0; i < 4; ++i) {
+
+		switch (i) {
+		case 0:		/* DSP */
+			if (!(device[i].io0 && device[i].irq && device[i].mem))
+				continue;
+			break;
+		case 1:		/* MPU */
+			if (!(device[i].io0 && device[i].irq))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring MPU to I/O 0x%x IRQ %d\n",
+			       device[i].io0, device[i].irq);
+			break;
+		case 2:		/* IDE */
+			if (!(device[i].io0 && device[i].io1 && device[i].irq))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n",
+			       device[i].io0, device[i].io1, device[i].irq);
+			break;
+		case 3:		/* Joystick */
+			if (!(device[i].io0))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring joystick to I/O 0x%x\n",
+			       device[i].io0);
+			break;
+		}
+
+		/* Configure the device */
+		if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem))
+			return -EIO;
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR				("Andrew Veliath <andrewtv@usa.net>");
+MODULE_DESCRIPTION			("Turtle Beach " LONGNAME " Linux Driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM				(io, "i");
+MODULE_PARM				(irq, "i");
+MODULE_PARM				(mem, "i");
+MODULE_PARM				(write_ndelay, "i");
+MODULE_PARM				(fifosize, "i");
+MODULE_PARM				(calibrate_signal, "i");
+#ifndef MSND_CLASSIC
+MODULE_PARM				(digital, "i");
+MODULE_PARM				(cfg, "i");
+MODULE_PARM				(reset, "i");
+MODULE_PARM				(mpu_io, "i");
+MODULE_PARM				(mpu_irq, "i");
+MODULE_PARM				(ide_io0, "i");
+MODULE_PARM				(ide_io1, "i");
+MODULE_PARM				(ide_irq, "i");
+MODULE_PARM				(joystick_io, "i");
+#endif
+
+static int io __initdata =		-1;
+static int irq __initdata =		-1;
+static int mem __initdata =		-1;
+static int write_ndelay __initdata =	-1;
+
+#ifndef MSND_CLASSIC
+/* Pinnacle/Fiji non-PnP Config Port */
+static int cfg __initdata =		-1;
+
+/* Extra Peripheral Configuration */
+static int reset __initdata = 0;
+static int mpu_io __initdata = 0;
+static int mpu_irq __initdata = 0;
+static int ide_io0 __initdata = 0;
+static int ide_io1 __initdata = 0;
+static int ide_irq __initdata = 0;
+static int joystick_io __initdata = 0;
+
+/* If we have the digital daugherboard... */
+static int digital __initdata = 0;
+#endif
+
+static int fifosize __initdata =	DEFFIFOSIZE;
+static int calibrate_signal __initdata = 0;
+
+#else /* not a module */
+
+static int write_ndelay __initdata =	-1;
+
+#ifdef MSND_CLASSIC
+static int io __initdata =		CONFIG_MSNDCLAS_IO;
+static int irq __initdata =		CONFIG_MSNDCLAS_IRQ;
+static int mem __initdata =		CONFIG_MSNDCLAS_MEM;
+#else /* Pinnacle/Fiji */
+
+static int io __initdata =		CONFIG_MSNDPIN_IO;
+static int irq __initdata =		CONFIG_MSNDPIN_IRQ;
+static int mem __initdata =		CONFIG_MSNDPIN_MEM;
+
+/* Pinnacle/Fiji non-PnP Config Port */
+#ifdef CONFIG_MSNDPIN_NONPNP
+#  ifndef CONFIG_MSNDPIN_CFG
+#    define CONFIG_MSNDPIN_CFG		0x250
+#  endif
+#else
+#  ifdef CONFIG_MSNDPIN_CFG
+#    undef CONFIG_MSNDPIN_CFG
+#  endif
+#  define CONFIG_MSNDPIN_CFG		-1
+#endif
+static int cfg __initdata =		CONFIG_MSNDPIN_CFG;
+/* If not a module, we don't need to bother with reset=1 */
+static int reset;
+
+/* Extra Peripheral Configuration (Default: Disable) */
+#ifndef CONFIG_MSNDPIN_MPU_IO
+#  define CONFIG_MSNDPIN_MPU_IO		0
+#endif
+static int mpu_io __initdata =		CONFIG_MSNDPIN_MPU_IO;
+
+#ifndef CONFIG_MSNDPIN_MPU_IRQ
+#  define CONFIG_MSNDPIN_MPU_IRQ	0
+#endif
+static int mpu_irq __initdata =		CONFIG_MSNDPIN_MPU_IRQ;
+
+#ifndef CONFIG_MSNDPIN_IDE_IO0
+#  define CONFIG_MSNDPIN_IDE_IO0	0
+#endif
+static int ide_io0 __initdata =		CONFIG_MSNDPIN_IDE_IO0;
+
+#ifndef CONFIG_MSNDPIN_IDE_IO1
+#  define CONFIG_MSNDPIN_IDE_IO1	0
+#endif
+static int ide_io1 __initdata =		CONFIG_MSNDPIN_IDE_IO1;
+
+#ifndef CONFIG_MSNDPIN_IDE_IRQ
+#  define CONFIG_MSNDPIN_IDE_IRQ	0
+#endif
+static int ide_irq __initdata =		CONFIG_MSNDPIN_IDE_IRQ;
+
+#ifndef CONFIG_MSNDPIN_JOYSTICK_IO
+#  define CONFIG_MSNDPIN_JOYSTICK_IO	0
+#endif
+static int joystick_io __initdata =	CONFIG_MSNDPIN_JOYSTICK_IO;
+
+/* Have SPDIF (Digital) Daughterboard */
+#ifndef CONFIG_MSNDPIN_DIGITAL
+#  define CONFIG_MSNDPIN_DIGITAL	0
+#endif
+static int digital __initdata =		CONFIG_MSNDPIN_DIGITAL;
+
+#endif /* MSND_CLASSIC */
+
+#ifndef CONFIG_MSND_FIFOSIZE
+#  define CONFIG_MSND_FIFOSIZE		DEFFIFOSIZE
+#endif
+static int fifosize __initdata =	CONFIG_MSND_FIFOSIZE;
+
+#ifndef CONFIG_MSND_CALSIGNAL
+#  define CONFIG_MSND_CALSIGNAL		0
+#endif
+static int
+calibrate_signal __initdata =		CONFIG_MSND_CALSIGNAL;
+#endif /* MODULE */
+
+
+static int __init msnd_init(void)
+{
+	int err;
+#ifndef MSND_CLASSIC
+	static msnd_pinnacle_cfg_t pinnacle_devs;
+#endif /* MSND_CLASSIC */
+
+	printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version "
+	       VERSION ", Copyright (C) 1998 Andrew Veliath\n");
+
+	if (io == -1 || irq == -1 || mem == -1)
+		printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
+
+#ifdef MSND_CLASSIC
+	if (io == -1 ||
+	    !(io == 0x290 ||
+	      io == 0x260 ||
+	      io == 0x250 ||
+	      io == 0x240 ||
+	      io == 0x230 ||
+	      io == 0x220 ||
+	      io == 0x210 ||
+	      io == 0x3e0)) {
+		printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n");
+		return -EINVAL;
+	}
+#else
+	if (io == -1 ||
+		io < 0x100 ||
+		io > 0x3e0 ||
+		(io % 0x10) != 0) {
+			printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n");
+			return -EINVAL;
+	}
+#endif /* MSND_CLASSIC */
+
+	if (irq == -1 ||
+	    !(irq == 5 ||
+	      irq == 7 ||
+	      irq == 9 ||
+	      irq == 10 ||
+	      irq == 11 ||
+	      irq == 12)) {
+		printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
+		return -EINVAL;
+	}
+
+	if (mem == -1 ||
+	    !(mem == 0xb0000 ||
+	      mem == 0xc8000 ||
+	      mem == 0xd0000 ||
+	      mem == 0xd8000 ||
+	      mem == 0xe0000 ||
+	      mem == 0xe8000)) {
+		printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
+		       "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n");
+		return -EINVAL;
+	}
+
+#ifdef MSND_CLASSIC
+	switch (irq) {
+	case 5: dev.irqid = HPIRQ_5; break;
+	case 7: dev.irqid = HPIRQ_7; break;
+	case 9: dev.irqid = HPIRQ_9; break;
+	case 10: dev.irqid = HPIRQ_10; break;
+	case 11: dev.irqid = HPIRQ_11; break;
+	case 12: dev.irqid = HPIRQ_12; break;
+	}
+
+	switch (mem) {
+	case 0xb0000: dev.memid = HPMEM_B000; break;
+	case 0xc8000: dev.memid = HPMEM_C800; break;
+	case 0xd0000: dev.memid = HPMEM_D000; break;
+	case 0xd8000: dev.memid = HPMEM_D800; break;
+	case 0xe0000: dev.memid = HPMEM_E000; break;
+	case 0xe8000: dev.memid = HPMEM_E800; break;
+	}
+#else
+	if (cfg == -1) {
+		printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+	} else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) {
+		printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n");
+		return -EINVAL;
+	} else {
+		printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg);
+
+		/* DSP */
+		pinnacle_devs[0].io0 = io;
+		pinnacle_devs[0].irq = irq;
+		pinnacle_devs[0].mem = mem;
+
+		/* The following are Pinnacle specific */
+
+		/* MPU */
+		pinnacle_devs[1].io0 = mpu_io;
+		pinnacle_devs[1].irq = mpu_irq;
+
+		/* IDE */
+		pinnacle_devs[2].io0 = ide_io0;
+		pinnacle_devs[2].io1 = ide_io1;
+		pinnacle_devs[2].irq = ide_irq;
+
+		/* Joystick */
+		pinnacle_devs[3].io0 = joystick_io;
+
+		if (check_region(cfg, 2)) {
+			printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg);
+			return -EIO;
+		}
+
+		request_region(cfg, 2, "Pinnacle/Fiji Config");
+		if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) {
+			printk(KERN_ERR LOGNAME ": Device configuration error\n");
+			release_region(cfg, 2);
+			return -EIO;
+		}
+		release_region(cfg, 2);
+	}
+#endif /* MSND_CLASSIC */
+
+	if (fifosize < 16)
+		fifosize = 16;
+
+	if (fifosize > 1024)
+		fifosize = 1024;
+
+	set_default_audio_parameters();
+#ifdef MSND_CLASSIC
+	dev.type = msndClassic;
+#else
+	dev.type = msndPinnacle;
+#endif
+	dev.io = io;
+	dev.numio = DSP_NUMIO;
+	dev.irq = irq;
+	dev.base = mem;
+	dev.fifosize = fifosize * 1024;
+	dev.calibrate_signal = calibrate_signal ? 1 : 0;
+	dev.recsrc = 0;
+	dev.dspq_data_buff = DSPQ_DATA_BUFF;
+	dev.dspq_buff_size = DSPQ_BUFF_SIZE;
+	if (write_ndelay == -1)
+		write_ndelay = CONFIG_MSND_WRITE_NDELAY;
+	if (write_ndelay)
+		clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
+	else
+		set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
+#ifndef MSND_CLASSIC
+	if (digital)
+		set_bit(F_HAVEDIGITAL, &dev.flags);
+#endif
+	init_waitqueue_head(&dev.writeblock);
+	init_waitqueue_head(&dev.readblock);
+	init_waitqueue_head(&dev.writeflush);
+	msnd_fifo_init(&dev.DAPF);
+	msnd_fifo_init(&dev.DARF);
+	spin_lock_init(&dev.lock);
+	printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize);
+	if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) {
+		printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n");
+		return err;
+	}
+
+	if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) {
+		printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n");
+		msnd_fifo_free(&dev.DAPF);
+		return err;
+	}
+
+	if ((err = probe_multisound()) < 0) {
+		printk(KERN_ERR LOGNAME ": Probe failed\n");
+		msnd_fifo_free(&dev.DAPF);
+		msnd_fifo_free(&dev.DARF);
+		return err;
+	}
+
+	if ((err = attach_multisound()) < 0) {
+		printk(KERN_ERR LOGNAME ": Attach failed\n");
+		msnd_fifo_free(&dev.DAPF);
+		msnd_fifo_free(&dev.DARF);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit msdn_cleanup(void)
+{
+	unload_multisound();
+	msnd_fifo_free(&dev.DAPF);
+	msnd_fifo_free(&dev.DARF);
+}
+
+module_init(msnd_init);
+module_exit(msdn_cleanup);
diff -Nru linux/sound/oss/msnd_pinnacle.h linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.h
--- linux/sound/oss/msnd_pinnacle.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,249 @@
+/*********************************************************************
+ *
+ * msnd_pinnacle.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $
+ *
+ ********************************************************************/
+#ifndef __MSND_PINNACLE_H
+#define __MSND_PINNACLE_H
+
+#include <linux/config.h>
+
+#define DSP_NUMIO				0x08
+
+#define IREG_LOGDEVICE				0x07
+#define IREG_ACTIVATE				0x30
+#define LD_ACTIVATE				0x01
+#define LD_DISACTIVATE				0x00
+#define IREG_EECONTROL				0x3F
+#define IREG_MEMBASEHI				0x40
+#define IREG_MEMBASELO				0x41
+#define IREG_MEMCONTROL				0x42
+#define IREG_MEMRANGEHI				0x43
+#define IREG_MEMRANGELO				0x44
+#define MEMTYPE_8BIT				0x00
+#define MEMTYPE_16BIT				0x02
+#define MEMTYPE_RANGE				0x00
+#define MEMTYPE_HIADDR				0x01
+#define IREG_IO0_BASEHI				0x60
+#define IREG_IO0_BASELO				0x61
+#define IREG_IO1_BASEHI				0x62
+#define IREG_IO1_BASELO				0x63
+#define IREG_IRQ_NUMBER				0x70
+#define IREG_IRQ_TYPE				0x71
+#define IRQTYPE_HIGH				0x02
+#define IRQTYPE_LOW				0x00
+#define IRQTYPE_LEVEL				0x01
+#define IRQTYPE_EDGE				0x00
+
+#define	HP_DSPR					0x04
+#define	HP_BLKS					0x04
+
+#define HPDSPRESET_OFF				2
+#define HPDSPRESET_ON				0
+
+#define HPBLKSEL_0				2
+#define HPBLKSEL_1				3
+
+#define	HIMT_DAT_OFF				0x03
+
+#define	HIDSP_PLAY_UNDER			0x00
+#define	HIDSP_INT_PLAY_UNDER			0x01
+#define	HIDSP_SSI_TX_UNDER  			0x02
+#define HIDSP_RECQ_OVERFLOW			0x08
+#define HIDSP_INT_RECORD_OVER			0x09
+#define HIDSP_SSI_RX_OVERFLOW			0x0a
+
+#define	HIDSP_MIDI_IN_OVER			0x10
+
+#define	HIDSP_MIDI_FRAME_ERR			0x11
+#define	HIDSP_MIDI_PARITY_ERR			0x12
+#define	HIDSP_MIDI_OVERRUN_ERR			0x13
+
+#define HIDSP_INPUT_CLIPPING			0x20
+#define	HIDSP_MIX_CLIPPING			0x30
+#define HIDSP_DAT_IN_OFF			0x21
+
+#define	HDEXAR_SET_ANA_IN			0
+#define	HDEXAR_CLEAR_PEAKS			1
+#define	HDEXAR_IN_SET_POTS			2
+#define	HDEXAR_AUX_SET_POTS			3
+#define	HDEXAR_CAL_A_TO_D			4
+#define	HDEXAR_RD_EXT_DSP_BITS			5
+
+#define	HDEXAR_SET_SYNTH_IN			4
+#define	HDEXAR_READ_DAT_IN			5
+#define	HDEXAR_MIC_SET_POTS			6
+#define	HDEXAR_SET_DAT_IN			7
+
+#define HDEXAR_SET_SYNTH_48			8
+#define HDEXAR_SET_SYNTH_44			9
+
+#define TIME_PRO_RESET_DONE			0x028A
+#define TIME_PRO_SYSEX				0x001E
+#define TIME_PRO_RESET				0x0032
+
+#define AGND					0x01
+#define SIGNAL					0x02
+
+#define EXT_DSP_BIT_DCAL			0x0001
+#define EXT_DSP_BIT_MIDI_CON			0x0002
+
+#define BUFFSIZE				0x8000
+#define HOSTQ_SIZE				0x40
+
+#define SRAM_CNTL_START				0x7F00
+#define SMA_STRUCT_START			0x7F40
+
+#define DAP_BUFF_SIZE				0x2400
+#define DAR_BUFF_SIZE				0x2000
+
+#define DAPQ_STRUCT_SIZE			0x10
+#define DARQ_STRUCT_SIZE			0x10
+#define DAPQ_BUFF_SIZE				(3 * 0x10)
+#define DARQ_BUFF_SIZE				(3 * 0x10)
+#define MODQ_BUFF_SIZE				0x400
+#define MIDQ_BUFF_SIZE				0x800
+#define DSPQ_BUFF_SIZE				0x5A0
+
+#define DAPQ_DATA_BUFF				0x6C00
+#define DARQ_DATA_BUFF				0x6C30
+#define MODQ_DATA_BUFF				0x6C60
+#define MIDQ_DATA_BUFF				0x7060
+#define DSPQ_DATA_BUFF				0x7860
+
+#define DAPQ_OFFSET				SRAM_CNTL_START
+#define DARQ_OFFSET				(SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET				(SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET				(SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET				(SRAM_CNTL_START + 0x20)
+
+#define MOP_WAVEHDR				0
+#define MOP_EXTOUT				1
+#define MOP_HWINIT				0xfe
+#define MOP_NONE				0xff
+#define MOP_MAX					1
+
+#define MIP_EXTIN				0
+#define MIP_WAVEHDR				1
+#define MIP_HWINIT				0xfe
+#define MIP_MAX					1
+
+/* Pinnacle/Fiji SMA Common Data */
+#define SMA_wCurrPlayBytes			0x0000
+#define SMA_wCurrRecordBytes			0x0002
+#define SMA_wCurrPlayVolLeft			0x0004
+#define SMA_wCurrPlayVolRight			0x0006
+#define SMA_wCurrInVolLeft			0x0008
+#define SMA_wCurrInVolRight			0x000a
+#define SMA_wCurrMHdrVolLeft			0x000c
+#define SMA_wCurrMHdrVolRight			0x000e
+#define SMA_dwCurrPlayPitch			0x0010
+#define SMA_dwCurrPlayRate			0x0014
+#define SMA_wCurrMIDIIOPatch			0x0018
+#define SMA_wCurrPlayFormat			0x001a
+#define SMA_wCurrPlaySampleSize			0x001c
+#define SMA_wCurrPlayChannels			0x001e
+#define SMA_wCurrPlaySampleRate			0x0020
+#define SMA_wCurrRecordFormat			0x0022
+#define SMA_wCurrRecordSampleSize		0x0024
+#define SMA_wCurrRecordChannels			0x0026
+#define SMA_wCurrRecordSampleRate		0x0028
+#define SMA_wCurrDSPStatusFlags			0x002a
+#define SMA_wCurrHostStatusFlags		0x002c
+#define SMA_wCurrInputTagBits			0x002e
+#define SMA_wCurrLeftPeak			0x0030
+#define SMA_wCurrRightPeak			0x0032
+#define SMA_bMicPotPosLeft			0x0034
+#define SMA_bMicPotPosRight			0x0035
+#define SMA_bMicPotMaxLeft			0x0036
+#define SMA_bMicPotMaxRight			0x0037
+#define SMA_bInPotPosLeft			0x0038
+#define SMA_bInPotPosRight			0x0039
+#define SMA_bAuxPotPosLeft			0x003a
+#define SMA_bAuxPotPosRight			0x003b
+#define SMA_bInPotMaxLeft			0x003c
+#define SMA_bInPotMaxRight			0x003d
+#define SMA_bAuxPotMaxLeft			0x003e
+#define SMA_bAuxPotMaxRight			0x003f
+#define SMA_bInPotMaxMethod			0x0040
+#define SMA_bAuxPotMaxMethod			0x0041
+#define SMA_wCurrMastVolLeft			0x0042
+#define SMA_wCurrMastVolRight			0x0044
+#define SMA_wCalFreqAtoD			0x0046
+#define SMA_wCurrAuxVolLeft			0x0048
+#define SMA_wCurrAuxVolRight			0x004a
+#define SMA_wCurrPlay1VolLeft			0x004c
+#define SMA_wCurrPlay1VolRight			0x004e
+#define SMA_wCurrPlay2VolLeft			0x0050
+#define SMA_wCurrPlay2VolRight			0x0052
+#define SMA_wCurrPlay3VolLeft			0x0054
+#define SMA_wCurrPlay3VolRight			0x0056
+#define SMA_wCurrPlay4VolLeft			0x0058
+#define SMA_wCurrPlay4VolRight			0x005a
+#define SMA_wCurrPlay1PeakLeft			0x005c
+#define SMA_wCurrPlay1PeakRight			0x005e
+#define SMA_wCurrPlay2PeakLeft			0x0060
+#define SMA_wCurrPlay2PeakRight			0x0062
+#define SMA_wCurrPlay3PeakLeft			0x0064
+#define SMA_wCurrPlay3PeakRight			0x0066
+#define SMA_wCurrPlay4PeakLeft			0x0068
+#define SMA_wCurrPlay4PeakRight			0x006a
+#define SMA_wCurrPlayPeakLeft			0x006c
+#define SMA_wCurrPlayPeakRight			0x006e
+#define SMA_wCurrDATSR				0x0070
+#define SMA_wCurrDATRXCHNL			0x0072
+#define SMA_wCurrDATTXCHNL			0x0074
+#define SMA_wCurrDATRXRate			0x0076
+#define SMA_dwDSPPlayCount			0x0078
+#define SMA__size				0x007c
+
+#ifdef HAVE_DSPCODEH
+#  include "pndsperm.c"
+#  include "pndspini.c"
+#  define PERMCODE		pndsperm
+#  define INITCODE		pndspini
+#  define PERMCODESIZE		sizeof(pndsperm)
+#  define INITCODESIZE		sizeof(pndspini)
+#else
+#  ifndef CONFIG_MSNDPIN_INIT_FILE
+#    define CONFIG_MSNDPIN_INIT_FILE				\
+				"/etc/sound/pndspini.bin"
+#  endif
+#  ifndef CONFIG_MSNDPIN_PERM_FILE
+#    define CONFIG_MSNDPIN_PERM_FILE				\
+				"/etc/sound/pndsperm.bin"
+#  endif
+#  define PERMCODEFILE		CONFIG_MSNDPIN_PERM_FILE
+#  define INITCODEFILE		CONFIG_MSNDPIN_INIT_FILE
+#  define PERMCODE		dspini
+#  define INITCODE		permini
+#  define PERMCODESIZE		sizeof_dspini
+#  define INITCODESIZE		sizeof_permini
+#endif
+#define LONGNAME		"MultiSound (Pinnacle/Fiji)"
+
+#endif /* __MSND_PINNACLE_H */
diff -Nru linux/sound/oss/nec_vrc5477.c linux-2.4.19-pre5-mjc/sound/oss/nec_vrc5477.c
--- linux/sound/oss/nec_vrc5477.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/nec_vrc5477.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,2039 @@
+/***********************************************************************
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * drivers/sound/nec_vrc5477.c
+ *     AC97 sound dirver for NEC Vrc5477 chip (an integrated, 
+ *     multi-function controller chip for MIPS CPUs)
+ *
+ * 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 code is derived from ite8172.c, which is written by Steve Longerbeam.
+ *
+ * Features:
+ *   Currently we only support the following capabilities:
+ *	. mono output to PCM L/R (line out).
+ *	. stereo output to PCM L/R (line out).
+ *	. mono input from PCM L (line in).
+ *	. stereo output from PCM (line in).
+ *	. sampling rate at 48k or variable sampling rate 
+ *	. support /dev/dsp, /dev/mixer devices, standard OSS devices.
+ *	. only support 16-bit PCM format (hardware limit, no software
+ *	  translation) 
+ *	. support duplex, but no trigger or realtime.
+ *	
+ *   Specifically the following are not supported:
+ *	. app-set frag size.
+ *	. mmap'ed buffer access
+ */
+
+/* 
+ * Original comments from ite8172.c file.
+ */
+
+/*
+ *
+ * Notes:
+ *
+ *  1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are
+ *     taken, slightly modified or not at all, from the ES1371 driver,
+ *     so refer to the credits in es1371.c for those. The rest of the
+ *     code (probe, open, read, write, the ISR, etc.) is new.
+ *  2. The following support is untested:
+ *      * Memory mapping the audio buffers, and the ioctl controls that go
+ *        with it.
+ *      * S/PDIF output.
+ *  3. The following is not supported:
+ *      * I2S input.
+ *      * legacy audio mode.
+ *  4. Support for volume button interrupts is implemented but doesn't
+ *     work yet.
+ *
+ *  Revision history
+ *    02.08.2001  0.1   Initial release
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wrapper.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+#include <asm/ddb5xxx/debug.h>
+
+#undef VRC5477_AC97_VERBOSE_DEBUG
+
+/* one must turn on CONFIG_LL_DEBUG before VERBOSE_DEBUG is turned */
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+#if !defined(CONFIG_LL_DEBUG)
+#error "You must turn CONFIG_LL_DEBUG"
+#endif
+#endif
+
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+static u16 inTicket=0; 		/* check sync between intr & write */
+static u16 outTicket=0;
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define         VRC5477_INT_CLR         0x0
+#define         VRC5477_INT_STATUS	0x0
+#define         VRC5477_CODEC_WR        0x4
+#define         VRC5477_CODEC_RD        0x8
+#define         VRC5477_CTRL            0x18
+#define         VRC5477_ACLINK_CTRL     0x1c
+#define         VRC5477_INT_MASK        0x24
+
+#define		VRC5477_DAC1_CTRL	0x30
+#define		VRC5477_DAC1L		0x34
+#define		VRC5477_DAC1_BADDR	0x38
+#define		VRC5477_DAC2_CTRL	0x3c
+#define		VRC5477_DAC2L		0x40
+#define		VRC5477_DAC2_BADDR	0x44
+#define		VRC5477_DAC3_CTRL	0x48
+#define		VRC5477_DAC3L		0x4c
+#define		VRC5477_DAC3_BADDR	0x50
+
+#define		VRC5477_ADC1_CTRL	0x54
+#define		VRC5477_ADC1L		0x58
+#define		VRC5477_ADC1_BADDR	0x5c
+#define		VRC5477_ADC2_CTRL	0x60
+#define		VRC5477_ADC2L		0x64
+#define		VRC5477_ADC2_BADDR	0x68
+#define		VRC5477_ADC3_CTRL	0x6c
+#define		VRC5477_ADC3L		0x70
+#define		VRC5477_ADC3_BADDR	0x74
+
+#define		VRC5477_CODEC_WR_RWC	(1 << 23)
+
+#define		VRC5477_CODEC_RD_RRDYA	(1 << 31)
+#define		VRC5477_CODEC_RD_RRDYD	(1 << 30)
+
+#define		VRC5477_ACLINK_CTRL_RST_ON	(1 << 15)
+#define		VRC5477_ACLINK_CTRL_RST_TIME	0x7f
+#define		VRC5477_ACLINK_CTRL_SYNC_ON	(1 << 30)
+#define		VRC5477_ACLINK_CTRL_CK_STOP_ON	(1 << 31)
+
+#define		VRC5477_CTRL_DAC2ENB		(1 << 15) 
+#define		VRC5477_CTRL_ADC2ENB		(1 << 14) 
+#define		VRC5477_CTRL_DAC1ENB		(1 << 13) 
+#define		VRC5477_CTRL_ADC1ENB		(1 << 12) 
+
+#define		VRC5477_INT_MASK_NMASK		(1 << 31) 
+#define		VRC5477_INT_MASK_DAC1END	(1 << 5) 
+#define		VRC5477_INT_MASK_DAC2END	(1 << 4) 
+#define		VRC5477_INT_MASK_DAC3END	(1 << 3) 
+#define		VRC5477_INT_MASK_ADC1END	(1 << 2) 
+#define		VRC5477_INT_MASK_ADC2END	(1 << 1) 
+#define		VRC5477_INT_MASK_ADC3END	(1 << 0) 
+
+#define		VRC5477_DMA_ACTIVATION		(1 << 31)
+#define		VRC5477_DMA_WIP			(1 << 30)
+
+
+#define VRC5477_AC97_MODULE_NAME "NEC_Vrc5477_audio"
+#define PFX VRC5477_AC97_MODULE_NAME ": "
+
+/* --------------------------------------------------------------------- */
+
+struct vrc5477_ac97_state {
+	/* list of vrc5477_ac97 devices */
+	struct list_head devs;
+
+	/* the corresponding pci_dev structure */
+	struct pci_dev *dev;
+
+	/* soundcore stuff */
+	int dev_audio;
+
+	/* hardware resources */
+	unsigned long io;
+	unsigned int irq;
+
+#ifdef CONFIG_LL_DEBUG
+	/* debug /proc entry */
+	struct proc_dir_entry *ps;
+	struct proc_dir_entry *ac97_ps;
+#endif /* CONFIG_LL_DEBUG */
+
+	struct ac97_codec codec;
+
+	unsigned dacChannels, adcChannels;
+	unsigned short dacRate, adcRate;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *lbuf, *rbuf;
+		dma_addr_t lbufDma, rbufDma;
+		unsigned bufOrder;
+		unsigned numFrag;
+		unsigned fragShift;
+		unsigned fragSize;	/* redundant */
+		unsigned fragTotalSize;	/* = numFrag * fragSize(real)  */
+		unsigned nextIn;
+		unsigned nextOut;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* OSS stuff */
+		unsigned stopped:1;
+		unsigned ready:1;
+	} dma_dac, dma_adc;
+
+	#define	WORK_BUF_SIZE	2048
+	struct {
+		u16 lchannel;
+		u16 rchannel;
+	} workBuf[WORK_BUF_SIZE/4];
+};
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(devs);
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned ld2(unsigned int x)
+{
+    unsigned r = 0;
+	
+    if (x >= 0x10000) {
+	x >>= 16;
+	r += 16;
+    }
+    if (x >= 0x100) {
+	x >>= 8;
+	r += 8;
+    }
+    if (x >= 0x10) {
+	x >>= 4;
+	r += 4;
+    }
+    if (x >= 4) {
+	x >>= 2;
+	r += 2;
+    }
+    if (x >= 2)
+	r++;
+    return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static u16 rdcodec(struct ac97_codec *codec, u8 addr)
+{
+	struct vrc5477_ac97_state *s = 
+		(struct vrc5477_ac97_state *)codec->private_data;
+	unsigned long flags;
+	u32 result;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	/* wait until we can access codec registers */
+	while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);
+
+	/* write the address and "read" command to codec */
+	addr = addr & 0x7f;
+	outl((addr << 16) | VRC5477_CODEC_WR_RWC, s->io + VRC5477_CODEC_WR);
+
+	/* get the return result */
+	udelay(100); /* workaround hardware bug */
+	while ( (result = inl(s->io + VRC5477_CODEC_RD)) & 
+                (VRC5477_CODEC_RD_RRDYA | VRC5477_CODEC_RD_RRDYD) ) {
+		/* we get either addr or data, or both */
+		if (result & VRC5477_CODEC_RD_RRDYA) {
+			MIPS_ASSERT(addr == ((result >> 16) & 0x7f) );
+		}
+		if (result & VRC5477_CODEC_RD_RRDYD) {
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	return result & 0xffff;;
+}
+
+
+static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
+{
+	struct vrc5477_ac97_state *s = 
+		(struct vrc5477_ac97_state *)codec->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	/* wait until we can access codec registers */
+	while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);
+
+	/* write the address and value to codec */
+	outl((addr << 16) | data, s->io + VRC5477_CODEC_WR);
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void waitcodec(struct ac97_codec *codec)
+{
+	struct vrc5477_ac97_state *s = 
+		(struct vrc5477_ac97_state *)codec->private_data;
+
+	/* wait until we can access codec registers */
+	while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static void vrc5477_ac97_delay(int msec)
+{
+	unsigned long tmo;
+	signed long tmo2;
+
+	if (in_interrupt())
+		return;
+    
+	tmo = jiffies + (msec*HZ)/1000;
+	for (;;) {
+		tmo2 = tmo - jiffies;
+		if (tmo2 <= 0)
+			break;
+		schedule_timeout(tmo2);
+	}
+}
+
+
+static void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate)
+{
+	wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate);
+	s->adcRate = rate;
+}
+
+
+static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate)
+{
+	wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate);
+	s->dacRate = rate;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+extern inline void 
+stop_dac(struct vrc5477_ac97_state *s)
+{
+	struct dmabuf* db = &s->dma_dac;
+	unsigned long flags;
+	u32 temp;
+    
+	spin_lock_irqsave(&s->lock, flags);
+
+	if (db->stopped) {
+		spin_unlock_irqrestore(&s->lock, flags);
+		return;
+	}
+
+	/* deactivate the dma */
+	outl(0, s->io + VRC5477_DAC1_CTRL);
+	outl(0, s->io + VRC5477_DAC2_CTRL);
+
+	/* wait for DAM completely stop */
+	while (inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP);
+	while (inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP);
+
+	/* disable dac slots in aclink */
+	temp = inl(s->io + VRC5477_CTRL);
+	temp &= ~ (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB);
+	outl (temp, s->io + VRC5477_CTRL);
+
+	/* disable interrupts */
+	temp = inl(s->io + VRC5477_INT_MASK);
+	temp &= ~ (VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END); 
+	outl (temp, s->io + VRC5477_INT_MASK);
+
+	/* clear pending ones */
+	outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, 
+	     s->io +  VRC5477_INT_CLR);
+    
+	db->stopped = 1;
+    
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac(struct vrc5477_ac97_state *s)
+{
+	struct dmabuf* db = &s->dma_dac;
+	unsigned long flags;
+	u32 dmaLength;
+	u32 temp;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	if (!db->stopped) {
+		spin_unlock_irqrestore(&s->lock, flags);
+		return;
+	}
+
+	/* we should have some data to do the DMA trasnfer */
+	MIPS_ASSERT(db->count >= db->fragSize);
+
+	/* clear pending fales interrupts */
+	outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, 
+	     s->io +  VRC5477_INT_CLR);
+
+	/* enable interrupts */
+	temp = inl(s->io + VRC5477_INT_MASK);
+	temp |= VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END;
+	outl(temp, s->io +  VRC5477_INT_MASK);
+
+	/* setup dma base addr */
+	outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC1_BADDR);
+	if (s->dacChannels == 1) {
+		outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR);
+	} else {
+		outl(db->rbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR);
+	}
+
+	/* set dma length, in the unit of 0x10 bytes */
+	dmaLength = db->fragSize >> 4;
+	outl(dmaLength, s->io + VRC5477_DAC1L);
+	outl(dmaLength, s->io + VRC5477_DAC2L);
+
+	/* activate dma */
+	outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC1_CTRL);
+	outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC2_CTRL);
+
+	/* enable dac slots - we should hear the music now! */
+	temp = inl(s->io + VRC5477_CTRL);
+	temp |= (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB);
+	outl (temp, s->io + VRC5477_CTRL);
+
+	/* it is time to setup next dma transfer */
+	MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP);
+	MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP);
+
+	temp = db->nextOut + db->fragSize;
+	if (temp >= db->fragTotalSize) {
+		MIPS_ASSERT(temp == db->fragTotalSize);
+		temp = 0;
+	}
+
+	outl(db->lbufDma + temp, s->io + VRC5477_DAC1_BADDR);
+	if (s->dacChannels == 1) {
+		outl(db->lbufDma + temp, s->io + VRC5477_DAC2_BADDR);
+	} else {
+		outl(db->rbufDma + temp, s->io + VRC5477_DAC2_BADDR);
+	}
+
+	db->stopped = 0;
+
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+	outTicket = *(u16*)(db->lbuf+db->nextOut);
+	if (db->count > db->fragSize) {
+		MIPS_ASSERT((u16)(outTicket+1) == *(u16*)(db->lbuf+temp));
+	}
+#endif
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_adc(struct vrc5477_ac97_state *s)
+{
+	struct dmabuf* db = &s->dma_adc;
+	unsigned long flags;
+	u32 temp;
+    
+	spin_lock_irqsave(&s->lock, flags);
+
+	if (db->stopped) {
+		spin_unlock_irqrestore(&s->lock, flags);
+		return;
+	}
+
+	/* deactivate the dma */
+	outl(0, s->io + VRC5477_ADC1_CTRL);
+	outl(0, s->io + VRC5477_ADC2_CTRL);
+
+	/* disable adc slots in aclink */
+	temp = inl(s->io + VRC5477_CTRL);
+	temp &= ~ (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB);
+	outl (temp, s->io + VRC5477_CTRL);
+
+	/* disable interrupts */
+        temp = inl(s->io + VRC5477_INT_MASK);
+        temp &= ~ (VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END); 
+        outl (temp, s->io + VRC5477_INT_MASK);
+
+	/* clear pending ones */
+	outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, 
+	     s->io +  VRC5477_INT_CLR);
+    
+	db->stopped = 1;
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct vrc5477_ac97_state *s)
+{
+	struct dmabuf* db = &s->dma_adc;
+	unsigned long flags;
+	u32 dmaLength;
+	u32 temp;
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	if (!db->stopped) {
+		spin_unlock_irqrestore(&s->lock, flags);
+		return;
+	}
+
+	/* we should at least have some free space in the buffer */
+	MIPS_ASSERT(db->count < db->fragTotalSize - db->fragSize * 2);
+
+	/* clear pending ones */
+	outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, 
+	     s->io +  VRC5477_INT_CLR);
+
+        /* enable interrupts */
+        temp = inl(s->io + VRC5477_INT_MASK);
+        temp |= VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END;
+        outl(temp, s->io +  VRC5477_INT_MASK);
+
+	/* setup dma base addr */
+	outl(db->lbufDma + db->nextIn, s->io + VRC5477_ADC1_BADDR);
+	outl(db->rbufDma + db->nextIn, s->io + VRC5477_ADC2_BADDR);
+
+	/* setup dma length */
+	dmaLength = db->fragSize >> 4;
+	outl(dmaLength, s->io + VRC5477_ADC1L);
+	outl(dmaLength, s->io + VRC5477_ADC2L);
+
+	/* activate dma */
+	outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC1_CTRL);
+	outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC2_CTRL);
+
+	/* enable adc slots */
+	temp = inl(s->io + VRC5477_CTRL);
+	temp |= (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB);
+	outl (temp, s->io + VRC5477_CTRL);
+
+	/* it is time to setup next dma transfer */
+	temp = db->nextIn + db->fragSize;
+	if (temp >= db->fragTotalSize) {
+		MIPS_ASSERT(temp == db->fragTotalSize);
+		temp = 0;
+	}
+	outl(db->lbufDma + temp, s->io + VRC5477_ADC1_BADDR);
+	outl(db->rbufDma + temp, s->io + VRC5477_ADC2_BADDR);
+
+	db->stopped = 0;
+
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+extern inline void dealloc_dmabuf(struct vrc5477_ac97_state *s, 
+				  struct dmabuf *db)
+{
+	if (db->lbuf) {
+		MIPS_ASSERT(db->rbuf);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder,
+				    db->lbuf, db->lbufDma);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder,
+				    db->rbuf, db->rbufDma);
+		db->lbuf = db->rbuf = NULL;
+	}
+	db->nextIn = db->nextOut = 0;
+	db->ready = 0;
+}
+
+static int prog_dmabuf(struct vrc5477_ac97_state *s, 
+		       struct dmabuf *db,
+		       unsigned rate)
+{
+	int order;
+	unsigned bufsize;
+
+	if (!db->lbuf) {
+		MIPS_ASSERT(!db->rbuf);
+
+		db->ready = 0;
+		for (order = DMABUF_DEFAULTORDER; 
+		     order >= DMABUF_MINORDER; 
+		     order--) {
+			db->lbuf = pci_alloc_consistent(s->dev,
+							PAGE_SIZE << order,
+							&db->lbufDma);
+			db->rbuf = pci_alloc_consistent(s->dev,
+							PAGE_SIZE << order,
+							&db->rbufDma);
+			if (db->lbuf && db->rbuf) break;
+			if (db->lbuf) {
+			    MIPS_ASSERT(!db->rbuf);
+			    pci_free_consistent(s->dev, 
+						PAGE_SIZE << order,
+						db->lbuf,
+						db->lbufDma);
+			}
+		}
+		if (!db->lbuf) {
+			MIPS_ASSERT(!db->rbuf);
+			return -ENOMEM;
+		}
+
+		db->bufOrder = order;
+	}
+
+	db->count = 0;
+	db->nextIn = db->nextOut = 0;
+    
+	bufsize = PAGE_SIZE << db->bufOrder;
+	db->fragShift = ld2(rate * 2 / 100);
+	if (db->fragShift < 4) db->fragShift = 4;
+
+	db->numFrag = bufsize >> db->fragShift;
+	while (db->numFrag < 4 && db->fragShift > 4) {
+		db->fragShift--;
+		db->numFrag = bufsize >> db->fragShift;
+	}
+	db->fragSize = 1 << db->fragShift;
+	db->fragTotalSize = db->numFrag << db->fragShift;
+	memset(db->lbuf, 0, db->fragTotalSize);
+	memset(db->rbuf, 0, db->fragTotalSize);
+    
+	db->ready = 1;
+
+	return 0;
+}
+
+extern inline int prog_dmabuf_adc(struct vrc5477_ac97_state *s)
+{
+    stop_adc(s);
+    return prog_dmabuf(s, &s->dma_adc, s->adcRate);
+}
+
+extern inline int prog_dmabuf_dac(struct vrc5477_ac97_state *s)
+{
+    stop_dac(s);
+    return prog_dmabuf(s, &s->dma_dac, s->dacRate);
+}
+
+
+/* --------------------------------------------------------------------- */
+/* hold spinlock for the following! */
+
+static inline void vrc5477_ac97_adc_interrupt(struct vrc5477_ac97_state *s)
+{
+	struct dmabuf* adc = &s->dma_adc;
+	unsigned temp;
+
+	/* we need two frags avaiable because one is already being used
+	 * and the other will be used when next interrupt happens.
+	 */
+	if (adc->count >= adc->fragTotalSize - adc->fragSize) {
+		stop_adc(s);
+		adc->error++;
+		printk(KERN_INFO PFX "adc overrun\n");
+		return;
+	}
+
+	/* set the base addr for next DMA transfer */
+	temp = adc->nextIn + 2*adc->fragSize;
+	if (temp >= adc->fragTotalSize) {
+		MIPS_ASSERT( (temp == adc->fragTotalSize) ||
+                             (temp == adc->fragTotalSize + adc->fragSize) );
+		temp -= adc->fragTotalSize;
+	}
+	outl(adc->lbufDma + temp, s->io + VRC5477_ADC1_BADDR);
+	outl(adc->rbufDma + temp, s->io + VRC5477_ADC2_BADDR);
+
+	/* adjust nextIn */
+	adc->nextIn += adc->fragSize;
+	if (adc->nextIn >= adc->fragTotalSize) {
+		MIPS_ASSERT(adc->nextIn == adc->fragTotalSize);
+		adc->nextIn = 0;
+	}
+
+	/* adjust count */
+	adc->count += adc->fragSize;
+
+	/* wake up anybody listening */
+	if (waitqueue_active(&adc->wait)) {
+		wake_up_interruptible(&adc->wait);
+	}	
+}
+
+static inline void vrc5477_ac97_dac_interrupt(struct vrc5477_ac97_state *s)
+{
+	struct dmabuf* dac = &s->dma_dac;
+	unsigned temp;
+
+	/* next DMA transfer should already started */
+	MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP);
+	MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP);
+
+	/* let us set for next next DMA transfer */
+	temp = dac->nextOut + dac->fragSize*2;
+	if (temp >= dac->fragTotalSize) {
+		MIPS_ASSERT( (temp == dac->fragTotalSize) || 
+                             (temp == dac->fragTotalSize + dac->fragSize) );
+		temp -= dac->fragTotalSize;
+	}
+	outl(dac->lbufDma + temp, s->io + VRC5477_DAC1_BADDR);
+	if (s->dacChannels == 1) {
+		outl(dac->lbufDma + temp, s->io + VRC5477_DAC2_BADDR);
+	} else {
+		outl(dac->rbufDma + temp, s->io + VRC5477_DAC2_BADDR);
+	}
+
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+	if (*(u16*)(dac->lbuf +  dac->nextOut) != outTicket) {
+		printk("assert fail: - %d vs %d\n", 
+		        *(u16*)(dac->lbuf +  dac->nextOut),
+                        outTicket);
+                MIPS_ASSERT(1 == 0);
+	}
+#endif
+
+	/* adjust nextOut pointer */
+	dac->nextOut += dac->fragSize;
+	if (dac->nextOut >= dac->fragTotalSize) {
+		MIPS_ASSERT(dac->nextOut == dac->fragTotalSize);
+		dac->nextOut = 0;
+	}
+
+	/* adjust count */
+	dac->count -= dac->fragSize;
+	if (dac->count <=0 ) {
+		MIPS_ASSERT(dac->count == 0);
+		MIPS_ASSERT(dac->nextIn == dac->nextOut);
+		/* buffer under run */
+		stop_dac(s);
+	}
+
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+	if (dac->count) {
+		outTicket ++;
+		MIPS_ASSERT(*(u16*)(dac->lbuf +  dac->nextOut) == outTicket);
+	}
+#endif
+	
+	/* we cannot have both under run and someone is waiting on us */
+	MIPS_ASSERT(! (waitqueue_active(&dac->wait) && (dac->count <= 0)) );
+
+	/* wake up anybody listening */
+	if (waitqueue_active(&dac->wait))
+		wake_up_interruptible(&dac->wait);
+}
+
+static void vrc5477_ac97_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)dev_id;
+	u32 irqStatus;
+	u32 adcInterrupts, dacInterrupts;
+
+	spin_lock(&s->lock);
+
+	/* get irqStatus and clear the detected ones */
+	irqStatus = inl(s->io + VRC5477_INT_STATUS);
+	outl(irqStatus, s->io + VRC5477_INT_CLR);
+
+	/* let us see what we get */
+	dacInterrupts = VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END;
+	adcInterrupts = VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END;
+	if (irqStatus & dacInterrupts) {
+		/* we should get both interrupts, but just in case ...  */
+		if (irqStatus & VRC5477_INT_MASK_DAC1END) {
+			vrc5477_ac97_dac_interrupt(s);
+		}
+		if ( (irqStatus & dacInterrupts) != dacInterrupts ) {
+			printk(KERN_WARNING "vrc5477_ac97 : dac interrupts not in sync!!!\n");
+			stop_dac(s);
+			start_dac(s);
+		}
+	} else if (irqStatus & adcInterrupts) {
+		/* we should get both interrupts, but just in case ...  */
+		if(irqStatus & VRC5477_INT_MASK_ADC1END) {
+			vrc5477_ac97_adc_interrupt(s);
+		} 
+		if ( (irqStatus & adcInterrupts) != adcInterrupts ) {
+			printk(KERN_WARNING "vrc5477_ac97 : adc interrupts not in sync!!!\n");
+			stop_adc(s);
+			start_adc(s);
+		}
+	}
+
+	spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int vrc5477_ac97_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct list_head *list;
+	struct vrc5477_ac97_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct vrc5477_ac97_state, devs);
+		if (s->codec.dev_mixer == minor)
+			break;
+	}
+	file->private_data = s;
+	return 0;
+}
+
+static int vrc5477_ac97_release_mixdev(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
+			unsigned long arg)
+{
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int vrc5477_ac97_ioctl_mixdev(struct inode *inode, struct file *file,
+				     unsigned int cmd, unsigned long arg)
+{
+    struct vrc5477_ac97_state *s = 
+	    (struct vrc5477_ac97_state *)file->private_data;
+    struct ac97_codec *codec = &s->codec;
+
+    return mixdev_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations vrc5477_ac97_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		vrc5477_ac97_ioctl_mixdev,
+	open:		vrc5477_ac97_open_mixdev,
+	release:	vrc5477_ac97_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct vrc5477_ac97_state *s, int nonblock)
+{
+	unsigned long flags;
+	int count, tmo;
+	
+	if (!s->dma_dac.ready)
+		return 0;
+
+	for (;;) {
+		spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+			break;
+		if (nonblock)
+			return -EBUSY;
+		tmo = 1000 * count / s->dacRate / 2;
+		vrc5477_ac97_delay(tmo);
+	}
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline 
+copy_two_channel_adc_to_user(struct vrc5477_ac97_state *s, 
+		             char *buffer, 
+			     int copyCount)
+{
+	struct dmabuf *db = &s->dma_adc;
+	int bufStart = db->nextOut;
+	for (; copyCount > 0; ) {
+		int i;
+		int count = copyCount;
+		if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2;
+		for (i=0; i< count/2; i++) {
+			s->workBuf[i].lchannel = 
+				*(u16*)(db->lbuf + bufStart + i*2);
+			s->workBuf[i].rchannel = 
+				*(u16*)(db->rbuf + bufStart + i*2);
+		}
+		if (copy_to_user(buffer, s->workBuf, count*2)) {
+			return -1;
+		}
+
+		copyCount -= count;
+		bufStart += count;
+		MIPS_ASSERT(bufStart <= db->fragTotalSize);
+		buffer += count *2;
+	}
+	return 0;
+}
+
+/* return the total bytes that is copied */
+static int inline 
+copy_adc_to_user(struct vrc5477_ac97_state *s,
+		 char * buffer,
+		 size_t count,
+		 int avail)
+{
+	struct dmabuf *db = &s->dma_adc;
+	int copyCount=0;
+	int copyFragCount=0;
+	int totalCopyCount = 0;
+	int totalCopyFragCount = 0;
+	unsigned long flags;
+
+	/* adjust count to signel channel byte count */
+	count >>= s->adcChannels - 1;
+
+	/* we may have to "copy" twice as ring buffer wraps around */
+	for (; (avail > 0) && (count > 0); ) {
+		/* determine max possible copy count for single channel */
+		copyCount = count;
+		if (copyCount > avail) {
+			copyCount = avail;
+		}
+		if (copyCount + db->nextOut > db->fragTotalSize) {
+			copyCount = db->fragTotalSize - db->nextOut;
+			MIPS_ASSERT((copyCount % db->fragSize) == 0);
+		}
+
+		copyFragCount = (copyCount-1) >> db->fragShift;
+		copyFragCount = (copyFragCount+1) << db->fragShift;
+		MIPS_ASSERT(copyFragCount >= copyCount);
+
+		/* we copy differently based on adc channels */
+		if (s->adcChannels == 1) {
+			if (copy_to_user(buffer, 
+					 db->lbuf + db->nextOut, 
+					 copyCount)) 
+				return -1;
+		} else {
+			/* *sigh* we have to mix two streams into one  */
+			if (copy_two_channel_adc_to_user(s, buffer, copyCount))
+				return -1;
+		}	
+
+		count -= copyCount;
+		totalCopyCount += copyCount;
+		avail -= copyFragCount;
+		totalCopyFragCount += copyFragCount;
+
+		buffer += copyCount << (s->adcChannels-1);
+
+		db->nextOut += copyFragCount;
+		if (db->nextOut >= db->fragTotalSize) {
+			MIPS_ASSERT(db->nextOut == db->fragTotalSize);
+			db->nextOut = 0;
+		}
+
+		MIPS_ASSERT((copyFragCount % db->fragSize) == 0);
+		MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount));
+	}
+
+	spin_lock_irqsave(&s->lock, flags);
+        db->count -= totalCopyFragCount;
+        spin_unlock_irqrestore(&s->lock, flags);
+
+	return totalCopyCount << (s->adcChannels-1);
+}
+
+static ssize_t 
+vrc5477_ac97_read(struct file *file, 
+		  char *buffer,
+		  size_t count, 
+		  loff_t *ppos)
+{
+	struct vrc5477_ac97_state *s = 
+		(struct vrc5477_ac97_state *)file->private_data;
+	struct dmabuf *db = &s->dma_adc;
+	ssize_t ret = 0;
+	unsigned long flags;
+	int copyCount;
+	size_t avail;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+
+	MIPS_ASSERT(db->ready);
+
+	while (count > 0) {
+		// wait for samples in capture buffer
+		do {
+			spin_lock_irqsave(&s->lock, flags);
+			if (db->stopped)
+				start_adc(s);
+			avail = db->count;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (avail <= 0) {
+				if (file->f_flags & O_NONBLOCK) {
+					if (!ret)
+						ret = -EAGAIN;
+					return ret;
+				}
+				interruptible_sleep_on(&db->wait);
+				if (signal_pending(current)) {
+					if (!ret)
+						ret = -ERESTARTSYS;
+					return ret;
+				}
+			}
+		} while (avail <= 0);
+
+		MIPS_ASSERT( (avail % db->fragSize) == 0);
+		copyCount = copy_adc_to_user(s, buffer, count, avail);
+		if (copyCount <=0 ) {
+			if (!ret) ret = -EFAULT;
+			return ret;
+		}
+
+		count -= copyCount;
+		buffer += copyCount;
+		ret += copyCount;
+	} // while (count > 0)
+
+	return ret;
+}
+
+static int inline 
+copy_two_channel_dac_from_user(struct vrc5477_ac97_state *s, 
+			       const char *buffer, 
+			       int copyCount)
+{
+	struct dmabuf *db = &s->dma_dac;
+	int bufStart = db->nextIn;
+
+	MIPS_ASSERT(db->ready);
+
+        for (; copyCount > 0; ) {
+                int i;
+                int count = copyCount;
+                if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2;
+                if (copy_from_user(s->workBuf, buffer, count*2)) {
+                        return -1;
+                }
+                for (i=0; i< count/2; i++) {
+			*(u16*)(db->lbuf + bufStart + i*2) = 
+				s->workBuf[i].lchannel;
+			*(u16*)(db->rbuf + bufStart + i*2) = 
+				s->workBuf[i].rchannel;
+                }
+
+                copyCount -= count;
+		bufStart += count;
+		MIPS_ASSERT(bufStart <= db->fragTotalSize);
+                buffer += count *2;
+        }
+        return 0;
+
+}
+
+/* return the total bytes that is copied */
+static int inline 
+copy_dac_from_user(struct vrc5477_ac97_state *s, 
+		   const char *buffer, 
+		   size_t count, 
+		   int avail)
+{	
+        struct dmabuf *db = &s->dma_dac;
+        int copyCount=0;
+        int copyFragCount=0;
+        int totalCopyCount = 0;
+        int totalCopyFragCount = 0;
+        unsigned long flags;
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+	int i;
+#endif
+
+        /* adjust count to signel channel byte count */
+        count >>= s->dacChannels - 1;
+
+        /* we may have to "copy" twice as ring buffer wraps around */
+        for (; (avail > 0) && (count > 0); ) {
+                /* determine max possible copy count for single channel */
+                copyCount = count;
+                if (copyCount > avail) {
+                        copyCount = avail;
+		}
+                if (copyCount + db->nextIn > db->fragTotalSize) {
+                        copyCount = db->fragTotalSize - db->nextIn;
+                        MIPS_ASSERT((copyCount % db->fragSize) == 0);
+                        MIPS_ASSERT(copyCount > 0);
+                }
+
+		copyFragCount = (copyCount-1) >> db->fragShift;
+		copyFragCount = (copyFragCount+1) << db->fragShift;
+		MIPS_ASSERT(copyFragCount >= copyCount);
+
+		/* we copy differently based on the number channels */
+		if (s->dacChannels == 1) {
+			if (copy_from_user(db->lbuf + db->nextIn,
+					   buffer,
+					   copyCount)) 
+				return -1;
+			/* fill gaps with 0 */
+			memset(db->lbuf + db->nextIn + copyCount,
+			       0,
+			       copyFragCount - copyCount);
+		} else {
+			/* we have demux the stream into two separate ones */
+			if (copy_two_channel_dac_from_user(s, buffer, copyCount))
+				return -1;
+			/* fill gaps with 0 */
+			memset(db->lbuf + db->nextIn + copyCount,
+			       0,
+			       copyFragCount - copyCount);
+			memset(db->rbuf + db->nextIn + copyCount,
+			       0,
+			       copyFragCount - copyCount);
+		}
+
+#if defined(VRC5477_AC97_VERBOSE_DEBUG)
+		for (i=0; i< copyFragCount; i+= db->fragSize) {
+			*(u16*)(db->lbuf + db->nextIn + i) = inTicket ++;
+		}
+#endif
+
+		count -= copyCount;
+		totalCopyCount =+ copyCount;
+		avail -= copyFragCount;
+		totalCopyFragCount += copyFragCount;
+
+		buffer += copyCount << (s->dacChannels - 1);
+
+		db->nextIn += copyFragCount;
+		if (db->nextIn >= db->fragTotalSize) {
+			MIPS_ASSERT(db->nextIn == db->fragTotalSize);
+			db->nextIn = 0;
+		}
+
+		MIPS_ASSERT((copyFragCount % db->fragSize) == 0);
+		MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount));
+	}
+
+	spin_lock_irqsave(&s->lock, flags);
+        db->count += totalCopyFragCount;
+	if (db->stopped) {
+		start_dac(s);
+	}
+
+	/* nextIn should not be equal to nextOut unless we are full */
+	MIPS_ASSERT( ( (db->count == db->fragTotalSize) && 
+                       (db->nextIn == db->nextOut) ) ||
+                     ( (db->count < db->fragTotalSize) &&
+                       (db->nextIn != db->nextOut) ) );
+
+        spin_unlock_irqrestore(&s->lock, flags);
+
+        return totalCopyCount << (s->dacChannels-1);
+
+}
+
+static ssize_t vrc5477_ac97_write(struct file *file, const char *buffer,
+				  size_t count, loff_t *ppos)
+{
+	struct vrc5477_ac97_state *s = 
+		(struct vrc5477_ac97_state *)file->private_data;
+	struct dmabuf *db = &s->dma_dac;
+	ssize_t ret;
+	unsigned long flags;
+	int copyCount, avail;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+    
+	while (count > 0) {
+		// wait for space in playback buffer
+		do {
+			spin_lock_irqsave(&s->lock, flags);
+			avail = db->fragTotalSize - db->count;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (avail <= 0) {
+				if (file->f_flags & O_NONBLOCK) {
+					if (!ret)
+						ret = -EAGAIN;
+					return ret;
+				}
+				interruptible_sleep_on(&db->wait);
+				if (signal_pending(current)) {
+					if (!ret)
+						ret = -ERESTARTSYS;
+					return ret;
+				}
+			}
+		} while (avail <= 0);
+	
+		MIPS_ASSERT( (avail % db->fragSize) == 0);
+		copyCount = copy_dac_from_user(s, buffer, count, avail);
+		if (copyCount < 0) {
+			if (!ret) ret = -EFAULT;
+			return ret;
+		}
+
+		count -= copyCount;
+		buffer += copyCount;
+		ret += copyCount;
+	} // while (count > 0)
+	
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int vrc5477_ac97_poll(struct file *file,
+				      struct poll_table_struct *wait)
+{
+	struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac.wait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragSize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if ((signed)s->dma_dac.fragTotalSize >=
+		    s->dma_dac.count + (signed)s->dma_dac.fragSize)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+#ifdef CONFIG_LL_DEBUG
+static struct ioctl_str_t {
+    unsigned int cmd;
+    const char* str;
+} ioctl_str[] = {
+    {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
+    {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
+    {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
+    {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
+    {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
+    {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
+    {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
+    {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
+    {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
+    {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
+    {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
+    {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
+    {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
+    {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
+    {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
+    {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
+    {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
+    {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
+    {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
+    {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
+    {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
+    {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
+    {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
+    {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
+    {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
+    {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
+    {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
+    {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
+    {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
+    {OSS_GETVERSION, "OSS_GETVERSION"},
+    {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
+    {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
+    {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
+    {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
+};
+#endif    
+
+static int vrc5477_ac97_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	int count;
+	int val, ret;
+
+#ifdef CONFIG_LL_DEBUG
+	for (count=0; count<sizeof(ioctl_str)/sizeof(ioctl_str[0]); count++) {
+		if (ioctl_str[count].cmd == cmd)
+			break;
+	}
+	if (count < sizeof(ioctl_str)/sizeof(ioctl_str[0]))
+		printk(KERN_INFO PFX "ioctl %s\n", ioctl_str[count].str);
+	else
+		printk(KERN_INFO PFX "ioctl unknown, 0x%x\n", cmd);
+#endif
+    
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, file->f_flags & O_NONBLOCK);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX, (int *)arg);
+		
+	case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.count = 0;
+			s->dma_dac.nextIn = s->dma_dac.nextOut = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.count = 0;
+			s->dma_adc.nextIn = s->dma_adc.nextOut = 0;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				set_adc_rate(s, val);
+				if ((ret = prog_dmabuf_adc(s)))
+					return ret;
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				set_dac_rate(s, val);
+				if ((ret = prog_dmabuf_dac(s)))
+					return ret;
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ?
+				s->adcRate : s->dacRate, (int *)arg);
+
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			if (val)
+				s->adcChannels = 2;
+			else
+				s->adcChannels = 1;
+			if ((ret = prog_dmabuf_adc(s)))
+				return ret;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			if (val)
+				s->dacChannels = 2;
+			else
+				s->dacChannels = 1;
+			if ((ret = prog_dmabuf_dac(s)))
+				return ret;
+		}
+		return 0;
+
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			if ( (val != 1) && (val != 2)) val = 2;
+
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dacChannels = val;
+				if ((ret = prog_dmabuf_adc(s)))
+					return ret;
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dacChannels = val;
+				if ((ret = prog_dmabuf_dac(s)))
+					return ret;
+			}
+		}
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+		return put_user(AFMT_S16_LE, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			if (val != AFMT_S16_LE) return -EINVAL;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				if ((ret = prog_dmabuf_adc(s)))
+					return ret;
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				if ((ret = prog_dmabuf_dac(s)))
+					return ret;
+			}
+		} else {
+			val = AFMT_S16_LE;
+		}
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+		return 0;
+
+	case SNDCTL_DSP_GETTRIGGER:
+	case SNDCTL_DSP_SETTRIGGER:
+		/* NO trigger */
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		abinfo.fragsize = s->dma_dac.fragSize << (s->dacChannels-1);
+		spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		abinfo.bytes = (s->dma_dac.fragTotalSize - count) << 
+			(s->dacChannels-1);
+		abinfo.fragstotal = s->dma_dac.numFrag;
+		abinfo.fragments = abinfo.bytes >> s->dma_dac.fragShift >> 
+			(s->dacChannels-1);      
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		abinfo.fragsize = s->dma_adc.fragSize << (s->adcChannels-1);
+		spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		abinfo.bytes = count << (s->adcChannels-1);
+		abinfo.fragstotal = s->dma_adc.numFrag;
+		abinfo.fragments = (abinfo.bytes >> s->dma_adc.fragShift) >>
+			(s->adcChannels-1);      
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+	case SNDCTL_DSP_NONBLOCK:
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(count, (int *)arg);
+
+	case SNDCTL_DSP_GETIPTR:
+	case SNDCTL_DSP_GETOPTR:
+		/* we cannot get DMA ptr */
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE)
+			return put_user(s->dma_dac.fragSize << (s->dacChannels-1), (int *)arg);
+		else
+			return put_user(s->dma_adc.fragSize << (s->adcChannels-1), (int *)arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		/* we ignore fragment size request */
+		return 0;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		/* what is this for? [jsun] */
+		return 0;
+
+	case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ?
+				s->adcRate : s->dacRate, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		if (file->f_mode & FMODE_READ)
+			return put_user(s->adcChannels, (int *)arg);
+		else
+			return put_user(s->dacChannels ? 2 : 1, (int *)arg);
+	    
+	case SOUND_PCM_READ_BITS:
+		return put_user(16, (int *)arg);
+
+	case SOUND_PCM_WRITE_FILTER:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_READ_FILTER:
+		return -EINVAL;
+	}
+
+	return mixdev_ioctl(&s->codec, cmd, arg);
+}
+
+
+static int vrc5477_ac97_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct vrc5477_ac97_state *s;
+	int ret=0;
+    
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct vrc5477_ac97_state, devs);
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+	file->private_data = s;
+
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+
+	spin_lock_irqsave(&s->lock, flags);
+
+	if (file->f_mode & FMODE_READ) {
+		/* set default settings */
+		set_adc_rate(s, 48000);
+		s->adcChannels = 2;
+
+		ret = prog_dmabuf_adc(s);
+		if (ret) goto bailout;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		/* set default settings */
+		set_dac_rate(s, 48000);
+		s->dacChannels = 2;
+
+		ret = prog_dmabuf_dac(s);
+		if (ret) goto bailout;
+	}
+
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+
+ bailout:
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	up(&s->open_sem);
+	return ret;
+}
+
+static int vrc5477_ac97_release(struct inode *inode, struct file *file)
+{
+	struct vrc5477_ac97_state *s = 
+		(struct vrc5477_ac97_state *)file->private_data;
+
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		dealloc_dmabuf(s, &s->dma_dac);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(s, &s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations vrc5477_ac97_audio_fops = {
+	owner:	THIS_MODULE,
+	llseek:		no_llseek,
+	read:		vrc5477_ac97_read,
+	write:		vrc5477_ac97_write,
+	poll:		vrc5477_ac97_poll,
+	ioctl:		vrc5477_ac97_ioctl,
+	// mmap:	vrc5477_ac97_mmap,
+	open:		vrc5477_ac97_open,
+	release:	vrc5477_ac97_release,
+};
+
+
+/* --------------------------------------------------------------------- */
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * for debugging purposes, we'll create a proc device that dumps the
+ * CODEC chipstate
+ */
+
+#ifdef CONFIG_LL_DEBUG
+
+struct {
+       const char *regname;
+       unsigned regaddr;
+} vrc5477_ac97_regs[] = {
+	{"VRC5477_INT_STATUS", VRC5477_INT_STATUS},
+	{"VRC5477_CODEC_WR", VRC5477_CODEC_WR},
+	{"VRC5477_CODEC_RD", VRC5477_CODEC_RD},
+	{"VRC5477_CTRL", VRC5477_CTRL},
+	{"VRC5477_ACLINK_CTRL", VRC5477_ACLINK_CTRL},
+	{"VRC5477_INT_MASK", VRC5477_INT_MASK},
+	{"VRC5477_DAC1_CTRL", VRC5477_DAC1_CTRL},
+	{"VRC5477_DAC1L", VRC5477_DAC1L},
+	{"VRC5477_DAC1_BADDR", VRC5477_DAC1_BADDR},
+	{"VRC5477_DAC2_CTRL", VRC5477_DAC2_CTRL},
+	{"VRC5477_DAC2L", VRC5477_DAC2L},
+	{"VRC5477_DAC2_BADDR", VRC5477_DAC2_BADDR},
+	{"VRC5477_DAC3_CTRL", VRC5477_DAC3_CTRL},
+	{"VRC5477_DAC3L", VRC5477_DAC3L},
+	{"VRC5477_DAC3_BADDR", VRC5477_DAC3_BADDR},
+	{"VRC5477_ADC1_CTRL", VRC5477_ADC1_CTRL},
+	{"VRC5477_ADC1L", VRC5477_ADC1L},
+	{"VRC5477_ADC1_BADDR", VRC5477_ADC1_BADDR},
+	{"VRC5477_ADC2_CTRL", VRC5477_ADC2_CTRL},
+	{"VRC5477_ADC2L", VRC5477_ADC2L},
+	{"VRC5477_ADC2_BADDR", VRC5477_ADC2_BADDR},
+	{"VRC5477_ADC3_CTRL", VRC5477_ADC3_CTRL},
+	{"VRC5477_ADC3L", VRC5477_ADC3L},
+	{"VRC5477_ADC3_BADDR", VRC5477_ADC3_BADDR},
+	{NULL, 0x0}
+};
+
+static int proc_vrc5477_ac97_dump (char *buf, char **start, off_t fpos,
+				   int length, int *eof, void *data)
+{
+	struct vrc5477_ac97_state *s;
+	int cnt, len = 0;
+
+	if (list_empty(&devs))
+		return 0;
+	s = list_entry(devs.next, struct vrc5477_ac97_state, devs);
+
+	/* print out header */
+	len += sprintf(buf + len, "\n\t\tVrc5477 Audio Debug\n\n");
+
+	// print out digital controller state
+	len += sprintf (buf + len, "NEC Vrc5477 Audio Controller registers\n");
+	len += sprintf (buf + len, "---------------------------------\n");
+	for (cnt=0; vrc5477_ac97_regs[cnt].regname != NULL; cnt++) {
+		len+= sprintf (buf + len, "%-20s = %08x\n",
+			       vrc5477_ac97_regs[cnt].regname,
+			       inl(s->io + vrc5477_ac97_regs[cnt].regaddr));
+	}
+   
+	/* print out driver state */
+	len += sprintf (buf + len, "NEC Vrc5477 Audio driver states\n");
+	len += sprintf (buf + len, "---------------------------------\n");
+	len += sprintf (buf + len, "dacChannels  = %d\n", s->dacChannels);
+	len += sprintf (buf + len, "adcChannels  = %d\n", s->adcChannels);
+	len += sprintf (buf + len, "dacRate  = %d\n", s->dacRate);
+	len += sprintf (buf + len, "adcRate  = %d\n", s->adcRate);
+
+	len += sprintf (buf + len, "dma_dac is %s ready\n",  
+	                s->dma_dac.ready? "" : "not");
+        if (s->dma_dac.ready) {
+		len += sprintf (buf + len, "dma_dac is %s stopped.\n",  
+	                        s->dma_dac.stopped? "" : "not");
+		len += sprintf (buf + len, "dma_dac.fragSize = %x\n", 
+                                s->dma_dac.fragSize);
+		len += sprintf (buf + len, "dma_dac.fragShift = %x\n", 
+                                s->dma_dac.fragShift);
+		len += sprintf (buf + len, "dma_dac.numFrag = %x\n", 
+                                s->dma_dac.numFrag);
+		len += sprintf (buf + len, "dma_dac.fragTotalSize = %x\n", 
+                                s->dma_dac.fragTotalSize);
+		len += sprintf (buf + len, "dma_dac.nextIn = %x\n", 
+                                s->dma_dac.nextIn);
+		len += sprintf (buf + len, "dma_dac.nextOut = %x\n", 
+                                s->dma_dac.nextOut);
+		len += sprintf (buf + len, "dma_dac.count = %x\n", 
+                                s->dma_dac.count);
+	}
+
+	len += sprintf (buf + len, "dma_adc is %s ready\n",  
+	                s->dma_adc.ready? "" : "not");
+        if (s->dma_adc.ready) {
+		len += sprintf (buf + len, "dma_adc is %s stopped.\n",  
+	                        s->dma_adc.stopped? "" : "not");
+		len += sprintf (buf + len, "dma_adc.fragSize = %x\n", 
+                                s->dma_adc.fragSize);
+		len += sprintf (buf + len, "dma_adc.fragShift = %x\n", 
+                                s->dma_adc.fragShift);
+		len += sprintf (buf + len, "dma_adc.numFrag = %x\n", 
+                                s->dma_adc.numFrag);
+		len += sprintf (buf + len, "dma_adc.fragTotalSize = %x\n", 
+                                s->dma_adc.fragTotalSize);
+		len += sprintf (buf + len, "dma_adc.nextIn = %x\n", 
+                                s->dma_adc.nextIn);
+		len += sprintf (buf + len, "dma_adc.nextOut = %x\n", 
+                                s->dma_adc.nextOut);
+		len += sprintf (buf + len, "dma_adc.count = %x\n", 
+                                s->dma_adc.count);
+	}
+	 
+	/* print out CODEC state */
+	len += sprintf (buf + len, "\nAC97 CODEC registers\n");
+	len += sprintf (buf + len, "----------------------\n");
+	for (cnt=0; cnt <= 0x7e; cnt = cnt +2)
+		len+= sprintf (buf + len, "reg %02x = %04x\n",
+			       cnt, rdcodec(&s->codec, cnt));
+
+	if (fpos >=len){
+		*start = buf;
+		*eof =1;
+		return 0;
+	}
+	*start = buf + fpos;
+	if ((len -= fpos) > length)
+		return length;
+	*eof =1;
+	return len;
+
+}
+#endif /* CONFIG_LL_DEBUG */
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices; only used for command line params */
+#define NR_DEVICE 5
+
+static unsigned int devindex = 0;
+
+MODULE_AUTHOR("Monta Vista Software, jsun@mvista.com or jsun@junsun.net");
+MODULE_DESCRIPTION("NEC Vrc5477 audio (AC97) Driver");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+extern void jsun_scan_pci_bus(void);
+extern void vrc5477_show_pci_regs(void);
+extern void vrc5477_show_pdar_regs(void);
+
+/* -------------------------------------------------------- */
+#define         AC97_BASE               0xbb000000
+#define         myinl(x)                  *(volatile u32*)(AC97_BASE + (x))
+#define         myoutl(x,y)               *(volatile u32*)(AC97_BASE + (y)) = (x)
+
+u16 myrdcodec(u8 addr)
+{
+        u32 result;
+
+        /* wait until we can access codec registers */
+        // while (inl(VRC5477_CODEC_WR) & 0x80000000);
+
+        /* write the address and "read" command to codec */
+        addr = addr & 0x7f;
+        myoutl((addr << 16) | VRC5477_CODEC_WR_RWC, VRC5477_CODEC_WR);
+
+        /* get the return result */
+        udelay(100); /* workaround hardware bug */
+        // dump_memory(0xbb000000, 48);
+        while ( ((result=myinl(VRC5477_CODEC_RD)) & 0xc0000000) != 0xc0000000);
+        MIPS_ASSERT(addr == ((result >> 16) & 0x7f) );
+        return result & 0xffff;
+}
+
+void mywrcodec(u8 addr, u16 data)
+{
+        /* wait until we can access codec registers */
+        while (myinl(VRC5477_CODEC_WR) & 0x80000000);
+
+        /* write the address and value to codec */
+        myoutl((addr << 16) | data, VRC5477_CODEC_WR);
+
+}
+
+
+void jsun_ac97_test(struct vrc5477_ac97_state *s)
+{
+        int i;
+
+        /* reset codec */
+	/*
+        wrcodec(&s->codec, 0, 0);
+        while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000);
+	*/
+        mywrcodec(0, 0);
+        while (myinl(VRC5477_CODEC_WR) & 0x80000000);
+
+	for (i=0; i< 0x40; i+=4) {	
+	MIPS_ASSERT(inl(s->io+i) == myinl(i));
+	}
+
+        printk("codec registers : ");
+        for (i=0; i<= 0x3a; i+=2) {
+                if ( (i%0x10) == 0) {
+                        printk("\n%02x\t", i);
+                }
+                // printk("%04x\t", rdcodec(&s->codec, i));
+                printk("%04x\t", myrdcodec(i));
+        }
+        printk("\n\n");
+        printk("codec registers : ");
+        for (i=0; i<= 0x3a; i+=2) {
+                if ( (i%0x10) == 0) {
+                        printk("\n%02x\t", i);
+                }
+                printk("%04x\t", rdcodec(&s->codec, i));
+        }
+        printk("\n\n");
+}
+
+static int __devinit vrc5477_ac97_probe(struct pci_dev *pcidev,
+					const struct pci_device_id *pciid)
+{
+	struct vrc5477_ac97_state *s;
+	char proc_str[80];
+
+	MIPS_DEBUG(printk("vrc5477_ac97_probe() invoked\n"));
+
+	if (pcidev->irq == 0) 
+		return -1;
+
+	if (!(s = kmalloc(sizeof(struct vrc5477_ac97_state), GFP_KERNEL))) {
+		printk(KERN_ERR PFX "alloc of device struct failed\n");
+		return -1;
+	}
+	memset(s, 0, sizeof(struct vrc5477_ac97_state));
+
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+
+	s->dev = pcidev;
+	s->io = pci_resource_start(pcidev, 0);
+	s->irq = pcidev->irq;
+
+	s->codec.private_data = s;
+	s->codec.id = 0;
+	s->codec.codec_read = rdcodec;
+	s->codec.codec_write = wrcodec;
+	s->codec.codec_wait = waitcodec;
+
+	/* setting some other default values such as
+	 * adcChannels, adcRate is done in open() so that
+         * no persistent state across file opens.
+	 */
+
+	if (!request_region(s->io, pci_resource_len(pcidev,0),
+			    VRC5477_AC97_MODULE_NAME)) {
+		printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n",
+		       s->io, s->io + pci_resource_len(pcidev,0)-1);
+		goto err_region;
+	}
+	if (request_irq(s->irq, vrc5477_ac97_interrupt, SA_INTERRUPT,
+			VRC5477_AC97_MODULE_NAME, s)) {
+		printk(KERN_ERR PFX "irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+
+	printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq);
+
+	/* register devices */
+	if ((s->dev_audio = register_sound_dsp(&vrc5477_ac97_audio_fops, -1)) < 0)
+		goto err_dev1;
+	if ((s->codec.dev_mixer =
+	     register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0)
+		goto err_dev2;
+
+#ifdef CONFIG_LL_DEBUG
+	/* intialize the debug proc device */
+	s->ps = create_proc_read_entry(VRC5477_AC97_MODULE_NAME, 0, NULL,
+				       proc_vrc5477_ac97_dump, NULL);
+#endif /* CONFIG_LL_DEBUG */
+	
+	/* enable pci io and bus mastering */
+	if (pci_enable_device(pcidev))
+		goto err_dev3;
+	pci_set_master(pcidev);
+
+/*
+jsun_scan_pci_bus();
+vrc5477_show_pci_regs();
+vrc5477_show_pdar_regs();
+*/
+
+	/* cold reset the AC97 */
+	outl(VRC5477_ACLINK_CTRL_RST_ON | VRC5477_ACLINK_CTRL_RST_TIME,
+	     s->io + VRC5477_ACLINK_CTRL);
+	while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON);
+
+/*
+jsun_ac97_test(s);
+*/
+
+	/* codec init */
+	if (!ac97_probe_codec(&s->codec))
+		goto err_dev3;
+
+#ifdef CONFIG_LL_DEBUG
+	sprintf(proc_str, "driver/%s/%d/ac97", 
+		VRC5477_AC97_MODULE_NAME, s->codec.id);
+	s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL,
+					     ac97_read_proc, &s->codec);
+	/* TODO : why this proc file does not show up? */
+#endif
+
+        /* let us get the default volumne louder */
+        wrcodec(&s->codec, 0x2, 0);
+        wrcodec(&s->codec, 0x18, 0x0707);
+	/* mute line in loopback to line out */
+	wrcodec(&s->codec, 0x10, 0x8000);
+
+	/* by default we select line in the input */
+	wrcodec(&s->codec, 0x1a, 0x0404);
+	/* pick middle value for record gain */
+	// wrcodec(&s->codec, 0x1c, 0x0707);
+	wrcodec(&s->codec, 0x1c, 0x0f0f);
+	wrcodec(&s->codec, 0x1e, 0x07);
+
+	/* enable the master interrupt but disable all others */
+	outl(VRC5477_INT_MASK_NMASK, s->io + VRC5477_INT_MASK);
+
+	/* store it in the driver field */
+	pci_set_drvdata(pcidev, s);
+	pcidev->dma_mask = 0xffffffff;
+	/* put it into driver list */
+	list_add_tail(&s->devs, &devs);
+	/* increment devindex */
+	if (devindex < NR_DEVICE-1)
+		devindex++;
+	return 0;
+
+ err_dev3:
+	unregister_sound_mixer(s->codec.dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR PFX "cannot register misc device\n");
+	free_irq(s->irq, s);
+ err_irq:
+	release_region(s->io, pci_resource_len(pcidev,0));
+ err_region:
+	kfree(s);
+	return -1;
+}
+
+static void __devinit vrc5477_ac97_remove(struct pci_dev *dev)
+{
+	struct vrc5477_ac97_state *s = pci_get_drvdata(dev);
+
+	if (!s)
+		return;
+	list_del(&s->devs);
+#ifdef CONFIG_LL_DEBUG
+	if (s->ps)
+		remove_proc_entry(VRC5477_AC97_MODULE_NAME, NULL);
+#endif /* CONFIG_LL_DEBUG */
+	synchronize_irq();
+	free_irq(s->irq, s);
+	release_region(s->io, pci_resource_len(dev,0));
+	unregister_sound_dsp(s->dev_audio);
+	unregister_sound_mixer(s->codec.dev_mixer);
+	kfree(s);
+	pci_set_drvdata(dev, NULL);
+}
+
+
+#define		PCI_VENDOR_ID_NEC		0x1033
+#define		PCI_DEVICE_ID_NEC_VRC5477_AC97	0x00A6
+static struct pci_device_id id_table[] __devinitdata = {
+    { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_VRC5477_AC97, 
+      PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+    { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver vrc5477_ac97_driver = {
+	name: VRC5477_AC97_MODULE_NAME,
+	id_table: id_table,
+	probe: vrc5477_ac97_probe,
+	remove: vrc5477_ac97_remove
+};
+
+static int __init init_vrc5477_ac97(void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk("Vrc5477 AC97 driver: version v0.1 time " __TIME__ " " __DATE__ " by Jun Sun\n");
+	return pci_module_init(&vrc5477_ac97_driver);
+}
+
+static void __exit cleanup_vrc5477_ac97(void)
+{
+	printk(KERN_INFO PFX "unloading\n");
+	pci_unregister_driver(&vrc5477_ac97_driver);
+}
+
+module_init(init_vrc5477_ac97);
+module_exit(cleanup_vrc5477_ac97);
+
diff -Nru linux/sound/oss/nm256.h linux-2.4.19-pre5-mjc/sound/oss/nm256.h
--- linux/sound/oss/nm256.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/nm256.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,290 @@
+#ifndef _NM256_H_
+#define _NM256_H_
+
+#include "ac97.h"
+
+/* The revisions that we currently handle.  */
+enum nm256rev {
+    REV_NM256AV, REV_NM256ZX
+};
+
+/* Per-card structure. */
+struct nm256_info 
+{
+    /* Magic number used to verify that this struct is valid. */
+#define NM_MAGIC_SIG 0x55aa00ff
+    int magsig;
+
+    /* Revision number */
+    enum nm256rev rev;
+
+    struct ac97_hwint mdev;
+
+    /* Our audio device numbers. */
+    int dev[2];
+
+    /* The # of times each device has been opened. (Should only be 
+       0 or 1). */
+    int opencnt[2];
+
+    /* We use two devices, because we can do simultaneous play and record.
+       This keeps track of which device is being used for what purpose;
+       these are the actual device numbers. */
+    int dev_for_play;
+    int dev_for_record;
+
+    /* The mixer device. */
+    int mixer_oss_dev;
+
+    /* 
+     * Can only be opened once for each operation.  These aren't set
+     * until an actual I/O operation is performed; this allows one
+     * device to be open for read/write without inhibiting I/O to
+     * the other device.
+     */
+    int is_open_play;
+    int is_open_record;
+
+    /* Non-zero if we're currently playing a sample. */
+    int playing;
+    /* Ditto for recording a sample. */
+    int recording;
+
+    /* The two memory ports.  */
+    struct nm256_ports {
+	/* Physical address of the port. */
+	u32 physaddr;
+	/* Our mapped-in pointer. */
+	char *ptr;
+	/* PTR's offset within the physical port.  */
+	u32 start_offset;
+	/* And the offset of the end of the buffer.  */
+	u32 end_offset;
+    } port[2];
+
+    /* The following are offsets within memory port 1. */
+    u32 coeffBuf;
+    u32 allCoeffBuf;
+
+    /* Record and playback buffers. */
+    u32 abuf1, abuf2;
+
+    /* Offset of the AC97 mixer in memory port 2. */
+    u32 mixer;
+
+    /* Offset of the mixer status register in memory port 2.  */
+    u32 mixer_status_offset;
+
+    /* Non-zero if we have written initial values to the mixer. */
+    u8 mixer_values_init;
+
+    /* 
+     * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means
+     * it's ready.  
+     */
+    u16 mixer_status_mask;
+
+    /* The sizes of the playback and record ring buffers. */
+    u32 playbackBufferSize;
+    u32 recordBufferSize;
+
+    /* Are the coefficient values in the memory cache current? */
+    u8 coeffsCurrent;
+
+    /* For writes, the amount we last wrote. */
+    u32 requested_amt;
+    /* The start of the block currently playing. */
+    u32 curPlayPos;
+
+    /* The amount of data we were requested to record. */
+    u32 requestedRecAmt;
+    /* The offset of the currently-recording block. */
+    u32 curRecPos;
+    /* The destination buffer. */
+    char *recBuf;
+
+    /* Our IRQ number. */
+    int irq;
+
+    /* A flag indicating how many times we've grabbed the IRQ. */
+    int has_irq;
+
+    /* The card interrupt service routine. */
+    void (*introutine) (int, void *, struct pt_regs *);
+
+    /* Current audio config, cached. */
+    struct sinfo {
+	u32 samplerate;
+	u8 bits;
+	u8 stereo;
+    } sinfo[2]; /* goes with each device */
+
+    /* The cards are stored in a chain;  this is the next card. */
+    struct nm256_info *next_card;
+};
+
+/* Debug flag--bigger numbers mean more output. */
+extern int nm256_debug;
+
+/* The BIOS signature. */
+#define NM_SIGNATURE 0x4e4d0000
+/* Signature mask. */
+#define NM_SIG_MASK 0xffff0000
+
+/* Size of the second memory area. */
+#define NM_PORT2_SIZE 4096
+
+/* The base offset of the mixer in the second memory area. */
+#define NM_MIXER_OFFSET 0x600
+
+/* The maximum size of a coefficient entry. */
+#define NM_MAX_COEFFICIENT 0x5000
+
+/* The interrupt register. */
+#define NM_INT_REG 0xa04
+/* And its bits. */
+#define NM_PLAYBACK_INT 0x40
+#define NM_RECORD_INT 0x100
+#define NM_MISC_INT_1 0x4000
+#define NM_MISC_INT_2 0x1
+#define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1)
+
+/* The AV's "mixer ready" status bit and location. */
+#define NM_MIXER_STATUS_OFFSET 0xa04
+#define NM_MIXER_READY_MASK 0x0800
+#define NM_MIXER_PRESENCE 0xa06
+#define NM_PRESENCE_MASK 0x0050
+#define NM_PRESENCE_VALUE 0x0040
+
+/*
+ * For the ZX.  It uses the same interrupt register, but it holds 32
+ * bits instead of 16.
+ */
+#define NM2_PLAYBACK_INT 0x10000
+#define NM2_RECORD_INT 0x80000
+#define NM2_MISC_INT_1 0x8
+#define NM2_MISC_INT_2 0x2
+#define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X))
+
+/* The ZX's "mixer ready" status bit and location. */
+#define NM2_MIXER_STATUS_OFFSET 0xa06
+#define NM2_MIXER_READY_MASK 0x0800
+
+/* The playback registers start from here. */
+#define NM_PLAYBACK_REG_OFFSET 0x0
+/* The record registers start from here. */
+#define NM_RECORD_REG_OFFSET 0x200
+
+/* The rate register is located 2 bytes from the start of the register area. */
+#define NM_RATE_REG_OFFSET 2
+
+/* Mono/stereo flag, number of bits on playback, and rate mask. */
+#define NM_RATE_STEREO 1
+#define NM_RATE_BITS_16 2
+#define NM_RATE_MASK 0xf0
+
+/* Playback enable register. */
+#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
+#define NM_PLAYBACK_ENABLE_FLAG 1
+#define NM_PLAYBACK_ONESHOT 2
+#define NM_PLAYBACK_FREERUN 4
+
+/* Mutes the audio output. */
+#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
+#define NM_AUDIO_MUTE_LEFT 0x8000
+#define NM_AUDIO_MUTE_RIGHT 0x0080
+
+/* Recording enable register. */
+#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
+#define NM_RECORD_ENABLE_FLAG 1
+#define NM_RECORD_FREERUN 2
+
+#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
+#define NM_RBUFFER_END   (NM_RECORD_REG_OFFSET + 0x10)
+#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
+#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)
+
+#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
+#define NM_PBUFFER_END   (NM_PLAYBACK_REG_OFFSET + 0x14)
+#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
+#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)
+
+/* A few trivial routines to make it easier to work with the registers
+   on the chip. */
+
+/* This is a common code portion used to fix up the port offsets. */
+#define NM_FIX_PORT \
+  if (port < 1 || port > 2 || card == NULL) \
+      return -1; \
+\
+    if (offset < card->port[port - 1].start_offset \
+	|| offset >= card->port[port - 1].end_offset) { \
+	printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \
+	return -1; \
+    } \
+    offset -= card->port[port - 1].start_offset;
+
+#define DEFwritePortX(X, func) \
+static inline int nm256_writePort##X (struct nm256_info *card,\
+				      int port, int offset, int value)\
+{\
+    u##X *addr;\
+\
+    if (nm256_debug > 1)\
+        printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\
+\
+    NM_FIX_PORT;\
+\
+    addr = (u##X *)(card->port[port - 1].ptr + offset);\
+    func (value, addr);\
+    return 0;\
+}
+
+DEFwritePortX (8, writeb)
+DEFwritePortX (16, writew)
+DEFwritePortX (32, writel)
+
+#define DEFreadPortX(X, func) \
+static inline u##X nm256_readPort##X (struct nm256_info *card,\
+					int port, int offset)\
+{\
+    u##X *addr;\
+\
+    NM_FIX_PORT\
+\
+    addr = (u##X *)(card->port[port - 1].ptr + offset);\
+    return func(addr);\
+}
+
+DEFreadPortX (8, readb)
+DEFreadPortX (16, readw)
+DEFreadPortX (32, readl)
+
+static inline int
+nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset,
+		      int amt)
+{
+    NM_FIX_PORT;
+    memcpy_toio (card->port[port - 1].ptr + offset, src, amt);
+    return 0;
+}
+
+static inline int
+nm256_readBuffer8 (struct nm256_info *card, u8 *dst, int port, int offset,
+		     int amt)
+{
+    NM_FIX_PORT;
+    memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt);
+    return 0;
+}
+
+/* Returns a non-zero value if we should use the coefficient cache. */
+extern int nm256_cachedCoefficients (struct nm256_info *card);
+
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Nru linux/sound/oss/nm256_audio.c linux-2.4.19-pre5-mjc/sound/oss/nm256_audio.c
--- linux/sound/oss/nm256_audio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/nm256_audio.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1690 @@
+/* 
+ * Audio driver for the NeoMagic 256AV and 256ZX chipsets in native
+ * mode, with AC97 mixer support.
+ *
+ * Overall design and parts of this code stolen from vidc_*.c and
+ * skeleton.c.
+ *
+ * Yeah, there are a lot of magic constants in here.  You tell ME what
+ * they are.  I just get this stuff psychically, remember? 
+ *
+ * This driver was written by someone who wishes to remain anonymous. 
+ * It is in the public domain, so share and enjoy.  Try to make a profit
+ * off of it; go on, I dare you.  
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added some __init
+ * 19-04-2001	Marcus Meissner <mm@caldera.de>
+ *		Ported to 2.4 PCI API.
+ */
+
+#define __NO_VERSION__
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include "sound_config.h"
+#include "nm256.h"
+#include "nm256_coeff.h"
+
+int nm256_debug;
+static int force_load;
+
+/* 
+ * The size of the playback reserve.  When the playback buffer has less
+ * than NM256_PLAY_WMARK_SIZE bytes to output, we request a new
+ * buffer.
+ */
+#define NM256_PLAY_WMARK_SIZE 512
+
+static struct audio_driver nm256_audio_driver;
+
+static int nm256_grabInterrupt (struct nm256_info *card);
+static int nm256_releaseInterrupt (struct nm256_info *card);
+static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy);
+static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy);
+static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data);
+
+/* These belong in linux/pci.h. */
+#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
+#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+
+/* List of cards.  */
+static struct nm256_info *nmcard_list;
+
+/* Release the mapped-in memory for CARD.  */
+static void
+nm256_release_ports (struct nm256_info *card)
+{
+    int x;
+
+    for (x = 0; x < 2; x++) {
+	if (card->port[x].ptr != NULL) {
+	    iounmap (card->port[x].ptr);
+	    card->port[x].ptr = NULL;
+	}
+    }
+}
+
+/* 
+ * Map in the memory ports for CARD, if they aren't already mapped in
+ * and have been configured.  If successful, a zero value is returned;
+ * otherwise any previously mapped-in areas are released and a non-zero
+ * value is returned.
+ *
+ * This is invoked twice, once for each port.  Ideally it would only be
+ * called once, but we now need to map in the second port in order to
+ * check how much memory the card has on the 256ZX.
+ */
+static int
+nm256_remap_ports (struct nm256_info *card)
+{
+    int x;
+
+    for (x = 0; x < 2; x++) {
+	if (card->port[x].ptr == NULL && card->port[x].end_offset > 0) {
+	    u32 physaddr 
+		= card->port[x].physaddr + card->port[x].start_offset;
+	    u32 size 
+		= card->port[x].end_offset - card->port[x].start_offset;
+
+	    card->port[x].ptr = ioremap_nocache (physaddr, size);
+						  
+	    if (card->port[x].ptr == NULL) {
+		printk (KERN_ERR "NM256: Unable to remap port %d\n", x + 1);
+		nm256_release_ports (card);
+		return -1;
+	    }
+	}
+    }
+    return 0;
+}
+
+/* Locate the card in our list. */
+static struct nm256_info *
+nm256_find_card (int dev)
+{
+    struct nm256_info *card;
+
+    for (card = nmcard_list; card != NULL; card = card->next_card)
+	if (card->dev[0] == dev || card->dev[1] == dev)
+	    return card;
+
+    return NULL;
+}
+
+/*
+ * Ditto, but find the card struct corresponding to the mixer device DEV 
+ * instead. 
+ */
+static struct nm256_info *
+nm256_find_card_for_mixer (int dev)
+{
+    struct nm256_info *card;
+
+    for (card = nmcard_list; card != NULL; card = card->next_card)
+	if (card->mixer_oss_dev == dev)
+	    return card;
+
+    return NULL;
+}
+
+static int usecache;
+static int buffertop;
+
+/* Check to see if we're using the bank of cached coefficients. */
+int
+nm256_cachedCoefficients (struct nm256_info *card)
+{
+    return usecache;
+}
+
+/* The actual rates supported by the card. */
+static int samplerates[9] = {
+    8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999
+};
+
+/*
+ * Set the card samplerate, word size and stereo mode to correspond to
+ * the settings in the CARD struct for the specified device in DEV.
+ * We keep two separate sets of information, one for each device; the
+ * hardware is not actually configured until a read or write is
+ * attempted.
+ */
+
+int
+nm256_setInfo (int dev, struct nm256_info *card)
+{
+    int x;
+    int w;
+    int targetrate;
+
+    if (card->dev[0] == dev)
+	w = 0;
+    else if (card->dev[1] == dev)
+	w = 1;
+    else
+	return -ENODEV;
+
+    targetrate = card->sinfo[w].samplerate;
+
+    if ((card->sinfo[w].bits != 8 && card->sinfo[w].bits != 16)
+	|| targetrate < samplerates[0]
+	|| targetrate > samplerates[7])
+	return -EINVAL;
+
+    for (x = 0; x < 8; x++)
+	if (targetrate < ((samplerates[x] + samplerates[x + 1]) / 2))
+	    break;
+
+    if (x < 8) {
+	u8 ratebits = ((x << 4) & NM_RATE_MASK);
+	if (card->sinfo[w].bits == 16)
+	    ratebits |= NM_RATE_BITS_16;
+	if (card->sinfo[w].stereo)
+	    ratebits |= NM_RATE_STEREO;
+
+	card->sinfo[w].samplerate = samplerates[x];
+
+
+	if (card->dev_for_play == dev && card->playing) {
+	    if (nm256_debug)
+		printk (KERN_DEBUG "Setting play ratebits to 0x%x\n",
+			ratebits);
+	    nm256_loadCoefficient (card, 0, x);
+	    nm256_writePort8 (card, 2,
+			      NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
+			      ratebits);
+	}
+
+	if (card->dev_for_record == dev && card->recording) {
+	    if (nm256_debug)
+		printk (KERN_DEBUG "Setting record ratebits to 0x%x\n",
+			ratebits);
+	    nm256_loadCoefficient (card, 1, x);
+	    nm256_writePort8 (card, 2,
+			      NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
+			      ratebits);
+	}
+	return 0;
+    }
+    else
+	return -EINVAL;
+}
+
+/* Start the play process going. */
+static void
+startPlay (struct nm256_info *card)
+{
+    if (! card->playing) {
+	card->playing = 1;
+	if (nm256_grabInterrupt (card) == 0) {
+	    nm256_setInfo (card->dev_for_play, card);
+
+	    /* Enable playback engine and interrupts. */
+	    nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG,
+			      NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
+
+	    /* Enable both channels. */
+	    nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, 0x0);
+	}
+    }
+}
+
+/* 
+ * Request one chunk of AMT bytes from the recording device.  When the
+ * operation is complete, the data will be copied into BUFFER and the
+ * function DMAbuf_inputintr will be invoked.
+ */
+
+static void
+nm256_startRecording (struct nm256_info *card, char *buffer, u32 amt)
+{
+    u32 endpos;
+    int enableEngine = 0;
+    u32 ringsize = card->recordBufferSize;
+    unsigned long flags;
+
+    if (amt > (ringsize / 2)) {
+	/*
+	 * Of course this won't actually work right, because the
+	 * caller is going to assume we will give what we got asked
+	 * for.
+	 */
+	printk (KERN_ERR "NM256: Read request too large: %d\n", amt);
+	amt = ringsize / 2;
+    }
+
+    if (amt < 8) {
+	printk (KERN_ERR "NM256: Read request too small; %d\n", amt);
+	return;
+    }
+
+    save_flags (flags);
+    cli ();
+    /*
+     * If we're not currently recording, set up the start and end registers
+     * for the recording engine.
+     */
+    if (! card->recording) {
+	card->recording = 1;
+	if (nm256_grabInterrupt (card) == 0) {
+	    card->curRecPos = 0;
+	    nm256_setInfo (card->dev_for_record, card);
+	    nm256_writePort32 (card, 2, NM_RBUFFER_START, card->abuf2);
+	    nm256_writePort32 (card, 2, NM_RBUFFER_END,
+				 card->abuf2 + ringsize);
+
+	    nm256_writePort32 (card, 2, NM_RBUFFER_CURRP,
+				 card->abuf2 + card->curRecPos);
+	    enableEngine = 1;
+	}
+	else {
+	    /* Not sure what else to do here.  */
+	    restore_flags (flags);
+	    return;
+	}
+    }
+
+    /* 
+     * If we happen to go past the end of the buffer a bit (due to a
+     * delayed interrupt) it's OK.  So might as well set the watermark
+     * right at the end of the data we want.
+     */
+    endpos = card->abuf2 + ((card->curRecPos + amt) % ringsize);
+
+    card->recBuf = buffer;
+    card->requestedRecAmt = amt;
+    nm256_writePort32 (card, 2, NM_RBUFFER_WMARK, endpos);
+    /* Enable recording engine and interrupts. */
+    if (enableEngine)
+	nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG,
+			    NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN);
+
+    restore_flags (flags);
+}
+
+/* Stop the play engine. */
+static void
+stopPlay (struct nm256_info *card)
+{
+    /* Shut off sound from both channels. */
+    nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG,
+		       NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
+    /* Disable play engine. */
+    nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, 0);
+    if (card->playing) {
+	nm256_releaseInterrupt (card);
+
+	/* Reset the relevant state bits. */
+	card->playing = 0;
+	card->curPlayPos = 0;
+    }
+}
+
+/* Stop recording. */
+static void
+stopRecord (struct nm256_info *card)
+{
+    /* Disable recording engine. */
+    nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, 0);
+
+    if (card->recording) {
+	nm256_releaseInterrupt (card);
+
+	card->recording = 0;
+	card->curRecPos = 0;
+    }
+}
+
+/*
+ * Ring buffers, man.  That's where the hip-hop, wild-n-wooly action's at.
+ * 1972?  (Well, I suppose it was cheep-n-easy to implement.)
+ *
+ * Write AMT bytes of BUFFER to the playback ring buffer, and start the
+ * playback engine running.  It will only accept up to 1/2 of the total
+ * size of the ring buffer.  No check is made that we're about to overwrite
+ * the currently-playing sample.
+ */
+
+static void
+nm256_write_block (struct nm256_info *card, char *buffer, u32 amt)
+{
+    u32 ringsize = card->playbackBufferSize;
+    u32 endstop;
+    unsigned long flags;
+
+    if (amt > (ringsize / 2)) {
+	printk (KERN_ERR "NM256: Write request too large: %d\n", amt);
+	amt = (ringsize / 2);
+    }
+
+    if (amt < NM256_PLAY_WMARK_SIZE) {
+	printk (KERN_ERR "NM256: Write request too small: %d\n", amt);
+	return;
+    }
+
+    card->curPlayPos %= ringsize;
+
+    card->requested_amt = amt;
+
+    save_flags (flags);
+    cli ();
+
+    if ((card->curPlayPos + amt) >= ringsize) {
+	u32 rem = ringsize - card->curPlayPos;
+
+	nm256_writeBuffer8 (card, buffer, 1,
+			      card->abuf1 + card->curPlayPos,
+			      rem);
+	if (amt > rem)
+	    nm256_writeBuffer8 (card, buffer + rem, 1, card->abuf1,
+				  amt - rem);
+    } 
+    else
+	nm256_writeBuffer8 (card, buffer, 1,
+			      card->abuf1 + card->curPlayPos,
+			      amt);
+
+    /*
+     * Setup the start-n-stop-n-limit registers, and start that engine
+     * goin'. 
+     *
+     * Normally we just let it wrap around to avoid the click-click
+     * action scene.
+     */
+    if (! card->playing) {
+	/* The PBUFFER_END register in this case points to one sample
+	   before the end of the buffer. */
+	int w = (card->dev_for_play == card->dev[0] ? 0 : 1);
+	int sampsize = (card->sinfo[w].bits == 16 ? 2 : 1);
+
+	if (card->sinfo[w].stereo)
+	    sampsize *= 2;
+
+	/* Need to set the not-normally-changing-registers up. */
+	nm256_writePort32 (card, 2, NM_PBUFFER_START,
+			     card->abuf1 + card->curPlayPos);
+	nm256_writePort32 (card, 2, NM_PBUFFER_END,
+			     card->abuf1 + ringsize - sampsize);
+	nm256_writePort32 (card, 2, NM_PBUFFER_CURRP,
+			     card->abuf1 + card->curPlayPos);
+    }
+    endstop = (card->curPlayPos + amt - NM256_PLAY_WMARK_SIZE) % ringsize;
+    nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop);
+
+    if (! card->playing)
+	startPlay (card);
+
+    restore_flags (flags);
+}
+
+/*  We just got a card playback interrupt; process it.  */
+static void
+nm256_get_new_block (struct nm256_info *card)
+{
+    /* Check to see how much got played so far. */
+    u32 amt = nm256_readPort32 (card, 2, NM_PBUFFER_CURRP) - card->abuf1;
+
+    if (amt >= card->playbackBufferSize) {
+	printk (KERN_ERR "NM256: Sound playback pointer invalid!\n");
+	amt = 0;
+    }
+
+    if (amt < card->curPlayPos)
+	amt = (card->playbackBufferSize - card->curPlayPos) + amt;
+    else
+	amt -= card->curPlayPos;
+
+    if (card->requested_amt > (amt + NM256_PLAY_WMARK_SIZE)) {
+	u32 endstop =
+	    card->curPlayPos + card->requested_amt - NM256_PLAY_WMARK_SIZE;
+	nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop);
+    } 
+    else {
+	card->curPlayPos += card->requested_amt;
+	/* Get a new block to write.  This will eventually invoke
+	   nm256_write_block () or stopPlay ().  */
+	DMAbuf_outputintr (card->dev_for_play, 1);
+    }
+}
+
+/* Ultra cheez-whiz.  But I'm too lazy to grep headers. */
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+
+/* 
+ * Read the last-recorded block from the ring buffer, copy it into the
+ * saved buffer pointer, and invoke DMAuf_inputintr() with the recording
+ * device. 
+ */
+
+static void
+nm256_read_block (struct nm256_info *card)
+{
+    /* Grab the current position of the recording pointer. */
+    u32 currptr = nm256_readPort32 (card, 2, NM_RBUFFER_CURRP) - card->abuf2;
+    u32 amtToRead = card->requestedRecAmt;
+    u32 ringsize = card->recordBufferSize;
+
+    if (currptr >= card->recordBufferSize) {
+	printk (KERN_ERR "NM256: Sound buffer record pointer invalid!\n");
+        currptr = 0;
+    }
+
+    /*
+     * This test is probably redundant; we shouldn't be here unless
+     * it's true.
+     */
+    if (card->recording) {
+	/* If we wrapped around, copy everything from the start of our
+	   recording buffer to the end of the buffer. */
+	if (currptr < card->curRecPos) {
+	    u32 amt = MIN (ringsize - card->curRecPos, amtToRead);
+
+	    nm256_readBuffer8 (card, card->recBuf, 1,
+				 card->abuf2 + card->curRecPos,
+				 amt);
+	    amtToRead -= amt;
+	    card->curRecPos += amt;
+	    card->recBuf += amt;
+	    if (card->curRecPos == ringsize)
+		card->curRecPos = 0;
+	}
+
+	if ((card->curRecPos < currptr) && (amtToRead > 0)) {
+	    u32 amt = MIN (currptr - card->curRecPos, amtToRead);
+	    nm256_readBuffer8 (card, card->recBuf, 1,
+				 card->abuf2 + card->curRecPos, amt);
+	    card->curRecPos = ((card->curRecPos + amt) % ringsize);
+	}
+	card->recBuf = NULL;
+	card->requestedRecAmt = 0;
+	DMAbuf_inputintr (card->dev_for_record);
+    }
+}
+#undef MIN
+
+/* 
+ * Initialize the hardware. 
+ */
+static void
+nm256_initHw (struct nm256_info *card)
+{
+    /* Reset everything. */
+    nm256_writePort8 (card, 2, 0x0, 0x11);
+    nm256_writePort16 (card, 2, 0x214, 0);
+
+    stopRecord (card);
+    stopPlay (card);
+}
+
+/* 
+ * Handle a potential interrupt for the device referred to by DEV_ID. 
+ *
+ * I don't like the cut-n-paste job here either between the two routines,
+ * but there are sufficient differences between the two interrupt handlers
+ * that parameterizing it isn't all that great either.  (Could use a macro,
+ * I suppose...yucky bleah.)
+ */
+
+static void
+nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
+{
+    struct nm256_info *card = (struct nm256_info *)dev_id;
+    u16 status;
+    static int badintrcount = 0;
+
+    if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) {
+	printk (KERN_ERR "NM256: Bad card pointer\n");
+	return;
+    }
+
+    status = nm256_readPort16 (card, 2, NM_INT_REG);
+
+    /* Not ours. */
+    if (status == 0) {
+	if (badintrcount++ > 1000) {
+	    /*
+	     * I'm not sure if the best thing is to stop the card from
+	     * playing or just release the interrupt (after all, we're in
+	     * a bad situation, so doing fancy stuff may not be such a good
+	     * idea).
+	     *
+	     * I worry about the card engine continuing to play noise
+	     * over and over, however--that could become a very
+	     * obnoxious problem.  And we know that when this usually
+	     * happens things are fairly safe, it just means the user's
+	     * inserted a PCMCIA card and someone's spamming us with IRQ 9s.
+	     */
+
+	    if (card->playing)
+		stopPlay (card);
+	    if (card->recording)
+		stopRecord (card);
+	    badintrcount = 0;
+	}
+	return;
+    }
+
+    badintrcount = 0;
+
+    /* Rather boring; check for individual interrupts and process them. */
+
+    if (status & NM_PLAYBACK_INT) {
+	status &= ~NM_PLAYBACK_INT;
+	NM_ACK_INT (card, NM_PLAYBACK_INT);
+
+	if (card->playing)
+	    nm256_get_new_block (card);
+    }
+
+    if (status & NM_RECORD_INT) {
+	status &= ~NM_RECORD_INT;
+	NM_ACK_INT (card, NM_RECORD_INT);
+
+	if (card->recording)
+	    nm256_read_block (card);
+    }
+
+    if (status & NM_MISC_INT_1) {
+	u8 cbyte;
+
+	status &= ~NM_MISC_INT_1;
+	printk (KERN_ERR "NM256: Got misc interrupt #1\n");
+	NM_ACK_INT (card, NM_MISC_INT_1);
+	nm256_writePort16 (card, 2, NM_INT_REG, 0x8000);
+	cbyte = nm256_readPort8 (card, 2, 0x400);
+	nm256_writePort8 (card, 2, 0x400, cbyte | 2);
+    }
+
+    if (status & NM_MISC_INT_2) {
+	u8 cbyte;
+
+	status &= ~NM_MISC_INT_2;
+	printk (KERN_ERR "NM256: Got misc interrupt #2\n");
+	NM_ACK_INT (card, NM_MISC_INT_2);
+	cbyte = nm256_readPort8 (card, 2, 0x400);
+	nm256_writePort8 (card, 2, 0x400, cbyte & ~2);
+    }
+
+    /* Unknown interrupt. */
+    if (status) {
+	printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n",
+		status);
+	/* Pray. */
+	NM_ACK_INT (card, status);
+    }
+}
+
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ * This handler is for the 256ZX, and is very similar to the non-ZX
+ * routine.
+ */
+
+static void
+nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy)
+{
+    struct nm256_info *card = (struct nm256_info *)dev_id;
+    u32 status;
+    static int badintrcount = 0;
+
+    if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) {
+	printk (KERN_ERR "NM256: Bad card pointer\n");
+	return;
+    }
+
+    status = nm256_readPort32 (card, 2, NM_INT_REG);
+
+    /* Not ours. */
+    if (status == 0) {
+	if (badintrcount++ > 1000) {
+	    printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n");
+	    /*
+	     * I'm not sure if the best thing is to stop the card from
+	     * playing or just release the interrupt (after all, we're in
+	     * a bad situation, so doing fancy stuff may not be such a good
+	     * idea).
+	     *
+	     * I worry about the card engine continuing to play noise
+	     * over and over, however--that could become a very
+	     * obnoxious problem.  And we know that when this usually
+	     * happens things are fairly safe, it just means the user's
+	     * inserted a PCMCIA card and someone's spamming us with 
+	     * IRQ 9s.
+	     */
+
+	    if (card->playing)
+		stopPlay (card);
+	    if (card->recording)
+		stopRecord (card);
+	    badintrcount = 0;
+	}
+	return;
+    }
+
+    badintrcount = 0;
+
+    /* Rather boring; check for individual interrupts and process them. */
+
+    if (status & NM2_PLAYBACK_INT) {
+	status &= ~NM2_PLAYBACK_INT;
+	NM2_ACK_INT (card, NM2_PLAYBACK_INT);
+
+	if (card->playing)
+	    nm256_get_new_block (card);
+    }
+
+    if (status & NM2_RECORD_INT) {
+	status &= ~NM2_RECORD_INT;
+	NM2_ACK_INT (card, NM2_RECORD_INT);
+
+	if (card->recording)
+	    nm256_read_block (card);
+    }
+
+    if (status & NM2_MISC_INT_1) {
+	u8 cbyte;
+
+	status &= ~NM2_MISC_INT_1;
+	printk (KERN_ERR "NM256: Got misc interrupt #1\n");
+	NM2_ACK_INT (card, NM2_MISC_INT_1);
+	cbyte = nm256_readPort8 (card, 2, 0x400);
+	nm256_writePort8 (card, 2, 0x400, cbyte | 2);
+    }
+
+    if (status & NM2_MISC_INT_2) {
+	u8 cbyte;
+
+	status &= ~NM2_MISC_INT_2;
+	printk (KERN_ERR "NM256: Got misc interrupt #2\n");
+	NM2_ACK_INT (card, NM2_MISC_INT_2);
+	cbyte = nm256_readPort8 (card, 2, 0x400);
+	nm256_writePort8 (card, 2, 0x400, cbyte & ~2);
+    }
+
+    /* Unknown interrupt. */
+    if (status) {
+	printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n",
+		status);
+	/* Pray. */
+	NM2_ACK_INT (card, status);
+    }
+}
+
+/* 
+ * Request our interrupt.
+ */
+static int
+nm256_grabInterrupt (struct nm256_info *card)
+{
+    if (card->has_irq++ == 0) {
+	if (request_irq (card->irq, card->introutine, SA_SHIRQ,
+			 "NM256_audio", card) < 0) {
+	    printk (KERN_ERR "NM256: can't obtain IRQ %d\n", card->irq);
+	    return -1;
+	}
+    }
+    return 0;
+}
+
+/* 
+ * Release our interrupt. 
+ */
+static int
+nm256_releaseInterrupt (struct nm256_info *card)
+{
+    if (card->has_irq <= 0) {
+	printk (KERN_ERR "nm256: too many calls to releaseInterrupt\n");
+	return -1;
+    }
+    card->has_irq--;
+    if (card->has_irq == 0) {
+	free_irq (card->irq, card);
+    }
+    return 0;
+}
+
+/*
+ * Waits for the mixer to become ready to be written; returns a zero value
+ * if it timed out.
+ */
+
+static int
+nm256_isReady (struct ac97_hwint *dev)
+{
+    struct nm256_info *card = (struct nm256_info *)dev->driver_private;
+    int t2 = 10;
+    u32 testaddr;
+    u16 testb;
+    int done = 0;
+
+    if (card->magsig != NM_MAGIC_SIG) {
+	printk (KERN_ERR "NM256: Bad magic signature in isReady!\n");
+	return 0;
+    }
+
+    testaddr = card->mixer_status_offset;
+    testb = card->mixer_status_mask;
+
+    /* 
+     * Loop around waiting for the mixer to become ready. 
+     */
+    while (! done && t2-- > 0) {
+	if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0)
+	    done = 1;
+	else
+	    udelay (100);
+    }
+    return done;
+}
+
+/*
+ * Return the contents of the AC97 mixer register REG.  Returns a positive
+ * value if successful, or a negative error code.
+ */
+static int
+nm256_readAC97Reg (struct ac97_hwint *dev, u8 reg)
+{
+    struct nm256_info *card = (struct nm256_info *)dev->driver_private;
+
+    if (card->magsig != NM_MAGIC_SIG) {
+	printk (KERN_ERR "NM256: Bad magic signature in readAC97Reg!\n");
+	return -EINVAL;
+    }
+
+    if (reg < 128) {
+	int res;
+
+	nm256_isReady (dev);
+	res = nm256_readPort16 (card, 2, card->mixer + reg);
+	/* Magic delay.  Bleah yucky.  */
+        udelay (1000);
+	return res;
+    }
+    else
+	return -EINVAL;
+}
+
+/* 
+ * Writes VALUE to AC97 mixer register REG.  Returns 0 if successful, or
+ * a negative error code. 
+ */
+static int
+nm256_writeAC97Reg (struct ac97_hwint *dev, u8 reg, u16 value)
+{
+    unsigned long flags;
+    int tries = 2;
+    int done = 0;
+    u32 base;
+
+    struct nm256_info *card = (struct nm256_info *)dev->driver_private;
+
+    if (card->magsig != NM_MAGIC_SIG) {
+	printk (KERN_ERR "NM256: Bad magic signature in writeAC97Reg!\n");
+	return -EINVAL;
+    }
+
+    base = card->mixer;
+
+    save_flags (flags);
+    cli ();
+
+    nm256_isReady (dev);
+
+    /* Wait for the write to take, too. */
+    while ((tries-- > 0) && !done) {
+	nm256_writePort16 (card, 2, base + reg, value);
+	if (nm256_isReady (dev)) {
+	    done = 1;
+	    break;
+	}
+
+    }
+
+    restore_flags (flags);
+    udelay (1000);
+
+    return ! done;
+}
+
+/* 
+ * Initial register values to be written to the AC97 mixer.
+ * While most of these are identical to the reset values, we do this
+ * so that we have most of the register contents cached--this avoids
+ * reading from the mixer directly (which seems to be problematic,
+ * probably due to ignorance).
+ */
+struct initialValues 
+{
+    unsigned short port;
+    unsigned short value;
+};
+
+static struct initialValues nm256_ac97_initial_values[] = 
+{
+    { AC97_MASTER_VOL_STEREO, 0x8000 },
+    { AC97_HEADPHONE_VOL,     0x8000 },
+    { AC97_MASTER_VOL_MONO,   0x0000 },
+    { AC97_PCBEEP_VOL,        0x0000 },
+    { AC97_PHONE_VOL,         0x0008 },
+    { AC97_MIC_VOL,           0x8000 },
+    { AC97_LINEIN_VOL,        0x8808 },
+    { AC97_CD_VOL,            0x8808 },
+    { AC97_VIDEO_VOL,         0x8808 },
+    { AC97_AUX_VOL,           0x8808 },
+    { AC97_PCMOUT_VOL,        0x0808 },
+    { AC97_RECORD_SELECT,     0x0000 },
+    { AC97_RECORD_GAIN,       0x0B0B },
+    { AC97_GENERAL_PURPOSE,   0x0000 },
+    { 0xffff, 0xffff }
+};
+
+/* Initialize the AC97 into a known state.  */
+static int
+nm256_resetAC97 (struct ac97_hwint *dev)
+{
+    struct nm256_info *card = (struct nm256_info *)dev->driver_private;
+    int x;
+
+    if (card->magsig != NM_MAGIC_SIG) {
+	printk (KERN_ERR "NM256: Bad magic signature in resetAC97!\n");
+	return -EINVAL;
+    }
+
+    /* Reset the mixer.  'Tis magic!  */
+    nm256_writePort8 (card, 2, 0x6c0, 1);
+    nm256_writePort8 (card, 2, 0x6cc, 0x87);
+    nm256_writePort8 (card, 2, 0x6cc, 0x80);
+    nm256_writePort8 (card, 2, 0x6cc, 0x0);
+
+    if (! card->mixer_values_init) {
+	for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) {
+	    ac97_put_register (dev,
+			       nm256_ac97_initial_values[x].port,
+			       nm256_ac97_initial_values[x].value);
+	    card->mixer_values_init = 1;
+	}
+    }
+
+    return 0;
+}
+
+/*
+ * We don't do anything particularly special here; it just passes the
+ * mixer ioctl to the AC97 driver.
+ */
+static int
+nm256_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+    struct nm256_info *card = nm256_find_card_for_mixer (dev);
+    if (card != NULL)
+	return ac97_mixer_ioctl (&(card->mdev), cmd, arg);
+    else
+	return -ENODEV;
+}
+
+static struct mixer_operations nm256_mixer_operations = {
+    owner:	THIS_MODULE,
+    id:		"NeoMagic",
+    name:	"NM256AC97Mixer",
+    ioctl:	nm256_default_mixer_ioctl
+};
+
+/*
+ * Default settings for the OSS mixer.  These are set last, after the
+ * mixer is initialized.
+ *
+ * I "love" C sometimes.  Got braces?
+ */
+static struct ac97_mixer_value_list mixer_defaults[] = {
+    { SOUND_MIXER_VOLUME,  { { 85, 85 } } },
+    { SOUND_MIXER_SPEAKER, { { 100 } } },
+    { SOUND_MIXER_PCM,     { { 65, 65 } } },
+    { SOUND_MIXER_CD,      { { 65, 65 } } },
+    { -1,                  {  { 0,  0 } } }
+};
+
+
+/* Installs the AC97 mixer into CARD.  */
+static int __init
+nm256_install_mixer (struct nm256_info *card)
+{
+    int mixer;
+
+    card->mdev.reset_device = nm256_resetAC97;
+    card->mdev.read_reg = nm256_readAC97Reg;
+    card->mdev.write_reg = nm256_writeAC97Reg;
+    card->mdev.driver_private = (void *)card;
+
+    if (ac97_init (&(card->mdev)))
+	return -1;
+
+    mixer = sound_alloc_mixerdev();
+    if (num_mixers >= MAX_MIXER_DEV) {
+	printk ("NM256 mixer: Unable to alloc mixerdev\n");
+	return -1;
+    }
+
+    mixer_devs[mixer] = &nm256_mixer_operations;
+    card->mixer_oss_dev = mixer;
+
+    /* Some reasonable default values.  */
+    ac97_set_values (&(card->mdev), mixer_defaults);
+
+    printk(KERN_INFO "Initialized AC97 mixer\n");
+    return 0;
+}
+
+/* Perform a full reset on the hardware; this is invoked when an APM
+   resume event occurs.  */
+static void
+nm256_full_reset (struct nm256_info *card)
+{
+    nm256_initHw (card);
+    ac97_reset (&(card->mdev));
+}
+
+/* 
+ * See if the signature left by the NM256 BIOS is intact; if so, we use
+ * the associated address as the end of our audio buffer in the video
+ * RAM.
+ */
+
+static void __init
+nm256_peek_for_sig (struct nm256_info *card)
+{
+    u32 port1offset 
+	= card->port[0].physaddr + card->port[0].end_offset - 0x0400;
+    /* The signature is located 1K below the end of video RAM.  */
+    char *temp = ioremap_nocache (port1offset, 16);
+    /* Default buffer end is 5120 bytes below the top of RAM.  */
+    u32 default_value = card->port[0].end_offset - 0x1400;
+    u32 sig;
+
+    /* Install the default value first, so we don't have to repeatedly
+       do it if there is a problem.  */
+    card->port[0].end_offset = default_value;
+
+    if (temp == NULL) {
+	printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n");
+	return;
+    }
+    sig = readl (temp);
+    if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
+	u32 pointer = readl (temp + 4);
+
+	/*
+	 * If it's obviously invalid, don't use it (the port already has a
+	 * suitable default value set).
+	 */
+	if (pointer != 0xffffffff)
+	    card->port[0].end_offset = pointer;
+
+	printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n",
+		pointer);
+    }
+
+    iounmap (temp);
+}
+
+/* 
+ * Install a driver for the PCI device referenced by PCIDEV.
+ * VERSTR is a human-readable version string.
+ */
+
+static int __init
+nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr)
+{
+    struct nm256_info *card;
+    struct pm_dev *pmdev;
+    int x;
+
+    if (pci_enable_device(pcidev))
+	    return 0;
+
+    card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL);
+    if (card == NULL) {
+	printk (KERN_ERR "NM256: out of memory!\n");
+	return 0;
+    }
+
+    card->magsig = NM_MAGIC_SIG;
+    card->playing  = 0;
+    card->recording = 0;
+    card->rev = rev;
+
+    /* Init the memory port info.  */
+    for (x = 0; x < 2; x++) {
+	card->port[x].physaddr = pci_resource_start (pcidev, x);
+	card->port[x].ptr = NULL;
+	card->port[x].start_offset = 0;
+	card->port[x].end_offset = 0;
+    }
+
+    /* Port 2 is easy.  */
+    card->port[1].start_offset = 0;
+    card->port[1].end_offset = NM_PORT2_SIZE;
+
+    /* Yuck.  But we have to map in port 2 so we can check how much RAM the
+       card has.  */
+    if (nm256_remap_ports (card)) {
+	kfree (card);
+	return 0;
+    }
+
+    /* 
+     * The NM256 has two memory ports.  The first port is nothing
+     * more than a chunk of video RAM, which is used as the I/O ring
+     * buffer.  The second port has the actual juicy stuff (like the
+     * mixer and the playback engine control registers).
+     */
+
+    if (card->rev == REV_NM256AV) {
+	/* Ok, try to see if this is a non-AC97 version of the hardware. */
+	int pval = nm256_readPort16 (card, 2, NM_MIXER_PRESENCE);
+	if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
+	    if (! force_load) {
+		printk (KERN_ERR "NM256: This doesn't look to me like the AC97-compatible version.\n");
+		printk (KERN_ERR "       You can force the driver to load by passing in the module\n");
+		printk (KERN_ERR "       parameter:\n");
+		printk (KERN_ERR "              force_ac97 = 1\n");
+		printk (KERN_ERR "\n");
+		printk (KERN_ERR "       More likely, you should be using the appropriate SB-16 or\n");
+		printk (KERN_ERR "       CS4232 driver instead.  (If your BIOS has settings for\n");
+		printk (KERN_ERR "       IRQ and/or DMA for the sound card, this is *not* the correct\n");
+		printk (KERN_ERR "       driver to use.)\n");
+		nm256_release_ports (card);
+		kfree (card);
+		return 0;
+	    }
+	    else {
+		printk (KERN_INFO "NM256: Forcing driver load as per user request.\n");
+	    }
+	}
+	else {
+	 /*   printk (KERN_INFO "NM256: Congratulations. You're not running Eunice.\n")*/;
+	}
+	card->port[0].end_offset = 2560 * 1024;
+	card->introutine = nm256_interrupt;
+	card->mixer_status_offset = NM_MIXER_STATUS_OFFSET;
+	card->mixer_status_mask = NM_MIXER_READY_MASK;
+    } 
+    else {
+	/* Not sure if there is any relevant detect for the ZX or not.  */
+	if (nm256_readPort8 (card, 2, 0xa0b) != 0)
+	    card->port[0].end_offset = 6144 * 1024;
+	else
+	    card->port[0].end_offset = 4096 * 1024;
+
+	card->introutine = nm256_interrupt_zx;
+	card->mixer_status_offset = NM2_MIXER_STATUS_OFFSET;
+	card->mixer_status_mask = NM2_MIXER_READY_MASK;
+    }
+
+    if (buffertop >= 98304 && buffertop < card->port[0].end_offset)
+	card->port[0].end_offset = buffertop;
+    else
+	nm256_peek_for_sig (card);
+
+    card->port[0].start_offset = card->port[0].end_offset - 98304;
+
+    printk (KERN_INFO "NM256: Mapping port 1 from 0x%x - 0x%x\n",
+	    card->port[0].start_offset, card->port[0].end_offset);
+
+    if (nm256_remap_ports (card)) {
+	kfree (card);
+	return 0;
+    }
+
+    /* See if we can get the interrupt. */
+
+    card->irq = pcidev->irq;
+    card->has_irq = 0;
+
+    if (nm256_grabInterrupt (card) != 0) {
+	nm256_release_ports (card);
+	kfree (card);
+	return 0;
+    }
+
+    nm256_releaseInterrupt (card);
+
+    /*
+     *	Init the board.
+     */
+
+    card->playbackBufferSize = 16384;
+    card->recordBufferSize = 16384;
+
+    card->coeffBuf = card->port[0].end_offset - NM_MAX_COEFFICIENT;
+    card->abuf2 = card->coeffBuf - card->recordBufferSize;
+    card->abuf1 = card->abuf2 - card->playbackBufferSize;
+    card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4);
+
+    /* Fixed setting. */
+    card->mixer = NM_MIXER_OFFSET;
+    card->mixer_values_init = 0;
+
+    card->is_open_play = 0;
+    card->is_open_record = 0;
+
+    card->coeffsCurrent = 0;
+
+    card->opencnt[0] = 0; card->opencnt[1] = 0;
+
+    /* Reasonable default settings, but largely unnecessary. */
+    for (x = 0; x < 2; x++) {
+	card->sinfo[x].bits = 8;
+	card->sinfo[x].stereo = 0;
+	card->sinfo[x].samplerate = 8000;
+    }
+
+    nm256_initHw (card);
+
+    for (x = 0; x < 2; x++) {
+	if ((card->dev[x] =
+	     sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+				    "NM256", &nm256_audio_driver,
+				    sizeof(struct audio_driver),
+				    DMA_NODMA, AFMT_U8 | AFMT_S16_LE,
+				    NULL, -1, -1)) >= 0) {
+	    /* 1K minimum buffer size. */
+	    audio_devs[card->dev[x]]->min_fragment = 10;
+	    /* Maximum of 8K buffer size. */
+	    audio_devs[card->dev[x]]->max_fragment = 13;
+	}
+	else {
+	    printk(KERN_ERR "NM256: Too many PCM devices available\n");
+	    nm256_release_ports (card);
+	    kfree (card);
+	    return 0;
+	}
+    }
+
+    pci_set_drvdata(pcidev,card);
+
+    /* Insert the card in the list.  */
+    card->next_card = nmcard_list;
+    nmcard_list = card;
+
+    printk(KERN_INFO "Initialized NeoMagic %s audio in PCI native mode\n",
+	   verstr);
+
+    /* 
+     * And our mixer.  (We should allow support for other mixers, maybe.)
+     */
+
+    nm256_install_mixer (card);
+
+    pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), handle_pm_event);
+    if (pmdev)
+        pmdev->data = card;
+
+    return 1;
+}
+
+
+/*
+ * PM event handler, so the card is properly reinitialized after a power
+ * event.
+ */
+static int
+handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+    struct nm256_info *crd = (struct nm256_info*) dev->data;
+    if (crd) {
+        switch (rqst) {
+	case PM_SUSPEND:
+	    break;
+	case PM_RESUME:
+            {
+                int playing = crd->playing;
+                nm256_full_reset (crd);
+                /*
+                 * A little ugly, but that's ok; pretend the
+                 * block we were playing is done. 
+                 */
+                if (playing)
+                    DMAbuf_outputintr (crd->dev_for_play, 1);
+            }
+	    break;
+	}
+    }
+    return 0;
+}
+
+static int __devinit
+nm256_probe(struct pci_dev *pcidev,const struct pci_device_id *pciid)
+{
+    if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO)
+	return nm256_install(pcidev, REV_NM256AV, "256AV");
+    if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO)
+	return nm256_install(pcidev, REV_NM256ZX, "256ZX");
+    return -1; /* should not come here ... */
+}
+
+static void __devinit
+nm256_remove(struct pci_dev *pcidev) {
+    struct nm256_info *xcard = pci_get_drvdata(pcidev);
+    struct nm256_info *card,*next_card = NULL;
+
+    for (card = nmcard_list; card != NULL; card = next_card) {
+	next_card = card->next_card;
+	if (card == xcard) {
+	    stopPlay (card);
+	    stopRecord (card);
+	    if (card->has_irq)
+		free_irq (card->irq, card);
+	    nm256_release_ports (card);
+	    sound_unload_mixerdev (card->mixer_oss_dev);
+	    sound_unload_audiodev (card->dev[0]);
+	    sound_unload_audiodev (card->dev[1]);
+	    kfree (card);
+	    break;
+	}
+    }
+    if (nmcard_list == card)
+    	nmcard_list = next_card;
+}
+
+/*
+ * Open the device
+ *
+ * DEV  - device
+ * MODE - mode to open device (logical OR of OPEN_READ and OPEN_WRITE)
+ *
+ * Called when opening the DMAbuf               (dmabuf.c:259)
+ */
+static int
+nm256_audio_open(int dev, int mode)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+    int w;
+	
+    if (card == NULL)
+	return -ENODEV;
+
+    if (card->dev[0] == dev)
+	w = 0;
+    else if (card->dev[1] == dev)
+	w = 1;
+    else
+	return -ENODEV;
+
+    if (card->opencnt[w] > 0)
+	return -EBUSY;
+
+    /* No bits set? Huh? */
+    if (! ((mode & OPEN_READ) || (mode & OPEN_WRITE)))
+	return -EIO;
+
+    /*
+     * If it's open for both read and write, and the card's currently
+     * being read or written to, then do the opposite of what has
+     * already been done.  Otherwise, don't specify any mode until the
+     * user actually tries to do I/O.  (Some programs open the device
+     * for both read and write, but only actually do reading or writing.)
+     */
+
+    if ((mode & OPEN_WRITE) && (mode & OPEN_READ)) {
+	if (card->is_open_play)
+	    mode = OPEN_WRITE;
+	else if (card->is_open_record)
+	    mode = OPEN_READ;
+	else mode = 0;
+    }
+	
+    if (mode & OPEN_WRITE) {
+	if (card->is_open_play == 0) {
+	    card->dev_for_play = dev;
+	    card->is_open_play = 1;
+	}
+	else
+	    return -EBUSY;
+    }
+
+    if (mode & OPEN_READ) {
+	if (card->is_open_record == 0) {
+	    card->dev_for_record = dev;
+	    card->is_open_record = 1;
+	}
+	else
+	    return -EBUSY;
+    }
+
+    card->opencnt[w]++;
+    return 0;
+}
+
+/*
+ * Close the device
+ *
+ * DEV  - device
+ *
+ * Called when closing the DMAbuf               (dmabuf.c:477)
+ *      after halt_xfer
+ */
+static void
+nm256_audio_close(int dev)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+	
+    if (card != NULL) {
+	int w;
+
+	if (card->dev[0] == dev)
+	    w = 0;
+	else if (card->dev[1] == dev)
+	    w = 1;
+	else
+	    return;
+
+	card->opencnt[w]--;
+	if (card->opencnt[w] <= 0) {
+	    card->opencnt[w] = 0;
+
+	    if (card->dev_for_play == dev) {
+		stopPlay (card);
+		card->is_open_play = 0;
+		card->dev_for_play = -1;
+	    }
+
+	    if (card->dev_for_record == dev) {
+		stopRecord (card);
+		card->is_open_record = 0;
+		card->dev_for_record = -1;
+	    }
+	}
+    }
+}
+
+/* Standard ioctl handler. */
+static int
+nm256_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+    int ret;
+    u32 oldinfo;
+    int w;
+
+    struct nm256_info *card = nm256_find_card (dev);
+
+    if (card == NULL)
+	return -ENODEV;
+
+    if (dev == card->dev[0])
+	w = 0;
+    else
+	w = 1;
+
+    /* 
+     * The code here is messy.  There are probably better ways to do
+     * it.  (It should be possible to handle it the same way the AC97 mixer 
+     * is done.)
+     */
+    switch (cmd)
+	{
+	case SOUND_PCM_WRITE_RATE:
+	    if (get_user(ret, (int *) arg))
+		return -EFAULT;
+
+	    if (ret != 0) {
+		oldinfo = card->sinfo[w].samplerate;
+		card->sinfo[w].samplerate = ret;
+		ret = nm256_setInfo(dev, card);
+		if (ret != 0)
+		    card->sinfo[w].samplerate = oldinfo;
+	    }
+	    if (ret == 0)
+		ret = card->sinfo[w].samplerate;
+	    break;
+
+	case SOUND_PCM_READ_RATE:
+	    ret = card->sinfo[w].samplerate;
+	    break;
+
+	case SNDCTL_DSP_STEREO:
+	    if (get_user(ret, (int *) arg))
+		return -EFAULT;
+
+	    card->sinfo[w].stereo = ret ? 1 : 0;
+	    ret = nm256_setInfo (dev, card);
+	    if (ret == 0)
+		ret = card->sinfo[w].stereo;
+
+	    break;
+
+	case SOUND_PCM_WRITE_CHANNELS:
+	    if (get_user(ret, (int *) arg))
+		return -EFAULT;
+
+	    if (ret < 1 || ret > 3)
+		ret = card->sinfo[w].stereo + 1;
+	    else {
+		card->sinfo[w].stereo = ret - 1;
+		ret = nm256_setInfo (dev, card);
+		if (ret == 0)
+		    ret = card->sinfo[w].stereo + 1;
+	    }
+	    break;
+
+	case SOUND_PCM_READ_CHANNELS:
+	    ret = card->sinfo[w].stereo + 1;
+	    break;
+
+	case SNDCTL_DSP_SETFMT:
+	    if (get_user(ret, (int *) arg))
+		return -EFAULT;
+
+	    if (ret != 0) {
+		oldinfo = card->sinfo[w].bits;
+		card->sinfo[w].bits = ret;
+		ret = nm256_setInfo (dev, card);
+		if (ret != 0)
+		    card->sinfo[w].bits = oldinfo;
+	    }
+	    if (ret == 0)
+		ret = card->sinfo[w].bits;
+	    break;
+
+	case SOUND_PCM_READ_BITS:
+	    ret = card->sinfo[w].bits;
+	    break;
+
+	default:
+	    return -EINVAL;
+	}
+    return put_user(ret, (int *) arg);
+}
+
+/*
+ * Given the sound device DEV and an associated physical buffer PHYSBUF, 
+ * return a pointer to the actual buffer in kernel space. 
+ *
+ * This routine should exist as part of the soundcore routines.
+ */
+
+static char *
+nm256_getDMAbuffer (int dev, unsigned long physbuf)
+{
+    struct audio_operations *adev = audio_devs[dev];
+    struct dma_buffparms *dmap = adev->dmap_out;
+    char *dma_start =
+	(char *)(physbuf - (unsigned long)dmap->raw_buf_phys 
+		 + (unsigned long)dmap->raw_buf);
+
+    return dma_start;
+}
+
+
+/*
+ * Output a block to sound device
+ *
+ * dev          - device number
+ * buf          - physical address of buffer
+ * total_count  - total byte count in buffer
+ * intrflag     - set if this has been called from an interrupt 
+ *				  (via DMAbuf_outputintr)
+ * restart_dma  - set if engine needs to be re-initialised
+ *
+ * Called when:
+ *  1. Starting output                                  (dmabuf.c:1327)
+ *  2.                                                  (dmabuf.c:1504)
+ *  3. A new buffer needs to be sent to the device      (dmabuf.c:1579)
+ */
+static void
+nm256_audio_output_block(int dev, unsigned long physbuf,
+				       int total_count, int intrflag)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+
+    if (card != NULL) {
+	char *dma_buf = nm256_getDMAbuffer (dev, physbuf);
+	card->is_open_play = 1;
+	card->dev_for_play = dev;
+	nm256_write_block (card, dma_buf, total_count);
+    }
+}
+
+/* Ditto, but do recording instead.  */
+static void
+nm256_audio_start_input(int dev, unsigned long physbuf, int count,
+			int intrflag)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+
+    if (card != NULL) {
+	char *dma_buf = nm256_getDMAbuffer (dev, physbuf);
+	card->is_open_record = 1;
+	card->dev_for_record = dev;
+	nm256_startRecording (card, dma_buf, count);
+    }
+}
+
+/* 
+ * Prepare for inputting samples to DEV. 
+ * Each requested buffer will be BSIZE byes long, with a total of
+ * BCOUNT buffers. 
+ */
+
+static int
+nm256_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+
+    if (card == NULL) 
+	return -ENODEV;
+
+    if (card->is_open_record && card->dev_for_record != dev)
+	return -EBUSY;
+
+    audio_devs[dev]->dmap_in->flags |= DMA_NODMA;
+    return 0;
+}
+
+/*
+ * Prepare for outputting samples to `dev'
+ *
+ * Each buffer that will be passed will be `bsize' bytes long,
+ * with a total of `bcount' buffers.
+ *
+ * Called when:
+ *  1. A trigger enables audio output                   (dmabuf.c:978)
+ *  2. We get a write buffer without dma_mode setup     (dmabuf.c:1152)
+ *  3. We restart a transfer                            (dmabuf.c:1324)
+ */
+
+static int
+nm256_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+
+    if (card == NULL)
+	return -ENODEV;
+
+    if (card->is_open_play && card->dev_for_play != dev)
+	return -EBUSY;
+
+    audio_devs[dev]->dmap_out->flags |= DMA_NODMA;
+    return 0;
+}
+
+/* Stop the current operations associated with DEV.  */
+static void
+nm256_audio_reset(int dev)
+{
+    struct nm256_info *card = nm256_find_card (dev);
+
+    if (card != NULL) {
+	if (card->dev_for_play == dev)
+	    stopPlay (card);
+	if (card->dev_for_record == dev)
+	    stopRecord (card);
+    }
+}
+
+static int
+nm256_audio_local_qlen(int dev)
+{
+    return 0;
+}
+
+static struct audio_driver nm256_audio_driver =
+{
+    owner:		THIS_MODULE,
+    open:		nm256_audio_open,
+    close:		nm256_audio_close,
+    output_block:	nm256_audio_output_block,
+    start_input:	nm256_audio_start_input,
+    ioctl:		nm256_audio_ioctl,
+    prepare_for_input:	nm256_audio_prepare_for_input,
+    prepare_for_output:nm256_audio_prepare_for_output,
+    halt_io:		nm256_audio_reset,
+    local_qlen:		nm256_audio_local_qlen,
+};
+
+static struct pci_device_id nm256_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO,
+	PCI_ANY_ID, PCI_ANY_ID, 0, 0},
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO,
+	PCI_ANY_ID, PCI_ANY_ID, 0, 0},
+	{0,}
+};
+MODULE_DEVICE_TABLE(pci, nm256_pci_tbl);
+MODULE_LICENSE("GPL");
+
+
+struct pci_driver nm256_pci_driver = {
+	name:"nm256_audio",
+	id_table:nm256_pci_tbl,
+	probe:nm256_probe,
+	remove:nm256_remove,
+};
+
+MODULE_PARM (usecache, "i");
+MODULE_PARM (buffertop, "i");
+MODULE_PARM (nm256_debug, "i");
+MODULE_PARM (force_load, "i");
+
+static int __init do_init_nm256(void)
+{
+    printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.1p\n");
+    return pci_module_init(&nm256_pci_driver);
+}
+
+static void __exit cleanup_nm256 (void)
+{
+    pci_unregister_driver(&nm256_pci_driver);
+    pm_unregister_all (&handle_pm_event);
+}
+
+module_init(do_init_nm256);
+module_exit(cleanup_nm256);
+
+/*
+ * Local variables:
+ *  c-basic-offset: 4
+ * End:
+ */
diff -Nru linux/sound/oss/nm256_coeff.h linux-2.4.19-pre5-mjc/sound/oss/nm256_coeff.h
--- linux/sound/oss/nm256_coeff.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/nm256_coeff.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,4697 @@
+#ifndef NM256_COEFF_H
+#define NM256_COEFF_H
+
+#define NM_TOTAL_COEFF_COUNT 0x3158
+
+static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { 
+	0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21,
+	0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01,
+	0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06,
+	0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1,
+	0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF,
+	0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E,
+	0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC,
+	0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05,
+	0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+	0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+	0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9,
+	0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01,
+	0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84,
+	0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+	0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+	0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C,
+	0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00,
+	0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5,
+	0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33,
+	0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00,
+	0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1,
+	0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38,
+	0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA,
+	0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD,
+	0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38,
+	0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F,
+	0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05,
+	0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06,
+	0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32,
+	0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93,
+	0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC,
+	0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+	0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA,
+	0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9,
+	0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+	0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+	0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68,
+	0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32,
+	0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00,
+	0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76,
+	0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53,
+	0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04,
+	0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01,
+	0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20,
+	0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF,
+	0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43,
+	0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1,
+	0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD,
+	0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8,
+	0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5,
+	0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04,
+	0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04,
+	0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D,
+	0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE,
+	0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD,
+	0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19,
+	0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2,
+	0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD,
+	0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF,
+	0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D,
+	0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01,
+	0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70,
+	0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07,
+	0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9,
+	0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66,
+	0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF,
+	0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F,
+	0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F,
+	0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C,
+	0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00,
+	0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF,
+	0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18,
+	0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB,
+	0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC,
+	0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98,
+	0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00,
+	0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A,
+	0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06,
+	0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01,
+	0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05,
+	0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A,
+	0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+	0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76,
+	0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD,
+	0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8,
+	0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40,
+	0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+	0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+	0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85,
+	0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36,
+	0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42,
+	0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+	0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D,
+	0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF,
+	0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39,
+	0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48,
+	0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95,
+	0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01,
+	0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F,
+	0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F,
+	0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1,
+	0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD,
+	0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE,
+	0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A,
+	0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A,
+	0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03,
+	0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05,
+	0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E,
+	0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01,
+	0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC,
+	0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B,
+	0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2,
+	0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF,
+	0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48,
+	0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01,
+	0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+	0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+	0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+	0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11,
+	0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF,
+	0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B,
+	0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A,
+	0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32,
+	0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF,
+	0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C,
+	0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE,
+	0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01,
+	0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC,
+	0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1,
+	0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00,
+	0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16,
+	0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06,
+	0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04,
+	0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04,
+	0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B,
+	0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00,
+	0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3,
+	0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD,
+	0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7,
+	0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3,
+	0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+	0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+	0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9,
+	0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A,
+	0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00,
+	0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD,
+	0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF,
+	0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A,
+	0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48,
+	0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB,
+	0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01,
+	0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91,
+	0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8,
+	0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1,
+	0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF,
+	0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA,
+	0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD,
+	0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD,
+	0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE,
+	0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06,
+	0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF,
+	0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+	0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94,
+	0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC,
+	0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB,
+	0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4,
+	0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF,
+	0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A,
+	0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01,
+	0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D,
+	0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10,
+	0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68,
+	0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+	0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B,
+	0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF,
+	0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28,
+	0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25,
+	0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF,
+	0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC,
+	0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90,
+	0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE,
+	0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16,
+	0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC,
+	0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE,
+	0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00,
+	0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0,
+	0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07,
+	0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03,
+	0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91,
+	0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73,
+	0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE,
+	0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33,
+	0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5,
+	0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20,
+	0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+	0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+	0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+	0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3,
+	0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E,
+	0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00,
+	0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47,
+	0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96,
+	0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46,
+	0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1,
+	0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01,
+	0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74,
+	0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B,
+	0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2,
+	0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF,
+	0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D,
+	0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A,
+	0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE,
+	0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06,
+	0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23,
+	0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF,
+	0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81,
+	0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC,
+	0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9,
+	0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5,
+	0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2,
+	0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF,
+	0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED,
+	0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01,
+	0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+	0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+	0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF,
+	0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25,
+	0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF,
+	0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9,
+	0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20,
+	0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF,
+	0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2,
+	0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B,
+	0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB,
+	0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC,
+	0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA,
+	0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56,
+	0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07,
+	0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+	0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02,
+	0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB,
+	0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E,
+	0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01,
+	0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4,
+	0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85,
+	0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+	0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+	0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+	0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02,
+	0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41,
+	0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00,
+	0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07,
+	0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF,
+	0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA,
+	0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44,
+	0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01,
+	0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5,
+	0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00,
+	0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A,
+	0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3,
+	0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00,
+	0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A,
+	0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C,
+	0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00,
+	0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06,
+	0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D,
+	0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+	0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0,
+	0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC,
+	0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5,
+	0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8,
+	0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7,
+	0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00,
+	0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64,
+	0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE,
+	0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39,
+	0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A,
+	0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE,
+	0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF,
+	0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD,
+	0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF,
+	0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D,
+	0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B,
+	0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE,
+	0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54,
+	0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3,
+	0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8,
+	0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C,
+	0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC,
+	0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63,
+	0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8,
+	0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06,
+	0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+	0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00,
+	0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34,
+	0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C,
+	0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00,
+	0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3,
+	0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0,
+	0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+	0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+	0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+	0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36,
+	0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44,
+	0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF,
+	0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA,
+	0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00,
+	0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4,
+	0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42,
+	0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01,
+	0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E,
+	0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31,
+	0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4,
+	0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01,
+	0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86,
+	0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D,
+	0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01,
+	0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07,
+	0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11,
+	0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88,
+	0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC,
+	0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF,
+	0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA,
+	0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92,
+	0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA,
+	0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE,
+	0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+	0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+	0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF,
+	0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5,
+	0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF,
+	0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17,
+	0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16,
+	0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01,
+	0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C,
+	0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF,
+	0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C,
+	0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6,
+	0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC,
+	0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8,
+	0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF,
+	0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8,
+	0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06,
+	0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16,
+	0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF,
+	0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99,
+	0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7,
+	0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00,
+	0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2,
+	0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F,
+	0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00,
+	0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+	0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D,
+	0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46,
+	0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF,
+	0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57,
+	0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00,
+	0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3,
+	0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F,
+	0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC,
+	0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01,
+	0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A,
+	0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00,
+	0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF,
+	0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5,
+	0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02,
+	0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9,
+	0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8,
+	0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03,
+	0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07,
+	0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0,
+	0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+	0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0,
+	0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC,
+	0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9,
+	0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD,
+	0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66,
+	0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+	0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36,
+	0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF,
+	0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39,
+	0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24,
+	0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6,
+	0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF,
+	0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11,
+	0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9,
+	0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11,
+	0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01,
+	0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74,
+	0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF,
+	0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57,
+	0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4,
+	0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04,
+	0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC,
+	0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A,
+	0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF,
+	0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8,
+	0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06,
+	0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C,
+	0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD,
+	0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08,
+	0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A,
+	0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF,
+	0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32,
+	0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1,
+	0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70,
+	0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00,
+	0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+	0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5,
+	0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47,
+	0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E,
+	0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF,
+	0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26,
+	0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00,
+	0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6,
+	0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B,
+	0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01,
+	0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E,
+	0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0,
+	0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7,
+	0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD,
+	0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F,
+	0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66,
+	0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04,
+	0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD,
+	0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06,
+	0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A,
+	0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00,
+	0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48,
+	0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC,
+	0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93,
+	0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01,
+	0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34,
+	0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+	0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA,
+	0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF,
+	0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+	0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+	0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+	0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF,
+	0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10,
+	0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35,
+	0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C,
+	0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01,
+	0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30,
+	0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF,
+	0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38,
+	0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3,
+	0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC,
+	0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A,
+	0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B,
+	0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05,
+	0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03,
+	0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04,
+	0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF,
+	0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1,
+	0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE,
+	0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B,
+	0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1,
+	0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3,
+	0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF,
+	0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+	0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE,
+	0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48,
+	0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71,
+	0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF,
+	0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C,
+	0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00,
+	0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB,
+	0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37,
+	0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01,
+	0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2,
+	0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70,
+	0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8,
+	0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD,
+	0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9,
+	0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0,
+	0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05,
+	0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE,
+	0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06,
+	0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40,
+	0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00,
+	0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53,
+	0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC,
+	0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20,
+	0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC,
+	0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D,
+	0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6,
+	0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00,
+	0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53,
+	0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E,
+	0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66,
+	0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF,
+	0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06,
+	0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF,
+	0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08,
+	0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01,
+	0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57,
+	0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF,
+	0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32,
+	0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2,
+	0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD,
+	0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B,
+	0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17,
+	0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04,
+	0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03,
+	0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24,
+	0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8,
+	0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD,
+	0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23,
+	0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1,
+	0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8,
+	0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF,
+	0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+	0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+	0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83,
+	0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04,
+	0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C,
+	0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02,
+	0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00,
+	0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3,
+	0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33,
+	0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00,
+	0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB,
+	0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B,
+	0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA,
+	0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD,
+	0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58,
+	0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14,
+	0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06,
+	0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06,
+	0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31,
+	0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0,
+	0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC,
+	0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+	0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA,
+	0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4,
+	0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA,
+	0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00,
+	0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+	0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+	0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00,
+	0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06,
+	0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49,
+	0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04,
+	0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01,
+	0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE,
+	0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF,
+	0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46,
+	0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1,
+	0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD,
+	0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0,
+	0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92,
+	0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04,
+	0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04,
+	0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B,
+	0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA,
+	0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD,
+	0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18,
+	0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2,
+	0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE,
+	0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF,
+	0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+	0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+	0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E,
+	0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08,
+	0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4,
+	0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F,
+	0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF,
+	0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B,
+	0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E,
+	0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D,
+	0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00,
+	0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E,
+	0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C,
+	0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC,
+	0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC,
+	0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6,
+	0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00,
+	0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B,
+	0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06,
+	0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01,
+	0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05,
+	0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+	0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC,
+	0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD,
+	0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8,
+	0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A,
+	0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2,
+	0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01,
+	0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88,
+	0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37,
+	0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41,
+	0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00,
+	0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20,
+	0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF,
+	0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D,
+	0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48,
+	0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96,
+	0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01,
+	0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7,
+	0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75,
+	0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1,
+	0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD,
+	0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE,
+	0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F,
+	0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5,
+	0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03,
+	0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05,
+	0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D,
+	0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3,
+	0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC,
+	0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+	0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2,
+	0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+	0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+	0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B,
+	0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C,
+	0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B,
+	0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0,
+	0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF,
+	0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66,
+	0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29,
+	0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32,
+	0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF,
+	0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E,
+	0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4,
+	0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01,
+	0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC,
+	0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB,
+	0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00,
+	0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12,
+	0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06,
+	0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04,
+	0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04,
+	0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F,
+	0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+	0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12,
+	0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD,
+	0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7,
+	0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD,
+	0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0,
+	0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01,
+	0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+	0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+	0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00,
+	0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64,
+	0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF,
+	0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E,
+	0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47,
+	0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD,
+	0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73,
+	0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF,
+	0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1,
+	0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF,
+	0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC,
+	0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1,
+	0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD,
+	0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE,
+	0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06,
+	0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF,
+	0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+	0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E,
+	0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC,
+	0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9,
+	0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4,
+	0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+	0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+	0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+	0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C,
+	0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11,
+	0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63,
+	0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+	0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2,
+	0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF,
+	0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22,
+	0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25,
+	0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF,
+	0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20,
+	0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87,
+	0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE,
+	0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15,
+	0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC,
+	0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3,
+	0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00,
+	0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7,
+	0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07,
+	0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03,
+	0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97,
+	0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+	0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA,
+	0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02,
+	0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+	0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5,
+	0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A,
+	0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE,
+	0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01,
+	0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7,
+	0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E,
+	0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00,
+	0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4,
+	0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99,
+	0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46,
+	0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3,
+	0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01,
+	0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60,
+	0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25,
+	0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2,
+	0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF,
+	0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D,
+	0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD,
+	0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE,
+	0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06,
+	0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15,
+	0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+	0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84,
+	0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC,
+	0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7,
+	0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6,
+	0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0,
+	0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+	0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+	0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+	0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15,
+	0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C,
+	0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF,
+	0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3,
+	0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF,
+	0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1,
+	0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F,
+	0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF,
+	0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8,
+	0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23,
+	0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA,
+	0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC,
+	0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB,
+	0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49,
+	0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07,
+	0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+	0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01,
+	0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2,
+	0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07,
+	0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01,
+	0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4,
+	0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E,
+	0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B,
+	0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01,
+	0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+	0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+	0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00,
+	0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB,
+	0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF,
+	0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC,
+	0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44,
+	0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01,
+	0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB,
+	0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00,
+	0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6,
+	0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3,
+	0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00,
+	0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48,
+	0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E,
+	0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00,
+	0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06,
+	0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81,
+	0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+	0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC,
+	0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC,
+	0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3,
+	0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8,
+	0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4,
+	0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+	0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+	0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A,
+	0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9,
+	0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF,
+	0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44,
+	0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF,
+	0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74,
+	0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A,
+	0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE,
+	0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B,
+	0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC,
+	0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8,
+	0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC,
+	0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F,
+	0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF,
+	0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96,
+	0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06,
+	0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+	0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00,
+	0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C,
+	0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56,
+	0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00,
+	0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3,
+	0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8,
+	0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00,
+	0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6,
+	0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+	0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A,
+	0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44,
+	0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+	0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1,
+	0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00,
+	0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6,
+	0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41,
+	0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01,
+	0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D,
+	0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F,
+	0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4,
+	0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01,
+	0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73,
+	0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D,
+	0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01,
+	0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07,
+	0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08,
+	0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E,
+	0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC,
+	0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD,
+	0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA,
+	0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F,
+	0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+	0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+	0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F,
+	0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB,
+	0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF,
+	0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89,
+	0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D,
+	0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15,
+	0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01,
+	0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6,
+	0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF,
+	0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86,
+	0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6,
+	0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC,
+	0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0,
+	0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF,
+	0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2,
+	0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06,
+	0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+	0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE,
+	0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3,
+	0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3,
+	0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF,
+	0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2,
+	0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35,
+	0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00,
+	0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD,
+	0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01,
+	0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+	0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+	0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF,
+	0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41,
+	0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00,
+	0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E,
+	0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE,
+	0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01,
+	0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1,
+	0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00,
+	0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF,
+	0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5,
+	0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02,
+	0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95,
+	0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6,
+	0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03,
+	0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07,
+	0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9,
+	0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00,
+	0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF,
+	0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC,
+	0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7,
+	0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE,
+	0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62,
+	0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+	0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+	0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+	0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A,
+	0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25,
+	0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2,
+	0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF,
+	0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0,
+	0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF,
+	0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F,
+	0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11,
+	0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01,
+	0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26,
+	0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF,
+	0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53,
+	0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4,
+	0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04,
+	0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC,
+	0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D,
+	0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D,
+	0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06,
+	0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+	0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD,
+	0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12,
+	0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29,
+	0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF,
+	0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+	0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1,
+	0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75,
+	0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00,
+	0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0,
+	0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA,
+	0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47,
+	0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60,
+	0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF,
+	0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02,
+	0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00,
+	0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B,
+	0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01,
+	0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C,
+	0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2,
+	0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7,
+	0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD,
+	0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64,
+	0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70,
+	0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04,
+	0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD,
+	0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06,
+	0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65,
+	0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+	0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71,
+	0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC,
+	0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91,
+	0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01,
+	0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F,
+	0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+	0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+	0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+	0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44,
+	0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29,
+	0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80,
+	0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF,
+	0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E,
+	0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B,
+	0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C,
+	0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01,
+	0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB,
+	0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF,
+	0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37,
+	0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2,
+	0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC,
+	0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29,
+	0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D,
+	0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05,
+	0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03,
+	0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0,
+	0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3,
+	0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE,
+	0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B,
+	0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1,
+	0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7,
+	0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF,
+	0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF,
+	0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+	0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+	0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF,
+	0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E,
+	0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00,
+	0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA,
+	0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37,
+	0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01,
+	0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7,
+	0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83,
+	0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8,
+	0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD,
+	0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC,
+	0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8,
+	0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05,
+	0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE,
+	0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06,
+	0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E,
+	0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+	0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86,
+	0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC,
+	0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+	0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC,
+	0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68,
+	0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+	0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+	0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55,
+	0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E,
+	0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63,
+	0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF,
+	0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94,
+	0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4,
+	0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08,
+	0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01,
+	0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C,
+	0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF,
+	0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33,
+	0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2,
+	0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD,
+	0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06,
+	0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6,
+	0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04,
+	0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04,
+	0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10,
+	0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F,
+	0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD,
+	0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22,
+	0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1,
+	0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA,
+	0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF,
+	0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0,
+	0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01,
+	0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81,
+	0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04,
+	0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07,
+	0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+	0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7,
+	0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00,
+	0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0,
+	0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32,
+	0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00,
+	0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5,
+	0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F,
+	0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA,
+	0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC,
+	0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78,
+	0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17,
+	0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06,
+	0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06,
+	0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31,
+	0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D,
+	0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD,
+	0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+	0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA,
+	0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE,
+	0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+	0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+	0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D,
+	0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33,
+	0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E,
+	0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00,
+	0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96,
+	0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E,
+	0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04,
+	0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01,
+	0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD,
+	0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF,
+	0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49,
+	0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1,
+	0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD,
+	0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8,
+	0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E,
+	0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03,
+	0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04,
+	0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38,
+	0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97,
+	0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD,
+	0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16,
+	0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2,
+	0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF,
+	0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+	0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5,
+	0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01,
+	0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C,
+	0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+	0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+	0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7,
+	0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF,
+	0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97,
+	0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E,
+	0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D,
+	0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00,
+	0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C,
+	0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F,
+	0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC,
+	0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC,
+	0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3,
+	0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00,
+	0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A,
+	0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06,
+	0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02,
+	0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05,
+	0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00,
+	0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03,
+	0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD,
+	0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C,
+	0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8,
+	0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54,
+	0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+	0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+	0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B,
+	0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37,
+	0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40,
+	0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00,
+	0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2,
+	0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF,
+	0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42,
+	0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48,
+	0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98,
+	0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01,
+	0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0,
+	0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A,
+	0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1,
+	0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD,
+	0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE,
+	0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43,
+	0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF,
+	0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03,
+	0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05,
+	0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B,
+	0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7,
+	0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC,
+	0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08,
+	0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3,
+	0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF,
+	0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86,
+	0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01,
+	0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A,
+	0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C,
+	0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96,
+	0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E,
+	0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF,
+	0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60,
+	0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29,
+	0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF,
+	0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0,
+	0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA,
+	0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01,
+	0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC,
+	0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04,
+	0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+	0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D,
+	0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06,
+	0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05,
+	0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04,
+	0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63,
+	0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+	0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62,
+	0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD,
+	0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7,
+	0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7,
+	0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+	0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+	0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0,
+	0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B,
+	0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00,
+	0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC,
+	0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF,
+	0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72,
+	0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47,
+	0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF,
+	0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56,
+	0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7,
+	0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1,
+	0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF,
+	0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD,
+	0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4,
+	0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD,
+	0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE,
+	0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06,
+	0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF,
+	0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF,
+	0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A,
+	0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC,
+	0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8,
+	0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4,
+	0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF,
+	0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D,
+	0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01,
+	0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+	0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+	0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+	0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97,
+	0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF,
+	0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B,
+	0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24,
+	0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF,
+	0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45,
+	0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D,
+	0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD,
+	0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC,
+	0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08,
+	0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00,
+	0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE,
+	0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07,
+	0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03,
+	0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D,
+	0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94,
+	0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02,
+	0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+	0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5,
+	0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33,
+	0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+	0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+	0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB,
+	0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F,
+	0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00,
+	0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81,
+	0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF,
+	0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D,
+	0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46,
+	0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5,
+	0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01,
+	0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D,
+	0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30,
+	0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2,
+	0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00,
+	0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC,
+	0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0,
+	0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF,
+	0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06,
+	0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07,
+	0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+	0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87,
+	0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC,
+	0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5,
+	0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6,
+	0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE,
+	0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF,
+	0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4,
+	0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01,
+	0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F,
+	0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16,
+	0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28,
+	0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60,
+	0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF,
+	0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9,
+	0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F,
+	0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE,
+	0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE,
+	0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B,
+	0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA,
+	0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC,
+	0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB,
+	0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B,
+	0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07,
+	0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D,
+	0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01,
+	0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9,
+	0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1,
+	0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01,
+	0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4,
+	0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96,
+	0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+	0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+	0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B,
+	0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42,
+	0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00,
+	0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50,
+	0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF,
+	0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF,
+	0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44,
+	0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01,
+	0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1,
+	0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00,
+	0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3,
+	0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3,
+	0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00,
+	0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35,
+	0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F,
+	0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00,
+	0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06,
+	0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76,
+	0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9,
+	0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC,
+	0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1,
+	0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8,
+	0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1,
+	0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F,
+	0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE,
+	0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+	0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+	0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF,
+	0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC,
+	0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF,
+	0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C,
+	0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A,
+	0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35,
+	0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE,
+	0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1,
+	0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5,
+	0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8,
+	0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC,
+	0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B,
+	0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+	0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84,
+	0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06,
+	0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12,
+	0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00,
+	0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45,
+	0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31,
+	0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00,
+	0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3,
+	0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF,
+	0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+	0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F,
+	0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44,
+	0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF,
+	0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB,
+	0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00,
+	0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+	0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41,
+	0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01,
+	0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C,
+	0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E,
+	0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4,
+	0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01,
+	0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60,
+	0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D,
+	0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02,
+	0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07,
+	0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE,
+	0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4,
+	0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC,
+	0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB,
+	0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB,
+	0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B,
+	0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00,
+	0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73,
+	0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF,
+	0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20,
+	0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7,
+	0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF,
+	0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B,
+	0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04,
+	0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15,
+	0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01,
+	0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70,
+	0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF,
+	0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81,
+	0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5,
+	0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC,
+	0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7,
+	0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+	0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B,
+	0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06,
+	0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+	0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE,
+	0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC,
+	0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F,
+	0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF,
+	0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2,
+	0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B,
+	0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00,
+	0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+	0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+	0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76,
+	0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46,
+	0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51,
+	0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF,
+	0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C,
+	0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00,
+	0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E,
+	0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF,
+	0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01,
+	0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8,
+	0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00,
+	0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00,
+	0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5,
+	0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE,
+	0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0,
+	0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00,
+	0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3,
+	0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03,
+	0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07,
+	0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2,
+	0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00,
+	0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF,
+	0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC,
+	0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5,
+	0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE,
+	0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E,
+	0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+	0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A,
+	0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF,
+	0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+	0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+	0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+	0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF,
+	0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30,
+	0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF,
+	0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95,
+	0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10,
+	0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01,
+	0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9,
+	0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF,
+	0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50,
+	0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4,
+	0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03,
+	0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC,
+	0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20,
+	0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83,
+	0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06,
+	0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+	0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD,
+	0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C,
+	0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07,
+	0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF,
+	0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+	0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1,
+	0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A,
+	0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00,
+	0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+	0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF,
+	0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48,
+	0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62,
+	0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF,
+	0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1,
+	0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00,
+	0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A,
+	0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01,
+	0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B,
+	0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4,
+	0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7,
+	0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD,
+	0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89,
+	0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B,
+	0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04,
+	0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD,
+	0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06,
+	0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61,
+	0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+	0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B,
+	0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC,
+	0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F,
+	0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01,
+	0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B,
+	0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+	0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6,
+	0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF,
+	0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45,
+	0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A,
+	0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D,
+	0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF,
+	0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C,
+	0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20,
+	0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C,
+	0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01,
+	0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7,
+	0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF,
+	0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35,
+	0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2,
+	0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC,
+	0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28,
+	0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F,
+	0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05,
+	0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03,
+	0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC,
+	0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6,
+	0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE,
+	0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A,
+	0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1,
+	0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB,
+	0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF,
+	0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+	0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+	0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8,
+	0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48,
+	0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF,
+	0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD,
+	0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00,
+	0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8,
+	0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36,
+	0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00,
+	0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC,
+	0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96,
+	0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8,
+	0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD,
+	0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F,
+	0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF,
+	0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05,
+	0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06,
+	0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C,
+	0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+	0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB,
+	0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC,
+	0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+	0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB,
+	0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72,
+	0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7,
+	0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00,
+	0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+	0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+	0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+	0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF,
+	0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23,
+	0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA,
+	0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07,
+	0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01,
+	0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2,
+	0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF,
+	0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34,
+	0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2,
+	0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD,
+	0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01,
+	0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4,
+	0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04,
+	0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04,
+	0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD,
+	0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76,
+	0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD,
+	0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21,
+	0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1,
+	0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD,
+	0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF,
+	0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+	0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+	0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F,
+	0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04,
+	0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03,
+	0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF,
+	0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89,
+	0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00,
+	0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD,
+	0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32,
+	0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00,
+	0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10,
+	0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73,
+	0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA,
+	0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC,
+	0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98,
+	0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B,
+	0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06,
+	0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05,
+	0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31,
+	0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00,
+	0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C,
+	0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD,
+	0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+	0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA,
+	0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9,
+	0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD,
+	0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00,
+	0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F,
+	0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33,
+	0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D,
+	0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00,
+	0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25,
+	0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11,
+	0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48,
+	0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84,
+	0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A,
+	0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00,
+	0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1,
+	0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3,
+	0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE,
+	0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7,
+	0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00,
+	0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26,
+	0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06,
+	0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16,
+	0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF,
+	0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A,
+	0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84,
+	0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD,
+	0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8,
+	0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9,
+	0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2,
+	0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+	0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+	0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12,
+	0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42,
+	0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B,
+	0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF,
+	0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7,
+	0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00,
+	0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4,
+	0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20,
+	0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01,
+	0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C,
+	0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF,
+	0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04,
+	0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2,
+	0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD,
+	0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70,
+	0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+	0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+	0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+	0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B,
+	0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD,
+	0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7,
+	0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC,
+	0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC,
+	0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2,
+	0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC,
+	0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79,
+	0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+	0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+	0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47,
+	0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44,
+	0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45,
+	0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF,
+	0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32,
+	0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF,
+	0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77,
+	0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B,
+	0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01,
+	0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E,
+	0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF,
+	0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E,
+	0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2,
+	0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD,
+	0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07,
+	0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00,
+	0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96,
+	0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07,
+	0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02,
+	0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25,
+	0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF,
+	0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85,
+	0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC,
+	0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C,
+	0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF,
+	0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49,
+	0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+	0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+	0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F,
+	0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46,
+	0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53,
+	0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE,
+	0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF,
+	0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11,
+	0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16,
+	0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01,
+	0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20,
+	0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF,
+	0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10,
+	0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3,
+	0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC,
+	0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83,
+	0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00,
+	0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+	0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+	0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03,
+	0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45,
+	0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF,
+	0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C,
+	0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC,
+	0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86,
+	0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03,
+	0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14,
+	0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+	0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+	0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+	0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8,
+	0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48,
+	0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64,
+	0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF,
+	0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54,
+	0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF,
+	0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2,
+	0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11,
+	0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01,
+	0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55,
+	0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF,
+	0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9,
+	0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5,
+	0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC,
+	0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC,
+	0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00,
+	0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38,
+	0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06,
+	0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04,
+	0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A,
+	0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+	0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7,
+	0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC,
+	0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24,
+	0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB,
+	0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3,
+	0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+	0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+	0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+	0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1,
+	0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48,
+	0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78,
+	0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF,
+	0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46,
+	0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF,
+	0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E,
+	0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C,
+	0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01,
+	0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD,
+	0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00,
+	0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77,
+	0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6,
+	0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF,
+	0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC,
+	0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C,
+	0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+	0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+	0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+	0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF,
+	0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05,
+	0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A,
+	0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+	0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC,
+	0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC,
+	0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29,
+	0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9,
+	0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19,
+	0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+	0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+	0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+	0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C,
+	0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05,
+	0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA,
+	0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF,
+	0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3,
+	0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF,
+	0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8,
+	0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08,
+	0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01,
+	0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14,
+	0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00,
+	0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44,
+	0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8,
+	0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC,
+	0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F,
+	0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00,
+	0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15,
+	0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05,
+	0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06,
+	0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9,
+	0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00,
+	0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10,
+	0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC,
+	0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E,
+	0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7,
+	0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E,
+	0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00,
+	0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+	0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68,
+	0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09,
+	0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2,
+	0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF,
+	0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A,
+	0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF,
+	0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42,
+	0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04,
+	0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01,
+	0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97,
+	0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00,
+	0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D,
+	0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9,
+	0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC,
+	0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1,
+	0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+	0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+	0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06,
+	0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C,
+	0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00,
+	0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7,
+	0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC,
+	0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32,
+	0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6,
+	0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE,
+	0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00,
+	0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+	0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56,
+	0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D,
+	0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89,
+	0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF,
+	0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60,
+	0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40,
+	0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48,
+	0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98,
+	0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01,
+	0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80,
+	0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00,
+	0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD,
+	0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB,
+	0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC,
+	0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F,
+	0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+	0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D,
+	0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04,
+	0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06,
+	0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96,
+	0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00,
+	0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0,
+	0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC,
+	0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35,
+	0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4,
+	0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66,
+	0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF,
+	0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+	0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47,
+	0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12,
+	0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51,
+	0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF,
+	0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64,
+	0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71,
+	0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47,
+	0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE,
+	0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01,
+	0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6,
+	0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C,
+	0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02,
+	0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C,
+	0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC,
+	0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA,
+	0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+	0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+	0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+	0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07,
+	0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18,
+	0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00,
+	0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B,
+	0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD,
+	0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3,
+	0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4,
+	0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF,
+	0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+	0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+	0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D,
+	0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17,
+	0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B,
+	0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF,
+	0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B,
+	0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C,
+	0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46,
+	0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4,
+	0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01,
+	0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61,
+	0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D,
+	0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE,
+	0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16,
+	0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC,
+	0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21,
+	0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13,
+	0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02,
+	0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07,
+	0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5,
+	0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00,
+	0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94,
+	0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD,
+	0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2,
+	0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17,
+	0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF,
+	0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+	0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+	0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37,
+	0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C,
+	0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA,
+	0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00,
+	0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56,
+	0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF,
+	0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE,
+	0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44,
+	0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9,
+	0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00,
+	0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47,
+	0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36,
+	0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB,
+	0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11,
+	0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC,
+	0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27,
+	0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+	0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+	0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD,
+	0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07,
+	0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E,
+	0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00,
+	0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05,
+	0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD,
+	0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33,
+	0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1,
+	0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C,
+	0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+	0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+	0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37,
+	0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21,
+	0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD,
+	0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00,
+	0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68,
+	0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF,
+	0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+	0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41,
+	0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED,
+	0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00,
+	0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C,
+	0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD,
+	0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8,
+	0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C,
+	0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD,
+	0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF,
+	0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D,
+	0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF,
+	0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE,
+	0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06,
+	0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42,
+	0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47,
+	0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02,
+	0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E,
+	0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1,
+	0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94,
+	0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+	0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+	0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+	0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C,
+	0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26,
+	0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96,
+	0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00,
+	0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0,
+	0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF,
+	0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E,
+	0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF,
+	0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00,
+	0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6,
+	0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+	0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93,
+	0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6,
+	0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08,
+	0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD,
+	0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC,
+	0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+	0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+	0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF,
+	0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06,
+	0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31,
+	0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94,
+	0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01,
+	0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26,
+	0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1,
+	0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE,
+	0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+	0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+	0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+	0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49,
+	0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B,
+	0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76,
+	0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00,
+	0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E,
+	0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF,
+	0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B,
+	0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF,
+	0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48,
+	0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00,
+	0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C,
+	0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4,
+	0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04,
+	0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE,
+	0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33,
+	0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A,
+	0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02,
+	0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01,
+	0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05,
+	0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6,
+	0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00,
+	0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C,
+	0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1,
+	0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8,
+	0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+	0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+	0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C,
+	0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30,
+	0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C,
+	0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00,
+	0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1,
+	0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF,
+	0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8,
+	0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37,
+	0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF,
+	0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6,
+	0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B,
+	0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3,
+	0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01,
+	0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF,
+	0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A,
+	0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+	0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+	0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+	0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04,
+	0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04,
+	0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C,
+	0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
+	0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48,
+	0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF,
+	0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E,
+	0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2,
+	0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5,
+	0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+	0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+	0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76,
+	0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34,
+	0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49,
+	0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF,
+	0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2,
+	0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01,
+	0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE,
+	0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32,
+	0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE,
+	0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94,
+	0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31,
+	0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2,
+	0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF,
+	0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00,
+	0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6,
+	0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+	0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC,
+	0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04,
+	0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08,
+	0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03,
+	0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93,
+	0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF,
+	0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6,
+	0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE,
+	0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF,
+	0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3,
+	0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00,
+	0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+	0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+	0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96,
+	0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38,
+	0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C,
+	0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF,
+	0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C,
+	0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00,
+	0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94,
+	0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E,
+	0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E,
+	0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE,
+	0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47,
+	0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42,
+	0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1,
+	0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+	0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00,
+	0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D,
+	0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+	0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+	0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C,
+	0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02,
+	0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD,
+	0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+	0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C,
+	0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE,
+	0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED,
+	0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5,
+	0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7,
+	0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00,
+	0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+	0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+	0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD,
+	0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C,
+	0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF,
+	0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69,
+	0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00,
+	0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C,
+	0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29,
+	0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01,
+	0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05,
+	0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF,
+	0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E,
+	0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1,
+	0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD,
+	0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01,
+	0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46,
+	0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00,
+	0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27,
+	0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06,
+	0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11,
+	0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00,
+	0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36,
+	0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47,
+	0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD,
+	0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9,
+	0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7,
+	0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE,
+	0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+	0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+	0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA,
+	0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40,
+	0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8,
+	0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00,
+	0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17,
+	0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24,
+	0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01,
+	0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94,
+	0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF,
+	0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5,
+	0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1,
+	0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE,
+	0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13,
+	0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00,
+	0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+	0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+	0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16,
+	0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF,
+	0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D,
+	0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61,
+	0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD,
+	0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4,
+	0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA,
+	0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C,
+	0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+	0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+	0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B,
+	0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43,
+	0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D,
+	0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF,
+	0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77,
+	0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00,
+	0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4,
+	0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F,
+	0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01,
+	0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B,
+	0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF,
+	0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18,
+	0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2,
+	0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD,
+	0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA,
+	0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00,
+	0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA,
+	0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07,
+	0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C,
+	0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD,
+	0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C,
+	0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6,
+	0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC,
+	0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE,
+	0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD,
+	0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71,
+	0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+	0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+	0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51,
+	0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45,
+	0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47,
+	0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF,
+	0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB,
+	0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF,
+	0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66,
+	0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A,
+	0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01,
+	0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0,
+	0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF,
+	0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96,
+	0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3,
+	0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD,
+	0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D,
+	0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00,
+	0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+	0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+	0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03,
+	0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD,
+	0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF,
+	0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80,
+	0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC,
+	0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98,
+	0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00,
+	0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40,
+	0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+	0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+	0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89,
+	0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47,
+	0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56,
+	0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61,
+	0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF,
+	0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15,
+	0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01,
+	0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7,
+	0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF,
+	0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C,
+	0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4,
+	0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC,
+	0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3,
+	0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00,
+	0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1,
+	0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07,
+	0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03,
+	0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D,
+	0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF,
+	0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97,
+	0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC,
+	0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F,
+	0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC,
+	0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42,
+	0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+	0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+	0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2,
+	0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48,
+	0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68,
+	0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF,
+	0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5,
+	0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF,
+	0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E,
+	0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10,
+	0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01,
+	0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10,
+	0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF,
+	0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9,
+	0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5,
+	0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC,
+	0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15,
+	0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00,
+	0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+	0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+	0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04,
+	0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44,
+	0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+	0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14,
+	0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC,
+	0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25,
+	0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA,
+	0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8,
+	0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+	0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+	0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA,
+	0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48,
+	0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C,
+	0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF,
+	0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86,
+	0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF,
+	0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19,
+	0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B,
+	0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01,
+	0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC,
+	0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00,
+	0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A,
+	0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7,
+	0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF,
+	0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC,
+	0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E,
+	0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00,
+	0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C,
+	0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06,
+	0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF,
+	0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05,
+	0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77,
+	0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00,
+	0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD,
+	0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC,
+	0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A,
+	0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9,
+	0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E,
+	0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+	0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78,
+	0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06,
+	0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1,
+	0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF,
+	0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15,
+	0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF,
+	0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3,
+	0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07,
+	0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01,
+	0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7,
+	0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00,
+	0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A,
+	0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8,
+	0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC,
+	0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38,
+	0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00,
+	0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+	0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+	0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06,
+	0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9,
+	0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00,
+	0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55,
+	0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC,
+	0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F,
+	0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7,
+	0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2,
+	0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00,
+	0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+	0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+	0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64,
+	0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A,
+	0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8,
+	0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF,
+	0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62,
+	0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF,
+	0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14,
+	0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48,
+	0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86,
+	0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01,
+	0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C,
+	0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00,
+	0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45,
+	0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA,
+	0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC,
+	0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00,
+	0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00,
+	0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83,
+	0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05,
+	0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06,
+	0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10,
+	0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00,
+	0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20,
+	0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC,
+	0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33,
+	0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6,
+	0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11,
+	0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00,
+	0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+	0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53,
+	0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E,
+	0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F,
+	0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF,
+	0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80,
+	0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49,
+	0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48,
+	0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C,
+	0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01,
+	0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85,
+	0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00,
+	0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25,
+	0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB,
+	0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC,
+	0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96,
+	0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF,
+	0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+	0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+	0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06,
+	0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E,
+	0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00,
+	0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E,
+	0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC,
+	0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36,
+	0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4,
+	0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77,
+	0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF,
+	0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+	0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+	0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45,
+	0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13,
+	0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47,
+	0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF,
+	0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81,
+	0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79,
+	0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47,
+	0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2,
+	0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01,
+	0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC,
+	0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7,
+	0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01,
+	0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B,
+	0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC,
+	0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7,
+	0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF,
+	0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70,
+	0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03,
+	0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07,
+	0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04,
+	0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00,
+	0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C,
+	0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD,
+	0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3,
+	0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4,
+	0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF,
+	0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+	0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+	0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B,
+	0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18,
+	0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12,
+	0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF,
+	0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78,
+	0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2,
+	0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46,
+	0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8,
+	0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01,
+	0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84,
+	0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A,
+	0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE,
+	0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16,
+	0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC,
+	0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26,
+	0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+	0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+	0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD,
+	0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07,
+	0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71,
+	0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7,
+	0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02,
+	0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05,
+	0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+	0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED,
+	0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00,
+	0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84,
+	0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+	0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+	0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00,
+	0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04,
+	0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B,
+	0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11,
+	0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00,
+	0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C,
+	0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00,
+	0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8,
+	0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3,
+	0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD,
+	0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23,
+	0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF,
+	0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+	0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+	0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07,
+	0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5,
+	0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF,
+	0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC,
+	0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD,
+	0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35,
+	0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4,
+	0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71,
+	0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+	0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44,
+	0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF,
+	0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+	0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+	0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+	0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+	0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5,
+	0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00,
+	0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1,
+	0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07,
+	0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF,
+	0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A,
+	0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00,
+	0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B,
+	0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6,
+	0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF,
+	0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE,
+	0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99,
+	0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+	0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+	0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06,
+	0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E,
+	0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8,
+	0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC,
+	0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2,
+	0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20,
+	0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C,
+	0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00,
+	0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+	0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+	0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF,
+	0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F,
+	0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00,
+	0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43,
+	0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48,
+	0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99,
+	0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE,
+	0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7,
+	0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00,
+	0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6,
+	0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9,
+	0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF,
+	0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C,
+	0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF,
+	0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+	0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+	0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00,
+	0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05,
+	0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35,
+	0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF,
+	0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1,
+	0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC,
+	0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D,
+	0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1,
+	0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A,
+	0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36,
+	0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01,
+	0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+	0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+	0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+	0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB,
+	0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00,
+	0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E,
+	0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46,
+	0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6,
+	0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01,
+	0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D,
+	0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF,
+	0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74,
+	0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC,
+	0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00,
+	0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01,
+	0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8,
+	0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+	0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+	0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06,
+	0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03,
+	0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D,
+	0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+	0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56,
+	0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC,
+	0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A,
+	0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1,
+	0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB,
+	0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00,
+	0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF,
+	0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+	0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+	0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+	0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A,
+	0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF,
+	0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8,
+	0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41,
+	0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE,
+	0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01,
+	0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9,
+	0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF,
+	0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65,
+	0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD,
+	0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14,
+	0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE,
+	0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E,
+	0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00,
+	0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+	0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+	0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F,
+	0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01,
+	0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15,
+	0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00,
+	0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD,
+	0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC,
+	0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD,
+	0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3,
+	0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00,
+	0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A,
+	0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+	0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+	0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81,
+	0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF,
+	0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A,
+	0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F,
+	0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01,
+	0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85,
+	0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF,
+	0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4,
+	0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7,
+	0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A,
+	0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD,
+	0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91,
+	0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00,
+	0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+	0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+	0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A,
+	0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD,
+	0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4,
+	0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00,
+	0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB,
+	0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD,
+	0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7,
+	0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7,
+	0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA,
+	0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF,
+	0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D,
+	0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01,
+	0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+	0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+	0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF,
+	0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40,
+	0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC,
+	0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32,
+	0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01,
+	0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A,
+	0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00,
+	0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48,
+	0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3,
+	0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03,
+	0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC,
+	0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D,
+	0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00,
+	0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+	0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+	0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00,
+	0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03,
+	0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C,
+	0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00,
+	0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6,
+	0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE,
+	0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC,
+	0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD,
+	0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C,
+	0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF,
+	0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B,
+	0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01,
+	0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+	0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+	0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF,
+	0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52,
+	0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59,
+	0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28,
+	0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01,
+	0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E,
+	0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00,
+	0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37,
+	0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1,
+	0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC,
+	0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F,
+	0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00,
+	0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+	0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+	0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF,
+	0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05,
+	0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE,
+	0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+	0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8,
+	0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01,
+	0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20,
+	0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC,
+	0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50,
+	0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF,
+	0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70,
+	0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01,
+	0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+	0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+	0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00,
+	0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B,
+	0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF,
+	0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E,
+	0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01,
+	0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B,
+	0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90,
+	0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1,
+	0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD,
+	0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC,
+	0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB,
+	0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF,
+	0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+	0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+	0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06,
+	0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49,
+	0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF,
+	0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E,
+	0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF,
+	0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8,
+	0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C,
+	0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00,
+	0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83,
+	0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE,
+	0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+	0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+	0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00,
+	0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7,
+	0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF,
+	0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7,
+	0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14,
+	0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32,
+	0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00,
+	0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47,
+	0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57,
+	0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2,
+	0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC,
+	0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29,
+	0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+	0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+	0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+	0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07,
+	0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C,
+	0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB,
+	0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD,
+	0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33,
+	0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5,
+	0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D,
+	0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+	0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59,
+	0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF,
+	0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C,
+	0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+	0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+	0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00,
+	0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13,
+	0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF,
+	0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12,
+	0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B,
+	0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29,
+	0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF,
+	0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19,
+	0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00,
+	0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85,
+	0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4,
+	0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE,
+	0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD,
+	0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6,
+	0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+	0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+	0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07,
+	0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78,
+	0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E,
+	0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC,
+	0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3,
+	0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE,
+	0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C,
+	0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00,
+	0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+	0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+	0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+	0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF,
+	0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D,
+	0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00,
+	0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18,
+	0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48,
+	0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87,
+	0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE,
+	0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61,
+	0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00,
+	0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06,
+	0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7,
+	0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF,
+	0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE,
+	0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00,
+	0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF,
+	0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+	0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+	0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06,
+	0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33,
+	0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E,
+	0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC,
+	0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32,
+	0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1,
+	0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E,
+	0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18,
+	0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01,
+	0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+	0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+	0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF,
+	0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC,
+	0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00,
+	0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C,
+	0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47,
+	0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3,
+	0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01,
+	0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73,
+	0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF,
+	0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB,
+	0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB,
+	0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00,
+	0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00,
+	0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1,
+	0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF,
+	0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+	0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+	0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04,
+	0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04,
+	0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55,
+	0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+	0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74,
+	0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC,
+	0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23,
+	0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1,
+	0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7,
+	0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2,
+	0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01,
+	0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+	0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+	0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+	0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E,
+	0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF,
+	0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5,
+	0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43,
+	0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE,
+	0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01,
+	0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1,
+	0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF,
+	0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC,
+	0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF,
+	0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18,
+	0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02,
+	0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8,
+	0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00,
+	0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+	0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+	0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B,
+	0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02,
+	0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF,
+	0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00,
+	0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F,
+	0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC,
+	0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+	0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2,
+	0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00,
+	0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06,
+	0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+	0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+	0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+	0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF,
+	0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE,
+	0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF,
+	0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6,
+	0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D,
+	0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03,
+	0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01,
+	0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02,
+	0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF,
+	0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6,
+	0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9,
+	0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD,
+	0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B,
+	0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00,
+	0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+	0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+	0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00,
+	0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06,
+	0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16,
+	0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF,
+	0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88,
+	0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02,
+	0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC,
+	0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A,
+	0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18,
+	0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF,
+	0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3,
+	0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00,
+	0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75,
+	0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10,
+	0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72,
+	0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF,
+	0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6,
+	0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF,
+	0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C,
+	0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10,
+	0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD,
+	0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00,
+	0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35,
+	0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1,
+	0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E,
+	0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9,
+	0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02,
+	0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F,
+	0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF,
+	0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71,
+	0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05,
+	0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF,
+	0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06,
+	0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6,
+	0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF,
+	0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B,
+	0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02,
+	0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89,
+	0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E,
+	0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1,
+	0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF,
+	0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD,
+	0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00,
+	0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F,
+	0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10,
+	0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E,
+	0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF,
+	0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA,
+	0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF,
+	0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6,
+	0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10,
+	0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2,
+	0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00,
+	0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3,
+	0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF,
+	0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50,
+	0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E,
+	0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2,
+	0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02,
+	0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03,
+	0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF,
+	0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB,
+	0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06,
+	0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00,
+	0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05,
+	0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73,
+	0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF,
+	0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88,
+	0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02,
+	0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93,
+	0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E,
+	0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9,
+	0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF,
+	0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E,
+	0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00,
+	0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C,
+	0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10,
+	0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C,
+	0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF,
+	0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38,
+	0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF,
+	0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6,
+	0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10,
+	0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97,
+	0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00,
+	0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D,
+	0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF,
+	0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB,
+	0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A,
+	0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00,
+	0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02,
+	0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99,
+	0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF,
+	0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45,
+	0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06,
+	0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00,
+	0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04,
+	0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4,
+	0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF,
+	0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00,
+	0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03,
+	0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D,
+	0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D,
+	0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7,
+	0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF,
+	0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05,
+	0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01,
+	0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D,
+	0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10,
+	0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D,
+	0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF,
+	0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C,
+	0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF,
+	0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB,
+	0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10,
+	0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D,
+	0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00,
+	0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3,
+	0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF,
+	0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C,
+	0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B,
+	0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01,
+	0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F,
+	0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF,
+	0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF,
+	0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07,
+	0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6,
+	0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09,
+	0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D,
+	0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+	0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73,
+	0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03,
+	0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8,
+	0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D,
+	0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA,
+	0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF,
+	0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9,
+	0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF,
+	0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70,
+	0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F,
+	0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72,
+	0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF,
+	0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6,
+	0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF,
+	0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96,
+	0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10,
+	0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84,
+	0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF,
+	0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52,
+	0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF,
+	0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3,
+	0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B,
+	0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE,
+	0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01,
+	0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5,
+	0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF,
+	0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C,
+	0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07,
+	0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC,
+	0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09,
+	0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2,
+	0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+	0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3,
+	0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04,
+	0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3,
+	0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C,
+	0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24,
+	0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF,
+	0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E,
+	0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF,
+	0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75,
+	0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F,
+	0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A,
+	0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF,
+	0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15,
+	0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF,
+	0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0,
+	0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F,
+	0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7,
+	0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF,
+	0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC,
+	0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF,
+	0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F,
+	0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C,
+	0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC,
+	0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04,
+	0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E,
+	0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00,
+	0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B,
+	0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08,
+	0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2,
+	0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08,
+	0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E,
+	0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+	0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50,
+	0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04,
+	0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE,
+	0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C,
+	0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53,
+	0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF,
+	0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88,
+	0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF,
+	0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C,
+	0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F,
+	0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86,
+	0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF,
+	0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A,
+	0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF,
+	0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF,
+	0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F,
+	0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF,
+	0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E,
+	0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF,
+	0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2,
+	0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C,
+	0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9,
+	0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04,
+	0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6,
+	0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00,
+	0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD,
+	0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09,
+	0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7,
+	0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07,
+	0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91,
+	0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF,
+	0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82,
+	0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01,
+	0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8,
+	0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B,
+	0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A,
+	0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF,
+	0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65,
+	0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF,
+	0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90,
+	0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10,
+	0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81,
+	0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF,
+	0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73,
+	0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF,
+	0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74,
+	0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F,
+	0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4,
+	0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF,
+	0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8,
+	0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF,
+	0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B,
+	0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D,
+	0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5,
+	0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03,
+	0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32,
+	0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF,
+	0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72,
+	0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09,
+	0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA,
+	0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07,
+	0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA,
+	0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF,
+	0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14,
+	0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01,
+	0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1,
+	0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B,
+	0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6,
+	0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF,
+	0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37,
+	0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00,
+	0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81,
+	0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10,
+	0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79,
+	0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF,
+	0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91,
+	0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF,
+	0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41,
+	0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10,
+	0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA,
+	0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01,
+	0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88,
+	0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF,
+	0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B,
+	0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D,
+	0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0,
+	0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03,
+	0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1,
+	0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF,
+	0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22,
+	0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04,
+	0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD,
+	0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06,
+	0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A,
+	0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF,
+	0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F,
+	0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02,
+	0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA,
+	0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A,
+	0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A,
+	0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF,
+	0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF,
+	0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00,
+	0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77,
+	0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10,
+	0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73,
+	0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF,
+	0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4,
+	0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF,
+	0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14,
+	0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10,
+	0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF,
+	0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00,
+	0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A,
+	0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1,
+	0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E,
+	0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA,
+	0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02,
+	0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53,
+	0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF,
+	0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D,
+	0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05,
+	0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF,
+	0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06,
+	0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF,
+	0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF,
+	0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23,
+	0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02,
+	0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87,
+	0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E,
+	0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D,
+	0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF,
+	0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB,
+	0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00,
+	0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70,
+	0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10,
+	0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E,
+	0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF,
+	0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB,
+	0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF,
+	0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC,
+	0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10,
+	0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4,
+	0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00,
+	0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9,
+	0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF,
+	0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F,
+	0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E,
+	0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3,
+	0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02,
+	0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7,
+	0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF,
+	0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7,
+	0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06,
+	0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00,
+	0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05,
+	0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B,
+	0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF,
+	0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1,
+	0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02,
+	0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91,
+	0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E,
+	0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5,
+	0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF,
+	0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D,
+	0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00,
+	0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D,
+	0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10,
+	0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C,
+	0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF,
+	0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27,
+	0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF,
+	0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB,
+	0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10,
+	0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99,
+	0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00,
+	0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34,
+	0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF,
+	0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3,
+	0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A,
+	0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00,
+	0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02,
+	0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D,
+	0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF,
+	0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61,
+	0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06,
+	0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00,
+	0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05,
+	0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC,
+	0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF,
+	0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A,
+	0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03,
+	0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B,
+	0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D,
+	0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1,
+	0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF,
+	0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16,
+	0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00,
+	0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C,
+	0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10,
+	0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D,
+	0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF,
+	0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D,
+	0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF,
+	0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0,
+	0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10,
+	0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F,
+	0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00,
+	0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA,
+	0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF,
+	0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43,
+	0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A,
+	0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01,
+	0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13,
+	0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF,
+	0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB,
+	0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07,
+	0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4,
+	0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09,
+	0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E,
+	0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+	0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E,
+	0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03,
+	0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6,
+	0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D,
+	0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3,
+	0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF,
+	0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA,
+	0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF,
+	0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F,
+	0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F,
+	0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71,
+	0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF,
+	0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47,
+	0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE,
+	0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30,
+	0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20,
+	0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42,
+	0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF,
+	0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC,
+	0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00,
+	0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F,
+	0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09,
+	0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD,
+	0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB,
+	0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2,
+	0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73,
+	0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03,
+	0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE,
+	0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC,
+	0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49,
+	0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF,
+	0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65,
+	0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB,
+	0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36,
+	0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E,
+	0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02,
+	0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00,
+	0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0,
+	0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00,
+	0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1,
+	0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21,
+	0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00,
+	0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62,
+	0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00,
+	0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52,
+	0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20,
+	0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D,
+	0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF,
+	0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2,
+	0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00,
+	0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78,
+	0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A,
+	0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD,
+	0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB,
+	0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4,
+	0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58,
+	0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04,
+	0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF,
+	0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB,
+	0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F,
+	0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+	0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56,
+	0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB,
+	0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D,
+	0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D,
+	0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9,
+	0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00,
+	0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A,
+	0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00,
+	0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB,
+	0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21,
+	0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00,
+	0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F,
+	0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00,
+	0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71,
+	0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20,
+	0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58,
+	0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00,
+	0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9,
+	0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00,
+	0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96,
+	0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B,
+	0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC,
+	0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB,
+	0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10,
+	0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF,
+	0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37,
+	0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB,
+	0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB,
+	0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D,
+	0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+	0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C,
+	0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB,
+	0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24,
+	0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C,
+	0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD,
+	0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00,
+	0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24,
+	0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00,
+	0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3,
+	0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21,
+	0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6,
+	0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00,
+	0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A,
+	0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00,
+	0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C,
+	0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21,
+	0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64,
+	0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00,
+	0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02,
+	0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00,
+	0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB,
+	0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C,
+	0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB,
+	0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB,
+	0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56,
+	0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF,
+	0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69,
+	0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB,
+	0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB,
+	0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44,
+	0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF,
+	0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49,
+	0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB,
+	0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D,
+	0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B,
+	0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E,
+	0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00,
+	0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD,
+	0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00,
+	0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8,
+	0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20,
+	0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2,
+	0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00,
+	0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF,
+	0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00,
+	0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4,
+	0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21,
+	0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70,
+	0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00,
+	0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A,
+	0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00,
+	0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4,
+	0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D,
+	0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA,
+	0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB,
+	0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8,
+	0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF,
+	0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4,
+	0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB,
+	0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB,
+	0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53,
+	0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF,
+	0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B,
+	0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB,
+	0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16,
+	0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A,
+	0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D,
+	0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00,
+	0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26,
+	0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF,
+	0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB,
+	0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20,
+	0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD,
+	0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00,
+	0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5,
+	0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9,
+	0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21,
+	0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D,
+	0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00,
+	0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32,
+	0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00,
+	0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12,
+	0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E,
+	0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9,
+	0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB,
+	0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05,
+	0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF,
+	0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA,
+	0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC,
+	0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9,
+	0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03,
+	0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2,
+	0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+	0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54,
+	0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB,
+	0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F,
+	0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09,
+	0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A,
+	0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00,
+	0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90,
+	0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF,
+	0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC,
+	0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20,
+	0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7,
+	0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00,
+	0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4,
+	0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00,
+	0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB,
+	0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21,
+	0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A,
+	0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00,
+	0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47,
+	0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00,
+	0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43,
+	0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F,
+	0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8,
+	0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB,
+	0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E,
+	0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF,
+	0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B,
+	0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC,
+	0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8,
+	0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02,
+	0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65,
+	0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66,
+	0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB,
+	0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A,
+	0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08,
+	0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4,
+	0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00,
+	0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB,
+	0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF,
+	0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA,
+	0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F,
+	0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1,
+	0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00,
+	0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8,
+	0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00,
+	0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5,
+	0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17,
+	0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB,
+	0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00,
+	0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59,
+	0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00,
+	0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77,
+	0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10,
+	0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7,
+	0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB,
+	0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2,
+	0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF,
+	0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98,
+	0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC,
+	0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7,
+	0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01,
+	0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29,
+	0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80,
+	0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB,
+	0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05,
+	0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07,
+	0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC,
+	0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00,
+	0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38,
+	0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF,
+	0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6,
+	0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F,
+	0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9,
+	0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00,
+	0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91,
+	0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00,
+	0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30,
+	0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18,
+	0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE,
+	0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00,
+	0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66,
+	0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00,
+	0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE,
+	0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11,
+	0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7,
+	0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC,
+	0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62,
+	0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF,
+	0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF,
+	0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC,
+	0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7,
+	0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01,
+	0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF,
+	0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF,
+	0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F,
+	0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD,
+	0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02,
+	0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06,
+	0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72,
+	0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00,
+	0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78,
+	0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF,
+	0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1,
+	0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E,
+	0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0,
+	0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00,
+	0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F,
+	0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00,
+	0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B,
+	0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19,
+	0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00,
+	0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E,
+	0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00,
+	0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7,
+	0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13,
+	0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7,
+	0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC,
+	0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED,
+	0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF,
+	0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73,
+	0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD,
+	0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7,
+	0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00,
+	0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5,
+	0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF,
+	0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87,
+	0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD,
+	0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE,
+	0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05,
+	0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38,
+	0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00,
+	0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC,
+	0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE,
+	0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA,
+	0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E,
+	0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6,
+	0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00,
+	0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21,
+	0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00,
+	0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5,
+	0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A,
+	0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05,
+	0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD,
+	0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63,
+	0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF,
+	0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22,
+	0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14,
+	0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7,
+	0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC,
+	0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84,
+	0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF,
+	0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1,
+	0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD,
+	0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7,
+	0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF,
+	0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E,
+	0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF,
+	0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B,
+	0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD,
+	0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC,
+	0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04,
+	0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC,
+	0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00,
+	0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3,
+	0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE,
+	0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2,
+	0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D,
+	0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA,
+	0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00,
+	0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6,
+	0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00,
+	0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD,
+	0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B,
+	0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09,
+	0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE,
+	0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48,
+	0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E,
+	0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15,
+	0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8,
+	0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC,
+	0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26,
+	0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF,
+	0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C,
+	0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE,
+	0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8,
+	0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF,
+	0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49,
+	0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF,
+	0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69,
+	0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC,
+	0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B,
+	0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16,
+	0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD,
+	0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00,
+	0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0,
+	0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE,
+	0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9,
+	0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C,
+	0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB,
+	0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00,
+	0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C,
+	0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00,
+	0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13,
+	0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C,
+	0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F,
+	0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE,
+	0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36,
+	0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A,
+	0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16,
+	0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9,
+	0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC,
+	0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4,
+	0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF,
+	0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12,
+	0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE,
+	0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9,
+	0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE,
+	0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18,
+	0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF,
+	0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54,
+	0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC,
+	0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E,
+	0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15,
+	0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC,
+	0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04,
+	0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE,
+	0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE,
+	0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B,
+	0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB,
+	0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00,
+	0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14,
+	0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00,
+	0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47,
+	0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C,
+	0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15,
+	0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE,
+	0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C,
+	0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00,
+	0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56,
+	0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04,
+	0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00,
+	0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD,
+	0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C,
+	0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF,
+	0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6,
+	0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8,
+	0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE,
+	0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42,
+	0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00,
+	0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5,
+	0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03,
+	0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC,
+	0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3,
+	0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF,
+	0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF,
+	0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3,
+	0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE,
+	0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68,
+	0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A,
+	0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1,
+	0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00,
+	0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8,
+	0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00,
+	0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB,
+	0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27,
+	0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3,
+	0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE,
+	0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E,
+	0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF,
+	0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4,
+	0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4,
+	0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01,
+	0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03,
+	0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F,
+	0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62,
+	0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05,
+	0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05,
+	0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB,
+	0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54,
+	0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03,
+	0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1,
+	0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3,
+	0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7,
+	0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF,
+	0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB,
+	0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE,
+	0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55,
+	0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28,
+	0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4,
+	0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00,
+	0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8,
+	0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00,
+	0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8,
+	0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29,
+	0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD,
+	0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE,
+	0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02,
+	0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF,
+	0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A,
+	0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3,
+	0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01,
+	0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03,
+	0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28,
+	0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+	0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16,
+	0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05,
+	0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05,
+	0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00,
+	0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE,
+	0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04,
+	0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5,
+	0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4,
+	0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2,
+	0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF,
+	0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F,
+	0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE,
+	0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43,
+	0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26,
+	0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6,
+	0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00,
+	0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98,
+	0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00,
+	0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25,
+	0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B,
+	0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7,
+	0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE,
+	0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8,
+	0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF,
+	0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F,
+	0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3,
+	0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02,
+	0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03,
+	0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39,
+	0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00,
+	0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC,
+	0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05,
+	0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05,
+	0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40,
+	0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71,
+	0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04,
+	0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9,
+	0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4,
+	0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E,
+	0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF,
+	0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70,
+	0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE,
+	0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31,
+	0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24,
+	0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7,
+	0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00,
+	0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68,
+	0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00,
+	0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51,
+	0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D,
+	0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1,
+	0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE,
+	0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0,
+	0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF,
+	0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4,
+	0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3,
+	0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02,
+	0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02,
+	0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41,
+	0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00,
+	0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84,
+	0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06,
+	0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D,
+	0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9,
+	0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8,
+	0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D,
+	0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04,
+	0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5,
+	0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D,
+	0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF,
+	0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42,
+	0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD,
+	0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20,
+	0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22,
+	0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07,
+	0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00,
+	0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24,
+	0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00,
+	0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C,
+	0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F,
+	0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB,
+	0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE,
+	0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC,
+	0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF,
+	0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79,
+	0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3,
+	0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03,
+	0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02,
+	0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40,
+	0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00,
+	0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C,
+	0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06,
+	0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E,
+	0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9,
+	0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2,
+	0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0,
+	0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04,
+	0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5,
+	0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F,
+	0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF,
+	0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16,
+	0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD,
+	0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10,
+	0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20,
+	0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17,
+	0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+	0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD,
+	0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00,
+	0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7,
+	0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31,
+	0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5,
+	0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF,
+	0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C,
+	0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF,
+	0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F,
+	0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2,
+	0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03,
+	0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02,
+	0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35,
+	0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00,
+	0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3,
+	0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06,
+	0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F,
+	0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA,
+	0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2,
+	0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A,
+	0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05,
+	0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6,
+	0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34,
+	0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF,
+	0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED,
+	0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD,
+	0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00,
+	0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D,
+	0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26,
+	0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00,
+	0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F,
+	0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00,
+	0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1,
+	0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32,
+	0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0,
+	0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF,
+	0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41,
+	0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF,
+	0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6,
+	0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2,
+	0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04,
+	0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01,
+	0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20,
+	0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00,
+	0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8,
+	0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06,
+	0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10,
+	0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB,
+	0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5,
+	0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00,
+	0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1,
+	0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC,
+	0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+	0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6,
+	0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D,
+	0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF,
+	0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8,
+	0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD,
+	0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2,
+	0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B,
+	0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33,
+	0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00,
+	0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB,
+	0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01,
+	0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9,
+	0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34,
+	0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA,
+	0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF,
+	0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B,
+	0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF,
+	0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F,
+	0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2,
+	0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05,
+	0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01,
+	0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02,
+	0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00,
+	0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A,
+	0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06,
+	0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10,
+	0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC,
+	0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE,
+	0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00,
+	0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2,
+	0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC,
+	0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7,
+	0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09,
+	0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB,
+	0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD,
+	0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4,
+	0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19,
+	0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F,
+	0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00,
+	0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C,
+	0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01,
+	0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20,
+	0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36,
+	0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5,
+	0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF,
+	0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB,
+	0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF,
+	0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A,
+	0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3,
+	0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06,
+	0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01,
+	0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9,
+	0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00,
+	0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09,
+	0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06,
+	0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11,
+	0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC,
+	0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC,
+	0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00,
+	0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34,
+	0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC,
+	0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B,
+	0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8,
+	0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8,
+	0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00,
+	0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95,
+	0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD,
+	0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7,
+	0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17,
+	0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A,
+	0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00,
+	0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87,
+	0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01,
+	0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45,
+	0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37,
+	0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0,
+	0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF,
+	0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61,
+	0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF,
+	0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8,
+	0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3,
+	0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06,
+	0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00,
+	0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7,
+	0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00,
+	0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3,
+	0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06,
+	0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11,
+	0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD,
+	0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F,
+	0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00,
+	0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87,
+	0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC,
+	0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96,
+	0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08,
+	0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D,
+	0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+	0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89,
+	0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD,
+	0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB,
+	0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15,
+	0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53,
+	0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF,
+	0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5,
+	0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01,
+	0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69,
+	0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39,
+	0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B,
+	0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF,
+	0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF,
+	0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF,
+	0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79,
+	0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3,
+	0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07,
+	0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00,
+	0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A,
+	0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00,
+	0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58,
+	0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06,
+	0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10,
+	0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE,
+	0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27,
+	0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00,
+	0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE,
+	0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC,
+	0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92,
+	0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06,
+	0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56,
+	0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+	0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88,
+	0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD,
+	0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0,
+	0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13,
+	0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A,
+	0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF,
+	0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6,
+	0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01,
+	0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A,
+	0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A,
+	0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97,
+	0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00,
+	0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93,
+	0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF,
+	0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D,
+	0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3,
+	0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08,
+	0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00,
+	0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23,
+	0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00,
+	0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7,
+	0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05,
+	0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10,
+	0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE,
+	0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45,
+	0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00,
+	0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69,
+	0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC,
+	0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F,
+	0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04,
+	0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D,
+	0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
+	0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94,
+	0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC,
+	0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6,
+	0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11,
+	0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F,
+	0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF,
+	0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB,
+	0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01,
+	0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9,
+	0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C,
+	0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93,
+	0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00,
+	0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F,
+	0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF,
+	0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4,
+	0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4,
+	0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09,
+	0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF,
+	0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1,
+	0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00,
+	0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90,
+	0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05,
+	0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F,
+	0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF,
+	0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67,
+	0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00,
+	0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8,
+	0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD,
+	0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C,
+	0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02,
+	0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41,
+	0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF,
+	0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A,
+	0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01,
+	0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD,
+	0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F,
+	0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63,
+	0x01, 0x8D, 0xFF, 0x0F, 0x00 
+};
+
+static u16
+CoefficientSizes[] = {
+    /* Playback */
+    0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000,
+    /* Record */
+    0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000,
+};
+
+#ifndef JUST_DATA
+
+static u16
+nm256_getStartOffset (u8 which)
+{
+    u16 offset = 0;
+
+    while (which-- > 0)
+	offset += CoefficientSizes[which];
+
+    return offset;
+}
+
+static void
+nm256_loadOneCoefficient (struct nm256_info *card, int devnum, u32 port, 
+			  u16 which)
+{
+    u32 coeffBuf = (which < 8) ? card->coeffBuf : card->allCoeffBuf;
+    u16 offset = nm256_getStartOffset (which);
+    u16 size = CoefficientSizes[which];
+
+    card->coeffsCurrent = 0;
+
+    if (nm256_debug)
+	printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d, size %d, port 0x%x\n",
+		coeffBuf, coeffBuf + size - 1, which, size, port);
+    nm256_writeBuffer8 (card, coefficients + offset, 1, coeffBuf, size);
+    nm256_writePort32 (card, 2, port + 0, coeffBuf);
+    /* ???  Record seems to behave differently than playback.  */
+    if (devnum == 0)
+	size--;
+    nm256_writePort32 (card, 2, port + 4, coeffBuf + size);
+}
+
+static void
+nm256_loadAllCoefficients (struct nm256_info *card)
+{
+    nm256_writeBuffer8 (card, coefficients, 1, card->allCoeffBuf,
+			  NM_TOTAL_COEFF_COUNT * 4);
+    card->coeffsCurrent = 1;
+}
+
+void
+nm256_loadCoefficient (struct nm256_info *card, int which, int number)
+{
+    static u16 addrs[3] = { 0x1c, 0x21c, 0x408 };
+    /* The enable register for the specified engine.  */
+    u32 poffset = (which == 1 ? 0x200 : 1);
+
+    if (nm256_readPort8 (card, 2, poffset) & 1) {
+	printk (KERN_ERR "NM256: Engine was enabled while loading coefficients!\n");
+	return;
+    }
+
+    /* The recording engine uses coefficient values 8-15.  */
+    if (which == 1)
+	number += 8;
+
+    if (! nm256_cachedCoefficients (card))
+	nm256_loadOneCoefficient (card, which, addrs[which], number);
+    else {
+	u32 base = card->allCoeffBuf;
+	u32 offset = nm256_getStartOffset (number);
+	u32 endOffset = offset + CoefficientSizes[number];
+
+	if (nm256_debug)
+	    printk (KERN_DEBUG "loading coefficient %d at port 0x%x, offset %d (0x%x-0x%x)\n",
+		    number, addrs[which], offset, base + offset,
+		    base + endOffset - 1);
+
+	if (! card->coeffsCurrent)
+	    nm256_loadAllCoefficients (card);
+
+	nm256_writePort32 (card, 2, addrs[which], base + offset);
+	nm256_writePort32 (card, 2, addrs[which] + 4, base + endOffset - 1);
+    }
+}
+
+#endif /* JUST_DATA */
+
+#endif
+
+/*
+ * Local variables:
+ *  c-basic-offset: 4
+ * End:
+ */
diff -Nru linux/sound/oss/opl3.c linux-2.4.19-pre5-mjc/sound/oss/opl3.c
--- linux/sound/oss/opl3.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/opl3.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1253 @@
+/*
+ * sound/opl3.c
+ *
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Changes
+ *	Thomas Sailer   	ioctl code reworked (vmalloc/vfree removed)
+ *	Alan Cox		modularisation, fixed sound_mem allocs.
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	get rid of check_region, use request_region for
+ *				OPL4, release it on exit, some cleanups.
+ *
+ * Status
+ *	Believed to work. Badly needs rewriting a bit to support multiple
+ *	OPL3 devices.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+/*
+ * Major improvements to the FM handling 30AUG92 by Rob Hooft,
+ * hooft@chem.ruu.nl
+ */
+
+#include "sound_config.h"
+
+#include "opl3.h"
+#include "opl3_hw.h"
+
+#define MAX_VOICE	18
+#define OFFS_4OP	11
+
+struct voice_info
+{
+	unsigned char   keyon_byte;
+	long            bender;
+	long            bender_range;
+	unsigned long   orig_freq;
+	unsigned long   current_freq;
+	int             volume;
+	int             mode;
+	int             panning;	/* 0xffff means not set */
+};
+
+typedef struct opl_devinfo
+{
+	int             base;
+	int             left_io, right_io;
+	int             nr_voice;
+	int             lv_map[MAX_VOICE];
+
+	struct voice_info voc[MAX_VOICE];
+	struct voice_alloc_info *v_alloc;
+	struct channel_info *chn_info;
+
+	struct sbi_instrument i_map[SBFM_MAXINSTR];
+	struct sbi_instrument *act_i[MAX_VOICE];
+
+	struct synth_info fm_info;
+
+	int             busy;
+	int             model;
+	unsigned char   cmask;
+
+	int             is_opl4;
+	int            *osp;
+} opl_devinfo;
+
+static struct opl_devinfo *devc = NULL;
+
+static int      detected_model;
+
+static int      store_instr(int instr_no, struct sbi_instrument *instr);
+static void     freq_to_fnum(int freq, int *block, int *fnum);
+static void     opl3_command(int io_addr, unsigned int addr, unsigned int val);
+static int      opl3_kill_note(int dev, int voice, int note, int velocity);
+
+static void enter_4op_mode(void)
+{
+	int i;
+	static int v4op[MAX_VOICE] = {
+		0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17
+	};
+
+	devc->cmask = 0x3f;	/* Connect all possible 4 OP voice operators */
+	opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);
+
+	for (i = 0; i < 3; i++)
+		pv_map[i].voice_mode = 4;
+	for (i = 3; i < 6; i++)
+		pv_map[i].voice_mode = 0;
+
+	for (i = 9; i < 12; i++)
+		pv_map[i].voice_mode = 4;
+	for (i = 12; i < 15; i++)
+		pv_map[i].voice_mode = 0;
+
+	for (i = 0; i < 12; i++)
+		devc->lv_map[i] = v4op[i];
+	devc->v_alloc->max_voice = devc->nr_voice = 12;
+}
+
+static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	struct sbi_instrument ins;
+	
+	switch (cmd) {
+		case SNDCTL_FM_LOAD_INSTR:
+			printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+			if (copy_from_user(&ins, arg, sizeof(ins)))
+				return -EFAULT;
+			if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
+				printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
+				return -EINVAL;
+			}
+			return store_instr(ins.channel, &ins);
+
+		case SNDCTL_SYNTH_INFO:
+			devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
+			if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
+
+		case SNDCTL_FM_4OP_ENABLE:
+			if (devc->model == 2)
+				enter_4op_mode();
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+}
+
+int opl3_detect(int ioaddr, int *osp)
+{
+	/*
+	 * This function returns 1 if the FM chip is present at the given I/O port
+	 * The detection algorithm plays with the timer built in the FM chip and
+	 * looks for a change in the status register.
+	 *
+	 * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+	 * boards.
+	 *
+	 * Note2! The chip is initialized if detected.
+	 */
+
+	unsigned char stat1, signature;
+	int i;
+
+	if (devc != NULL)
+	{
+		printk(KERN_ERR "opl3: Only one OPL3 supported.\n");
+		return 0;
+	}
+
+	devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL);
+
+	if (devc == NULL)
+	{
+		printk(KERN_ERR "opl3: Can't allocate memory for the device control "
+			"structure \n ");
+		return 0;
+	}
+
+	memset(devc, 0, sizeof(*devc));
+	strcpy(devc->fm_info.name, "OPL2");
+
+	if (!request_region(ioaddr, 4, devc->fm_info.name)) {
+		printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr);
+		goto cleanup_devc;
+	}
+
+	devc->osp = osp;
+	devc->base = ioaddr;
+
+	/* Reset timers 1 and 2 */
+	opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+
+	/* Reset the IRQ of the FM chip */
+	opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+	signature = stat1 = inb(ioaddr);	/* Status register */
+
+	if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
+		signature != 0x0f)
+	{
+		MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature));
+		goto cleanup_region;
+	}
+
+	if (signature == 0x06)		/* OPL2 */
+	{
+		detected_model = 2;
+	}
+	else if (signature == 0x00 || signature == 0x0f)	/* OPL3 or OPL4 */
+	{
+		unsigned char tmp;
+
+		detected_model = 3;
+
+		/*
+		 * Detect availability of OPL4 (_experimental_). Works probably
+		 * only after a cold boot. In addition the OPL4 port
+		 * of the chip may not be connected to the PC bus at all.
+		 */
+
+		opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
+		opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
+
+		if ((tmp = inb(ioaddr)) == 0x02)	/* Have a OPL4 */
+		{
+			detected_model = 4;
+		}
+
+		if (request_region(ioaddr - 8, 2, "OPL4"))	/* OPL4 port was free */
+		{
+			int tmp;
+
+			outb((0x02), ioaddr - 8);	/* Select OPL4 ID register */
+			udelay(10);
+			tmp = inb(ioaddr - 7);		/* Read it */
+			udelay(10);
+
+			if (tmp == 0x20)	/* OPL4 should return 0x20 here */
+			{
+				detected_model = 4;
+				outb((0xF8), ioaddr - 8);	/* Select OPL4 FM mixer control */
+				udelay(10);
+				outb((0x1B), ioaddr - 7);	/* Write value */
+				udelay(10);
+			}
+			else
+			{ /* release OPL4 port */
+				release_region(ioaddr - 8, 2);
+				detected_model = 3;
+			}
+		}
+		opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0);
+	}
+	for (i = 0; i < 9; i++)
+		opl3_command(ioaddr, KEYON_BLOCK + i, 0);	/*
+								 * Note off
+								 */
+
+	opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+	opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00);	/*
+								 * Melodic mode.
+								 */
+	return 1;
+cleanup_region:
+	release_region(ioaddr, 4);
+cleanup_devc:
+	kfree(devc);
+	devc = NULL;
+	return 0;
+}
+
+static int opl3_kill_note  (int devno, int voice, int note, int velocity)
+{
+	 struct physical_voice_info *map;
+
+	 if (voice < 0 || voice >= devc->nr_voice)
+		 return 0;
+
+	 devc->v_alloc->map[voice] = 0;
+
+	 map = &pv_map[devc->lv_map[voice]];
+	 DEB(printk("Kill note %d\n", voice));
+
+	 if (map->voice_mode == 0)
+		 return 0;
+
+	 opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);
+	 devc->voc[voice].keyon_byte = 0;
+	 devc->voc[voice].bender = 0;
+	 devc->voc[voice].volume = 64;
+	 devc->voc[voice].panning = 0xffff;	/* Not set */
+	 devc->voc[voice].bender_range = 200;
+	 devc->voc[voice].orig_freq = 0;
+	 devc->voc[voice].current_freq = 0;
+	 devc->voc[voice].mode = 0;
+	 return 0;
+}
+
+#define HIHAT			0
+#define CYMBAL			1
+#define TOMTOM			2
+#define SNARE			3
+#define BDRUM			4
+#define UNDEFINED		TOMTOM
+#define DEFAULT			TOMTOM
+
+static int store_instr(int instr_no, struct sbi_instrument *instr)
+{
+	if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
+		printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
+	memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr));
+	return 0;
+}
+
+static int opl3_set_instr  (int dev, int voice, int instr_no)
+{
+	if (voice < 0 || voice >= devc->nr_voice)
+		return 0;
+	if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+		instr_no = 0;	/* Acoustic piano (usually) */
+
+	devc->act_i[voice] = &devc->i_map[instr_no];
+	return 0;
+}
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
+ * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+
+static char fm_volume_table[128] =
+{
+	-64, -48, -40, -35, -32, -29, -27, -26,
+	-24, -23, -21, -20, -19, -18, -18, -17,
+	-16, -15, -15, -14, -13, -13, -12, -12,
+	-11, -11, -10, -10, -10, -9, -9, -8,
+	-8, -8, -7, -7, -7, -6, -6, -6,
+	-5, -5, -5, -5, -4, -4, -4, -4,
+	-3, -3, -3, -3, -2, -2, -2, -2,
+	-2, -1, -1, -1, -1, 0, 0, 0,
+	0, 0, 0, 1, 1, 1, 1, 1,
+	1, 2, 2, 2, 2, 2, 2, 2,
+	3, 3, 3, 3, 3, 3, 3, 4,
+	4, 4, 4, 4, 4, 4, 4, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	6, 6, 6, 6, 6, 6, 6, 6,
+	6, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 8, 8, 8, 8, 8
+};
+
+static void calc_vol(unsigned char *regbyte, int volume, int main_vol)
+{
+	int level = (~*regbyte & 0x3f);
+
+	if (main_vol > 127)
+		main_vol = 127;
+	volume = (volume * main_vol) / 127;
+
+	if (level)
+		level += fm_volume_table[volume];
+
+	if (level > 0x3f)
+		level = 0x3f;
+	if (level < 0)
+		level = 0;
+
+	*regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void set_voice_volume(int voice, int volume, int main_vol)
+{
+	unsigned char vol1, vol2, vol3, vol4;
+	struct sbi_instrument *instr;
+	struct physical_voice_info *map;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	map = &pv_map[devc->lv_map[voice]];
+	instr = devc->act_i[voice];
+
+	if (!instr)
+		instr = &devc->i_map[0];
+
+	if (instr->channel < 0)
+		return;
+
+	if (devc->voc[voice].mode == 0)
+		return;
+
+	if (devc->voc[voice].mode == 2)
+	{
+		vol1 = instr->operators[2];
+		vol2 = instr->operators[3];
+		if ((instr->operators[10] & 0x01))
+		{
+			calc_vol(&vol1, volume, main_vol);
+			calc_vol(&vol2, volume, main_vol);
+		}
+		else
+		{
+			calc_vol(&vol2, volume, main_vol);
+		}
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+	}
+	else
+	{	/*
+		 * 4 OP voice
+		 */
+		int connection;
+
+		vol1 = instr->operators[2];
+		vol2 = instr->operators[3];
+		vol3 = instr->operators[OFFS_4OP + 2];
+		vol4 = instr->operators[OFFS_4OP + 3];
+
+		/*
+		 * The connection method for 4 OP devc->voc is defined by the rightmost
+		 * bits at the offsets 10 and 10+OFFS_4OP
+		 */
+
+		connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+		switch (connection)
+		{
+			case 0:
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			case 1:
+				calc_vol(&vol2, volume, main_vol);
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			case 2:
+				calc_vol(&vol1, volume, main_vol);
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			case 3:
+				calc_vol(&vol1, volume, main_vol);
+				calc_vol(&vol3, volume, main_vol);
+				calc_vol(&vol4, volume, main_vol);
+				break;
+
+			default:
+				;
+		}
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4);
+	}
+}
+
+static int opl3_start_note (int dev, int voice, int note, int volume)
+{
+	unsigned char data, fpc;
+	int block, fnum, freq, voice_mode, pan;
+	struct sbi_instrument *instr;
+	struct physical_voice_info *map;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return 0;
+
+	map = &pv_map[devc->lv_map[voice]];
+	pan = devc->voc[voice].panning;
+
+	if (map->voice_mode == 0)
+		return 0;
+
+	if (note == 255)	/*
+				 * Just change the volume
+				 */
+	{
+		set_voice_volume(voice, volume, devc->voc[voice].volume);
+		return 0;
+	}
+
+	/*
+	 * Kill previous note before playing
+	 */
+	
+	opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff);	/*
+									 * Carrier
+									 * volume to
+									 * min
+									 */
+	opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff);	/*
+									 * Modulator
+									 * volume to
+									 */
+
+	if (map->voice_mode == 4)
+	{
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+		opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+	}
+
+	opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00);	/*
+									 * Note
+									 * off
+									 */
+
+	instr = devc->act_i[voice];
+	
+	if (!instr)
+		instr = &devc->i_map[0];
+
+	if (instr->channel < 0)
+	{
+		printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice);
+		return 0;
+	}
+
+	if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+		return 0;	/*
+				 * Cannot play
+				 */
+
+	voice_mode = map->voice_mode;
+
+	if (voice_mode == 4)
+	{
+		int voice_shift;
+
+		voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
+		voice_shift += map->voice_num;
+
+		if (instr->key != OPL3_PATCH)	/*
+						 * Just 2 OP patch
+						 */
+		{
+			voice_mode = 2;
+			devc->cmask &= ~(1 << voice_shift);
+		}
+		else
+		{
+			devc->cmask |= (1 << voice_shift);
+		}
+
+		opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
+	}
+
+	/*
+	 * Set Sound Characteristics
+	 */
+	
+	opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+	opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+	/*
+	 * Set Attack/Decay
+	 */
+	
+	opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+	opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+	/*
+	 * Set Sustain/Release
+	 */
+	
+	opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+	opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+	/*
+	 * Set Wave Select
+	 */
+
+	opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+	opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+	/*
+	 * Set Feedback/Connection
+	 */
+	
+	fpc = instr->operators[10];
+
+	if (pan != 0xffff)
+	{
+		fpc &= ~STEREO_BITS;
+		if (pan < -64)
+			fpc |= VOICE_TO_LEFT;
+		else
+			if (pan > 64)
+				fpc |= VOICE_TO_RIGHT;
+			else
+				fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT);
+	}
+
+	if (!(fpc & 0x30))
+		fpc |= 0x30;	/*
+				 * Ensure that at least one chn is enabled
+				 */
+	opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc);
+
+	/*
+	 * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+	 */
+
+	if (voice_mode == 4)
+	{
+		/*
+		 * Set Sound Characteristics
+		 */
+	
+		opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+		opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+		/*
+		 * Set Attack/Decay
+		 */
+		
+		opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+		opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+		/*
+		 * Set Sustain/Release
+		 */
+		
+		opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+		opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+		/*
+		 * Set Wave Select
+		 */
+		
+		opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+		opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+		/*
+		 * Set Feedback/Connection
+		 */
+		
+		fpc = instr->operators[OFFS_4OP + 10];
+		if (!(fpc & 0x30))
+			 fpc |= 0x30;	/*
+					 * Ensure that at least one chn is enabled
+					 */
+		opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+	}
+
+	devc->voc[voice].mode = voice_mode;
+	set_voice_volume(voice, volume, devc->voc[voice].volume);
+
+	freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000;
+
+	/*
+	 * Since the pitch bender may have been set before playing the note, we
+	 * have to calculate the bending now.
+	 */
+
+	freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
+	devc->voc[voice].current_freq = freq;
+
+	freq_to_fnum(freq, &block, &fnum);
+
+	/*
+	 * Play note
+	 */
+
+	data = fnum & 0xff;	/*
+				 * Least significant bits of fnumber
+				 */
+	opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+	data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+		 devc->voc[voice].keyon_byte = data;
+	opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+	if (voice_mode == 4)
+		opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+
+	return 0;
+}
+
+static void freq_to_fnum    (int freq, int *block, int *fnum)
+{
+	int f, octave;
+
+	/*
+	 * Converts the note frequency to block and fnum values for the FM chip
+	 */
+	/*
+	 * First try to compute the block -value (octave) where the note belongs
+	 */
+
+	f = freq;
+
+	octave = 5;
+
+	if (f == 0)
+		octave = 0;
+	else if (f < 261)
+	{
+		while (f < 261)
+		{
+			octave--;
+			f <<= 1;
+		}
+	}
+	else if (f > 493)
+	{
+		while (f > 493)
+		{
+			 octave++;
+			 f >>= 1;
+		}
+	}
+
+	if (octave > 7)
+		octave = 7;
+
+	*fnum = freq * (1 << (20 - octave)) / 49716;
+	*block = octave;
+}
+
+static void opl3_command    (int io_addr, unsigned int addr, unsigned int val)
+{
+	 int i;
+
+	/*
+	 * The original 2-OP synth requires a quite long delay after writing to a
+	 * register. The OPL-3 survives with just two INBs
+	 */
+
+	outb(((unsigned char) (addr & 0xff)), io_addr);
+
+	if (devc->model != 2)
+		udelay(10);
+	else
+		for (i = 0; i < 2; i++)
+			inb(io_addr);
+
+	outb(((unsigned char) (val & 0xff)), io_addr + 1);
+
+	if (devc->model != 2)
+		udelay(30);
+	else
+		for (i = 0; i < 2; i++)
+			inb(io_addr);
+}
+
+static void opl3_reset(int devno)
+{
+	int i;
+
+	for (i = 0; i < 18; i++)
+		devc->lv_map[i] = i;
+
+	for (i = 0; i < devc->nr_voice; i++)
+	{
+		opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+			KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);
+
+		opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+			KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);
+
+		if (pv_map[devc->lv_map[i]].voice_mode == 4)
+		{
+			opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+				KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);
+
+			opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+				KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
+		}
+
+		opl3_kill_note(devno, i, 0, 64);
+	}
+
+	if (devc->model == 2)
+	{
+		devc->v_alloc->max_voice = devc->nr_voice = 18;
+
+		for (i = 0; i < 18; i++)
+			pv_map[i].voice_mode = 2;
+
+	}
+}
+
+static int opl3_open(int dev, int mode)
+{
+	int i;
+
+	if (devc->busy)
+		return -EBUSY;
+	devc->busy = 1;
+
+	devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
+	devc->v_alloc->timestamp = 0;
+
+	for (i = 0; i < 18; i++)
+	{
+		devc->v_alloc->map[i] = 0;
+		devc->v_alloc->alloc_times[i] = 0;
+	}
+
+	devc->cmask = 0x00;	/*
+				 * Just 2 OP mode
+				 */
+	if (devc->model == 2)
+		opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
+	return 0;
+}
+
+static void opl3_close(int dev)
+{
+	devc->busy = 0;
+	devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
+
+	devc->fm_info.nr_drums = 0;
+	devc->fm_info.perc_mode = 0;
+
+	opl3_reset(dev);
+}
+
+static void opl3_hw_control(int dev, unsigned char *event)
+{
+}
+
+static int opl3_load_patch(int dev, int format, const char *addr,
+		int offs, int count, int pmgr_flag)
+{
+	struct sbi_instrument ins;
+
+	if (count <sizeof(ins))
+	{
+		printk(KERN_WARNING "FM Error: Patch record too short\n");
+		return -EINVAL;
+	}
+
+	if(copy_from_user(&((char *) &ins)[offs], &(addr)[offs], sizeof(ins) - offs))
+		return -EFAULT;
+
+	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+	{
+		printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
+		return -EINVAL;
+	}
+	ins.key = format;
+
+	return store_instr(ins.channel, &ins);
+}
+
+static void opl3_panning(int dev, int voice, int value)
+{
+	devc->voc[voice].panning = value;
+}
+
+static void opl3_volume_method(int dev, int mode)
+{
+}
+
+#define SET_VIBRATO(cell) { \
+	tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+	if (pressure > 110) \
+		tmp |= 0x40;		/* Vibrato on */ \
+	opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+
+static void opl3_aftertouch(int dev, int voice, int pressure)
+{
+	int tmp;
+	struct sbi_instrument *instr;
+	struct physical_voice_info *map;
+
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	map = &pv_map[devc->lv_map[voice]];
+
+	DEB(printk("Aftertouch %d\n", voice));
+
+	if (map->voice_mode == 0)
+		return;
+
+	/*
+	 * Adjust the amount of vibrato depending the pressure
+	 */
+
+	instr = devc->act_i[voice];
+
+	if (!instr)
+		instr = &devc->i_map[0];
+
+	if (devc->voc[voice].mode == 4)
+	{
+		int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+		switch (connection)
+		{
+			case 0:
+				SET_VIBRATO(4);
+				break;
+
+			case 1:
+				SET_VIBRATO(2);
+				SET_VIBRATO(4);
+				break;
+
+			case 2:
+				SET_VIBRATO(1);
+				SET_VIBRATO(4);
+				break;
+
+			case 3:
+				SET_VIBRATO(1);
+				SET_VIBRATO(3);
+				SET_VIBRATO(4);
+				break;
+
+		}
+		/*
+		 * Not implemented yet
+		 */
+	}
+	else
+	{
+		SET_VIBRATO(1);
+
+		if ((instr->operators[10] & 0x01))	/*
+							 * Additive synthesis
+							 */
+			SET_VIBRATO(2);
+	}
+}
+
+#undef SET_VIBRATO
+
+static void bend_pitch(int dev, int voice, int value)
+{
+	unsigned char data;
+	int block, fnum, freq;
+	struct physical_voice_info *map;
+
+	map = &pv_map[devc->lv_map[voice]];
+
+	if (map->voice_mode == 0)
+		return;
+
+	devc->voc[voice].bender = value;
+	if (!value)
+		return;
+	if (!(devc->voc[voice].keyon_byte & 0x20))
+		return;	/*
+			 * Not keyed on
+			 */
+
+	freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
+	devc->voc[voice].current_freq = freq;
+
+	freq_to_fnum(freq, &block, &fnum);
+
+	data = fnum & 0xff;	/*
+				 * Least significant bits of fnumber
+				 */
+	opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+	data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+	devc->voc[voice].keyon_byte = data;
+	opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+}
+
+static void opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	switch (ctrl_num)
+	{
+		case CTRL_PITCH_BENDER:
+			bend_pitch(dev, voice, value);
+			break;
+
+		case CTRL_PITCH_BENDER_RANGE:
+			devc->voc[voice].bender_range = value;
+			break;
+
+		case CTL_MAIN_VOLUME:
+			devc->voc[voice].volume = value / 128;
+			break;
+
+		case CTL_PAN:
+			devc->voc[voice].panning = (value * 2) - 128;
+			break;
+	}
+}
+
+static void opl3_bender(int dev, int voice, int value)
+{
+	if (voice < 0 || voice >= devc->nr_voice)
+		return;
+
+	bend_pitch(dev, voice, value - 8192);
+}
+
+static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+	int i, p, best, first, avail, best_time = 0x7fffffff;
+	struct sbi_instrument *instr;
+	int is4op;
+	int instr_no;
+
+	if (chn < 0 || chn > 15)
+		instr_no = 0;
+	else
+		instr_no = devc->chn_info[chn].pgm_num;
+
+	instr = &devc->i_map[instr_no];
+	if (instr->channel < 0 ||	/* Instrument not loaded */
+		devc->nr_voice != 12)	/* Not in 4 OP mode */
+		is4op = 0;
+	else if (devc->nr_voice == 12)	/* 4 OP mode */
+		is4op = (instr->key == OPL3_PATCH);
+	else
+		is4op = 0;
+
+	if (is4op)
+	{
+		first = p = 0;
+		avail = 6;
+	}
+	else
+	{
+		if (devc->nr_voice == 12)	/* 4 OP mode. Use the '2 OP only' operators first */
+			first = p = 6;
+		else
+			first = p = 0;
+		avail = devc->nr_voice;
+	}
+
+	/*
+	 *    Now try to find a free voice
+	 */
+	best = first;
+
+	for (i = 0; i < avail; i++)
+	{
+		if (alloc->map[p] == 0)
+		{
+			return p;
+		}
+		if (alloc->alloc_times[p] < best_time)		/* Find oldest playing note */
+		{
+			best_time = alloc->alloc_times[p];
+			best = p;
+		}
+		p = (p + 1) % avail;
+	}
+
+	/*
+	 *    Insert some kind of priority mechanism here.
+	 */
+
+	if (best < 0)
+		best = 0;
+	if (best > devc->nr_voice)
+		best -= devc->nr_voice;
+
+	return best;	/* All devc->voc in use. Select the first one. */
+}
+
+static void opl3_setup_voice(int dev, int voice, int chn)
+{
+	struct channel_info *info =
+	&synth_devs[dev]->chn_info[chn];
+
+	opl3_set_instr(dev, voice, info->pgm_num);
+
+	devc->voc[voice].bender = 0;
+	devc->voc[voice].bender_range = info->bender_range;
+	devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME];
+	devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
+}
+
+static struct synth_operations opl3_operations =
+{
+	owner:		THIS_MODULE,
+	id:		"OPL",
+	info:		NULL,
+	midi_dev:	0,
+	synth_type:	SYNTH_TYPE_FM,
+	synth_subtype:	FM_TYPE_ADLIB,
+	open:		opl3_open,
+	close:		opl3_close,
+	ioctl:		opl3_ioctl,
+	kill_note:	opl3_kill_note,
+	start_note:	opl3_start_note,
+	set_instr:	opl3_set_instr,
+	reset:		opl3_reset,
+	hw_control:	opl3_hw_control,
+	load_patch:	opl3_load_patch,
+	aftertouch:	opl3_aftertouch,
+	controller:	opl3_controller,
+	panning:	opl3_panning,
+	volume_method:	opl3_volume_method,
+	bender:		opl3_bender,
+	alloc_voice:	opl3_alloc_voice,
+	setup_voice:	opl3_setup_voice
+};
+
+int opl3_init(int ioaddr, int *osp, struct module *owner)
+{
+	int i;
+	int me;
+
+	if (devc == NULL)
+	{
+		printk(KERN_ERR "opl3: Device control structure not initialized.\n");
+		return -1;
+	}
+
+	if ((me = sound_alloc_synthdev()) == -1)
+	{
+		printk(KERN_WARNING "opl3: Too many synthesizers\n");
+		return -1;
+	}
+
+	devc->nr_voice = 9;
+
+	devc->fm_info.device = 0;
+	devc->fm_info.synth_type = SYNTH_TYPE_FM;
+	devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
+	devc->fm_info.perc_mode = 0;
+	devc->fm_info.nr_voices = 9;
+	devc->fm_info.nr_drums = 0;
+	devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
+	devc->fm_info.capabilities = 0;
+	devc->left_io = ioaddr;
+	devc->right_io = ioaddr + 2;
+
+	if (detected_model <= 2)
+		devc->model = 1;
+	else
+	{
+		devc->model = 2;
+		if (detected_model == 4)
+			devc->is_opl4 = 1;
+	}
+
+	opl3_operations.info = &devc->fm_info;
+
+	synth_devs[me] = &opl3_operations;
+
+	if (owner)
+		synth_devs[me]->owner = owner;
+	
+	sequencer_init();
+	devc->v_alloc = &opl3_operations.alloc;
+	devc->chn_info = &opl3_operations.chn_info[0];
+
+	if (devc->model == 2)
+	{
+		if (devc->is_opl4) 
+			strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM");
+		else 
+			strcpy(devc->fm_info.name, "Yamaha OPL3");
+
+		devc->v_alloc->max_voice = devc->nr_voice = 18;
+		devc->fm_info.nr_drums = 0;
+		devc->fm_info.synth_subtype = FM_TYPE_OPL3;
+		devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
+
+		for (i = 0; i < 18; i++)
+		{
+			if (pv_map[i].ioaddr == USE_LEFT)
+				pv_map[i].ioaddr = devc->left_io;
+			else
+				pv_map[i].ioaddr = devc->right_io;
+		}
+		opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
+		opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
+	}
+	else
+	{
+		strcpy(devc->fm_info.name, "Yamaha OPL2");
+		devc->v_alloc->max_voice = devc->nr_voice = 9;
+		devc->fm_info.nr_drums = 0;
+
+		for (i = 0; i < 18; i++)
+			pv_map[i].ioaddr = devc->left_io;
+	};
+	conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1);
+
+	for (i = 0; i < SBFM_MAXINSTR; i++)
+		devc->i_map[i].channel = -1;
+
+	return me;
+}
+
+EXPORT_SYMBOL(opl3_init);
+EXPORT_SYMBOL(opl3_detect);
+
+static int me;
+
+static int io = -1;
+
+MODULE_PARM(io, "i");
+
+static int __init init_opl3 (void)
+{
+	printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n");
+
+	if (io != -1)	/* User loading pure OPL3 module */
+	{
+		if (!opl3_detect(io, NULL))
+		{
+			return -ENODEV;
+		}
+
+		me = opl3_init(io, NULL, THIS_MODULE);
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_opl3(void)
+{
+	if (devc && io != -1)
+	{
+		if (devc->base) {
+			release_region(devc->base,4);
+			if (devc->is_opl4)
+				release_region(devc->base - 8, 2);
+		}
+		kfree(devc);
+		devc = NULL;
+		sound_unload_synthdev(me);
+	}
+}
+
+module_init(init_opl3);
+module_exit(cleanup_opl3);
+
+#ifndef MODULE
+static int __init setup_opl3(char *str)
+{
+        /* io  */
+	int ints[2];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+
+	return 1;
+}
+
+__setup("opl3=", setup_opl3);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/opl3.h linux-2.4.19-pre5-mjc/sound/oss/opl3.h
--- linux/sound/oss/opl3.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/opl3.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,11 @@
+/*
+ *	opl3.h
+ *
+ * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
+ *
+ */
+
+int opl3_detect (int ioaddr, int *osp);
+int opl3_init(int ioaddr, int *osp, struct module *owner);
+
+void enable_opl3_mode(int left, int right, int both);
diff -Nru linux/sound/oss/opl3_hw.h linux-2.4.19-pre5-mjc/sound/oss/opl3_hw.h
--- linux/sound/oss/opl3_hw.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/opl3_hw.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,246 @@
+/*
+ *	opl3_hw.h	- Definitions of the OPL-3 registers
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ *	The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ *	of the right side.
+ *
+ *	Another special register at the right side is at offset 4. It contains
+ *	a bit mask defining which voices are used as 4 OP voices.
+ *
+ *	The percussive mode is implemented in the left side only.
+ *
+ *	With the above exceptions the both sides can be operated independently.
+ *	
+ *	A 4 OP voice can be created by setting the corresponding
+ *	bit at offset 4 of the right side.
+ *
+ *	For example setting the rightmost bit (0x01) changes the
+ *	first voice on the right side to the 4 OP mode. The fourth
+ *	voice is made inaccessible.
+ *
+ *	If a voice is set to the 2 OP mode, it works like 2 OP modes
+ *	of the original YM3812 (AdLib). In addition the voice can 
+ *	be connected the left, right or both stereo channels. It can
+ *	even be left unconnected. This works with 4 OP voices also.
+ *
+ *	The stereo connection bits are located in the FEEDBACK_CONNECTION
+ *	register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ *	in the second half of the voice.
+ */
+
+/*
+ *	Register numbers for the global registers
+ */
+
+#define TEST_REGISTER				0x01
+#define   ENABLE_WAVE_SELECT		0x20
+
+#define TIMER1_REGISTER				0x02
+#define TIMER2_REGISTER				0x03
+#define TIMER_CONTROL_REGISTER			0x04	/* Left side */
+#define   IRQ_RESET			0x80
+#define   TIMER1_MASK			0x40
+#define   TIMER2_MASK			0x20
+#define   TIMER1_START			0x01
+#define   TIMER2_START			0x02
+
+#define CONNECTION_SELECT_REGISTER		0x04	/* Right side */
+#define   RIGHT_4OP_0			0x01
+#define   RIGHT_4OP_1			0x02
+#define   RIGHT_4OP_2			0x04
+#define   LEFT_4OP_0			0x08
+#define   LEFT_4OP_1			0x10
+#define   LEFT_4OP_2			0x20
+
+#define OPL3_MODE_REGISTER			0x05	/* Right side */
+#define   OPL3_ENABLE			0x01
+#define   OPL4_ENABLE			0x02
+
+#define KBD_SPLIT_REGISTER			0x08	/* Left side */
+#define   COMPOSITE_SINE_WAVE_MODE	0x80		/* Don't use with OPL-3? */
+#define   KEYBOARD_SPLIT		0x40
+
+#define PERCOSSION_REGISTER			0xbd	/* Left side only */
+#define   TREMOLO_DEPTH			0x80
+#define   VIBRATO_DEPTH			0x40
+#define	  PERCOSSION_ENABLE		0x20
+#define   BASSDRUM_ON			0x10
+#define   SNAREDRUM_ON			0x08
+#define   TOMTOM_ON			0x04
+#define   CYMBAL_ON			0x02
+#define   HIHAT_ON			0x01
+
+/*
+ *	Offsets to the register banks for operators. To get the
+ *	register number just add the operator offset to the bank offset
+ *
+ *	AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+#define AM_VIB					0x20
+#define   TREMOLO_ON			0x80
+#define   VIBRATO_ON			0x40
+#define   SUSTAIN_ON			0x20
+#define   KSR				0x10 	/* Key scaling rate */
+#define   MULTIPLE_MASK		0x0f	/* Frequency multiplier */
+
+ /*
+  *	KSL/Total level (0x40 to 0x55)
+  */
+#define KSL_LEVEL				0x40
+#define   KSL_MASK			0xc0	/* Envelope scaling bits */
+#define   TOTAL_LEVEL_MASK		0x3f	/* Strength (volume) of OP */
+
+/*
+ *	Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY				0x60
+#define   ATTACK_MASK			0xf0
+#define   DECAY_MASK			0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE				0x80
+#define   SUSTAIN_MASK			0xf0
+#define   RELEASE_MASK			0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT			0xe0
+
+/*
+ *	Offsets to the register banks for voices. Just add to the
+ *	voice number to get the register number.
+ *
+ *	F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW				0xa0
+
+/*
+ *	F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK					0xb0
+#define	  KEYON_BIT				0x20
+#define	  BLOCKNUM_MASK				0x1c
+#define   FNUM_HIGH_MASK			0x03
+
+/*
+ *	Feedback / Connection (0xc0 to 0xc8)
+ *
+ *	These registers have two new bits when the OPL-3 mode
+ *	is selected. These bits controls connecting the voice
+ *	to the stereo channels. For 4 OP voices this bit is
+ *	defined in the second half of the voice (add 3 to the
+ *	register offset).
+ *
+ *	For 4 OP voices the connection bit is used in the
+ *	both halves (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION				0xc0
+#define   FEEDBACK_MASK				0x0e	/* Valid just for 1st OP of a voice */
+#define   CONNECTION_BIT			0x01
+/*
+ *	In the 4 OP mode there is four possible configurations how the
+ *	operators can be connected together (in 2 OP modes there is just
+ *	AM or FM). The 4 OP connection mode is defined by the rightmost
+ *	bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
+ *
+ *	First half	Second half	Mode
+ *
+ *					 +---+
+ *					 v   |
+ *	0		0		>+-1-+--2--3--4-->
+ *
+ *
+ *					
+ *					 +---+
+ *					 |   |
+ *	0		1		>+-1-+--2-+
+ *						  |->
+ *					>--3----4-+
+ *					
+ *					 +---+
+ *					 |   |
+ *	1		0		>+-1-+-----+
+ *						   |->
+ *					>--2--3--4-+
+ *
+ *					 +---+
+ *					 |   |
+ *	1		1		>+-1-+--+
+ *						|
+ *					>--2--3-+->
+ *						|
+ *					>--4----+
+ */
+#define   STEREO_BITS				0x30	/* OPL-3 only */
+#define     VOICE_TO_LEFT		0x10
+#define     VOICE_TO_RIGHT		0x20
+
+/*
+ * 	Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+		unsigned char voice_num;
+		unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+		unsigned short ioaddr; /* I/O port (left or right side) */
+		unsigned char op[4]; /* Operator offsets */
+	};
+
+/*
+ *	There is 18 possible 2 OP voices
+ *	(9 in the left and 9 in the right).
+ *	The first OP is the modulator and 2nd is the carrier.
+ *
+ *	The first three voices in the both sides may be connected
+ *	with another voice to a 4 OP voice. For example voice 0
+ *	can be connected with voice 3. The operators of voice 3 are
+ *	used as operators 3 and 4 of the new 4 OP voice.
+ *	In this case the 2 OP voice number 0 is the 'first half' and
+ *	voice 3 is the second.
+ */
+
+#define USE_LEFT	0
+#define USE_RIGHT	1
+
+static struct physical_voice_info pv_map[18] =
+{
+/*       No Mode Side		OP1	OP2	OP3   OP4	*/
+/*	---------------------------------------------------	*/
+	{ 0,  2, USE_LEFT,	{0x00,	0x03,	0x08, 0x0b}},
+	{ 1,  2, USE_LEFT,	{0x01,	0x04,	0x09, 0x0c}},
+	{ 2,  2, USE_LEFT,	{0x02,	0x05,	0x0a, 0x0d}},
+
+	{ 3,  2, USE_LEFT,	{0x08,	0x0b,	0x00, 0x00}},
+	{ 4,  2, USE_LEFT,	{0x09,	0x0c,	0x00, 0x00}},
+	{ 5,  2, USE_LEFT,	{0x0a,	0x0d,	0x00, 0x00}},
+
+	{ 6,  2, USE_LEFT,	{0x10,	0x13,	0x00, 0x00}}, /* Used by percussive voices */
+	{ 7,  2, USE_LEFT,	{0x11,	0x14,	0x00, 0x00}}, /* if the percussive mode */
+	{ 8,  2, USE_LEFT,	{0x12,	0x15,	0x00, 0x00}}, /* is selected */
+
+	{ 0,  2, USE_RIGHT,	{0x00,	0x03,	0x08, 0x0b}},
+	{ 1,  2, USE_RIGHT,	{0x01,	0x04,	0x09, 0x0c}},
+	{ 2,  2, USE_RIGHT,	{0x02,	0x05,	0x0a, 0x0d}},
+
+	{ 3,  2, USE_RIGHT,	{0x08,	0x0b,	0x00, 0x00}},
+	{ 4,  2, USE_RIGHT,	{0x09,	0x0c,	0x00, 0x00}},
+	{ 5,  2, USE_RIGHT,	{0x0a,	0x0d,	0x00, 0x00}},
+
+	{ 6,  2, USE_RIGHT,	{0x10,	0x13,	0x00, 0x00}},
+	{ 7,  2, USE_RIGHT,	{0x11,	0x14,	0x00, 0x00}},
+	{ 8,  2, USE_RIGHT,	{0x12,	0x15,	0x00, 0x00}}
+};
+/*
+ *	DMA buffer calls
+ */
diff -Nru linux/sound/oss/opl3sa.c linux-2.4.19-pre5-mjc/sound/oss/opl3sa.c
--- linux/sound/oss/opl3sa.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/opl3sa.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,337 @@
+/*
+ * sound/opl3sa.c
+ *
+ * Low level driver for Yamaha YMF701B aka OPL3-SA chip
+ * 
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ *	Alan Cox		Modularisation
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	got rid of attach_uart401
+ *
+ * FIXME:
+ * 	Check for install of mpu etc is wrong, should check result of the mss stuff
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#undef  SB_OK
+
+#include "sound_config.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+#ifdef SB_OK
+#include "sb.h"
+static int sb_initialized = 0;
+#endif
+
+static int kilroy_was_here = 0;	/* Don't detect twice */
+static int mpu_initialized = 0;
+
+static int *opl3sa_osp = NULL;
+
+static unsigned char opl3sa_read(int addr)
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+	save_flags(flags);
+	cli();
+	outb((0x1d), 0xf86);	/* password */
+	outb(((unsigned char) addr), 0xf86);	/* address */
+	tmp = inb(0xf87);	/* data */
+	restore_flags(flags);
+
+	return tmp;
+}
+
+static void opl3sa_write(int addr, int data)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb((0x1d), 0xf86);	/* password */
+	outb(((unsigned char) addr), 0xf86);	/* address */
+	outb(((unsigned char) data), 0xf87);	/* data */
+	restore_flags(flags);
+}
+
+static int __init opl3sa_detect(void)
+{
+	int tmp;
+
+	if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04)
+	{
+		DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01)));
+		/* return 0; */
+	}
+
+	/*
+	 * Check that the password feature has any effect
+	 */
+	
+	if (inb(0xf87) == tmp)
+	{
+		DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87)));
+		return 0;
+	}
+	tmp = (opl3sa_read(0x04) & 0xe0) >> 5;
+
+	if (tmp != 0 && tmp != 1)
+	{
+		DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp));
+		return 0;
+	}
+	DDB(printk("OPL3-SA mode %x detected\n", tmp));
+
+	opl3sa_write(0x01, 0x00);	/* Disable MSS */
+	opl3sa_write(0x02, 0x00);	/* Disable SB */
+	opl3sa_write(0x03, 0x00);	/* Disable MPU */
+
+	return 1;
+}
+
+/*
+ *    Probe and attach routines for the Windows Sound System mode of
+ *     OPL3-SA
+ */
+
+static int __init probe_opl3sa_wss(struct address_info *hw_config)
+{
+	int ret;
+	unsigned char tmp = 0x24;	/* WSS enable */
+
+	if (check_region(0xf86, 2))	/* Control port is busy */
+		return 0;
+	/*
+	 * Check if the IO port returns valid signature. The original MS Sound
+	 * system returns 0x04 while some cards (OPL3-SA for example)
+	 * return 0x00.
+	 */
+
+	if (check_region(hw_config->io_base, 8))
+	{
+		printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base);
+		return 0;
+	}
+	opl3sa_osp = hw_config->osp;
+
+	if (!opl3sa_detect())
+	{
+		printk(KERN_ERR "OSS: OPL3-SA chip not found\n");
+		return 0;
+	}
+	
+	switch (hw_config->io_base)
+	{
+		case 0x530:
+			tmp |= 0x00;
+			break;
+		case 0xe80:
+			tmp |= 0x08;
+			break;
+		case 0xf40:
+			tmp |= 0x10;
+			break;
+		case 0x604:
+			tmp |= 0x18;
+			break;
+		default:
+			printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base);
+		  return 0;
+	}
+
+	opl3sa_write(0x01, tmp);	/* WSS setup register */
+	kilroy_was_here = 1;
+
+	ret = probe_ms_sound(hw_config);
+	if (ret)
+		request_region(0xf86, 2, "OPL3-SA");
+
+	return ret;
+}
+
+static void __init attach_opl3sa_wss(struct address_info *hw_config)
+{
+	int nm = num_mixers;
+
+	/* FIXME */
+	attach_ms_sound(hw_config, THIS_MODULE);
+	if (num_mixers > nm)	/* A mixer was installed */
+	{
+		AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD);
+		AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH);
+		AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
+	}
+}
+
+
+static int __init probe_opl3sa_mpu(struct address_info *hw_config)
+{
+	unsigned char conf;
+	static signed char irq_bits[] = {
+		-1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4
+	};
+
+	if (!kilroy_was_here)
+		return 0;	/* OPL3-SA has not been detected earlier */
+
+	if (mpu_initialized)
+	{
+		DDB(printk("OPL3-SA: MPU mode already initialized\n"));
+		return 0;
+	}
+	if (hw_config->irq > 10)
+	{
+		printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	if (irq_bits[hw_config->irq] == -1)
+	{
+		printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	switch (hw_config->io_base)
+	{
+		case 0x330:
+			conf = 0x00;
+			break;
+		case 0x332:
+			conf = 0x20;
+			break;
+		case 0x334:
+			conf = 0x40;
+			break;
+		case 0x300:
+			conf = 0x60;
+			break;
+		default:
+			return 0;	/* Invalid port */
+	}
+
+	conf |= 0x83;		/* MPU & OPL3 (synth) & game port enable */
+	conf |= irq_bits[hw_config->irq] << 2;
+
+	opl3sa_write(0x03, conf);
+
+	mpu_initialized = 1;
+	hw_config->name = "OPL3-SA (MPU401)";
+
+	return probe_uart401(hw_config, THIS_MODULE);
+}
+
+static void __exit unload_opl3sa_wss(struct address_info *hw_config)
+{
+	int dma2 = hw_config->dma2;
+
+	if (dma2 == -1)
+		dma2 = hw_config->dma;
+
+	release_region(0xf86, 2);
+	release_region(hw_config->io_base, 4);
+
+	ad1848_unload(hw_config->io_base + 4,
+		      hw_config->irq,
+		      hw_config->dma,
+		      dma2,
+		      0);
+	sound_unload_audiodev(hw_config->slots[0]);
+}
+
+static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config)
+{
+	unload_uart401(hw_config);
+}
+
+#ifdef SB_OK
+static inline void __exit unload_opl3sa_sb(struct address_info *hw_config)
+{
+	sb_dsp_unload(hw_config);
+}
+#endif
+
+static int found_mpu;
+
+static struct address_info cfg;
+static struct address_info cfg_mpu;
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma2	= -1;
+static int __initdata mpu_io	= -1;
+static int __initdata mpu_irq	= -1;
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma2,"i");
+MODULE_PARM(mpu_io,"i");
+MODULE_PARM(mpu_irq,"i");
+
+static int __init init_opl3sa(void)
+{
+	if (io == -1 || irq == -1 || dma == -1) {
+		printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n");
+		return -EINVAL;
+	}
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma2;
+	
+	cfg_mpu.io_base = mpu_io;
+	cfg_mpu.irq = mpu_irq;
+
+	if (probe_opl3sa_wss(&cfg) == 0)
+		return -ENODEV;
+
+	found_mpu=probe_opl3sa_mpu(&cfg_mpu);
+
+	attach_opl3sa_wss(&cfg);
+	return 0;
+}
+
+static void __exit cleanup_opl3sa(void)
+{
+	if(found_mpu)
+		unload_opl3sa_mpu(&cfg_mpu);
+	unload_opl3sa_wss(&cfg);
+}
+
+module_init(init_opl3sa);
+module_exit(cleanup_opl3sa);
+
+#ifndef MODULE
+static int __init setup_opl3sa(char *str)
+{
+	/* io, irq, dma, dma2, mpu_io, mpu_irq */
+	int ints[7];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	mpu_io	= ints[5];
+	mpu_irq	= ints[6];
+
+	return 1;
+}
+
+__setup("opl3sa=", setup_opl3sa);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/opl3sa2.c linux-2.4.19-pre5-mjc/sound/oss/opl3sa2.c
--- linux/sound/oss/opl3sa2.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/opl3sa2.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1190 @@
+/*
+ * sound/opl3sa2.c
+ *
+ * A low level driver for Yamaha OPL3-SA2 and SA3 cards.
+ * NOTE: All traces of the name OPL3-SAx have now (December 2000) been
+ *       removed from the driver code, as an email exchange with Yamaha
+ *       provided the information that the YMF-719 is indeed just a
+ *       re-badged 715.
+ *
+ * Copyright 1998-2001 Scott Murray <scott@spiteful.org>
+ *
+ * Originally based on the CS4232 driver (in cs4232.c) by Hannu Savolainen
+ * and others.  Now incorporates code/ideas from pss.c, also by Hannu
+ * Savolainen.  Both of those files are distributed with the following
+ * license:
+ *
+ * "Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ *  OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ *  Version 2 (June 1991). See the "COPYING" file distributed with this software
+ *  for more info."
+ *
+ * As such, in accordance with the above license, this file, opl3sa2.c, is
+ * distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 (June 1991).
+ * See the "COPYING" file distributed with this software for more information.
+ *
+ * Change History
+ * --------------
+ * Scott Murray            Original driver (Jun 14, 1998)
+ * Paul J.Y. Lahaie        Changed probing / attach code order
+ * Scott Murray            Added mixer support (Dec 03, 1998)
+ * Scott Murray            Changed detection code to be more forgiving,
+ *                         added force option as last resort,
+ *                         fixed ioctl return values. (Dec 30, 1998)
+ * Scott Murray            Simpler detection code should work all the time now
+ *                         (with thanks to Ben Hutchings for the heuristic),
+ *                         removed now unnecessary force option. (Jan 5, 1999)
+ * Christoph Hellwig	   Adapted to module_init/module_exit (Mar 4, 2000)
+ * Scott Murray            Reworked SA2 versus SA3 mixer code, updated chipset
+ *                         version detection code (again!). (Dec 5, 2000)
+ * Scott Murray            Adjusted master volume mixer scaling. (Dec 6, 2000)
+ * Scott Murray            Based on a patch by Joel Yliluoma (aka Bisqwit),
+ *                         integrated wide mixer and adjusted mic, bass, treble
+ *                         scaling. (Dec 6, 2000)
+ * Scott Murray            Based on a patch by Peter Englmaier, integrated
+ *                         ymode and loopback options. (Dec 6, 2000)
+ * Scott Murray            Inspired by a patch by Peter Englmaier, and based on
+ *                         what ALSA does, added initialization code for the
+ *                         default DMA and IRQ settings. (Dec 6, 2000)
+ * Scott Murray            Added some more checks to the card detection code,
+ *                         based on what ALSA does. (Dec 12, 2000)
+ * Scott Murray            Inspired by similar patches from John Fremlin,
+ *                         Jim Radford, Mike Rolig, and Ingmar Steen, added 2.4
+ *                         ISA PnP API support, mainly based on bits from
+ *                         sb_card.c and awe_wave.c. (Dec 12, 2000)
+ * Scott Murray            Some small cleanups to the init code output.
+ *                         (Jan 7, 2001)
+ * Zwane Mwaikambo	   Added PM support. (Dec 4 2001)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/isapnp.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include "sound_config.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+/* Useful control port indexes: */
+#define OPL3SA2_PM	     0x01
+#define OPL3SA2_SYS_CTRL     0x02
+#define OPL3SA2_IRQ_CONFIG   0x03
+#define OPL3SA2_DMA_CONFIG   0x06
+#define OPL3SA2_MASTER_LEFT  0x07
+#define OPL3SA2_MASTER_RIGHT 0x08
+#define OPL3SA2_MIC          0x09
+#define OPL3SA2_MISC         0x0A
+
+#define OPL3SA3_WIDE         0x14
+#define OPL3SA3_BASS         0x15
+#define OPL3SA3_TREBLE       0x16
+
+/* Useful constants: */
+#define DEFAULT_VOLUME 50
+#define DEFAULT_MIC    50
+#define DEFAULT_TIMBRE 0
+
+/* Power saving modes */
+#define OPL3SA2_PM_MODE1	0x05
+#define OPL3SA2_PM_MODE2	0x04
+#define OPL3SA2_PM_MODE3	0x03
+
+/* For checking against what the card returns: */
+#define VERSION_UNKNOWN 0
+#define VERSION_YMF711  1
+#define VERSION_YMF715  2
+#define VERSION_YMF715B 3
+#define VERSION_YMF715E 4
+/* also assuming that anything > 4 but <= 7 is a 715E */
+
+/* Chipset type constants for use below */
+#define CHIPSET_UNKNOWN -1
+#define CHIPSET_OPL3SA2 0
+#define CHIPSET_OPL3SA3 1
+
+#ifdef __ISAPNP__
+#define OPL3SA2_CARDS_MAX 4
+#else
+#define OPL3SA2_CARDS_MAX 1
+#endif
+
+/* This should be pretty obvious */
+static int opl3sa2_cards_num; /* = 0 */
+
+/* What's my version(s)? */
+static int chipset[OPL3SA2_CARDS_MAX] = { CHIPSET_UNKNOWN };
+
+/* Oh well, let's just cache the name(s) */
+static char chipset_name[OPL3SA2_CARDS_MAX][12];
+
+/* Where's my mixer(s)? */
+static int opl3sa2_mixer[OPL3SA2_CARDS_MAX] = { -1 };
+
+/* Bag o' mixer data */
+typedef struct opl3sa2_mixerdata_tag {
+	unsigned short cfg_port;
+	unsigned short padding;
+	unsigned char  reg;
+	unsigned int   in_suspend;
+	struct pm_dev  *pmdev;
+	unsigned int   card;
+	unsigned int   volume_l;
+	unsigned int   volume_r;
+	unsigned int   mic;
+	unsigned int   bass_l;
+	unsigned int   bass_r;
+	unsigned int   treble_l;
+	unsigned int   treble_r;
+	unsigned int   wide_l;
+	unsigned int   wide_r;
+} opl3sa2_mixerdata;
+static opl3sa2_mixerdata opl3sa2_data[OPL3SA2_CARDS_MAX];
+
+static struct address_info cfg[OPL3SA2_CARDS_MAX];
+static struct address_info cfg_mss[OPL3SA2_CARDS_MAX];
+static struct address_info cfg_mpu[OPL3SA2_CARDS_MAX];
+
+/* Our parameters */
+static int __initdata io	= -1;
+static int __initdata mss_io	= -1;
+static int __initdata mpu_io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma2	= -1;
+static int __initdata ymode	= -1;
+static int __initdata loopback	= -1;
+
+#ifdef __ISAPNP__
+/* PnP specific parameters */
+static int __initdata isapnp = 1;
+static int __initdata multiple = 1;
+
+/* PnP devices */
+struct pci_dev* opl3sa2_dev[OPL3SA2_CARDS_MAX];
+
+/* Whether said devices have been activated */
+static int opl3sa2_activated[OPL3SA2_CARDS_MAX];
+#else
+static int __initdata isapnp; /* = 0 */
+static int __initdata multiple; /* = 0 */
+#endif
+
+MODULE_DESCRIPTION("Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver).");
+MODULE_AUTHOR("Scott Murray <scott@spiteful.org>");
+MODULE_LICENSE("GPL");
+
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "Set I/O base of OPL3-SA2 or SA3 card (usually 0x370.  Address must be even and must be from 0x100 to 0xFFE)");
+
+MODULE_PARM(mss_io, "i");
+MODULE_PARM_DESC(mss_io, "Set MSS (audio) I/O base (0x530, 0xE80, or other. Address must end in 0 or 4 and must be from 0x530 to 0xF48)");
+
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM_DESC(mpu_io, "Set MIDI I/O base (0x330 or other. Address must be even and must be from 0x300 to 0x334)");
+
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(mss_irq, "Set MSS (audio) IRQ (5, 7, 9, 10, 11, 12)");
+
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "Set MSS (audio) first DMA channel (0, 1, 3)");
+
+MODULE_PARM(dma2, "i");
+MODULE_PARM_DESC(dma2, "Set MSS (audio) second DMA channel (0, 1, 3)");
+
+MODULE_PARM(ymode, "i");
+MODULE_PARM_DESC(ymode, "Set Yamaha 3D enhancement mode (0 = Desktop/Normal, 1 = Notebook PC (1), 2 = Notebook PC (2), 3 = Hi-Fi)");
+
+MODULE_PARM(loopback, "i");
+MODULE_PARM_DESC(loopback, "Set A/D input source. Useful for echo cancellation (0 = Mic Rch (default), 1 = Mono output loopback)");
+
+#ifdef __ISAPNP__
+MODULE_PARM(isapnp, "i");
+MODULE_PARM_DESC(isapnp, "When set to 0, ISA PnP support will be disabled");
+
+MODULE_PARM(multiple, "i");
+MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards");
+#endif
+
+
+/*
+ * Standard read and write functions
+*/
+
+static inline void opl3sa2_write(unsigned short port,
+				 unsigned char  index,
+				 unsigned char  data)
+{
+	outb_p(index, port);
+	outb(data, port + 1);
+}
+
+
+static inline void opl3sa2_read(unsigned short port,
+				unsigned char  index,
+				unsigned char* data)
+{
+	outb_p(index, port);
+	*data = inb(port + 1);
+}
+
+
+/*
+ * All of the mixer functions...
+ */
+
+static void opl3sa2_set_volume(opl3sa2_mixerdata* devc, int left, int right)
+{
+	static unsigned char scale[101] = {
+		0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e,
+		0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c,
+		0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09,
+		0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08,
+		0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+		0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+		0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+		0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
+		0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00
+	};
+	unsigned char vol;
+
+	vol = scale[left];
+
+	/* If level is zero, turn on mute */
+	if(!left)
+		vol |= 0x80;
+
+	opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_LEFT, vol);
+
+	vol = scale[right];
+
+	/* If level is zero, turn on mute */
+	if(!right)
+		vol |= 0x80;
+
+	opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_RIGHT, vol);
+}
+
+
+static void opl3sa2_set_mic(opl3sa2_mixerdata* devc, int level)
+{
+	unsigned char vol = 0x1F;
+
+	if((level >= 0) && (level <= 100))
+		vol = 0x1F - (unsigned char) (32 * level / 101);
+
+	/* If level is zero, turn on mute */
+	if(!level)
+		vol |= 0x80;
+
+	opl3sa2_write(devc->cfg_port, OPL3SA2_MIC, vol);
+}
+
+
+static void opl3sa3_set_bass(opl3sa2_mixerdata* devc, int left, int right)
+{
+	unsigned char bass;
+
+	bass = left ? ((unsigned char) (8 * left / 101)) : 0; 
+	bass |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4;
+
+	opl3sa2_write(devc->cfg_port, OPL3SA3_BASS, bass);
+}
+
+
+static void opl3sa3_set_treble(opl3sa2_mixerdata* devc, int left, int right)
+{	
+	unsigned char treble;
+
+	treble = left ? ((unsigned char) (8 * left / 101)) : 0; 
+	treble |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4;
+
+	opl3sa2_write(devc->cfg_port, OPL3SA3_TREBLE, treble);
+}
+
+
+static void opl3sa3_set_wide(opl3sa2_mixerdata* devc, int left, int right)
+{	
+	unsigned char wide;
+
+	wide = left ? ((unsigned char) (8 * left / 101)) : 0; 
+	wide |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4;
+
+	opl3sa2_write(devc->cfg_port, OPL3SA3_WIDE, wide);
+}
+
+
+static void opl3sa2_mixer_reset(opl3sa2_mixerdata* devc, int card)
+{
+	if(devc) {
+		opl3sa2_set_volume(devc, DEFAULT_VOLUME, DEFAULT_VOLUME);
+		devc->volume_l = devc->volume_r = DEFAULT_VOLUME;
+
+		opl3sa2_set_mic(devc, DEFAULT_MIC);
+		devc->mic = DEFAULT_MIC;
+
+		if(chipset[card] == CHIPSET_OPL3SA3) {
+			opl3sa3_set_bass(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE);
+			devc->bass_l = devc->bass_r = DEFAULT_TIMBRE;
+			opl3sa3_set_treble(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE);
+			devc->treble_l = devc->treble_r = DEFAULT_TIMBRE;
+		}
+	}
+}
+
+
+static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card)
+{
+	if (devc) {
+		opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r);
+		opl3sa2_set_mic(devc, devc->mic);
+
+		if (chipset[card] == CHIPSET_OPL3SA3) {
+			opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r);
+			opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r);
+		}
+	}
+}
+
+
+static inline void arg_to_vol_mono(unsigned int vol, int* value)
+{
+	int left;
+	
+	left = vol & 0x00ff;
+	if (left > 100)
+		left = 100;
+	*value = left;
+}
+
+
+static inline void arg_to_vol_stereo(unsigned int vol, int* aleft, int* aright)
+{
+	arg_to_vol_mono(vol, aleft);
+	arg_to_vol_mono(vol >> 8, aright);
+}
+
+
+static inline int ret_vol_mono(int vol)
+{
+	return ((vol << 8) | vol);
+}
+
+
+static inline int ret_vol_stereo(int left, int right)
+{
+	return ((right << 8) | left);
+}
+
+
+static int opl3sa2_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int cmdf = cmd & 0xff;
+
+	opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc;
+	
+	switch(cmdf) {
+		case SOUND_MIXER_VOLUME:
+		case SOUND_MIXER_MIC:
+		case SOUND_MIXER_DEVMASK:
+		case SOUND_MIXER_STEREODEVS: 
+		case SOUND_MIXER_RECMASK:
+		case SOUND_MIXER_RECSRC:
+		case SOUND_MIXER_CAPS: 
+			break;
+
+		default:
+			return -EINVAL;
+	}
+	
+	if(((cmd >> 8) & 0xff) != 'M')
+		return -EINVAL;
+		
+	if(_SIOC_DIR (cmd) & _SIOC_WRITE) {
+		switch (cmdf) {
+			case SOUND_MIXER_VOLUME:
+				arg_to_vol_stereo(*(unsigned int*)arg,
+						  &devc->volume_l, &devc->volume_r); 
+				opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r);
+				*(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r);
+				return 0;
+		  
+			case SOUND_MIXER_MIC:
+				arg_to_vol_mono(*(unsigned int*)arg, &devc->mic);
+				opl3sa2_set_mic(devc, devc->mic);
+				*(int*)arg = ret_vol_mono(devc->mic);
+				return 0;
+
+			default:
+				return -EINVAL;
+		}
+	}
+	else {
+		/*
+		 * Return parameters
+		 */
+		switch (cmdf) {
+			case SOUND_MIXER_DEVMASK:
+				*(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+				return 0;
+		  
+			case SOUND_MIXER_STEREODEVS:
+				*(int*)arg = SOUND_MASK_VOLUME;
+				return 0;
+		  
+			case SOUND_MIXER_RECMASK:
+				/* No recording devices */
+				return (*(int*)arg = 0);
+
+			case SOUND_MIXER_CAPS:
+				*(int*)arg = SOUND_CAP_EXCL_INPUT;
+				return 0;
+
+			case SOUND_MIXER_RECSRC:
+				/* No recording source */
+				return (*(int*)arg = 0);
+
+			case SOUND_MIXER_VOLUME:
+				*(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r);
+				return 0;
+			  
+			case SOUND_MIXER_MIC:
+				*(int*)arg = ret_vol_mono(devc->mic);
+				return 0;
+
+			default:
+				return -EINVAL;
+		}
+	}
+}
+/* opl3sa2_mixer_ioctl end */
+
+
+static int opl3sa3_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int cmdf = cmd & 0xff;
+
+	opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc;
+
+	switch(cmdf) {
+		case SOUND_MIXER_BASS:
+		case SOUND_MIXER_TREBLE:
+		case SOUND_MIXER_DIGITAL1:
+		case SOUND_MIXER_DEVMASK:
+		case SOUND_MIXER_STEREODEVS: 
+			break;
+
+		default:
+			return opl3sa2_mixer_ioctl(dev, cmd, arg);
+	}
+
+	if(((cmd >> 8) & 0xff) != 'M')
+		return -EINVAL;
+		
+	if(_SIOC_DIR (cmd) & _SIOC_WRITE) {
+		switch (cmdf) {
+			case SOUND_MIXER_BASS:
+				arg_to_vol_stereo(*(unsigned int*)arg,
+						  &devc->bass_l, &devc->bass_r); 
+				opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r);
+				*(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r);
+				return 0;
+		  
+			case SOUND_MIXER_TREBLE:
+				arg_to_vol_stereo(*(unsigned int*)arg,
+						  &devc->treble_l, &devc->treble_r); 
+				opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r);
+				*(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r);
+				return 0;
+
+			case SOUND_MIXER_DIGITAL1:
+				arg_to_vol_stereo(*(unsigned int*)arg,
+						  &devc->wide_l, &devc->wide_r); 
+				opl3sa3_set_wide(devc, devc->wide_l, devc->wide_r);
+				*(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r);
+				return 0;
+
+			default:
+				return -EINVAL;
+		}
+	}
+	else			
+	{
+		/*
+		 * Return parameters
+		 */
+		switch (cmdf) {
+			case SOUND_MIXER_DEVMASK:
+				*(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC |
+					      SOUND_MASK_BASS | SOUND_MASK_TREBLE |
+					      SOUND_MASK_DIGITAL1);
+				return 0;
+		  
+			case SOUND_MIXER_STEREODEVS:
+				*(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_BASS |
+					      SOUND_MASK_TREBLE | SOUND_MASK_DIGITAL1);
+				return 0;
+		  
+			case SOUND_MIXER_BASS:
+				*(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r);
+				return 0;
+			  
+			case SOUND_MIXER_TREBLE:
+				*(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r);
+				return 0;
+
+			case SOUND_MIXER_DIGITAL1:
+				*(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r);
+				return 0;
+
+			default:
+				return -EINVAL;
+		}
+	}
+}
+/* opl3sa3_mixer_ioctl end */
+
+
+static struct mixer_operations opl3sa2_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"OPL3-SA2",
+	name:	"Yamaha OPL3-SA2",
+	ioctl:	opl3sa2_mixer_ioctl
+};
+
+static struct mixer_operations opl3sa3_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"OPL3-SA3",
+	name:	"Yamaha OPL3-SA3",
+	ioctl:	opl3sa3_mixer_ioctl
+};
+
+/* End of mixer-related stuff */
+
+
+/*
+ * Component probe, attach, unload functions
+ */
+
+static inline int __init probe_opl3sa2_mpu(struct address_info* hw_config)
+{
+	return probe_mpu401(hw_config);
+}
+
+
+static inline void __init attach_opl3sa2_mpu(struct address_info* hw_config)
+{
+	attach_mpu401(hw_config, THIS_MODULE);
+}
+
+
+static inline void __exit unload_opl3sa2_mpu(struct address_info *hw_config)
+{
+	unload_mpu401(hw_config);
+}
+
+
+static inline int __init probe_opl3sa2_mss(struct address_info* hw_config)
+{
+	return probe_ms_sound(hw_config);
+}
+
+
+static void __init attach_opl3sa2_mss(struct address_info* hw_config)
+{
+	int initial_mixers;
+
+	initial_mixers = num_mixers;
+	attach_ms_sound(hw_config, THIS_MODULE);	/* Slot 0 */
+	if(hw_config->slots[0] != -1) {
+		/* Did the MSS driver install? */
+		if(num_mixers == (initial_mixers + 1)) {
+			/* The MSS mixer is installed, reroute mixers appropiately */
+			AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD);
+			AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH);
+			AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
+		}
+		else {
+			printk(KERN_ERR "opl3sa2: MSS mixer not installed?\n");
+		}
+	}
+}
+
+
+static inline void __exit unload_opl3sa2_mss(struct address_info* hw_config)
+{
+	unload_ms_sound(hw_config);
+}
+
+
+static int __init probe_opl3sa2(struct address_info* hw_config, int card)
+{
+	unsigned char misc;
+	unsigned char tmp;
+	unsigned char version;
+	char tag;
+
+	/*
+	 * Verify that the I/O port range is free.
+	 */
+	if(check_region(hw_config->io_base, 2)) {
+		printk(KERN_ERR "opl3sa2: Control I/O port %#x not free\n",
+		       hw_config->io_base);
+		return 0;
+	}
+
+	/*
+	 * Check if writing to the read-only version bits of the miscellaneous
+	 * register succeeds or not (it should not).
+	 */
+	opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc);
+	opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc ^ 0x07);
+	opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &tmp);
+	if(tmp != misc) {
+		printk(KERN_ERR "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n",
+		       hw_config->io_base);
+		return 0;
+	}
+
+	/*
+	 * Check if the MIC register is accessible.
+	 */
+	opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp);
+	opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, 0x8a);
+	opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp);
+	if((tmp & 0x9f) != 0x8a) {
+		printk(KERN_ERR
+		       "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n",
+		       hw_config->io_base);
+		return 0;
+	}
+	opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, tmp);
+
+	/*
+	 * Determine chipset type (SA2 or SA3)
+	 *
+	 * This is done by looking at the chipset version in the lower 3 bits
+	 * of the miscellaneous register.
+	 */
+	version = misc & 0x07;
+	printk(KERN_DEBUG "opl3sa2: chipset version = %#x\n", version);
+	switch(version) {
+		case 0:
+			chipset[card] = CHIPSET_UNKNOWN;
+			tag = '?'; /* silence compiler warning */
+			printk(KERN_ERR
+			       "opl3sa2: Unknown Yamaha audio controller version\n");
+			break;
+
+		case VERSION_YMF711:
+			chipset[card] = CHIPSET_OPL3SA2;
+			tag = '2';
+			printk(KERN_INFO "opl3sa2: Found OPL3-SA2 (YMF711)\n");
+			break;
+
+		case VERSION_YMF715:
+			chipset[card] = CHIPSET_OPL3SA3;
+			tag = '3';
+			printk(KERN_INFO
+			       "opl3sa2: Found OPL3-SA3 (YMF715 or YMF719)\n");
+			break;
+
+		case VERSION_YMF715B:
+			chipset[card] = CHIPSET_OPL3SA3;
+			tag = '3';
+			printk(KERN_INFO
+			       "opl3sa2: Found OPL3-SA3 (YMF715B or YMF719B)\n");
+			break;
+
+		case VERSION_YMF715E:
+		default:
+			chipset[card] = CHIPSET_OPL3SA3;
+			tag = '3';
+			printk(KERN_INFO
+			       "opl3sa2: Found OPL3-SA3 (YMF715E or YMF719E)\n");
+			break;
+	}
+
+	if(chipset[card] != CHIPSET_UNKNOWN) {
+		/* Generate a pretty name */
+		sprintf(chipset_name[card], "OPL3-SA%c", tag);
+		return 1;
+	}
+	return 0;
+}
+
+
+static void __init attach_opl3sa2(struct address_info* hw_config, int card)
+{
+   	request_region(hw_config->io_base, 2, chipset_name[card]);
+
+	/* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */
+	opl3sa2_write(hw_config->io_base, OPL3SA2_IRQ_CONFIG, 0x0d);
+
+	/* Initialize DMA configuration */
+	if(hw_config->dma2 == hw_config->dma) {
+		/* Want DMA configuration DMA-B: -, DMA-A: WSS-P+WSS-R */
+		opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x03);
+	}
+	else {
+		/* Want DMA configuration DMA-B: WSS-R, DMA-A: WSS-P */
+		opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x21);
+	}
+}
+
+
+static void __init attach_opl3sa2_mixer(struct address_info *hw_config, int card)
+{
+	struct mixer_operations* mixer_operations;
+	opl3sa2_mixerdata* devc;
+
+	/* Install master mixer */
+	if(chipset[card] == CHIPSET_OPL3SA3) {
+		mixer_operations = &opl3sa3_mixer_operations;
+	}
+	else {
+		mixer_operations = &opl3sa2_mixer_operations;
+	}
+
+	if((devc = &opl3sa2_data[card])) {
+		devc->cfg_port = hw_config->io_base;
+
+		opl3sa2_mixer[card] = sound_install_mixer(MIXER_DRIVER_VERSION,
+							  mixer_operations->name,
+							  mixer_operations,
+							  sizeof(struct mixer_operations),
+							  devc);
+		if(opl3sa2_mixer[card] < 0) {
+			printk(KERN_ERR "opl3sa2: Could not install %s master mixer\n",
+				 mixer_operations->name);
+		}
+		else
+			opl3sa2_mixer_reset(devc, card);
+	}
+}
+
+
+static void __init opl3sa2_clear_slots(struct address_info* hw_config)
+{
+	int i;
+
+	for(i = 0; i < 6; i++) {
+		hw_config->slots[i] = -1;
+	}
+}
+
+
+static void __init opl3sa2_set_ymode(struct address_info* hw_config, int ymode)
+{
+	/*
+	 * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and
+	 * it's supported.
+	 *
+	 * 0: Desktop (aka normal)   5-12 cm speakers
+	 * 1: Notebook PC mode 1     3 cm speakers
+	 * 2: Notebook PC mode 2     1.5 cm speakers
+	 * 3: Hi-fi                  16-38 cm speakers
+	 */
+	if(ymode >= 0 && ymode <= 3) {
+		unsigned char sys_ctrl;
+
+		opl3sa2_read(hw_config->io_base, OPL3SA2_SYS_CTRL, &sys_ctrl);
+		sys_ctrl = (sys_ctrl & 0xcf) | ((ymode & 3) << 4);
+		opl3sa2_write(hw_config->io_base, OPL3SA2_SYS_CTRL, sys_ctrl);
+	}
+	else {
+		printk(KERN_ERR "opl3sa2: not setting ymode, it must be one of 0,1,2,3\n");
+	}
+}
+
+
+static void __init opl3sa2_set_loopback(struct address_info* hw_config, int loopback)
+{
+	if(loopback >= 0 && loopback <= 1) {
+		unsigned char misc;
+
+		opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc);
+		misc = (misc & 0xef) | ((loopback & 1) << 4);
+		opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc);
+	}
+	else {
+		printk(KERN_ERR "opl3sa2: not setting loopback, it must be either 0 or 1\n");
+	}
+}
+
+
+static void __exit unload_opl3sa2(struct address_info* hw_config, int card)
+{
+        /* Release control ports */
+	release_region(hw_config->io_base, 2);
+
+	/* Unload mixer */
+	if(opl3sa2_mixer[card] >= 0)
+		sound_unload_mixerdev(opl3sa2_mixer[card]);
+}
+
+
+#ifdef __ISAPNP__
+
+struct isapnp_device_id isapnp_opl3sa2_list[] __initdata = {
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021),
+		0 },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_opl3sa2_list);
+
+static int __init opl3sa2_isapnp_probe(struct address_info* hw_cfg,
+				       struct address_info* mss_cfg,
+				       struct address_info* mpu_cfg,
+				       int card)
+{
+	static struct pci_dev* dev;
+	int ret;
+
+	/* Find and configure device */
+	dev = isapnp_find_dev(NULL,
+			      ISAPNP_VENDOR('Y','M','H'),
+			      ISAPNP_FUNCTION(0x0021),
+			      dev);
+	if(dev == NULL) {
+		return -ENODEV;
+	}
+
+	/*
+	 * If device is active, assume configured with /proc/isapnp
+	 * and use anyway. Any other way to check this?
+	 */
+	ret = dev->prepare(dev);
+	if(ret && ret != -EBUSY) {
+		printk(KERN_ERR "opl3sa2: ISA PnP found device that could not be autoconfigured.\n");
+		return -ENODEV;
+	}
+	if(ret == -EBUSY) {
+		opl3sa2_activated[card] = 1;
+	}
+	else {
+		if(dev->activate(dev) < 0) {
+			printk(KERN_WARNING "opl3sa2: ISA PnP activate failed\n");
+			opl3sa2_activated[card] = 0;
+			return -ENODEV;
+		}
+
+		printk(KERN_DEBUG
+		       "opl3sa2: Activated ISA PnP card %d (active=%d)\n",
+		       card, dev->active);
+
+	}
+
+	/* Our own config: */
+	hw_cfg->io_base = dev->resource[4].start;
+	hw_cfg->irq     = dev->irq_resource[0].start;
+	hw_cfg->dma     = dev->dma_resource[0].start;
+	hw_cfg->dma2    = dev->dma_resource[1].start;
+	
+	/* The MSS config: */
+	mss_cfg->io_base      = dev->resource[1].start;
+	mss_cfg->irq          = dev->irq_resource[0].start;
+	mss_cfg->dma          = dev->dma_resource[0].start;
+	mss_cfg->dma2         = dev->dma_resource[1].start;
+	mss_cfg->card_subtype = 1; /* No IRQ or DMA setup */
+
+	mpu_cfg->io_base       = dev->resource[3].start;
+	mpu_cfg->irq           = dev->irq_resource[0].start;
+	mpu_cfg->dma           = -1;
+	mpu_cfg->dma2          = -1;
+	mpu_cfg->always_detect = 1; /* It's there, so use shared IRQs */
+
+	/* Call me paranoid: */
+	opl3sa2_clear_slots(hw_cfg);
+	opl3sa2_clear_slots(mss_cfg);
+	opl3sa2_clear_slots(mpu_cfg);
+
+	opl3sa2_dev[card] = dev;
+
+	return 0;
+}
+#endif /* __ISAPNP__ */
+
+/* End of component functions */
+
+/* Power Management support functions */
+static int opl3sa2_suspend(struct pm_dev *pdev, unsigned char pm_mode)
+{
+	unsigned long flags;
+	opl3sa2_mixerdata *p;
+
+	if (!pdev)
+		return -EINVAL;
+
+	save_flags(flags);
+	cli();
+
+	p = (opl3sa2_mixerdata *) pdev->data;
+	p->in_suspend = 1;
+	switch (pm_mode) {
+	case 1:
+		pm_mode = OPL3SA2_PM_MODE1;
+		break;
+	case 2:
+		pm_mode = OPL3SA2_PM_MODE2;
+		break;
+	case 3:
+		pm_mode = OPL3SA2_PM_MODE3;
+		break;
+	default:
+		pm_mode = OPL3SA2_PM_MODE3;
+		break;
+	}
+
+	/* its supposed to automute before suspending, so we wont bother */
+	opl3sa2_read(p->cfg_port, OPL3SA2_PM, &p->reg);
+	opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg | pm_mode);
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int opl3sa2_resume(struct pm_dev *pdev)
+{
+	unsigned long flags;
+	opl3sa2_mixerdata *p;
+
+	if (!pdev)
+		return -EINVAL;
+
+	p = (opl3sa2_mixerdata *) pdev->data;
+	save_flags(flags);
+	cli();
+
+	/* I don't think this is necessary */
+	opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg);
+	opl3sa2_mixer_restore(p, p->card);
+	p->in_suspend = 0;
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int opl3sa2_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
+{
+	unsigned char mode = (unsigned  char)data;
+
+	switch (rqst) {
+		case PM_SUSPEND:
+			return opl3sa2_suspend(pdev, mode);
+
+		case PM_RESUME:
+			return opl3sa2_resume(pdev);
+	}
+	return 0;
+}
+
+/*
+ * Install OPL3-SA2 based card(s).
+ *
+ * Need to have ad1848 and mpu401 loaded ready.
+ */
+static int __init init_opl3sa2(void)
+{
+        int card;
+	int max;
+
+	/* Sanitize isapnp and multiple settings */
+	isapnp = isapnp != 0 ? 1 : 0;
+	multiple = multiple != 0 ? 1 : 0;
+	
+	max = (multiple && isapnp) ? OPL3SA2_CARDS_MAX : 1;
+	for(card = 0; card < max; card++, opl3sa2_cards_num++) {
+#ifdef __ISAPNP__
+		/*
+		 * Please remember that even with __ISAPNP__ defined one
+		 * should still be able to disable PNP support for this 
+		 * single driver!
+		 */
+		if(isapnp && opl3sa2_isapnp_probe(&cfg[card],
+						  &cfg_mss[card],
+						  &cfg_mpu[card],
+						  card) < 0) {
+			if(!opl3sa2_cards_num)
+				printk(KERN_INFO "opl3sa2: No PnP cards found\n");
+			if(io == -1)
+				break;
+			isapnp=0;
+			printk(KERN_INFO "opl3sa2: Search for a card at 0x%d.\n", io);
+			/* Fall through */
+		}
+#endif
+		/* If a user wants an I/O then assume they meant it */
+		
+		if(!isapnp) {
+			if(io == -1 || irq == -1 || dma == -1 ||
+			   dma2 == -1 || mss_io == -1) {
+				printk(KERN_ERR
+				       "opl3sa2: io, mss_io, irq, dma, and dma2 must be set\n");
+				return -EINVAL;
+			}
+
+			/*
+			 * Our own config:
+			 * (NOTE: IRQ and DMA aren't used, so they're set to
+			 *  give pretty output from conf_printf. :)
+			 */
+			cfg[card].io_base = io;
+			cfg[card].irq     = irq;
+			cfg[card].dma     = dma;
+			cfg[card].dma2    = dma2;
+	
+			/* The MSS config: */
+			cfg_mss[card].io_base      = mss_io;
+			cfg_mss[card].irq          = irq;
+			cfg_mss[card].dma          = dma;
+			cfg_mss[card].dma2         = dma2;
+			cfg_mss[card].card_subtype = 1; /* No IRQ or DMA setup */
+
+			cfg_mpu[card].io_base       = mpu_io;
+			cfg_mpu[card].irq           = irq;
+			cfg_mpu[card].dma           = -1;
+			cfg_mpu[card].always_detect = 1; /* Use shared IRQs */
+
+			/* Call me paranoid: */
+			opl3sa2_clear_slots(&cfg[card]);
+			opl3sa2_clear_slots(&cfg_mss[card]);
+			opl3sa2_clear_slots(&cfg_mpu[card]);
+		}
+
+		if(!probe_opl3sa2(&cfg[card], card) ||
+		   !probe_opl3sa2_mss(&cfg_mss[card])) {
+			/*
+			 * If one or more cards are already registered, don't
+			 * return an error but print a warning.  Note, this
+			 * should never really happen unless the hardware or
+			 * ISA PnP screwed up.
+			 */
+			if(opl3sa2_cards_num) {
+				printk(KERN_WARNING
+				       "opl3sa2: There was a problem probing one "
+				       " of the ISA PNP cards, continuing\n");
+				opl3sa2_cards_num--;
+				continue;
+			} else
+				return -ENODEV;
+		}
+
+		attach_opl3sa2(&cfg[card], card);
+		conf_printf(chipset_name[card], &cfg[card]);
+		attach_opl3sa2_mss(&cfg_mss[card]);
+		attach_opl3sa2_mixer(&cfg[card], card);
+
+		opl3sa2_data[card].card = card;
+		/* register our power management capabilities */
+		opl3sa2_data[card].pmdev = pm_register(PM_ISA_DEV, card, opl3sa2_pm_callback);
+		if (opl3sa2_data[card].pmdev)
+			opl3sa2_data[card].pmdev->data = &opl3sa2_data[card];
+
+		/*
+		 * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and
+		 * it's supported.
+		 */
+		if(ymode != -1) {
+			if(chipset[card] == CHIPSET_OPL3SA2) {
+				printk(KERN_ERR
+				       "opl3sa2: ymode not supported on OPL3-SA2\n");
+			}
+			else {
+				opl3sa2_set_ymode(&cfg[card], ymode);
+			}
+		}
+
+
+		/* Set A/D input to Mono loopback if asked to. */
+		if(loopback != -1) {
+			opl3sa2_set_loopback(&cfg[card], loopback);
+		}
+		
+		/* Attach MPU if we've been asked to do so */
+		if(cfg_mpu[card].io_base != -1) {
+			if(probe_opl3sa2_mpu(&cfg_mpu[card])) {
+				attach_opl3sa2_mpu(&cfg_mpu[card]);
+			}
+		}
+	}
+
+	if(isapnp) {
+		printk(KERN_NOTICE "opl3sa2: %d PnP card(s) found.\n", opl3sa2_cards_num);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Uninstall OPL3-SA2 based card(s).
+ */
+static void __exit cleanup_opl3sa2(void)
+{
+	int card;
+
+	for(card = 0; card < opl3sa2_cards_num; card++) {
+		if (opl3sa2_data[card].pmdev)
+			pm_unregister(opl3sa2_data[card].pmdev);
+
+	        if(cfg_mpu[card].slots[1] != -1) {
+			unload_opl3sa2_mpu(&cfg_mpu[card]);
+		}
+		unload_opl3sa2_mss(&cfg_mss[card]);
+		unload_opl3sa2(&cfg[card], card);
+
+#ifdef __ISAPNP__
+		if(opl3sa2_activated[card] && opl3sa2_dev[card]) {
+			opl3sa2_dev[card]->deactivate(opl3sa2_dev[card]);
+
+			printk(KERN_DEBUG
+			       "opl3sa2: Deactivated ISA PnP card %d (active=%d)\n",
+			       card, opl3sa2_dev[card]->active);
+		}
+#endif
+	}
+}
+
+module_init(init_opl3sa2);
+module_exit(cleanup_opl3sa2);
+
+#ifndef MODULE
+static int __init setup_opl3sa2(char *str)
+{
+	/* io, irq, dma, dma2,... */
+#ifdef __ISAPNP__
+	int ints[11];
+#else
+	int ints[9];
+#endif
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io       = ints[1];
+	irq      = ints[2];
+	dma      = ints[3];
+	dma2     = ints[4];
+	mss_io   = ints[5];
+	mpu_io   = ints[6];
+	ymode    = ints[7];
+	loopback = ints[8];
+#ifdef __ISAPNP__
+	isapnp   = ints[9];
+	multiple = ints[10];
+#endif
+	return 1;
+}
+
+__setup("opl3sa2=", setup_opl3sa2);
+#endif
diff -Nru linux/sound/oss/os.h linux-2.4.19-pre5-mjc/sound/oss/os.h
--- linux/sound/oss/os.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/os.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,57 @@
+#define ALLOW_SELECT
+#undef NO_INLINE_ASM
+#define SHORT_BANNERS
+#define MANUAL_PNP
+#undef  DO_TIMINGS
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE > 131328
+#define LINUX21X
+#endif
+
+#ifdef __KERNEL__
+#include <linux/utsname.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/param.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#ifdef __alpha__
+#include <asm/segment.h>
+#endif
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#include <linux/pci.h>
+#endif
+
+#include <linux/wrapper.h>
+#include <linux/soundcard.h>
+
+#define FALSE	0
+#define TRUE	1
+
+extern int sound_alloc_dma(int chn, char *deviceID);
+extern int sound_open_dma(int chn, char *deviceID);
+extern void sound_free_dma(int chn);
+extern void sound_close_dma(int chn);
+
+extern void reprogram_timer(void);
+
+#define USE_AUTOINIT_DMA
+
+extern caddr_t sound_mem_blocks[1024];
+extern int sound_nblocks;
+
+#undef PSEUDO_DMA_AUTOINIT
+#define ALLOW_BUFFER_MAPPING
+
+extern struct file_operations oss_sound_fops;
diff -Nru linux/sound/oss/pas2.h linux-2.4.19-pre5-mjc/sound/oss/pas2.h
--- linux/sound/oss/pas2.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/pas2.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,23 @@
+/*
+ * pas2.h
+ *
+ * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
+ *
+ */
+
+/*	From pas_card.c	*/
+int pas_set_intr(int mask);
+int pas_remove_intr(int mask);
+unsigned char pas_read(int ioaddr);
+void pas_write(unsigned char data, int ioaddr);
+
+/*	From pas_audio.c */
+void pas_pcm_interrupt(unsigned char status, int cause);
+void pas_pcm_init(struct address_info *hw_config);
+
+/*	From pas_mixer.c */
+int pas_init_mixer(void);
+
+/*	From pas_midi.c */
+void pas_midi_init(void);
+void pas_midi_interrupt(void);
diff -Nru linux/sound/oss/pas2_card.c linux-2.4.19-pre5-mjc/sound/oss/pas2_card.c
--- linux/sound/oss/pas2_card.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/pas2_card.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,464 @@
+/*
+ * sound/pas2_card.c
+ *
+ * Detection routine for the Pro Audio Spectrum cards.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+#include "sb.h"
+
+static unsigned char dma_bits[] = {
+	4, 1, 2, 3, 0, 5, 6, 7
+};
+
+static unsigned char irq_bits[] = {
+	0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11
+};
+
+static unsigned char sb_irq_bits[] = {
+	0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 
+	0x00, 0x08, 0x28, 0x30, 0x38, 0, 0
+};
+
+static unsigned char sb_dma_bits[] = {
+	0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0
+};
+
+/*
+ * The Address Translation code is used to convert I/O register addresses to
+ * be relative to the given base -register
+ */
+
+int             translate_code = 0;
+static int      pas_intr_mask = 0;
+static int      pas_irq = 0;
+static int      pas_sb_base = 0;
+#ifndef CONFIG_PAS_JOYSTICK
+static int	joystick = 0;
+#else
+static int 	joystick = 1;
+#endif
+#ifdef SYMPHONY_PAS
+static int 	symphony = 1;
+#else
+static int 	symphony = 0;
+#endif
+#ifdef BROKEN_BUS_CLOCK
+static int	broken_bus_clock = 1;
+#else
+static int	broken_bus_clock = 0;
+#endif
+
+static struct address_info cfg;
+static struct address_info cfg2;
+
+char            pas_model = 0;
+static char    *pas_model_names[] = {
+	"", 
+	"Pro AudioSpectrum+", 
+	"CDPC", 
+	"Pro AudioSpectrum 16", 
+	"Pro AudioSpectrum 16D"
+};
+
+/*
+ * pas_read() and pas_write() are equivalents of inb and outb 
+ * These routines perform the I/O address translation required
+ * to support other than the default base address
+ */
+
+extern void     mix_write(unsigned char data, int ioaddr);
+
+unsigned char pas_read(int ioaddr)
+{
+	return inb(ioaddr + translate_code);
+}
+
+void pas_write(unsigned char data, int ioaddr)
+{
+	outb((data), ioaddr + translate_code);
+}
+
+/******************* Begin of the Interrupt Handler ********************/
+
+void pasintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	int             status;
+
+	status = pas_read(0x0B89);
+	pas_write(status, 0x0B89);	/* Clear interrupt */
+
+	if (status & 0x08)
+	{
+		  pas_pcm_interrupt(status, 1);
+		  status &= ~0x08;
+	}
+	if (status & 0x10)
+	{
+		  pas_midi_interrupt();
+		  status &= ~0x10;
+	}
+}
+
+int pas_set_intr(int mask)
+{
+	if (!mask)
+		return 0;
+
+	pas_intr_mask |= mask;
+
+	pas_write(pas_intr_mask, 0x0B8B);
+	return 0;
+}
+
+int pas_remove_intr(int mask)
+{
+	if (!mask)
+		return 0;
+
+	pas_intr_mask &= ~mask;
+	pas_write(pas_intr_mask, 0x0B8B);
+
+	return 0;
+}
+
+/******************* End of the Interrupt handler **********************/
+
+/******************* Begin of the Initialization Code ******************/
+
+static int __init config_pas_hw(struct address_info *hw_config)
+{
+	char            ok = 1;
+	unsigned        int_ptrs;	/* scsi/sound interrupt pointers */
+
+	pas_irq = hw_config->irq;
+
+	pas_write(0x00, 0x0B8B);
+	pas_write(0x36, 0x138B);
+	pas_write(0x36, 0x1388);
+	pas_write(0, 0x1388);
+	pas_write(0x74, 0x138B);
+	pas_write(0x74, 0x1389);
+	pas_write(0, 0x1389);
+
+	pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
+	pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
+	pas_write(0x01 | 0x02 | 0x04 | 0x10	/*
+						 * |
+						 * 0x80
+						 */ , 0xB88);
+
+	pas_write(0x80
+		  | joystick?0x40:0
+		  ,0xF388);
+
+	if (pas_irq < 0 || pas_irq > 15)
+	{
+		printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+		hw_config->irq=-1;
+		ok = 0;
+	}
+	else
+	{
+		int_ptrs = pas_read(0xF38A);
+		int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq];
+		pas_write(int_ptrs, 0xF38A);
+		if (!irq_bits[pas_irq])
+		{
+			printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+			hw_config->irq=-1;
+			ok = 0;
+		}
+		else
+		{
+			if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) {
+				printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq);
+				hw_config->irq=-1;
+				ok = 0;
+			}
+		}
+	}
+
+	if (hw_config->dma < 0 || hw_config->dma > 7)
+	{
+		printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+		hw_config->dma=-1;
+		ok = 0;
+	}
+	else
+	{
+		pas_write(dma_bits[hw_config->dma], 0xF389);
+		if (!dma_bits[hw_config->dma])
+		{
+			printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+			hw_config->dma=-1;
+			ok = 0;
+		}
+		else
+		{
+			if (sound_alloc_dma(hw_config->dma, "PAS16"))
+			{
+				printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
+				hw_config->dma=-1;
+				ok = 0;
+			}
+		}
+	}
+
+	/*
+	 * This fixes the timing problems of the PAS due to the Symphony chipset
+	 * as per Media Vision.  Only define this if your PAS doesn't work correctly.
+	 */
+
+	if(symphony)
+	{
+		outb((0x05), 0xa8);
+		outb((0x60), 0xa9);
+	}
+
+	if(broken_bus_clock)
+		pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
+	else
+		/*
+		 * pas_write(0x01, 0x8388);
+		 */
+		pas_write(0x01 | 0x10 | 0x20, 0x8388);
+
+	pas_write(0x18, 0x838A);	/* ??? */
+	pas_write(0x20 | 0x01, 0x0B8A);		/* Mute off, filter = 17.897 kHz */
+	pas_write(8, 0xBF8A);
+
+	mix_write(0x80 | 5, 0x078B);
+	mix_write(5, 0x078B);
+
+#if !defined(DISABLE_SB_EMULATION)
+
+	{
+		struct address_info *sb_config;
+
+		sb_config = &cfg2;
+		if (sb_config->io_base)
+		{
+			unsigned char   irq_dma;
+
+			/*
+			 * Turn on Sound Blaster compatibility
+			 * bit 1 = SB emulation
+			 * bit 0 = MPU401 emulation (CDPC only :-( )
+			 */
+			
+			pas_write(0x02, 0xF788);
+
+			/*
+			 * "Emulation address"
+			 */
+			
+			pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
+			pas_sb_base = sb_config->io_base;
+
+			if (!sb_dma_bits[sb_config->dma])
+				printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
+
+			if (!sb_irq_bits[sb_config->irq])
+				printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
+
+			irq_dma = sb_dma_bits[sb_config->dma] |
+				sb_irq_bits[sb_config->irq];
+
+			pas_write(irq_dma, 0xFB8A);
+		}
+		else
+			pas_write(0x00, 0xF788);
+	}
+#else
+	pas_write(0x00, 0xF788);
+#endif
+
+	if (!ok)
+		printk(KERN_WARNING "PAS16: Driver not enabled\n");
+
+	return ok;
+}
+
+static int __init detect_pas_hw(struct address_info *hw_config)
+{
+	unsigned char   board_id, foo;
+
+	/*
+	 * WARNING: Setting an option like W:1 or so that disables warm boot reset
+	 * of the card will screw up this detect code something fierce. Adding code
+	 * to handle this means possibly interfering with other cards on the bus if
+	 * you have something on base port 0x388. SO be forewarned.
+	 */
+
+	outb((0xBC), 0x9A01);	/* Activate first board */
+	outb((hw_config->io_base >> 2), 0x9A01);	/* Set base address */
+	translate_code = hw_config->io_base - 0x388;
+	pas_write(1, 0xBF88);	/* Select one wait states */
+
+	board_id = pas_read(0x0B8B);
+
+	if (board_id == 0xff)
+		return 0;
+
+	/*
+	 * We probably have a PAS-series board, now check for a PAS16-series board
+	 * by trying to change the board revision bits. PAS16-series hardware won't
+	 * let you do this - the bits are read-only.
+	 */
+
+	foo = board_id ^ 0xe0;
+
+	pas_write(foo, 0x0B8B);
+	foo = pas_read(0x0B8B);
+	pas_write(board_id, 0x0B8B);
+
+	if (board_id != foo)
+		return 0;
+
+	pas_model = pas_read(0xFF88);
+
+	return pas_model;
+}
+
+static void __init attach_pas_card(struct address_info *hw_config)
+{
+	pas_irq = hw_config->irq;
+
+	if (detect_pas_hw(hw_config))
+	{
+
+		if ((pas_model = pas_read(0xFF88)))
+		{
+			char            temp[100];
+
+			sprintf(temp,
+			    "%s rev %d", pas_model_names[(int) pas_model],
+				    pas_read(0x2789));
+			conf_printf(temp, hw_config);
+		}
+		if (config_pas_hw(hw_config))
+		{
+			pas_pcm_init(hw_config);
+
+#if !defined(MODULE) && !defined(DISABLE_SB_EMULATION)
+			sb_dsp_disable_midi(pas_sb_base);	/* No MIDI capability */
+#endif
+
+			pas_midi_init();
+			pas_init_mixer();
+		}
+	}
+}
+
+static inline int __init probe_pas(struct address_info *hw_config)
+{
+	return detect_pas_hw(hw_config);
+}
+
+static void __exit unload_pas(struct address_info *hw_config)
+{
+	extern int pas_audiodev;
+	extern int pas2_mididev;
+
+	if (hw_config->dma>0)
+		sound_free_dma(hw_config->dma);
+	if (hw_config->irq>0)
+		free_irq(hw_config->irq, hw_config);
+
+	if(pas_audiodev!=-1)
+		sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
+	if(pas2_mididev!=-1)
+	        sound_unload_mididev(pas2_mididev);
+	if(pas_audiodev!=-1)
+		sound_unload_audiodev(pas_audiodev);
+}
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma16	= -1;	/* Set this for modules that need it */
+
+static int __initdata sb_io	= 0;
+static int __initdata sb_irq	= -1;
+static int __initdata sb_dma	= -1;
+static int __initdata sb_dma16	= -1;
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma16,"i");
+
+MODULE_PARM(sb_io,"i");
+MODULE_PARM(sb_irq,"i");
+MODULE_PARM(sb_dma,"i");
+MODULE_PARM(sb_dma16,"i");
+
+MODULE_PARM(joystick,"i");
+MODULE_PARM(symphony,"i");
+MODULE_PARM(broken_bus_clock,"i");
+
+MODULE_LICENSE("GPL");
+
+static int __init init_pas2(void)
+{
+	printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma16;
+
+	cfg2.io_base = sb_io;
+	cfg2.irq = sb_irq;
+	cfg2.dma = sb_dma;
+	cfg2.dma2 = sb_dma16;
+
+	if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
+		printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
+		return -EINVAL;
+	}
+
+	if (!probe_pas(&cfg))
+		return -ENODEV;
+	attach_pas_card(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_pas2(void)
+{
+	unload_pas(&cfg);
+}
+
+module_init(init_pas2);
+module_exit(cleanup_pas2);
+
+#ifndef MODULE
+static int __init setup_pas2(char *str)
+{
+	/* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */
+	int ints[9];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma16	= ints[4];
+
+	sb_io	= ints[5];
+	sb_irq	= ints[6];
+	sb_dma	= ints[7];
+	sb_dma16 = ints[8];
+
+	return 1;
+}
+
+__setup("pas2=", setup_pas2);
+#endif
diff -Nru linux/sound/oss/pas2_midi.c linux-2.4.19-pre5-mjc/sound/oss/pas2_midi.c
--- linux/sound/oss/pas2_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/pas2_midi.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,264 @@
+/*
+ * sound/pas2_midi.c
+ *
+ * The low level driver for the PAS Midi Interface.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Bartlomiej Zolnierkiewicz	: Added __init to pas_init_mixer()
+ */
+
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+static int      midi_busy = 0, input_opened = 0;
+static int      my_dev;
+
+int pas2_mididev=-1;
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+
+static void     (*midi_input_intr) (int dev, unsigned char data);
+
+static int pas_midi_open(int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+	int             err;
+	unsigned long   flags;
+	unsigned char   ctrl;
+
+
+	if (midi_busy)
+		return -EBUSY;
+
+	/*
+	 * Reset input and output FIFO pointers
+	 */
+	pas_write(0x20 | 0x40,
+		  0x178b);
+
+	save_flags(flags);
+	cli();
+
+	if ((err = pas_set_intr(0x10)) < 0)
+	{
+		restore_flags(flags);
+		return err;
+	}
+	/*
+	 * Enable input available and output FIFO empty interrupts
+	 */
+
+	ctrl = 0;
+	input_opened = 0;
+	midi_input_intr = input;
+
+	if (mode == OPEN_READ || mode == OPEN_READWRITE)
+	{
+		ctrl |= 0x04;	/* Enable input */
+		input_opened = 1;
+	}
+	if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+	{
+		ctrl |= 0x08 | 0x10;	/* Enable output */
+	}
+	pas_write(ctrl, 0x178b);
+
+	/*
+	 * Acknowledge any pending interrupts
+	 */
+
+	pas_write(0xff, 0x1B88);
+
+	restore_flags(flags);
+
+	midi_busy = 1;
+	qlen = qhead = qtail = 0;
+	return 0;
+}
+
+static void pas_midi_close(int dev)
+{
+
+	/*
+	 * Reset FIFO pointers, disable intrs
+	 */
+	pas_write(0x20 | 0x40, 0x178b);
+
+	pas_remove_intr(0x10);
+	midi_busy = 0;
+}
+
+static int dump_to_midi(unsigned char midi_byte)
+{
+	int fifo_space, x;
+
+	fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
+
+	/*
+	 * The MIDI FIFO space register and it's documentation is nonunderstandable.
+	 * There seem to be no way to differentiate between buffer full and buffer
+	 * empty situations. For this reason we don't never write the buffer
+	 * completely full. In this way we can assume that 0 (or is it 15)
+	 * means that the buffer is empty.
+	 */
+
+	if (fifo_space < 2 && fifo_space != 0)	/* Full (almost) */
+		return 0;	/* Ask upper layers to retry after some time */
+
+	pas_write(midi_byte, 0x178A);
+
+	return 1;
+}
+
+static int pas_midi_out(int dev, unsigned char midi_byte)
+{
+
+	unsigned long flags;
+
+	/*
+	 * Drain the local queue first
+	 */
+
+	save_flags(flags);
+	cli();
+
+	while (qlen && dump_to_midi(tmp_queue[qhead]))
+	{
+		qlen--;
+		qhead++;
+	}
+
+	restore_flags(flags);
+
+	/*
+	 *	Output the byte if the local queue is empty.
+	 */
+
+	if (!qlen)
+		if (dump_to_midi(midi_byte))
+			return 1;
+
+	/*
+	 *	Put to the local queue
+	 */
+
+	if (qlen >= 256)
+		return 0;	/* Local queue full */
+
+	save_flags(flags);
+	cli();
+
+	tmp_queue[qtail] = midi_byte;
+	qlen++;
+	qtail++;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+static int pas_midi_start_read(int dev)
+{
+	return 0;
+}
+
+static int pas_midi_end_read(int dev)
+{
+	return 0;
+}
+
+static void pas_midi_kick(int dev)
+{
+}
+
+static int pas_buffer_status(int dev)
+{
+	return qlen;
+}
+
+#define MIDI_SYNTH_NAME	"Pro Audio Spectrum Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations pas_midi_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		pas_midi_open,
+	close:		pas_midi_close,
+	outputc:	pas_midi_out,
+	start_read:	pas_midi_start_read,
+	end_read:	pas_midi_end_read,
+	kick:		pas_midi_kick,
+	buffer_status:	pas_buffer_status,
+};
+
+void __init pas_midi_init(void)
+{
+	int dev = sound_alloc_mididev();
+
+	if (dev == -1)
+	{
+		printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
+		return;
+	}
+	std_midi_synth.midi_dev = my_dev = dev;
+	midi_devs[dev] = &pas_midi_operations;
+	pas2_mididev = dev;
+	sequencer_init();
+}
+
+void pas_midi_interrupt(void)
+{
+	unsigned char   stat;
+	int             i, incount;
+	unsigned long   flags;
+
+	stat = pas_read(0x1B88);
+
+	if (stat & 0x04)	/* Input data available */
+	{
+		incount = pas_read(0x1B89) & 0x0f;	/* Input FIFO size */
+		if (!incount)
+			incount = 16;
+
+		for (i = 0; i < incount; i++)
+			if (input_opened)
+			{
+				midi_input_intr(my_dev, pas_read(0x178A));
+			} else
+				pas_read(0x178A);	/* Flush */
+	}
+	if (stat & (0x08 | 0x10))
+	{
+		save_flags(flags);
+		cli();
+
+		while (qlen && dump_to_midi(tmp_queue[qhead]))
+		{
+			qlen--;
+			qhead++;
+		}
+
+		restore_flags(flags);
+	}
+	if (stat & 0x40)
+	{
+		printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
+	}
+	pas_write(stat, 0x1B88);	/* Acknowledge interrupts */
+}
diff -Nru linux/sound/oss/pas2_mixer.c linux-2.4.19-pre5-mjc/sound/oss/pas2_mixer.c
--- linux/sound/oss/pas2_mixer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/pas2_mixer.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,335 @@
+
+/*
+ * sound/pas2_mixer.c
+ *
+ * Mixer routines for the Pro Audio Spectrum cards.
+ */
+
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer()
+ */
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+#ifndef DEB
+#define DEB(what)		/* (what) */
+#endif
+
+extern int      translate_code;
+extern char     pas_model;
+extern int     *pas_osp;
+extern int      pas_audiodev;
+
+static int      rec_devices = (SOUND_MASK_MIC);		/* Default recording source */
+static int      mode_control = 0;
+
+#define POSSIBLE_RECORDING_DEVICES	(SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_ALTPCM)
+
+#define SUPPORTED_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
+					 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV)
+
+static int     *levels;
+
+static int      default_levels[32] =
+{
+	0x3232,			/* Master Volume */
+	0x3232,			/* Bass */
+	0x3232,			/* Treble */
+	0x5050,			/* FM */
+	0x4b4b,			/* PCM */
+	0x3232,			/* PC Speaker */
+	0x4b4b,			/* Ext Line */
+	0x4b4b,			/* Mic */
+	0x4b4b,			/* CD */
+	0x6464,			/* Recording monitor */
+	0x4b4b,			/* SB PCM */
+	0x6464			/* Recording level */
+};
+
+void
+mix_write(unsigned char data, int ioaddr)
+{
+	/*
+	 * The Revision D cards have a problem with their MVA508 interface. The
+	 * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+	 * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+	 * 1. We need to do this because it isn't timing problem but chip access
+	 * sequence problem.
+	 */
+
+	if (pas_model == 4)
+	  {
+		  outw(data | (data << 8), (ioaddr + translate_code) - 1);
+		  outb((0x80), 0);
+	} else
+		pas_write(data, ioaddr);
+}
+
+static int
+mixer_output(int right_vol, int left_vol, int div, int bits,
+	     int mixer)		/* Input or output mixer */
+{
+	int             left = left_vol * div / 100;
+	int             right = right_vol * div / 100;
+
+
+	if (bits & 0x10)
+	  {
+		  left |= mixer;
+		  right |= mixer;
+	  }
+	if (bits == 0x03 || bits == 0x04)
+	  {
+		  mix_write(0x80 | bits, 0x078B);
+		  mix_write(left, 0x078B);
+		  right_vol = left_vol;
+	} else
+	  {
+		  mix_write(0x80 | 0x20 | bits, 0x078B);
+		  mix_write(left, 0x078B);
+		  mix_write(0x80 | 0x40 | bits, 0x078B);
+		  mix_write(right, 0x078B);
+	  }
+
+	return (left_vol | (right_vol << 8));
+}
+
+static void
+set_mode(int new_mode)
+{
+	mix_write(0x80 | 0x05, 0x078B);
+	mix_write(new_mode, 0x078B);
+
+	mode_control = new_mode;
+}
+
+static int
+pas_mixer_set(int whichDev, unsigned int level)
+{
+	int             left, right, devmask, changed, i, mixer = 0;
+
+	DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+	left = level & 0x7f;
+	right = (level & 0x7f00) >> 8;
+
+	if (whichDev < SOUND_MIXER_NRDEVICES) {
+		if ((1 << whichDev) & rec_devices)
+			mixer = 0x20;
+		else
+			mixer = 0x00;
+	}
+
+	switch (whichDev)
+	  {
+	  case SOUND_MIXER_VOLUME:	/* Master volume (0-63) */
+		  levels[whichDev] = mixer_output(right, left, 63, 0x01, 0);
+		  break;
+
+		  /*
+		   * Note! Bass and Treble are mono devices. Will use just the left
+		   * channel.
+		   */
+	  case SOUND_MIXER_BASS:	/* Bass (0-12) */
+		  levels[whichDev] = mixer_output(right, left, 12, 0x03, 0);
+		  break;
+	  case SOUND_MIXER_TREBLE:	/* Treble (0-12) */
+		  levels[whichDev] = mixer_output(right, left, 12, 0x04, 0);
+		  break;
+
+	  case SOUND_MIXER_SYNTH:	/* Internal synthesizer (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer);
+		  break;
+	  case SOUND_MIXER_PCM:	/* PAS PCM (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer);
+		  break;
+	  case SOUND_MIXER_ALTPCM:	/* SB PCM (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer);
+		  break;
+	  case SOUND_MIXER_SPEAKER:	/* PC speaker (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer);
+		  break;
+	  case SOUND_MIXER_LINE:	/* External line (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer);
+		  break;
+	  case SOUND_MIXER_CD:	/* CD (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer);
+		  break;
+	  case SOUND_MIXER_MIC:	/* External microphone (0-31) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer);
+		  break;
+	  case SOUND_MIXER_IMIX:	/* Recording monitor (0-31) (Output mixer only) */
+		  levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01,
+						  0x00);
+		  break;
+	  case SOUND_MIXER_RECLEV:	/* Recording level (0-15) */
+		  levels[whichDev] = mixer_output(right, left, 15, 0x02, 0);
+		  break;
+
+
+	  case SOUND_MIXER_RECSRC:
+		  devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+		  changed = devmask ^ rec_devices;
+		  rec_devices = devmask;
+
+		  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+			  if (changed & (1 << i))
+			    {
+				    pas_mixer_set(i, levels[i]);
+			    }
+		  return rec_devices;
+		  break;
+
+	  default:
+		  return -EINVAL;
+	  }
+
+	return (levels[whichDev]);
+}
+
+/*****/
+
+static void
+pas_mixer_reset(void)
+{
+	int             foo;
+
+	DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+
+	for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+		pas_mixer_set(foo, levels[foo]);
+
+	set_mode(0x04 | 0x01);
+}
+
+static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int level,v ;
+
+	DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+	if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
+		if (get_user(level, (int *)arg))
+			return -EFAULT;
+		if (level == -1)  /* Return current settings */
+			level = (mode_control & 0x04);
+		else {
+			mode_control &= ~0x04;
+			if (level)
+				mode_control |= 0x04;
+			set_mode(mode_control);
+		}
+		level = !!level;
+		return put_user(level, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
+		if (get_user(level, (int *)arg))
+			return -EFAULT;
+		if (level == -1) { /* Return current settings */
+			if (!(mode_control & 0x03))
+				level = 0;
+			else
+				level = ((mode_control & 0x03) + 1) * 20;
+		} else {
+			int i = 0;
+			
+			level &= 0x7f;
+			if (level)
+				i = (level / 20) - 1;
+			mode_control &= ~0x03;
+			mode_control |= i & 0x03;
+			set_mode(mode_control);
+			if (i)
+				i = (i + 1) * 20;
+			level = i;
+		}
+		return put_user(level, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
+		if (get_user(level, (int *)arg))
+			return -EFAULT;
+		if (level == -1)	/* Return current settings */
+			level = !(pas_read(0x0B8A) & 0x20);
+		else {
+			if (level)
+				pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
+			else
+				pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A);
+
+			level = !(pas_read(0x0B8A) & 0x20);
+		}
+		return put_user(level, (int *)arg);
+	}
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (get_user(v, (int *)arg))
+			return -EFAULT;
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			v = pas_mixer_set(cmd & 0xff, v);
+		} else {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				v = rec_devices;
+				break;
+				
+			case SOUND_MIXER_STEREODEVS:
+				v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
+				break;
+				
+			case SOUND_MIXER_DEVMASK:
+				v = SUPPORTED_MIXER_DEVICES;
+				break;
+				
+			case SOUND_MIXER_RECMASK:
+				v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				v = 0;	/* No special capabilities */
+				break;
+				
+			default:
+				v = levels[cmd & 0xff];
+				break;
+			}
+		}
+		return put_user(v, (int *)arg);
+	}
+	return -EINVAL;
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"PAS16",
+	name:	"Pro Audio Spectrum 16",
+	ioctl:	pas_mixer_ioctl
+};
+
+int __init
+pas_init_mixer(void)
+{
+	int             d;
+
+	levels = load_mixer_volumes("PAS16_1", default_levels, 1);
+
+	pas_mixer_reset();
+
+	if ((d = sound_alloc_mixerdev()) != -1)
+	  {
+		  audio_devs[pas_audiodev]->mixer_dev = d;
+		  mixer_devs[d] = &pas_mixer_operations;
+	  }
+	return 1;
+}
diff -Nru linux/sound/oss/pas2_pcm.c linux-2.4.19-pre5-mjc/sound/oss/pas2_pcm.c
--- linux/sound/oss/pas2_pcm.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/pas2_pcm.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,438 @@
+/*
+ * pas2_pcm.c Audio routines for PAS16
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox	   : Swatted a double allocation of device bug. Made a few
+ *		     more things module options.
+ * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
+ */
+
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include "pas2.h"
+
+#ifndef DEB
+#define DEB(WHAT)
+#endif
+
+#define PAS_PCM_INTRBITS (0x08)
+/*
+ * Sample buffer timer interrupt enable
+ */
+
+#define PCM_NON	0
+#define PCM_DAC	1
+#define PCM_ADC	2
+
+static unsigned long pcm_speed = 0;	/* sampling rate */
+static unsigned char pcm_channels = 1;	/* channels (1 or 2) */
+static unsigned char pcm_bits = 8;	/* bits/sample (8 or 16) */
+static unsigned char pcm_filter = 0;	/* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count = 0;
+static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
+static int      pcm_busy = 0;
+int             pas_audiodev = -1;
+static int      open_mode = 0;
+
+static int pcm_set_speed(int arg)
+{
+	int foo, tmp;
+	unsigned long flags;
+
+	if (arg == 0)
+		return pcm_speed;
+
+	if (arg > 44100)
+		arg = 44100;
+	if (arg < 5000)
+		arg = 5000;
+
+	if (pcm_channels & 2)
+	{
+		foo = (596590 + (arg / 2)) / arg;
+		arg = (596590 + (foo / 2)) / foo;
+	}
+	else
+	{
+		foo = (1193180 + (arg / 2)) / arg;
+		arg = (1193180 + (foo / 2)) / foo;
+	}
+
+	pcm_speed = arg;
+
+	tmp = pas_read(0x0B8A);
+
+	/*
+	 * Set anti-aliasing filters according to sample rate. You really *NEED*
+	 * to enable this feature for all normal recording unless you want to
+	 * experiment with aliasing effects.
+	 * These filters apply to the selected "recording" source.
+	 * I (pfw) don't know the encoding of these 5 bits. The values shown
+	 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+	 *
+	 * I cleared bit 5 of these values, since that bit controls the master
+	 * mute flag. (Olav Wlfelschneider)
+	 *
+	 */
+#if !defined NO_AUTO_FILTER_SET
+	tmp &= 0xe0;
+	if (pcm_speed >= 2 * 17897)
+		tmp |= 0x01;
+	else if (pcm_speed >= 2 * 15909)
+		tmp |= 0x02;
+	else if (pcm_speed >= 2 * 11931)
+		tmp |= 0x09;
+	else if (pcm_speed >= 2 * 8948)
+		tmp |= 0x11;
+	else if (pcm_speed >= 2 * 5965)
+		tmp |= 0x19;
+	else if (pcm_speed >= 2 * 2982)
+		tmp |= 0x04;
+	pcm_filter = tmp;
+#endif
+
+	save_flags(flags);
+	cli();
+
+	pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
+	pas_write(0x00 | 0x30 | 0x04, 0x138B);
+	pas_write(foo & 0xff, 0x1388);
+	pas_write((foo >> 8) & 0xff, 0x1388);
+	pas_write(tmp, 0x0B8A);
+
+	restore_flags(flags);
+
+	return pcm_speed;
+}
+
+static int pcm_set_channels(int arg)
+{
+
+	if ((arg != 1) && (arg != 2))
+		return pcm_channels;
+
+	if (arg != pcm_channels)
+	{
+		pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
+
+		pcm_channels = arg;
+		pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */
+	}
+	return pcm_channels;
+}
+
+static int pcm_set_bits(int arg)
+{
+	if (arg == 0)
+		return pcm_bits;
+
+	if ((arg & pcm_bitsok) != arg)
+		return pcm_bits;
+
+	if (arg != pcm_bits)
+	{
+		pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
+
+		pcm_bits = arg;
+	}
+	return pcm_bits;
+}
+
+static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int val, ret;
+
+	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+	switch (cmd) 
+	{
+	case SOUND_PCM_WRITE_RATE:
+		if (get_user(val, (int *)arg)) 
+			return -EFAULT;
+		ret = pcm_set_speed(val);
+		break;
+
+	case SOUND_PCM_READ_RATE:
+		ret = pcm_speed;
+		break;
+		
+	case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val + 1) - 1;
+		break;
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (get_user(val, (int *)arg)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val);
+		break;
+
+	case SOUND_PCM_READ_CHANNELS:
+		ret = pcm_channels;
+		break;
+
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		ret = pcm_set_bits(val);
+		break;
+		
+	case SOUND_PCM_READ_BITS:
+		ret = pcm_bits;
+		break;
+  
+	default:
+		return -EINVAL;
+	}
+	return put_user(ret, (int *)arg);
+}
+
+static void pas_audio_reset(int dev)
+{
+	DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
+
+	pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */
+}
+
+static int pas_audio_open(int dev, int mode)
+{
+	int             err;
+	unsigned long   flags;
+
+	DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
+
+	save_flags(flags);
+	cli();
+	if (pcm_busy)
+	{
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	pcm_busy = 1;
+	restore_flags(flags);
+
+	if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
+		return err;
+
+
+	pcm_count = 0;
+	open_mode = mode;
+
+	return 0;
+}
+
+static void pas_audio_close(int dev)
+{
+	unsigned long   flags;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
+
+	save_flags(flags);
+	cli();
+
+	pas_audio_reset(dev);
+	pas_remove_intr(PAS_PCM_INTRBITS);
+	pcm_mode = PCM_NON;
+
+	pcm_busy = 0;
+	restore_flags(flags);
+}
+
+static void pas_audio_output_block(int dev, unsigned long buf, int count,
+		       int intrflag)
+{
+	unsigned long   flags, cnt;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+	cnt = count;
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		cnt >>= 1;
+
+	if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    cnt == pcm_count)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	pas_write(pas_read(0xF8A) & ~0x40,
+		  0xF8A);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+
+	if (count != pcm_count)
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+
+		pcm_count = count;
+	}
+	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
+#ifdef NO_TRIGGER
+	pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+#endif
+
+	pcm_mode = PCM_DAC;
+
+	restore_flags(flags);
+}
+
+static void pas_audio_start_input(int dev, unsigned long buf, int count,
+		      int intrflag)
+{
+	unsigned long   flags;
+	int             cnt;
+
+	DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+	cnt = count;
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		cnt >>= 1;
+
+	if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    cnt == pcm_count)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+
+	if (count != pcm_count)
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+
+		pcm_count = count;
+	}
+	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
+#ifdef NO_TRIGGER
+	pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+#endif
+
+	pcm_mode = PCM_ADC;
+
+	restore_flags(flags);
+}
+
+#ifndef NO_TRIGGER
+static void pas_audio_trigger(int dev, int state)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	state &= open_mode;
+
+	if (state & PCM_ENABLE_OUTPUT)
+		pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+	else if (state & PCM_ENABLE_INPUT)
+		pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+	else
+		pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+	restore_flags(flags);
+}
+#endif
+
+static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	pas_audio_reset(dev);
+	return 0;
+}
+
+static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	pas_audio_reset(dev);
+	return 0;
+}
+
+static struct audio_driver pas_audio_driver =
+{
+	owner:		THIS_MODULE,
+	open:		pas_audio_open,
+	close:		pas_audio_close,
+	output_block:	pas_audio_output_block,
+	start_input:	pas_audio_start_input,
+	ioctl:		pas_audio_ioctl,
+	prepare_for_input:	pas_audio_prepare_for_input,
+	prepare_for_output:	pas_audio_prepare_for_output,
+	halt_io:		pas_audio_reset,
+	trigger:	pas_audio_trigger
+};
+
+void __init pas_pcm_init(struct address_info *hw_config)
+{
+	DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
+
+	pcm_bitsok = 8;
+	if (pas_read(0xEF8B) & 0x08)
+		pcm_bitsok |= 16;
+
+	pcm_set_speed(DSP_DEFAULT_SPEED);
+
+	if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+					"Pro Audio Spectrum",
+					&pas_audio_driver,
+					sizeof(struct audio_driver),
+					DMA_AUTOMODE,
+					AFMT_U8 | AFMT_S16_LE,
+					NULL,
+					hw_config->dma,
+					hw_config->dma)) < 0)
+		printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
+}
+
+void pas_pcm_interrupt(unsigned char status, int cause)
+{
+	if (cause == 1)
+	{
+		/*
+		 * Halt the PCM first. Otherwise we don't have time to start a new
+		 * block before the PCM chip proceeds to the next sample
+		 */
+
+		if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
+			pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+		switch (pcm_mode)
+		{
+			case PCM_DAC:
+				DMAbuf_outputintr(pas_audiodev, 1);
+				break;
+
+			case PCM_ADC:
+				DMAbuf_inputintr(pas_audiodev);
+				break;
+
+			default:
+				printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
+		}
+	}
+}
diff -Nru linux/sound/oss/pss.c linux-2.4.19-pre5-mjc/sound/oss/pss.c
--- linux/sound/oss/pss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/pss.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1238 @@
+/*
+ * sound/pss.c
+ *
+ * The low level driver for the Personal Sound System (ECHO ESC614).
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer	ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox		modularisation, clean up.
+ *
+ * 98-02-21: Vladimir Michl <vladimir.michl@upol.cz>
+ *          Added mixer device for Beethoven ADSP-16 (master volume,
+ *	    bass, treble, synth), only for speakers.
+ *          Fixed bug in pss_write (exchange parameters)
+ *          Fixed config port of SB
+ *          Requested two regions for PSS (PSS mixer, PSS config)
+ *          Modified pss_download_boot
+ *          To probe_pss_mss added test for initialize AD1848
+ * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz>
+ *          Fixed computation of mixer volumes
+ * 04-05-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
+ *          Added code that allows the user to enable his cdrom and/or 
+ *          joystick through the module parameters pss_cdrom_port and 
+ *          pss_enable_joystick.  pss_cdrom_port takes a port address as its
+ *          argument.  pss_enable_joystick takes either a 0 or a non-0 as its
+ *          argument.
+ * 04-06-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
+ *          Separated some code into new functions for easier reuse.  
+ *          Cleaned up and streamlined new code.  Added code to allow a user 
+ *          to only use this driver for enabling non-sound components 
+ *          through the new module parameter pss_no_sound (flag).  Added 
+ *          code that would allow a user to decide whether the driver should 
+ *          reset the configured hardware settings for the PSS board through 
+ *          the module parameter pss_keep_settings (flag).   This flag will 
+ *          allow a user to free up resources in use by this card if needbe, 
+ *          furthermore it allows him to use this driver to just enable the 
+ *          emulations and then be unloaded as it is no longer needed.  Both 
+ *          new settings are only available to this driver if compiled as a 
+ *          module.  The default settings of all new parameters are set to 
+ *          load the driver as it did in previous versions.
+ * 04-07-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
+ *          Added module parameter pss_firmware to allow the user to tell 
+ *          the driver where the fireware file is located.  The default 
+ *          setting is the previous hardcoded setting "/etc/sound/pss_synth".
+ * 00-03-03: Christoph Hellwig <chhellwig@gmx.net>
+ *	    Adapted to module_init/module_exit
+ * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *	    Added __init to probe_pss(), attach_pss() and probe_pss_mpu()
+ * 02-Jan-2001: Chris Rankin
+ *          Specify that this module owns the coprocessor
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+#include "sound_firmware.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+/*
+ * PSS registers.
+ */
+#define REG(x)	(devc->base+x)
+#define	PSS_DATA	0
+#define	PSS_STATUS	2
+#define PSS_CONTROL	2
+#define	PSS_ID		4
+#define	PSS_IRQACK	4
+#define	PSS_PIO		0x1a
+
+/*
+ * Config registers
+ */
+#define CONF_PSS	0x10
+#define CONF_WSS	0x12
+#define CONF_SB		0x14
+#define CONF_CDROM	0x16
+#define CONF_MIDI	0x18
+
+/*
+ * Status bits.
+ */
+#define PSS_FLAG3     0x0800
+#define PSS_FLAG2     0x0400
+#define PSS_FLAG1     0x1000
+#define PSS_FLAG0     0x0800
+#define PSS_WRITE_EMPTY  0x8000
+#define PSS_READ_FULL    0x4000
+
+/*
+ * WSS registers
+ */
+#define WSS_INDEX 4
+#define WSS_DATA 5
+
+/*
+ * WSS status bits
+ */
+#define WSS_INITIALIZING 0x80
+#define WSS_AUTOCALIBRATION 0x20
+
+#define NO_WSS_MIXER	-1
+
+#include "coproc.h"
+
+#include "pss_boot.h"
+
+/* If compiled into kernel, it enable or disable pss mixer */
+#ifdef CONFIG_PSS_MIXER
+static unsigned char pss_mixer = 1;
+#else
+static unsigned char pss_mixer = 0;
+#endif
+
+
+typedef struct pss_mixerdata {
+	unsigned int volume_l;
+	unsigned int volume_r;
+	unsigned int bass;
+	unsigned int treble;
+	unsigned int synth;
+} pss_mixerdata;
+
+typedef struct pss_confdata {
+	int             base;
+	int             irq;
+	int             dma;
+	int            *osp;
+	pss_mixerdata   mixer;
+	int             ad_mixer_dev;
+} pss_confdata;
+  
+static pss_confdata pss_data;
+static pss_confdata *devc = &pss_data;
+
+static int      pss_initialized = 0;
+static int      nonstandard_microcode = 0;
+static int	pss_cdrom_port = -1;	/* Parameter for the PSS cdrom port */
+static int	pss_enable_joystick = 0;/* Parameter for enabling the joystick */
+
+static void pss_write(pss_confdata *devc, int data)
+{
+	int i, limit;
+
+	limit = jiffies + HZ/10;	/* The timeout is 0.1 seconds */
+	/*
+	 * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+	 * called while interrupts are disabled. This means that the timer is
+	 * disabled also. However the timeout situation is a abnormal condition.
+	 * Normally the DSP should be ready to accept commands after just couple of
+	 * loops.
+	 */
+
+	for (i = 0; i < 5000000 && time_before(jiffies, limit); i++)
+ 	{
+ 		if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY)
+ 		{
+ 			outw(data, REG(PSS_DATA));
+ 			return;
+ 		}
+ 	}
+ 	printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data);
+}
+
+int __init probe_pss(struct address_info *hw_config)
+{
+	unsigned short id;
+	int irq, dma;
+
+	devc->base = hw_config->io_base;
+	irq = devc->irq = hw_config->irq;
+	dma = devc->dma = hw_config->dma;
+	devc->osp = hw_config->osp;
+
+	if (devc->base != 0x220 && devc->base != 0x240)
+		if (devc->base != 0x230 && devc->base != 0x250)		/* Some cards use these */
+			return 0;
+
+	if (check_region(devc->base, 0x19 /*16*/)) { 
+		printk(KERN_ERR "PSS: I/O port conflict\n");
+		return 0;
+	}
+	id = inw(REG(PSS_ID));
+	if ((id >> 8) != 'E') {
+		printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n",  devc->base,  id); 
+		return 0;
+	}
+	return 1;
+}
+
+static int set_irq(pss_confdata * devc, int dev, int irq)
+{
+	static unsigned short irq_bits[16] =
+	{
+		0x0000, 0x0000, 0x0000, 0x0008,
+		0x0000, 0x0010, 0x0000, 0x0018,
+		0x0000, 0x0020, 0x0028, 0x0030,
+		0x0038, 0x0000, 0x0000, 0x0000
+	};
+
+	unsigned short  tmp, bits;
+
+	if (irq < 0 || irq > 15)
+		return 0;
+
+	tmp = inw(REG(dev)) & ~0x38;	/* Load confreg, mask IRQ bits out */
+
+	if ((bits = irq_bits[irq]) == 0 && irq != 0)
+	{
+		printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq);
+		return 0;
+	}
+	outw(tmp | bits, REG(dev));
+	return 1;
+}
+
+static int set_io_base(pss_confdata * devc, int dev, int base)
+{
+	unsigned short  tmp = inw(REG(dev)) & 0x003f;
+	unsigned short  bits = (base & 0x0ffc) << 4;
+
+	outw(bits | tmp, REG(dev));
+
+	return 1;
+}
+
+static int set_dma(pss_confdata * devc, int dev, int dma)
+{
+	static unsigned short dma_bits[8] =
+	{
+		0x0001, 0x0002, 0x0000, 0x0003,
+		0x0000, 0x0005, 0x0006, 0x0007
+	};
+
+	unsigned short  tmp, bits;
+
+	if (dma < 0 || dma > 7)
+		return 0;
+
+	tmp = inw(REG(dev)) & ~0x07;	/* Load confreg, mask DMA bits out */
+
+	if ((bits = dma_bits[dma]) == 0 && dma != 4)
+	{
+		  printk(KERN_ERR "PSS: Invalid DMA %d\n", dma);
+		  return 0;
+	}
+	outw(tmp | bits, REG(dev));
+	return 1;
+}
+
+static int pss_reset_dsp(pss_confdata * devc)
+{
+	unsigned long   i, limit = jiffies + HZ/10;
+
+	outw(0x2000, REG(PSS_CONTROL));
+	for (i = 0; i < 32768 && (limit-jiffies >= 0); i++)
+		inw(REG(PSS_CONTROL));
+	outw(0x0000, REG(PSS_CONTROL));
+	return 1;
+}
+
+static int pss_put_dspword(pss_confdata * devc, unsigned short word)
+{
+	int i, val;
+
+	for (i = 0; i < 327680; i++)
+	{
+		val = inw(REG(PSS_STATUS));
+		if (val & PSS_WRITE_EMPTY)
+		{
+			outw(word, REG(PSS_DATA));
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int pss_get_dspword(pss_confdata * devc, unsigned short *word)
+{
+	int i, val;
+
+	for (i = 0; i < 327680; i++)
+	{
+		val = inw(REG(PSS_STATUS));
+		if (val & PSS_READ_FULL)
+		{
+			*word = inw(REG(PSS_DATA));
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
+{
+	int i, limit, val, count;
+
+	if (flags & CPF_FIRST)
+	{
+/*_____ Warn DSP software that a boot is coming */
+		outw(0x00fe, REG(PSS_DATA));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && time_before(jiffies, limit); i++)
+			if (inw(REG(PSS_DATA)) == 0x5500)
+				break;
+
+		outw(*block++, REG(PSS_DATA));
+		pss_reset_dsp(devc);
+	}
+	count = 1;
+	while ((flags&CPF_LAST) || count<size )
+	{
+		int j;
+
+		for (j = 0; j < 327670; j++)
+		{
+/*_____ Wait for BG to appear */
+			if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
+				break;
+		}
+
+		if (j == 327670)
+		{
+			/* It's ok we timed out when the file was empty */
+			if (count >= size && flags & CPF_LAST)
+				break;
+			else
+			{
+				printk("\n");
+				printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size);
+				return 0;
+			}
+		}
+/*_____ Send the next byte */
+		if (count >= size) 
+		{
+			/* If not data in block send 0xffff */
+			outw (0xffff, REG (PSS_DATA));
+		}
+		else
+		{
+			/*_____ Send the next byte */
+			outw (*block++, REG (PSS_DATA));
+		};
+		count++;
+	}
+
+	if (flags & CPF_LAST)
+	{
+/*_____ Why */
+		outw(0, REG(PSS_DATA));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && (limit - jiffies >= 0); i++)
+			val = inw(REG(PSS_STATUS));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && (limit-jiffies >= 0); i++)
+		{
+			val = inw(REG(PSS_STATUS));
+			if (val & 0x4000)
+				break;
+		}
+
+		/* now read the version */
+		for (i = 0; i < 32000; i++)
+		{
+			val = inw(REG(PSS_STATUS));
+			if (val & PSS_READ_FULL)
+				break;
+		}
+		if (i == 32000)
+			return 0;
+
+		val = inw(REG(PSS_DATA));
+		/* printk( "<PSS: microcode version %d.%d loaded>",  val/16,  val % 16); */
+	}
+	return 1;
+}
+
+/* Mixer */
+static void set_master_volume(pss_confdata *devc, int left, int right)
+{
+	static unsigned char log_scale[101] =  {
+		0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee,
+		0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+		0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+		0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+		0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+		0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+		0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+		0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+		0xfe, 0xfe, 0xff, 0xff, 0xff
+	};
+	pss_write(devc, 0x0010);
+	pss_write(devc, log_scale[left] | 0x0000);
+	pss_write(devc, 0x0010);
+	pss_write(devc, log_scale[right] | 0x0100);
+}
+
+static void set_synth_volume(pss_confdata *devc, int volume)
+{
+	int vol = ((0x8000*volume)/100L);
+	pss_write(devc, 0x0080);
+	pss_write(devc, vol);
+	pss_write(devc, 0x0081);
+	pss_write(devc, vol);
+}
+
+static void set_bass(pss_confdata *devc, int level)
+{
+	int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0;
+	pss_write(devc, 0x0010);
+	pss_write(devc, vol | 0x0200);
+};
+
+static void set_treble(pss_confdata *devc, int level)
+{	
+	int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0;
+	pss_write(devc, 0x0010);
+	pss_write(devc, vol | 0x0300);
+};
+
+static void pss_mixer_reset(pss_confdata *devc)
+{
+	set_master_volume(devc, 33, 33);
+	set_bass(devc, 50);
+	set_treble(devc, 50);
+	set_synth_volume(devc, 30);
+	pss_write (devc, 0x0010);
+	pss_write (devc, 0x0800 | 0xce);	/* Stereo */
+	
+	if(pss_mixer)
+	{
+		devc->mixer.volume_l = devc->mixer.volume_r = 33;
+		devc->mixer.bass = 50;
+		devc->mixer.treble = 50;
+		devc->mixer.synth = 30;
+	}
+}
+
+static void arg_to_volume_mono(unsigned int volume, int *aleft)
+{
+	int left;
+	
+	left = volume & 0x00ff;
+	if (left > 100)
+		left = 100;
+	*aleft = left;
+}
+
+static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright)
+{
+	arg_to_volume_mono(volume, aleft);
+	arg_to_volume_mono(volume >> 8, aright);
+}
+
+static int ret_vol_mono(int left)
+{
+	return ((left << 8) | left);
+}
+
+static int ret_vol_stereo(int left, int right)
+{
+	return ((right << 8) | left);
+}
+
+static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, caddr_t arg)
+{
+	if (devc->ad_mixer_dev != NO_WSS_MIXER) 
+		return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg);
+	else 
+		return -EINVAL;
+}
+
+static int pss_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+	pss_confdata *devc = mixer_devs[dev]->devc;
+	int cmdf = cmd & 0xff;
+	
+	if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) &&
+		(cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) &&
+		(cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) &&
+		(cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) &&
+		(cmdf != SOUND_MIXER_RECSRC)) 
+	{
+		return call_ad_mixer(devc, cmd, arg);
+	}
+	
+	if (((cmd >> 8) & 0xff) != 'M')	
+		return -EINVAL;
+		
+	if (_SIOC_DIR (cmd) & _SIOC_WRITE)
+	{
+		switch (cmdf)	
+		{
+			case SOUND_MIXER_RECSRC:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				else
+				{
+					if (*(int *)arg != 0)
+						return -EINVAL;
+					return 0;
+				}
+			case SOUND_MIXER_VOLUME:
+				arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l,
+					&devc->mixer.volume_r); 
+				set_master_volume(devc, devc->mixer.volume_l,
+					devc->mixer.volume_r);
+				return ret_vol_stereo(devc->mixer.volume_l,
+					devc->mixer.volume_r);
+		  
+			case SOUND_MIXER_BASS:
+				arg_to_volume_mono(*(unsigned int *)arg,
+					&devc->mixer.bass);
+				set_bass(devc, devc->mixer.bass);
+				return ret_vol_mono(devc->mixer.bass);
+		  
+			case SOUND_MIXER_TREBLE:
+				arg_to_volume_mono(*(unsigned int *)arg,
+					&devc->mixer.treble);
+				set_treble(devc, devc->mixer.treble);
+				return ret_vol_mono(devc->mixer.treble);
+		  
+			case SOUND_MIXER_SYNTH:
+				arg_to_volume_mono(*(unsigned int *)arg,
+					&devc->mixer.synth);
+				set_synth_volume(devc, devc->mixer.synth);
+				return ret_vol_mono(devc->mixer.synth);
+		  
+			default:
+				return -EINVAL;
+		}
+	}
+	else			
+	{
+		/*
+		 * Return parameters
+		 */
+		switch (cmdf)
+		{
+
+			case SOUND_MIXER_DEVMASK:
+				if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
+					*(int *)arg = 0; /* no mixer devices */
+				return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH);
+		  
+			case SOUND_MIXER_STEREODEVS:
+				if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
+					*(int *)arg = 0; /* no stereo devices */
+				return (*(int *)arg |= SOUND_MASK_VOLUME);
+		  
+			case SOUND_MIXER_RECMASK:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				else
+					return (*(int *)arg = 0); /* no record devices */
+
+			case SOUND_MIXER_CAPS:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				else
+					return (*(int *)arg = SOUND_CAP_EXCL_INPUT);
+
+			case SOUND_MIXER_RECSRC:
+				if (devc->ad_mixer_dev != NO_WSS_MIXER)
+					return call_ad_mixer(devc, cmd, arg);
+				else
+					return (*(int *)arg = 0); /* no record source */
+
+			case SOUND_MIXER_VOLUME:
+				return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r));
+			  
+			case SOUND_MIXER_BASS:
+				return (*(int *)arg = ret_vol_mono(devc->mixer.bass));
+			  
+			case SOUND_MIXER_TREBLE:
+				return (*(int *)arg = ret_vol_mono(devc->mixer.treble));
+			  
+			case SOUND_MIXER_SYNTH:
+				return (*(int *)arg = ret_vol_mono(devc->mixer.synth));
+			default:
+				return -EINVAL;
+		}
+	}
+}
+
+static struct mixer_operations pss_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"SOUNDPORT",
+	name:	"PSS-AD1848",
+	ioctl:	pss_mixer_ioctl
+};
+
+void disable_all_emulations(void)
+{
+	outw(0x0000, REG(CONF_PSS));	/* 0x0400 enables joystick */
+	outw(0x0000, REG(CONF_WSS));
+	outw(0x0000, REG(CONF_SB));
+	outw(0x0000, REG(CONF_MIDI));
+	outw(0x0000, REG(CONF_CDROM));
+}
+
+void configure_nonsound_components(void)
+{
+	/* Configure Joystick port */
+
+	if(pss_enable_joystick)
+	{
+		outw(0x0400, REG(CONF_PSS));	/* 0x0400 enables joystick */
+		printk(KERN_INFO "PSS: joystick enabled.\n");
+	}
+	else
+	{
+		printk(KERN_INFO "PSS: joystick port not enabled.\n");
+	}
+
+	/* Configure CDROM port */
+
+	if(pss_cdrom_port == -1)	/* If cdrom port enablation wasn't requested */
+	{
+		printk(KERN_INFO "PSS: CDROM port not enabled.\n");
+	}
+	else if(check_region(pss_cdrom_port, 2))
+	{
+		printk(KERN_ERR "PSS: CDROM I/O port conflict.\n");
+	}
+	else if(!set_io_base(devc, CONF_CDROM, pss_cdrom_port))
+	{
+		printk(KERN_ERR "PSS: CDROM I/O port could not be set.\n");
+	}
+	else					/* CDROM port successfully configured */
+	{
+		printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port);
+	}
+}
+
+void __init attach_pss(struct address_info *hw_config)
+{
+	unsigned short  id;
+	char tmp[100];
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->dma = hw_config->dma;
+	devc->osp = hw_config->osp;
+	devc->ad_mixer_dev = NO_WSS_MIXER;
+
+	if (!probe_pss(hw_config))
+		return;
+
+	request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation");
+	request_region(hw_config->io_base + 0x10, 0x9, "PSS config");
+
+	id = inw(REG(PSS_ID)) & 0x00ff;
+
+	/*
+	 * Disable all emulations. Will be enabled later (if required).
+	 */
+	 
+	disable_all_emulations();
+
+#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
+	if (sound_alloc_dma(hw_config->dma, "PSS"))
+	{
+		printk("pss.c: Can't allocate DMA channel.\n");
+		return;
+	}
+	if (!set_irq(devc, CONF_PSS, devc->irq))
+	{
+		printk("PSS: IRQ allocation error.\n");
+		return;
+	}
+	if (!set_dma(devc, CONF_PSS, devc->dma))
+	{
+		printk(KERN_ERR "PSS: DMA allocation error\n");
+		return;
+	}
+#endif
+
+	configure_nonsound_components();
+	pss_initialized = 1;
+	sprintf(tmp, "ECHO-PSS  Rev. %d", id);
+	conf_printf(tmp, hw_config);
+}
+
+int __init probe_pss_mpu(struct address_info *hw_config)
+{
+	int timeout;
+
+	if (!pss_initialized)
+		return 0;
+
+	if (check_region(hw_config->io_base, 2))
+	{
+		printk(KERN_ERR "PSS: MPU I/O port conflict\n");
+		return 0;
+	}
+	if (!set_io_base(devc, CONF_MIDI, hw_config->io_base))
+	{
+		  printk(KERN_ERR "PSS: MIDI base could not be set.\n");
+		  return 0;
+	}
+	if (!set_irq(devc, CONF_MIDI, hw_config->irq))
+	{
+		  printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n");
+		  return 0;
+	}
+	if (!pss_synthLen)
+	{
+		printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n");
+		return 0;
+	}
+	if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+	{
+		printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+		return 0;
+	}
+
+	/*
+	 * Finally wait until the DSP algorithm has initialized itself and
+	 * deactivates receive interrupt.
+	 */
+
+	for (timeout = 900000; timeout > 0; timeout--)
+	{
+		if ((inb(hw_config->io_base + 1) & 0x80) == 0)	/* Input data avail */
+			inb(hw_config->io_base);	/* Discard it */
+		else
+			break;	/* No more input */
+	}
+
+	return probe_mpu401(hw_config);
+}
+
+static int pss_coproc_open(void *dev_info, int sub_device)
+{
+	switch (sub_device)
+	{
+		case COPR_MIDI:
+			if (pss_synthLen == 0)
+			{
+				printk(KERN_ERR "PSS: MIDI synth microcode not available.\n");
+				return -EIO;
+			}
+			if (nonstandard_microcode)
+				if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+			{
+				printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+				return -EIO;
+			}
+			nonstandard_microcode = 0;
+			break;
+
+		default:
+			break;
+	}
+	return 0;
+}
+
+static void pss_coproc_close(void *dev_info, int sub_device)
+{
+	return;
+}
+
+static void pss_coproc_reset(void *dev_info)
+{
+	if (pss_synthLen)
+		if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+		{
+			printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+		}
+	nonstandard_microcode = 0;
+}
+
+static int download_boot_block(void *dev_info, copr_buffer * buf)
+{
+	if (buf->len <= 0 || buf->len > sizeof(buf->data))
+		return -EINVAL;
+
+	if (!pss_download_boot(devc, buf->data, buf->len, buf->flags))
+	{
+		printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n");
+		return -EIO;
+	}
+	nonstandard_microcode = 1;	/* The MIDI microcode has been overwritten */
+	return 0;
+}
+
+static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
+{
+	copr_buffer *buf;
+	copr_msg *mbuf;
+	copr_debug_buf dbuf;
+	unsigned short tmp;
+	unsigned long flags;
+	unsigned short *data;
+	int i, err;
+	/* printk( "PSS coproc ioctl %x %x %d\n",  cmd,  arg,  local); */
+	
+	switch (cmd) 
+	{
+		case SNDCTL_COPR_RESET:
+			pss_coproc_reset(dev_info);
+			return 0;
+
+		case SNDCTL_COPR_LOAD:
+			buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+			if (buf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(buf, arg, sizeof(copr_buffer))) {
+				vfree(buf);
+				return -EFAULT;
+			}
+			err = download_boot_block(dev_info, buf);
+			vfree(buf);
+			return err;
+		
+		case SNDCTL_COPR_SENDMSG:
+			mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
+			if (mbuf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(mbuf, arg, sizeof(copr_msg))) {
+				vfree(mbuf);
+				return -EFAULT;
+			}
+			data = (unsigned short *)(mbuf->data);
+			save_flags(flags);
+			cli();
+			for (i = 0; i < mbuf->len; i++) {
+				if (!pss_put_dspword(devc, *data++)) {
+					restore_flags(flags);
+					mbuf->len = i;	/* feed back number of WORDs sent */
+					err = copy_to_user(arg, mbuf, sizeof(copr_msg));
+					vfree(mbuf);
+					return err ? -EFAULT : -EIO;
+				}
+			}
+			restore_flags(flags);
+			vfree(mbuf);
+			return 0;
+
+		case SNDCTL_COPR_RCVMSG:
+			err = 0;
+			mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
+			if (mbuf == NULL)
+				return -ENOSPC;
+			data = (unsigned short *)mbuf->data;
+			save_flags(flags);
+			cli();
+			for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) {
+				mbuf->len = i;	/* feed back number of WORDs read */
+				if (!pss_get_dspword(devc, data++)) {
+					if (i == 0)
+						err = -EIO;
+					break;
+				}
+			}
+			restore_flags(flags);
+			if (copy_to_user(arg, mbuf, sizeof(copr_msg)))
+				err = -EFAULT;
+			vfree(mbuf);
+			return err;
+		
+		case SNDCTL_COPR_RDATA:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d0)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_get_dspword(devc, &tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			dbuf.parm1 = tmp;
+			restore_flags(flags);
+			if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
+				return -EFAULT;
+			return 0;
+		
+		case SNDCTL_COPR_WDATA:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d1)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			tmp = (unsigned int)dbuf.parm2 & 0xffff;
+			if (!pss_put_dspword(devc, tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			restore_flags(flags);
+			return 0;
+		
+		case SNDCTL_COPR_WCODE:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d3)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			tmp = (unsigned int)dbuf.parm2 & 0x00ff;
+			if (!pss_put_dspword(devc, tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
+			if (!pss_put_dspword(devc, tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			restore_flags(flags);
+			return 0;
+		
+		case SNDCTL_COPR_RCODE:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d2)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
+				restore_flags(flags);
+				return -EIO;
+			}
+			dbuf.parm1 = tmp << 8;
+			if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
+				restore_flags(flags);
+				return -EIO;
+			}
+			dbuf.parm1 |= tmp & 0x00ff;
+			restore_flags(flags);
+			if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
+				return -EFAULT;
+			return 0;
+
+		default:
+			return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+static coproc_operations pss_coproc_operations =
+{
+	"ADSP-2115",
+	THIS_MODULE,
+	pss_coproc_open,
+	pss_coproc_close,
+	pss_coproc_ioctl,
+	pss_coproc_reset,
+	&pss_data
+};
+
+static void __init attach_pss_mpu(struct address_info *hw_config)
+{
+	attach_mpu401(hw_config, THIS_MODULE);	/* Slot 1 */
+	if (hw_config->slots[1] != -1)	/* The MPU driver installed itself */
+		midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
+}
+
+static int __init probe_pss_mss(struct address_info *hw_config)
+{
+	volatile int timeout;
+
+	if (!pss_initialized)
+		return 0;
+
+	if (check_region(hw_config->io_base, 8))
+	{
+		  printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
+		  return 0;
+	}
+	if (!set_io_base(devc, CONF_WSS, hw_config->io_base))
+	{
+		printk("PSS: WSS base not settable.\n");
+		return 0;
+	}
+	if (!set_irq(devc, CONF_WSS, hw_config->irq))
+	{
+		printk("PSS: WSS IRQ allocation error.\n");
+		return 0;
+	}
+	if (!set_dma(devc, CONF_WSS, hw_config->dma))
+	{
+		printk(KERN_ERR "PSS: WSS DMA allocation error\n");
+		return 0;
+	}
+	/*
+	 * For some reason the card returns 0xff in the WSS status register
+	 * immediately after boot. Probably MIDI+SB emulation algorithm
+	 * downloaded to the ADSP2115 spends some time initializing the card.
+	 * Let's try to wait until it finishes this task.
+	 */
+	for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) &
+	  WSS_INITIALIZING); timeout++)
+		;
+
+	outb((0x0b), hw_config->io_base + WSS_INDEX);	/* Required by some cards */
+
+	for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) &&
+	  (timeout < 100000); timeout++)
+		;
+
+	return probe_ms_sound(hw_config);
+}
+
+static void __init attach_pss_mss(struct address_info *hw_config)
+{
+	int        my_mix = -999;	/* gcc shut up */
+	
+	devc->ad_mixer_dev = NO_WSS_MIXER;
+	if (pss_mixer) 
+	{
+		if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION,
+			"PSS-SPEAKERS and AD1848 (through MSS audio codec)",
+			&pss_mixer_operations,
+			sizeof (struct mixer_operations),
+			devc)) < 0) 
+		{
+			printk(KERN_ERR "Could not install PSS mixer\n");
+			return;
+		}
+	}
+	pss_mixer_reset(devc);
+	attach_ms_sound(hw_config, THIS_MODULE);	/* Slot 0 */
+
+	if (hw_config->slots[0] != -1)
+	{
+		/* The MSS driver installed itself */
+		audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations;
+		if (pss_mixer && (num_mixers == (my_mix + 2)))
+		{
+			/* The MSS mixer installed */
+			devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev;
+		}
+	}
+}
+
+static inline void __exit unload_pss(struct address_info *hw_config)
+{
+	release_region(hw_config->io_base, 0x10);
+	release_region(hw_config->io_base+0x10, 0x9);
+}
+
+static inline void __exit unload_pss_mpu(struct address_info *hw_config)
+{
+	unload_mpu401(hw_config);
+}
+
+static inline void __exit unload_pss_mss(struct address_info *hw_config)
+{
+	unload_ms_sound(hw_config);
+}
+
+
+static struct address_info cfg;
+static struct address_info cfg2;
+static struct address_info cfg_mpu;
+
+static int pss_io __initdata	= -1;
+static int mss_io __initdata	= -1;
+static int mss_irq __initdata	= -1;
+static int mss_dma __initdata	= -1;
+static int mpu_io __initdata	= -1;
+static int mpu_irq __initdata	= -1;
+static int pss_no_sound __initdata = 0;	/* Just configure non-sound components */
+static int pss_keep_settings  = 1;	/* Keep hardware settings at module exit */
+static char *pss_firmware = "/etc/sound/pss_synth";
+
+MODULE_PARM(pss_io, "i");
+MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)");
+MODULE_PARM(mss_io, "i");
+MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)");
+MODULE_PARM(mss_irq, "i");
+MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)");
+MODULE_PARM(mss_dma, "i");
+MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)");
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)");
+MODULE_PARM(mpu_irq, "i");
+MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
+MODULE_PARM(pss_cdrom_port, "i");
+MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)");
+MODULE_PARM(pss_enable_joystick, "i");
+MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)");
+MODULE_PARM(pss_no_sound, "i");
+MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)");
+MODULE_PARM(pss_keep_settings, "i");
+MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)");
+MODULE_PARM(pss_firmware, "s");
+MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)");
+MODULE_PARM(pss_mixer, "b");
+MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards.");
+MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl");
+MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).\n");
+MODULE_LICENSE("GPL");
+
+
+static int fw_load = 0;
+static int pssmpu = 0, pssmss = 0;
+
+/*
+ *    Load a PSS sound card module
+ */
+
+static int __init init_pss(void)
+{
+
+	if(pss_no_sound)		/* If configuring only nonsound components */
+	{
+		cfg.io_base = pss_io;
+		if(!probe_pss(&cfg))
+			return -ENODEV;
+		printk(KERN_INFO "ECHO-PSS  Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff);
+		printk(KERN_INFO "PSS: loading in no sound mode.\n");
+		disable_all_emulations();
+		configure_nonsound_components();
+		return 0;
+	}
+
+	cfg.io_base = pss_io;
+
+	cfg2.io_base = mss_io;
+	cfg2.irq = mss_irq;
+	cfg2.dma = mss_dma;
+
+	cfg_mpu.io_base = mpu_io;
+	cfg_mpu.irq = mpu_irq;
+
+	if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) {
+		printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n");
+		return -EINVAL;
+	}
+
+	if (!pss_synth) {
+		fw_load = 1;
+		pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth);
+	}
+	if (!probe_pss(&cfg))
+		return -ENODEV;
+	attach_pss(&cfg);
+	/*
+	 *    Attach stuff
+	 */
+	if (probe_pss_mpu(&cfg_mpu)) {
+		pssmpu = 1;
+		attach_pss_mpu(&cfg_mpu);
+	}
+	if (probe_pss_mss(&cfg2)) {
+		pssmss = 1;
+		attach_pss_mss(&cfg2);
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_pss(void)
+{
+	if(!pss_no_sound)
+	{
+		if(fw_load && pss_synth)
+			vfree(pss_synth);
+		if(pssmss)
+			unload_pss_mss(&cfg2);
+		if(pssmpu)
+			unload_pss_mpu(&cfg_mpu);
+		unload_pss(&cfg);
+	}
+
+	if(!pss_keep_settings)	/* Keep hardware settings if asked */
+	{
+		disable_all_emulations();
+		printk(KERN_INFO "Resetting PSS sound card configurations.\n");
+	}
+}
+
+module_init(init_pss);
+module_exit(cleanup_pss);
+
+#ifndef MODULE
+static int __init setup_pss(char *str)
+{
+	/* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */
+	int ints[7];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	pss_io	= ints[1];
+	mss_io	= ints[2];
+	mss_irq	= ints[3];
+	mss_dma	= ints[4];
+	mpu_io	= ints[5];
+	mpu_irq	= ints[6];
+
+	return 1;
+}
+
+__setup("pss=", setup_pss);
+#endif
diff -Nru linux/sound/oss/rme96xx.c linux-2.4.19-pre5-mjc/sound/oss/rme96xx.c
--- linux/sound/oss/rme96xx.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/rme96xx.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1565 @@
+/* (C) 2000 Guenter Geiger <geiger@debian.org>
+   with copy/pastes from the driver of Winfried Ritsch <ritsch@iem.kug.ac.at>
+   based on es1370.c
+
+
+
+   *  10 Jan 2001: 0.1 initial version
+   *  19 Jan 2001: 0.2 fixed bug in select()
+   *  27 Apr 2001: 0.3 more than one card usable
+   *  11 May 2001: 0.4 fixed for SMP, included into kernel source tree
+   *  17 May 2001: 0.5 draining code didn't work on new cards
+   *  18 May 2001: 0.6 remove synchronize_irq() call 
+
+TODO:
+   - test more than one card --- done
+   - check for pci IOREGION (see es1370) in rme96xx_probe ??
+   - error detection
+   - mmap interface
+   - mixer mmap interface
+   - mixer ioctl
+   - get rid of noise upon first open (why ??)
+   - allow multiple open(at least for read)
+   - allow multiple open for non overlapping regions
+   - recheck the multiple devices part (offsets of different devices, etc)
+   - do decent draining in _release --- done
+   - SMP support
+*/
+
+#ifndef RMEVERSION
+#define RMEVERSION "0.6"
+#endif
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include "rme96xx.h"
+
+#define NR_DEVICE 2
+
+static int devices = 1;
+MODULE_PARM(devices, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(devices, "number of dsp devices allocated by the driver");
+
+
+MODULE_AUTHOR("Guenter Geiger, geiger@debian.org");
+MODULE_DESCRIPTION("RME9652/36 \"Hammerfall\" Driver");
+MODULE_LICENSE("GPL");
+
+
+#ifdef DEBUG
+#define DBG(x) printk("RME_DEBUG:");x
+#define COMM(x) printk("RME_COMM: " x "\n");
+#else
+#define DBG(x) while (0) {}
+#define COMM(x)
+#endif
+
+/*-------------------------------------------------------------------------- 
+                        Preporcessor Macros and Definitions
+ --------------------------------------------------------------------------*/
+
+#define RME96xx_MAGIC 0x6473
+
+/* Registers-Space in offsets from base address with 16MByte size */
+
+#define RME96xx_IO_EXTENT     16l*1024l*1024l
+#define RME96xx_CHANNELS_PER_CARD 26
+
+/*                  Write - Register */
+
+/* 0,4,8,12,16,20,24,28 ... hardware init (erasing fifo-pointer intern) */
+#define RME96xx_num_of_init_regs   8
+
+#define RME96xx_init_buffer       (0/4)
+#define RME96xx_play_buffer       (32/4)  /* pointer to 26x64kBit RAM from mainboard */
+#define RME96xx_rec_buffer        (36/4)  /* pointer to 26x64kBit RAM from mainboard */
+#define RME96xx_control_register  (64/4)  /* exact meaning see below */
+#define RME96xx_irq_clear         (96/4)  /* irq acknowledge */
+#define RME96xx_time_code         (100/4) /* if used with alesis adat */
+#define RME96xx_thru_base         (128/4) /* 132...228 Thru for 26 channels */
+#define RME96xx_thru_channels     RME96xx_CHANNELS_PER_CARD
+
+/*                     Read Register */
+
+#define RME96xx_status_register    0     /* meaning see below */
+
+
+
+/* Status Register: */
+/* ------------------------------------------------------------------------ */
+#define RME96xx_IRQ          0x0000001 /* IRQ is High if not reset by RMExx_irq_clear */
+#define RME96xx_lock_2       0x0000002 /* ADAT 3-PLL: 1=locked, 0=unlocked */
+#define RME96xx_lock_1       0x0000004 /* ADAT 2-PLL: 1=locked, 0=unlocked */
+#define RME96xx_lock_0       0x0000008 /* ADAT 1-PLL: 1=locked, 0=unlocked */
+
+#define RME96xx_fs48         0x0000010 /* sample rate 0 ...44.1/88.2,  1 ... 48/96 Khz */
+#define RME96xx_wsel_rd      0x0000020 /* if Word-Clock is used and valid then 1 */
+#define RME96xx_buf_pos1     0x0000040 /* Bit 6..15 : Position of buffer-pointer in 64Bytes-blocks */
+#define RME96xx_buf_pos2     0x0000080 /* resolution +/- 1 64Byte/block (since 64Bytes bursts) */
+ 
+#define RME96xx_buf_pos3     0x0000100 /* 10 bits = 1024 values */
+#define RME96xx_buf_pos4     0x0000200 /* if we mask off the first 6 bits, we can take the status */
+#define RME96xx_buf_pos5     0x0000400 /* register as sample counter in the hardware buffer */
+#define RME96xx_buf_pos6     0x0000800 
+
+#define RME96xx_buf_pos7     0x0001000 
+#define RME96xx_buf_pos8     0x0002000 
+#define RME96xx_buf_pos9     0x0004000
+#define RME96xx_buf_pos10    0x0008000 
+
+#define RME96xx_sync_2       0x0010000 /* if ADAT-IN3 synced to system clock */
+#define RME96xx_sync_1       0x0020000 /* if ADAT-IN2 synced to system clock */
+#define RME96xx_sync_0       0x0040000 /* if ADAT-IN1 synced to system clock */
+#define RME96xx_DS_rd        0x0080000 /* 1=Double Speed, 0=Normal Speed */
+
+#define RME96xx_tc_busy      0x0100000 /* 1=time-code copy in progress (960ms) */
+#define RME96xx_tc_out       0x0200000 /* time-code out bit */
+#define RME96xx_F_0          0x0400000 /*  000=64kHz, 100=88.2kHz, 011=96kHz  */
+#define RME96xx_F_1          0x0800000 /*  111=32kHz, 110=44.1kHz, 101=48kHz, */
+
+#define RME96xx_F_2          0x1000000 /*  od external Crystal Chip if ERF=1*/
+#define RME96xx_ERF          0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/
+#define RME96xx_buffer_id    0x4000000 /* toggles by each interrupt on rec/play */
+#define RME96xx_tc_valid     0x8000000 /* 1 = a signal is detected on time-code input */
+
+/* Status Register Fields */
+
+#define RME96xx_lock            (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2)
+#define RME96xx_buf_pos          0x000FFC0 
+#define RME96xx_sync            (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2)
+#define RME96xx_F               (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2)
+
+
+
+/* Control-Register: */			    
+/*--------------------------------------------------------------------------------*/
+
+#define RME96xx_start_bit	   0x0001    /* start record/play */
+#define RME96xx_latency0	   0x0002    /* Bit 0 -  Buffer size or latency */
+#define RME96xx_latency1	   0x0004    /* Bit 1 - Buffer size or latency */
+#define RME96xx_latency2	   0x0008    /* Bit 2 - Buffer size or latency */
+
+#define RME96xx_Master		   0x0010   /* Clock Mode Master=1,Slave/Auto=0 */
+#define RME96xx_IE		   0x0020   /* Interupt Enable */
+#define RME96xx_freq		   0x0040   /* samplerate 0=44.1/88.2, 1=48/96 kHz*/
+
+
+#define RME96xx_DS                 0x0100   /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
+#define RME96xx_PRO		   0x0200    /* spdif 0=consumer, 1=professional Mode*/
+#define RME96xx_EMP		   0x0400    /* spdif Emphasis 0=None, 1=ON */
+#define RME96xx_Dolby		   0x0800    /* spdif Non-audio bit 1=set, 0=unset */
+
+#define RME96xx_opt_out	           0x1000  /* Use 1st optical OUT as SPDIF: 1=yes,0=no */
+#define RME96xx_wsel               0x2000  /* use Wordclock as sync (overwrites master)*/
+#define RME96xx_inp_0              0x4000  /* SPDIF-IN: 00=optical (ADAT1),     */
+#define RME96xx_inp_1              0x8000  /* 01=koaxial (Cinch), 10=Internal CDROM*/
+
+#define RME96xx_SyncRef0           0x10000  /* preferred sync-source in autosync */
+#define RME96xx_SyncRef1           0x20000  /* 00=ADAT1,01=ADAT2,10=ADAT3,11=SPDIF */
+
+
+#define RME96xx_ctrl_init            (RME96xx_latency0 |\
+                                     RME96xx_Master |\
+                                     RME96xx_inp_1)
+                              
+
+
+/* Control register fields and shortcuts */
+
+#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2)
+#define RME96xx_inp         (RME96xx_inp_0|RME96xx_inp_1)
+#define RME96xx_SyncRef    (RME96xx_SyncRef0|RME96xx_SyncRef1)
+/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit0 */
+
+#define RME96xx_SET_LATENCY(x)   (((x)&0x7)<<1)
+#define RME96xx_GET_LATENCY(x)   (((x)>>1)&0x7)
+#define RME96xx_SET_inp(x) (((x)&0x3)<<14)
+#define RME96xx_GET_inp(x)   (((x)>>14)&0x3)
+#define RME96xx_SET_SyncRef(x) (((x)&0x3)<<17)
+#define RME96xx_GET_SyncRef(x)   (((x)>>17)&0x3)
+
+
+/* buffer sizes */
+#define RME96xx_BYTES_PER_SAMPLE  4 /* sizeof(u32) */
+#define RME_16K 16*1024
+
+#define RME96xx_DMA_MAX_SAMPLES  (RME_16K)
+#define RME96xx_DMA_MAX_SIZE     (RME_16K * RME96xx_BYTES_PER_SAMPLE)
+#define RME96xx_DMA_MAX_SIZE_ALL (RME96xx_DMA_MAX_SIZE * RME96xx_CHANNELS_PER_CARD)
+
+#define RME96xx_NUM_OF_FRAGMENTS     2
+#define RME96xx_FRAGMENT_MAX_SIZE    (RME96xx_DMA_MAX_SIZE/2)
+#define RME96xx_FRAGMENT_MAX_SAMPLES (RME96xx_DMA_MAX_SAMPLES/2)
+#define RME96xx_MAX_LATENCY       7   /* 16k samples */
+
+
+#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */
+
+#define RME_MESS "rme96xx:"
+/*------------------------------------------------------------------------ 
+                  Types, struct and function declarations 
+ ------------------------------------------------------------------------*/
+
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT RME_MESS" invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != RME96xx_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+
+static struct file_operations rme96xx_audio_fops;
+static struct file_operations rme96xx_mixer_fops;
+static int numcards;
+
+typedef int32_t raw_sample_t;
+
+typedef struct _rme96xx_info {
+
+	/* hardware settings */
+	int magic;
+	struct pci_dev * pcidev; /* pci_dev structure */
+	unsigned long *iobase;	
+	unsigned int irq;
+
+	/* list of rme96xx devices */
+	struct list_head devs;
+
+	spinlock_t lock;
+
+	u32 *recbuf;             /* memory for rec buffer */
+	u32 *playbuf;            /* memory for play buffer */
+
+	u32 control_register;
+
+	u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */
+
+	int open_count;
+
+
+	int rate;
+	int latency;
+	unsigned int fragsize;
+	int started;
+
+	int hwptr; /* can be negativ because of pci burst offset  */
+	unsigned int hwbufid;  /* set by interrupt, buffer which is written/read now */
+	
+	struct dmabuf {
+
+		unsigned int format;
+		int formatshift;
+		int inchannels;       /* number of channels for device */
+		int outchannels;       /* number of channels for device */
+		int mono; /* if true, we play mono on 2 channels */
+		int inoffset; /* which channel is considered the first one */
+         	int outoffset;
+		
+		/* state */
+		int opened;               /* open() made */
+		int started;              /* first write/read */
+		int mmapped;              /* mmap */
+		int open_mode;
+
+		struct _rme96xx_info *s;  
+
+		/* pointer to read/write position in buffer */
+		unsigned readptr;          
+		unsigned writeptr;          
+
+		unsigned error; /* over/underruns cleared on sync again */
+
+		/* waiting and locking */
+		wait_queue_head_t wait;
+		struct semaphore  open_sem;
+		wait_queue_head_t open_wait;
+
+	} dma[RME96xx_MAX_DEVS]; 
+
+	int dspnum[RME96xx_MAX_DEVS];  /* register with sound subsystem */ 
+	int mixer;  /* register with sound subsystem */ 
+} rme96xx_info;
+
+
+/* fiddling with the card (first level hardware control) */
+
+inline void rme96xx_set_ctrl(rme96xx_info* s,int mask)
+{
+
+	s->control_register|=mask;
+	writel(s->control_register,s->iobase + RME96xx_control_register);
+
+}
+
+inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask)
+{
+
+	s->control_register&=(~mask);
+	writel(s->control_register,s->iobase + RME96xx_control_register);
+
+}
+
+
+
+/* the hwbuf in the status register seems to have some jitter, to get rid of
+   it, we first only let the numbers grow, to be on the secure side we 
+   subtract a certain amount RME96xx_BURSTBYTES from the resulting number */
+
+/* the function returns the hardware pointer in bytes */
+#define RME96xx_BURSTBYTES -64  /* bytes by which hwptr could be off */
+
+inline int rme96xx_gethwptr(rme96xx_info* s,int exact)
+{
+	long flags;
+	if (exact) {
+		unsigned int hwp;
+/* the hwptr seems to be rather unreliable :(, so we don't use it */
+		spin_lock_irqsave(&s->lock,flags);
+		
+		hwp  = readl(s->iobase + RME96xx_status_register) & 0xffc0;
+		s->hwptr = (hwp < s->hwptr) ? s->hwptr : hwp;
+//		s->hwptr = hwp;
+
+		spin_unlock_irqrestore(&s->lock,flags);
+		return (s->hwptr+RME96xx_BURSTBYTES) & ((s->fragsize<<1)-1);
+	}
+	return (s->hwbufid ? s->fragsize : 0);
+}
+
+inline void rme96xx_setlatency(rme96xx_info* s,int l)
+{
+	s->latency = l;
+	s->fragsize = 1<<(8+l);
+	rme96xx_unset_ctrl(s,RME96xx_latency);
+	rme96xx_set_ctrl(s,RME96xx_SET_LATENCY(l));	
+}
+
+
+static void rme96xx_clearbufs(struct dmabuf* dma)
+{
+	int i,j;
+	unsigned long flags;
+
+	/* clear dmabufs */
+	for(i=0;i<devices;i++) {
+		for (j=0;j<dma->outchannels + dma->mono;j++)
+			memset(&dma->s->playbuf[(dma->outoffset + j)*RME96xx_DMA_MAX_SAMPLES], 
+			       0, RME96xx_DMA_MAX_SIZE);
+	}
+	spin_lock_irqsave(&dma->s->lock,flags);
+	dma->writeptr = 0;
+	dma->readptr = 0;
+	spin_unlock_irqrestore(&dma->s->lock,flags);
+}
+
+static int rme96xx_startcard(rme96xx_info *s,int stop)
+{
+	int i;
+	long flags;
+
+	COMM       ("startcard");
+	if(s->control_register & RME96xx_IE){
+		/* disable interrupt first */
+		
+		rme96xx_unset_ctrl( s,RME96xx_start_bit );
+		udelay(10);
+		rme96xx_unset_ctrl( s,RME96xx_IE);
+		spin_lock_irqsave(&s->lock,flags); /* timing is critical */
+		s->started = 0;
+		spin_unlock_irqrestore(&s->lock,flags);
+		if (stop) {
+		     COMM("Sound card stopped");
+		     return 1;
+		}
+	}
+	COMM       ("interupt disabled");
+	/* first initialize all pointers on card */
+	for(i=0;i<RME96xx_num_of_init_regs;i++){
+		writel(0,s->iobase + i);
+		udelay(10); /* ?? */
+	}
+	COMM       ("regs cleaned");
+
+	spin_lock_irqsave(&s->lock,flags); /* timing is critical */
+	udelay(10);
+	s->started = 1;
+	s->hwptr = 0;
+	spin_unlock_irqrestore(&s->lock,flags);
+
+	rme96xx_set_ctrl( s, RME96xx_IE | RME96xx_start_bit);
+
+
+	COMM("Sound card started");
+  
+	return 1;
+}
+
+
+inline int rme96xx_getospace(struct dmabuf * dma, unsigned int hwp)
+{
+	int cnt;
+	int  swptr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dma->s->lock,flags); 
+	swptr = dma->writeptr;
+	cnt = (hwp - swptr);
+	
+	if (cnt < 0) {
+	     cnt = ((dma->s->fragsize<<1) - swptr);
+	}
+	spin_unlock_irqrestore(&dma->s->lock,flags);
+	return cnt;
+}
+
+inline int rme96xx_getispace(struct dmabuf * dma, unsigned int hwp)
+{
+	int cnt;
+	int  swptr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dma->s->lock,flags); 
+	swptr = dma->readptr;
+	cnt = (hwp - swptr);
+	 
+	if (cnt < 0) {
+		cnt = ((dma->s->fragsize<<1) - swptr);
+	}
+	spin_unlock_irqrestore(&dma->s->lock,flags);
+	return cnt;
+}
+
+
+inline int rme96xx_copyfromuser(struct dmabuf* dma,const char* buffer,int count,int hop)
+{
+	int swptr = dma->writeptr;
+	switch (dma->format) {
+	case AFMT_S32_BLOCKED:
+	{
+	     char* buf = (char*)buffer;
+	     int cnt = count/dma->outchannels;
+	     int i;
+	     for (i=0;i < dma->outchannels;i++) {
+		  char* hwbuf =(char*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES];
+		  hwbuf+=swptr;
+
+		  if (copy_from_user(hwbuf,buf, cnt))
+		       return -1;
+		  buf+=hop;
+	     }
+	     swptr+=cnt;
+	     break;
+	}
+	case AFMT_S16_LE:
+	{
+	     int i,j;
+	     int cnt = count/dma->outchannels;
+	     for (i=0;i < dma->outchannels + dma->mono;i++) {
+		     short* sbuf = (short*)buffer + i*(!dma->mono);
+		     short* hwbuf =(short*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES];	     
+		     hwbuf+=(swptr>>1);
+		     for (j=0;j<(cnt>>1);j++) {
+			     hwbuf++; /* skip the low 16 bits */
+			     __get_user(*hwbuf++,sbuf++);
+			     sbuf+=(dma->outchannels-1);
+		     }
+	     }
+	     swptr += (cnt<<1);
+	     break;
+	}
+	default:
+	     printk(RME_MESS" unsupported format\n");
+	     return -1;
+	} /* switch */
+
+	swptr&=((dma->s->fragsize<<1) -1);
+	dma->writeptr = swptr;
+
+	return 0;
+}
+
+/* The count argument is the number of bytes */
+inline int rme96xx_copytouser(struct dmabuf* dma,const char* buffer,int count,int hop)
+{
+	int swptr = dma->readptr;
+	switch (dma->format) {
+	case AFMT_S32_BLOCKED:
+	{
+	     char* buf = (char*)buffer;
+	     int cnt = count/dma->inchannels;
+	     int i;
+
+	     for (i=0;i < dma->inchannels;i++) {
+		  char* hwbuf =(char*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES];
+		  hwbuf+=swptr;
+
+		  if (copy_to_user(buf,hwbuf,cnt))
+		       return -1;
+		  buf+=hop;
+	     }
+	     swptr+=cnt;
+	     break;
+	}
+	case AFMT_S16_LE:
+	{
+	     int i,j;
+	     int cnt = count/dma->inchannels;
+	     for (i=0;i < dma->inchannels;i++) {
+		  short* sbuf = (short*)buffer + i;
+		  short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES];	     
+		  hwbuf+=(swptr>>1);
+		  for (j=0;j<(cnt>>1);j++) {
+		       hwbuf++;
+		       __put_user(*hwbuf++,sbuf++);
+		       sbuf+=(dma->inchannels-1);
+		  }
+	     }
+	     swptr += (cnt<<1);
+	     break;
+	}
+	default:
+	     printk(RME_MESS" unsupported format\n");
+	     return -1;
+	} /* switch */
+	
+	swptr&=((dma->s->fragsize<<1) -1);	
+	dma->readptr = swptr;
+	return 0;
+}
+
+
+static void rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int i;
+	rme96xx_info *s = (rme96xx_info *)dev_id;
+	struct dmabuf *db;
+	u32 status;
+	unsigned long flags;
+
+	status = readl(s->iobase + RME96xx_status_register);
+	if (!(status & RME96xx_IRQ)) {
+		return;
+	}
+
+	spin_lock_irqsave(&s->lock,flags);
+	writel(0,s->iobase + RME96xx_irq_clear);
+
+	s->hwbufid = (status & RME96xx_buffer_id)>>26;	
+	if ((status & 0xffc0) <= 256) s->hwptr = 0; 
+	for(i=0;i<devices;i++)
+	{
+		db = &(s->dma[i]);
+		if(db->started > 0)
+			wake_up(&(db->wait));		
+	}  
+	spin_unlock_irqrestore(&s->lock,flags);
+}
+
+
+
+/*---------------------------------------------------------------------------- 
+ PCI detection and module initialization stuff 
+ ----------------------------------------------------------------------------*/
+
+void* busmaster_malloc(int size) {
+     int pg; /* 2 s exponent of memory size */
+        char *buf;
+
+        DBG(printk("kernel malloc pages ..\n"));
+        
+        for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+
+        buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg);
+
+        if (buf) {
+                struct page* page, *last_page;
+
+                page = virt_to_page(buf);
+                last_page = virt_to_page(buf + (1 << pg));
+                DBG(printk("setting reserved bit\n"));
+                while (page < last_page) {
+			SetPageReserved(page);
+                        page++;
+                }
+		return buf;
+        }
+	DBG(printk("allocated %ld",(long)buf));
+	return NULL;
+}
+
+void busmaster_free(void* ptr,int size) {
+        int pg;
+	struct page* page, *last_page;
+
+        if (ptr == NULL)
+                return;
+
+        for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+
+        page = virt_to_page(ptr);
+        last_page = page + (1 << pg);
+        while (page < last_page) {
+		ClearPageReserved(page);
+		page++;
+	}
+	DBG(printk("freeing pages\n"));
+        free_pages((unsigned long) ptr, pg);
+	DBG(printk("done\n"));
+}
+
+/* initialize those parts of the info structure which are not pci detectable resources */
+
+static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) {
+
+	init_MUTEX(&dma->open_sem);
+	init_waitqueue_head(&dma->open_wait);
+	init_waitqueue_head(&dma->wait);
+	dma->s = s; 
+	dma->error = 0;
+
+	dma->format = AFMT_S32_BLOCKED;
+	dma->formatshift = 0;
+	dma->inchannels = dma->outchannels = 1;
+	dma->inoffset = ioffset;
+	dma->outoffset = ooffset;
+
+	dma->opened=0;
+	dma->started=0;
+	dma->mmapped=0;
+	dma->open_mode=0;
+	dma->mono=0;
+
+	rme96xx_clearbufs(dma);
+	return 0;
+}
+
+
+int rme96xx_init(rme96xx_info* s)
+{
+	int i;
+	DBG(printk(__FUNCTION__"\n"));
+	numcards++;
+
+	s->magic = RME96xx_MAGIC; 
+
+	spin_lock_init(&s->lock);
+
+	COMM            ("setup busmaster memory")
+	s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL);
+	s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL);
+
+	if (!s->recbuf || !s->playbuf) {
+		printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n");
+		return -ENODEV;
+	}
+
+	COMM            ("setting rec and playbuffers")
+
+	writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer);
+  	writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer);
+
+	COMM             ("initializing control register")
+	rme96xx_unset_ctrl(s,0xffffffff);
+	rme96xx_set_ctrl(s,RME96xx_ctrl_init);
+
+
+	COMM              ("setup devices")	
+	for (i=0;i < devices;i++) {
+		struct dmabuf * dma = &s->dma[i];
+		rme96xx_dmabuf_init(s,dma,2*i,2*i);
+	}
+
+	s->started = 0;
+	rme96xx_setlatency(s,7);
+
+	printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); 
+	return 0;
+}
+
+
+/* open uses this to figure out which device was opened .. this seems to be 
+   unnecessary complex */
+
+static LIST_HEAD(devs);
+
+static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+{
+	int i;
+	rme96xx_info *s;
+
+	DBG(printk(__FUNCTION__"\n"));
+	
+	if (pcidev->irq == 0) 
+		return -1;
+	if (!pci_dma_supported(pcidev, 0xffffffff)) {
+		printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n");
+		return -1;
+	}
+	if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) {
+		printk(KERN_WARNING RME_MESS" out of memory\n");
+		return -1;
+	}
+	memset(s, 0, sizeof(rme96xx_info));
+
+	s->pcidev = pcidev;
+	s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT);
+	s->irq = pcidev->irq;
+
+        DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq));
+
+	if (pci_enable_device(pcidev))
+		goto err_irq;
+	if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "es1370", s)) {
+		printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+	
+	/* initialize the card */
+
+	i = 0;
+	if (rme96xx_init(s) < 0) {
+		printk(KERN_ERR RME_MESS" initialization failed\n");
+		goto err_devices;
+	}
+	for (i=0;i<devices;i++) {
+		if ((s->dspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0)
+			goto err_devices;
+	}
+
+	if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0)
+		goto err_devices;
+
+	pci_set_drvdata(pcidev, s);
+	pcidev->dma_mask = 0xffffffff; /* ????? */
+	/* put it into driver list */
+	list_add_tail(&s->devs, &devs);
+
+	DBG(printk("initialization successful\n"));
+	return 0;
+
+	/* error handler */
+ err_devices:
+	while (i--) 
+		unregister_sound_dsp(s->dspnum[i]);
+	free_irq(s->irq,s);
+ err_irq:
+	kfree(s);
+	return -1;
+}
+
+
+static void __devinit rme96xx_remove(struct pci_dev *dev)
+{
+	int i;
+	rme96xx_info *s = pci_get_drvdata(dev);
+
+	if (!s) {
+		printk(KERN_ERR"device structure not valid\n");
+		return ;
+	}
+
+	if (s->started) rme96xx_startcard(s,0);
+
+	i = devices;
+	while (i) {
+		i--;
+		unregister_sound_dsp(s->dspnum[i]);
+	}
+	
+	unregister_sound_mixer(s->mixer);
+/*	synchronize_irq(); This call got lost somehow ? */
+        free_irq(s->irq,s);
+	busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL);
+	busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL);
+	kfree(s);
+	pci_set_drvdata(dev, NULL);
+}
+
+
+#ifndef PCI_VENDOR_ID_RME 
+#define PCI_VENDOR_ID_RME 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_RME9652
+#define PCI_DEVICE_ID_RME9652 0x3fc4
+#endif
+#ifndef PCI_ANY_ID
+#define PCI_ANY_ID 0
+#endif
+
+static struct pci_device_id id_table[] __devinitdata = {
+	{ PCI_VENDOR_ID_RME, PCI_DEVICE_ID_RME9652, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver rme96xx_driver = {
+	name: "rme96xx",
+	id_table: id_table,
+	probe: rme96xx_probe,
+	remove: rme96xx_remove
+};
+
+static int __init init_rme96xx(void)
+{
+  
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices);
+        numcards = 0;
+	return pci_module_init(&rme96xx_driver);
+}
+
+static void __exit cleanup_rme96xx(void)
+{
+	printk(KERN_INFO RME_MESS" unloading\n");
+	pci_unregister_driver(&rme96xx_driver);
+}
+
+module_init(init_rme96xx);
+module_exit(cleanup_rme96xx);
+
+
+
+
+
+/*-------------------------------------------------------------------------- 
+   Implementation of file operations 
+---------------------------------------------------------------------------*/
+
+#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED)
+
+
+static int rme96xx_ioctl(struct inode *in, struct file *file, 
+						unsigned int cmd, unsigned long arg)
+{
+
+
+	struct dmabuf * dma = (struct dmabuf *)file->private_data; 
+	rme96xx_info *s = dma->s;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int count;
+	int val = 0;
+
+	VALIDATE_STATE(s);
+
+	DBG(printk("ioctl %ud\n",cmd));
+
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+#if 0
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);
+#endif
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+//		rme96xx_clearbufs(dma);
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+/* generally it's not a problem if we change the speed 
+			if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE))
+				return -EINVAL;
+*/
+			spin_lock_irqsave(&s->lock, flags);
+
+			switch (val) {
+			case 44100:
+			case 88200:
+				rme96xx_unset_ctrl(s,RME96xx_freq);
+				break;
+			case 48000: 
+			case 96000: 
+				rme96xx_set_ctrl(s,RME96xx_freq);
+				break;
+			default:
+				rme96xx_unset_ctrl(s,RME96xx_freq);
+				val = 44100;
+			}
+			if (val > 50000)
+				rme96xx_set_ctrl(s,RME96xx_DS);
+			else
+				rme96xx_unset_ctrl(s,RME96xx_DS);
+			s->rate = val;
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		DBG(printk("speed set to %d\n",val));
+		return put_user(val, (int *)arg);
+		
+        case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		
+		if (!val) {
+			DBG(printk("setting to mono\n")); 
+			dma->mono=1; 
+			dma->inchannels = 1;
+			dma->outchannels = 1;
+		}
+		else {
+			DBG(printk("setting to stereo\n")); 
+			dma->mono = 0;
+			dma->inchannels = 2;
+			dma->outchannels = 2;
+		}
+		return 0;
+        case SNDCTL_DSP_CHANNELS:
+		/* remember to check for resonable offset/channel pairs here */
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (file->f_mode & FMODE_WRITE) { 			
+			if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) 
+				dma->outchannels = val;
+			else
+				dma->outchannels = val = 2;
+			DBG(printk("setting to outchannels %d\n",val)); 
+		}
+		if (file->f_mode & FMODE_READ) {
+			if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) 
+				dma->inchannels = val;
+			else
+				dma->inchannels = val = 2;
+			DBG(printk("setting to inchannels %d\n",val)); 
+		}
+
+		dma->mono=0;
+
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(RME96xx_FMT, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		DBG(printk("setting to format %x\n",val)); 
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			if (val & RME96xx_FMT)
+				dma->format = val;
+			switch (dma->format) {
+			case AFMT_S16_LE:
+				dma->formatshift=1;
+				break;
+			case AFMT_S32_BLOCKED:
+				dma->formatshift=0;
+				break;
+			}
+		}
+		return put_user(dma->format, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+#if 0
+		if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) 
+			val |= PCM_ENABLE_OUTPUT;
+#endif
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+#if 0
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+					return ret;
+				start_dac2(s);
+			} else
+				stop_dac2(s);
+		}
+#endif
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+
+		val = rme96xx_gethwptr(dma->s,0);
+
+
+		count = rme96xx_getospace(dma,val);
+		if (!s->started) count = s->fragsize*2;
+		abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift;
+                abinfo.bytes = (count*dma->outchannels)>>dma->formatshift;
+                abinfo.fragstotal = 2;
+                abinfo.fragments = (count > s->fragsize); 
+
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+
+		val = rme96xx_gethwptr(dma->s,0);
+
+		count = rme96xx_getispace(dma,val);
+
+		abinfo.fragsize = (s->fragsize*dma->inchannels)>>dma->formatshift;
+                abinfo.bytes = (count*dma->inchannels)>>dma->formatshift;;
+                abinfo.fragstotal = 2;
+                abinfo.fragments = count > s->fragsize; 
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY: /* What shold this exactly do ? , 
+				      ATM it is just abinfo.bytes */
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+
+		val = rme96xx_gethwptr(dma->s,0);
+		count = val - dma->readptr;
+		if (count < 0)
+			count += s->fragsize<<1;
+
+		return put_user(count, (int *)arg);
+
+
+/* check out how to use mmaped mode (can only be blocked !!!) */
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		val = rme96xx_gethwptr(dma->s,0);
+		spin_lock_irqsave(&s->lock,flags);
+                cinfo.bytes = s->fragsize<<1;;
+		count = val - dma->readptr;
+		if (count < 0)
+			count += s->fragsize<<1;
+
+                cinfo.blocks = (count > s->fragsize); 
+                cinfo.ptr = val;
+		if (dma->mmapped)
+			dma->readptr &= s->fragsize<<1;
+		spin_unlock_irqrestore(&s->lock,flags);
+
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		val = rme96xx_gethwptr(dma->s,0);
+		spin_lock_irqsave(&s->lock,flags);
+                cinfo.bytes = s->fragsize<<1;;
+		count = val - dma->writeptr;
+		if (count < 0)
+			count += s->fragsize<<1;
+
+                cinfo.blocks = (count > s->fragsize); 
+                cinfo.ptr = val;
+		if (dma->mmapped)
+			dma->writeptr &= s->fragsize<<1;
+		spin_unlock_irqrestore(&s->lock,flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+        case SNDCTL_DSP_GETBLKSIZE:
+	     return put_user(s->fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		val&=0xffff;
+		val -= 7;
+		if (val < 0) val = 0;
+		if (val > 7) val = 7;
+		rme96xx_setlatency(s,val);
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+#if 0
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac2.subdivision = val;
+#endif		
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user(s->rate, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user(dma->outchannels, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		switch (dma->format) {
+			case AFMT_S32_BLOCKED:
+				val = 32;
+				break;
+			case AFMT_S16_LE:
+				val = 16;
+				break;
+		}
+		return put_user(val, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+
+
+	return -ENODEV;
+}
+
+
+
+static int rme96xx_open(struct inode *in, struct file *f)
+{
+	int minor = minor(in->i_rdev);
+	struct list_head *list;
+	int devnum = ((minor-3)/16) % devices; /* default = 0 */
+	rme96xx_info *s;
+	struct dmabuf* dma;
+	DECLARE_WAITQUEUE(wait, current); 
+
+	DBG(printk("device num %d open\n",devnum));
+
+/* ??? */
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, rme96xx_info, devs);
+		if (!((s->dspnum[devnum] ^ minor) & ~0xf)) 
+			break;
+	}
+       	VALIDATE_STATE(s);
+/* ??? */
+
+	dma = &s->dma[devnum];
+	f->private_data = dma;
+	/* wait for device to become free */
+	down(&s->dma[devnum].open_sem);
+	while (dma->open_mode & f->f_mode) {
+		if (f->f_flags & O_NONBLOCK) {
+			up(&dma->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&dma->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&dma->open_sem);
+		schedule();
+		remove_wait_queue(&dma->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&dma->open_sem);
+	}
+
+	COMM                ("hardware open")
+
+	if (!s->dma[devnum].opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset);
+
+	s->dma[devnum].open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE));
+	s->dma[devnum].opened = 1;
+	up(&s->dma[devnum].open_sem);
+
+	DBG(printk("device num %d open finished\n",devnum));
+	return 0;
+}
+
+static int rme96xx_release(struct inode *in, struct file *file)
+{
+	struct dmabuf * dma = (struct dmabuf*) file->private_data;
+	int hwp;
+	DBG(printk(__FUNCTION__"\n"));
+
+	COMM          ("draining")
+	if (dma->open_mode & FMODE_WRITE) {
+#if 0 /* Why doesn't this work with some cards ?? */
+	     hwp = rme96xx_gethwptr(dma->s,0);
+	     while (rme96xx_getospace(dma,hwp)) {
+		  interruptible_sleep_on(&(dma->wait));
+		  hwp = rme96xx_gethwptr(dma->s,0);
+	     }
+#endif
+	     rme96xx_clearbufs(dma);
+	}
+
+	dma->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+
+	if (!(dma->open_mode & (FMODE_READ|FMODE_WRITE))) {
+	     dma->opened = 0;
+	     if (dma->s->started) rme96xx_startcard(dma->s,1);
+	}
+
+	wake_up(&dma->open_wait);
+	up(&dma->open_sem);
+
+	return 0;
+}
+
+
+static ssize_t rme96xx_write(struct file *file, const char *buffer, 
+									  size_t count, loff_t *ppos)
+{
+	struct dmabuf *dma = (struct dmabuf *)file->private_data;
+	ssize_t ret = 0;
+	int cnt; /* number of bytes from "buffer" that will/can be used */
+	int hop = count/dma->outchannels;
+	int hwp;
+	int exact = (file->f_flags & O_NONBLOCK); 
+
+
+	if(dma == NULL || (dma->s) == NULL) 
+		return -ENXIO;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (dma->mmapped || !dma->opened)
+		return -ENXIO;
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+
+	if (! (dma->open_mode  & FMODE_WRITE))
+                return -ENXIO;
+
+	if (!dma->s->started) rme96xx_startcard(dma->s,exact);
+	hwp = rme96xx_gethwptr(dma->s,0);
+
+	if(!(dma->started)){		 
+		COMM          ("first write")
+			
+		dma->readptr = hwp;
+		dma->writeptr = hwp;
+		dma->started = 1;
+		COMM          ("first write done")
+	}
+
+  	while (count > 0) {
+		cnt = rme96xx_getospace(dma,hwp);		
+		cnt>>=dma->formatshift;
+		cnt*=dma->outchannels;
+		if (cnt > count)
+			cnt = count;
+
+		if (cnt != 0) {
+		        if (rme96xx_copyfromuser(dma,buffer,cnt,hop))
+				return ret ? ret : -EFAULT;
+			count -= cnt;
+			buffer += cnt;
+			ret += cnt;
+			if (count == 0) return ret;
+		}
+		if (file->f_flags & O_NONBLOCK)
+			return ret ? ret : -EAGAIN;
+		
+		if ((hwp - dma->writeptr) <= 0) {
+			interruptible_sleep_on(&(dma->wait));
+			
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+		}			
+
+		hwp = rme96xx_gethwptr(dma->s,exact);
+
+	}; /* count > 0 */
+
+	return ret;
+}
+
+static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t *ppos)
+{ 
+	struct dmabuf *dma = (struct dmabuf *)file->private_data;
+	ssize_t ret = 0;
+	int cnt;
+	int hop = count/dma->inchannels;
+	int hwp;
+	int exact = (file->f_flags & O_NONBLOCK); 
+
+
+	if(dma == NULL || (dma->s) == NULL) 
+		return -ENXIO;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (dma->mmapped || !dma->opened)
+		return -ENXIO;
+
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+
+	if (! (dma->open_mode  & FMODE_READ))
+                return -ENXIO;
+
+	if (count > ((dma->s->fragsize*dma->inchannels)>>dma->formatshift))
+	    return -EFAULT;
+
+	if (!dma->s->started) rme96xx_startcard(dma->s,exact);
+	hwp = rme96xx_gethwptr(dma->s,0);
+
+	if(!(dma->started)){		 
+		COMM          ("first read")
+		     
+		dma->writeptr = hwp;
+		dma->readptr = hwp;
+		dma->started = 1;
+	}
+
+  	while (count > 0) {
+		cnt = rme96xx_getispace(dma,hwp);		
+		cnt>>=dma->formatshift;
+		cnt*=dma->inchannels;
+
+		if (cnt > count)
+			cnt = count;
+
+		if (cnt != 0) {
+		        
+			if (rme96xx_copytouser(dma,buffer,cnt,hop))
+				return ret ? ret : -EFAULT;
+			
+			count -= cnt;
+			buffer += cnt;
+			ret += cnt;
+			if (count == 0) return ret;
+		}
+		if (file->f_flags & O_NONBLOCK)
+			return ret ? ret : -EAGAIN;
+		
+		if ((hwp - dma->readptr) <= 0) {
+			interruptible_sleep_on(&(dma->wait));
+			
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+		}			
+		hwp = rme96xx_gethwptr(dma->s,exact);
+
+	}; /* count > 0 */
+
+	return ret;
+}
+
+static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) {
+	struct dmabuf *dma = (struct dmabuf *)file->private_data;
+	rme96xx_info* s = dma->s;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+
+	if (vma->vm_pgoff != 0) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	size = vma->vm_end - vma->vm_start;
+	if (size > RME96xx_DMA_MAX_SIZE) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+
+
+	if (vma->vm_flags & VM_WRITE) {
+		if (!s->started) rme96xx_startcard(s,1);
+
+		if (remap_page_range(vma->vm_start, virt_to_phys(s->playbuf + dma->outoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) {
+			unlock_kernel();
+			return -EAGAIN;
+		}
+	} 
+	else if (vma->vm_flags & VM_READ) {
+		if (!s->started) rme96xx_startcard(s,1);
+		if (remap_page_range(vma->vm_start, virt_to_phys(s->playbuf + dma->inoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) {
+			unlock_kernel();
+			return -EAGAIN;
+		}
+	} else  {
+		unlock_kernel();
+		return -EINVAL;
+	}
+
+
+/* this is the mapping */
+
+	dma->mmapped = 1;
+	unlock_kernel();
+	return 0;
+}
+
+static unsigned int rme96xx_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct dmabuf *dma = (struct dmabuf *)file->private_data;
+	rme96xx_info* s = dma->s;
+	unsigned int mask = 0;
+	unsigned int hwp,cnt;
+
+        DBG(printk("rme96xx poll_wait ...\n"));
+	VALIDATE_STATE(s);
+
+	if (!s->started) {
+		  mask |= POLLOUT | POLLWRNORM;
+	}
+	poll_wait(file, &dma->wait, wait);
+
+	hwp = rme96xx_gethwptr(dma->s,0);
+
+        DBG(printk("rme96xx poll: ..cnt %d > %d\n",cnt,s->fragsize));	
+
+	cnt = rme96xx_getispace(dma,hwp);
+
+	if (file->f_mode & FMODE_READ) 
+	     if (cnt > 0)
+		  mask |= POLLIN | POLLRDNORM;
+
+
+
+	cnt = rme96xx_getospace(dma,hwp);
+
+	if (file->f_mode & FMODE_WRITE) 
+	     if (cnt > 0)
+		  mask |= POLLOUT | POLLWRNORM;
+
+
+//        printk("rme96xx poll_wait ...%d > %d\n",rme96xx_getospace(dma,hwp),rme96xx_getispace(dma,hwp));
+
+	return mask;
+}
+
+
+static struct file_operations rme96xx_audio_fops = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	owner: THIS_MODULE,
+#endif
+	read: rme96xx_read,
+	write: rme96xx_write,
+	poll: rme96xx_poll,
+	ioctl: rme96xx_ioctl,  
+	mmap: rm96xx_mmap,
+	open: rme96xx_open,  
+	release: rme96xx_release 
+};
+
+static int rme96xx_mixer_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct list_head *list;
+	rme96xx_info *s;
+
+	COMM  ("mixer open");
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, rme96xx_info, devs);
+		if (s->mixer== minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+
+	COMM                       ("mixer opened")
+	return 0;
+}
+
+static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	rme96xx_info *s = (rme96xx_info *)file->private_data;
+	u32 status;
+
+	status = readl(s->iobase + RME96xx_status_register);
+
+	VALIDATE_STATE(s);
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		rme_mixer mixer;
+		copy_from_user(&mixer,(void*)arg,sizeof(mixer));
+		
+		if (file->f_mode & FMODE_WRITE) {
+		     s->dma[mixer.devnr].outoffset = mixer.o_offset;
+		     s->dma[mixer.devnr].inoffset = mixer.i_offset;
+		}
+
+		mixer.o_offset = s->dma[mixer.devnr].outoffset;
+		mixer.i_offset = s->dma[mixer.devnr].inoffset;
+
+		return copy_to_user((void *)arg, &mixer, sizeof(mixer)) ? -EFAULT : 0;
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) {
+		return put_user(status, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE3) {
+	     u32 control;
+	     copy_from_user(&control,(void*)arg,sizeof(control)); 
+	     if (file->f_mode & FMODE_WRITE) {
+		  s->control_register = control;
+		  writel(control,s->iobase + RME96xx_control_register);
+	     }
+
+	     return put_user(s->control_register, (int *)arg);
+	}
+	return -1;
+}
+
+
+
+static int rme96xx_mixer_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static /*const*/ struct file_operations rme96xx_mixer_fops = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	owner: THIS_MODULE,
+#endif
+	ioctl:		rme96xx_mixer_ioctl,
+	open:		rme96xx_mixer_open,
+	release:	rme96xx_mixer_release,
+};
diff -Nru linux/sound/oss/rme96xx.h linux-2.4.19-pre5-mjc/sound/oss/rme96xx.h
--- linux/sound/oss/rme96xx.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/rme96xx.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,61 @@
+/* (C) 2000 Guenter Geiger <geiger@debian.org>
+   with copy/pastes from the driver of Winfried Ritsch <ritsch@iem.kug.ac.at>
+*/
+
+
+#ifndef AFMT_S32_BLOCKED
+#define AFMT_S32_BLOCKED 0x0000400
+#endif
+
+#ifndef AFMT_S16_BLOCKED 
+#define AFMT_S16_BLOCKED 0x0000800
+#endif
+
+
+typedef struct rme_status {
+     unsigned int irq:1;    /* high or low */
+     unsigned int lockmask:3;  /* ADAT1, ADAT2, ADAT3 */
+     unsigned int sr48:1;   /* current sample rate */
+     unsigned int wclock:1; /* wordclock used ? */
+     unsigned int bufpoint:10;
+
+     unsigned int syncmask:3;  /* ADAT1, ADAT2, ADAT3 */
+     unsigned int doublespeed:1;
+     unsigned int tc_busy:1;
+     unsigned int tc_out:1;
+     unsigned int crystalrate:3;
+     unsigned int spdif_error:1;
+     unsigned int bufid:1;
+     unsigned int tc_valid:1;     
+} rme_status_t;
+
+
+typedef struct rme_control {
+     unsigned int start:1;
+     unsigned int latency:3;
+
+     unsigned int master:1;
+     unsigned int ie:1;
+     unsigned int sr48:1;
+     unsigned int spare:1;
+
+     unsigned int doublespeed:1;
+     unsigned int pro:1;
+     unsigned int emphasis:1;
+     unsigned int dolby:1;
+
+     unsigned int opt_out:1;
+     unsigned int wordclock:1;
+     unsigned int spdif_in:2;
+
+     unsigned int sync_ref:2;
+} rme_ctrl_t;
+
+
+typedef struct _rme_mixer {
+     int i_offset;
+     int o_offset;
+     int devnr;
+     int spare[8];
+} rme_mixer;
+
diff -Nru linux/sound/oss/sb.h linux-2.4.19-pre5-mjc/sound/oss/sb.h
--- linux/sound/oss/sb.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb.h	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,187 @@
+#define DSP_RESET	(devc->base + 0x6)
+#define DSP_READ	(devc->base + 0xA)
+#define DSP_WRITE	(devc->base + 0xC)
+#define DSP_COMMAND	(devc->base + 0xC)
+#define DSP_STATUS	(devc->base + 0xC)
+#define DSP_DATA_AVAIL	(devc->base + 0xE)
+#define DSP_DATA_AVL16	(devc->base + 0xF)
+#define MIXER_ADDR	(devc->base + 0x4)
+#define MIXER_DATA	(devc->base + 0x5)
+#define OPL3_LEFT	(devc->base + 0x0)
+#define OPL3_RIGHT	(devc->base + 0x2)
+#define OPL3_BOTH	(devc->base + 0x8)
+/* DSP Commands */
+
+#define DSP_CMD_SPKON		0xD1
+#define DSP_CMD_SPKOFF		0xD3
+#define DSP_CMD_DMAON		0xD0
+#define DSP_CMD_DMAOFF		0xD4
+
+#define IMODE_NONE		0
+#define IMODE_OUTPUT		PCM_ENABLE_OUTPUT
+#define IMODE_INPUT		PCM_ENABLE_INPUT
+#define IMODE_INIT		3
+#define IMODE_MIDI		4
+
+#define NORMAL_MIDI	0
+#define UART_MIDI	1
+
+
+/*
+ * Device models
+ */
+#define MDL_NONE	0
+#define MDL_SB1		1	/* SB1.0 or 1.5 */
+#define MDL_SB2		2	/* SB2.0 */
+#define MDL_SB201	3	/* SB2.01 */
+#define MDL_SBPRO	4	/* SB Pro */
+#define MDL_SB16	5	/* SB16/32/AWE */
+#define MDL_SBPNP 	6	/* SB16/32/AWE PnP */
+#define MDL_JAZZ	10	/* Media Vision Jazz16 */
+#define MDL_SMW		11	/* Logitech SoundMan Wave (Jazz16) */
+#define MDL_ESS		12	/* ESS ES688 and ES1688 */
+#define MDL_AZTECH	13	/* Aztech Sound Galaxy family */
+#define MDL_ES1868MIDI	14	/* MIDI port of ESS1868 */
+#define MDL_AEDSP	15	/* Audio Excel DSP 16 */
+#define MDL_ESSPCI	16	/* ESS PCI card */
+#define MDL_YMPCI	17	/* Yamaha PCI sb in emulation */
+
+#define SUBMDL_ALS007	42	/* ALS-007 differs from SB16 only in mixer */
+				/* register assignment */
+#define SUBMDL_ALS100	43	/* ALS-100 allows sampling rates of up */
+				/* to 48kHz */
+				
+/*
+ * Config flags
+ */
+#define SB_NO_MIDI	0x00000001
+#define SB_NO_MIXER	0x00000002
+#define SB_NO_AUDIO	0x00000004
+#define SB_NO_RECORDING	0x00000008 /* No audio recording */
+#define SB_MIDI_ONLY	(SB_NO_AUDIO|SB_NO_MIXER)
+#define SB_PCI_IRQ	0x00000010 /* PCI shared IRQ */
+
+struct mixer_def {
+	unsigned int regno: 8;
+	unsigned int bitoffs:4;
+	unsigned int nbits:4;
+};
+
+typedef struct mixer_def mixer_tab[32][2];
+typedef struct mixer_def mixer_ent;
+
+struct sb_module_options
+{
+	int  esstype;	/* ESS chip type */
+	int  acer;	/* Do acer notebook init? */
+	int  sm_games;	/* Logitech soundman games? */
+};
+
+typedef struct sb_devc {
+	   int dev;
+
+	/* Hardware parameters */
+	   int *osp;
+	   int minor, major;
+	   int type;
+	   int model, submodel;
+	   int caps;
+#	define SBCAP_STEREO	0x00000001
+#	define SBCAP_16BITS	0x00000002
+
+	/* Hardware resources */
+	   int base;
+	   int irq;
+	   int dma8, dma16;
+	   
+	   int pcibase;		/* For ESS Maestro etc */
+
+	/* State variables */
+ 	   int opened;
+	/* new audio fields for full duplex support */
+	   int fullduplex;
+	   int duplex;
+	   int speed, bits, channels;
+	   volatile int irq_ok;
+	   volatile int intr_active, irq_mode;
+	/* duplicate audio fields for full duplex support */
+	   volatile int intr_active_16, irq_mode_16;
+
+	/* Mixer fields */
+	   int *levels;
+	   mixer_tab *iomap;
+	   int mixer_caps, recmask, outmask, supported_devices;
+	   int supported_rec_devices, supported_out_devices;
+	   int my_mixerdev;
+	   int sbmixnum;
+
+	/* Audio fields */
+	   unsigned long trg_buf;
+	   int      trigger_bits;
+	   int      trg_bytes;
+	   int      trg_intrflag;
+	   int      trg_restart;
+	/* duplicate audio fields for full duplex support */
+	   unsigned long trg_buf_16;
+	   int      trigger_bits_16;
+	   int      trg_bytes_16;
+	   int      trg_intrflag_16;
+	   int      trg_restart_16;
+
+	   unsigned char tconst;
+	
+	/* MIDI fields */
+	   int my_mididev;
+	   int input_opened;
+	   int midi_broken;
+	   void (*midi_input_intr) (int dev, unsigned char data);
+	   void *midi_irq_cookie;		/* IRQ cookie for the midi */
+
+	   spinlock_t lock;
+
+	   struct sb_module_options sbmo;	/* Module options */
+
+	} sb_devc;
+	
+/*
+ *	PCI card types
+ */
+
+#define	SB_PCI_ESSMAESTRO	1	/* ESS Maestro Legacy */
+#define	SB_PCI_YAMAHA		2	/* Yamaha Legacy */
+
+/* 
+ *	Functions
+ */
+ 
+int sb_dsp_command (sb_devc *devc, unsigned char val);
+int sb_dsp_get_byte(sb_devc * devc);
+int sb_dsp_reset (sb_devc *devc);
+void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
+unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
+int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo);
+int sb_dsp_init (struct address_info *hw_config, struct module *owner);
+void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
+int sb_mixer_init(sb_devc *devc, struct module *owner);
+void sb_mixer_unload(sb_devc *devc);
+void sb_mixer_set_stereo (sb_devc *devc, int mode);
+void smw_mixer_init(sb_devc *devc);
+void sb_dsp_midi_init (sb_devc *devc, struct module *owner);
+void sb_audio_init (sb_devc *devc, char *name, struct module *owner);
+void sb_midi_interrupt (sb_devc *devc);
+void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
+int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right);
+
+int sb_audio_open(int dev, int mode);
+void sb_audio_close(int dev);
+
+extern sb_devc *last_sb;
+
+/*	From sb_common.c */
+void sb_dsp_disable_midi(int port);
+void sb_dsp_disable_recording(int port);
+int probe_sbmpu (struct address_info *hw_config, struct module *owner);
+void unload_sbmpu (struct address_info *hw_config);
+
+void unload_sb16(struct address_info *hw_info);
+void unload_sb16midi(struct address_info *hw_info);
diff -Nru linux/sound/oss/sb_audio.c linux-2.4.19-pre5-mjc/sound/oss/sb_audio.c
--- linux/sound/oss/sb_audio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_audio.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1093 @@
+/*
+ * sound/sb_audio.c
+ *
+ * Audio routines for Sound Blaster compatible cards.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes
+ *	Alan Cox	:	Formatting and clean ups
+ *
+ * Status
+ *	Mostly working. Weird uart bug causing irq storms
+ *
+ * Daniel J. Rodriksson: Changes to make sb16 work full duplex.
+ *                       Maybe other 16 bit cards in this code could behave
+ *                       the same.
+ * Chris Rankin:         Use spinlocks instead of CLI/STI
+ */
+
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+
+#include "sb_mixer.h"
+#include "sb.h"
+
+#include "sb_ess.h"
+
+int sb_audio_open(int dev, int mode)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+	{
+		  printk(KERN_ERR "Sound Blaster: incomplete initialization.\n");
+		  return -ENXIO;
+	}
+	if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
+	{
+		if (mode == OPEN_READ)
+			return -EPERM;
+	}
+	spin_lock_irqsave(&devc->lock, flags);
+	if (devc->opened)
+	{
+		  spin_unlock_irqrestore(&devc->lock, flags);
+		  return -EBUSY;
+	}
+	if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
+	{
+		if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit"))
+		{
+		  	spin_unlock_irqrestore(&devc->lock, flags);
+			return -EBUSY;
+		}
+	}
+	devc->opened = mode;
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->irq_mode = IMODE_NONE;
+	devc->irq_mode_16 = IMODE_NONE;
+	devc->fullduplex = devc->duplex &&
+		((mode & OPEN_READ) && (mode & OPEN_WRITE));
+	sb_dsp_reset(devc);
+
+	/* At first glance this check isn't enough, some ESS chips might not 
+	 * have a RECLEV. However if they don't common_mixer_set will refuse 
+	 * cause devc->iomap has no register mapping for RECLEV
+	 */
+	if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV);
+
+	/* The ALS007 seems to require that the DSP be removed from the output */
+	/* in order for recording to be activated properly.  This is done by   */
+	/* setting the appropriate bits of the output control register 4ch to  */
+	/* zero.  This code assumes that the output control registers are not  */
+	/* used anywhere else and therefore the DSP bits are *always* ON for   */
+	/* output and OFF for sampling.                                        */
+
+	if (devc->submodel == SUBMDL_ALS007) 
+	{
+		if (mode & OPEN_READ) 
+			sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
+				sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9);
+		else
+			sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
+				sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
+	}
+	return 0;
+}
+
+void sb_audio_close(int dev)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	/* fix things if mmap turned off fullduplex */
+	if(devc->duplex
+	   && !devc->fullduplex
+	   && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE))
+	{
+		struct dma_buffparms *dmap_temp;
+		dmap_temp = audio_devs[dev]->dmap_out;
+		audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in;
+		audio_devs[dev]->dmap_in = dmap_temp;
+	}
+	audio_devs[dev]->dmap_out->dma = devc->dma8;
+	audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ?
+		devc->dma16 : devc->dma8;
+
+	if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
+		sound_close_dma(devc->dma16);
+
+	/* For ALS007, turn DSP output back on if closing the device for read */
+	
+	if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) 
+	{
+		sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
+			sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
+	}
+	devc->opened = 0;
+}
+
+static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes,
+		    int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
+	{
+		devc->trg_buf = buf;
+		devc->trg_bytes = nr_bytes;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_OUTPUT;
+	}
+	else
+	{
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = nr_bytes;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_OUTPUT;
+	}
+}
+
+static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
+	{
+		devc->trg_buf = buf;
+		devc->trg_bytes = count;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_INPUT;
+	}
+	else
+	{
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = count;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_INPUT;
+	}
+}
+
+/*
+ * SB1.x compatible routines 
+ */
+
+static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x14))		/* 8 bit DAC using DMA */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+	}
+	else
+		printk(KERN_WARNING "Sound Blaster:  unable to start DAC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->intr_active = 1;
+}
+
+static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x24))		/* 8 bit ADC using DMA */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+	}
+	else
+		printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->intr_active = 1;
+}
+
+static void sb1_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	bits &= devc->irq_mode;
+
+	if (!bits)
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	else
+	{
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+				break;
+		}
+	}
+	devc->trigger_bits = bits;
+}
+
+static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKON);
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sb1_audio_set_speed(int dev, int speed)
+{
+	int max_speed = 23000;
+	sb_devc *devc = audio_devs[dev]->devc;
+	int tmp;
+
+	if (devc->opened & OPEN_READ)
+		max_speed = 13000;
+
+	if (speed > 0)
+	{
+		if (speed < 4000)
+			speed = 4000;
+
+		if (speed > max_speed)
+			speed = max_speed;
+
+		devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+		tmp = 256 - devc->tconst;
+		speed = (1000000 + tmp / 2) / tmp;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+static short sb1_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	return devc->channels = 1;
+}
+
+static unsigned int sb1_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc        *devc = audio_devs[dev]->devc;
+	return devc->bits = 8;
+}
+
+static void sb1_audio_halt_xfer(int dev)
+{
+	unsigned long flags;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	sb_dsp_reset(devc);
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+/*
+ * SB 2.0 and SB 2.01 compatible routines
+ */
+
+static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes,
+			int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char cmd;
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x48))		/* DSP Block size */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+
+		if (devc->speed * devc->channels <= 23000)
+			cmd = 0x1c;	/* 8 bit PCM output */
+		else
+			cmd = 0x90;	/* 8 bit high speed PCM output (SB2.01/Pro) */
+
+		if (!sb_dsp_command(devc, cmd))
+			printk(KERN_ERR "Sound Blaster:  unable to start DAC.\n");
+	}
+	else
+		printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->intr_active = 1;
+}
+
+static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	unsigned long flags;
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char cmd;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x48))		/* DSP Block size */
+	{
+		sb_dsp_command(devc, (unsigned char) (count & 0xff));
+		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+
+		if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000))
+			cmd = 0x2c;	/* 8 bit PCM input */
+		else
+			cmd = 0x98;	/* 8 bit high speed PCM input (SB2.01/Pro) */
+
+		if (!sb_dsp_command(devc, cmd))
+			printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
+	}
+	else
+		printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->intr_active = 1;
+}
+
+static void sb20_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	bits &= devc->irq_mode;
+
+	if (!bits)
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	else
+	{
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+						devc->trg_intrflag);
+			    break;
+		}
+	}
+	devc->trigger_bits = bits;
+}
+
+/*
+ * SB2.01 specific speed setup
+ */
+
+static int sb201_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int tmp;
+	int s = speed * devc->channels;
+
+	if (speed > 0)
+	{
+		if (speed < 4000)
+			speed = 4000;
+		if (speed > 44100)
+			speed = 44100;
+		if (devc->opened & OPEN_READ && speed > 15000)
+			speed = 15000;
+		devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
+		tmp = 256 - devc->tconst;
+		speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * SB Pro specific routines
+ */
+
+static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount)
+{				/* For SB Pro and Jazz16 */
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+	unsigned char bits = 0;
+
+	if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+		audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma =
+			devc->bits == 16 ? devc->dma16 : devc->dma8;
+
+	if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+		if (devc->bits == AFMT_S16_LE)
+			bits = 0x04;	/* 16 bit mode */
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+	if (devc->channels == 1)
+		sb_dsp_command(devc, 0xa0 | bits);	/* Mono input */
+	else
+		sb_dsp_command(devc, 0xa8 | bits);	/* Stereo input */
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount)
+{				/* For SB Pro and Jazz16 */
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned char bits = 0;
+
+	if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+		audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8;
+	if (devc->model == MDL_SBPRO)
+		sb_mixer_set_stereo(devc, devc->channels == 2);
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (sb_dsp_command(devc, 0x40))
+		sb_dsp_command(devc, devc->tconst);
+	sb_dsp_command(devc, DSP_CMD_SPKON);
+
+	if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+	{
+		if (devc->bits == AFMT_S16_LE)
+			bits = 0x04;	/* 16 bit mode */
+
+		if (devc->channels == 1)
+			sb_dsp_command(devc, 0xa0 | bits);	/* Mono output */
+		else
+			sb_dsp_command(devc, 0xa8 | bits);	/* Stereo output */
+	}
+	else
+	{
+		tmp = sb_getmixer(devc, 0x0e);
+		if (devc->channels == 1)
+			tmp &= ~0x02;
+		else
+			tmp |= 0x02;
+		sb_setmixer(devc, 0x0e, tmp);
+	}
+	spin_unlock_irqrestore(&devc->lock, flags);
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sbpro_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (speed > 0)
+	{
+		if (speed < 4000)
+			speed = 4000;
+		if (speed > 44100)
+			speed = 44100;
+		if (devc->channels > 1 && speed > 22050)
+			speed = 22050;
+		sb201_audio_set_speed(dev, speed);
+	}
+	return devc->speed;
+}
+
+static short sbpro_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (channels == 1 || channels == 2)
+	{
+		if (channels != devc->channels)
+		{
+			devc->channels = channels;
+			if (devc->model == MDL_SBPRO && devc->channels == 2)
+				sbpro_audio_set_speed(dev, devc->speed);
+		}
+	}
+	return devc->channels;
+}
+
+static int jazz16_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (speed > 0)
+	{
+		int tmp;
+		int s = speed * devc->channels;
+
+		if (speed < 5000)
+			speed = 5000;
+		if (speed > 44100)
+			speed = 44100;
+
+		devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
+
+		tmp = 256 - devc->tconst;
+		speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * SB16 specific routines
+ */
+
+static int sb16_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int	max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100;
+
+	if (speed > 0)
+	{
+		if (speed < 5000)	/* which of these */
+			speed = 4000;	/* is correct ??? */
+
+		if (speed > max_speed)
+			speed = max_speed;
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+static unsigned int sb16_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (bits != 0)
+	{
+		if (bits == AFMT_U8 || bits == AFMT_S16_LE)
+			devc->bits = bits;
+		else
+			devc->bits = AFMT_U8;
+	}
+
+	return devc->bits;
+}
+
+static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex)
+	{
+		audio_devs[dev]->dmap_out->dma =
+			audio_devs[dev]->dmap_in->dma =
+				devc->bits == AFMT_S16_LE ?
+					devc->dma16 : devc->dma8;
+	}
+	else if (devc->bits == AFMT_S16_LE)
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma8;
+		audio_devs[dev]->dmap_in->dma = devc->dma16;
+	}
+	else
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma16;
+		audio_devs[dev]->dmap_in->dma = devc->dma8;
+	}
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex)
+	{
+		audio_devs[dev]->dmap_out->dma =
+			audio_devs[dev]->dmap_in->dma =
+				devc->bits == AFMT_S16_LE ?
+					devc->dma16 : devc->dma8;
+	}
+	else if (devc->bits == AFMT_S16_LE)
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma8;
+		audio_devs[dev]->dmap_in->dma = devc->dma16;
+	}
+	else
+	{
+		audio_devs[dev]->dmap_out->dma = devc->dma16;
+		audio_devs[dev]->dmap_in->dma = devc->dma8;
+	}
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static void sb16_audio_output_block(int dev, unsigned long buf, int count,
+			int intrflag)
+{
+	unsigned long   flags, cnt;
+	sb_devc        *devc = audio_devs[dev]->devc;
+	unsigned long   bits;
+
+	if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
+	{
+		devc->irq_mode = IMODE_OUTPUT;
+		devc->intr_active = 1;
+	}
+	else
+	{
+		devc->irq_mode_16 = IMODE_OUTPUT;
+		devc->intr_active_16 = 1;
+	}
+
+	/* save value */
+	spin_lock_irqsave(&devc->lock, flags);
+	bits = devc->bits;
+	if (devc->fullduplex)
+		devc->bits = (devc->bits == AFMT_S16_LE) ?
+			AFMT_U8 : AFMT_S16_LE;
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	cnt = count;
+	if (devc->bits == AFMT_S16_LE)
+		cnt >>= 1;
+	cnt--;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+	sb_dsp_command(devc, 0x41);
+	sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
+	sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
+
+	sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
+	sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
+			      (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
+	sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
+	sb_dsp_command(devc, (unsigned char) (cnt >> 8));
+
+	/* restore real value after all programming */
+	devc->bits = bits;
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+
+/*
+ *	This fails on the Cyrix MediaGX. If you don't have the DMA enabled
+ *	before the first sample arrives it locks up. However even if you
+ *	do enable the DMA in time you just get DMA timeouts and missing
+ *	interrupts and stuff, so for now I've not bothered fixing this either.
+ */
+ 
+static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+	unsigned long   flags, cnt;
+	sb_devc        *devc = audio_devs[dev]->devc;
+
+	if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
+	{
+		devc->irq_mode = IMODE_INPUT;
+		devc->intr_active = 1;
+	}
+	else
+	{
+		devc->irq_mode_16 = IMODE_INPUT;
+		devc->intr_active_16 = 1;
+	}
+
+	cnt = count;
+	if (devc->bits == AFMT_S16_LE)
+		cnt >>= 1;
+	cnt--;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+	sb_dsp_command(devc, 0x42);
+	sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
+	sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
+
+	sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce));
+	sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
+			      (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
+	sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
+	sb_dsp_command(devc, (unsigned char) (cnt >> 8));
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+static void sb16_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	int bits_16 = bits & devc->irq_mode_16;
+	bits &= devc->irq_mode;
+
+	if (!bits && !bits_16)
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	else
+	{
+		if (bits)
+		{
+			switch (devc->irq_mode)
+			{
+				case IMODE_INPUT:
+					sb16_audio_start_input(dev,
+							devc->trg_buf,
+							devc->trg_bytes,
+							devc->trg_intrflag);
+					break;
+
+				case IMODE_OUTPUT:
+					sb16_audio_output_block(dev,
+							devc->trg_buf,
+							devc->trg_bytes,
+							devc->trg_intrflag);
+					break;
+			}
+		}
+		if (bits_16)
+		{
+			switch (devc->irq_mode_16)
+			{
+				case IMODE_INPUT:
+					sb16_audio_start_input(dev,
+							devc->trg_buf_16,
+							devc->trg_bytes_16,
+							devc->trg_intrflag_16);
+					break;
+
+				case IMODE_OUTPUT:
+					sb16_audio_output_block(dev,
+							devc->trg_buf_16,
+							devc->trg_bytes_16,
+							devc->trg_intrflag_16);
+					break;
+			}
+		}
+	}
+
+	devc->trigger_bits = bits | bits_16;
+}
+
+static unsigned char lbuf8[2048];
+static signed short *lbuf16 = (signed short *)lbuf8;
+#define LBUFCOPYSIZE 1024
+static void
+sb16_copy_from_user(int dev,
+		char *localbuf, int localoffs,
+		const char *userbuf, int useroffs,
+		int max_in, int max_out,
+		int *used, int *returned,
+		int len)
+{
+	sb_devc       *devc = audio_devs[dev]->devc;
+	int           i, c, p, locallen;
+	unsigned char *buf8;
+	signed short  *buf16;
+
+	/* if not duplex no conversion */
+	if (!devc->fullduplex)
+	{
+		copy_from_user (localbuf + localoffs, userbuf + useroffs, len);
+		*used = len;
+		*returned = len;
+	}
+	else if (devc->bits == AFMT_S16_LE)
+	{
+		/* 16 -> 8 */
+		/* max_in >> 1, max number of samples in ( 16 bits ) */
+		/* max_out, max number of samples out ( 8 bits ) */
+		/* len, number of samples that will be taken ( 16 bits )*/
+		/* c, count of samples remaining in buffer ( 16 bits )*/
+		/* p, count of samples already processed ( 16 bits )*/
+		len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1);
+		c = len;
+		p = 0;
+		buf8 = (unsigned char *)(localbuf + localoffs);
+		while (c)
+		{
+			locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
+			/* << 1 in order to get 16 bit samples */
+			copy_from_user (lbuf16,
+					userbuf+useroffs + (p << 1),
+					locallen << 1);
+			for (i = 0; i < locallen; i++)
+			{
+				buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80;
+			}
+			c -= locallen; p += locallen;
+		}
+		/* used = ( samples * 16 bits size ) */
+		*used = len << 1;
+		/* returned = ( samples * 8 bits size ) */
+		*returned = len;
+	}
+	else
+	{
+		/* 8 -> 16 */
+		/* max_in, max number of samples in ( 8 bits ) */
+		/* max_out >> 1, max number of samples out ( 16 bits ) */
+		/* len, number of samples that will be taken ( 8 bits )*/
+		/* c, count of samples remaining in buffer ( 8 bits )*/
+		/* p, count of samples already processed ( 8 bits )*/
+		len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in;
+		c = len;
+		p = 0;
+		buf16 = (signed short *)(localbuf + localoffs);
+		while (c)
+		{
+			locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
+			copy_from_user (lbuf8,
+					userbuf+useroffs + p,
+					locallen);
+			for (i = 0; i < locallen; i++)
+			{
+				buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8;
+			}
+	      		c -= locallen; p += locallen;
+		}
+		/* used = ( samples * 8 bits size ) */
+		*used = len;
+		/* returned = ( samples * 16 bits size ) */
+		*returned = len << 1;
+	}
+}
+
+static void
+sb16_audio_mmap(int dev)
+{
+	sb_devc       *devc = audio_devs[dev]->devc;
+	devc->fullduplex = 0;
+}
+
+static struct audio_driver sb1_audio_driver =	/* SB1.x */
+{
+	owner:		THIS_MODULE,
+	open:		sb_audio_open,
+	close:		sb_audio_close,
+	output_block:	sb_set_output_parms,
+	start_input:	sb_set_input_parms,
+	prepare_for_input:	sb1_audio_prepare_for_input,
+	prepare_for_output:	sb1_audio_prepare_for_output,
+	halt_io:	sb1_audio_halt_xfer,
+	trigger:	sb1_audio_trigger,
+	set_speed:	sb1_audio_set_speed,
+	set_bits:	sb1_audio_set_bits,
+	set_channels:	sb1_audio_set_channels
+};
+
+static struct audio_driver sb20_audio_driver =	/* SB2.0 */
+{
+	owner:		THIS_MODULE,
+	open:		sb_audio_open,
+	close:		sb_audio_close,
+	output_block:	sb_set_output_parms,
+	start_input:	sb_set_input_parms,
+	prepare_for_input:	sb1_audio_prepare_for_input,
+	prepare_for_output:	sb1_audio_prepare_for_output,
+	halt_io:	sb1_audio_halt_xfer,
+	trigger:	sb20_audio_trigger,
+	set_speed:	sb1_audio_set_speed,
+	set_bits:	sb1_audio_set_bits,
+	set_channels:	sb1_audio_set_channels
+};
+
+static struct audio_driver sb201_audio_driver =		/* SB2.01 */
+{
+	owner:		THIS_MODULE,
+	open:		sb_audio_open,
+	close:		sb_audio_close,
+	output_block:	sb_set_output_parms,
+	start_input:	sb_set_input_parms,
+	prepare_for_input:	sb1_audio_prepare_for_input,
+	prepare_for_output:	sb1_audio_prepare_for_output,
+	halt_io:	sb1_audio_halt_xfer,
+	trigger:	sb20_audio_trigger,
+	set_speed:	sb201_audio_set_speed,
+	set_bits:	sb1_audio_set_bits,
+	set_channels:	sb1_audio_set_channels
+};
+
+static struct audio_driver sbpro_audio_driver =		/* SB Pro */
+{
+	owner:		THIS_MODULE,
+	open:		sb_audio_open,
+	close:		sb_audio_close,
+	output_block:	sb_set_output_parms,
+	start_input:	sb_set_input_parms,
+	prepare_for_input:	sbpro_audio_prepare_for_input,
+	prepare_for_output:	sbpro_audio_prepare_for_output,
+	halt_io:	sb1_audio_halt_xfer,
+	trigger:	sb20_audio_trigger,
+	set_speed:	sbpro_audio_set_speed,
+	set_bits:	sb1_audio_set_bits,
+	set_channels:	sbpro_audio_set_channels
+};
+
+static struct audio_driver jazz16_audio_driver =	/* Jazz16 and SM Wave */
+{
+	owner:		THIS_MODULE,
+	open:		sb_audio_open,
+	close:		sb_audio_close,
+	output_block:	sb_set_output_parms,
+	start_input:	sb_set_input_parms,
+	prepare_for_input:	sbpro_audio_prepare_for_input,
+	prepare_for_output:	sbpro_audio_prepare_for_output,
+	halt_io:	sb1_audio_halt_xfer,
+	trigger:	sb20_audio_trigger,
+	set_speed:	jazz16_audio_set_speed,
+	set_bits:	sb16_audio_set_bits,
+	set_channels:	sbpro_audio_set_channels
+};
+
+static struct audio_driver sb16_audio_driver =	/* SB16 */
+{
+	owner:		THIS_MODULE,
+	open:		sb_audio_open,
+	close:		sb_audio_close,
+	output_block:	sb_set_output_parms,
+	start_input:	sb_set_input_parms,
+	prepare_for_input:	sb16_audio_prepare_for_input,
+	prepare_for_output:	sb16_audio_prepare_for_output,
+	halt_io:	sb1_audio_halt_xfer,
+	copy_user:	sb16_copy_from_user,
+	trigger:	sb16_audio_trigger,
+	set_speed:	sb16_audio_set_speed,
+	set_bits:	sb16_audio_set_bits,
+	set_channels:	sbpro_audio_set_channels,
+	mmap:		sb16_audio_mmap
+};
+
+void sb_audio_init(sb_devc * devc, char *name, struct module *owner)
+{
+	int audio_flags = 0;
+	int format_mask = AFMT_U8;
+
+	struct audio_driver *driver = &sb1_audio_driver;
+
+	switch (devc->model)
+	{
+		case MDL_SB1:	/* SB1.0 or SB 1.5 */
+			DDB(printk("Will use standard SB1.x driver\n"));
+			audio_flags = DMA_HARDSTOP;
+			break;
+
+		case MDL_SB2:
+			DDB(printk("Will use SB2.0 driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			driver = &sb20_audio_driver;
+			break;
+
+		case MDL_SB201:
+			DDB(printk("Will use SB2.01 (high speed) driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			driver = &sb201_audio_driver;
+			break;
+
+		case MDL_JAZZ:
+		case MDL_SMW:
+			DDB(printk("Will use Jazz16 driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			format_mask |= AFMT_S16_LE;
+			driver = &jazz16_audio_driver;
+			break;
+
+		case MDL_ESS:
+			DDB(printk("Will use ESS ES688/1688 driver\n"));
+			driver = ess_audio_init (devc, &audio_flags, &format_mask);
+			break;
+
+		case MDL_SB16:
+			DDB(printk("Will use SB16 driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			format_mask |= AFMT_S16_LE;
+			if (devc->dma8 != devc->dma16 && devc->dma16 != -1)
+			{
+				audio_flags |= DMA_DUPLEX;
+				devc->duplex = 1;
+			}
+			driver = &sb16_audio_driver;
+			break;
+
+		default:
+			DDB(printk("Will use SB Pro driver\n"));
+			audio_flags = DMA_AUTOMODE;
+			driver = &sbpro_audio_driver;
+	}
+
+	if (owner)
+			driver->owner = owner;
+	
+	if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+				name,driver, sizeof(struct audio_driver),
+				audio_flags, format_mask, devc,
+				devc->dma8,
+				devc->duplex ? devc->dma16 : devc->dma8)) < 0)
+	{
+		  printk(KERN_ERR "Sound Blaster:  unable to install audio.\n");
+		  return;
+	}
+	audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev;
+	audio_devs[devc->dev]->min_fragment = 5;
+}
diff -Nru linux/sound/oss/sb_card.c linux-2.4.19-pre5-mjc/sound/oss/sb_card.c
--- linux/sound/oss/sb_card.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_card.c	Mon Apr  8 22:31:22 2002
@@ -0,0 +1,1035 @@
+/*
+ * sound/sb_card.c
+ *
+ * Detection routine for the Sound Blaster cards.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * 26-11-1999 Patched to compile without ISA PnP support in the
+ * kernel - Daniel Stone (tamriel@ductape.net) 
+ *
+ * 06-01-2000 Refined and bugfixed ISA PnP support, added
+ *  CMI 8330 support - Alessandro Zummo <azummo@ita.flashnet.it>
+ *
+ * 18-01-2000 Separated sb_card and sb_common
+ *  Jeff Garzik <jgarzik@mandrakesoft.com>
+ *
+ * 04-02-2000 Added Soundblaster AWE 64 PnP support, isapnpjump
+ *  Alessandro Zummo <azummo@ita.flashnet.it>
+ *
+ * 11-02-2000 Added Soundblaster AWE 32 PnP support, refined PnP code
+ *  Alessandro Zummo <azummo@ita.flashnet.it>
+ *
+ * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup
+ *  Alessandro Zummo <azummo@ita.flashnet.it>
+ *
+ * 13-03-2000 Added some more cards, thanks to Torsten Werner.
+ *  Removed joystick and wavetable code, there are better places for them.
+ *  Code cleanup plus some fixes. 
+ *  Alessandro Zummo <azummo@ita.flashnet.it>
+ * 
+ * 26-03-2000 Fixed acer, esstype and sm_games module options.
+ *  Alessandro Zummo <azummo@ita.flashnet.it>
+ *
+ * 12-04-2000 ISAPnP cleanup, reorg, fixes, and multiple card support.
+ *  Thanks to Gal Quri and Alessandro Zummo for testing and fixes.
+ *  Paul E. Laufer <pelaufer@csupomona.edu>
+ *
+ * 06-05-2000 added another card. Daniel M. Newman <dmnewman@pobox.com>
+ *
+ * 25-05-2000 Added Creative SB AWE64 Gold (CTL00B2). 
+ * 	Pl-Kristian Engstad <engstad@att.net>
+ *
+ * 12-08-2000 Added Creative SB32 PnP (CTL009F).
+ * 	Kasatenko Ivan Alex. <skywriter@rnc.ru>
+ *
+ * 21-09-2000 Got rid of attach_sbmpu
+ * 	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * 28-10-2000 Added pnplegacy support
+ * 	Daniel Church <dchurch@mbhs.edu>
+ *
+ * 01-10-2001 Added a new flavor of Creative SB AWE64 PnP (CTL00E9).
+ *      Jerome Cornet <jcornet@free.fr>
+ */
+
+#include <linux/config.h>
+#include <linux/mca.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/isapnp.h>
+
+#include "sound_config.h"
+
+#include "sb_mixer.h"
+#include "sb.h"
+
+#ifdef __ISAPNP__
+#define SB_CARDS_MAX 5
+#else
+#define SB_CARDS_MAX 1
+#endif
+
+static int sbmpu[SB_CARDS_MAX] = {0};
+static int sb_cards_num = 0;
+
+extern void *smw_free;
+
+/*
+ *    Note DMA2 of -1 has the right meaning in the SB16 driver as well
+ *    as here. It will cause either an error if it is needed or a fallback
+ *    to the 8bit channel.
+ */
+
+static int __initdata mpu_io	= 0;
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma16	= -1;   /* Set this for modules that need it */
+static int __initdata type	= 0;    /* Can set this to a specific card type */
+static int __initdata esstype   = 0;	/* ESS chip type */
+static int __initdata acer 	= 0;	/* Do acer notebook init? */
+static int __initdata sm_games 	= 0;	/* Logitech soundman games? */
+
+static void __init attach_sb_card(struct address_info *hw_config)
+{
+	if(!sb_dsp_init(hw_config, THIS_MODULE))
+		hw_config->slots[0] = -1;
+}
+
+static int __init probe_sb(struct address_info *hw_config)
+{
+	struct sb_module_options sbmo;
+
+	if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1)
+	{
+		printk(KERN_ERR "sb: I/O, IRQ, and DMA are mandatory\n");
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_MCA
+	/* MCA code added by ZP Gu (zpg@castle.net) */
+	if (MCA_bus) {               /* no multiple REPLY card probing */
+		int slot;
+		u8 pos2, pos3, pos4;
+
+		slot = mca_find_adapter( 0x5138, 0 );
+		if( slot == MCA_NOTFOUND ) 
+		{
+			slot = mca_find_adapter( 0x5137, 0 );
+
+			if (slot != MCA_NOTFOUND)
+				mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" );
+		}
+		else
+		{
+			mca_set_adapter_name( slot, "REPLY SB16 Adapter" );
+		}
+
+		if (slot != MCA_NOTFOUND) 
+		{
+			mca_mark_as_used(slot);
+			pos2 = mca_read_stored_pos( slot, 2 );
+			pos3 = mca_read_stored_pos( slot, 3 );
+			pos4 = mca_read_stored_pos( slot, 4 );
+
+			if (pos2 & 0x4) 
+			{
+				/* enabled? */
+				static unsigned short irq[] = { 0, 5, 7, 10 };
+				/*
+				static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 };
+       				*/
+
+				hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6);
+				hw_config->irq = irq[(pos4 >> 5) & 0x3];
+				hw_config->dma = pos3 & 0xf;
+				/* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */
+				hw_config->dma2 = (pos3 >> 4) & 0x3;
+				if (hw_config->dma2 == 0)
+					hw_config->dma2 = hw_config->dma;
+				else
+					hw_config->dma2 += 4;
+				/*
+					hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3];
+				*/
+	
+				printk(KERN_INFO "sb: Reply MCA SB at slot=%d \
+iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n",
+						slot+1,
+				        	hw_config->io_base, hw_config->irq,
+	        				hw_config->dma, hw_config->dma2);
+			}
+			else
+			{
+				printk (KERN_INFO "sb: Reply SB Base I/O address disabled\n");
+			}
+		}
+	}
+#endif
+
+	/* Setup extra module options */
+
+	sbmo.acer 	= acer;
+	sbmo.sm_games	= sm_games;
+	sbmo.esstype	= esstype;
+
+	return sb_dsp_detect(hw_config, 0, 0, &sbmo);
+}
+
+static void __exit unload_sb(struct address_info *hw_config, int card)
+{
+	if(hw_config->slots[0]!=-1)
+		sb_dsp_unload(hw_config, sbmpu[card]);
+}
+
+static struct address_info cfg[SB_CARDS_MAX];
+static struct address_info cfg_mpu[SB_CARDS_MAX];
+
+struct pci_dev 	*sb_dev[SB_CARDS_MAX] 	= {NULL}, 
+		*mpu_dev[SB_CARDS_MAX]	= {NULL},
+		*opl_dev[SB_CARDS_MAX]	= {NULL};
+
+
+#ifdef __ISAPNP__
+static int isapnp	= 1;
+static int isapnpjump	= 0;
+static int multiple	= 1;
+static int pnplegacy	= 0;
+static int reverse	= 0;
+static int uart401	= 0;
+
+static int audio_activated[SB_CARDS_MAX] = {0};
+static int mpu_activated[SB_CARDS_MAX]   = {0};
+static int opl_activated[SB_CARDS_MAX]   = {0};
+#else
+static int isapnp	= 0;
+static int multiple	= 0;
+static int pnplegacy	= 0;
+#endif
+
+MODULE_DESCRIPTION("Soundblaster driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(io,		"i");
+MODULE_PARM(irq,	"i");
+MODULE_PARM(dma,	"i");
+MODULE_PARM(dma16,	"i");
+MODULE_PARM(mpu_io,	"i");
+MODULE_PARM(type,	"i");
+MODULE_PARM(sm_games,	"i");
+MODULE_PARM(esstype,	"i");
+MODULE_PARM(acer,	"i");
+
+#ifdef __ISAPNP__
+MODULE_PARM(isapnp,	"i");
+MODULE_PARM(isapnpjump,	"i");
+MODULE_PARM(multiple,	"i");
+MODULE_PARM(pnplegacy,	"i");
+MODULE_PARM(reverse,	"i");
+MODULE_PARM(uart401,	"i");
+MODULE_PARM_DESC(isapnp,	"When set to 0, Plug & Play support will be disabled");
+MODULE_PARM_DESC(isapnpjump,	"Jumps to a specific slot in the driver's PnP table. Use the source, Luke.");
+MODULE_PARM_DESC(multiple,	"When set to 0, will not search for multiple cards");
+MODULE_PARM_DESC(pnplegacy,	"When set to 1, will search for a legacy SB card along with any PnP cards.");
+MODULE_PARM_DESC(reverse,	"When set to 1, will reverse ISAPnP search order");
+MODULE_PARM_DESC(uart401,	"When set to 1, will attempt to detect and enable the mpu on some clones");
+#endif
+
+MODULE_PARM_DESC(io,		"Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
+MODULE_PARM_DESC(irq,		"IRQ (5,7,9,10)");
+MODULE_PARM_DESC(dma,		"8-bit DMA channel (0,1,3)");
+MODULE_PARM_DESC(dma16,		"16-bit DMA channel (5,6,7)");
+MODULE_PARM_DESC(mpu_io,	"Mpu base address");
+MODULE_PARM_DESC(type,		"You can set this to specific card type");
+MODULE_PARM_DESC(sm_games,	"Enable support for Logitech soundman games");
+MODULE_PARM_DESC(esstype,	"ESS chip type");
+MODULE_PARM_DESC(acer,		"Set this to detect cards in some ACER notebooks");
+
+#ifdef __ISAPNP__
+
+/* Please add new entries at the end of the table */
+static struct {
+	char *name; 
+	unsigned short	card_vendor, card_device, 
+			audio_vendor, audio_function,
+			mpu_vendor, mpu_function,
+			opl_vendor, opl_function;
+	short dma, dma2, mpu_io, mpu_irq; /* see sb_init() */
+} sb_isapnp_list[] __initdata = {
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster 16", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster Vibra16S", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster Vibra16C", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster Vibra16CL", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster Vibra16X", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32", 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+        {"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 32",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Creative SB32 PnP",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64 Gold",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64 Gold",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045),
+		0,0,0,0,
+		0,1,1,-1},
+	{"Sound Blaster AWE 64",
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045),
+		0,0,0,0,
+		0,1,1,-1},
+	{"ESS 1688",
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968),
+		0,0,0,0,
+		0,1,2,-1},
+	{"ESS 1868",
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868),
+		0,0,0,0,
+		0,1,2,-1},
+	{"ESS 1868",
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611),
+		0,0,0,0,
+		0,1,2,-1},
+	{"ESS 1869 PnP AudioDrive", 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869),
+		0,0,0,0,
+		0,1,2,-1},
+	{"ESS 1869",
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869),
+		0,0,0,0,
+		0,1,2,-1},
+	{"ESS 1878",
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878),
+		0,0,0,0,
+		0,1,2,-1},
+	{"ESS 1879",
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879),
+		0,0,0,0,
+		0,1,2,-1},
+	{"CMI 8330 SoundPRO",
+		ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
+		0,1,0,-1},
+	{"Diamond DT0197H",
+		ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), 
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		0,-1,0,0},
+	{"ALS007",
+		ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		0,-1,0,0},
+	{"ALS100",
+		ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		1,0,0,0},
+	{"ALS110",
+		ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		1,0,0,0},
+	{"ALS120",
+		ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		1,0,0,0},
+	{"ALS200",
+		ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		1,0,0,0},
+	{"RTL3000",
+		ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001),
+		1,0,0,0},
+	{0}
+};
+
+static struct isapnp_device_id id_table[] __devinitdata = {
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), 
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 },
+
+	{	ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9),
+		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), 0 },
+
+	{	ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), 
+		ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), 0 },
+
+	{	ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), 
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), 
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), 
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), 
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), 0 },
+
+	{	ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+
+	{	ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000),
+		ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 },
+
+	{	ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000),
+		ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 },
+
+	{	ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000),
+		ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev)
+{
+	int err;
+
+	/* Device already active? Let's use it */
+	if(dev->active)
+		return(dev);
+	
+	if((err = dev->activate(dev)) < 0) {
+		printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err);
+
+		dev->deactivate(dev);
+
+		return(NULL);
+	}
+	return(dev);
+}
+
+static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card)
+{
+
+	/* Configure Audio device */
+	if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL)))
+	{
+		int ret;
+		ret = sb_dev[card]->prepare(sb_dev[card]);
+		/* If device is active, assume configured with /proc/isapnp
+		 * and use anyway. Some other way to check this? */
+		if(ret && ret != -EBUSY) {
+			printk(KERN_ERR "sb: ISAPnP found device that could not be autoconfigured.\n");
+			return(NULL);
+		}
+		if(ret == -EBUSY)
+			audio_activated[card] = 1;
+		
+		if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card])))
+		{
+			hw_config->io_base 	= sb_dev[card]->resource[0].start;
+			hw_config->irq 		= sb_dev[card]->irq_resource[0].start;
+			hw_config->dma 		= sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma].start;
+			if(sb_isapnp_list[slot].dma2 != -1)
+				hw_config->dma2 = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma2].start;
+			else
+				hw_config->dma2 = -1;
+		} else
+			return(NULL);
+	} else
+		return(NULL);
+
+	/* Cards with separate OPL3 device (ALS, CMI, etc.)
+	 * This is just to activate the device so the OPL module can use it */
+	if(sb_isapnp_list[slot].opl_vendor || sb_isapnp_list[slot].opl_function) {
+		if((opl_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].opl_vendor, sb_isapnp_list[slot].opl_function, NULL))) {
+			int ret = opl_dev[card]->prepare(opl_dev[card]);
+			/* If device is active, assume configured with
+			 * /proc/isapnp and use anyway */
+			if(ret && ret != -EBUSY) {
+				printk(KERN_ERR "sb: OPL device could not be autoconfigured.\n");
+				return(sb_dev[card]);
+			}
+			if(ret == -EBUSY)
+				opl_activated[card] = 1;
+
+			/* Some have irq and dma for opl. the opl3 driver wont
+			 * use 'em so don't configure 'em and hope it works -PEL */
+			opl_dev[card]->irq_resource[0].flags = 0;
+			opl_dev[card]->dma_resource[0].flags = 0;
+
+			opl_dev[card] = activate_dev(sb_isapnp_list[slot].name, "opl3", opl_dev[card]);
+		} else
+			printk(KERN_ERR "sb: %s isapnp panic: opl3 device not found\n", sb_isapnp_list[slot].name);
+	}
+
+	/* Cards with MPU as part of Audio device (CTL and ESS) */
+	if(!sb_isapnp_list[slot].mpu_vendor) {
+		mpu_config->io_base	= sb_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start;
+		return(sb_dev[card]);
+	}
+	
+	/* Cards with separate MPU device (ALS, CMI, etc.) */
+	if(!uart401)
+		return(sb_dev[card]);
+	if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL)))
+	{
+		int ret = mpu_dev[card]->prepare(mpu_dev[card]);
+		/* If device is active, assume configured with /proc/isapnp
+		 * and use anyway */
+		if(ret && ret != -EBUSY) {
+			printk(KERN_ERR "sb: MPU device could not be autoconfigured.\n");
+			return(sb_dev[card]);
+		}
+		if(ret == -EBUSY)
+			mpu_activated[card] = 1;
+		
+		/* Some cards ask for irq but don't need them - azummo */
+		if(sb_isapnp_list[slot].mpu_irq == -1)
+			mpu_dev[card]->irq_resource[0].flags = 0;
+		
+		if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card]))) {
+			mpu_config->io_base = mpu_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start;
+			if(sb_isapnp_list[slot].mpu_irq != -1)
+				mpu_config->irq = mpu_dev[card]->irq_resource[sb_isapnp_list[slot].mpu_irq].start;
+		}
+	}
+	else
+		printk(KERN_ERR "sb: %s isapnp panic: mpu not found\n", sb_isapnp_list[slot].name);
+	
+	return(sb_dev[card]);
+}
+
+static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card)
+{
+	char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name;
+
+	printk(KERN_INFO "sb: %s detected\n", busname); 
+
+	/* Initialize this baby. */
+
+	if(sb_init(bus, hw_config, mpu_config, slot, card)) {
+		/* We got it. */
+		
+		printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
+		       busname,
+		       hw_config->io_base, hw_config->irq, hw_config->dma,
+		       hw_config->dma2);
+		return 1;
+	}
+	else
+		printk(KERN_INFO "sb: Failed to initialize %s\n", busname);
+
+	return 0;
+}
+
+static int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card)
+{
+	static int first = 1;
+	int i;
+
+	/* Count entries in sb_isapnp_list */
+	for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++);
+	i--;
+
+	/* Check and adjust isapnpjump */
+	if( isapnpjump < 0 || isapnpjump > i) {
+		isapnpjump = reverse ? i : 0;
+		printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump);
+	}
+
+	if(!first || !reverse)
+		i = isapnpjump;
+	first = 0;
+	while(sb_isapnp_list[i].card_vendor != 0) {
+		static struct pci_bus *bus = NULL;
+
+		while ((bus = isapnp_find_card(
+				sb_isapnp_list[i].card_vendor,
+				sb_isapnp_list[i].card_device,
+				bus))) {
+	
+			if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) {
+				isapnpjump = i; /* start next search from here */
+				return 0;
+			}
+		}
+		i += reverse ? -1 : 1;
+	}
+
+	return -ENODEV;
+}
+#endif
+
+static int __init init_sb(void)
+{
+	int card, max = (multiple && isapnp) ? SB_CARDS_MAX : 1;
+
+	printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+	
+	for(card = 0; card < max; card++, sb_cards_num++) {
+#ifdef __ISAPNP__
+		/* Please remember that even with __ISAPNP__ defined one
+		 * should still be able to disable PNP support for this 
+		 * single driver! */
+		if((!pnplegacy||card>0) && isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) {
+			if(!sb_cards_num) {
+				/* Found no ISAPnP cards, so check for a non-pnp
+				 * card and set the detection loop for 1 cycle
+				 */
+				printk(KERN_NOTICE "sb: No ISAPnP cards found, trying standard ones...\n");
+				isapnp = 0;
+				max = 1;
+			} else
+				/* found all the ISAPnP cards so exit the
+				 * detection loop. */
+				break;
+		}
+#endif
+
+		if(!isapnp || (pnplegacy&&card==0)) {
+			cfg[card].io_base	= io;
+			cfg[card].irq		= irq;
+			cfg[card].dma		= dma;
+			cfg[card].dma2		= dma16;
+		}
+
+		cfg[card].card_subtype = type;
+
+		if (!probe_sb(&cfg[card])) {
+			/* if one or more cards already registered, don't
+			 * return an error but print a warning. Note, this
+			 * should never really happen unless the hardware
+			 * or ISAPnP screwed up. */
+			if (sb_cards_num) {
+				printk(KERN_WARNING "sb.c: There was a " \
+				  "problem probing one of your SoundBlaster " \
+				  "ISAPnP soundcards. Continuing.\n");
+				card--;
+				sb_cards_num--;
+				continue;
+			} else if(pnplegacy && isapnp) {
+				printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \
+				  "found.  Continuing with PnP detection.\n");
+				pnplegacy=0;
+				card--;
+				continue;
+			} else
+				return -ENODEV;
+		}
+		attach_sb_card(&cfg[card]);
+
+		if(cfg[card].slots[0]==-1) {
+			if(card==0 && pnplegacy && isapnp) {
+				printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \
+				  "found.  Continuing with PnP detection.\n");
+				pnplegacy=0;
+				card--;
+				continue;
+			} else
+				return -ENODEV;
+		}
+		
+		if (!isapnp||(pnplegacy&&card==0))
+			cfg_mpu[card].io_base = mpu_io;
+		if (probe_sbmpu(&cfg_mpu[card], THIS_MODULE))
+			sbmpu[card] = 1;
+	}
+
+	if(isapnp)
+		printk(KERN_NOTICE "sb: %d Soundblaster PnP card(s) found.\n", sb_cards_num);
+
+	return 0;
+}
+
+static void __exit cleanup_sb(void)
+{
+	int i;
+	
+	if (smw_free) {
+		vfree(smw_free);
+		smw_free = NULL;
+	}
+
+	for(i = 0; i < sb_cards_num; i++) {
+		unload_sb(&cfg[i], i);
+		if (sbmpu[i])
+			unload_sbmpu(&cfg_mpu[i]);
+
+#ifdef __ISAPNP__
+		if(!audio_activated[i] && sb_dev[i])
+			sb_dev[i]->deactivate(sb_dev[i]);
+		if(!mpu_activated[i] && mpu_dev[i])
+			mpu_dev[i]->deactivate(mpu_dev[i]);
+		if(!opl_activated[i] && opl_dev[i])
+			opl_dev[i]->deactivate(opl_dev[i]);
+#endif
+	}
+}
+
+module_init(init_sb);
+module_exit(cleanup_sb);
+
+#ifndef MODULE
+static int __init setup_sb(char *str)
+{
+	/* io, irq, dma, dma2 - just the basics */
+	int ints[5];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma16	= ints[4];
+
+	return 1;
+}
+__setup("sb=", setup_sb);
+#endif
diff -Nru linux/sound/oss/sb_common.c linux-2.4.19-pre5-mjc/sound/oss/sb_common.c
--- linux/sound/oss/sb_common.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_common.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1295 @@
+/*
+ * sound/sb_common.c
+ *
+ * Common routines for Sound Blaster compatible cards.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts
+ *                       for full duplex support ( only sb16 by now )
+ * Rolf Fokkens:	 Added (BETA?) support for ES1887 chips.
+ * (fokkensr@vertis.nl)	 Which means: You can adjust the recording levels.
+ *
+ * 2000/01/18 - separated sb_card and sb_common -
+ * Jeff Garzik <jgarzik@mandrakesoft.com>
+ *
+ * 2000/09/18 - got rid of attach_uart401
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * 2001/01/26 - replaced CLI/STI with spinlocks
+ * Chris Rankin <rankinc@zipworld.com.au>
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+#include "sound_firmware.h"
+
+#include "mpu401.h"
+
+#include "sb_mixer.h"
+#include "sb.h"
+#include "sb_ess.h"
+
+/*
+ * global module flag
+ */
+
+int sb_be_quiet;
+
+static sb_devc *detected_devc;	/* For communication from probe to init */
+static sb_devc *last_devc;	/* For MPU401 initialization */
+
+static unsigned char jazz_irq_bits[] = {
+	0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6
+};
+
+static unsigned char jazz_dma_bits[] = {
+	0, 1, 0, 2, 0, 3, 0, 4
+};
+
+void *smw_free;
+
+/*
+ * Jazz16 chipset specific control variables
+ */
+
+static int jazz16_base;			/* Not detected */
+static unsigned char jazz16_bits;	/* I/O relocation bits */
+static spinlock_t jazz16_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Logitech Soundman Wave specific initialization code
+ */
+
+#ifdef SMW_MIDI0001_INCLUDED
+#include "smw-midi0001.h"
+#else
+static unsigned char *smw_ucode;
+static int      smw_ucodeLen;
+
+#endif
+
+sb_devc *last_sb;		/* Last sb loaded */
+
+int sb_dsp_command(sb_devc * devc, unsigned char val)
+{
+	int i;
+	unsigned long limit;
+
+	limit = jiffies + HZ / 10;	/* Timeout */
+	
+	/*
+	 * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
+	 * called while interrupts are disabled. This means that the timer is
+	 * disabled also. However the timeout situation is a abnormal condition.
+	 * Normally the DSP should be ready to accept commands after just couple of
+	 * loops.
+	 */
+
+	for (i = 0; i < 500000 && (limit-jiffies)>0; i++)
+	{
+		if ((inb(DSP_STATUS) & 0x80) == 0)
+		{
+			outb((val), DSP_COMMAND);
+			return 1;
+		}
+	}
+	printk(KERN_WARNING "Sound Blaster:  DSP command(%x) timeout.\n", val);
+	return 0;
+}
+
+int sb_dsp_get_byte(sb_devc * devc)
+{
+	int i;
+
+	for (i = 1000; i; i--)
+	{
+		if (inb(DSP_DATA_AVAIL) & 0x80)
+			return inb(DSP_READ);
+	}
+	return 0xffff;
+}
+
+static void sb_intr (sb_devc *devc)
+{
+	int status;
+	unsigned char   src = 0xff;
+
+	if (devc->model == MDL_SB16)
+	{
+		src = sb_getmixer(devc, IRQ_STAT);	/* Interrupt source register */
+
+		if (src & 4)						/* MPU401 interrupt */
+			if(devc->midi_irq_cookie)
+				uart401intr(devc->irq, devc->midi_irq_cookie, NULL);
+
+		if (!(src & 3))
+			return;	/* Not a DSP interrupt */
+	}
+	if (devc->intr_active && (!devc->fullduplex || (src & 0x01)))
+	{
+		switch (devc->irq_mode)
+		{
+			case IMODE_OUTPUT:
+				DMAbuf_outputintr(devc->dev, 1);
+				break;
+
+			case IMODE_INPUT:
+				DMAbuf_inputintr(devc->dev);
+				break;
+
+			case IMODE_INIT:
+				break;
+
+			case IMODE_MIDI:
+				sb_midi_interrupt(devc);
+				break;
+
+			default:
+				/* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */
+				;
+		}
+	}
+	else if (devc->intr_active_16 && (src & 0x02))
+	{
+		switch (devc->irq_mode_16)
+		{
+			case IMODE_OUTPUT:
+				DMAbuf_outputintr(devc->dev, 1);
+				break;
+
+			case IMODE_INPUT:
+				DMAbuf_inputintr(devc->dev);
+				break;
+
+			case IMODE_INIT:
+				break;
+
+			default:
+				/* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */
+				;
+		}
+	}
+	/*
+	 * Acknowledge interrupts 
+	 */
+
+	if (src & 0x01)
+		status = inb(DSP_DATA_AVAIL);
+
+	if (devc->model == MDL_SB16 && src & 0x02)
+		status = inb(DSP_DATA_AVL16);
+}
+
+static void pci_intr(sb_devc *devc)
+{
+	int src = inb(devc->pcibase+0x1A);
+	src&=3;
+	if(src)
+		sb_intr(devc);
+}
+
+static void sbintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	sb_devc *devc = dev_id;
+
+	devc->irq_ok = 1;
+
+	switch (devc->model) {
+	case MDL_ESSPCI:
+		pci_intr (devc);
+		break;
+		
+	case MDL_ESS:
+		ess_intr (devc);
+		break;
+	default:
+		sb_intr (devc);
+		break;
+	}
+}
+
+int sb_dsp_reset(sb_devc * devc)
+{
+	int loopc;
+
+	DEB(printk("Entered sb_dsp_reset()\n"));
+
+	if (devc->model == MDL_ESS) return ess_dsp_reset (devc);
+
+	/* This is only for non-ESS chips */
+
+	outb(1, DSP_RESET);
+
+	udelay(10);
+	outb(0, DSP_RESET);
+	udelay(30);
+
+	for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
+
+	if (inb(DSP_READ) != 0xAA)
+	{
+		DDB(printk("sb: No response to RESET\n"));
+		return 0;	/* Sorry */
+	}
+
+	DEB(printk("sb_dsp_reset() OK\n"));
+
+	return 1;
+}
+
+static void dsp_get_vers(sb_devc * devc)
+{
+	int i;
+
+	unsigned long   flags;
+
+	DDB(printk("Entered dsp_get_vers()\n"));
+	spin_lock_irqsave(&devc->lock, flags);
+	devc->major = devc->minor = 0;
+	sb_dsp_command(devc, 0xe1);	/* Get version */
+
+	for (i = 100000; i; i--)
+	{
+		if (inb(DSP_DATA_AVAIL) & 0x80)
+		{
+			if (devc->major == 0)
+				devc->major = inb(DSP_READ);
+			else
+			{
+				devc->minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&devc->lock, flags);
+	DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor));
+}
+
+static int sb16_set_dma_hw(sb_devc * devc)
+{
+	int bits;
+
+	if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
+	{
+		printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
+		return 0;
+	}
+	bits = (1 << devc->dma8);
+
+	if (devc->dma16 >= 5 && devc->dma16 <= 7)
+		bits |= (1 << devc->dma16);
+
+	sb_setmixer(devc, DMA_NR, bits);
+	return 1;
+}
+
+static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
+{
+	/*
+	 * This routine initializes new MIDI port setup register of SB Vibra (CT2502).
+	 */
+	unsigned char   bits = sb_getmixer(devc, 0x84) & ~0x06;
+
+	switch (hw_config->io_base)
+	{
+		case 0x300:
+			sb_setmixer(devc, 0x84, bits | 0x04);
+			break;
+
+		case 0x330:
+			sb_setmixer(devc, 0x84, bits | 0x00);
+			break;
+
+		default:
+			sb_setmixer(devc, 0x84, bits | 0x02);		/* Disable MPU */
+			printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
+	}
+}
+
+static int sb16_set_irq_hw(sb_devc * devc, int level)
+{
+	int ival;
+
+	switch (level)
+	{
+		case 5:
+			ival = 2;
+			break;
+		case 7:
+			ival = 4;
+			break;
+		case 9:
+			ival = 1;
+			break;
+		case 10:
+			ival = 8;
+			break;
+		default:
+			printk(KERN_ERR "SB16: Invalid IRQ%d\n", level);
+			return 0;
+	}
+	sb_setmixer(devc, IRQ_NR, ival);
+	return 1;
+}
+
+static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char bits = 0;
+	unsigned long flags;
+
+	if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
+		return;
+
+	switch (hw_config->io_base)
+	{
+		case 0x220:
+			bits = 1;
+			break;
+		case 0x240:
+			bits = 2;
+			break;
+		case 0x260:
+			bits = 3;
+			break;
+		default:
+			return;
+	}
+	bits = jazz16_bits = bits << 5;
+	jazz16_base = hw_config->io_base;
+
+	/*
+	 *	Magic wake up sequence by writing to 0x201 (aka Joystick port)
+	 */
+	spin_lock_irqsave(&jazz16_lock, flags);
+	outb((0xAF), 0x201);
+	outb((0x50), 0x201);
+	outb((bits), 0x201);
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+}
+
+static int init_Jazz16(sb_devc * devc, struct address_info *hw_config)
+{
+	char name[100];
+	/*
+	 * First try to check that the card has Jazz16 chip. It identifies itself
+	 * by returning 0x12 as response to DSP command 0xfa.
+	 */
+
+	if (!sb_dsp_command(devc, 0xfa))
+		return 0;
+
+	if (sb_dsp_get_byte(devc) != 0x12)
+		return 0;
+
+	/*
+	 * OK so far. Now configure the IRQ and DMA channel used by the card.
+	 */
+	if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0)
+	{
+		printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
+		return 0;
+	}
+	if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0)
+	{
+		  printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
+		  return 0;
+	}
+	if (hw_config->dma2 < 0)
+	{
+		printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n");
+		return 0;
+	}
+	if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0)
+	{
+		printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
+		return 0;
+	}
+	devc->dma16 = hw_config->dma2;
+
+	if (!sb_dsp_command(devc, 0xfb))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] |
+			(jazz_dma_bits[hw_config->dma2] << 4)))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq]))
+		return 0;
+
+	/*
+	 * Now we have configured a standard Jazz16 device. 
+	 */
+	devc->model = MDL_JAZZ;
+	strcpy(name, "Jazz16");
+
+	hw_config->name = "Jazz16";
+	devc->caps |= SB_NO_MIDI;
+	return 1;
+}
+
+static void relocate_ess1688(sb_devc * devc)
+{
+	unsigned char bits;
+
+	switch (devc->base)
+	{
+		case 0x220:
+			bits = 0x04;
+			break;
+		case 0x230:
+			bits = 0x05;
+			break;
+		case 0x240:
+			bits = 0x06;
+			break;
+		case 0x250:
+			bits = 0x07;
+			break;
+		default:
+			return;	/* Wrong port */
+	}
+
+	DDB(printk("Doing ESS1688 address selection\n"));
+	
+	/*
+	 * ES1688 supports two alternative ways for software address config.
+	 * First try the so called Read-Sequence-Key method.
+	 */
+
+	/* Reset the sequence logic */
+	inb(0x229);
+	inb(0x229);
+	inb(0x229);
+
+	/* Perform the read sequence */
+	inb(0x22b);
+	inb(0x229);
+	inb(0x22b);
+	inb(0x229);
+	inb(0x229);
+	inb(0x22b);
+	inb(0x229);
+
+	/* Select the base address by reading from it. Then probe using the port. */
+	inb(devc->base);
+	if (sb_dsp_reset(devc))	/* Bingo */
+		return;
+
+#if 0				/* This causes system lockups (Nokia 386/25 at least) */
+	/*
+	 * The last resort is the system control register method.
+	 */
+
+	outb((0x00), 0xfb);	/* 0xFB is the unlock register */
+	outb((0x00), 0xe0);	/* Select index 0 */
+	outb((bits), 0xe1);	/* Write the config bits */
+	outb((0x00), 0xf9);	/* 0xFB is the lock register */
+#endif
+}
+
+int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo)
+{
+	sb_devc sb_info;
+	sb_devc *devc = &sb_info;
+
+	memset((char *) &sb_info, 0, sizeof(sb_info));	/* Zero everything */
+
+	/* Copy module options in place */
+	if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options));
+
+	sb_info.my_mididev = -1;
+	sb_info.my_mixerdev = -1;
+	sb_info.dev = -1;
+
+	/*
+	 * Initialize variables 
+	 */
+	
+	DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base));
+	if (check_region(hw_config->io_base, 16))
+	{
+#ifdef MODULE
+		printk(KERN_INFO "sb: I/O region in use.\n");
+#endif
+		return 0;
+	}
+
+	devc->lock = SPIN_LOCK_UNLOCKED;
+	devc->type = hw_config->card_subtype;
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->dma8 = hw_config->dma;
+
+	devc->dma16 = -1;
+	devc->pcibase = pciio;
+	
+	if(pci == SB_PCI_ESSMAESTRO)
+	{
+		devc->model = MDL_ESSPCI;
+		devc->caps |= SB_PCI_IRQ;
+		hw_config->driver_use_1 |= SB_PCI_IRQ;
+		hw_config->card_subtype	= MDL_ESSPCI;
+	}
+	
+	if(pci == SB_PCI_YAMAHA)
+	{
+		devc->model = MDL_YMPCI;
+		devc->caps |= SB_PCI_IRQ;
+		hw_config->driver_use_1 |= SB_PCI_IRQ;
+		hw_config->card_subtype	= MDL_YMPCI;
+		
+		printk("Yamaha PCI mode.\n");
+	}
+	
+	if (devc->sbmo.acer)
+	{
+		unsigned long flags;
+
+		spin_lock_irqsave(&devc->lock, flags);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x00);
+		spin_unlock_irqrestore(&devc->lock, flags);
+	}
+	/*
+	 * Detect the device
+	 */
+
+	if (sb_dsp_reset(devc))
+		dsp_get_vers(devc);
+	else
+		devc->major = 0;
+
+	if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
+		if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
+			relocate_Jazz16(devc, hw_config);
+
+	if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0))
+		relocate_ess1688(devc);
+
+	if (!sb_dsp_reset(devc))
+	{
+		DDB(printk("SB reset failed\n"));
+#ifdef MODULE
+		printk(KERN_INFO "sb: dsp reset failed.\n");
+#endif
+		return 0;
+	}
+	if (devc->major == 0)
+		dsp_get_vers(devc);
+
+	if (devc->major == 3 && devc->minor == 1)
+	{
+		if (devc->type == MDL_AZTECH)		/* SG Washington? */
+		{
+			if (sb_dsp_command(devc, 0x09))
+				if (sb_dsp_command(devc, 0x00))	/* Enter WSS mode */
+				{
+					int i;
+
+					/* Have some delay */
+					for (i = 0; i < 10000; i++)
+						inb(DSP_DATA_AVAIL);
+					devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
+					devc->model = MDL_AZTECH;
+				}
+		}
+	}
+	
+	if(devc->type == MDL_ESSPCI)
+		devc->model = MDL_ESSPCI;
+		
+	if(devc->type == MDL_YMPCI)
+	{
+		printk("YMPCI selected\n");
+		devc->model = MDL_YMPCI;
+	}
+		
+	/*
+	 * Save device information for sb_dsp_init()
+	 */
+
+
+	detected_devc = (sb_devc *)kmalloc(sizeof(sb_devc), GFP_KERNEL);
+	if (detected_devc == NULL)
+	{
+		printk(KERN_ERR "sb: Can't allocate memory for device information\n");
+		return 0;
+	}
+	memcpy(detected_devc, devc, sizeof(sb_devc));
+	MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
+	return 1;
+}
+
+int sb_dsp_init(struct address_info *hw_config, struct module *owner)
+{
+	sb_devc *devc;
+	char name[100];
+	extern int sb_be_quiet;
+	int	mixer22, mixer30;
+	
+/*
+ * Check if we had detected a SB device earlier
+ */
+	DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base));
+	name[0] = 0;
+
+	if (detected_devc == NULL)
+	{
+		MDB(printk("No detected device\n"));
+		return 0;
+	}
+	devc = detected_devc;
+	detected_devc = NULL;
+
+	if (devc->base != hw_config->io_base)
+	{
+		DDB(printk("I/O port mismatch\n"));
+		return 0;
+	}
+	/*
+	 * Now continue initialization of the device
+	 */
+
+	devc->caps = hw_config->driver_use_1;
+
+	if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0)
+	{			/* IRQ setup */
+		
+		/*
+		 *	ESS PCI cards do shared PCI IRQ stuff. Since they
+		 *	will get shared PCI irq lines we must cope.
+		 */
+		 
+		int i=(devc->caps&SB_PCI_IRQ)?SA_SHIRQ:0;
+		
+		if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0)
+		{
+			printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
+			return 0;
+		}
+		devc->irq_ok = 0;
+
+		if (devc->major == 4)
+			if (!sb16_set_irq_hw(devc, devc->irq))	/* Unsupported IRQ */
+			{
+				free_irq(devc->irq, devc);
+				return 0;
+			}
+		if ((devc->type == 0 || devc->type == MDL_ESS) &&
+			devc->major == 3 && devc->minor == 1)
+		{		/* Handle various chipsets which claim they are SB Pro compatible */
+			if ((devc->type != 0 && devc->type != MDL_ESS) ||
+				!ess_init(devc, hw_config))
+			{
+				if ((devc->type != 0 && devc->type != MDL_JAZZ &&
+					 devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config))
+				{
+					DDB(printk("This is a genuine SB Pro\n"));
+				}
+			}
+		}
+		if (devc->major == 4 && devc->minor <= 11 )	/* Won't work */
+			devc->irq_ok = 1;
+		else
+		{
+			int n;
+
+			for (n = 0; n < 3 && devc->irq_ok == 0; n++)
+			{
+				if (sb_dsp_command(devc, 0xf2))	/* Cause interrupt immediately */
+				{
+					int i;
+
+					for (i = 0; !devc->irq_ok && i < 10000; i++);
+				}
+			}
+			if (!devc->irq_ok)
+				printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
+			else
+			{
+				DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
+			}
+		}
+	}			/* IRQ setup */
+	request_region(hw_config->io_base, 16, "soundblaster");
+
+	last_sb = devc;
+	
+	switch (devc->major)
+	{
+		case 1:		/* SB 1.0 or 1.5 */
+			devc->model = hw_config->card_subtype = MDL_SB1;
+			break;
+
+		case 2:		/* SB 2.x */
+			if (devc->minor == 0)
+				devc->model = hw_config->card_subtype = MDL_SB2;
+			else
+				devc->model = hw_config->card_subtype = MDL_SB201;
+			break;
+
+		case 3:		/* SB Pro and most clones */
+			switch (devc->model) {
+			case 0:
+				devc->model = hw_config->card_subtype = MDL_SBPRO;
+				if (hw_config->name == NULL)
+					hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
+				break;
+			case MDL_ESS:
+				ess_dsp_init(devc, hw_config);
+				break;
+			}
+			break;
+
+		case 4:
+			devc->model = hw_config->card_subtype = MDL_SB16;
+			/* 
+			 * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0
+			 * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas
+			 * a "standard" SB16 doesn't have a register at 0x4c.  ALS100 actively
+			 * updates register 0x22 whenever 0x30 changes, as per the SB16 spec.
+			 * Since ALS007 doesn't, this can be used to differentiate the 2 cards.
+			 */
+			if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) 
+			{
+				mixer30 = sb_getmixer(devc,0x30);
+				sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f);
+				sb_setmixer(devc,0x30,0xff);
+				/* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */
+				/* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10.     */
+				if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) 
+				{
+					devc->submodel = SUBMDL_ALS100;
+					if (hw_config->name == NULL)
+						hw_config->name = "Sound Blaster 16 (ALS-100)";
+        			}
+        			else
+        			{
+        				sb_setmixer(devc,0x3c,0x1f);    /* Enable all inputs */
+					sb_setmixer(devc,0x4c,0x1f);
+					sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */
+					devc->submodel = SUBMDL_ALS007;
+					if (hw_config->name == NULL)
+						hw_config->name = "Sound Blaster 16 (ALS-007)";
+				}
+				sb_setmixer(devc,0x30,mixer30);
+			}
+			else if (hw_config->name == NULL)
+				hw_config->name = "Sound Blaster 16";
+
+			if (hw_config->dma2 == -1)
+				devc->dma16 = devc->dma8;
+			else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
+			{
+				printk(KERN_WARNING  "SB16: Bad or missing 16 bit DMA channel\n");
+				devc->dma16 = devc->dma8;
+			}
+			else
+				devc->dma16 = hw_config->dma2;
+
+			if(!sb16_set_dma_hw(devc)) {
+				free_irq(devc->irq, devc);
+			        release_region(hw_config->io_base, 16);
+				return 0;
+			}
+
+			devc->caps |= SB_NO_MIDI;
+	}
+
+	if (!(devc->caps & SB_NO_MIXER))
+		if (devc->major == 3 || devc->major == 4)
+			sb_mixer_init(devc, owner);
+
+	if (!(devc->caps & SB_NO_MIDI))
+		sb_dsp_midi_init(devc, owner);
+
+	if (hw_config->name == NULL)
+		hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
+
+	sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor);
+	conf_printf(name, hw_config);
+
+	/*
+	 * Assuming that a sound card is Sound Blaster (compatible) is the most common
+	 * configuration error and the mother of all problems. Usually sound cards
+	 * emulate SB Pro but in addition they have a 16 bit native mode which should be
+	 * used in Unix. See Readme.cards for more information about configuring OSS/Free
+	 * properly.
+	 */
+	if (devc->model <= MDL_SBPRO)
+	{
+		if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
+		{
+			printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n");
+			printk(KERN_INFO "In many cases there is another way to configure OSS so that\n");
+			printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n");
+			printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n");
+		}
+		else if (!sb_be_quiet && devc->model == MDL_SBPRO)
+		{
+			printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor);
+			printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n");
+			printk(KERN_INFO "is incorrectly configured.\n");
+		}
+	}
+	hw_config->card_subtype = devc->model;
+	hw_config->slots[0]=devc->dev;
+	last_devc = devc;	/* For SB MPU detection */
+
+	if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
+	{
+		if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
+		{
+			printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
+		}
+		if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+		{
+			if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
+				printk(KERN_WARNING "Sound Blaster:  can't allocate 16 bit DMA channel %d.\n", devc->dma16);
+		}
+		sb_audio_init(devc, name, owner);
+		hw_config->slots[0]=devc->dev;
+	}
+	else
+	{
+		MDB(printk("Sound Blaster:  no audio devices found.\n"));
+	}
+	return 1;
+}
+
+void sb_dsp_disable_midi(int io_base)
+{
+}
+
+void sb_dsp_disable_recording(int io_base)
+{
+}
+
+/* if (sbmpu) below we allow mpu401 to manage the midi devs
+   otherwise we have to unload them. (Andrzej Krzysztofowicz) */
+   
+void sb_dsp_unload(struct address_info *hw_config, int sbmpu)
+{
+	sb_devc *devc;
+
+	devc = audio_devs[hw_config->slots[0]]->devc;
+
+	if (devc && devc->base == hw_config->io_base)
+	{
+		if ((devc->model & MDL_ESS) && devc->pcibase)
+			release_region(devc->pcibase, 8);
+
+		release_region(devc->base, 16);
+
+		if (!(devc->caps & SB_NO_AUDIO))
+		{
+			sound_free_dma(devc->dma8);
+			if (devc->dma16 >= 0)
+				sound_free_dma(devc->dma16);
+		}
+		if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI))
+		{
+			if (devc->irq > 0)
+				free_irq(devc->irq, devc);
+
+			sb_mixer_unload(devc);
+			/* We don't have to do this bit any more the UART401 is its own
+				master  -- Krzysztof Halasa */
+			/* But we have to do it, if UART401 is not detected */
+			if (!sbmpu)
+				sound_unload_mididev(devc->my_mididev);
+			sound_unload_audiodev(devc->dev);
+		}
+		kfree(devc);
+	}
+	else
+		release_region(hw_config->io_base, 16);
+	if(detected_devc)
+		kfree(detected_devc);
+}
+
+/*
+ *	Mixer access routines
+ *
+ *	ES1887 modifications: some mixer registers reside in the
+ *	range above 0xa0. These must be accessed in another way.
+ */
+
+void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
+{
+	unsigned long flags;
+
+	if (devc->model == MDL_ESS) return ess_setmixer (devc, port, value);
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+	udelay(20);
+	outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+	udelay(20);
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+unsigned int sb_getmixer(sb_devc * devc, unsigned int port)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	if (devc->model == MDL_ESS) return ess_getmixer (devc, port);
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+	udelay(20);
+	val = inb(MIXER_DATA);
+	udelay(20);
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	return val;
+}
+
+void sb_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = sb_getmixer(devc, reg);
+	value = (value & ~mask) | (val & mask);
+	sb_setmixer(devc, reg, value);
+}
+
+/*
+ *	MPU401 MIDI initialization.
+ */
+
+static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&jazz16_lock, flags);  /* NOT the SB card? */
+
+	outb((addr & 0xff), base + 1);	/* Low address bits */
+	outb((addr >> 8), base + 2);	/* High address bits */
+	outb((val), base);	/* Data */
+
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+}
+
+static unsigned char smw_getmem(sb_devc * devc, int base, int addr)
+{
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&jazz16_lock, flags);  /* NOT the SB card? */
+
+	outb((addr & 0xff), base + 1);	/* Low address bits */
+	outb((addr >> 8), base + 2);	/* High address bits */
+	val = inb(base);	/* Data */
+
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+	return val;
+}
+
+static int smw_midi_init(sb_devc * devc, struct address_info *hw_config)
+{
+	int mpu_base = hw_config->io_base;
+	int mp_base = mpu_base + 4;		/* Microcontroller base */
+	int i;
+	unsigned char control;
+
+
+	/*
+	 *  Reset the microcontroller so that the RAM can be accessed
+	 */
+
+	control = inb(mpu_base + 7);
+	outb((control | 3), mpu_base + 7);	/* Set last two bits to 1 (?) */
+	outb(((control & 0xfe) | 2), mpu_base + 7);	/* xxxxxxx0 resets the mc */
+
+	mdelay(3);	/* Wait at least 1ms */
+
+	outb((control & 0xfc), mpu_base + 7);	/* xxxxxx00 enables RAM */
+
+	/*
+	 *  Detect microcontroller by probing the 8k RAM area
+	 */
+	smw_putmem(devc, mp_base, 0, 0x00);
+	smw_putmem(devc, mp_base, 1, 0xff);
+	udelay(10);
+
+	if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff)
+	{
+		DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1)));
+		return 0;	/* No RAM */
+	}
+	/*
+	 *  There is RAM so assume it's really a SM Wave
+	 */
+
+	devc->model = MDL_SMW;
+	smw_mixer_init(devc);
+
+#ifdef MODULE
+	if (!smw_ucode)
+	{
+		smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
+		smw_free = smw_ucode;
+	}
+#endif
+	if (smw_ucodeLen > 0)
+	{
+		if (smw_ucodeLen != 8192)
+		{
+			printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n");
+			return 1;
+		}
+		/*
+		 *  Download microcode
+		 */
+
+		for (i = 0; i < 8192; i++)
+			smw_putmem(devc, mp_base, i, smw_ucode[i]);
+
+		/*
+		 *  Verify microcode
+		 */
+
+		for (i = 0; i < 8192; i++)
+			if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
+			{
+				printk(KERN_ERR "SM Wave: Microcode verification failed\n");
+				return 0;
+			}
+	}
+	control = 0;
+#ifdef SMW_SCSI_IRQ
+	/*
+	 * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
+	 * is disabled by default.
+	 *
+	 * FIXME - make this a module option
+	 *
+	 * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
+	 */
+	{
+		static unsigned char scsi_irq_bits[] = {
+			0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0
+		};
+		control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
+	}
+#endif
+
+#ifdef SMW_OPL4_ENABLE
+	/*
+	 *  Make the OPL4 chip visible on the PC bus at 0x380.
+	 *
+	 *  There is no need to enable this feature since this driver
+	 *  doesn't support OPL4 yet. Also there is no RAM in SM Wave so
+	 *  enabling OPL4 is pretty useless.
+	 */
+	control |= 0x10;	/* Uses IRQ12 if bit 0x20 == 0 */
+	/* control |= 0x20;      Uncomment this if you want to use IRQ7 */
+#endif
+	outb((control | 0x03), mpu_base + 7);	/* xxxxxx11 restarts */
+	hw_config->name = "SoundMan Wave";
+	return 1;
+}
+
+static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
+{
+	int mpu_base = hw_config->io_base;
+	int sb_base = devc->base;
+	int irq = hw_config->irq;
+
+	unsigned char bits = 0;
+	unsigned long flags;
+
+	if (irq < 0)
+		irq *= -1;
+
+	if (irq < 1 || irq > 15 ||
+	    jazz_irq_bits[irq] == 0)
+	{
+		printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
+		return 0;
+	}
+	switch (sb_base)
+	{
+		case 0x220:
+			bits = 1;
+			break;
+		case 0x240:
+			bits = 2;
+			break;
+		case 0x260:
+			bits = 3;
+			break;
+		default:
+			return 0;
+	}
+	bits = jazz16_bits = bits << 5;
+	switch (mpu_base)
+	{
+		case 0x310:
+			bits |= 1;
+			break;
+		case 0x320:
+			bits |= 2;
+			break;
+		case 0x330:
+			bits |= 3;
+			break;
+		default:
+			printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
+			return 0;
+	}
+	/*
+	 *	Magic wake up sequence by writing to 0x201 (aka Joystick port)
+	 */
+	spin_lock_irqsave(&jazz16_lock, flags);
+	outb(0xAF, 0x201);
+	outb(0x50, 0x201);
+	outb(bits, 0x201);
+	spin_unlock_irqrestore(&jazz16_lock, flags);
+
+	hw_config->name = "Jazz16";
+	smw_midi_init(devc, hw_config);
+
+	if (!sb_dsp_command(devc, 0xfb))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] |
+			    (jazz_dma_bits[devc->dma16] << 4)))
+		return 0;
+
+	if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] |
+			    (jazz_irq_bits[irq] << 4)))
+		return 0;
+
+	return 1;
+}
+
+int probe_sbmpu(struct address_info *hw_config, struct module *owner)
+{
+	sb_devc *devc = last_devc;
+	int ret;
+
+	if (last_devc == NULL)
+		return 0;
+
+	last_devc = 0;
+
+	if (hw_config->io_base <= 0)
+	{
+		/* The real vibra16 is fine about this, but we have to go
+		   wipe up after Cyrix again */
+		   	   
+		if(devc->model == MDL_SB16 && devc->minor >= 12)
+		{
+			unsigned char   bits = sb_getmixer(devc, 0x84) & ~0x06;
+			sb_setmixer(devc, 0x84, bits | 0x02);		/* Disable MPU */
+		}
+		return 0;
+	}
+
+#if defined(CONFIG_SOUND_ALSA_MPU401)
+	if (devc->model == MDL_ESS)
+	{
+		if (check_region(hw_config->io_base, 2))
+		{
+			printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
+			return 0;
+		}
+		if (!ess_midi_init(devc, hw_config))
+			return 0;
+		hw_config->name = "ESS1xxx MPU";
+		devc->midi_irq_cookie = NULL;
+		if (!probe_mpu401(hw_config))
+			return 0;
+		attach_mpu401(hw_config, owner);
+		if (last_sb->irq == -hw_config->irq)
+			last_sb->midi_irq_cookie=(void *)hw_config->slots[1];
+		return 1;
+	}
+#endif
+
+	switch (devc->model)
+	{
+		case MDL_SB16:
+			if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
+			{
+				printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base);
+				return 0;
+			}
+			hw_config->name = "Sound Blaster 16";
+			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+				hw_config->irq = -devc->irq;
+			if (devc->minor > 12)		/* What is Vibra's version??? */
+				sb16_set_mpu_port(devc, hw_config);
+			break;
+
+		case MDL_JAZZ:
+			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+				hw_config->irq = -devc->irq;
+			if (!init_Jazz16_midi(devc, hw_config))
+				return 0;
+			break;
+
+		case MDL_YMPCI:
+			hw_config->name = "Yamaha PCI Legacy";
+			printk("Yamaha PCI legacy UART401 check.\n");
+			break;
+		default:
+			return 0;
+	}
+	
+	ret = probe_uart401(hw_config, owner);
+	if (ret)
+		last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc;
+	return ret;
+}
+
+void unload_sbmpu(struct address_info *hw_config)
+{
+#if defined(CONFIG_SOUND_ALSA_MPU401)
+	if (!strcmp (hw_config->name, "ESS1xxx MPU")) {
+		unload_mpu401(hw_config);
+		return;
+	}
+#endif
+	unload_uart401(hw_config);
+}
+
+EXPORT_SYMBOL(sb_dsp_init);
+EXPORT_SYMBOL(sb_dsp_detect);
+EXPORT_SYMBOL(sb_dsp_unload);
+EXPORT_SYMBOL(sb_dsp_disable_midi);
+EXPORT_SYMBOL(sb_be_quiet);
+EXPORT_SYMBOL(probe_sbmpu);
+EXPORT_SYMBOL(unload_sbmpu);
+EXPORT_SYMBOL(smw_free);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/sb_ess.c linux-2.4.19-pre5-mjc/sound/oss/sb_ess.c
--- linux/sound/oss/sb_ess.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_ess.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1826 @@
+#undef FKS_LOGGING
+#undef FKS_TEST
+
+/*
+ * tabs should be 4 spaces, in vi(m): set tabstop=4
+ *
+ * TODO: 	consistency speed calculations!!
+ *			cleanup!
+ * ????:	Did I break MIDI support?
+ *
+ * History:
+ *
+ * Rolf Fokkens	 (Dec 20 1998):	ES188x recording level support on a per
+ * fokkensr@vertis.nl			input basis.
+ *				 (Dec 24 1998):	Recognition of ES1788, ES1887, ES1888,
+ *								ES1868, ES1869 and ES1878. Could be used for
+ *								specific handling in the future. All except
+ *								ES1887 and ES1888 and ES688 are handled like
+ *								ES1688.
+ *				 (Dec 27 1998):	RECLEV for all (?) ES1688+ chips. ES188x now
+ *								have the "Dec 20" support + RECLEV
+ *				 (Jan  2 1999):	Preparation for Full Duplex. This means
+ *								Audio 2 is now used for playback when dma16
+ *								is specified. The next step would be to use
+ *								Audio 1 and Audio 2 at the same time.
+ *				 (Jan  9 1999):	Put all ESS stuff into sb_ess.[ch], this
+ *								includes both the ESS stuff that has been in
+ *								sb_*[ch] before I touched it and the ESS support
+ *								I added later
+ *				 (Jan 23 1999):	Full Duplex seems to work. I wrote a small
+ *								test proggy which works OK. Haven't found
+ *								any applications to test it though. So why did
+ *								I bother to create it anyway?? :) Just for
+ *								fun.
+ *				 (May  2 1999):	I tried to be too smart by "introducing"
+ *								ess_calc_best_speed (). The idea was that two
+ *								dividers could be used to setup a samplerate,
+ *								ess_calc_best_speed () would choose the best.
+ *								This works for playback, but results in
+ *								recording problems for high samplerates. I
+ *								fixed this by removing ess_calc_best_speed ()
+ *								and just doing what the documentation says. 
+ * Andy Sloane   (Jun  4 1999): Stole some code from ALSA to fix the playback
+ * andy@guildsoftware.com		speed on ES1869, ES1879, ES1887, and ES1888.
+ * 								1879's were previously ignored by this driver;
+ * 								added (untested) support for those.
+ * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for
+ * zezo@inet.bg					_ALL_ ESS models, not only ES1887
+ *
+ * This files contains ESS chip specifics. It's based on the existing ESS
+ * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This
+ * file adds features like:
+ * - Chip Identification (as shown in /proc/sound)
+ * - RECLEV support for ES1688 and later
+ * - 6 bits playback level support chips later than ES1688
+ * - Recording level support on a per-device basis for ES1887
+ * - Full-Duplex for ES1887
+ *
+ * Full duplex is enabled by specifying dma16. While the normal dma must
+ * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit
+ * DMA channel, while the others are 8 bit..
+ *
+ * ESS detection isn't full proof (yet). If it fails an additional module
+ * parameter esstype can be specified to be one of the following:
+ * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888
+ * -1 means: mimic 2.0 behaviour, 
+ *  0 means: auto detect.
+ *   others: explicitly specify chip
+ * -1 is default, cause auto detect still doesn't work.
+ */
+
+/*
+ * About the documentation
+ *
+ * I don't know if the chips all are OK, but the documentation is buggy. 'cause
+ * I don't have all the cips myself, there's a lot I cannot verify. I'll try to
+ * keep track of my latest insights about his here. If you have additional info,
+ * please enlighten me (fokkensr@vertis.nl)!
+ *
+ * I had the impression that ES1688 also has 6 bit master volume control. The
+ * documentation about ES1888 (rev C, october '95) claims that ES1888 has
+ * the following features ES1688 doesn't have:
+ * - 6 bit master volume
+ * - Full Duplex
+ * So ES1688 apparently doesn't have 6 bit master volume control, but the
+ * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too?
+ * Without RECLEV ES688 won't be much fun I guess.
+ *
+ * From the ES1888 (rev C, october '95) documentation I got the impression
+ * that registers 0x68 to 0x6e don't exist which means: no recording volume
+ * controls. To my surprise the ES888 documentation (1/14/96) claims that
+ * ES888 does have these record mixer registers, but that ES1888 doesn't have
+ * 0x69 and 0x6b. So the rest should be there.
+ *
+ * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2
+ * is both record and playback. I think I should use Audio 2 for all playback.
+ *
+ * The documentation is an adventure: it's close but not fully accurate. I
+ * found out that after a reset some registers are *NOT* reset, though the
+ * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are
+ * related to the Audio 2 channel. I also was suprised about the consequenses
+ * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves
+ * into ES1888 mode. This means that it claims IRQ 11, which happens to be my
+ * ISDN adapter. Needless to say it no longer worked. I now understand why
+ * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS
+ * did it.
+ *
+ * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed
+ * as if it's exactly the same as register 0xa1. This is *NOT* true. The
+ * description of 0x70 in ES1869 docs is accurate however.
+ * Well, the assumption about ES1869 was wrong: register 0x70 is very much
+ * like register 0xa1, except that bit 7 is allways 1, whatever you want
+ * it to be.
+ *
+ * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2
+ * has effect.
+ *
+ * Software reset not being able to reset all registers is great! Especially
+ * the fact that register 0x78 isn't reset is great when you wanna change back
+ * to single dma operation (simplex): audio 2 is still operation, and uses the
+ * same dma as audio 1: your ess changes into a funny echo machine.
+ *
+ * Received the new that ES1688 is detected as a ES1788. Did some thinking:
+ * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register
+ * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If
+ * can be modified, it's a 1688", which lead to a correct detection
+ * of my ES1887. It resulted however in bad detection of 1688 (reported by mail)
+ * and 1868 (if no PnP detection first): they result in a 1788 being detected.
+ * I don't have docs on 1688, but I do have docs on 1868: The documentation is
+ * probably inaccurate in the fact that I should check bit 2, not bit 3. This
+ * is what I do now.
+ */
+
+/*
+ * About recognition of ESS chips
+ *
+ * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
+ * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but
+ * during detection the text claims that "this chip may be ..." when a step
+ * fails. This scheme is used to distinct between the above chips.
+ * It appears however that some PnP chips like ES1868 are recognized as ES1788
+ * by the ES1887 detection scheme. These PnP chips can be detected in another
+ * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think)
+ * by repeatedly reading mixer register 0x40. This is done by ess_identify in
+ * sb_common.c.
+ * This results in the following detection steps:
+ * - distinct between ES688 and ES1688+ (as always done in this driver)
+ *   if ES688 we're ready
+ * - try to detect ES1868, ES1869 or ES1878
+ *   if successful we're ready
+ * - try to detect ES1888, ES1887 or ES1788
+ *   if successful we're ready
+ * - Dunno. Must be 1688. Will do in general
+ *
+ * About RECLEV support:
+ *
+ * The existing ES1688 support didn't take care of the ES1688+ recording
+ * levels very well. Whenever a device was selected (recmask) for recording
+ * it's recording level was loud, and it couldn't be changed. The fact that
+ * internal register 0xb4 could take care of RECLEV, didn't work meaning until
+ * it's value was restored every time the chip was reset; this reset the
+ * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
+ *
+ * About ES1887 support:
+ *
+ * The ES1887 has separate registers to control the recording levels, for all
+ * inputs. The ES1887 specific software makes these levels the same as their
+ * corresponding playback levels, unless recmask says they aren't recorded. In
+ * the latter case the recording volumes are 0.
+ * Now recording levels of inputs can be controlled, by changing the playback
+ * levels. Futhermore several devices can be recorded together (which is not
+ * possible with the ES1688.
+ * Besides the separate recording level control for each input, the common
+ * recordig level can also be controlled by RECLEV as described above.
+ *
+ * Not only ES1887 have this recording mixer. I know the following from the
+ * documentation:
+ * ES688	no
+ * ES1688	no
+ * ES1868	no
+ * ES1869	yes
+ * ES1878	no
+ * ES1879	yes
+ * ES1888	no/yes	Contradicting documentation; most recent: yes
+ * ES1946	yes		This is a PCI chip; not handled by this driver
+ */
+
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+#include "sb_mixer.h"
+#include "sb.h"
+
+#include "sb_ess.h"
+
+#define ESSTYPE_LIKE20	-1		/* Mimic 2.0 behaviour					*/
+#define ESSTYPE_DETECT	0		/* Mimic 2.0 behaviour					*/
+
+#define SUBMDL_ES1788	0x10	/* Subtype ES1788 for specific handling */
+#define SUBMDL_ES1868	0x11	/* Subtype ES1868 for specific handling */
+#define SUBMDL_ES1869	0x12	/* Subtype ES1869 for specific handling */
+#define SUBMDL_ES1878	0x13	/* Subtype ES1878 for specific handling */
+#define SUBMDL_ES1879	0x16    /* ES1879 was initially forgotten */
+#define SUBMDL_ES1887	0x14	/* Subtype ES1887 for specific handling */
+#define SUBMDL_ES1888	0x15	/* Subtype ES1888 for specific handling */
+
+#define SB_CAP_ES18XX_RATE 0x100
+
+#define ES1688_CLOCK1 795444 /* 128 - div */
+#define ES1688_CLOCK2 397722 /* 256 - div */
+#define ES18XX_CLOCK1 793800 /* 128 - div */
+#define ES18XX_CLOCK2 768000 /* 256 - div */
+
+#ifdef FKS_LOGGING
+static void ess_show_mixerregs (sb_devc *devc);
+#endif
+static int ess_read (sb_devc * devc, unsigned char reg);
+static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data);
+static void ess_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
+
+/****************************************************************************
+ *																			*
+ *									ESS audio								*
+ *																			*
+ ****************************************************************************/
+
+struct ess_command {short cmd; short data;};
+
+/*
+ * Commands for initializing Audio 1 for input (record)
+ */
+static struct ess_command ess_i08m[] =		/* input 8 bit mono */
+	{ {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
+static struct ess_command ess_i16m[] =		/* input 16 bit mono */
+	{ {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
+static struct ess_command ess_i08s[] =		/* input 8 bit stereo */
+	{ {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
+static struct ess_command ess_i16s[] =		/* input 16 bit stereo */
+	{ {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
+
+static struct ess_command *ess_inp_cmds[] =
+	{ ess_i08m, ess_i16m, ess_i08s, ess_i16s };
+
+
+/*
+ * Commands for initializing Audio 1 for output (playback)
+ */
+static struct ess_command ess_o08m[] =		/* output 8 bit mono */
+	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
+static struct ess_command ess_o16m[] =		/* output 16 bit mono */
+	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
+static struct ess_command ess_o08s[] =		/* output 8 bit stereo */
+	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
+static struct ess_command ess_o16s[] =		/* output 16 bit stereo */
+	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
+
+static struct ess_command *ess_out_cmds[] =
+	{ ess_o08m, ess_o16m, ess_o08s, ess_o16s };
+
+static void ess_exec_commands
+	(sb_devc *devc, struct ess_command *cmdtab[])
+{
+	struct ess_command *cmd;
+
+	cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
+
+	while (cmd->cmd != -1) {
+		ess_write (devc, cmd->cmd, cmd->data);
+		cmd++;
+	}
+}
+
+static void ess_change
+	(sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = ess_read (devc, reg);
+	value = (value & ~mask) | (val & mask);
+	ess_write (devc, reg, value);
+}
+
+static void ess_set_output_parms
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (devc->duplex) {
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = nr_bytes;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_OUTPUT;
+	} else {
+		devc->trg_buf = buf;
+		devc->trg_bytes = nr_bytes;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_OUTPUT;
+	}
+}
+
+static void ess_set_input_parms
+	(int dev, unsigned long buf, int count, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	devc->trg_buf = buf;
+	devc->trg_bytes = count;
+	devc->trg_intrflag = intrflag;
+	devc->irq_mode = IMODE_INPUT;
+}
+
+static int ess_calc_div (int clock, int revert, int *speedp, int *diffp)
+{
+	int divider;
+	int speed, diff;
+	int retval;
+
+	speed   = *speedp;
+	divider = (clock + speed / 2) / speed;
+	retval  = revert - divider;
+	if (retval > revert - 1) {
+		retval  = revert - 1;
+		divider = revert - retval;
+	}
+	/* This line is suggested. Must be wrong I think
+	*speedp = (clock + divider / 2) / divider;
+	So I chose the next one */
+
+	*speedp	= clock / divider;
+	diff	= speed - *speedp;
+	if (diff < 0) diff =-diff;
+	*diffp  = diff;
+
+	return retval;
+}
+
+static int ess_calc_best_speed
+	(int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp)
+{
+	int speed1 = *speedp, speed2 = *speedp;
+	int div1, div2;
+	int diff1, diff2;
+	int retval;
+
+	div1 = ess_calc_div (clock1, rev1, &speed1, &diff1);
+	div2 = ess_calc_div (clock2, rev2, &speed2, &diff2);
+
+	if (diff1 < diff2) {
+		*divp   = div1;
+		*speedp = speed1;
+		retval  = 1;
+	} else {
+	/*	*divp   = div2; */
+		*divp   = 0x80 | div2;
+		*speedp = speed2;
+		retval  = 2;
+	}
+
+	return retval;
+}
+
+/*
+ * Depending on the audiochannel ESS devices can
+ * have different clock settings. These are made consistent for duplex
+ * however.
+ * callers of ess_speed only do an audionum suggestion, which means
+ * input suggests 1, output suggests 2. This suggestion is only true
+ * however when doing duplex.
+ */
+static void ess_common_speed (sb_devc *devc, int *speedp, int *divp)
+{
+	int diff = 0, div;
+
+	if (devc->duplex) {
+		/*
+		 * The 0x80 is important for the first audio channel
+		 */
+		if (devc->submodel == SUBMDL_ES1888) {
+			div = 0x80 | ess_calc_div (795500, 256, speedp, &diff);
+		} else {
+			div = 0x80 | ess_calc_div (795500, 128, speedp, &diff);
+		}
+	} else if(devc->caps & SB_CAP_ES18XX_RATE) {
+		if (devc->submodel == SUBMDL_ES1888) {
+			ess_calc_best_speed(397700, 128, 795500, 256, 
+						&div, speedp);
+		} else {
+			ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, 
+						&div, speedp);
+		}
+	} else {
+		if (*speedp > 22000) {
+			div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff);
+		} else {
+			div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff);
+		}
+	}
+	*divp = div;
+}
+
+static void ess_speed (sb_devc *devc, int audionum)
+{
+	int speed;
+	int div, div2;
+
+	ess_common_speed (devc, &(devc->speed), &div);
+
+#ifdef FKS_REG_LOGGING
+printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div);
+#endif
+
+	/* Set filter roll-off to 90% of speed/2 */
+	speed = (devc->speed * 9) / 20;
+
+	div2 = 256 - 7160000 / (speed * 82);
+
+	if (!devc->duplex) audionum = 1;
+
+	if (audionum == 1) {
+		/* Change behaviour of register A1 *
+		sb_chg_mixer(devc, 0x71, 0x20, 0x20)
+		* For ES1869 only??? */
+		ess_write (devc, 0xa1, div);
+		ess_write (devc, 0xa2, div2);
+	} else {
+		ess_setmixer (devc, 0x70, div);
+		/*
+		 * FKS: fascinating: 0x72 doesn't seem to work.
+		 */
+		ess_write (devc, 0xa2, div2);
+		ess_setmixer (devc, 0x72, div2);
+	}
+}
+
+static int ess_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	ess_speed(devc, 1);
+
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+
+	ess_write (devc, 0xb8, 0x0e);	/* Auto init DMA mode */
+	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
+	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/DMA request) */
+
+	ess_exec_commands (devc, ess_inp_cmds);
+
+	ess_change (devc, 0xb1, 0xf0, 0x50);
+	ess_change (devc, 0xb2, 0xf0, 0x50);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	sb_dsp_reset(devc);
+	ess_speed(devc, 1);
+	ess_write (devc, 0xb8, 4);	/* Auto init DMA mode */
+	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
+	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/request) */
+
+	ess_exec_commands (devc, ess_out_cmds);
+
+	ess_change (devc, 0xb1, 0xf0, 0x50);	/* Enable DMA */
+	ess_change (devc, 0xb2, 0xf0, 0x50);	/* Enable IRQ */
+
+	sb_dsp_command(devc, DSP_CMD_SPKON);	/* There be sound! */
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char bits;
+
+/* FKS: qqq
+	sb_dsp_reset(devc);
+*/
+
+	/*
+	 * Auto-Initialize:
+	 * DMA mode + demand mode (8 bytes/request, yes I want it all!)
+	 * But leave 16-bit DMA bit untouched!
+	 */
+	ess_chgmixer (devc, 0x78, 0xd0, 0xd0);
+
+	ess_speed(devc, 2);
+
+	/* bits 4:3 on ES1887 represent recording source. Keep them! */
+	bits = ess_getmixer (devc, 0x7a) & 0x18;
+
+	/* Set stereo/mono */
+	if (devc->channels != 1) bits |= 0x02;
+
+	/* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */
+	if (devc->bits != AFMT_U8) bits |= 0x05;	/* 16 bit */
+
+	/* Enable DMA, IRQ will be shared (hopefully)*/
+	bits |= 0x60;
+
+	ess_setmixer (devc, 0x7a, bits);
+
+	ess_mixer_reload (devc, SOUND_MIXER_PCM);	/* There be sound! */
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n"
+, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma);
+#endif
+
+	if (devc->duplex) {
+		return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount);
+	} else {
+		return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount);
+	}
+}
+
+static void ess_audio_halt_xfer(int dev)
+{
+	unsigned long flags;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	sb_dsp_reset(devc);
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	/*
+	 * Audio 2 may still be operational! Creates awful sounds!
+	 */
+	if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00);
+}
+
+static void ess_audio_start_input
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+
+	ess_change (devc, 0xb8, 0x0f, 0x0f);	/* Go */
+	devc->intr_active = 1;
+}
+
+static void ess_audio_output_block_audio1
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+
+	ess_change (devc, 0xb8, 0x05, 0x05);	/* Go */
+	devc->intr_active = 1;
+}
+
+static void ess_audio_output_block_audio2
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1;
+	count--;
+
+	ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff));
+	ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+	ess_chgmixer (devc, 0x78, 0x03, 0x03);   /* Go */
+
+	devc->irq_mode_16 = IMODE_OUTPUT;
+		devc->intr_active_16 = 1;
+}
+
+static void ess_audio_output_block
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (devc->duplex) {
+		ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag);
+	} else {
+		ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag);
+	}
+}
+
+/*
+ * FKS: the if-statements for both bits and bits_16 are quite alike.
+ * Combine this...
+ */
+static void ess_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	int bits_16 = bits & devc->irq_mode_16;
+	bits &= devc->irq_mode;
+
+	if (!bits && !bits_16) {
+		/* FKS oh oh.... wrong?? for dma 16? */
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	}
+
+	if (bits) {
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+					devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+					devc->trg_intrflag);
+				break;
+		}
+	}
+
+	if (bits_16) {
+		switch (devc->irq_mode_16) {
+		case IMODE_INPUT:
+			ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16,
+					devc->trg_intrflag_16);
+			break;
+
+		case IMODE_OUTPUT:
+			ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16,
+					devc->trg_intrflag_16);
+			break;
+		}
+	}
+
+	devc->trigger_bits = bits | bits_16;
+}
+
+static int ess_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int minspeed, maxspeed, dummydiv;
+
+	if (speed > 0) {
+		minspeed = (devc->duplex ? 6215  : 5000 );
+		maxspeed = (devc->duplex ? 44100 : 48000);
+		if (speed < minspeed) speed = minspeed;
+		if (speed > maxspeed) speed = maxspeed;
+
+		ess_common_speed (devc, &speed, &dummydiv);
+
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * FKS: This is a one-on-one copy of sb1_audio_set_bits
+ */
+static unsigned int ess_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (bits != 0) {
+		if (bits == AFMT_U8 || bits == AFMT_S16_LE) {
+			devc->bits = bits;
+		} else {
+			devc->bits = AFMT_U8;
+		}
+	}
+
+	return devc->bits;
+}
+
+/*
+ * FKS: This is a one-on-one copy of sbpro_audio_set_channels
+ * (*) Modified it!!
+ */
+static short ess_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (channels == 1 || channels == 2) devc->channels = channels;
+
+	return devc->channels;
+}
+
+static struct audio_driver ess_audio_driver =   /* ESS ES688/1688 */
+{
+	owner:			THIS_MODULE,
+	open:			sb_audio_open,
+	close:			sb_audio_close,
+	output_block:	ess_set_output_parms,
+	start_input:	ess_set_input_parms,
+	prepare_for_input:	ess_audio_prepare_for_input,
+	prepare_for_output:	ess_audio_prepare_for_output,
+	halt_io:		ess_audio_halt_xfer,
+	trigger:		ess_audio_trigger,
+	set_speed:		ess_audio_set_speed,
+	set_bits:		ess_audio_set_bits,
+	set_channels:	ess_audio_set_channels
+};
+
+/*
+ * ess_audio_init must be called from sb_audio_init
+ */
+struct audio_driver *ess_audio_init
+		(sb_devc *devc, int *audio_flags, int *format_mask)
+{
+	*audio_flags = DMA_AUTOMODE;
+	*format_mask |= AFMT_S16_LE;
+
+	if (devc->duplex) {
+		int tmp_dma;
+		/*
+		 * sb_audio_init thinks dma8 is for playback and
+		 * dma16 is for record. Not now! So swap them.
+		 */
+		tmp_dma		= devc->dma16;
+		devc->dma16	= devc->dma8;
+		devc->dma8	= tmp_dma;
+
+		*audio_flags |= DMA_DUPLEX;
+	}
+
+	return &ess_audio_driver;
+}
+
+/****************************************************************************
+ *																			*
+ *								ESS common									*
+ *																			*
+ ****************************************************************************/
+static void ess_handle_channel
+	(char *channel, int dev, int intr_active, unsigned char flag, int irq_mode)
+{
+	if (!intr_active || !flag) return;
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
+#endif
+	switch (irq_mode) {
+		case IMODE_OUTPUT:
+			DMAbuf_outputintr (dev, 1);
+			break;
+
+		case IMODE_INPUT:
+			DMAbuf_inputintr (dev);
+			break;
+
+		case IMODE_INIT:
+			break;
+
+		default:;
+			/* printk(KERN_WARN "ESS: Unexpected interrupt\n"); */
+	}
+}
+
+/*
+ * FKS: TODO!!! Finish this!
+ *
+ * I think midi stuff uses uart401, without interrupts.
+ * So IMODE_MIDI isn't a value for devc->irq_mode.
+ */
+void ess_intr (sb_devc *devc)
+{
+	int				status;
+	unsigned char	src;
+
+	if (devc->submodel == SUBMDL_ES1887) {
+		src = ess_getmixer (devc, 0x7f) >> 4;
+	} else {
+		src = 0xff;
+	}
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src);
+#endif
+	ess_handle_channel
+		( "Audio 1"
+		, devc->dev, devc->intr_active   , src & 0x01, devc->irq_mode   );
+	ess_handle_channel
+		( "Audio 2"
+		, devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16);
+	/*
+	 * Acknowledge interrupts
+	 */
+	if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) {
+		ess_chgmixer (devc, 0x7a, 0x80, 0x00);
+	}
+
+	if (src & 0x01) {
+		status = inb(DSP_DATA_AVAIL);
+	}
+}
+
+static void ess_extended (sb_devc * devc)
+{
+	/* Enable extended mode */
+
+	sb_dsp_command(devc, 0xc6);
+}
+
+static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
+{
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data);
+#endif
+	/* Write a byte to an extended mode register of ES1688 */
+
+	if (!sb_dsp_command(devc, reg))
+		return 0;
+
+	return sb_dsp_command(devc, data);
+}
+
+static int ess_read (sb_devc * devc, unsigned char reg)
+{
+	/* Read a byte from an extended mode register of ES1688 */
+
+	/* Read register command */
+	if (!sb_dsp_command(devc, 0xc0)) return -1;
+
+	if (!sb_dsp_command(devc, reg )) return -1;
+
+	return sb_dsp_get_byte(devc);
+}
+
+int ess_dsp_reset(sb_devc * devc)
+{
+	int loopc;
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: ess_dsp_reset 1\n");
+ess_show_mixerregs (devc);
+#endif
+
+	DEB(printk("Entered ess_dsp_reset()\n"));
+
+	outb(3, DSP_RESET); /* Reset FIFO too */
+
+	udelay(10);
+	outb(0, DSP_RESET);
+	udelay(30);
+
+	for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
+
+	if (inb(DSP_READ) != 0xAA) {
+		DDB(printk("sb: No response to RESET\n"));
+		return 0;   /* Sorry */
+	}
+	ess_extended (devc);
+
+	DEB(printk("sb_dsp_reset() OK\n"));
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: dsp_reset 2\n");
+ess_show_mixerregs (devc);
+#endif
+
+	return 1;
+}
+
+static int ess_irq_bits (int irq)
+{
+	switch (irq) {
+	case 2:
+	case 9:
+		return 0;
+
+	case 5:
+		return 1;
+
+	case 7:
+		return 2;
+
+	case 10:
+		return 3;
+
+	default:
+		printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq);
+		return -1;
+	}
+}
+
+/*
+ *	Set IRQ configuration register for all ESS models
+ */
+static int ess_common_set_irq_hw (sb_devc * devc)
+{
+	int irq_bits;
+
+	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0;
+
+	if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) {
+		printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * I wanna use modern ES1887 mixer irq handling. Funny is the
+ * fact that my BIOS wants the same. But suppose someone's BIOS
+ * doesn't do this!
+ * This is independent of duplex. If there's a 1887 this will
+ * prevent it from going into 1888 mode.
+ */
+static void ess_es1887_set_irq_hw (sb_devc * devc)
+{
+	int irq_bits;
+
+	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return;
+
+	ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1));
+}
+
+static int ess_set_irq_hw (sb_devc * devc)
+{
+	if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc);
+
+	return ess_common_set_irq_hw (devc);
+}
+
+#ifdef FKS_TEST
+
+/*
+ * FKS_test:
+ *	for ES1887: 00, 18, non wr bits: 0001 1000
+ *	for ES1868: 00, b8, non wr bits: 1011 1000
+ *	for ES1888: 00, f8, non wr bits: 1111 1000
+ *	for ES1688: 00, f8, non wr bits: 1111 1000
+ *	+   ES968
+ */
+
+static void FKS_test (sb_devc * devc)
+{
+	int val1, val2;
+	val1 = ess_getmixer (devc, 0x64);
+	ess_setmixer (devc, 0x64, ~val1);
+	val2 = ess_getmixer (devc, 0x64) ^ ~val1;
+	ess_setmixer (devc, 0x64, val1);
+	val1 ^= ess_getmixer (devc, 0x64);
+printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff));
+};
+#endif
+
+static unsigned int ess_identify (sb_devc * devc)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
+
+	udelay(20);
+	val  = inb(MIXER_DATA) << 8;
+	udelay(20);
+	val |= inb(MIXER_DATA);
+	udelay(20);
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	return val;
+}
+
+/*
+ * ESS technology describes a detection scheme in their docs. It involves
+ * fiddling with the bits in certain mixer registers. ess_probe is supposed
+ * to help.
+ *
+ * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but
+ * should be written 0 only. Check this.
+ */
+static int ess_probe (sb_devc * devc, int reg, int xorval)
+{
+	int  val1, val2, val3;
+
+	val1 = ess_getmixer (devc, reg);
+	val2 = val1 ^ xorval;
+	ess_setmixer (devc, reg, val2);
+	val3 = ess_getmixer (devc, reg);
+	ess_setmixer (devc, reg, val1);
+
+	return (val2 == val3);
+}
+
+int ess_init(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char cfg;
+	int ess_major = 0, ess_minor = 0;
+	int i;
+	static char name[100], modelname[10];
+
+	/*
+	 * Try to detect ESS chips.
+	 */
+
+	sb_dsp_command(devc, 0xe7); /* Return identification */
+
+	for (i = 1000; i; i--) {
+		if (inb(DSP_DATA_AVAIL) & 0x80) {
+			if (ess_major == 0) {
+				ess_major = inb(DSP_READ);
+			} else {
+				ess_minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
+
+	if (ess_major == 0) return 0;
+
+	if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+		sprintf(name, "ESS ES488 AudioDrive (rev %d)",
+			ess_minor & 0x0f);
+		hw_config->name = name;
+		devc->model = MDL_SBPRO;
+		return 1;
+	}
+
+	/*
+	 * This the detection heuristic of ESS technology, though somewhat
+	 * changed to actually make it work.
+	 * This results in the following detection steps:
+	 * - distinct between ES688 and ES1688+ (as always done in this driver)
+	 *   if ES688 we're ready
+	 * - try to detect ES1868, ES1869 or ES1878 (ess_identify)
+	 *   if successful we're ready
+	 * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887)
+	 *   if successful we're ready
+	 * - Dunno. Must be 1688. Will do in general
+	 *
+	 * This is the most BETA part of the software: Will the detection
+	 * always work?
+	 */
+	devc->model = MDL_ESS;
+	devc->submodel = ess_minor & 0x0f;
+
+	if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+		char *chip = NULL;
+		int submodel = -1;
+
+		switch (devc->sbmo.esstype) {
+		case ESSTYPE_DETECT:
+		case ESSTYPE_LIKE20:
+			break;
+		case 688:
+			submodel = 0x00;
+			break;
+		case 1688:
+			submodel = 0x08;
+			break;
+		case 1868:
+			submodel = SUBMDL_ES1868;
+			break;
+		case 1869:
+			submodel = SUBMDL_ES1869;
+			break;
+		case 1788:
+			submodel = SUBMDL_ES1788;
+			break;
+		case 1878:
+			submodel = SUBMDL_ES1878;
+			break;
+		case 1879:
+			submodel = SUBMDL_ES1879;
+			break;
+		case 1887:
+			submodel = SUBMDL_ES1887;
+			break;
+		case 1888:
+			submodel = SUBMDL_ES1888;
+			break;
+		default:
+			printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype);
+			return 0;
+		};
+		if (submodel != -1) {
+			devc->submodel = submodel;
+			sprintf (modelname, "ES%d", devc->sbmo.esstype);
+			chip = modelname;
+		};
+		if (chip == NULL && (ess_minor & 0x0f) < 8) {
+			chip = "ES688";
+		};
+#ifdef FKS_TEST
+FKS_test (devc);
+#endif
+		/*
+		 * If Nothing detected yet, and we want 2.0 behaviour...
+		 * Then let's assume it's ES1688.
+		 */
+		if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) {
+			chip = "ES1688";
+		};
+
+		if (chip == NULL) {
+			int type;
+
+			type = ess_identify (devc);
+
+			switch (type) {
+			case 0x1868:
+				chip = "ES1868";
+				devc->submodel = SUBMDL_ES1868;
+				break;
+			case 0x1869:
+				chip = "ES1869";
+				devc->submodel = SUBMDL_ES1869;
+				break;
+			case 0x1878:
+				chip = "ES1878";
+				devc->submodel = SUBMDL_ES1878;
+				break;
+			case 0x1879:
+				chip = "ES1879";
+				devc->submodel = SUBMDL_ES1879;
+				break;
+			default:
+				if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) {
+					printk ("ess_init: Unrecognized %04x\n", type);
+				}
+			};
+		};
+#if 0
+		/*
+		 * this one failed:
+		 * the probing of bit 4 is another thought: from ES1788 and up, all
+		 * chips seem to have hardware volume control. Bit 4 is readonly to
+		 * check if a hardware volume interrupt has fired.
+		 * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable
+		 * for these chips.
+		 */
+		if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) {
+#endif
+		/*
+		 * the probing of bit 2 is my idea. The ES1887 docs want me to probe
+		 * bit 3. This results in ES1688 being detected as ES1788.
+		 * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have
+		 * HardWare Volume, I think they don't have this IRQE.
+		 */
+		if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) {
+			if (ess_probe (devc, 0x70, 0x7f)) {
+				if (ess_probe (devc, 0x64, (1 << 5))) {
+					chip = "ES1887";
+					devc->submodel = SUBMDL_ES1887;
+				} else {
+					chip = "ES1888";
+					devc->submodel = SUBMDL_ES1888;
+				}
+			} else {
+				chip = "ES1788";
+				devc->submodel = SUBMDL_ES1788;
+			}
+		};
+		if (chip == NULL) {
+			chip = "ES1688";
+		};
+
+	    printk ( KERN_INFO "ESS chip %s %s%s\n"
+               , chip
+               , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
+                 ? "detected"
+                 : "specified"
+                 )
+               , ( devc->sbmo.esstype == ESSTYPE_LIKE20
+                 ? " (kernel 2.0 compatible)"
+                 : ""
+                 )
+               );
+
+		sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
+	} else {
+		strcpy(name, "Jazz16");
+	}
+
+	/* AAS: info stolen from ALSA: these boards have different clocks */
+	switch(devc->submodel) {
+/* APPARENTLY NOT 1869 AND 1887
+		case SUBMDL_ES1869:
+		case SUBMDL_ES1887:
+*/		
+		case SUBMDL_ES1888:
+			devc->caps |= SB_CAP_ES18XX_RATE;
+			break;
+	}
+
+	hw_config->name = name;
+	/* FKS: sb_dsp_reset to enable extended mode???? */
+	sb_dsp_reset(devc); /* Turn on extended mode */
+
+	/*
+	 *  Enable joystick and OPL3
+	 */
+	cfg = ess_getmixer (devc, 0x40);
+	ess_setmixer (devc, 0x40, cfg | 0x03);
+	if (devc->submodel >= 8) {		/* ES1688 */
+		devc->caps |= SB_NO_MIDI;   /* ES1688 uses MPU401 MIDI mode */
+	}
+	sb_dsp_reset (devc);
+
+	/*
+	 * This is important! If it's not done, the IRQ probe in sb_dsp_init
+	 * may fail.
+	 */
+	return ess_set_irq_hw (devc);
+}
+
+static int ess_set_dma_hw(sb_devc * devc)
+{
+	unsigned char cfg, dma_bits = 0, dma16_bits;
+	int dma;
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n"
+, devc->dma8, devc->dma16, devc->duplex);
+#endif
+
+	/*
+	 * FKS: It seems as if this duplex flag isn't set yet. Check it.
+	 */
+	dma = devc->dma8;
+
+	if (dma > 3 || dma < 0 || dma == 2) {
+		dma_bits = 0;
+		printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma);
+		return 0;
+	} else {
+		/* Extended mode DMA enable */
+		cfg = 0x50;
+
+		if (dma == 3) {
+			dma_bits = 3;
+		} else {
+			dma_bits = dma + 1;
+		}
+	}
+
+	if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) {
+		printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
+		return 0;
+	}
+
+	if (devc->duplex) {
+		dma = devc->dma16;
+		dma16_bits = 0;
+
+		if (dma >= 0) {
+			switch (dma) {
+			case 0:
+				dma_bits = 0x04;
+				break;
+			case 1:
+				dma_bits = 0x05;
+				break;
+			case 3:
+				dma_bits = 0x06;
+				break;
+			case 5:
+				dma_bits   = 0x07;
+				dma16_bits = 0x20;
+				break;
+			default:
+				printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
+				return 0;
+			};
+			ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
+			ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
+		}
+	}
+	return 1;
+}
+
+/*
+ * This one is called from sb_dsp_init.
+ *
+ * Return values:
+ *  0: Failed
+ *  1: Succeeded or doesn't apply (not SUBMDL_ES1887)
+ */
+int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
+{
+	/*
+	 * Caller also checks this, but anyway
+	 */
+	if (devc->model != MDL_ESS) {
+		printk (KERN_INFO "ess_dsp_init for non ESS chip\n");
+		return 1;
+	}
+	/*
+	 * This for ES1887 to run Full Duplex. Actually ES1888
+	 * is allowed to do so too. I have no idea yet if this
+	 * will work for ES1888 however.
+	 *
+	 * For SB16 having both dma8 and dma16 means enable
+	 * Full Duplex. Let's try this for ES1887 too
+	 *
+	 */
+	if (devc->submodel == SUBMDL_ES1887) {
+		if (hw_config->dma2 != -1) {
+			devc->dma16 = hw_config->dma2;
+		}
+		/*
+		 * devc->duplex initialization is put here, cause
+		 * ess_set_dma_hw needs it.
+		 */
+		if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
+			devc->duplex = 1;
+		}
+	}
+	if (!ess_set_dma_hw (devc)) {
+		free_irq(devc->irq, devc);
+		return 0;
+	}
+	return 1;
+}
+
+/****************************************************************************
+ *																			*
+ *									ESS mixer								*
+ *																			*
+ ****************************************************************************/
+
+#define ES688_RECORDING_DEVICES	\
+			( SOUND_MASK_LINE	| SOUND_MASK_MIC	| SOUND_MASK_CD		)
+#define ES688_MIXER_DEVICES		\
+			( SOUND_MASK_SYNTH	| SOUND_MASK_PCM	| SOUND_MASK_LINE	\
+			| SOUND_MASK_MIC	| SOUND_MASK_CD		| SOUND_MASK_VOLUME	\
+			| SOUND_MASK_LINE2	| SOUND_MASK_SPEAKER					)
+
+#define ES1688_RECORDING_DEVICES	\
+			( ES688_RECORDING_DEVICES					)
+#define ES1688_MIXER_DEVICES		\
+			( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV	)
+
+#define ES1887_RECORDING_DEVICES	\
+			( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH)
+#define ES1887_MIXER_DEVICES		\
+			( ES1688_MIXER_DEVICES											)
+
+/*
+ * Mixer registers of ES1887
+ *
+ * These registers specifically take care of recording levels. To make the
+ * mapping from playback devices to recording devices every recording
+ * devices = playback device + ES_REC_MIXER_RECDIFF
+ */
+#define ES_REC_MIXER_RECBASE	(SOUND_MIXER_LINE3 + 1)
+#define ES_REC_MIXER_RECDIFF	(ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH)
+
+#define ES_REC_MIXER_RECSYNTH	(SOUND_MIXER_SYNTH	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECPCM		(SOUND_MIXER_PCM	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECSPEAKER	(SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE	(SOUND_MIXER_LINE	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECMIC		(SOUND_MIXER_MIC	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECCD		(SOUND_MIXER_CD		 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECIMIX	(SOUND_MIXER_IMIX	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECALTPCM	(SOUND_MIXER_ALTPCM	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECRECLEV	(SOUND_MIXER_RECLEV	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECIGAIN	(SOUND_MIXER_IGAIN	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECOGAIN	(SOUND_MIXER_OGAIN	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE1	(SOUND_MIXER_LINE1	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE2	(SOUND_MIXER_LINE2	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE3	(SOUND_MIXER_LINE3	 + ES_REC_MIXER_RECDIFF)
+
+static mixer_tab es688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * The ES1688 specifics... hopefully correct...
+ * - 6 bit master volume
+ *   I was wrong, ES1888 docs say ES1688 didn't have it.
+ * - RECLEV control
+ * These may apply to ES688 too. I have no idea.
+ */
+static mixer_tab es1688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+static mixer_tab es1688later_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * This one is for all ESS chips with a record mixer.
+ * It's not used (yet) however
+ */
+static mixer_tab es_rec_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSYNTH,		0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE,		0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECMIC,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECCD,			0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECIMIX,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECALTPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECRECLEV,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECIGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECOGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE1,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE2,		0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * This one is for ES1887. It's little different from es_rec_mix: it
+ * has 0x7c for PCM playback level. This is because ES1887 uses
+ * Audio 2 for playback.
+ */
+static mixer_tab es1887_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x7c, 7, 4, 0x7c, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSYNTH,		0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE,		0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECMIC,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECCD,			0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECIMIX,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECALTPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECRECLEV,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECIGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECOGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE1,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE2,		0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
+};
+
+static int ess_has_rec_mixer (int submodel)
+{
+	switch (submodel) {
+	case SUBMDL_ES1887:
+		return 1;
+	default:
+		return 0;
+	};
+};
+
+#ifdef FKS_LOGGING
+static int ess_mixer_mon_regs[]
+	= { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f
+	  , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9
+	  , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9
+	  , 0x00};
+
+static void ess_show_mixerregs (sb_devc *devc)
+{
+	int *mp = ess_mixer_mon_regs;
+
+return;
+
+	while (*mp != 0) {
+		printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp)));
+		mp++;
+	}
+}
+#endif
+
+void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
+{
+	unsigned long flags;
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value);
+#endif
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (port >= 0xa0) {
+		ess_write (devc, port, value);
+	} else {
+		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+		udelay(20);
+		outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+		udelay(20);
+	};
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+unsigned int ess_getmixer (sb_devc * devc, unsigned int port)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	if (port >= 0xa0) {
+		val = ess_read (devc, port);
+	} else {
+		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+		udelay(20);
+		val = inb(MIXER_DATA);
+		udelay(20);
+	}
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	return val;
+}
+
+static void ess_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = ess_getmixer (devc, reg);
+	value = (value & ~mask) | (val & mask);
+	ess_setmixer (devc, reg, value);
+}
+
+/*
+ * ess_mixer_init must be called from sb_mixer_init
+ */
+void ess_mixer_init (sb_devc * devc)
+{
+	devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+
+	/*
+	* Take care of ES1887 specifics...
+	*/
+	switch (devc->submodel) {
+	case SUBMDL_ES1887:
+		devc->supported_devices		= ES1887_MIXER_DEVICES;
+		devc->supported_rec_devices	= ES1887_RECORDING_DEVICES;
+#ifdef FKS_LOGGING
+printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex);
+#endif
+		if (devc->duplex) {
+			devc->iomap				= &es1887_mix;
+		} else {
+			devc->iomap				= &es_rec_mix;
+		}
+		break;
+	default:
+		if (devc->submodel < 8) {
+			devc->supported_devices		= ES688_MIXER_DEVICES;
+			devc->supported_rec_devices	= ES688_RECORDING_DEVICES;
+			devc->iomap					= &es688_mix;
+		} else {
+			/*
+			 * es1688 has 4 bits master vol.
+			 * later chips have 6 bits (?)
+			 */
+			devc->supported_devices		= ES1688_MIXER_DEVICES;
+			devc->supported_rec_devices	= ES1688_RECORDING_DEVICES;
+			if (devc->submodel < 0x10) {
+				devc->iomap				= &es1688_mix;
+			} else {
+				devc->iomap				= &es1688later_mix;
+			}
+		}
+	}
+}
+
+/*
+ * Changing playback levels at an ESS chip with record mixer means having to
+ * take care of recording levels of recorded inputs (devc->recmask) too!
+ */
+int ess_mixer_set(sb_devc *devc, int dev, int left, int right)
+{
+	if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) {
+		sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right);
+	}
+	return sb_common_mixer_set (devc, dev, left, right);
+}
+
+/*
+ * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
+ * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
+ * helps.
+ */
+void ess_mixer_reload (sb_devc *devc, int dev)
+{
+	int left, right, value;
+
+	value = devc->levels[dev];
+	left  = value & 0x000000ff;
+	right = (value & 0x0000ff00) >> 8;
+
+	sb_common_mixer_set(devc, dev, left, right);
+}
+
+int es_rec_set_recmask(sb_devc * devc, int mask)
+{
+	int i, i_mask, cur_mask, diff_mask;
+	int value, left, right;
+
+#ifdef FKS_LOGGING
+printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask);
+#endif
+	/*
+	 * Changing the recmask on an ESS chip with recording mixer means:
+	 * (1) Find the differences
+	 * (2) For "turned-on"  inputs: make the recording level the playback level
+	 * (3) For "turned-off" inputs: make the recording level zero
+	 */
+	cur_mask  = devc->recmask;
+	diff_mask = (cur_mask ^ mask);
+
+	for (i = 0; i < 32; i++) {
+		i_mask = (1 << i);
+		if (diff_mask & i_mask) {	/* Difference? (1)  */
+			if (mask & i_mask) {	/* Turn it on  (2)  */
+				value = devc->levels[i];
+				left  = value & 0x000000ff;
+				right = (value & 0x0000ff00) >> 8;
+			} else {				/* Turn it off (3)  */
+				left  = 0;
+				left  = 0;
+				right = 0;
+			}
+			sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
+		}
+	}
+	return mask;
+}
+
+int ess_set_recmask(sb_devc * devc, int *mask)
+{
+	/* This applies to ESS chips with record mixers only! */
+
+	if (ess_has_rec_mixer (devc->submodel)) {
+		*mask	= es_rec_set_recmask (devc, *mask);
+		return 1;									/* Applied		*/
+	} else {
+		return 0;									/* Not applied	*/
+	}
+}
+
+/*
+ * ess_mixer_reset must be called from sb_mixer_reset
+ */
+int ess_mixer_reset (sb_devc * devc)
+{
+	/*
+	 * Separate actions for ESS chips with a record mixer:
+	 */
+	if (ess_has_rec_mixer (devc->submodel)) {
+		switch (devc->submodel) {
+		case SUBMDL_ES1887:
+			/*
+			 * Separate actions for ES1887:
+			 * Change registers 7a and 1c to make the record mixer the
+			 * actual recording source.
+			 */
+			ess_chgmixer(devc, 0x7a, 0x18, 0x08);
+			ess_chgmixer(devc, 0x1c, 0x07, 0x07);
+			break;
+		};
+		/*
+		 * Call set_recmask for proper initialization
+		 */
+		devc->recmask = devc->supported_rec_devices;
+		es_rec_set_recmask(devc, 0);
+		devc->recmask = 0;
+
+		return 1;	/* We took care of recmask.				*/
+	} else {
+		return 0;	/* We didn't take care; caller do it	*/
+	}
+}
+
+/****************************************************************************
+ *																			*
+ *								ESS midi									*
+ *																			*
+ ****************************************************************************/
+
+/*
+ * FKS: IRQ may be shared. Hm. And if so? Then What?
+ */
+int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char   cfg, tmp;
+
+	cfg = ess_getmixer (devc, 0x40) & 0x03;
+
+	if (devc->submodel < 8) {
+		ess_setmixer (devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
+		return 0;  					 /* ES688 doesn't support MPU401 mode */
+	}
+	tmp = (hw_config->io_base & 0x0f0) >> 4;
+
+	if (tmp > 3) {
+		ess_setmixer (devc, 0x40, cfg);
+		return 0;
+	}
+	cfg |= tmp << 3;
+
+	tmp = 1;		/* MPU enabled without interrupts */
+
+	/* May be shared: if so the value is -ve */
+
+	switch (abs(hw_config->irq)) {
+		case 9:
+			tmp = 0x4;
+			break;
+		case 5:
+			tmp = 0x5;
+			break;
+		case 7:
+			tmp = 0x6;
+			break;
+		case 10:
+			tmp = 0x7;
+			break;
+		default:
+			return 0;
+	}
+
+	cfg |= tmp << 5;
+	ess_setmixer (devc, 0x40, cfg | 0x03);
+
+	return 1;
+}
+
diff -Nru linux/sound/oss/sb_ess.h linux-2.4.19-pre5-mjc/sound/oss/sb_ess.h
--- linux/sound/oss/sb_ess.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_ess.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,34 @@
+/*
+ * Created: 9-Jan-1999 Rolf Fokkens
+ */
+
+extern void ess_intr
+		(sb_devc *devc);
+extern int ess_dsp_init
+		(sb_devc *devc, struct address_info *hw_config);
+
+extern struct audio_driver *ess_audio_init
+		(sb_devc *devc, int *audio_flags, int *format_mask);
+extern int ess_midi_init
+		(sb_devc *devc, struct address_info *hw_config);
+extern void ess_mixer_init
+		(sb_devc *devc);
+
+extern int ess_init
+		(sb_devc *devc, struct address_info *hw_config);
+extern int ess_dsp_reset
+		(sb_devc *devc);
+
+extern void ess_setmixer
+		(sb_devc *devc, unsigned int port, unsigned int value);
+extern unsigned int ess_getmixer
+		(sb_devc *devc, unsigned int port);
+extern int ess_mixer_set
+		(sb_devc *devc, int dev, int left, int right);
+extern int ess_mixer_reset
+		(sb_devc *devc);
+extern void ess_mixer_reload
+		(sb_devc * devc, int dev);
+extern int ess_set_recmask
+		(sb_devc *devc, int *mask);
+
diff -Nru linux/sound/oss/sb_midi.c linux-2.4.19-pre5-mjc/sound/oss/sb_midi.c
--- linux/sound/oss/sb_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,205 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the Sound Blaster DS chips.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+
+#include "sb.h"
+#undef SB_TEST_IRQ
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+
+static int sb_midi_open(int dev, int mode,
+	     void            (*input) (int dev, unsigned char data),
+	     void            (*output) (int dev)
+)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return -ENXIO;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	if (devc->opened)
+	{
+		spin_unlock_irqrestore(&devc->lock, flags);
+		return -EBUSY;
+	}
+	devc->opened = 1;
+	spin_unlock_irqrestore(&devc->lock, flags);
+
+	devc->irq_mode = IMODE_MIDI;
+	devc->midi_broken = 0;
+
+	sb_dsp_reset(devc);
+
+	if (!sb_dsp_command(devc, 0x35))	/* Start MIDI UART mode */
+	{
+		  devc->opened = 0;
+		  return -EIO;
+	}
+	devc->intr_active = 1;
+
+	if (mode & OPEN_READ)
+	{
+		devc->input_opened = 1;
+		devc->midi_input_intr = input;
+	}
+	return 0;
+}
+
+static void sb_midi_close(int dev)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return;
+
+	spin_lock_irqsave(&devc->lock, flags);
+	sb_dsp_reset(devc);
+	devc->intr_active = 0;
+	devc->input_opened = 0;
+	devc->opened = 0;
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+static int sb_midi_out(int dev, unsigned char midi_byte)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+
+	if (devc == NULL)
+		return 1;
+
+	if (devc->midi_broken)
+		return 1;
+
+	if (!sb_dsp_command(devc, midi_byte))
+	{
+		devc->midi_broken = 1;
+		return 1;
+	}
+	return 1;
+}
+
+static int sb_midi_start_read(int dev)
+{
+	return 0;
+}
+
+static int sb_midi_end_read(int dev)
+{
+	sb_devc *devc = midi_devs[dev]->devc;
+
+	if (devc == NULL)
+		return -ENXIO;
+
+	sb_dsp_reset(devc);
+	devc->intr_active = 0;
+	return 0;
+}
+
+static int sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
+{
+        return -EINVAL;
+}
+
+void sb_midi_interrupt(sb_devc * devc)
+{
+	unsigned long   flags;
+	unsigned char   data;
+
+	if (devc == NULL)
+		return;
+
+	spin_lock_irqsave(&devc->lock, flags);
+
+	data = inb(DSP_READ);
+	if (devc->input_opened)
+		devc->midi_input_intr(devc->my_mididev, data);
+
+	spin_unlock_irqrestore(&devc->lock, flags);
+}
+
+#define MIDI_SYNTH_NAME	"Sound Blaster Midi"
+#define MIDI_SYNTH_CAPS	0
+#include "midi_synth.h"
+
+static struct midi_operations sb_midi_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"Sound Blaster", 0, 0, SNDCARD_SB},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		sb_midi_open,
+	close:		sb_midi_close,
+	ioctl:		sb_midi_ioctl,
+	outputc:	sb_midi_out,
+	start_read:	sb_midi_start_read,
+	end_read:	sb_midi_end_read,
+};
+
+void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
+{
+	int dev;
+
+	if (devc->model < 2)	/* No MIDI support for SB 1.x */
+		return;
+
+	dev = sound_alloc_mididev();
+
+	if (dev == -1)
+	{
+		printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
+		return;
+	}
+	std_midi_synth.midi_dev = devc->my_mididev = dev;
+	midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
+	if (midi_devs[dev] == NULL)
+	{
+		printk(KERN_WARNING "Sound Blaster:  failed to allocate MIDI memory.\n");
+		sound_unload_mididev(dev);
+		  return;
+	}
+	memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
+	       sizeof(struct midi_operations));
+
+	if (owner)
+			midi_devs[dev]->owner = owner;
+	
+	midi_devs[dev]->devc = devc;
+
+
+	midi_devs[dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+	if (midi_devs[dev]->converter == NULL)
+	{
+		  printk(KERN_WARNING "Sound Blaster:  failed to allocate MIDI memory.\n");
+		  kfree(midi_devs[dev]);
+		  sound_unload_mididev(dev);
+		  return;
+	}
+	memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth,
+	       sizeof(struct synth_operations));
+
+	midi_devs[dev]->converter->id = "SBMIDI";
+	sequencer_init();
+}
diff -Nru linux/sound/oss/sb_mixer.c linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.c
--- linux/sound/oss/sb_mixer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,757 @@
+/*
+ * sound/sb_mixer.c
+ *
+ * The low level mixer driver for the Sound Blaster compatible cards.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer				: ioctl code reworked (vmalloc/vfree removed)
+ * Rolf Fokkens (Dec 20 1998)	: Moved ESS stuff into sb_ess.[ch]
+ * Stanislav Voronyi <stas@esc.kharkov.com>	: Support for AWE 3DSE device (Jun 7 1999)
+ */
+
+#include "sound_config.h"
+
+#define __SB_MIXER_C__
+
+#include "sb.h"
+#include "sb_mixer.h"
+
+#include "sb_ess.h"
+
+#define SBPRO_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
+#define SBPRO_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES	(SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+				 SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
+#define SB16_RECORDING_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD)
+
+#define SB16_OUTFILTER_DEVICES		(SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD)
+
+#define SB16_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | \
+					 SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
+					 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
+					SOUND_MASK_IMIX)
+
+/* These are the only devices that are working at the moment.  Others could
+ * be added once they are identified and a method is found to control them.
+ */
+#define ALS007_MIXER_DEVICES	(SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
+				 SOUND_MASK_PCM | SOUND_MASK_MIC | \
+				 SOUND_MASK_CD | \
+				 SOUND_MASK_VOLUME)
+
+static mixer_tab sbpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+static mixer_tab sb16_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x30, 7, 5, 0x31, 7, 5),
+MIX_ENT(SOUND_MIXER_BASS,	0x46, 7, 4, 0x47, 7, 4),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 7, 4, 0x45, 7, 4),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x34, 7, 5, 0x35, 7, 5),
+MIX_ENT(SOUND_MIXER_PCM,	0x32, 7, 5, 0x33, 7, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x3b, 7, 2, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x38, 7, 5, 0x39, 7, 5),
+MIX_ENT(SOUND_MIXER_MIC,	0x3a, 7, 5, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x36, 7, 5, 0x37, 7, 5),
+MIX_ENT(SOUND_MIXER_IMIX,	0x3c, 0, 1, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN,	0x3f, 7, 2, 0x40, 7, 2),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x41, 7, 2, 0x42, 7, 2)
+};
+
+static mixer_tab als007_mix = 
+{
+MIX_ENT(SOUND_MIXER_VOLUME,	0x62, 7, 4, 0x62, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x66, 7, 4, 0x66, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x64, 7, 4, 0x64, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x6a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+
+/* SM_GAMES          Master volume is lower and PCM & FM volumes
+			     higher than with SB Pro. This improves the
+			     sound quality */
+
+static int smg_default_levels[32] =
+{
+  0x2020,			/* Master Volume */
+  0x4b4b,			/* Bass */
+  0x4b4b,			/* Treble */
+  0x6464,			/* FM */
+  0x6464,			/* PCM */
+  0x4b4b,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x0000,			/* Mic */
+  0x4b4b,			/* CD */
+  0x4b4b,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Recording level */
+  0x4b4b,			/* Input gain */
+  0x4b4b,			/* Output gain */
+  0x4040,			/* Line1 */
+  0x4040,			/* Line2 */
+  0x1515			/* Line3 */
+};
+
+static int sb_default_levels[32] =
+{
+  0x5a5a,			/* Master Volume */
+  0x4b4b,			/* Bass */
+  0x4b4b,			/* Treble */
+  0x4b4b,			/* FM */
+  0x4b4b,			/* PCM */
+  0x4b4b,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x1010,			/* Mic */
+  0x4b4b,			/* CD */
+  0x0000,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Recording level */
+  0x4b4b,			/* Input gain */
+  0x4b4b,			/* Output gain */
+  0x4040,			/* Line1 */
+  0x4040,			/* Line2 */
+  0x1515			/* Line3 */
+};
+
+static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
+{
+	0x00,	/* SOUND_MIXER_VOLUME	*/
+	0x00,	/* SOUND_MIXER_BASS	*/
+	0x00,	/* SOUND_MIXER_TREBLE	*/
+	0x40,	/* SOUND_MIXER_SYNTH	*/
+	0x00,	/* SOUND_MIXER_PCM	*/
+	0x00,	/* SOUND_MIXER_SPEAKER	*/
+	0x10,	/* SOUND_MIXER_LINE	*/
+	0x01,	/* SOUND_MIXER_MIC	*/
+	0x04,	/* SOUND_MIXER_CD	*/
+	0x00,	/* SOUND_MIXER_IMIX	*/
+	0x00,	/* SOUND_MIXER_ALTPCM	*/
+	0x00,	/* SOUND_MIXER_RECLEV	*/
+	0x00,	/* SOUND_MIXER_IGAIN	*/
+	0x00	/* SOUND_MIXER_OGAIN	*/
+};
+
+static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+	0x00,	/* SOUND_MIXER_VOLUME	*/
+	0x00,	/* SOUND_MIXER_BASS	*/
+	0x00,	/* SOUND_MIXER_TREBLE	*/
+	0x20,	/* SOUND_MIXER_SYNTH	*/
+	0x00,	/* SOUND_MIXER_PCM	*/
+	0x00,	/* SOUND_MIXER_SPEAKER	*/
+	0x08,	/* SOUND_MIXER_LINE	*/
+	0x01,	/* SOUND_MIXER_MIC	*/
+	0x02,	/* SOUND_MIXER_CD	*/
+	0x00,	/* SOUND_MIXER_IMIX	*/
+	0x00,	/* SOUND_MIXER_ALTPCM	*/
+	0x00,	/* SOUND_MIXER_RECLEV	*/
+	0x00,	/* SOUND_MIXER_IGAIN	*/
+	0x00	/* SOUND_MIXER_OGAIN	*/
+};
+
+static char     smw_mix_regs[] =	/* Left mixer registers */
+{
+  0x0b,				/* SOUND_MIXER_VOLUME */
+  0x0d,				/* SOUND_MIXER_BASS */
+  0x0d,				/* SOUND_MIXER_TREBLE */
+  0x05,				/* SOUND_MIXER_SYNTH */
+  0x09,				/* SOUND_MIXER_PCM */
+  0x00,				/* SOUND_MIXER_SPEAKER */
+  0x03,				/* SOUND_MIXER_LINE */
+  0x01,				/* SOUND_MIXER_MIC */
+  0x07,				/* SOUND_MIXER_CD */
+  0x00,				/* SOUND_MIXER_IMIX */
+  0x00,				/* SOUND_MIXER_ALTPCM */
+  0x00,				/* SOUND_MIXER_RECLEV */
+  0x00,				/* SOUND_MIXER_IGAIN */
+  0x00,				/* SOUND_MIXER_OGAIN */
+  0x00,				/* SOUND_MIXER_LINE1 */
+  0x00,				/* SOUND_MIXER_LINE2 */
+  0x00				/* SOUND_MIXER_LINE3 */
+};
+
+static int      sbmixnum = 1;
+
+static void     sb_mixer_reset(sb_devc * devc);
+
+void sb_mixer_set_stereo(sb_devc * devc, int mode)
+{
+	sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
+}
+
+static int detect_mixer(sb_devc * devc)
+{
+	/* Just trust the mixer is there */
+	return 1;
+}
+
+static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
+{
+	unsigned char mask;
+	int shift;
+
+	mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
+	newval = (int) ((newval * mask) + 50) / 100;	/* Scale */
+
+	shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
+
+	*regval &= ~(mask << shift);	/* Mask out previous value */
+	*regval |= (newval & mask) << shift;	/* Set the new value */
+}
+
+static int sb_mixer_get(sb_devc * devc, int dev)
+{
+	if (!((1 << dev) & devc->supported_devices))
+		return -EINVAL;
+	return devc->levels[dev];
+}
+
+void smw_mixer_init(sb_devc * devc)
+{
+	int i;
+
+	sb_setmixer(devc, 0x00, 0x18);	/* Mute unused (Telephone) line */
+	sb_setmixer(devc, 0x10, 0x38);	/* Config register 2 */
+
+	devc->supported_devices = 0;
+	for (i = 0; i < sizeof(smw_mix_regs); i++)
+		if (smw_mix_regs[i] != 0)
+			devc->supported_devices |= (1 << i);
+
+	devc->supported_rec_devices = devc->supported_devices &
+		~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME);
+	sb_mixer_reset(devc);
+}
+
+int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
+{
+	int regoffs;
+	unsigned char val;
+
+	regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
+
+	if (regoffs == 0)
+		return -EINVAL;
+
+	val = sb_getmixer(devc, regoffs);
+	change_bits(devc, &val, dev, LEFT_CHN, left);
+
+	if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)	/*
+								 * Change register
+								 */
+	{
+		sb_setmixer(devc, regoffs, val);	/*
+							 * Save the old one
+							 */
+		regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
+
+		if (regoffs == 0)
+			return left | (left << 8);	/*
+							 * Just left channel present
+							 */
+
+		val = sb_getmixer(devc, regoffs);	/*
+							 * Read the new one
+							 */
+	}
+	change_bits(devc, &val, dev, RIGHT_CHN, right);
+
+	sb_setmixer(devc, regoffs, val);
+
+	return left | (right << 8);
+}
+
+static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
+{
+	int reg, val;
+
+	switch (dev)
+	{
+		case SOUND_MIXER_VOLUME:
+			sb_setmixer(devc, 0x0b, 96 - (96 * left / 100));	/* 96=mute, 0=max */
+			sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
+			break;
+
+		case SOUND_MIXER_BASS:
+		case SOUND_MIXER_TREBLE:
+			devc->levels[dev] = left | (right << 8);
+			/* Set left bass and treble values */
+			val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
+			val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
+			sb_setmixer(devc, 0x0d, val);
+
+			/* Set right bass and treble values */
+			val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
+			val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
+			sb_setmixer(devc, 0x0e, val);
+		
+			break;
+
+		default:
+			reg = smw_mix_regs[dev];
+			if (reg == 0)
+				return -EINVAL;
+			sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20);	/* 24=mute, 0=max */
+			sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
+	}
+
+	devc->levels[dev] = left | (right << 8);
+	return left | (right << 8);
+}
+
+static int sb_mixer_set(sb_devc * devc, int dev, int value)
+{
+	int left = value & 0x000000ff;
+	int right = (value & 0x0000ff00) >> 8;
+	int retval;
+
+	if (left > 100)
+		left = 100;
+	if (right > 100)
+		right = 100;
+
+	if (dev > 31)
+		return -EINVAL;
+
+	if (!(devc->supported_devices & (1 << dev)))	/*
+							 * Not supported
+							 */
+		return -EINVAL;
+
+	/* Differentiate depending on the chipsets */
+	switch (devc->model) {
+	case MDL_SMW:
+		retval = smw_mixer_set(devc, dev, left, right);
+		break;
+	case MDL_ESS:
+		retval = ess_mixer_set(devc, dev, left, right);
+		break;
+	default:
+		retval = sb_common_mixer_set(devc, dev, left, right);
+	}
+	if (retval >= 0) devc->levels[dev] = retval;
+
+	return retval;
+}
+
+/*
+ * set_recsrc doesn't apply to ES188x
+ */
+static void set_recsrc(sb_devc * devc, int src)
+{
+	sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
+}
+
+static int set_recmask(sb_devc * devc, int mask)
+{
+	int devmask, i;
+	unsigned char  regimageL, regimageR;
+
+	devmask = mask & devc->supported_rec_devices;
+
+	switch (devc->model)
+	{
+		case MDL_SBPRO:
+		case MDL_ESS:
+		case MDL_JAZZ:
+		case MDL_SMW:
+			if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
+				break;
+			};
+			if (devmask != SOUND_MASK_MIC &&
+				devmask != SOUND_MASK_LINE &&
+				devmask != SOUND_MASK_CD)
+			{
+				/*
+				 * More than one device selected. Drop the
+				 * previous selection
+				 */
+				devmask &= ~devc->recmask;
+			}
+			if (devmask != SOUND_MASK_MIC &&
+				devmask != SOUND_MASK_LINE &&
+				devmask != SOUND_MASK_CD)
+			{
+				/*
+				 * More than one device selected. Default to
+				 * mic
+				 */
+				devmask = SOUND_MASK_MIC;
+			}
+			if (devmask ^ devc->recmask)	/*
+							 *	Input source changed
+							 */
+			{
+				switch (devmask)
+				{
+					case SOUND_MASK_MIC:
+						set_recsrc(devc, SRC__MIC);
+						break;
+
+					case SOUND_MASK_LINE:
+						set_recsrc(devc, SRC__LINE);
+						break;
+
+					case SOUND_MASK_CD:
+						set_recsrc(devc, SRC__CD);
+						break;
+
+					default:
+						set_recsrc(devc, SRC__MIC);
+				}
+			}
+			break;
+
+		case MDL_SB16:
+			if (!devmask)
+				devmask = SOUND_MASK_MIC;
+
+			if (devc->submodel == SUBMDL_ALS007) 
+			{
+				switch (devmask) 
+				{
+					case SOUND_MASK_LINE:
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE);
+						break;
+					case SOUND_MASK_CD:
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD);
+						break;
+					case SOUND_MASK_SYNTH:
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH);
+						break;
+					default:           /* Also takes care of SOUND_MASK_MIC case */
+						sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC);
+						break;
+				}
+			}
+			else
+			{
+				regimageL = regimageR = 0;
+				for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				{
+					if ((1 << i) & devmask)
+					{
+						regimageL |= sb16_recmasks_L[i];
+						regimageR |= sb16_recmasks_R[i];
+					}
+					sb_setmixer (devc, SB16_IMASK_L, regimageL);
+					sb_setmixer (devc, SB16_IMASK_R, regimageR);
+				}
+			}
+			break;
+	}
+	devc->recmask = devmask;
+	return devc->recmask;
+}
+
+static int set_outmask(sb_devc * devc, int mask)
+{
+	int devmask, i;
+	unsigned char  regimage;
+
+	devmask = mask & devc->supported_out_devices;
+
+	switch (devc->model)
+	{
+		case MDL_SB16:
+			if (devc->submodel == SUBMDL_ALS007) 
+				break;
+			else
+			{
+				regimage = 0;
+				for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				{
+					if ((1 << i) & devmask)
+					{
+						regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]);
+					}
+					sb_setmixer (devc, SB16_OMASK, regimage);
+				}
+			}
+			break;
+		default:
+			break;
+	}
+
+	devc->outmask = devmask;
+	return devc->outmask;
+}
+
+static int sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	sb_devc *devc = mixer_devs[dev]->devc;
+	int val, ret;
+
+	/*
+	 * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1).
+	 * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1)
+	 *					      or mode==2 put 3DSE state to mode.
+	 */
+	if (devc->model == MDL_SB16) {
+		if (cmd == SOUND_MIXER_AGC) 
+		{
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			sb_setmixer(devc, 0x43, (~val) & 0x01);
+			return 0;
+		}
+		if (cmd == SOUND_MIXER_3DSE) 
+		{
+			/* I put here 15, but I don't know the exact version.
+			   At least my 4.13 havn't 3DSE, 4.16 has it. */
+			if (devc->minor < 15)
+				return -EINVAL;
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val == 0 || val == 1)
+				sb_chgmixer(devc, AWE_3DSE, 0x01, val);
+			else if (val == 2)
+			{
+				ret = sb_getmixer(devc, AWE_3DSE)&0x01;
+				return put_user(ret, (int *)arg);
+			}
+			else
+				return -EINVAL;
+			return 0;
+		}
+	}
+	if (((cmd >> 8) & 0xff) == 'M') 
+	{
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) 
+		{
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			switch (cmd & 0xff) 
+			{
+				case SOUND_MIXER_RECSRC:
+					ret = set_recmask(devc, val);
+					break;
+
+				case SOUND_MIXER_OUTSRC:
+					ret = set_outmask(devc, val);
+					break;
+
+				default:
+					ret = sb_mixer_set(devc, cmd & 0xff, val);
+			}
+		}
+		else switch (cmd & 0xff) 
+		{
+			case SOUND_MIXER_RECSRC:
+				ret = devc->recmask;
+				break;
+				  
+			case SOUND_MIXER_OUTSRC:
+				ret = devc->outmask;
+				break;
+				  
+			case SOUND_MIXER_DEVMASK:
+				ret = devc->supported_devices;
+				break;
+				  
+			case SOUND_MIXER_STEREODEVS:
+				ret = devc->supported_devices;
+				/* The ESS seems to have stereo mic controls */
+				if (devc->model == MDL_ESS)
+					ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
+				else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
+					ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+				break;
+				  
+			case SOUND_MIXER_RECMASK:
+				ret = devc->supported_rec_devices;
+				break;
+				  
+			case SOUND_MIXER_OUTMASK:
+				ret = devc->supported_out_devices;
+				break;
+				  
+			case SOUND_MIXER_CAPS:
+				ret = devc->mixer_caps;
+				break;
+				    
+			default:
+				ret = sb_mixer_get(devc, cmd & 0xff);
+				break;
+		}
+		return put_user(ret, (int *)arg); 
+	} else
+		return -EINVAL;
+}
+
+static struct mixer_operations sb_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"SB",
+	name:	"Sound Blaster",
+	ioctl:	sb_mixer_ioctl
+};
+
+static struct mixer_operations als007_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"ALS007",
+	name:	"Avance ALS-007",
+	ioctl:	sb_mixer_ioctl
+};
+
+static void sb_mixer_reset(sb_devc * devc)
+{
+	char name[32];
+	int i;
+
+	sprintf(name, "SB_%d", devc->sbmixnum);
+
+	if (devc->sbmo.sm_games)
+		devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
+	else
+		devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		sb_mixer_set(devc, i, devc->levels[i]);
+
+	if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
+		set_recmask(devc, SOUND_MASK_MIC);
+	};
+}
+
+int sb_mixer_init(sb_devc * devc, struct module *owner)
+{
+	int mixer_type = 0;
+	int m;
+
+	devc->sbmixnum = sbmixnum++;
+	devc->levels = NULL;
+
+	sb_setmixer(devc, 0x00, 0);	/* Reset mixer */
+
+	if (!(mixer_type = detect_mixer(devc)))
+		return 0;	/* No mixer. Why? */
+
+	switch (devc->model)
+	{
+		case MDL_ESSPCI:
+		case MDL_YMPCI:
+		case MDL_SBPRO:
+		case MDL_AZTECH:
+		case MDL_JAZZ:
+			devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+			devc->supported_devices = SBPRO_MIXER_DEVICES;
+			devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
+			devc->iomap = &sbpro_mix;
+			break;
+
+		case MDL_ESS:
+			ess_mixer_init (devc);
+			break;
+
+		case MDL_SMW:
+			devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+			devc->supported_devices = 0;
+			devc->supported_rec_devices = 0;
+			devc->iomap = &sbpro_mix;
+			smw_mixer_init(devc);
+			break;
+
+		case MDL_SB16:
+			devc->mixer_caps = 0;
+			devc->supported_rec_devices = SB16_RECORDING_DEVICES;
+			devc->supported_out_devices = SB16_OUTFILTER_DEVICES;
+			if (devc->submodel != SUBMDL_ALS007)
+			{
+				devc->supported_devices = SB16_MIXER_DEVICES;
+				devc->iomap = &sb16_mix;
+			}
+			else
+			{
+				devc->supported_devices = ALS007_MIXER_DEVICES;
+				devc->iomap = &als007_mix;
+			}
+			break;
+
+		default:
+			printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model);
+			return 0;
+	}
+
+	m = sound_alloc_mixerdev();
+	if (m == -1)
+		return 0;
+
+	mixer_devs[m] = (struct mixer_operations *)kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
+	if (mixer_devs[m] == NULL)
+	{
+		printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
+		sound_unload_mixerdev(m);
+		return 0;
+	}
+
+	if (devc->submodel != SUBMDL_ALS007)
+		memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations));
+	else
+		memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations));
+
+	mixer_devs[m]->devc = devc;
+
+	if (owner)
+			 mixer_devs[m]->owner = owner;
+	
+	devc->my_mixerdev = m;
+	sb_mixer_reset(devc);
+	return 1;
+}
+
+void sb_mixer_unload(sb_devc *devc)
+{
+	if (devc->my_mixerdev == -1)
+		return;
+
+	kfree(mixer_devs[devc->my_mixerdev]);
+	sound_unload_mixerdev(devc->my_mixerdev);
+	sbmixnum--;
+}
diff -Nru linux/sound/oss/sb_mixer.h linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.h
--- linux/sound/oss/sb_mixer.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,105 @@
+/*
+ * sound/sb_mixer.h
+ * 
+ * Definitions for the SB Pro and SB16 mixers
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+/*
+ * Modified:
+ *	Hunyue Yau	Jan 6 1994
+ *	Added defines for the Sound Galaxy NX Pro mixer.
+ *
+ *	Rolf Fokkens	Dec 20 1998
+ *	Added defines for some ES188x chips.
+ *
+ *	Rolf Fokkens	Dec 27 1998
+ *	Moved static stuff to sb_mixer.c
+ *
+ */
+/*
+ * Mixer registers
+ * 
+ * NOTE!	RECORD_SRC == IN_FILTER
+ */
+
+/* 
+ * Mixer registers of SB Pro
+ */
+#define VOC_VOL		0x04
+#define MIC_VOL		0x0A
+#define MIC_MIX		0x0A
+#define RECORD_SRC	0x0C
+#define IN_FILTER	0x0C
+#define OUT_FILTER	0x0E
+#define MASTER_VOL	0x22
+#define FM_VOL		0x26
+#define CD_VOL		0x28
+#define LINE_VOL	0x2E
+#define IRQ_NR		0x80
+#define DMA_NR		0x81
+#define IRQ_STAT	0x82
+#define OPSW		0x3c
+
+/*
+ * Additional registers on the SG NX Pro 
+ */
+#define COVOX_VOL	0x42
+#define TREBLE_LVL	0x44
+#define BASS_LVL	0x46
+
+#define FREQ_HI         (1 << 3)/* Use High-frequency ANFI filters */
+#define FREQ_LOW        0	/* Use Low-frequency ANFI filters */
+#define FILT_ON         0	/* Yes, 0 to turn it on, 1 for off */
+#define FILT_OFF        (1 << 5)
+
+#define MONO_DAC	0x00
+#define STEREO_DAC	0x02
+
+/*
+ * Mixer registers of SB16
+ */
+#define SB16_OMASK	0x3c
+#define SB16_IMASK_L	0x3d
+#define SB16_IMASK_R	0x3e
+
+#define LEFT_CHN	0
+#define RIGHT_CHN	1
+
+/*
+ * 3DSE register of AWE32/64
+ */
+#define AWE_3DSE	0x90
+
+/*
+ * Mixer registers of ALS007
+ */
+#define ALS007_RECORD_SRC	0x6c
+#define ALS007_OUTPUT_CTRL1	0x3c
+#define ALS007_OUTPUT_CTRL2	0x4c
+
+#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r)	\
+	{{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
+
+/*
+ *	Recording sources (SB Pro)
+ */
+
+#define SRC__MIC         1	/* Select Microphone recording source */
+#define SRC__CD          3	/* Select CD recording source */
+#define SRC__LINE        7	/* Use Line-in for recording source */
+
+/*
+ *	Recording sources for ALS-007
+ */
+
+#define ALS007_MIC	4
+#define ALS007_LINE	6
+#define ALS007_CD	2
+#define ALS007_SYNTH	7
diff -Nru linux/sound/oss/sequencer.c linux-2.4.19-pre5-mjc/sound/oss/sequencer.c
--- linux/sound/oss/sequencer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sequencer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1699 @@
+/*
+ * sound/sequencer.c
+ *
+ * The sequencer personality manager.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox	   : reformatted and fixed a pair of null pointer bugs
+ */
+#include <linux/kmod.h>
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#include "midi_ctrl.h"
+
+static int      sequencer_ok = 0;
+static struct sound_timer_operations *tmr;
+static int      tmr_no = -1;	/* Currently selected timer */
+static int      pending_timer = -1;	/* For timer change operation */
+extern unsigned long seq_time;
+
+static int      obsolete_api_used = 0;
+
+/*
+ * Local counts for number of synth and MIDI devices. These are initialized
+ * by the sequencer_open.
+ */
+static int      max_mididev = 0;
+static int      max_synthdev = 0;
+
+/*
+ * The seq_mode gives the operating mode of the sequencer:
+ *      1 = level1 (the default)
+ *      2 = level2 (extended capabilities)
+ */
+
+#define SEQ_1	1
+#define SEQ_2	2
+static int      seq_mode = SEQ_1;
+
+static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper);
+static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper);
+
+static int      midi_opened[MAX_MIDI_DEV] = {
+	0
+};
+
+static int      midi_written[MAX_MIDI_DEV] = {
+	0
+};
+
+static unsigned long prev_input_time = 0;
+static int      prev_event_time;
+
+#include "tuning.h"
+
+#define EV_SZ	8
+#define IEV_SZ	8
+
+static unsigned char *queue = NULL;
+static unsigned char *iqueue = NULL;
+
+static volatile int qhead = 0, qtail = 0, qlen = 0;
+static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
+static volatile int seq_playing = 0;
+static volatile int sequencer_busy = 0;
+static int      output_threshold;
+static long     pre_event_timeout;
+static unsigned synth_open_mask;
+
+static int      seq_queue(unsigned char *note, char nonblock);
+static void     seq_startplay(void);
+static int      seq_sync(void);
+static void     seq_reset(void);
+
+#if MAX_SYNTH_DEV > 15
+#error Too many synthesizer devices enabled.
+#endif
+
+int sequencer_read(int dev, struct file *file, char *buf, int count)
+{
+	int c = count, p = 0;
+	int ev_len;
+	unsigned long flags;
+
+	dev = dev >> 4;
+
+	ev_len = seq_mode == SEQ_1 ? 4 : 8;
+
+	save_flags(flags);
+	cli();
+
+	if (!iqlen)
+	{
+ 		if (file->f_flags & O_NONBLOCK) {
+  			restore_flags(flags);
+  			return -EAGAIN;
+  		}
+
+ 		interruptible_sleep_on_timeout(&midi_sleeper,
+					       pre_event_timeout);
+		if (!iqlen)
+		{
+			restore_flags(flags);
+			return 0;
+		}
+	}
+	while (iqlen && c >= ev_len)
+	{
+		char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
+		copy_to_user(&(buf)[p], fixit, ev_len);
+		p += ev_len;
+		c -= ev_len;
+
+		iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+		iqlen--;
+	}
+	restore_flags(flags);
+	return count - c;
+}
+
+static void sequencer_midi_output(int dev)
+{
+	/*
+	 * Currently NOP
+	 */
+}
+
+void seq_copy_to_input(unsigned char *event_rec, int len)
+{
+	unsigned long flags;
+
+	/*
+	 * Verify that the len is valid for the current mode.
+	 */
+
+	if (len != 4 && len != 8)
+		return;
+	if ((seq_mode == SEQ_1) != (len == 4))
+		return;
+
+	if (iqlen >= (SEQ_MAX_QUEUE - 1))
+		return;		/* Overflow */
+
+	save_flags(flags);
+	cli();
+	memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len);
+	iqlen++;
+	iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+	wake_up(&midi_sleeper);
+	restore_flags(flags);
+}
+
+static void sequencer_midi_input(int dev, unsigned char data)
+{
+	unsigned int tstamp;
+	unsigned char event_rec[4];
+
+	if (data == 0xfe)	/* Ignore active sensing */
+		return;
+
+	tstamp = jiffies - seq_time;
+
+	if (tstamp != prev_input_time)
+	{
+		tstamp = (tstamp << 8) | SEQ_WAIT;
+		seq_copy_to_input((unsigned char *) &tstamp, 4);
+		prev_input_time = tstamp;
+	}
+	event_rec[0] = SEQ_MIDIPUTC;
+	event_rec[1] = data;
+	event_rec[2] = dev;
+	event_rec[3] = 0;
+
+	seq_copy_to_input(event_rec, 4);
+}
+
+void seq_input_event(unsigned char *event_rec, int len)
+{
+	unsigned long this_time;
+
+	if (seq_mode == SEQ_2)
+		this_time = tmr->get_time(tmr_no);
+	else
+		this_time = jiffies - seq_time;
+
+	if (this_time != prev_input_time)
+	{
+		unsigned char   tmp_event[8];
+
+		tmp_event[0] = EV_TIMING;
+		tmp_event[1] = TMR_WAIT_ABS;
+		tmp_event[2] = 0;
+		tmp_event[3] = 0;
+		*(unsigned int *) &tmp_event[4] = this_time;
+
+		seq_copy_to_input(tmp_event, 8);
+		prev_input_time = this_time;
+	}
+	seq_copy_to_input(event_rec, len);
+}
+
+int sequencer_write(int dev, struct file *file, const char *buf, int count)
+{
+	unsigned char event_rec[EV_SZ], ev_code;
+	int p = 0, c, ev_size;
+	int err;
+	int mode = translate_mode(file);
+
+	dev = dev >> 4;
+
+	DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count));
+
+	if (mode == OPEN_READ)
+		return -EIO;
+
+	c = count;
+
+	while (c >= 4)
+	{
+		copy_from_user((char *) event_rec, &(buf)[p], 4);
+		ev_code = event_rec[0];
+
+		if (ev_code == SEQ_FULLSIZE)
+		{
+			int err, fmt;
+
+			dev = *(unsigned short *) &event_rec[2];
+			if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
+				return -ENXIO;
+
+			if (!(synth_open_mask & (1 << dev)))
+				return -ENXIO;
+
+			fmt = (*(short *) &event_rec[0]) & 0xffff;
+			err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0);
+			if (err < 0)
+				return err;
+
+			return err;
+		}
+		if (ev_code >= 128)
+		{
+			if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
+			{
+				printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code);
+				return -EINVAL;
+			}
+			ev_size = 8;
+
+			if (c < ev_size)
+			{
+				if (!seq_playing)
+					seq_startplay();
+				return count - c;
+			}
+			copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4);
+
+		}
+		else
+		{
+			if (seq_mode == SEQ_2)
+			{
+				printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n");
+				return -EINVAL;
+			}
+			ev_size = 4;
+
+			if (event_rec[0] != SEQ_MIDIPUTC)
+				obsolete_api_used = 1;
+		}
+
+		if (event_rec[0] == SEQ_MIDIPUTC)
+		{
+			if (!midi_opened[event_rec[2]])
+			{
+				int mode;
+				int dev = event_rec[2];
+
+				if (dev >= max_mididev || midi_devs[dev]==NULL)
+				{
+					/*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/
+					return -ENXIO;
+				}
+				mode = translate_mode(file);
+
+				if ((err = midi_devs[dev]->open(dev, mode,
+								sequencer_midi_input, sequencer_midi_output)) < 0)
+				{
+					seq_reset();
+					printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev);
+					return err;
+				}
+				midi_opened[dev] = 1;
+			}
+		}
+		if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0)))
+		{
+			int processed = count - c;
+
+			if (!seq_playing)
+				seq_startplay();
+
+			if (!processed && (file->f_flags & O_NONBLOCK))
+				return -EAGAIN;
+			else
+				return processed;
+		}
+		p += ev_size;
+		c -= ev_size;
+	}
+
+	if (!seq_playing)
+		seq_startplay();
+
+	return count;
+}
+
+static int seq_queue(unsigned char *note, char nonblock)
+{
+
+	/*
+	 * Test if there is space in the queue
+	 */
+
+	if (qlen >= SEQ_MAX_QUEUE)
+		if (!seq_playing)
+			seq_startplay();	/*
+						 * Give chance to drain the queue
+						 */
+
+	if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) {
+		/*
+		 * Sleep until there is enough space on the queue
+		 */
+		interruptible_sleep_on(&seq_sleeper);
+	}
+	if (qlen >= SEQ_MAX_QUEUE)
+	{
+		return 0;	/*
+				 * To be sure
+				 */
+	}
+	memcpy(&queue[qtail * EV_SZ], note, EV_SZ);
+
+	qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+	qlen++;
+
+	return 1;
+}
+
+static int extended_event(unsigned char *q)
+{
+	int dev = q[2];
+
+	if (dev < 0 || dev >= max_synthdev)
+		return -ENXIO;
+
+	if (!(synth_open_mask & (1 << dev)))
+		return -ENXIO;
+
+	switch (q[1])
+	{
+		case SEQ_NOTEOFF:
+			synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+			break;
+
+		case SEQ_NOTEON:
+			if (q[4] > 127 && q[4] != 255)
+				return 0;
+
+			if (q[5] == 0)
+			{
+				synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+				break;
+			}
+			synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
+			break;
+
+		case SEQ_PGMCHANGE:
+			synth_devs[dev]->set_instr(dev, q[3], q[4]);
+			break;
+
+		case SEQ_AFTERTOUCH:
+			synth_devs[dev]->aftertouch(dev, q[3], q[4]);
+			break;
+
+		case SEQ_BALANCE:
+			synth_devs[dev]->panning(dev, q[3], (char) q[4]);
+			break;
+
+		case SEQ_CONTROLLER:
+			synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
+			break;
+
+		case SEQ_VOLMODE:
+			if (synth_devs[dev]->volume_method != NULL)
+				synth_devs[dev]->volume_method(dev, q[3]);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int find_voice(int dev, int chn, int note)
+{
+	unsigned short key;
+	int i;
+
+	key = (chn << 8) | (note + 1);
+	for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+		if (synth_devs[dev]->alloc.map[i] == key)
+			return i;
+	return -1;
+}
+
+static int alloc_voice(int dev, int chn, int note)
+{
+	unsigned short  key;
+	int voice;
+
+	key = (chn << 8) | (note + 1);
+
+	voice = synth_devs[dev]->alloc_voice(dev, chn, note,
+					     &synth_devs[dev]->alloc);
+	synth_devs[dev]->alloc.map[voice] = key;
+	synth_devs[dev]->alloc.alloc_times[voice] =
+			synth_devs[dev]->alloc.timestamp++;
+	return voice;
+}
+
+static void seq_chn_voice_event(unsigned char *event_rec)
+{
+#define dev event_rec[1]
+#define cmd event_rec[2]
+#define chn event_rec[3]
+#define note event_rec[4]
+#define parm event_rec[5]
+
+	int voice = -1;
+
+	if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
+		return;
+	if (!(synth_open_mask & (1 << dev)))
+		return;
+	if (!synth_devs[dev])
+		return;
+
+	if (seq_mode == SEQ_2)
+	{
+		if (synth_devs[dev]->alloc_voice)
+			voice = find_voice(dev, chn, note);
+
+		if (cmd == MIDI_NOTEON && parm == 0)
+		{
+			cmd = MIDI_NOTEOFF;
+			parm = 64;
+		}
+	}
+
+	switch (cmd)
+	{
+		case MIDI_NOTEON:
+			if (note > 127 && note != 255)	/* Not a seq2 feature */
+				return;
+
+			if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
+			{
+				/* Internal synthesizer (FM, GUS, etc) */
+				voice = alloc_voice(dev, chn, note);
+			}
+			if (voice == -1)
+				voice = chn;
+
+			if (seq_mode == SEQ_2 && (int) dev < num_synths)
+			{
+				/*
+				 * The MIDI channel 10 is a percussive channel. Use the note
+				 * number to select the proper patch (128 to 255) to play.
+				 */
+
+				if (chn == 9)
+				{
+					synth_devs[dev]->set_instr(dev, voice, 128 + note);
+					synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
+				}
+				synth_devs[dev]->setup_voice(dev, voice, chn);
+			}
+			synth_devs[dev]->start_note(dev, voice, note, parm);
+			break;
+
+		case MIDI_NOTEOFF:
+			if (voice == -1)
+				voice = chn;
+			synth_devs[dev]->kill_note(dev, voice, note, parm);
+			break;
+
+		case MIDI_KEY_PRESSURE:
+			if (voice == -1)
+				voice = chn;
+			synth_devs[dev]->aftertouch(dev, voice, parm);
+			break;
+
+		default:;
+	}
+#undef dev
+#undef cmd
+#undef chn
+#undef note
+#undef parm
+}
+
+
+static void seq_chn_common_event(unsigned char *event_rec)
+{
+	unsigned char dev = event_rec[1];
+	unsigned char cmd = event_rec[2];
+	unsigned char chn = event_rec[3];
+	unsigned char p1 = event_rec[4];
+
+	/* unsigned char p2 = event_rec[5]; */
+	unsigned short w14 = *(short *) &event_rec[6];
+
+	if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
+		return;
+	if (!(synth_open_mask & (1 << dev)))
+		return;
+	if (!synth_devs[dev])
+		return;
+
+	switch (cmd)
+	{
+		case MIDI_PGM_CHANGE:
+			if (seq_mode == SEQ_2)
+			{
+				synth_devs[dev]->chn_info[chn].pgm_num = p1;
+				if ((int) dev >= num_synths)
+					synth_devs[dev]->set_instr(dev, chn, p1);
+			}
+			else
+				synth_devs[dev]->set_instr(dev, chn, p1);
+
+			break;
+
+		case MIDI_CTL_CHANGE:
+			if (seq_mode == SEQ_2)
+			{
+				if (chn > 15 || p1 > 127)
+					break;
+
+				synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
+
+				if (p1 < 32)	/* Setting MSB should clear LSB to 0 */
+					synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
+
+				if ((int) dev < num_synths)
+				{
+					int val = w14 & 0x7f;
+					int i, key;
+
+					if (p1 < 64)	/* Combine MSB and LSB */
+					{
+						val = ((synth_devs[dev]->
+							chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+							| (synth_devs[dev]->
+							chn_info[chn].controllers[p1 | 32] & 0x7f);
+						p1 &= ~32;
+					}
+					/* Handle all playing notes on this channel */
+
+					key = ((int) chn << 8);
+
+					for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+						if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+							synth_devs[dev]->controller(dev, i, p1, val);
+				}
+				else
+					synth_devs[dev]->controller(dev, chn, p1, w14);
+			}
+			else	/* Mode 1 */
+				synth_devs[dev]->controller(dev, chn, p1, w14);
+			break;
+
+		case MIDI_PITCH_BEND:
+			if (seq_mode == SEQ_2)
+			{
+				synth_devs[dev]->chn_info[chn].bender_value = w14;
+
+				if ((int) dev < num_synths)
+				{
+					/* Handle all playing notes on this channel */
+					int i, key;
+
+					key = (chn << 8);
+
+					for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+						if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+							synth_devs[dev]->bender(dev, i, w14);
+				}
+				else
+					synth_devs[dev]->bender(dev, chn, w14);
+			}
+			else	/* MODE 1 */
+				synth_devs[dev]->bender(dev, chn, w14);
+			break;
+
+		default:;
+	}
+}
+
+static int seq_timing_event(unsigned char *event_rec)
+{
+	unsigned char cmd = event_rec[1];
+	unsigned int parm = *(int *) &event_rec[4];
+
+	if (seq_mode == SEQ_2)
+	{
+		int ret;
+
+		if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
+			if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+				wake_up(&seq_sleeper);
+		return ret;
+	}
+	switch (cmd)
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+
+			/*
+			 * NOTE!  No break here. Execution of TMR_WAIT_REL continues in the
+			 * next case (TMR_WAIT_ABS)
+			 */
+
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				time = parm;
+				prev_event_time = time;
+
+				seq_playing = 1;
+				request_sound_timer(time);
+
+				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+					wake_up(&seq_sleeper);
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			seq_time = jiffies;
+			prev_input_time = 0;
+			prev_event_time = 0;
+			break;
+
+		case TMR_STOP:
+			break;
+
+		case TMR_CONTINUE:
+			break;
+
+		case TMR_TEMPO:
+			break;
+
+		case TMR_ECHO:
+			if (seq_mode == SEQ_2)
+				seq_copy_to_input(event_rec, 8);
+			else
+			{
+				parm = (parm << 8 | SEQ_ECHO);
+				seq_copy_to_input((unsigned char *) &parm, 4);
+			}
+			break;
+
+		default:;
+	}
+
+	return TIMER_NOT_ARMED;
+}
+
+static void seq_local_event(unsigned char *event_rec)
+{
+	unsigned char   cmd = event_rec[1];
+	unsigned int    parm = *((unsigned int *) &event_rec[4]);
+
+	switch (cmd)
+	{
+		case LOCL_STARTAUDIO:
+			DMAbuf_start_devices(parm);
+			break;
+
+		default:;
+	}
+}
+
+static void seq_sysex_message(unsigned char *event_rec)
+{
+	int dev = event_rec[1];
+	int i, l = 0;
+	unsigned char  *buf = &event_rec[2];
+
+	if ((int) dev > max_synthdev)
+		return;
+	if (!(synth_open_mask & (1 << dev)))
+		return;
+	if (!synth_devs[dev])
+		return;
+
+	l = 0;
+	for (i = 0; i < 6 && buf[i] != 0xff; i++)
+		l = i + 1;
+
+	if (!synth_devs[dev]->send_sysex)
+		return;
+	if (l > 0)
+		synth_devs[dev]->send_sysex(dev, buf, l);
+}
+
+static int play_event(unsigned char *q)
+{
+	/*
+	 * NOTE! This routine returns
+	 *   0 = normal event played.
+	 *   1 = Timer armed. Suspend playback until timer callback.
+	 *   2 = MIDI output buffer full. Restore queue and suspend until timer
+	 */
+	unsigned int *delay;
+
+	switch (q[0])
+	{
+		case SEQ_NOTEOFF:
+			if (synth_open_mask & (1 << 0))
+				if (synth_devs[0])
+					synth_devs[0]->kill_note(0, q[1], 255, q[3]);
+			break;
+
+		case SEQ_NOTEON:
+			if (q[4] < 128 || q[4] == 255)
+				if (synth_open_mask & (1 << 0))
+					if (synth_devs[0])
+						synth_devs[0]->start_note(0, q[1], q[2], q[3]);
+			break;
+
+		case SEQ_WAIT:
+			delay = (unsigned int *) q;	/*
+							 * Bytes 1 to 3 are containing the *
+							 * delay in 'ticks'
+							 */
+			*delay = (*delay >> 8) & 0xffffff;
+
+			if (*delay > 0)
+			{
+				long time;
+
+				seq_playing = 1;
+				time = *delay;
+				prev_event_time = time;
+
+				request_sound_timer(time);
+
+				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+					wake_up(&seq_sleeper);
+				/*
+				 * The timer is now active and will reinvoke this function
+				 * after the timer expires. Return to the caller now.
+				 */
+				return 1;
+			}
+			break;
+
+		case SEQ_PGMCHANGE:
+			if (synth_open_mask & (1 << 0))
+				if (synth_devs[0])
+					synth_devs[0]->set_instr(0, q[1], q[2]);
+			break;
+
+		case SEQ_SYNCTIMER: 	/*
+					 * Reset timer
+					 */
+			seq_time = jiffies;
+			prev_input_time = 0;
+			prev_event_time = 0;
+			break;
+
+		case SEQ_MIDIPUTC:	/*
+					 * Put a midi character
+					 */
+			if (midi_opened[q[2]])
+			{
+				int dev;
+
+				dev = q[2];
+
+				if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+					break;
+
+				if (!midi_devs[dev]->outputc(dev, q[1]))
+				{
+					/*
+					 * Output FIFO is full. Wait one timer cycle and try again.
+					 */
+
+					seq_playing = 1;
+					request_sound_timer(-1);
+					return 2;
+				}
+				else
+					midi_written[dev] = 1;
+			}
+			break;
+
+		case SEQ_ECHO:
+			seq_copy_to_input(q, 4);	/*
+							 * Echo back to the process
+							 */
+			break;
+
+		case SEQ_PRIVATE:
+			if ((int) q[1] < max_synthdev)
+				synth_devs[q[1]]->hw_control(q[1], q);
+			break;
+
+		case SEQ_EXTENDED:
+			extended_event(q);
+			break;
+
+		case EV_CHN_VOICE:
+			seq_chn_voice_event(q);
+			break;
+
+		case EV_CHN_COMMON:
+			seq_chn_common_event(q);
+			break;
+
+		case EV_TIMING:
+			if (seq_timing_event(q) == TIMER_ARMED)
+			{
+				return 1;
+			}
+			break;
+
+		case EV_SEQ_LOCAL:
+			seq_local_event(q);
+			break;
+
+		case EV_SYSEX:
+			seq_sysex_message(q);
+			break;
+
+		default:;
+	}
+	return 0;
+}
+
+static void seq_startplay(void)
+{
+	unsigned long flags;
+	int this_one, action;
+
+	while (qlen > 0)
+	{
+
+		save_flags(flags);
+		cli();
+		qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+		qlen--;
+		restore_flags(flags);
+
+		seq_playing = 1;
+
+		if ((action = play_event(&queue[this_one * EV_SZ])))
+		{		/* Suspend playback. Next timer routine invokes this routine again */
+			if (action == 2)
+			{
+				qlen++;
+				qhead = this_one;
+			}
+			return;
+		}
+	}
+
+	seq_playing = 0;
+
+	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+		wake_up(&seq_sleeper);
+}
+
+static void reset_controllers(int dev, unsigned char *controller, int update_dev)
+{
+	int i;
+	for (i = 0; i < 128; i++)
+		controller[i] = ctrl_def_values[i];
+}
+
+static void setup_mode2(void)
+{
+	int dev;
+
+	max_synthdev = num_synths;
+
+	for (dev = 0; dev < num_midis; dev++)
+	{
+		if (midi_devs[dev] && midi_devs[dev]->converter != NULL)
+		{
+			synth_devs[max_synthdev++] = midi_devs[dev]->converter;
+		}
+	}
+
+	for (dev = 0; dev < max_synthdev; dev++)
+	{
+		int chn;
+
+		synth_devs[dev]->sysex_ptr = 0;
+		synth_devs[dev]->emulation = 0;
+
+		for (chn = 0; chn < 16; chn++)
+		{
+			synth_devs[dev]->chn_info[chn].pgm_num = 0;
+			reset_controllers(dev,
+				synth_devs[dev]->chn_info[chn].controllers,0);
+			synth_devs[dev]->chn_info[chn].bender_value = (1 << 7);	/* Neutral */
+			synth_devs[dev]->chn_info[chn].bender_range = 200;
+		}
+	}
+	max_mididev = 0;
+	seq_mode = SEQ_2;
+}
+
+int sequencer_open(int dev, struct file *file)
+{
+	int retval, mode, i;
+	int level, tmp;
+	unsigned long flags;
+
+	if (!sequencer_ok)
+		sequencer_init();
+
+	level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
+
+	dev = dev >> 4;
+	mode = translate_mode(file);
+
+	DEB(printk("sequencer_open(dev=%d)\n", dev));
+
+	if (!sequencer_ok)
+	{
+/*		printk("Sound card: sequencer not initialized\n");*/
+		return -ENXIO;
+	}
+	if (dev)		/* Patch manager device (obsolete) */
+		return -ENXIO;
+
+	if(synth_devs[dev] == NULL)
+		request_module("synth0");
+
+	if (mode == OPEN_READ)
+	{
+		if (!num_midis)
+		{
+			/*printk("Sequencer: No MIDI devices. Input not possible\n");*/
+			sequencer_busy = 0;
+			return -ENXIO;
+		}
+	}
+	save_flags(flags);
+	cli();
+	if (sequencer_busy)
+	{
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	sequencer_busy = 1;
+	obsolete_api_used = 0;
+	restore_flags(flags);
+
+	max_mididev = num_midis;
+	max_synthdev = num_synths;
+	pre_event_timeout = MAX_SCHEDULE_TIMEOUT;
+	seq_mode = SEQ_1;
+
+	if (pending_timer != -1)
+	{
+		tmr_no = pending_timer;
+		pending_timer = -1;
+	}
+	if (tmr_no == -1)	/* Not selected yet */
+	{
+		int i, best;
+
+		best = -1;
+		for (i = 0; i < num_sound_timers; i++)
+			if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
+			{
+				tmr_no = i;
+				best = sound_timer_devs[i]->priority;
+			}
+		if (tmr_no == -1)	/* Should not be */
+			tmr_no = 0;
+	}
+	tmr = sound_timer_devs[tmr_no];
+
+	if (level == 2)
+	{
+		if (tmr == NULL)
+		{
+			/*printk("sequencer: No timer for level 2\n");*/
+			sequencer_busy = 0;
+			return -ENXIO;
+		}
+		setup_mode2();
+	}
+	if (!max_synthdev && !max_mididev)
+	{
+		sequencer_busy=0;
+		return -ENXIO;
+	}
+
+	synth_open_mask = 0;
+
+	for (i = 0; i < max_mididev; i++)
+	{
+		midi_opened[i] = 0;
+		midi_written[i] = 0;
+	}
+
+	for (i = 0; i < max_synthdev; i++)
+	{
+		if (synth_devs[i]==NULL)
+			continue;
+
+		if (synth_devs[i]->owner)
+			__MOD_INC_USE_COUNT (synth_devs[i]->owner);
+
+		if ((tmp = synth_devs[i]->open(i, mode)) < 0)
+		{
+			printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+			if (synth_devs[i]->midi_dev)
+				printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+		}
+		else
+		{
+			synth_open_mask |= (1 << i);
+			if (synth_devs[i]->midi_dev)
+				midi_opened[synth_devs[i]->midi_dev] = 1;
+		}
+	}
+
+	seq_time = jiffies;
+
+	prev_input_time = 0;
+	prev_event_time = 0;
+
+	if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+	{
+		/*
+		 * Initialize midi input devices
+		 */
+
+		for (i = 0; i < max_mididev; i++)
+			if (!midi_opened[i] && midi_devs[i])
+			{
+				if (midi_devs[i]->owner)
+					__MOD_INC_USE_COUNT (midi_devs[i]->owner);
+	
+				if ((retval = midi_devs[i]->open(i, mode,
+					sequencer_midi_input, sequencer_midi_output)) >= 0)
+				{
+					midi_opened[i] = 1;
+				}
+			}
+	}
+
+	if (seq_mode == SEQ_2) {
+		if (tmr->owner)
+			__MOD_INC_USE_COUNT (tmr->owner);
+		tmr->open(tmr_no, seq_mode);
+	}
+
+ 	init_waitqueue_head(&seq_sleeper);
+ 	init_waitqueue_head(&midi_sleeper);
+	output_threshold = SEQ_MAX_QUEUE / 2;
+
+	return 0;
+}
+
+void seq_drain_midi_queues(void)
+{
+	int i, n;
+
+	/*
+	 * Give the Midi drivers time to drain their output queues
+	 */
+
+	n = 1;
+
+	while (!signal_pending(current) && n)
+	{
+		n = 0;
+
+		for (i = 0; i < max_mididev; i++)
+			if (midi_opened[i] && midi_written[i])
+				if (midi_devs[i]->buffer_status != NULL)
+					if (midi_devs[i]->buffer_status(i))
+						n++;
+
+		/*
+		 * Let's have a delay
+		 */
+
+ 		if (n)
+ 			interruptible_sleep_on_timeout(&seq_sleeper,
+						       HZ/10);
+	}
+}
+
+void sequencer_release(int dev, struct file *file)
+{
+	int i;
+	int mode = translate_mode(file);
+
+	dev = dev >> 4;
+
+	DEB(printk("sequencer_release(dev=%d)\n", dev));
+
+	/*
+	 * Wait until the queue is empty (if we don't have nonblock)
+	 */
+
+	if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK))
+	{
+		while (!signal_pending(current) && qlen > 0)
+		{
+  			seq_sync();
+ 			interruptible_sleep_on_timeout(&seq_sleeper,
+						       3*HZ);
+ 			/* Extra delay */
+		}
+	}
+
+	if (mode != OPEN_READ)
+		seq_drain_midi_queues();	/*
+						 * Ensure the output queues are empty
+						 */
+	seq_reset();
+	if (mode != OPEN_READ)
+		seq_drain_midi_queues();	/*
+						 * Flush the all notes off messages
+						 */
+
+	for (i = 0; i < max_synthdev; i++)
+	{
+		if (synth_open_mask & (1 << i))	/*
+						 * Actually opened
+						 */
+			if (synth_devs[i])
+			{
+				synth_devs[i]->close(i);
+
+				if (synth_devs[i]->owner)
+					__MOD_DEC_USE_COUNT (synth_devs[i]->owner);
+
+				if (synth_devs[i]->midi_dev)
+					midi_opened[synth_devs[i]->midi_dev] = 0;
+			}
+	}
+
+	for (i = 0; i < max_mididev; i++)
+	{
+		if (midi_opened[i]) {
+			midi_devs[i]->close(i);
+			if (midi_devs[i]->owner)
+				__MOD_DEC_USE_COUNT (midi_devs[i]->owner);
+		}
+	}
+
+	if (seq_mode == SEQ_2) {
+		tmr->close(tmr_no);
+		if (tmr->owner)
+			__MOD_DEC_USE_COUNT (tmr->owner);
+	}
+
+	if (obsolete_api_used)
+		printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm);
+	sequencer_busy = 0;
+}
+
+static int seq_sync(void)
+{
+	unsigned long flags;
+
+	if (qlen && !seq_playing && !signal_pending(current))
+		seq_startplay();
+
+	save_flags(flags);
+	cli();
+ 	if (qlen > 0)
+ 		interruptible_sleep_on_timeout(&seq_sleeper, HZ);
+	restore_flags(flags);
+	return qlen;
+}
+
+static void midi_outc(int dev, unsigned char data)
+{
+	/*
+	 * NOTE! Calls sleep(). Don't call this from interrupt.
+	 */
+
+	int n;
+	unsigned long flags;
+
+	/*
+	 * This routine sends one byte to the Midi channel.
+	 * If the output FIFO is full, it waits until there
+	 * is space in the queue
+	 */
+
+	n = 3 * HZ;		/* Timeout */
+
+	save_flags(flags);
+	cli();
+ 	while (n && !midi_devs[dev]->outputc(dev, data)) {
+ 		interruptible_sleep_on_timeout(&seq_sleeper, HZ/25);
+  		n--;
+  	}
+	restore_flags(flags);
+}
+
+static void seq_reset(void)
+{
+	/*
+	 * NOTE! Calls sleep(). Don't call this from interrupt.
+	 */
+
+	int i;
+	int chn;
+	unsigned long flags;
+
+	sound_stop_timer();
+
+	seq_time = jiffies;
+	prev_input_time = 0;
+	prev_event_time = 0;
+
+	qlen = qhead = qtail = 0;
+	iqlen = iqhead = iqtail = 0;
+
+	for (i = 0; i < max_synthdev; i++)
+		if (synth_open_mask & (1 << i))
+			if (synth_devs[i])
+				synth_devs[i]->reset(i);
+
+	if (seq_mode == SEQ_2)
+	{
+		for (chn = 0; chn < 16; chn++)
+			for (i = 0; i < max_synthdev; i++)
+				if (synth_open_mask & (1 << i))
+					if (synth_devs[i])
+					{
+						synth_devs[i]->controller(i, chn, 123, 0);	/* All notes off */
+						synth_devs[i]->controller(i, chn, 121, 0);	/* Reset all ctl */
+						synth_devs[i]->bender(i, chn, 1 << 13);	/* Bender off */
+					}
+	}
+	else	/* seq_mode == SEQ_1 */
+	{
+		for (i = 0; i < max_mididev; i++)
+			if (midi_written[i])	/*
+						 * Midi used. Some notes may still be playing
+						 */
+			{
+				/*
+				 *      Sending just a ACTIVE SENSING message should be enough to stop all
+				 *      playing notes. Since there are devices not recognizing the
+				 *      active sensing, we have to send some all notes off messages also.
+				 */
+				midi_outc(i, 0xfe);
+
+				for (chn = 0; chn < 16; chn++)
+				{
+					midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f)));		/* control change */
+					midi_outc(i, 0x7b);	/* All notes off */
+					midi_outc(i, 0);	/* Dummy parameter */
+				}
+
+				midi_devs[i]->close(i);
+
+				midi_written[i] = 0;
+				midi_opened[i] = 0;
+			}
+	}
+
+	seq_playing = 0;
+
+	save_flags(flags);
+	cli();
+
+	if (waitqueue_active(&seq_sleeper)) {
+		/*      printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
+		wake_up(&seq_sleeper);
+	}
+	restore_flags(flags);
+}
+
+static void seq_panic(void)
+{
+	/*
+	 * This routine is called by the application in case the user
+	 * wants to reset the system to the default state.
+	 */
+
+	seq_reset();
+
+	/*
+	 * Since some of the devices don't recognize the active sensing and
+	 * all notes off messages, we have to shut all notes manually.
+	 *
+	 *      TO BE IMPLEMENTED LATER
+	 */
+
+	/*
+	 * Also return the controllers to their default states
+	 */
+}
+
+int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg)
+{
+	int midi_dev, orig_dev, val, err;
+	int mode = translate_mode(file);
+	struct synth_info inf;
+	struct seq_event_rec event_rec;
+	unsigned long flags;
+
+	orig_dev = dev = dev >> 4;
+
+	switch (cmd)
+	{
+		case SNDCTL_TMR_TIMEBASE:
+		case SNDCTL_TMR_TEMPO:
+		case SNDCTL_TMR_START:
+		case SNDCTL_TMR_STOP:
+		case SNDCTL_TMR_CONTINUE:
+		case SNDCTL_TMR_METRONOME:
+		case SNDCTL_TMR_SOURCE:
+			if (seq_mode != SEQ_2)
+				return -EINVAL;
+			return tmr->ioctl(tmr_no, cmd, arg);
+
+		case SNDCTL_TMR_SELECT:
+			if (seq_mode != SEQ_2)
+				return -EINVAL;
+			if (get_user(pending_timer, (int *)arg))
+				return -EFAULT;
+			if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL)
+			{
+				pending_timer = -1;
+				return -EINVAL;
+			}
+			val = pending_timer;
+			break;
+
+		case SNDCTL_SEQ_PANIC:
+			seq_panic();
+			return -EINVAL;
+
+		case SNDCTL_SEQ_SYNC:
+			if (mode == OPEN_READ)
+				return 0;
+			while (qlen > 0 && !signal_pending(current))
+				seq_sync();
+			return qlen ? -EINTR : 0;
+
+		case SNDCTL_SEQ_RESET:
+			seq_reset();
+			return 0;
+
+		case SNDCTL_SEQ_TESTMIDI:
+			if (__get_user(midi_dev, (int *)arg))
+				return -EFAULT;
+			if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev])
+				return -ENXIO;
+
+			if (!midi_opened[midi_dev] &&
+				(err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input,
+						     sequencer_midi_output)) < 0)
+				return err;
+			midi_opened[midi_dev] = 1;
+			return 0;
+
+		case SNDCTL_SEQ_GETINCOUNT:
+			if (mode == OPEN_WRITE)
+				return 0;
+			val = iqlen;
+			break;
+
+		case SNDCTL_SEQ_GETOUTCOUNT:
+			if (mode == OPEN_READ)
+				return 0;
+			val = SEQ_MAX_QUEUE - qlen;
+			break;
+
+		case SNDCTL_SEQ_GETTIME:
+			if (seq_mode == SEQ_2)
+				return tmr->ioctl(tmr_no, cmd, arg);
+			val = jiffies - seq_time;
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			/*
+			 * If *arg == 0, just return the current rate
+			 */
+			if (seq_mode == SEQ_2)
+				return tmr->ioctl(tmr_no, cmd, arg);
+
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val != 0)
+				return -EINVAL;
+			val = HZ;
+			break;
+
+		case SNDCTL_SEQ_RESETSAMPLES:
+		case SNDCTL_SYNTH_REMOVESAMPLE:
+		case SNDCTL_SYNTH_CONTROL:
+			if (get_user(dev, (int *)arg))
+				return -EFAULT;
+			if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			return synth_devs[dev]->ioctl(dev, cmd, arg);
+
+		case SNDCTL_SEQ_NRSYNTHS:
+			val = max_synthdev;
+			break;
+
+		case SNDCTL_SEQ_NRMIDIS:
+			val = max_mididev;
+			break;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			if (get_user(dev, (int *)arg))
+				return -EFAULT;
+			if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			val = synth_devs[dev]->ioctl(dev, cmd, arg);
+			break;
+
+		case SNDCTL_FM_4OP_ENABLE:
+			if (get_user(dev, (int *)arg))
+				return -EFAULT;
+			if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)))
+				return -ENXIO;
+			synth_devs[dev]->ioctl(dev, cmd, arg);
+			return 0;
+
+		case SNDCTL_SYNTH_INFO:
+			if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device))))
+				return -EFAULT;
+			if (dev < 0 || dev >= max_synthdev)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			return synth_devs[dev]->ioctl(dev, cmd, arg);
+
+		/* Like SYNTH_INFO but returns ID in the name field */
+		case SNDCTL_SYNTH_ID:
+			if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device))))
+				return -EFAULT;
+			if (dev < 0 || dev >= max_synthdev)
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+				return -EBUSY;
+			memcpy(&inf, synth_devs[dev]->info, sizeof(inf));
+			strncpy(inf.name, synth_devs[dev]->id, sizeof(inf.name));
+			inf.device = dev;
+			return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0;
+
+		case SNDCTL_SEQ_OUTOFBAND:
+			if (copy_from_user(&event_rec, arg, sizeof(event_rec)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			play_event(event_rec.arr);
+			restore_flags(flags);
+			return 0;
+
+		case SNDCTL_MIDI_INFO:
+			if (get_user(dev, (int *)(&(((struct midi_info *)arg)->device))))
+				return -EFAULT;
+			if (dev < 0 || dev >= max_mididev || !midi_devs[dev])
+				return -ENXIO;
+			midi_devs[dev]->info.device = dev;
+			return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0;
+
+		case SNDCTL_SEQ_THRESHOLD:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val < 1)
+				val = 1;
+			if (val >= SEQ_MAX_QUEUE)
+				val = SEQ_MAX_QUEUE - 1;
+			output_threshold = val;
+			return 0;
+
+		case SNDCTL_MIDI_PRETIME:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val < 0)
+				val = 0;
+			val = (HZ * val) / 10;
+			pre_event_timeout = val;
+			break;
+
+		default:
+			if (mode == OPEN_READ)
+				return -EIO;
+			if (!synth_devs[0])
+				return -ENXIO;
+			if (!(synth_open_mask & (1 << 0)))
+				return -ENXIO;
+			if (!synth_devs[0]->ioctl)
+				return -EINVAL;
+			return synth_devs[0]->ioctl(0, cmd, arg);
+	}
+	return put_user(val, (int *)arg);
+}
+
+/* No kernel lock - we're using the global irq lock here */
+unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait)
+{
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	dev = dev >> 4;
+
+	save_flags(flags);
+	cli();
+	/* input */
+	poll_wait(file, &midi_sleeper, wait);
+	if (iqlen)
+		mask |= POLLIN | POLLRDNORM;
+
+	/* output */
+	poll_wait(file, &seq_sleeper, wait);
+	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+		mask |= POLLOUT | POLLWRNORM;
+	restore_flags(flags);
+	return mask;
+}
+
+
+void sequencer_timer(unsigned long dummy)
+{
+	seq_startplay();
+}
+
+int note_to_freq(int note_num)
+{
+
+	/*
+	 * This routine converts a midi note to a frequency (multiplied by 1000)
+	 */
+
+	int note, octave, note_freq;
+	static int notes[] =
+	{
+		261632, 277189, 293671, 311132, 329632, 349232,
+		369998, 391998, 415306, 440000, 466162, 493880
+	};
+
+#define BASE_OCTAVE	5
+
+	octave = note_num / 12;
+	note = note_num % 12;
+
+	note_freq = notes[note];
+
+	if (octave < BASE_OCTAVE)
+		note_freq >>= (BASE_OCTAVE - octave);
+	else if (octave > BASE_OCTAVE)
+		note_freq <<= (octave - BASE_OCTAVE);
+
+	/*
+	 * note_freq >>= 1;
+	 */
+
+	return note_freq;
+}
+
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
+		 int vibrato_cents)
+{
+	unsigned long amount;
+	int negative, semitones, cents, multiplier = 1;
+
+	if (!bend)
+		return base_freq;
+	if (!range)
+		return base_freq;
+
+	if (!base_freq)
+		return base_freq;
+
+	if (range >= 8192)
+		range = 8192;
+
+	bend = bend * range / 8192;	/* Convert to cents */
+	bend += vibrato_cents;
+
+	if (!bend)
+		return base_freq;
+
+	negative = bend < 0 ? 1 : 0;
+
+	if (bend < 0)
+		bend *= -1;
+	if (bend > range)
+		bend = range;
+
+	/*
+	   if (bend > 2399)
+	   bend = 2399;
+	 */
+	while (bend > 2399)
+	{
+		multiplier *= 4;
+		bend -= 2400;
+	}
+
+	semitones = bend / 100;
+	if (semitones > 99)
+		semitones = 99;
+	cents = bend % 100;
+
+	amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000;
+
+	if (negative)
+		return (base_freq * 10000) / amount;	/* Bend down */
+	else
+		return (base_freq * amount) / 10000;	/* Bend up */
+}
+
+
+void sequencer_init(void)
+{
+	/* drag in sequencer_syms.o */
+	{
+		extern char sequencer_syms_symbol;
+		sequencer_syms_symbol = 0;
+	}
+
+	if (sequencer_ok)
+		return;
+	MIDIbuf_init();
+	queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ);
+	if (queue == NULL)
+	{
+		printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n");
+		return;
+	}
+	iqueue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * IEV_SZ);
+	if (iqueue == NULL)
+	{
+		printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n");
+		vfree(queue);
+		return;
+	}
+	sequencer_ok = 1;
+}
+
+void sequencer_unload(void)
+{
+	if(queue)
+	{
+		vfree(queue);
+		queue=NULL;
+	}
+	if(iqueue)
+	{
+		vfree(iqueue);
+		iqueue=NULL;
+	}
+}
diff -Nru linux/sound/oss/sequencer_syms.c linux-2.4.19-pre5-mjc/sound/oss/sequencer_syms.c
--- linux/sound/oss/sequencer_syms.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sequencer_syms.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,32 @@
+/*
+ * Exported symbols for sequencer driver.
+ * __NO_VERSION__ because this is still part of sound.o.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+char sequencer_syms_symbol;
+
+#include "sound_config.h"
+#include "sound_calls.h"
+
+EXPORT_SYMBOL(note_to_freq);
+EXPORT_SYMBOL(compute_finetune);
+EXPORT_SYMBOL(seq_copy_to_input);
+EXPORT_SYMBOL(seq_input_event);
+EXPORT_SYMBOL(sequencer_init);
+EXPORT_SYMBOL(sequencer_timer);
+
+EXPORT_SYMBOL(sound_timer_init);
+EXPORT_SYMBOL(sound_timer_interrupt);
+EXPORT_SYMBOL(sound_timer_syncinterval);
+EXPORT_SYMBOL(reprogram_timer);
+
+/* Tuning */
+
+#define _SEQUENCER_C_
+#include "tuning.h"
+
+EXPORT_SYMBOL(cent_tuning);
+EXPORT_SYMBOL(semitone_tuning);
diff -Nru linux/sound/oss/sgalaxy.c linux-2.4.19-pre5-mjc/sound/oss/sgalaxy.c
--- linux/sound/oss/sgalaxy.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sgalaxy.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,197 @@
+/*
+ * sound/sgalaxy.c
+ *
+ * Low level driver for Aztech Sound Galaxy cards.
+ * Copyright 1998 Artur Skawina <skawina@geocities.com>
+ *
+ * Supported cards:
+ *    Aztech Sound Galaxy Waverider Pro 32 - 3D
+ *    Aztech Sound Galaxy Washington 16
+ *
+ * Based on cs4232.c by Hannu Savolainen and Alan Cox.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added __init to sb_rst() and sb_cmd()
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+#include "ad1848.h"
+
+static void sleep( unsigned howlong )
+{
+	current->state   = TASK_INTERRUPTIBLE;
+	schedule_timeout(howlong);
+}
+
+#define DPORT 0x80
+
+/* Sound Blaster regs */
+
+#define SBDSP_RESET      0x6
+#define SBDSP_READ       0xA
+#define SBDSP_COMMAND    0xC
+#define SBDSP_STATUS     SBDSP_COMMAND
+#define SBDSP_DATA_AVAIL 0xE
+
+static int __init sb_rst(int base)
+{
+	int   i;
+   
+	outb( 1, base+SBDSP_RESET );     /* reset the DSP */
+	outb( 0, base+SBDSP_RESET );
+    
+	for ( i=0; i<500; i++ )          /* delay */
+		inb(DPORT);
+      
+	for ( i=0; i<100000; i++ )
+	{
+		if ( inb( base+SBDSP_DATA_AVAIL )&0x80 )
+			break;
+	}
+
+	if ( inb( base+SBDSP_READ )!=0xAA )
+		return 0;
+
+	return 1;
+}
+
+static int __init sb_cmd( int base, unsigned char val )
+{
+	int  i;
+
+	for ( i=100000; i; i-- )
+	{
+		if ( (inb( base+SBDSP_STATUS )&0x80)==0 )
+		{
+        		outb( val, base+SBDSP_COMMAND );
+        		break;
+		}
+	}
+	return i;      /* i>0 == success */
+}
+
+
+#define ai_sgbase    driver_use_1
+
+static int __init probe_sgalaxy( struct address_info *ai )
+{
+	if ( check_region( ai->io_base, 8 ) ) {
+		printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base);
+		return 0;
+	}
+        
+	if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) )
+		return probe_ms_sound(ai);  /* The card is already active, check irq etc... */
+
+	if ( check_region( ai->ai_sgbase, 0x10 ) ) {
+		printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase);
+		return 0;
+	}
+        
+	/* switch to MSS/WSS mode */
+   
+	sb_rst( ai->ai_sgbase );
+   
+	sb_cmd( ai->ai_sgbase, 9 );
+	sb_cmd( ai->ai_sgbase, 0 );
+
+	sleep( HZ/10 );
+
+      	return probe_ms_sound(ai);
+}
+
+static void __init attach_sgalaxy( struct address_info *ai )
+{
+	int n;
+	
+	request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" );
+ 
+	attach_ms_sound(ai, THIS_MODULE);
+	n=ai->slots[0];
+	
+	if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) {
+		AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE );   /* Line-in */
+		AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH );  /* FM+Wavetable*/
+		AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD );     /* CD */
+	}
+}
+
+static void __exit unload_sgalaxy( struct address_info *ai )
+{
+	unload_ms_sound( ai );
+	release_region( ai->ai_sgbase, 0x10 );
+}
+
+static struct address_info cfg;
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma2	= -1;
+static int __initdata sgbase	= -1;
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma2,"i");
+MODULE_PARM(sgbase,"i");
+
+static int __init init_sgalaxy(void)
+{
+	cfg.io_base   = io;
+	cfg.irq       = irq;
+	cfg.dma       = dma;
+	cfg.dma2      = dma2;
+	cfg.ai_sgbase = sgbase;
+
+	if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) {
+		printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n");
+		return -EINVAL;
+	}
+
+	if ( probe_sgalaxy(&cfg) == 0 )
+		return -ENODEV;
+
+	attach_sgalaxy(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_sgalaxy(void)
+{
+	unload_sgalaxy(&cfg);
+}
+
+module_init(init_sgalaxy);
+module_exit(cleanup_sgalaxy);
+
+#ifndef MODULE
+static int __init setup_sgalaxy(char *str)
+{
+	/* io, irq, dma, dma2, sgbase */
+	int ints[6];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	sgbase	= ints[5];
+
+	return 1;
+}
+
+__setup("sgalaxy=", setup_sgalaxy);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/skeleton.c linux-2.4.19-pre5-mjc/sound/oss/skeleton.c
--- linux/sound/oss/skeleton.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/skeleton.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,223 @@
+/*
+ *	PCI sound skeleton example
+ *
+ *	(c) 1998 Red Hat Software
+ *
+ *	This software may be used and distributed according to the 
+ *	terms of the GNU General Public License, incorporated herein by 
+ *	reference.
+ *
+ *	This example is designed to be built in the linux/drivers/sound
+ *	directory as part of a kernel build. The example is modular only
+ *	drop me a note once you have a working modular driver and want
+ *	to integrate it with the main code.
+ *		-- Alan <alan@redhat.com>
+ *
+ *	This is a first draft. Please report any errors, corrections or
+ *	improvements to me.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+
+#include "sound_config.h"
+
+/*
+ *	Define our PCI vendor ID here
+ */
+ 
+#ifndef PCI_VENDOR_MYIDENT
+#define PCI_VENDOR_MYIDENT			0x125D
+
+/*
+ *	PCI identity for the card.
+ */
+ 
+#define PCI_DEVICE_ID_MYIDENT_MYCARD1		0x1969
+#endif
+
+#define CARD_NAME	"ExampleWave 3D Pro Ultra ThingyWotsit"
+
+#define MAX_CARDS	8
+
+/*
+ *	Each address_info object holds the information about one of
+ *	our card resources. In this case the MSS emulation of our
+ *	ficticious card. Its used to manage and attach things.
+ */
+ 
+static struct address_info	mss_data[MAX_CARDS];
+static int 			cards = 0;
+
+/*
+ *	Install the actual card. This is an example
+ */
+
+static int mycard_install(struct pci_dev *pcidev)
+{
+	int iobase;
+	int mssbase;
+	int mpubase;
+	u8 x;
+	u16 w;
+	u32 v;
+	int i;
+	int dma;
+
+	/*
+	 *	Our imaginary code has its I/O on PCI address 0, a
+	 *	MSS on PCI address 1 and an MPU on address 2
+	 *
+	 *	For the example we will only initialise the MSS
+	 */
+	 	
+	iobase = pci_resource_start(pcidev, 0);
+	mssbase = pci_resource_start(pcidev, 1);
+	mpubase = pci_resource_start(pcidev, 2);
+	
+	/*
+	 *	Reset the board
+	 */
+	 
+	/*
+	 *	Wait for completion. udelay() waits in microseconds
+	 */
+	 
+	udelay(100);
+	
+	/*
+	 *	Ok card ready. Begin setup proper. You might for example
+	 *	load the firmware here
+	 */
+	
+	dma = card_specific_magic(ioaddr);
+	
+	/*
+	 *	Turn on legacy mode (example), There are also byte and
+	 *	dword (32bit) PCI configuration function calls
+	 */
+
+	pci_read_config_word(pcidev, 0x40, &w);
+	w&=~(1<<15);			/* legacy decode on */
+	w|=(1<<14);			/* Reserved write as 1 in this case */
+	w|=(1<<3)|(1<<1)|(1<<0);	/* SB on , FM on, MPU on */
+	pci_write_config_word(pcidev, 0x40, w);
+	
+	/*
+	 *	Let the user know we found his toy.
+	 */
+	 
+	printk(KERN_INFO "Programmed "CARD_NAME" at 0x%X to legacy mode.\n",
+		iobase);
+		
+	/*
+	 *	Now set it up the description of the card
+	 */
+	 
+	mss_data[cards].io_base = mssbase;
+	mss_data[cards].irq = pcidev->irq;
+	mss_data[cards].dma = dma;
+	
+	/*
+	 *	Check there is an MSS present
+	 */
+
+	if(ad1848_detect(mssbase, NULL, mss_data[cards].osp)==0)
+		return 0;
+		
+	/*
+	 *	Initialize it
+	 */
+	 
+	mss_data[cards].slots[3] = ad1848_init("MyCard MSS 16bit", 
+			mssbase,
+			mss_data[cards].irq,
+			mss_data[cards].dma,
+			mss_data[cards].dma,
+			0,
+			0,
+			THIS_MODULE);
+
+	cards++;	
+	return 1;
+}
+
+
+/*
+ * 	This loop walks the PCI configuration database and finds where
+ *	the sound cards are.
+ */
+ 
+int init_mycard(void)
+{
+	struct pci_dev *pcidev=NULL;
+	int count=0;
+		
+	if(!pci_present())
+		return -ENODEV;
+	
+		
+	while((pcidev = pci_find_device(PCI_VENDOR_MYIDENT, PCI_DEVICE_ID_MYIDENT_MYCARD1, pcidev))!=NULL)
+	{
+		if (pci_enable_device(pcidev))
+			continue;
+		count+=mycard_install(pcidev);
+		if(count)
+			return 0;
+		if(count==MAX_CARDS)
+			break;
+	}
+	
+	if(count==0)
+		return -ENODEV;
+	return 0;
+}
+
+/*
+ *	This function is called when the user or kernel loads the 
+ *	module into memory.
+ */
+
+
+int init_module(void)
+{
+	if(init_mycard()<0)
+	{
+		printk(KERN_ERR "No "CARD_NAME" cards found.\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ *	This is called when it is removed. It will only be removed 
+ *	when its use count is 0.
+ */
+ 
+void cleanup_module(void)
+{
+	for(i=0;i< cards; i++)
+	{
+		/*
+		 *	Free attached resources
+		 */
+		 
+		ad1848_unload(mss_data[i].io_base,
+			      mss_data[i].irq,
+			      mss_data[i].dma,
+			      mss_data[i].dma,
+			      0);
+		/*
+		 *	And disconnect the device from the kernel
+		 */
+		sound_unload_audiodevice(mss_data[i].slots[3]);
+	}
+}
+
diff -Nru linux/sound/oss/sonicvibes.c linux-2.4.19-pre5-mjc/sound/oss/sonicvibes.c
--- linux/sound/oss/sonicvibes.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sonicvibes.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2767 @@
+/*****************************************************************************/
+
+/*
+ *      sonicvibes.c  --  S3 Sonic Vibes audio driver.
+ *
+ *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *
+ *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to David C. Niemi
+ *
+ *
+ * Module command line parameters:
+ *   none so far
+ *
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  The card has both an FM and a Wavetable synth, but I have to figure
+ *  out first how to drive them...
+ *
+ *  Revision history
+ *    06.05.1998   0.1   Initial release
+ *    10.05.1998   0.2   Fixed many bugs, esp. ADC rate calculation
+ *                       First stab at a simple midi interface (no bells&whistles)
+ *    13.05.1998   0.3   Fix stupid cut&paste error: set_adc_rate was called instead of
+ *                       set_dac_rate in the FMODE_WRITE case in sv_open
+ *                       Fix hwptr out of bounds (now mpg123 works)
+ *    14.05.1998   0.4   Don't allow excessive interrupt rates
+ *    08.06.1998   0.5   First release using Alan Cox' soundcore instead of miscdevice
+ *    03.08.1998   0.6   Do not include modversions.h
+ *                       Now mixer behaviour can basically be selected between
+ *                       "OSS documented" and "OSS actual" behaviour
+ *    31.08.1998   0.7   Fix realplayer problems - dac.count issues
+ *    10.12.1998   0.8   Fix drain_dac trying to wait on not yet initialized DMA
+ *    16.12.1998   0.9   Fix a few f_file & FMODE_ bugs
+ *    06.01.1999   0.10  remove the silly SA_INTERRUPT flag.
+ *                       hopefully killed the egcs section type conflict
+ *    12.03.1999   0.11  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *    22.03.1999   0.12  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    05.04.1999   0.13  added code to sv_read and sv_write which should detect
+ *                       lockups of the sound chip and revive it. This is basically
+ *                       an ugly hack, but at least applications using this driver
+ *                       won't hang forever. I don't know why these lockups happen,
+ *                       it might well be the motherboard chipset (an early 486 PCI
+ *                       board with ALI chipset), since every busmastering 100MB
+ *                       ethernet card I've tried (Realtek 8139 and Macronix tulip clone)
+ *                       exhibit similar behaviour (they work for a couple of packets
+ *                       and then lock up and can be revived by ifconfig down/up).
+ *    07.04.1999   0.14  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *                       Note: dmaio hack might still be wrong on archs other than i386
+ *    15.06.1999   0.15  Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.16  Add pci_set_master
+ *    03.08.1999   0.17  adapt to Linus' new __setup/__initcall
+ *                       added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr"
+ *    12.08.1999   0.18  module_init/__setup fixes
+ *    24.08.1999   0.19  get rid of the dmaio kludge, replace with allocate_resource
+ *    31.08.1999   0.20  add spin_lock_init
+ *                       use new resource allocation to allocate DDMA IO space
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.21  change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    28.10.1999   0.22  More waitqueue races fixed
+ *    01.12.1999   0.23  New argument to allocate_resource
+ *    07.12.1999   0.24  More allocate_resource semantics change
+ *    08.01.2000   0.25  Prevent some ioctl's from returning bad count values on underrun/overrun;
+ *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
+ *                       use Martin Mares' pci_assign_resource
+ *    07.02.2000   0.26  Use pci_alloc_consistent and pci_register_driver
+ *    21.11.2000   0.27  Initialize dma buffers in poll, otherwise poll may return a bogus mask
+ *    12.12.2000   0.28  More dma buffer initializations, patch from
+ *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
+ *    31.01.2001   0.29  Register/Unregister gameport
+ *                       Fix SETTRIGGER non OSS API conformity
+ *    18.05.2001   0.30  PCI probing and error values cleaned up by Marcus
+ *                       Meissner <mm@caldera.de>
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/wrapper.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/gameport.h>
+
+#include "dm.h"
+
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_S3
+#define PCI_VENDOR_ID_S3             0x5333
+#endif
+#ifndef PCI_DEVICE_ID_S3_SONICVIBES
+#define PCI_DEVICE_ID_S3_SONICVIBES  0xca00
+#endif
+
+#define SV_MAGIC  ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES)
+
+#define SV_EXTENT_SB      0x10
+#define SV_EXTENT_ENH     0x10
+#define SV_EXTENT_SYNTH   0x4
+#define SV_EXTENT_MIDI    0x4
+#define SV_EXTENT_GAME    0x8
+#define SV_EXTENT_DMA     0x10
+
+/*
+ * we are not a bridge and thus use a resource for DDMA that is used for bridges but
+ * left empty for normal devices
+ */
+#define RESOURCE_SB       0
+#define RESOURCE_ENH      1
+#define RESOURCE_SYNTH    2
+#define RESOURCE_MIDI     3
+#define RESOURCE_GAME     4
+#define RESOURCE_DDMA     7
+
+#define SV_MIDI_DATA      0
+#define SV_MIDI_COMMAND   1
+#define SV_MIDI_STATUS    1
+
+#define SV_DMA_ADDR0      0
+#define SV_DMA_ADDR1      1
+#define SV_DMA_ADDR2      2
+#define SV_DMA_ADDR3      3
+#define SV_DMA_COUNT0     4
+#define SV_DMA_COUNT1     5
+#define SV_DMA_COUNT2     6
+#define SV_DMA_MODE       0xb
+#define SV_DMA_RESET      0xd
+#define SV_DMA_MASK       0xf
+
+/*
+ * DONT reset the DMA controllers unless you understand
+ * the reset semantics. Assuming reset semantics as in
+ * the 8237 does not work.
+ */
+
+#define DMA_MODE_AUTOINIT 0x10
+#define DMA_MODE_READ     0x44    /* I/O to memory, no autoinit, increment, single mode */
+#define DMA_MODE_WRITE    0x48    /* memory to I/O, no autoinit, increment, single mode */
+
+#define SV_CODEC_CONTROL  0
+#define SV_CODEC_INTMASK  1
+#define SV_CODEC_STATUS   2
+#define SV_CODEC_IADDR    4
+#define SV_CODEC_IDATA    5
+
+#define SV_CCTRL_RESET      0x80
+#define SV_CCTRL_INTADRIVE  0x20
+#define SV_CCTRL_WAVETABLE  0x08
+#define SV_CCTRL_REVERB     0x04
+#define SV_CCTRL_ENHANCED   0x01
+
+#define SV_CINTMASK_DMAA    0x01
+#define SV_CINTMASK_DMAC    0x04
+#define SV_CINTMASK_SPECIAL 0x08
+#define SV_CINTMASK_UPDOWN  0x40
+#define SV_CINTMASK_MIDI    0x80
+
+#define SV_CSTAT_DMAA       0x01
+#define SV_CSTAT_DMAC	    0x04
+#define SV_CSTAT_SPECIAL    0x08
+#define SV_CSTAT_UPDOWN	    0x40
+#define SV_CSTAT_MIDI	    0x80
+
+#define SV_CIADDR_TRD       0x80
+#define SV_CIADDR_MCE       0x40
+
+/* codec indirect registers */
+#define SV_CIMIX_ADCINL     0x00
+#define SV_CIMIX_ADCINR     0x01
+#define SV_CIMIX_AUX1INL    0x02
+#define SV_CIMIX_AUX1INR    0x03
+#define SV_CIMIX_CDINL      0x04
+#define SV_CIMIX_CDINR      0x05
+#define SV_CIMIX_LINEINL    0x06
+#define SV_CIMIX_LINEINR    0x07
+#define SV_CIMIX_MICIN      0x08
+#define SV_CIMIX_SYNTHINL   0x0A
+#define SV_CIMIX_SYNTHINR   0x0B
+#define SV_CIMIX_AUX2INL    0x0C
+#define SV_CIMIX_AUX2INR    0x0D
+#define SV_CIMIX_ANALOGINL  0x0E
+#define SV_CIMIX_ANALOGINR  0x0F
+#define SV_CIMIX_PCMINL     0x10
+#define SV_CIMIX_PCMINR     0x11
+
+#define SV_CIGAMECONTROL    0x09
+#define SV_CIDATAFMT        0x12
+#define SV_CIENABLE         0x13
+#define SV_CIUPDOWN         0x14
+#define SV_CIREVISION       0x15
+#define SV_CIADCOUTPUT      0x16
+#define SV_CIDMAABASECOUNT1 0x18
+#define SV_CIDMAABASECOUNT0 0x19
+#define SV_CIDMACBASECOUNT1 0x1c
+#define SV_CIDMACBASECOUNT0 0x1d
+#define SV_CIPCMSR0         0x1e
+#define SV_CIPCMSR1         0x1f
+#define SV_CISYNTHSR0       0x20
+#define SV_CISYNTHSR1       0x21
+#define SV_CIADCCLKSOURCE   0x22
+#define SV_CIADCALTSR       0x23
+#define SV_CIADCPLLM        0x24
+#define SV_CIADCPLLN        0x25
+#define SV_CISYNTHPLLM      0x26
+#define SV_CISYNTHPLLN      0x27
+#define SV_CIUARTCONTROL    0x2a
+#define SV_CIDRIVECONTROL   0x2b
+#define SV_CISRSSPACE       0x2c
+#define SV_CISRSCENTER      0x2d
+#define SV_CIWAVETABLESRC   0x2e
+#define SV_CIANALOGPWRDOWN  0x30
+#define SV_CIDIGITALPWRDOWN 0x31
+
+
+#define SV_CIMIX_ADCSRC_CD     0x20
+#define SV_CIMIX_ADCSRC_DAC    0x40
+#define SV_CIMIX_ADCSRC_AUX2   0x60
+#define SV_CIMIX_ADCSRC_LINE   0x80
+#define SV_CIMIX_ADCSRC_AUX1   0xa0
+#define SV_CIMIX_ADCSRC_MIC    0xc0
+#define SV_CIMIX_ADCSRC_MIXOUT 0xe0
+#define SV_CIMIX_ADCSRC_MASK   0xe0
+
+#define SV_CFMT_STEREO     0x01
+#define SV_CFMT_16BIT      0x02
+#define SV_CFMT_MASK       0x03
+#define SV_CFMT_ASHIFT     0   
+#define SV_CFMT_CSHIFT     4
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define SV_CENABLE_PPE     0x4
+#define SV_CENABLE_RE      0x2
+#define SV_CENABLE_PE      0x1
+
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 2
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define FMODE_DMFM 0x10
+
+/* --------------------------------------------------------------------- */
+
+struct sv_state {
+	/* magic */
+	unsigned int magic;
+
+	/* list of sonicvibes devices */
+	struct list_head devs;
+
+	/* the corresponding pci_dev structure */
+	struct pci_dev *dev;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_midi;
+	int dev_dmfm;
+
+	/* hardware resources */
+	unsigned long iosb, ioenh, iosynth, iomidi;  /* long for SPARC */
+	unsigned int iodmaa, iodmac, irq;
+
+        /* mixer stuff */
+        struct {
+                unsigned int modcnt;
+#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS
+		unsigned short vol[13];
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+        } mix;
+
+	/* wave stuff */
+	unsigned int rateadc, ratedac;
+	unsigned char fmt, enable;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		dma_addr_t dmaaddr;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned enabled:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		struct timer_list timer;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+
+	struct gameport gameport;
+};
+
+/* --------------------------------------------------------------------- */
+
+static LIST_HEAD(devs);
+static unsigned long wavetable_mem = 0;
+
+/* --------------------------------------------------------------------- */
+
+static inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#ifdef hweight32
+#undef hweight32
+#endif
+
+static inline unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver.
+ */
+
+#undef DMABYTEIO
+
+static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmaa, u;
+
+	count--;
+	for (u = 4; u > 0; u--, addr >>= 8, io++)
+		outb(addr & 0xff, io);
+	for (u = 3; u > 0; u--, count >>= 8, io++)
+		outb(count & 0xff, io);
+#else /* DMABYTEIO */
+	count--;
+	outl(addr, s->iodmaa + SV_DMA_ADDR0);
+	outl(count, s->iodmaa + SV_DMA_COUNT0);
+#endif /* DMABYTEIO */
+	outb(0x18, s->iodmaa + SV_DMA_MODE);
+}
+
+static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmac, u;
+
+	count >>= 1;
+	count--;
+	for (u = 4; u > 0; u--, addr >>= 8, io++)
+		outb(addr & 0xff, io);
+	for (u = 3; u > 0; u--, count >>= 8, io++)
+		outb(count & 0xff, io);
+#else /* DMABYTEIO */
+	count >>= 1;
+	count--;
+	outl(addr, s->iodmac + SV_DMA_ADDR0);
+	outl(count, s->iodmac + SV_DMA_COUNT0);
+#endif /* DMABYTEIO */
+	outb(0x14, s->iodmac + SV_DMA_MODE);
+}
+
+static inline unsigned get_dmaa(struct sv_state *s)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmaa+6, v = 0, u;
+
+	for (u = 3; u > 0; u--, io--) {
+		v <<= 8;
+		v |= inb(io);
+	}
+	return v + 1;
+#else /* DMABYTEIO */
+	return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1;
+#endif /* DMABYTEIO */
+}
+
+static inline unsigned get_dmac(struct sv_state *s)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmac+6, v = 0, u;
+
+	for (u = 3; u > 0; u--, io--) {
+		v <<= 8;
+		v |= inb(io);
+	}
+	return (v + 1) << 1;
+#else /* DMABYTEIO */
+	return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1;
+#endif /* DMABYTEIO */
+}
+
+static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data)
+{
+	outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb(data, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+}
+
+static unsigned char rdindir(struct sv_state *s, unsigned char idx)
+{
+	unsigned char v;
+
+	outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	v = inb(s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	return v;
+}
+
+static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR);
+	if (mask) {
+		s->fmt = inb(s->ioenh + SV_CODEC_IDATA);
+		udelay(10);
+	}
+	s->fmt = (s->fmt & mask) | data;
+	outb(s->fmt, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	outb(0, s->ioenh + SV_CODEC_IADDR);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+}
+
+static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data)
+{
+	outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+}
+
+#define REFFREQUENCY  24576000
+#define ADCMULT 512
+#define FULLRATE 48000
+
+static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate)
+{
+	unsigned long flags;
+	unsigned char r, m=0, n=0;
+	unsigned xm, xn, xr, xd, metric = ~0U;
+	/* the warnings about m and n used uninitialized are bogus and may safely be ignored */
+
+	if (rate < 625000/ADCMULT)
+		rate = 625000/ADCMULT;
+	if (rate > 150000000/ADCMULT)
+		rate = 150000000/ADCMULT;
+	/* slight violation of specs, needed for continuous sampling rates */
+	for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1);
+	for (xn = 3; xn < 35; xn++)
+		for (xm = 3; xm < 130; xm++) {
+			xr = REFFREQUENCY/ADCMULT * xm / xn;
+			xd = abs((signed)(xr - rate));
+			if (xd < metric) {
+				metric = xd;
+				m = xm - 2;
+				n = xn - 2;
+			}
+		}
+	reg &= 0x3f;
+	spin_lock_irqsave(&s->lock, flags);
+	outb(reg, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb(m, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	outb(reg+1, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb(r | n, s->ioenh + SV_CODEC_IDATA);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+	return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7);
+}
+
+#if 0
+
+static unsigned getpll(struct sv_state *s, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char m, n;
+
+	reg &= 0x3f;
+	spin_lock_irqsave(&s->lock, flags);
+	outb(reg, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	m = inb(s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	outb(reg+1, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	n = inb(s->ioenh + SV_CODEC_IDATA);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+	return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7);
+}
+
+#endif
+
+static void set_dac_rate(struct sv_state *s, unsigned rate)
+{
+	unsigned div;
+	unsigned long flags;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+	div = (rate * 65536 + FULLRATE/2) / FULLRATE;
+	if (div > 65535)
+		div = 65535;
+	spin_lock_irqsave(&s->lock, flags);
+	wrindir(s, SV_CIPCMSR1, div >> 8);
+	wrindir(s, SV_CIPCMSR0, div);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->ratedac = (div * FULLRATE + 32768) / 65536;
+}
+
+static void set_adc_rate(struct sv_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned rate1, rate2, div;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+	rate1 = setpll(s, SV_CIADCPLLM, rate);
+	div = (48000 + rate/2) / rate;
+	if (div > 8)
+		div = 8;
+	rate2 = (48000 + div/2) / div;
+	spin_lock_irqsave(&s->lock, flags);
+	wrindir(s, SV_CIADCALTSR, (div-1) << 4);
+	if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) {
+		wrindir(s, SV_CIADCCLKSOURCE, 0x10);
+		s->rateadc = rate2;
+	} else {
+		wrindir(s, SV_CIADCCLKSOURCE, 0x00);
+		s->rateadc = rate1;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void stop_adc(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->enable &= ~SV_CENABLE_RE;
+	wrindir(s, SV_CIENABLE, s->enable);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static inline void stop_dac(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE);
+	wrindir(s, SV_CIENABLE, s->enable);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE;
+		wrindir(s, SV_CIENABLE, s->enable);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
+	    && s->dma_adc.ready) {
+		s->enable |= SV_CENABLE_RE;
+		wrindir(s, SV_CIENABLE, s->enable);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+static void dealloc_dmabuf(struct sv_state *s, struct dmabuf *db)
+{
+	struct page *page, *pend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_unreserve(page);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+
+/* DMAA is used for playback, DMAC is used for recording */
+
+static int prog_dmabuf(struct sv_state *s, unsigned rec)
+{
+	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+	unsigned rate = rec ? s->rateadc : s->ratedac;
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	struct page *page, *pend;
+	unsigned char fmt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	fmt = s->fmt;
+	if (rec) {
+		s->enable &= ~SV_CENABLE_RE;
+		fmt >>= SV_CFMT_CSHIFT;
+	} else {
+		s->enable &= ~SV_CENABLE_PE;
+		fmt >>= SV_CFMT_ASHIFT;
+	}
+	wrindir(s, SV_CIENABLE, s->enable);
+	spin_unlock_irqrestore(&s->lock, flags);
+	fmt &= SV_CFMT_MASK;
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+			if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
+				break;
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
+			printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
+			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+		if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
+			printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
+			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (page = virt_to_page(db->rawbuf); page <= pend; page++)
+			mem_map_reserve(page);
+	}
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
+	spin_lock_irqsave(&s->lock, flags);
+	if (rec) {
+		set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift);
+		/* program enhanced mode registers */
+		wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8);
+		wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1);
+	} else {
+		set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift);
+		/* program enhanced mode registers */
+		wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8);
+		wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	db->enabled = 1;
+	db->ready = 1;
+	return 0;
+}
+
+static inline void clear_advance(struct sv_state *s)
+{
+	unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80;
+	unsigned char *buf = s->dma_dac.rawbuf;
+	unsigned bsize = s->dma_dac.dmasize;
+	unsigned bptr = s->dma_dac.swptr;
+	unsigned len = s->dma_dac.fragsize;
+
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(buf + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void sv_update_ptr(struct sv_state *s)
+{
+	unsigned hwptr;
+	int diff;
+
+	/* update ADC pointer */
+	if (s->dma_adc.ready) {
+		hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize;
+		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+			wake_up(&s->dma_adc.wait);
+		if (!s->dma_adc.mapped) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				s->enable &= ~SV_CENABLE_RE;
+				wrindir(s, SV_CIENABLE, s->enable);
+				s->dma_adc.error++;
+			}
+		}
+	}
+	/* update DAC pointer */
+	if (s->dma_dac.ready) {
+		hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize;
+		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+		s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+				wake_up(&s->dma_dac.wait);
+		} else {
+			s->dma_dac.count -= diff;
+			if (s->dma_dac.count <= 0) {
+				s->enable &= ~SV_CENABLE_PE;
+				wrindir(s, SV_CIENABLE, s->enable);
+				s->dma_dac.error++;
+			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+				clear_advance(s);
+				s->dma_dac.endcleared = 1;
+			}
+			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
+				wake_up(&s->dma_dac.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void sv_handle_midi(struct sv_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x80)) {
+		ch = inb(s->iomidi);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->iomidi);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+}
+
+static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct sv_state *s = (struct sv_state *)dev_id;
+	unsigned int intsrc;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inb(s->ioenh + SV_CODEC_STATUS);
+	if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI)))
+		return;
+	spin_lock(&s->lock);
+	sv_update_ptr(s);
+	sv_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+static void sv_midi_timer(unsigned long data)
+{
+	struct sv_state *s = (struct sv_state *)data;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&s->lock, flags);
+	sv_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->midi.timer.expires = jiffies+1;
+	add_timer(&s->midi.timer);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != SV_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+#define MT_4          1
+#define MT_5MUTE      2
+#define MT_4MUTEMONO  3
+#define MT_6MUTE      4
+
+static const struct {
+	unsigned left:5;
+	unsigned right:5;
+	unsigned type:3;
+	unsigned rec:3;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL,    SV_CIMIX_ADCINR,    MT_4,         0 },
+	[SOUND_MIXER_LINE1]  = { SV_CIMIX_AUX1INL,   SV_CIMIX_AUX1INR,   MT_5MUTE,     5 },
+	[SOUND_MIXER_CD]     = { SV_CIMIX_CDINL,     SV_CIMIX_CDINR,     MT_5MUTE,     1 },
+	[SOUND_MIXER_LINE]   = { SV_CIMIX_LINEINL,   SV_CIMIX_LINEINR,   MT_5MUTE,     4 },
+	[SOUND_MIXER_MIC]    = { SV_CIMIX_MICIN,     SV_CIMIX_ADCINL,    MT_4MUTEMONO, 6 },
+	[SOUND_MIXER_SYNTH]  = { SV_CIMIX_SYNTHINL,  SV_CIMIX_SYNTHINR,  MT_5MUTE,     2 },
+	[SOUND_MIXER_LINE2]  = { SV_CIMIX_AUX2INL,   SV_CIMIX_AUX2INR,   MT_5MUTE,     3 },
+	[SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE,     7 },
+	[SOUND_MIXER_PCM]    = { SV_CIMIX_PCMINL,    SV_CIMIX_PCMINR,    MT_6MUTE,     0 }
+};
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+static int return_mixval(struct sv_state *s, unsigned i, int *arg)
+{
+	unsigned long flags;
+	unsigned char l, r, rl, rr;
+
+	spin_lock_irqsave(&s->lock, flags);
+	l = rdindir(s, mixtable[i].left);
+	r = rdindir(s, mixtable[i].right);
+	spin_unlock_irqrestore(&s->lock, flags);
+	switch (mixtable[i].type) {
+	case MT_4:
+		r &= 0xf;
+		l &= 0xf;
+		rl = 10 + 6 * (l & 15);
+		rr = 10 + 6 * (r & 15);
+		break;
+
+	case MT_4MUTEMONO:
+		rl = 55 - 3 * (l & 15);
+		if (r & 0x10)
+			rl += 45;
+		rr = rl;
+		r = l;
+		break;
+
+	case MT_5MUTE:
+	default:
+		rl = 100 - 3 * (l & 31);
+		rr = 100 - 3 * (r & 31);
+		break;
+				
+	case MT_6MUTE:
+		rl = 100 - 3 * (l & 63) / 2;
+		rr = 100 - 3 * (r & 63) / 2;
+		break;
+	}
+	if (l & 0x80)
+		rl = 0;
+	if (r & 0x80)
+		rr = 0;
+	return put_user((rr << 8) | rl, arg);
+}
+
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
+static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = 
+{
+	[SOUND_MIXER_RECLEV] = 1,
+	[SOUND_MIXER_LINE1]  = 2,
+	[SOUND_MIXER_CD]     = 3,
+	[SOUND_MIXER_LINE]   = 4,
+	[SOUND_MIXER_MIC]    = 5,
+	[SOUND_MIXER_SYNTH]  = 6,
+	[SOUND_MIXER_LINE2]  = 7,
+	[SOUND_MIXER_VOLUME] = 8,
+	[SOUND_MIXER_PCM]    = 9
+};
+
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
+static unsigned mixer_recmask(struct sv_state *s)
+{
+	unsigned long flags;
+	int i, j;
+
+	spin_lock_irqsave(&s->lock, flags);
+	j = rdindir(s, SV_CIMIX_ADCINL) >> 5;
+	spin_unlock_irqrestore(&s->lock, flags);
+	j &= 7;
+	for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++);
+	return 1 << i;
+}
+
+static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg)
+{
+	unsigned long flags;
+	int i, val;
+	unsigned char l, r, rl, rr;
+
+	VALIDATE_STATE(s);
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "SonicVibes", sizeof(info.id));
+		strncpy(info.name, "S3 SonicVibes", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "SonicVibes", sizeof(info.id));
+		strncpy(info.name, "S3 SonicVibes", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (cmd == SOUND_MIXER_PRIVATE1) {  /* SRS settings */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		spin_lock_irqsave(&s->lock, flags);
+		if (val & 1) {
+			if (val & 2) {
+				l = 4 - ((val >> 2) & 7);
+				if (l & ~3)
+					l = 4;
+				r = 4 - ((val >> 5) & 7);
+				if (r & ~3)
+					r = 4;
+				wrindir(s, SV_CISRSSPACE, l);
+				wrindir(s, SV_CISRSCENTER, r);
+			} else
+				wrindir(s, SV_CISRSSPACE, 0x80);
+		}
+		l = rdindir(s, SV_CISRSSPACE);
+		r = rdindir(s, SV_CISRSCENTER);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (l & 0x80)
+			return put_user(0, (int *)arg);
+		return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg);
+	}
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_SIOC_DIR(cmd) == _SIOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_recmask(s), (int *)arg);
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].rec)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+                                return -EINVAL;
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+			return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+			if (!volidx[i])
+				return -EINVAL;
+			return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+		}
+	}
+        if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		i = hweight32(val);
+		if (i == 0)
+			return 0; /*val = mixer_recmask(s);*/
+		else if (i > 1) 
+			val &= ~mixer_recmask(s);
+		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (mixtable[i].rec)
+				break;
+		}
+		if (!mixtable[i].rec)
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5);
+		frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+			return -EINVAL;
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		l = val & 0xff;
+		r = (val >> 8) & 0xff;
+		if (mixtable[i].type == MT_4MUTEMONO)
+			l = (r + l) / 2;
+		if (l > 100)
+			l = 100;
+		if (r > 100)
+			r = 100;
+		spin_lock_irqsave(&s->lock, flags);
+		switch (mixtable[i].type) {
+		case MT_4:
+			if (l >= 10)
+				l -= 10;
+			if (r >= 10)
+				r -= 10;
+			frobindir(s, mixtable[i].left, 0xf0, l / 6);
+			frobindir(s, mixtable[i].right, 0xf0, l / 6);
+			break;
+
+		case MT_4MUTEMONO:
+			rr = 0;
+			if (l < 10)
+				rl = 0x80;
+			else {
+				if (l >= 55) {
+					rr = 0x10;
+					l -= 45;
+				}
+				rl = (55 - l) / 3;
+			}
+			wrindir(s, mixtable[i].left, rl);
+			frobindir(s, mixtable[i].right, ~0x10, rr);
+			break;
+			
+		case MT_5MUTE:
+			if (l < 7)
+				rl = 0x80;
+			else
+				rl = (100 - l) / 3;
+			if (r < 7)
+				rr = 0x80;
+			else
+				rr = (100 - r) / 3;
+			wrindir(s, mixtable[i].left, rl);
+			wrindir(s, mixtable[i].right, rr);
+			break;
+				
+		case MT_6MUTE:
+			if (l < 6)
+				rl = 0x80;
+			else
+				rl = (100 - l) * 2 / 3;
+			if (r < 6)
+				rr = 0x80;
+			else
+				rr = (100 - r) * 2 / 3;
+			wrindir(s, mixtable[i].left, rl);
+			wrindir(s, mixtable[i].right, rr);
+			break;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+		if (!volidx[i])
+			return -EINVAL;
+		s->mix.vol[volidx[i]-1] = val;
+		return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sv_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct list_head *list;
+	struct sv_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (s->dev_mixer == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	return 0;
+}
+
+static int sv_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	return 0;
+}
+
+static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations sv_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		sv_ioctl_mixdev,
+	open:		sv_open_mixdev,
+	release:	sv_release_mixdev,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct sv_state *s, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac.mapped || !s->dma_dac.ready)
+		return 0;
+        add_wait_queue(&s->dma_dac.wait, &wait);
+        for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac.wait, &wait);
+                        set_current_state(TASK_RUNNING);
+                        return -EBUSY;
+                }
+		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac;
+		tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK];
+		if (!schedule_timeout(tmo + 1))
+			printk(KERN_DEBUG "sv: dma timed out??\n");
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+        set_current_state(TASK_RUNNING);
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+#if 0
+	spin_lock_irqsave(&s->lock, flags);
+	sv_update_ptr(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+#endif
+        add_wait_queue(&s->dma_adc.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_adc.enabled)
+				start_adc(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			if (!schedule_timeout(HZ)) {
+				printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, 
+				       s->dma_adc.hwptr, s->dma_adc.swptr);
+				stop_adc(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
+				/* program enhanced mode registers */
+				wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8);
+				wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1);
+				s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_adc.enabled)
+			start_adc(s);
+	}
+        remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+#if 0
+	spin_lock_irqsave(&s->lock, flags);
+	sv_update_ptr(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+#endif
+        add_wait_queue(&s->dma_dac.wait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize-swptr;
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (s->dma_dac.enabled)
+				start_dac(s);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			if (!schedule_timeout(HZ)) {
+				printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, 
+				       s->dma_dac.hwptr, s->dma_dac.swptr);
+				stop_dac(s);
+				spin_lock_irqsave(&s->lock, flags);
+				set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
+				/* program enhanced mode registers */
+				wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8);
+				wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1);
+				s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		if (s->dma_dac.enabled)
+			start_dac(s);
+	}
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		if (!s->dma_dac.ready && prog_dmabuf(s, 1))
+			return 0;
+		poll_wait(file, &s->dma_dac.wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!s->dma_adc.ready && prog_dmabuf(s, 0))
+			return 0;
+		poll_wait(file, &s->dma_adc.wait, wait);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	sv_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int sv_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	struct dmabuf *db;
+	int ret = -EINVAL;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(s, 1)) != 0)
+			goto out;
+		db = &s->dma_dac;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			goto out;
+		db = &s->dma_adc;
+	} else 
+		goto out;
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		goto out;
+	db->mapped = 1;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int count;
+	int val, mapped, ret;
+	unsigned char fmtm, fmtd;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate(s, val);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				set_dac_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		fmtd = 0;
+		fmtm = ~0;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			if (val)
+				fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT;
+			else
+				fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			if (val)
+				fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT;
+			else
+				fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT);
+		}
+		set_fmt(s, fmtm, fmtd);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT;
+				else
+					fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT;
+				else
+					fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) 
+					   : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != AFMT_QUERY) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT;
+				else
+					fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT;
+				else
+					fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) 
+					   : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+					return ret;
+				s->dma_adc.enabled = 1;
+				start_adc(s);
+			} else {
+				s->dma_adc.enabled = 0;
+				stop_adc(s);
+			}
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+					return ret;
+				s->dma_dac.enabled = 1;
+				start_dac(s);
+			} else {
+				s->dma_dac.enabled = 0;
+				stop_dac(s);
+			}
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+		abinfo.fragsize = s->dma_dac.fragsize;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = s->dma_dac.dmasize - count;
+                abinfo.fragstotal = s->dma_dac.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                abinfo.bytes = count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+                count = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (count < 0)
+			count = 0;
+		return put_user(count, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+		count = s->dma_adc.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+                cinfo.bytes = s->dma_dac.total_bytes;
+		count = s->dma_dac.count;
+		if (count < 0)
+			count = 0;
+                cinfo.blocks = count >> s->dma_dac.fragshift;
+                cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(s, 0)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf(s, 1)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.ossfragshift = val & 0xffff;
+			s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac.ossfragshift < 4)
+				s->dma_dac.ossfragshift = 4;
+			if (s->dma_dac.ossfragshift > 15)
+				s->dma_dac.ossfragshift = 15;
+			if (s->dma_dac.ossmaxfrags < 4)
+				s->dma_dac.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+			return -EINVAL;
+                if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) 
+					   : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) 
+					   : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int sv_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned char fmtm = ~0, fmts = 0;
+	struct list_head *list;
+	struct sv_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (file->f_mode & FMODE_READ) {
+		fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT;
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		s->dma_adc.enabled = 1;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT;
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+		s->dma_dac.enabled = 1;
+		set_dac_rate(s, 8000);
+	}
+	set_fmt(s, fmtm, fmts);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int sv_release(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		dealloc_dmabuf(s, &s->dma_dac);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(s, &s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations sv_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		sv_read,
+	write:		sv_write,
+	poll:		sv_poll,
+	ioctl:		sv_ioctl,
+	mmap:		sv_mmap,
+	open:		sv_open,
+	release:	sv_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+	add_wait_queue(&s->midi.iwait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		if (cnt <= 0)
+                      __set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+                      if (file->f_flags & O_NONBLOCK) {
+                              if (!ret)
+                                      ret = -EAGAIN;
+                              break;
+                      }
+                      schedule();
+                      if (signal_pending(current)) {
+                              if (!ret)
+                                      ret = -ERESTARTSYS;
+                              break;
+                      }
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		break;
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.iwait, &wait);
+	return ret;
+}
+
+static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	if (count == 0)
+		return 0;
+	ret = 0;
+        add_wait_queue(&s->midi.owait, &wait);
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			sv_handle_midi(s);
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&s->midi.owait, &wait);
+	return ret;
+}
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int sv_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	struct list_head *list;
+	struct sv_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (s->dev_midi == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		//outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL);
+		outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK);
+		wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */
+		wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */
+		outb(0xff, s->iomidi+1); /* reset command */
+		outb(0x3f, s->iomidi+1); /* uart command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		init_timer(&s->midi.timer);
+		s->midi.timer.expires = jiffies+1;
+		s->midi.timer.data = (unsigned long)s;
+		s->midi.timer.function = sv_midi_timer;
+		add_timer(&s->midi.timer);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	return 0;
+}
+
+static int sv_midi_release(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	lock_kernel();
+	if (file->f_mode & FMODE_WRITE) {
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				set_current_state(TASK_RUNNING);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				printk(KERN_DEBUG "sv: midi timed out??\n");
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		set_current_state(TASK_RUNNING);
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK);
+		del_timer(&s->midi.timer);		
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations sv_midi_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		sv_midi_read,
+	write:		sv_midi_write,
+	poll:		sv_midi_poll,
+	open:		sv_midi_open,
+	release:	sv_midi_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	static const unsigned char op_offset[18] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+	};
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	struct dm_fm_voice v;
+	struct dm_fm_note n;
+	struct dm_fm_params p;
+	unsigned int io;
+	unsigned int regb;
+
+	switch (cmd) {		
+	case FM_IOCTL_RESET:
+		for (regb = 0xb0; regb < 0xb9; regb++) {
+			outb(regb, s->iosynth);
+			outb(0, s->iosynth+1);
+			outb(regb, s->iosynth+2);
+			outb(0, s->iosynth+3);
+		}
+		return 0;
+
+	case FM_IOCTL_PLAY_NOTE:
+		if (copy_from_user(&n, (void *)arg, sizeof(n)))
+			return -EFAULT;
+		if (n.voice >= 18)
+			return -EINVAL;
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xa0 + regb, io);
+		outb(n.fnum & 0xff, io+1);
+		outb(0xb0 + regb, io);
+		outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+		return 0;
+
+	case FM_IOCTL_SET_VOICE:
+		if (copy_from_user(&v, (void *)arg, sizeof(v)))
+			return -EFAULT;
+		if (v.voice >= 18)
+			return -EINVAL;
+		regb = op_offset[v.voice];
+		io = s->iosynth + ((v.op & 1) << 1);
+		outb(0x20 + regb, io);
+		outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | 
+		     ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+		outb(0x40 + regb, io);
+		outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+		outb(0x60 + regb, io);
+		outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+		outb(0x80 + regb, io);
+		outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+		outb(0xe0 + regb, io);
+		outb(v.waveform & 0x7, io+1);
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xc0 + regb, io);
+		outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+		     (v.connection & 1), io+1);
+		return 0;
+		
+	case FM_IOCTL_SET_PARAMS:
+		if (copy_from_user(&p, (void *)arg, sizeof(p)))
+			return -EFAULT;
+		outb(0x08, s->iosynth);
+		outb((p.kbd_split & 1) << 6, s->iosynth+1);
+		outb(0xbd, s->iosynth);
+		outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+		     ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1);
+		return 0;
+
+	case FM_IOCTL_SET_OPL:
+		outb(4, s->iosynth+2);
+		outb(arg, s->iosynth+3);
+		return 0;
+
+	case FM_IOCTL_SET_MODE:
+		outb(5, s->iosynth+2);
+		outb(arg & 1, s->iosynth+3);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int sv_dmfm_open(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
+	struct list_head *list;
+	struct sv_state *s;
+
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (s->dev_dmfm == minor)
+			break;
+	}
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DMFM) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		up(&s->open_sem);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	/* init the stuff */
+	outb(1, s->iosynth);
+	outb(0x20, s->iosynth+1); /* enable waveforms */
+	outb(4, s->iosynth+2);
+	outb(0, s->iosynth+3);  /* no 4op enabled */
+	outb(5, s->iosynth+2);
+	outb(1, s->iosynth+3);  /* enable OPL3 */
+	s->open_mode |= FMODE_DMFM;
+	up(&s->open_sem);
+	return 0;
+}
+
+static int sv_dmfm_release(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned int regb;
+
+	VALIDATE_STATE(s);
+	lock_kernel();
+	down(&s->open_sem);
+	s->open_mode &= ~FMODE_DMFM;
+	for (regb = 0xb0; regb < 0xb9; regb++) {
+		outb(regb, s->iosynth);
+		outb(0, s->iosynth+1);
+		outb(regb, s->iosynth+2);
+		outb(0, s->iosynth+3);
+	}
+	wake_up(&s->open_wait);
+	up(&s->open_sem);
+	unlock_kernel();
+	return 0;
+}
+
+static /*const*/ struct file_operations sv_dmfm_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		sv_dmfm_ioctl,
+	open:		sv_dmfm_open,
+	release:	sv_dmfm_release,
+};
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices; only used for command line params */
+#define NR_DEVICE 5
+
+static int reverb[NR_DEVICE] = { 0, };
+
+#if 0
+static int wavetable[NR_DEVICE] = { 0, };
+#endif
+
+static unsigned int devindex = 0;
+
+MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM");
+#if 0
+MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled");
+#endif
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("S3 SonicVibes Driver");
+MODULE_LICENSE("GPL");
+
+
+/* --------------------------------------------------------------------- */
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_RECLEV, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE2, 0x4040 },
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 }
+};
+
+#define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \
+				 (pci_resource_flags((dev), (num)) & IORESOURCE_IO))
+
+static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
+{
+	static char __initdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller";
+       	struct sv_state *s;
+	mm_segment_t fs;
+	int i, val, ret;
+	char *ddmaname;
+	unsigned ddmanamelen;
+
+	if ((ret=pci_enable_device(pcidev)))
+		return ret;
+
+	if (!RSRCISIOREGION(pcidev, RESOURCE_SB) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_ENH) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_MIDI) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_GAME))
+		return -ENODEV;
+	if (pcidev->irq == 0)
+		return -ENODEV;
+	if (pci_set_dma_mask(pcidev, 0x00ffffff)) {
+		printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n");
+		return -ENODEV;
+	}
+	/* try to allocate a DDMA resource if not already available */
+	if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) {
+		pcidev->resource[RESOURCE_DDMA].start = 0;
+		pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1;
+		pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO;
+		ddmanamelen = strlen(sv_ddma_name)+1;
+		if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL)))
+			return -1;
+		memcpy(ddmaname, sv_ddma_name, ddmanamelen);
+		pcidev->resource[RESOURCE_DDMA].name = ddmaname;
+		if (pci_assign_resource(pcidev, RESOURCE_DDMA)) {
+			pcidev->resource[RESOURCE_DDMA].name = NULL;
+			kfree(ddmaname);
+			printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n");
+			return -EBUSY;
+		}
+	}
+	if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) {
+		printk(KERN_WARNING "sv: out of memory\n");
+		return -ENOMEM;
+	}
+	memset(s, 0, sizeof(struct sv_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+	s->magic = SV_MAGIC;
+	s->dev = pcidev;
+	s->iosb = pci_resource_start(pcidev, RESOURCE_SB);
+	s->ioenh = pci_resource_start(pcidev, RESOURCE_ENH);
+	s->iosynth = pci_resource_start(pcidev, RESOURCE_SYNTH);
+	s->iomidi = pci_resource_start(pcidev, RESOURCE_MIDI);
+	s->iodmaa = pci_resource_start(pcidev, RESOURCE_DDMA);
+	s->iodmac = pci_resource_start(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA;
+	s->gameport.io = pci_resource_start(pcidev, RESOURCE_GAME);
+	pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9);  /* enable and use extended mode */
+	pci_write_config_dword(pcidev, 0x48, s->iodmac | 9);  /* enable */
+	printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#x %#x %#x\n",
+	       s->iosb, s->ioenh, s->iosynth, s->iomidi, s->gameport.io, s->iodmaa, s->iodmac);
+	s->irq = pcidev->irq;
+	
+	/* hack */
+	pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12);  /* wavetable base address */
+
+	ret = -EBUSY;
+	if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) {
+		printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1);
+		goto err_region5;
+	}
+	if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) {
+		printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1);
+		goto err_region4;
+	}
+	if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) {
+		printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1);
+		goto err_region3;
+	}
+	if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) {
+		printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1);
+		goto err_region2;
+	}
+	if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) {
+		printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1);
+		goto err_region1;
+	}
+	if (s->gameport.io && !request_region(s->gameport.io, SV_EXTENT_GAME, "ESS Solo1")) {
+		printk(KERN_ERR "sv: gameport io ports in use\n");
+		s->gameport.io = 0;
+	}
+	/* initialize codec registers */
+	outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */
+	udelay(50);
+	outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */
+	udelay(50);
+	outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */
+	     | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL);
+	inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */
+	wrindir(s, SV_CIDRIVECONTROL, 0);  /* drive current 16mA */
+	wrindir(s, SV_CIENABLE, s->enable = 0);  /* disable DMAA and DMAC */
+	outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK);
+	/* outb(0xff, s->iodmaa + SV_DMA_RESET); */
+	/* outb(0xff, s->iodmac + SV_DMA_RESET); */
+	inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
+	wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */
+	wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */
+	wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */
+	setpll(s, SV_CIADCPLLM, 8000);
+	wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */
+	wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff);
+	wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff);
+	wrindir(s, SV_CIADCOUTPUT, 0);
+	/* request irq */
+	if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) {
+		printk(KERN_ERR "sv: irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+	printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n",
+	       s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
+	/* register devices */
+	if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) {
+		ret = s->dev_audio;
+		goto err_dev1;
+	}
+	if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) {
+		ret = s->dev_mixer;
+		goto err_dev2;
+	}
+	if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) {
+		ret = s->dev_midi;
+		goto err_dev3;
+	}
+	if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) {
+		ret = s->dev_dmfm;
+		goto err_dev4;
+	}
+	pci_set_master(pcidev);  /* enable bus mastering */
+	/* initialize the chips */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE|SOUND_MASK_SYNTH;
+	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+	for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+	}
+	set_fs(fs);
+	/* register gameport */
+	gameport_register_port(&s->gameport);
+	/* store it in the driver field */
+	pci_set_drvdata(pcidev, s);
+	/* put it into driver list */
+	list_add_tail(&s->devs, &devs);
+	/* increment devindex */
+	if (devindex < NR_DEVICE-1)
+		devindex++;
+	return 0;
+
+ err_dev4:
+	unregister_sound_midi(s->dev_midi);
+ err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR "sv: cannot register misc device\n");
+	free_irq(s->irq, s);
+ err_irq:
+	if (s->gameport.io)
+		release_region(s->gameport.io, SV_EXTENT_GAME);
+	release_region(s->iosynth, SV_EXTENT_SYNTH);
+ err_region1:
+	release_region(s->iomidi, SV_EXTENT_MIDI);
+ err_region2:
+	release_region(s->iodmac, SV_EXTENT_DMA);
+ err_region3:
+	release_region(s->iodmaa, SV_EXTENT_DMA);
+ err_region4:
+	release_region(s->ioenh, SV_EXTENT_ENH);
+ err_region5:
+	kfree(s);
+	return ret;
+}
+
+static void __devinit sv_remove(struct pci_dev *dev)
+{
+	struct sv_state *s = pci_get_drvdata(dev);
+
+	if (!s)
+		return;
+	list_del(&s->devs);
+	outb(~0, s->ioenh + SV_CODEC_INTMASK);  /* disable ints */
+	synchronize_irq();
+	inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
+	wrindir(s, SV_CIENABLE, 0);     /* disable DMAA and DMAC */
+	/*outb(0, s->iodmaa + SV_DMA_RESET);*/
+	/*outb(0, s->iodmac + SV_DMA_RESET);*/
+	free_irq(s->irq, s);
+	if (s->gameport.io) {
+		gameport_unregister_port(&s->gameport);
+		release_region(s->gameport.io, SV_EXTENT_GAME);
+	}
+	release_region(s->iodmac, SV_EXTENT_DMA);
+	release_region(s->iodmaa, SV_EXTENT_DMA);
+	release_region(s->ioenh, SV_EXTENT_ENH);
+	release_region(s->iomidi, SV_EXTENT_MIDI);
+	release_region(s->iosynth, SV_EXTENT_SYNTH);
+	unregister_sound_dsp(s->dev_audio);
+	unregister_sound_mixer(s->dev_mixer);
+	unregister_sound_midi(s->dev_midi);
+	unregister_sound_special(s->dev_dmfm);
+	kfree(s);
+	pci_set_drvdata(dev, NULL);
+}
+
+static struct pci_device_id id_table[] __devinitdata = {
+       { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver sv_driver = {
+       name: "sonicvibes",
+       id_table: id_table,
+       probe: sv_probe,
+       remove: sv_remove
+};
+ 
+static int __init init_sonicvibes(void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "sv: version v0.30 time " __TIME__ " " __DATE__ "\n");
+#if 0
+	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
+		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+#endif
+	return pci_module_init(&sv_driver);
+}
+
+static void __exit cleanup_sonicvibes(void)
+{
+	printk(KERN_INFO "sv: unloading\n");
+	pci_unregister_driver(&sv_driver);
+ 	if (wavetable_mem)
+		free_pages(wavetable_mem, 20-PAGE_SHIFT);
+}
+
+module_init(init_sonicvibes);
+module_exit(cleanup_sonicvibes);
+
+/* --------------------------------------------------------------------- */
+
+#ifndef MODULE
+
+/* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */
+
+static int __init sonicvibes_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= NR_DEVICE)
+		return 0;
+#if 0
+	if (get_option(&str, &reverb[nr_dev]) == 2)
+		(void)get_option(&str, &wavetable[nr_dev]);
+#else
+	(void)get_option(&str, &reverb[nr_dev]);
+#endif
+
+	nr_dev++;
+	return 1;
+}
+
+__setup("sonicvibes=", sonicvibes_setup);
+
+#endif /* MODULE */
diff -Nru linux/sound/oss/sound_calls.h linux-2.4.19-pre5-mjc/sound/oss/sound_calls.h
--- linux/sound/oss/sound_calls.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sound_calls.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,91 @@
+/*
+ *	DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
+int DMAbuf_rmchars(int dev, int buff_no, int c);
+int DMAbuf_start_output(int dev, int buff_no, int l);
+int DMAbuf_move_wrpointer(int dev, int l);
+/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */
+void DMAbuf_init(int dev, int dma1, int dma2);
+void DMAbuf_deinit(int dev);
+int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+int DMAbuf_open_dma (int dev);
+void DMAbuf_close_dma (int dev);
+void DMAbuf_inputintr(int dev);
+void DMAbuf_outputintr(int dev, int underflow_flag);
+struct dma_buffparms;
+int DMAbuf_space_in_queue (int dev);
+int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
+int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
+unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait);
+void DMAbuf_start_devices(unsigned int devmask);
+void DMAbuf_reset (int dev);
+int DMAbuf_sync (int dev);
+
+/*
+ *	System calls for /dev/dsp and /dev/audio (audio.c)
+ */
+
+int audio_read (int dev, struct file *file, char *buf, int count);
+int audio_write (int dev, struct file *file, const char *buf, int count);
+int audio_open (int dev, struct file *file);
+void audio_release (int dev, struct file *file);
+int audio_ioctl (int dev, struct file *file,
+	   unsigned int cmd, caddr_t arg);
+void audio_init_devices (void);
+void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
+int dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
+
+/*
+ *	System calls for the /dev/sequencer
+ */
+
+int sequencer_read (int dev, struct file *file, char *buf, int count);
+int sequencer_write (int dev, struct file *file, const char *buf, int count);
+int sequencer_open (int dev, struct file *file);
+void sequencer_release (int dev, struct file *file);
+int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg);
+unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait);
+
+void sequencer_init (void);
+void sequencer_unload (void);
+void sequencer_timer(unsigned long dummy);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
+			       int vibrato_bend);
+void seq_input_event(unsigned char *event, int len);
+void seq_copy_to_input (unsigned char *event, int len);
+
+/*
+ *	System calls for the /dev/midi
+ */
+
+int MIDIbuf_read (int dev, struct file *file, char *buf, int count);
+int MIDIbuf_write (int dev, struct file *file, const char *buf, int count);
+int MIDIbuf_open (int dev, struct file *file);
+void MIDIbuf_release (int dev, struct file *file);
+int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg);
+unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait);
+int MIDIbuf_avail(int dev);
+
+void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
+void MIDIbuf_init(void);
+
+
+/*	From soundcard.c	*/
+void request_sound_timer (int count);
+void sound_stop_timer(void);
+void conf_printf(char *name, struct address_info *hw_config);
+void conf_printf2(char *name, int base, int irq, int dma, int dma2);
+
+/*	From sound_timer.c */
+void sound_timer_interrupt(void);
+void sound_timer_syncinterval(unsigned int new_usecs);
+
+/*      From midi_synth.c       */
+void do_midi_msg (int synthno, unsigned char *msg, int mlen);
diff -Nru linux/sound/oss/sound_config.h linux-2.4.19-pre5-mjc/sound/oss/sound_config.h
--- linux/sound/oss/sound_config.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sound_config.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,154 @@
+/* sound_config.h
+ *
+ * A driver for sound cards, misc. configuration parameters.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+#ifndef  _SOUND_CONFIG_H_
+#define  _SOUND_CONFIG_H_
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/sound.h>
+
+#include "os.h"
+#include "soundvers.h"
+
+
+#ifndef SND_DEFAULT_ENABLE
+#define SND_DEFAULT_ENABLE	1
+#endif
+
+#ifndef MAX_REALTIME_FACTOR
+#define MAX_REALTIME_FACTOR	4
+#endif
+
+/*
+ * Use always 64k buffer size. There is no reason to use shorter.
+ */
+#undef DSP_BUFFSIZE
+#define DSP_BUFFSIZE		(64*1024)
+
+#ifndef DSP_BUFFCOUNT
+#define DSP_BUFFCOUNT		1	/* 1 is recommended. */
+#endif
+
+#define FM_MONO		0x388	/* This is the I/O address used by AdLib */
+
+#ifndef CONFIG_PAS_BASE
+#define CONFIG_PAS_BASE	0x388
+#endif
+
+/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
+   driver. (There is no need to alter this) */
+#define SEQ_MAX_QUEUE	1024
+
+#define SBFM_MAXINSTR		(256)	/* Size of the FM Instrument bank */
+/* 128 instruments for general MIDI setup and 16 unassigned	 */
+
+#define SND_NDEVS	256	/* Number of supported devices */
+
+#define DSP_DEFAULT_SPEED	8000
+
+#define MAX_AUDIO_DEV	5
+#define MAX_MIXER_DEV	5
+#define MAX_SYNTH_DEV	5
+#define MAX_MIDI_DEV	6
+#define MAX_TIMER_DEV	4
+
+struct address_info {
+	int io_base;
+	int irq;
+	int dma;
+	int dma2;
+	int always_detect;	/* 1=Trust me, it's there */
+	char *name;
+	int driver_use_1;	/* Driver defined field 1 */
+	int driver_use_2;	/* Driver defined field 2 */
+	int *osp;	/* OS specific info */
+	int card_subtype;	/* Driver specific. Usually 0 */
+	void *memptr;           /* Module memory chainer */
+	int slots[6];           /* To remember driver slot ids */
+};
+
+#define SYNTH_MAX_VOICES	32
+
+struct voice_alloc_info {
+		int max_voice;
+		int used_voices;
+		int ptr;		/* For device specific use */
+		unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+		int timestamp;
+		int alloc_times[SYNTH_MAX_VOICES];
+	};
+
+struct channel_info {
+		int pgm_num;
+		int bender_value;
+		int bender_range;
+		unsigned char controllers[128];
+	};
+
+/*
+ * Process wakeup reasons
+ */
+#define WK_NONE		0x00
+#define WK_WAKEUP	0x01
+#define WK_TIMEOUT	0x02
+#define WK_SIGNAL	0x04
+#define WK_SLEEP	0x08
+#define WK_SELECT	0x10
+#define WK_ABORT	0x20
+
+#define OPEN_READ	PCM_ENABLE_INPUT
+#define OPEN_WRITE	PCM_ENABLE_OUTPUT
+#define OPEN_READWRITE	(OPEN_READ|OPEN_WRITE)
+
+#if OPEN_READ == FMODE_READ && OPEN_WRITE == FMODE_WRITE
+
+extern __inline__ int translate_mode(struct file *file)
+{
+	return file->f_mode;
+}
+
+#else
+
+extern __inline__ int translate_mode(struct file *file)
+{
+	return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) |
+		((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0);
+}
+
+#endif
+
+
+#include "sound_calls.h"
+#include "dev_table.h"
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+#ifndef DDB
+#define DDB(x) {}
+#endif
+
+#ifndef MDB
+#ifdef MODULE
+#define MDB(x) x
+#else
+#define MDB(x)
+#endif
+#endif
+
+#define TIMER_ARMED	121234
+#define TIMER_NOT_ARMED	1
+
+#endif
diff -Nru linux/sound/oss/sound_firmware.h linux-2.4.19-pre5-mjc/sound/oss/sound_firmware.h
--- linux/sound/oss/sound_firmware.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sound_firmware.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2 @@
+extern int mod_firmware_load(const char *fn, char **fp);
+
diff -Nru linux/sound/oss/sound_syms.c linux-2.4.19-pre5-mjc/sound/oss/sound_syms.c
--- linux/sound/oss/sound_syms.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sound_syms.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,53 @@
+/*
+ *	The sound core exports the following symbols to the rest of
+ *	modulespace.
+ *
+ *      (C) Copyright 1997      Alan Cox, Licensed under the GNU GPL
+ *
+ *	Thu May 27 1999 Andrew J. Kroll <ag784@freenet..buffalo..edu>
+ *	left out exported symbol... fixed
+ */
+
+#include <linux/module.h>
+#include "sound_config.h"
+#include "sound_calls.h"
+
+char sound_syms_symbol;
+
+EXPORT_SYMBOL(mixer_devs);
+EXPORT_SYMBOL(audio_devs);
+EXPORT_SYMBOL(num_mixers);
+EXPORT_SYMBOL(num_audiodevs);
+
+EXPORT_SYMBOL(midi_devs);
+EXPORT_SYMBOL(num_midis);
+EXPORT_SYMBOL(synth_devs);
+EXPORT_SYMBOL(num_synths);
+
+EXPORT_SYMBOL(sound_timer_devs);
+EXPORT_SYMBOL(num_sound_timers);
+
+EXPORT_SYMBOL(sound_install_audiodrv);
+EXPORT_SYMBOL(sound_install_mixer);
+EXPORT_SYMBOL(sound_alloc_dma);
+EXPORT_SYMBOL(sound_free_dma);
+EXPORT_SYMBOL(sound_open_dma);
+EXPORT_SYMBOL(sound_close_dma);
+EXPORT_SYMBOL(sound_alloc_audiodev);
+EXPORT_SYMBOL(sound_alloc_mididev);
+EXPORT_SYMBOL(sound_alloc_mixerdev);
+EXPORT_SYMBOL(sound_alloc_timerdev);
+EXPORT_SYMBOL(sound_alloc_synthdev);
+EXPORT_SYMBOL(sound_unload_audiodev);
+EXPORT_SYMBOL(sound_unload_mididev);
+EXPORT_SYMBOL(sound_unload_mixerdev);
+EXPORT_SYMBOL(sound_unload_timerdev);
+EXPORT_SYMBOL(sound_unload_synthdev);
+
+EXPORT_SYMBOL(load_mixer_volumes);
+
+EXPORT_SYMBOL(conf_printf);
+EXPORT_SYMBOL(conf_printf2);
+
+MODULE_DESCRIPTION("OSS Sound subsystem");
+MODULE_AUTHOR("Hannu Savolainen, et al.");
diff -Nru linux/sound/oss/sound_timer.c linux-2.4.19-pre5-mjc/sound/oss/sound_timer.c
--- linux/sound/oss/sound_timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sound_timer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,318 @@
+/*
+ * sound/sound_timer.c
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
+#include <linux/string.h>
+
+
+#include "sound_config.h"
+
+static volatile int initialized, opened, tmr_running;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+static volatile unsigned long usecs_per_tmr;	/* Length of the current interval */
+
+static struct sound_lowlev_timer *tmr;
+
+static unsigned long tmr2ticks(int tmr_value)
+{
+	/*
+	 *    Convert timer ticks to MIDI ticks
+	 */
+
+	unsigned long tmp;
+	unsigned long scale;
+
+	tmp = tmr_value * usecs_per_tmr;	/* Convert to usecs */
+	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
+	return (tmp + (scale / 2)) / scale;
+}
+
+void reprogram_timer(void)
+{
+	unsigned long   usecs_per_tick;
+
+	/*
+	 *	The user is changing the timer rate before setting a timer
+	 *	slap, bad bad not allowed.
+	 */
+	 
+	if(!tmr)
+		return;
+		
+	usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+
+	/*
+	 * Don't kill the system by setting too high timer rate
+	 */
+	if (usecs_per_tick < 2000)
+		usecs_per_tick = 2000;
+
+	usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
+}
+
+void sound_timer_syncinterval(unsigned int new_usecs)
+{
+	/*
+	 *    This routine is called by the hardware level if
+	 *      the clock frequency has changed for some reason.
+	 */
+	tmr_offs = tmr_ctr;
+	ticks_offs += tmr2ticks(tmr_ctr);
+	tmr_ctr = 0;
+	usecs_per_tmr = new_usecs;
+}
+
+static void tmr_reset(void)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	tmr_offs = 0;
+	ticks_offs = 0;
+	tmr_ctr = 0;
+	next_event_time = (unsigned long) -1;
+	prev_event_time = 0;
+	curr_ticks = 0;
+	restore_flags(flags);
+}
+
+static int timer_open(int dev, int mode)
+{
+	if (opened)
+		return -EBUSY;
+	tmr_reset();
+	curr_tempo = 60;
+	curr_timebase = 100;
+	opened = 1;
+	reprogram_timer();
+	return 0;
+}
+
+static void timer_close(int dev)
+{
+	opened = tmr_running = 0;
+	tmr->tmr_disable(tmr->dev);
+}
+
+static int timer_event(int dev, unsigned char *event)
+{
+	unsigned char cmd = event[1];
+	unsigned long parm = *(int *) &event[4];
+
+	switch (cmd)
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				if (parm <= curr_ticks)	/* It's the time */
+					return TIMER_NOT_ARMED;
+				time = parm;
+				next_event_time = prev_event_time = time;
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			tmr_reset();
+			tmr_running = 1;
+			reprogram_timer();
+			break;
+
+		case TMR_STOP:
+			tmr_running = 0;
+			break;
+
+		case TMR_CONTINUE:
+			tmr_running = 1;
+			reprogram_timer();
+			break;
+
+		case TMR_TEMPO:
+			if (parm)
+			{
+				if (parm < 8)
+					parm = 8;
+				if (parm > 250)
+					parm = 250;
+				tmr_offs = tmr_ctr;
+				ticks_offs += tmr2ticks(tmr_ctr);
+				tmr_ctr = 0;
+				curr_tempo = parm;
+				reprogram_timer();
+			}
+			break;
+
+		case TMR_ECHO:
+			seq_copy_to_input(event, 8);
+			break;
+
+		default:;
+	}
+	return TIMER_NOT_ARMED;
+}
+
+static unsigned long timer_get_time(int dev)
+{
+	if (!opened)
+		return 0;
+	return curr_ticks;
+}
+
+static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int val;
+
+	switch (cmd) 
+	{
+		case SNDCTL_TMR_SOURCE:
+			val = TMR_INTERNAL;
+			break;
+
+		case SNDCTL_TMR_START:
+			tmr_reset();
+			tmr_running = 1;
+			return 0;
+		
+		case SNDCTL_TMR_STOP:
+			tmr_running = 0;
+			return 0;
+
+		case SNDCTL_TMR_CONTINUE:
+			tmr_running = 1;
+			return 0;
+
+		case SNDCTL_TMR_TIMEBASE:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val) 
+			{
+				if (val < 1)
+					val = 1;
+				if (val > 1000)
+					val = 1000;
+				curr_timebase = val;
+			}
+			val = curr_timebase;
+			break;
+
+		case SNDCTL_TMR_TEMPO:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val) 
+			{
+				if (val < 8)
+					val = 8;
+				if (val > 250)
+					val = 250;
+				tmr_offs = tmr_ctr;
+				ticks_offs += tmr2ticks(tmr_ctr);
+				tmr_ctr = 0;
+				curr_tempo = val;
+				reprogram_timer();
+			}
+			val = curr_tempo;
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val != 0)	/* Can't change */
+				return -EINVAL;
+			val = ((curr_tempo * curr_timebase) + 30) / 60;
+			break;
+		
+		case SNDCTL_SEQ_GETTIME:
+			val = curr_ticks;
+			break;
+		
+		case SNDCTL_TMR_METRONOME:
+		default:
+			return -EINVAL;
+	}
+	return put_user(val, (int *)arg);
+}
+
+static void timer_arm(int dev, long time)
+{
+	if (time < 0)
+		time = curr_ticks + 1;
+	else if (time <= curr_ticks)	/* It's the time */
+		return;
+
+	next_event_time = prev_event_time = time;
+	return;
+}
+
+static struct sound_timer_operations sound_timer =
+{
+	owner:		THIS_MODULE,
+	info:		{"Sound Timer", 0},
+	priority:	1,	/* Priority */
+	devlink:	0,	/* Local device link */
+	open:		timer_open,
+	close:		timer_close,
+	event:		timer_event,
+	get_time:	timer_get_time,
+	ioctl:		timer_ioctl,
+	arm_timer:	timer_arm
+};
+
+void sound_timer_interrupt(void)
+{
+	if (!opened)
+		return;
+
+	tmr->tmr_restart(tmr->dev);
+
+	if (!tmr_running)
+		return;
+
+	tmr_ctr++;
+	curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
+
+	if (curr_ticks >= next_event_time)
+	{
+		next_event_time = (unsigned long) -1;
+		sequencer_timer(0);
+	}
+}
+
+void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
+{
+	int n;
+
+	if (initialized)
+	{
+		if (t->priority <= tmr->priority)
+			return;	/* There is already a similar or better timer */
+		tmr = t;
+		return;
+	}
+	initialized = 1;
+	tmr = t;
+
+	n = sound_alloc_timerdev();
+	if (n == -1)
+		n = 0;		/* Overwrite the system timer */
+	strcpy(sound_timer.info.name, name);
+	sound_timer_devs[n] = &sound_timer;
+}
diff -Nru linux/sound/oss/soundcard.c linux-2.4.19-pre5-mjc/sound/oss/soundcard.c
--- linux/sound/oss/soundcard.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/soundcard.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,782 @@
+/*
+ * linux/drivers/sound/soundcard.c
+ *
+ * Sound card driver for Linux
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer     : ioctl code reworked (vmalloc/vfree removed)
+ *                   integrated sound_switch.c
+ * Stefan Reinauer   : integrated /proc/sound (equals to /dev/sndstat,
+ *                   which should disappear in the near future)
+ * Eric Dumas	     : devfs support (22-Jan-98) <dumas@linux.eu.org> with
+ *                   fixups by C. Scott Ananian <cananian@alumni.princeton.edu>
+ * Richard Gooch     : moved common (non OSS-specific) devices to sound_core.c
+ * Rob Riggs	     : Added persistent DMA buffers support (1998/10/17)
+ * Christoph Hellwig : Some cleanup work (2000/03/01)
+ */
+
+#include <linux/config.h>
+
+#include "sound_config.h"
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kmod.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+
+/*
+ * This ought to be moved into include/asm/dma.h
+ */
+#ifndef valid_dma
+#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4)
+#endif
+
+/*
+ * Table for permanently allocated memory (used when unloading the module)
+ */
+caddr_t         sound_mem_blocks[1024];
+int             sound_nblocks = 0;
+
+/* Persistent DMA buffers */
+#ifdef CONFIG_SOUND_ALSA_DMAP
+int             sound_dmap_flag = 1;
+#else
+int             sound_dmap_flag = 0;
+#endif
+
+static char     dma_alloc_map[MAX_DMA_CHANNELS] = {0};
+
+#define DMA_MAP_UNAVAIL		0
+#define DMA_MAP_FREE		1
+#define DMA_MAP_BUSY		2
+
+
+unsigned long seq_time = 0;	/* Time for /dev/sequencer */
+
+/*
+ * Table for configurable mixer volume handling
+ */
+static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
+static int num_mixer_volumes = 0;
+
+int *load_mixer_volumes(char *name, int *levels, int present)
+{
+	int             i, n;
+
+	for (i = 0; i < num_mixer_volumes; i++) {
+		if (strcmp(name, mixer_vols[i].name) == 0) {
+			if (present)
+				mixer_vols[i].num = i;
+			return mixer_vols[i].levels;
+		}
+	}
+	if (num_mixer_volumes >= MAX_MIXER_DEV) {
+		printk(KERN_ERR "Sound: Too many mixers (%s)\n", name);
+		return levels;
+	}
+	n = num_mixer_volumes++;
+
+	strcpy(mixer_vols[n].name, name);
+
+	if (present)
+		mixer_vols[n].num = n;
+	else
+		mixer_vols[n].num = -1;
+
+	for (i = 0; i < 32; i++)
+		mixer_vols[n].levels[i] = levels[i];
+	return mixer_vols[n].levels;
+}
+
+static int set_mixer_levels(caddr_t arg)
+{
+        /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
+	mixer_vol_table buf;   
+
+	if (__copy_from_user(&buf, arg, sizeof(buf)))
+		return -EFAULT;
+	load_mixer_volumes(buf.name, buf.levels, 0);
+	if (__copy_to_user(arg, &buf, sizeof(buf)))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_mixer_levels(caddr_t arg)
+{
+	int n;
+
+	if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num))))
+		return -EFAULT;
+	if (n < 0 || n >= num_mixer_volumes)
+		return -EINVAL;
+	if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table)))
+		return -EFAULT;
+	return 0;
+}
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	int dev = minor(file->f_dentry->d_inode->i_rdev);
+	int ret = -EINVAL;
+
+	/*
+	 *	The OSS drivers aren't remotely happy without this locking,
+	 *	and unless someone fixes them when they are about to bite the
+	 *	big one anyway, we might as well bandage here..
+	 */
+	 
+	lock_kernel();
+	
+	DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		ret = audio_read(dev, file, buf, count);
+		break;
+
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		ret = sequencer_read(dev, file, buf, count);
+		break;
+
+	case SND_DEV_MIDIN:
+		ret = MIDIbuf_read(dev, file, buf, count);
+	}
+	unlock_kernel();
+	return ret;
+}
+
+static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	int dev = minor(file->f_dentry->d_inode->i_rdev);
+	int ret = -EINVAL;
+	
+	lock_kernel();
+	DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		ret =  sequencer_write(dev, file, buf, count);
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		ret = audio_write(dev, file, buf, count);
+		break;
+
+	case SND_DEV_MIDIN:
+		ret =  MIDIbuf_write(dev, file, buf, count);
+		break;
+	}
+	unlock_kernel();
+	return ret;
+}
+
+static int sound_open(struct inode *inode, struct file *file)
+{
+	int dev = minor(inode->i_rdev);
+	int retval;
+
+	DEB(printk("sound_open(dev=%d)\n", dev));
+	if ((dev >= SND_NDEVS) || (dev < 0)) {
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		dev >>= 4;
+		if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
+			char modname[20];
+			sprintf(modname, "mixer%d", dev);
+			request_module(modname);
+		}
+		if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
+			return -ENXIO;
+
+		if (mixer_devs[dev]->owner)
+			__MOD_INC_USE_COUNT (mixer_devs[dev]->owner);
+		break;
+
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		if ((retval = sequencer_open(dev, file)) < 0)
+			return retval;
+		break;
+
+	case SND_DEV_MIDIN:
+		if ((retval = MIDIbuf_open(dev, file)) < 0)
+			return retval;
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		if ((retval = audio_open(dev, file)) < 0)
+			return retval;
+		break;
+
+	default:
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int sound_release(struct inode *inode, struct file *file)
+{
+	int dev = minor(inode->i_rdev);
+
+	lock_kernel();
+	DEB(printk("sound_release(dev=%d)\n", dev));
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		dev >>= 4;
+		if (mixer_devs[dev]->owner)
+			__MOD_DEC_USE_COUNT (mixer_devs[dev]->owner);
+		break;
+		
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		sequencer_release(dev, file);
+		break;
+
+	case SND_DEV_MIDIN:
+		MIDIbuf_release(dev, file);
+		break;
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		audio_release(dev, file);
+		break;
+
+	default:
+		printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
+	}
+	unlock_kernel();
+
+	return 0;
+}
+
+static int get_mixer_info(int dev, caddr_t arg)
+{
+	mixer_info info;
+
+	strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
+	strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
+	info.name[sizeof(info.name)-1] = 0;
+	info.modify_counter = mixer_devs[dev]->modify_counter;
+	if (__copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_old_mixer_info(int dev, caddr_t arg)
+{
+	_old_mixer_info info;
+
+ 	strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
+ 	strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
+ 	info.name[sizeof(info.name)-1] = 0;	
+ 	if (copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg)
+{
+ 	if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
+ 		return -ENXIO;
+ 	/* Try to load the mixer... */
+ 	if (mixer_devs[mixdev] == NULL) {
+ 		char modname[20];
+ 		sprintf(modname, "mixer%d", mixdev);
+ 		request_module(modname);
+ 	}
+ 	if (mixdev >= num_mixers || !mixer_devs[mixdev])
+ 		return -ENXIO;
+	if (cmd == SOUND_MIXER_INFO)
+		return get_mixer_info(mixdev, arg);
+	if (cmd == SOUND_OLD_MIXER_INFO)
+		return get_old_mixer_info(mixdev, arg);
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		mixer_devs[mixdev]->modify_counter++;
+	if (!mixer_devs[mixdev]->ioctl)
+		return -EINVAL;
+	return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
+}
+
+static int sound_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	int err, len = 0, dtype;
+	int dev = minor(inode->i_rdev);
+
+	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
+		/*
+		 * Have to validate the address given by the process.
+		 */
+		len = _SIOC_SIZE(cmd);
+		if (len < 1 || len > 65536 || arg == 0)
+			return -EFAULT;
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+			if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0)
+				return err;
+		if (_SIOC_DIR(cmd) & _SIOC_READ)
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0)
+				return err;
+	}
+	DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+	if (cmd == OSS_GETVERSION)
+		return __put_user(SOUND_VERSION, (int *)arg);
+	
+	if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 &&   /* Mixer ioctl */
+	    (dev & 0x0f) != SND_DEV_CTL) {              
+		dtype = dev & 0x0f;
+		switch (dtype) {
+		case SND_DEV_DSP:
+		case SND_DEV_DSP16:
+		case SND_DEV_AUDIO:
+			return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
+						 cmd, (caddr_t)arg);
+			
+		default:
+			return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg);
+		}
+	}
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		if (cmd == SOUND_MIXER_GETLEVELS)
+			return get_mixer_levels((caddr_t)arg);
+		if (cmd == SOUND_MIXER_SETLEVELS)
+			return set_mixer_levels((caddr_t)arg);
+		return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg);
+
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_ioctl(dev, file, cmd, (caddr_t)arg);
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_ioctl(dev, file, cmd, (caddr_t)arg);
+		break;
+
+	case SND_DEV_MIDIN:
+		return MIDIbuf_ioctl(dev, file, cmd, (caddr_t)arg);
+		break;
+
+	}
+	return -EINVAL;
+}
+
+static unsigned int sound_poll(struct file *file, poll_table * wait)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	int dev = minor(inode->i_rdev);
+
+	DEB(printk("sound_poll(dev=%d)\n", dev));
+	switch (dev & 0x0f) {
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_poll(dev, file, wait);
+
+	case SND_DEV_MIDIN:
+		return MIDIbuf_poll(dev, file, wait);
+
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return DMAbuf_poll(file, dev >> 4, wait);
+	}
+	return 0;
+}
+
+static int sound_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	int dev_class;
+	unsigned long size;
+	struct dma_buffparms *dmap = NULL;
+	int dev = minor(file->f_dentry->d_inode->i_rdev);
+
+	dev_class = dev & 0x0f;
+	dev >>= 4;
+
+	if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) {
+		printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
+		return -EINVAL;
+	}
+	lock_kernel();
+	if (vma->vm_flags & VM_WRITE)	/* Map write and read/write to the output buf */
+		dmap = audio_devs[dev]->dmap_out;
+	else if (vma->vm_flags & VM_READ)
+		dmap = audio_devs[dev]->dmap_in;
+	else {
+		printk(KERN_ERR "Sound: Undefined mmap() access\n");
+		unlock_kernel();
+		return -EINVAL;
+	}
+
+	if (dmap == NULL) {
+		printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
+		unlock_kernel();
+		return -EIO;
+	}
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
+		unlock_kernel();
+		return -EIO;
+	}
+	if (dmap->mapping_flags) {
+		printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
+		unlock_kernel();
+		return -EIO;
+	}
+	if (vma->vm_pgoff != 0) {
+		printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
+		unlock_kernel();
+		return -EINVAL;
+	}
+	size = vma->vm_end - vma->vm_start;
+
+	if (size != dmap->bytes_in_use) {
+		printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
+	}
+	if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf),
+		vma->vm_end - vma->vm_start,
+		vma->vm_page_prot)) {
+		unlock_kernel();
+		return -EAGAIN;
+	}
+
+	dmap->mapping_flags |= DMA_MAP_MAPPED;
+
+	if( audio_devs[dev]->d->mmap)
+		audio_devs[dev]->d->mmap(dev);
+
+	memset(dmap->raw_buf,
+	       dmap->neutral_byte,
+	       dmap->bytes_in_use);
+	unlock_kernel();
+	return 0;
+}
+
+struct file_operations oss_sound_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		sound_read,
+	write:		sound_write,
+	poll:		sound_poll,
+	ioctl:		sound_ioctl,
+	mmap:		sound_mmap,
+	open:		sound_open,
+	release:	sound_release,
+};
+
+/*
+ *	Create the required special subdevices
+ */
+ 
+static int create_special_devices(void)
+{
+	int seq1,seq2;
+	seq1=register_sound_special(&oss_sound_fops, 1);
+	if(seq1==-1)
+		goto bad;
+	seq2=register_sound_special(&oss_sound_fops, 8);
+	if(seq2!=-1)
+		return 0;
+	unregister_sound_special(1);
+bad:
+	return -1;
+}
+
+
+/* These device names follow the official Linux device list,
+ * Documentation/devices.txt.  Let us know if there are other
+ * common names we should support for compatibility.
+ * Only those devices not created by the generic code in sound_core.c are
+ * registered here.
+ */
+static const struct {
+	unsigned short minor;
+	char *name;
+	umode_t mode;
+	int *num;
+} dev_list[] = { /* list of minor devices */
+/* seems to be some confusion here -- this device is not in the device list */
+	{SND_DEV_DSP16,     "dspW",	 S_IWUGO | S_IRUSR | S_IRGRP,
+	 &num_audiodevs},
+	{SND_DEV_AUDIO,     "audio",	 S_IWUGO | S_IRUSR | S_IRGRP,
+	 &num_audiodevs},
+};
+
+static char * 
+soundcard_make_name(char *buf, char *name, int idx) {
+	if (idx==0)
+		sprintf(buf, "sound/%s", name);
+	else
+		sprintf(buf, "sound/%s%d", name, idx);
+	return buf;
+}
+	
+/* Register/unregister audio entries */
+static void soundcard_register_devfs (int do_register)
+{
+	char name_buf[32];
+	int i, j, num;
+
+	for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) {
+		num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num;
+		for (j = 0; j < num || j == 0; j++) {
+			soundcard_make_name (name_buf, dev_list[i].name, j);
+			if (do_register)
+				devfs_register (NULL, name_buf, DEVFS_FL_NONE,
+					SOUND_MAJOR, dev_list[i].minor+ (j* 0x10),
+					S_IFCHR | dev_list[i].mode,
+					&oss_sound_fops, NULL);
+			else {
+				devfs_handle_t de;
+				
+				de = devfs_find_handle (NULL, name_buf, 0, 0,
+					DEVFS_SPECIAL_CHR, 0);
+				devfs_unregister (de);
+			}
+		}
+	}
+}
+
+
+static int dmabuf = 0;
+static int dmabug = 0;
+
+MODULE_PARM(dmabuf, "i");
+MODULE_PARM(dmabug, "i");
+
+static int __init oss_init(void)
+{
+	int             err;
+	
+	/* drag in sound_syms.o */
+	{
+		extern char sound_syms_symbol;
+		sound_syms_symbol = 0;
+	}
+
+#ifdef CONFIG_PCI
+	if(dmabug)
+		isa_dma_bridge_buggy = dmabug;
+#endif
+
+	err = create_special_devices();
+	if (err) {
+		printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
+		return err;
+	}
+
+	/* Protecting the innocent */
+	sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
+
+	soundcard_register_devfs(1);
+
+	if (sound_nblocks >= 1024)
+		printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
+	
+	return 0;
+}
+
+static void __exit oss_cleanup(void)
+{
+	int i;
+
+	if (MOD_IN_USE)
+		return;
+
+	soundcard_register_devfs (0);
+	
+	unregister_sound_special(1);
+	unregister_sound_special(8);
+
+	sound_stop_timer();
+
+	sequencer_unload();
+
+	for (i = 0; i < MAX_DMA_CHANNELS; i++)
+		if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) {
+			printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i);
+			sound_free_dma(i);
+		}
+
+	for (i = 0; i < sound_nblocks; i++)
+		vfree(sound_mem_blocks[i]);
+
+}
+
+module_init(oss_init);
+module_exit(oss_cleanup);
+MODULE_LICENSE("GPL");
+
+
+int sound_alloc_dma(int chn, char *deviceID)
+{
+	int err;
+
+	if ((err = request_dma(chn, deviceID)) != 0)
+		return err;
+
+	dma_alloc_map[chn] = DMA_MAP_FREE;
+
+	return 0;
+}
+
+int sound_open_dma(int chn, char *deviceID)
+{
+	unsigned long   flags;
+
+	if (!valid_dma(chn)) {
+		printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn);
+		return 1;
+	}
+	save_flags(flags);
+	cli();
+
+	if (dma_alloc_map[chn] != DMA_MAP_FREE) {
+		printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
+		restore_flags(flags);
+		return 1;
+	}
+	dma_alloc_map[chn] = DMA_MAP_BUSY;
+	restore_flags(flags);
+	return 0;
+}
+
+void sound_free_dma(int chn)
+{
+	if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) {
+		/* printk( "sound_free_dma: Bad access to DMA channel %d\n",  chn); */
+		return;
+	}
+	free_dma(chn);
+	dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
+}
+
+void sound_close_dma(int chn)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+
+	if (dma_alloc_map[chn] != DMA_MAP_BUSY) {
+		printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn);
+		restore_flags(flags);
+		return;
+	}
+	dma_alloc_map[chn] = DMA_MAP_FREE;
+	restore_flags(flags);
+}
+
+static void do_sequencer_timer(unsigned long dummy)
+{
+	sequencer_timer(0);
+}
+
+
+static struct timer_list seq_timer =
+{function: do_sequencer_timer};
+
+void request_sound_timer(int count)
+{
+	extern unsigned long seq_time;
+
+	if (count < 0) {
+		seq_timer.expires = (-count) + jiffies;
+		add_timer(&seq_timer);
+		return;
+	}
+	count += seq_time;
+
+	count -= jiffies;
+
+	if (count < 1)
+		count = 1;
+
+	seq_timer.expires = (count) + jiffies;
+	add_timer(&seq_timer);
+}
+
+void sound_stop_timer(void)
+{
+	del_timer(&seq_timer);;
+}
+
+void conf_printf(char *name, struct address_info *hw_config)
+{
+#ifndef CONFIG_SOUND_ALSA_TRACEINIT
+	return;
+#else
+	printk("<%s> at 0x%03x", name, hw_config->io_base);
+
+	if (hw_config->irq)
+		printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
+
+	if (hw_config->dma != -1 || hw_config->dma2 != -1)
+	{
+		printk(" dma %d", hw_config->dma);
+		if (hw_config->dma2 != -1)
+			printk(",%d", hw_config->dma2);
+	}
+	printk("\n");
+#endif
+}
+
+void conf_printf2(char *name, int base, int irq, int dma, int dma2)
+{
+#ifndef CONFIG_SOUND_ALSA_TRACEINIT
+	return;
+#else
+	printk("<%s> at 0x%03x", name, base);
+
+	if (irq)
+		printk(" irq %d", (irq > 0) ? irq : -irq);
+
+	if (dma != -1 || dma2 != -1)
+	{
+		  printk(" dma %d", dma);
+		  if (dma2 != -1)
+			  printk(",%d", dma2);
+	}
+	printk("\n");
+#endif
+}
diff -Nru linux/sound/oss/soundvers.h linux-2.4.19-pre5-mjc/sound/oss/soundvers.h
--- linux/sound/oss/soundvers.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/soundvers.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2 @@
+#define SOUND_VERSION_STRING "3.8s2++-971130"
+#define SOUND_INTERNAL_VERSION 0x030804
diff -Nru linux/sound/oss/sscape.c linux-2.4.19-pre5-mjc/sound/oss/sscape.c
--- linux/sound/oss/sscape.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sscape.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1530 @@
+/*
+ * sound/sscape.c
+ *
+ * Low level driver for Ensoniq SoundScape
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ *
+ * Thomas Sailer   	: ioctl code reworked (vmalloc/vfree removed)
+ * Sergey Smitienko	: ensoniq p'n'p support
+ * Christoph Hellwig	: adapted to module_init/module_exit
+ * Bartlomiej Zolnierkiewicz : added __init to attach_sscape()
+ * Chris Rankin		: Specify that this module owns the coprocessor
+ * Arnaldo C. de Melo	: added missing restore_flags in sscape_pnp_upload_file
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+#include "sound_firmware.h"
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kmod.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/wrapper.h>
+
+#include "coproc.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+/*
+ *    I/O ports
+ */
+#define MIDI_DATA       0
+#define MIDI_CTRL       1
+#define HOST_CTRL       2
+#define TX_READY	0x02
+#define RX_READY	0x01
+#define HOST_DATA       3
+#define ODIE_ADDR       4
+#define ODIE_DATA       5
+
+/*
+ *    Indirect registers
+ */
+
+#define GA_INTSTAT_REG	0
+#define GA_INTENA_REG	1
+#define GA_DMAA_REG	2
+#define GA_DMAB_REG	3
+#define GA_INTCFG_REG	4
+#define GA_DMACFG_REG	5
+#define GA_CDCFG_REG	6
+#define GA_SMCFGA_REG	7
+#define GA_SMCFGB_REG	8
+#define GA_HMCTL_REG	9
+
+/*
+ * DMA channel identifiers (A and B)
+ */
+
+#define SSCAPE_DMA_A	0
+#define SSCAPE_DMA_B	1
+
+#define PORT(name)	(devc->base+name)
+
+/*
+ * Host commands recognized by the OBP microcode
+ */
+ 
+#define CMD_GEN_HOST_ACK	0x80
+#define CMD_GEN_MPU_ACK		0x81
+#define CMD_GET_BOARD_TYPE	0x82
+#define CMD_SET_CONTROL		0x88	/* Old firmware only */
+#define CMD_GET_CONTROL		0x89	/* Old firmware only */
+#define CTL_MASTER_VOL		0
+#define CTL_MIC_MODE		2
+#define CTL_SYNTH_VOL		4
+#define CTL_WAVE_VOL		7
+#define CMD_SET_EXTMIDI		0x8a
+#define CMD_GET_EXTMIDI		0x8b
+#define CMD_SET_MT32		0x8c
+#define CMD_GET_MT32		0x8d
+
+#define CMD_ACK			0x80
+
+#define	IC_ODIE			1
+#define	IC_OPUS			2
+
+typedef struct sscape_info
+{
+	int	base, irq, dma;
+	
+	int	codec, codec_irq;	/* required to setup pnp cards*/
+	int	codec_type;
+	int	ic_type;
+	char*	raw_buf;
+	unsigned long	raw_buf_phys;
+	int	buffsize;		/* -------------------------- */
+	
+	int	ok;	/* Properly detected */
+	int	failed;
+	int	dma_allocated;
+	int	codec_audiodev;
+	int	opened;
+	int	*osp;
+	int	my_audiodev;
+} sscape_info;
+
+static struct sscape_info adev_info = {
+	0
+};
+
+static struct sscape_info *devc = &adev_info;
+static int sscape_mididev = -1;
+
+/* Some older cards have assigned interrupt bits differently than new ones */
+static char valid_interrupts_old[] = {
+	9, 7, 5, 15
+};
+
+static char valid_interrupts_new[] = {
+	9, 5, 7, 10
+};
+
+static char *valid_interrupts = valid_interrupts_new;
+
+/*
+ *	See the bottom of the driver. This can be set by spea =0/1.
+ */
+ 
+#ifdef REVEAL_SPEA
+static char old_hardware = 1;
+#else
+static char old_hardware = 0;
+#endif
+
+static void sleep(unsigned howlong)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(howlong);
+}
+
+static unsigned char sscape_read(struct sscape_info *devc, int reg)
+{
+	unsigned long flags;
+	unsigned char val;
+
+	save_flags(flags);
+	cli();
+	outb(reg, PORT(ODIE_ADDR));
+	val = inb(PORT(ODIE_DATA));
+	restore_flags(flags);
+	return val;
+}
+
+static void sscape_write(struct sscape_info *devc, int reg, int data)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb(reg, PORT(ODIE_ADDR));
+	outb(data, PORT(ODIE_DATA));
+	restore_flags(flags);
+}
+
+static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg)
+{
+	unsigned char res;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();	
+	outb( reg, devc -> codec);
+	res = inb (devc -> codec + 1);
+	restore_flags(flags);
+	return res;
+
+}
+
+static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	outb( reg, devc -> codec);
+	outb( data, devc -> codec + 1);
+	restore_flags(flags);
+}
+
+static void host_open(struct sscape_info *devc)
+{
+	outb((0x00), PORT(HOST_CTRL));	/* Put the board to the host mode */
+}
+
+static void host_close(struct sscape_info *devc)
+{
+	outb((0x03), PORT(HOST_CTRL));	/* Put the board to the MIDI mode */
+}
+
+static int host_write(struct sscape_info *devc, unsigned char *data, int count)
+{
+	unsigned long flags;
+	int i, timeout_val;
+
+	save_flags(flags);
+	cli();
+
+	/*
+	 * Send the command and data bytes
+	 */
+
+	for (i = 0; i < count; i++)
+	{
+		for (timeout_val = 10000; timeout_val > 0; timeout_val--)
+			if (inb(PORT(HOST_CTRL)) & TX_READY)
+				break;
+
+		if (timeout_val <= 0)
+		{
+			    restore_flags(flags);
+			    return 0;
+		}
+		outb(data[i], PORT(HOST_DATA));
+	}
+	restore_flags(flags);
+	return 1;
+}
+
+static int host_read(struct sscape_info *devc)
+{
+	unsigned long flags;
+	int timeout_val;
+	unsigned char data;
+
+	save_flags(flags);
+	cli();
+
+	/*
+	 * Read a byte
+	 */
+
+	for (timeout_val = 10000; timeout_val > 0; timeout_val--)
+		if (inb(PORT(HOST_CTRL)) & RX_READY)
+			break;
+
+	if (timeout_val <= 0)
+	{
+		restore_flags(flags);
+		return -1;
+	}
+	data = inb(PORT(HOST_DATA));
+	restore_flags(flags);
+	return data;
+}
+
+#if 0 /* unused */
+static int host_command1(struct sscape_info *devc, int cmd)
+{
+	unsigned char buf[10];
+	buf[0] = (unsigned char) (cmd & 0xff);
+	return host_write(devc, buf, 1);
+}
+#endif /* unused */
+
+
+static int host_command2(struct sscape_info *devc, int cmd, int parm1)
+{
+	unsigned char buf[10];
+
+	buf[0] = (unsigned char) (cmd & 0xff);
+	buf[1] = (unsigned char) (parm1 & 0xff);
+
+	return host_write(devc, buf, 2);
+}
+
+static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2)
+{
+	unsigned char buf[10];
+
+	buf[0] = (unsigned char) (cmd & 0xff);
+	buf[1] = (unsigned char) (parm1 & 0xff);
+	buf[2] = (unsigned char) (parm2 & 0xff);
+	return host_write(devc, buf, 3);
+}
+
+static void set_mt32(struct sscape_info *devc, int value)
+{
+	host_open(devc);
+	host_command2(devc, CMD_SET_MT32, value ? 1 : 0);
+	if (host_read(devc) != CMD_ACK)
+	{
+		/* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
+	}
+	host_close(devc);
+}
+
+static void set_control(struct sscape_info *devc, int ctrl, int value)
+{
+	host_open(devc);
+	host_command3(devc, CMD_SET_CONTROL, ctrl, value);
+	if (host_read(devc) != CMD_ACK)
+	{
+		/* printk( "SNDSCAPE: Setting control (%d) failed\n",  ctrl); */
+	}
+	host_close(devc);
+}
+
+static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
+{
+	unsigned char temp;
+
+	if (dma_chan != SSCAPE_DMA_A)
+	{
+		printk(KERN_WARNING "soundscape: Tried to use DMA channel  != A. Why?\n");
+		return;
+	}
+	audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
+	DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode);
+	audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
+
+	temp = devc->dma << 4;	/* Setup DMA channel select bits */
+	if (devc->dma <= 3)
+		temp |= 0x80;	/* 8 bit DMA channel */
+
+	temp |= 1;		/* Trigger DMA */
+	sscape_write(devc, GA_DMAA_REG, temp);
+	temp &= 0xfe;		/* Clear DMA trigger */
+	sscape_write(devc, GA_DMAA_REG, temp);
+}
+
+static int verify_mpu(struct sscape_info *devc)
+{
+	/*
+	 * The SoundScape board could be in three modes (MPU, 8250 and host).
+	 * If the card is not in the MPU mode, enabling the MPU driver will
+	 * cause infinite loop (the driver believes that there is always some
+	 * received data in the buffer.
+	 *
+	 * Detect this by looking if there are more than 10 received MIDI bytes
+	 * (0x00) in the buffer.
+	 */
+
+	int i;
+
+	for (i = 0; i < 10; i++)
+	{
+		if (inb(devc->base + HOST_CTRL) & 0x80)
+			return 1;
+
+		if (inb(devc->base) != 0x00)
+			return 1;
+	}
+	printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n");
+	return 0;
+}
+
+static int sscape_coproc_open(void *dev_info, int sub_device)
+{
+	if (sub_device == COPR_MIDI)
+	{
+		set_mt32(devc, 0);
+		if (!verify_mpu(devc))
+			return -EIO;
+	}
+	return 0;
+}
+
+static void sscape_coproc_close(void *dev_info, int sub_device)
+{
+	struct sscape_info *devc = dev_info;
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	if (devc->dma_allocated)
+	{
+		sscape_write(devc, GA_DMAA_REG, 0x20);	/* DMA channel disabled */
+		devc->dma_allocated = 0;
+	}
+	restore_flags(flags);
+	return;
+}
+
+static void sscape_coproc_reset(void *dev_info)
+{
+}
+
+static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag)
+{
+	unsigned long flags;
+	unsigned char temp;
+	volatile int done, timeout_val;
+	static unsigned char codec_dma_bits = 0;
+
+	if (flag & CPF_FIRST)
+	{
+		/*
+		 * First block. Have to allocate DMA and to reset the board
+		 * before continuing.
+		 */
+
+		save_flags(flags);
+		cli();
+		codec_dma_bits = sscape_read(devc, GA_CDCFG_REG);
+
+		if (devc->dma_allocated == 0)
+			devc->dma_allocated = 1;
+
+		restore_flags(flags);
+
+		sscape_write(devc, GA_HMCTL_REG, 
+			(temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f);	/*Reset */
+
+		for (timeout_val = 10000; timeout_val > 0; timeout_val--)
+			sscape_read(devc, GA_HMCTL_REG);	/* Delay */
+
+		/* Take board out of reset */
+		sscape_write(devc, GA_HMCTL_REG,
+			(temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80);
+	}
+	/*
+	 * Transfer one code block using DMA
+	 */
+	if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
+	{
+		printk(KERN_WARNING "soundscape: DMA buffer not available\n");
+		return 0;
+	}
+	memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
+
+	save_flags(flags);
+	cli();
+	
+	/******** INTERRUPTS DISABLED NOW ********/
+	
+	do_dma(devc, SSCAPE_DMA_A,
+	       audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
+	       size, DMA_MODE_WRITE);
+
+	/*
+	 * Wait until transfer completes.
+	 */
+	
+	done = 0;
+	timeout_val = 30;
+	while (!done && timeout_val-- > 0)
+	{
+		int resid;
+
+		if (HZ / 50)
+			sleep(HZ / 50);
+		clear_dma_ff(devc->dma);
+		if ((resid = get_dma_residue(devc->dma)) == 0)
+			done = 1;
+	}
+
+	restore_flags(flags);
+	if (!done)
+		return 0;
+
+	if (flag & CPF_LAST)
+	{
+		/*
+		 * Take the board out of reset
+		 */
+		outb((0x00), PORT(HOST_CTRL));
+		outb((0x00), PORT(MIDI_CTRL));
+
+		temp = sscape_read(devc, GA_HMCTL_REG);
+		temp |= 0x40;
+		sscape_write(devc, GA_HMCTL_REG, temp);	/* Kickstart the board */
+
+		/*
+		 * Wait until the ODB wakes up
+		 */
+
+		save_flags(flags);
+		cli();
+		done = 0;
+		timeout_val = 5 * HZ;
+		while (!done && timeout_val-- > 0)
+		{
+			unsigned char x;
+			
+			sleep(1);
+			x = inb(PORT(HOST_DATA));
+			if (x == 0xff || x == 0xfe)		/* OBP startup acknowledge */
+			{
+				DDB(printk("Soundscape: Acknowledge = %x\n", x));
+				done = 1;
+			}
+		}
+		sscape_write(devc, GA_CDCFG_REG, codec_dma_bits);
+
+		restore_flags(flags);
+		if (!done)
+		{
+			printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n");
+			return 0;
+		}
+		save_flags(flags);
+		cli();
+		done = 0;
+		timeout_val = 5 * HZ;
+		while (!done && timeout_val-- > 0)
+		{
+			sleep(1);
+			if (inb(PORT(HOST_DATA)) == 0xfe)	/* Host startup acknowledge */
+				done = 1;
+		}
+		restore_flags(flags);
+		if (!done)
+		{
+			printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
+			return 0;
+		}
+		printk(KERN_INFO "SoundScape board initialized OK\n");
+		set_control(devc, CTL_MASTER_VOL, 100);
+		set_control(devc, CTL_SYNTH_VOL, 100);
+
+#ifdef SSCAPE_DEBUG3
+		/*
+		 * Temporary debugging aid. Print contents of the registers after
+		 * downloading the code.
+		 */
+		{
+			int i;
+
+			for (i = 0; i < 13; i++)
+				printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
+		}
+#endif
+
+	}
+	return 1;
+}
+
+static int download_boot_block(void *dev_info, copr_buffer * buf)
+{
+	if (buf->len <= 0 || buf->len > sizeof(buf->data))
+		return -EINVAL;
+
+	if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags))
+	{
+		printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
+{
+	copr_buffer *buf;
+	int err;
+
+	switch (cmd) 
+	{
+		case SNDCTL_COPR_RESET:
+			sscape_coproc_reset(dev_info);
+			return 0;
+
+		case SNDCTL_COPR_LOAD:
+			buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+			if (buf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(buf, arg, sizeof(copr_buffer))) 
+			{
+				vfree(buf);
+				return -EFAULT;
+			}
+			err = download_boot_block(dev_info, buf);
+			vfree(buf);
+			return err;
+		
+		default:
+			return -EINVAL;
+	}
+}
+
+static coproc_operations sscape_coproc_operations =
+{
+	"SoundScape M68K",
+	THIS_MODULE,
+	sscape_coproc_open,
+	sscape_coproc_close,
+	sscape_coproc_ioctl,
+	sscape_coproc_reset,
+	&adev_info
+};
+
+static int sscape_detected = 0;
+static int sscape_is_pnp   = 0;
+
+void __init attach_sscape(struct address_info *hw_config)
+{
+#ifndef SSCAPE_REGS
+	/*
+	 * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
+	 * These values are card
+	 * dependent. If you have another SoundScape based card, you have to
+	 * find the correct values. Do the following:
+	 *  - Compile this driver with SSCAPE_DEBUG1 defined.
+	 *  - Shut down and power off your machine.
+	 *  - Boot with DOS so that the SSINIT.EXE program is run.
+	 *  - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
+	 *    when detecting the SoundScape.
+	 *  - Modify the following list to use the values printed during boot.
+	 *    Undefine the SSCAPE_DEBUG1
+	 */
+#define SSCAPE_REGS { \
+/* I0 */	0x00, \
+/* I1 */	0xf0, /* Note! Ignored. Set always to 0xf0 */ \
+/* I2 */	0x20, /* Note! Ignored. Set always to 0x20 */ \
+/* I3 */	0x20, /* Note! Ignored. Set always to 0x20 */ \
+/* I4 */	0xf5, /* Ignored */ \
+/* I5 */	0x10, \
+/* I6 */	0x00, \
+/* I7 */	0x2e, /* I7 MEM config A. Likely to vary between models */ \
+/* I8 */	0x00, /* I8 MEM config B. Likely to vary between models */ \
+/* I9 */	0x40 /* Ignored */ \
+	}
+#endif
+
+	unsigned long   flags;
+	static unsigned char regs[10] = SSCAPE_REGS;
+
+	int i, irq_bits = 0xff;
+
+	if (sscape_detected != hw_config->io_base)
+		return;
+
+	request_region(devc->base + 2, 6, "SoundScape");
+	if (old_hardware)
+	{
+		valid_interrupts = valid_interrupts_old;
+		conf_printf("Ensoniq SoundScape (old)", hw_config);
+	}
+	else
+		conf_printf("Ensoniq SoundScape", hw_config);
+
+	for (i = 0; i < sizeof(valid_interrupts); i++)
+	{
+		if (hw_config->irq == valid_interrupts[i])
+		{
+			irq_bits = i;
+			break;
+		}
+	}
+	if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
+	{
+		printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq);
+		return;
+	}
+	
+	if (!sscape_is_pnp) {
+	
+	    save_flags(flags);
+	    cli();
+	    for (i = 1; i < 10; i++)
+	    {
+		switch (i)
+		{
+			case 1:	/* Host interrupt enable */
+				sscape_write(devc, i, 0xf0);	/* All interrupts enabled */
+				break;
+
+			case 2:	/* DMA A status/trigger register */
+			case 3:	/* DMA B status/trigger register */
+				sscape_write(devc, i, 0x20);	/* DMA channel disabled */
+				break;
+
+			case 4:	/* Host interrupt config reg */
+				sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits);
+				break;
+
+			case 5:	/* Don't destroy CD-ROM DMA config bits (0xc0) */
+				sscape_write(devc, i, (regs[i] & 0x3f) | (sscape_read(devc, i) & 0xc0));
+				break;
+
+			case 6:	/* CD-ROM config (WSS codec actually) */
+				sscape_write(devc, i, regs[i]);
+				break;
+
+			case 9:	/* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
+				sscape_write(devc, i, (sscape_read(devc, i) & 0xf0) | 0x08);
+				break;
+
+			default:
+				sscape_write(devc, i, regs[i]);
+		}
+	    }
+	    restore_flags(flags);
+	}
+#ifdef SSCAPE_DEBUG2
+	/*
+	 * Temporary debugging aid. Print contents of the registers after
+	 * changing them.
+	 */
+	{
+		int i;
+
+		for (i = 0; i < 13; i++)
+			printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
+	}
+#endif
+
+	if (probe_mpu401(hw_config))
+		hw_config->always_detect = 1;
+	hw_config->name = "SoundScape";
+
+	hw_config->irq *= -1;	/* Negative value signals IRQ sharing */
+	attach_mpu401(hw_config, THIS_MODULE);
+	hw_config->irq *= -1;	/* Restore it */
+
+	if (hw_config->slots[1] != -1)	/* The MPU driver installed itself */
+	{
+		sscape_mididev = hw_config->slots[1];
+		midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations;
+	}
+	sscape_write(devc, GA_INTENA_REG, 0x80);	/* Master IRQ enable */
+	devc->ok = 1;
+	devc->failed = 0;
+}
+
+static int detect_ga(sscape_info * devc)
+{
+	unsigned char save;
+
+	DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base));
+
+	if (check_region(devc->base, 8))
+		return 0;
+
+	/*
+	 * First check that the address register of "ODIE" is
+	 * there and that it has exactly 4 writable bits.
+	 * First 4 bits
+	 */
+	
+	if ((save = inb(PORT(ODIE_ADDR))) & 0xf0)
+	{
+		DDB(printk("soundscape: Detect error A\n"));
+		return 0;
+	}
+	outb((0x00), PORT(ODIE_ADDR));
+	if (inb(PORT(ODIE_ADDR)) != 0x00)
+	{
+		DDB(printk("soundscape: Detect error B\n"));
+		return 0;
+	}
+	outb((0xff), PORT(ODIE_ADDR));
+	if (inb(PORT(ODIE_ADDR)) != 0x0f)
+	{
+		DDB(printk("soundscape: Detect error C\n"));
+		return 0;
+	}
+	outb((save), PORT(ODIE_ADDR));
+
+	/*
+	 * Now verify that some indirect registers return zero on some bits.
+	 * This may break the driver with some future revisions of "ODIE" but...
+	 */
+
+	if (sscape_read(devc, 0) & 0x0c)
+	{
+		DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0)));
+		return 0;
+	}
+	if (sscape_read(devc, 1) & 0x0f)
+	{
+		DDB(printk("soundscape: Detect error E\n"));
+		return 0;
+	}
+	if (sscape_read(devc, 5) & 0x0f)
+	{
+		DDB(printk("soundscape: Detect error F\n"));
+		return 0;
+	}
+	return 1;
+}
+
+static	int sscape_read_host_ctrl(sscape_info* devc)
+{
+	return host_read(devc);
+}
+
+static	void sscape_write_host_ctrl2(sscape_info *devc, int a, int b)
+{
+	host_command2(devc, a, b);
+}
+
+static int sscape_alloc_dma(sscape_info *devc)
+{
+	char *start_addr, *end_addr;
+	int dma_pagesize;
+	int sz, size;
+	struct page *page;
+
+	if (devc->raw_buf != NULL) return 0;	/* Already done */
+	dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024);
+	devc->raw_buf = NULL;
+	devc->buffsize = 8192*4;
+	if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize;
+	start_addr = NULL;
+	/*
+	 * Now loop until we get a free buffer. Try to get smaller buffer if
+	 * it fails. Don't accept smaller than 8k buffer for performance
+	 * reasons.
+	 */
+	while (start_addr == NULL && devc->buffsize > PAGE_SIZE) {
+		for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
+		devc->buffsize = PAGE_SIZE * (1 << sz);
+		start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
+		if (start_addr == NULL) devc->buffsize /= 2;
+	}
+
+	if (start_addr == NULL) {
+		printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n");
+		return 0;
+	} else {
+		/* make some checks */
+		end_addr = start_addr + devc->buffsize - 1;		
+		/* now check if it fits into the same dma-pagesize */
+
+		if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
+		    || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
+			printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize);
+			return 0;
+		}
+	}
+	devc->raw_buf = start_addr;
+	devc->raw_buf_phys = virt_to_bus(start_addr);
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		mem_map_reserve(page);
+	return 1;
+}
+
+static void sscape_free_dma(sscape_info *devc)
+{
+	int sz, size;
+	unsigned long start_addr, end_addr;
+	struct page *page;
+
+	if (devc->raw_buf == NULL) return;
+	for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1);
+	start_addr = (unsigned long) devc->raw_buf;
+	end_addr = start_addr + devc->buffsize;
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		mem_map_unreserve(page);
+
+	free_pages((unsigned long) devc->raw_buf, sz);
+	devc->raw_buf = NULL;
+}
+
+/* Intel version !!!!!!!!! */
+
+static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode)
+{
+	unsigned long flags;
+
+	flags = claim_dma_lock();
+	disable_dma(chan);
+	clear_dma_ff(chan);
+	set_dma_mode(chan, dma_mode);
+	set_dma_addr(chan, physaddr);
+	set_dma_count(chan, count);
+	enable_dma(chan);
+	release_dma_lock(flags);
+	return 0;
+}
+
+static void sscape_pnp_start_dma(sscape_info* devc, int arg )
+{
+	int reg;
+	if (arg == 0) reg = 2;
+	else reg = 3;
+
+	sscape_write(devc, reg, sscape_read( devc, reg) | 0x01);
+	sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE);
+}
+
+static int sscape_pnp_wait_dma (sscape_info* devc, int arg )
+{
+	int		reg;
+	unsigned long	i;
+	unsigned char	d;
+
+	if (arg == 0) reg = 2;
+	else reg = 3;
+
+	sleep ( 1 );
+	i = 0;
+	do {
+		d = sscape_read(devc, reg) & 1;
+		if ( d == 1)  break;
+		i++;
+	} while (i < 500000);
+	d = sscape_read(devc, reg) & 1; 
+	return d;
+}
+
+static	int	sscape_pnp_alloc_dma(sscape_info* devc)
+{
+	/* printk(KERN_INFO "sscape: requesting dma\n"); */
+	if (request_dma(devc -> dma, "sscape")) return 0;
+	/* printk(KERN_INFO "sscape: dma channel allocated\n"); */
+	if (!sscape_alloc_dma(devc)) {
+		free_dma(devc -> dma);
+		return 0;
+	};
+	return 1;
+}
+
+static	void	sscape_pnp_free_dma(sscape_info* devc)
+{
+	sscape_free_dma( devc);
+	free_dma(devc -> dma );	
+	/* printk(KERN_INFO "sscape: dma released\n"); */
+}
+
+static	int	sscape_pnp_upload_file(sscape_info* devc, char* fn)
+{	
+	int	     	done = 0;
+	int	     	timeout_val;
+	char*	     	data,*dt;
+	int	     	len,l;
+	unsigned long	flags;
+
+	sscape_write( devc, 9, sscape_read(devc, 9 )  & 0x3F );
+	sscape_write( devc, 2, (devc -> dma << 4) | 0x80 );
+	sscape_write( devc, 3, 0x20 );
+	sscape_write( devc, 9, sscape_read( devc, 9 )  | 0x80 );
+	
+	len = mod_firmware_load(fn, &data);
+	if (len == 0) {
+		    printk(KERN_ERR "sscape: file not found: %s\n", fn);
+		    return 0;
+	}
+	dt = data;
+	save_flags(flags);
+	cli();
+	while ( len > 0 ) {
+		if (len > devc -> buffsize) l = devc->buffsize;
+		else l = len;
+		len -= l;		
+		memcpy(devc->raw_buf, dt, l); dt += l;
+		sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48);
+		sscape_pnp_start_dma ( devc, 0 );
+		if (sscape_pnp_wait_dma ( devc, 0 ) == 0) {
+			restore_flags(flags);	    
+			return 0;
+		}
+	}
+	
+	restore_flags(flags);	    
+	vfree(data);
+	
+	outb(0, devc -> base + 2);
+	outb(0, devc -> base);
+
+	sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40);
+
+	timeout_val = 5 * HZ; 
+	while (!done && timeout_val-- > 0)
+	{
+		unsigned char x;
+		sleep(1);
+		x = inb( devc -> base + 3);
+		if (x == 0xff || x == 0xfe)		/* OBP startup acknowledge */
+		{
+			//printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
+			done = 1;
+		}
+	}
+	timeout_val = 5 * HZ;
+	done = 0;
+	while (!done && timeout_val-- > 0)
+	{
+		unsigned char x;
+		sleep(1);
+		x = inb( devc -> base + 3);
+		if (x == 0xfe)		/* OBP startup acknowledge */
+		{
+			//printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x);
+			done = 1;
+		}
+	}
+
+	if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n");
+
+	sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
+	sscape_write( devc, 3, (devc -> dma << 4) + 0x80);
+	return 1;
+}
+
+static void __init sscape_pnp_init_hw(sscape_info* devc)
+{	
+	unsigned char midi_irq = 0, sb_irq = 0;
+	unsigned i;
+	static	char code_file_name[23] = "/sndscape/sndscape.cox";
+	
+	int sscape_sb_enable		= 0;
+	int sscape_joystic_enable	= 0x7f;
+	int sscape_mic_enable		= 0;
+	int sscape_ext_midi		= 0;		
+
+	if ( !sscape_pnp_alloc_dma(devc) ) {
+		printk(KERN_ERR "sscape: faild to allocate dma\n");
+		return;
+	}
+
+	for (i = 0; i < 4; i++) {
+		if ( devc -> irq   == valid_interrupts[i] ) 
+			midi_irq = i;
+		if ( devc -> codec_irq == valid_interrupts[i] ) 
+			sb_irq = i;
+	}
+
+	sscape_write( devc, 5, 0x50);
+	sscape_write( devc, 7, 0x2e);
+	sscape_write( devc, 8, 0x00);
+
+	sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40);
+	sscape_write( devc, 3, ( devc -> dma << 4) | 0x80);
+
+	if ( sscape_sb_enable )
+		sscape_write (devc, 4, 0xF0 | (sb_irq << 2) | midi_irq);
+	else	
+		sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq);
+
+	i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0);
+	if ( sscape_sb_enable )
+		i |= devc->ic_type == IC_ODIE ? 0x05 : 0x07;	    
+	if (sscape_joystic_enable) i |= 8;
+	
+	sscape_write (devc, 9, i);
+	sscape_write (devc, 6, 0x80);
+	sscape_write (devc, 1, 0x80);
+
+	if (devc -> codec_type == 2) {
+		sscape_pnp_write_codec( devc, 0x0C, 0x50);
+		sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F);
+		sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0);
+		sscape_pnp_write_codec( devc, 29, 0x20);
+	}
+
+	if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) {
+		printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n");
+		sscape_pnp_free_dma(devc);
+		return;
+	}
+
+	i = sscape_read_host_ctrl( devc );
+	
+	if ( (i & 0x0F) >  7 ) {
+		printk(KERN_ERR "sscape: scope.cod faild\n");
+		sscape_pnp_free_dma(devc);
+		return;
+	}
+	if ( i & 0x10 ) sscape_write( devc, 7, 0x2F);
+	code_file_name[21] = (char) ( i & 0x0F) + 0x30;
+	if (sscape_pnp_upload_file( devc, code_file_name) == 0) {
+		printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name);
+		sscape_pnp_free_dma(devc);
+		return;
+	}
+	
+	if (devc->ic_type != IC_ODIE) {
+		sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) |
+		 ( sscape_mic_enable == 0 ? 0x00 : 0x80) );
+	}
+	sscape_write_host_ctrl2( devc, 0x84, 0x64 );  /* MIDI volume */
+	sscape_write_host_ctrl2( devc, 0x86, 0x64 );  /* MIDI volume?? */
+	sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi);
+
+	sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL
+	sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL
+	sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL
+	sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR
+
+	if (devc -> codec_type == 1) {
+		sscape_pnp_write_codec ( devc, 4, 0x1F );
+		sscape_pnp_write_codec ( devc, 5, 0x1F );
+		sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable);
+	} else {
+		int t;
+		sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1);
+		sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1));
+
+		t = sscape_pnp_read_codec( devc, 0x00) & 0xDF;
+		if ( (sscape_mic_enable == 0)) t |= 0;
+		else t |= 0x20;
+		sscape_pnp_write_codec ( devc, 0x00, t);
+		t = sscape_pnp_read_codec( devc, 0x01) & 0xDF;
+		if ( (sscape_mic_enable == 0) ) t |= 0;
+		else t |= 0x20;
+		sscape_pnp_write_codec ( devc, 0x01, t);
+		sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20);
+		outb(0, devc -> codec);
+	}
+	if (devc -> ic_type == IC_OPUS ) {
+		int i = sscape_read( devc, 9 );
+		sscape_write( devc, 9, i | 3 );
+		sscape_write( devc, 3, 0x40);
+
+		if (check_region(0x228, 1)) {
+		    	    outb(0, 0x228);
+			    release_region(0x228,1);
+		}
+		sscape_write( devc, 3, (devc -> dma << 4) | 0x80);
+		sscape_write( devc, 9, i );
+	}
+	
+	host_close ( devc );
+	sscape_pnp_free_dma(devc);
+}
+
+static int __init detect_sscape_pnp(sscape_info* devc)
+{
+	long	 i, irq_bits = 0xff;
+	unsigned int d;
+
+	DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base));
+
+	if (check_region(devc->base, 8)) {
+		printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->base);	
+		return 0;
+	}
+		
+	if (check_region(devc->codec, 2)) {
+		printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec);	
+		return 0;
+	}
+
+	if ( (inb( devc -> base + 2) & 0x78) != 0) return 0;
+
+	d = inb ( devc -> base + 4) & 0xF0;
+	if (  (d & 0x80) != 0)  return 0;
+	
+	if (d == 0) {
+			devc->codec_type = 1;
+			devc->ic_type = IC_ODIE;
+	}
+	else if ( (d & 0x60) != 0) {
+			devc->codec_type = 2;
+			devc->ic_type = IC_OPUS;
+	}
+	else if ( (d & 0x40) != 0) {
+			devc->codec_type = 2;
+			devc->ic_type = IC_ODIE;
+	} 
+	else return 0;
+	
+	sscape_is_pnp = 1;
+		
+	outb(0xFA, devc -> base+4);
+	if  ((inb( devc -> base+4) & 0x9F) != 0x0A)
+		return 0;
+	outb(0xFE, devc -> base+4);
+	if  ( (inb(devc -> base+4) & 0x9F) != 0x0E)
+		return 0;
+	if  ( (inb(devc -> base+5) & 0x9F) != 0x0E)
+		return 0;
+
+	if (devc->codec_type == 2) {
+		if (devc -> codec != devc -> base + 8)
+			printk("soundscape warning: incorrect codec port specified\n");
+		devc -> codec = devc -> base + 8;
+		d = 0x10 | (sscape_read(devc, 9)  & 0xCF);
+		sscape_write(devc, 9, d);
+		sscape_write(devc, 6, 0x80);
+	} else {
+		//todo: check codec is not base + 8
+	}
+
+	d  = (sscape_read(devc, 9) & 0x3F) | 0xC0;
+	sscape_write(devc, 9, d);
+
+	for (i = 0; i < 550000; i++)
+		if ( !(inb(devc -> codec) & 0x80) ) break;
+
+	d = inb(devc -> codec);
+	if (d & 0x80)
+		return 0;
+	if ( inb(devc -> codec + 2) == 0xFF)
+		return 0;
+
+	sscape_write(devc, 9, sscape_read(devc, 9)  & 0x3F );
+
+	d  = inb(devc -> codec) & 0x80;
+	if ( d == 0) {
+		printk(KERN_INFO "soundscape: hardware detected\n");
+		valid_interrupts = valid_interrupts_new;
+	} else	{
+		printk(KERN_INFO "soundscape: board looks like media fx\n");
+		valid_interrupts = valid_interrupts_old;
+		old_hardware = 1;
+	}
+
+	sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9)  & 0x3F) );
+
+	for (i = 0; i < 550000; i++)
+		if ( !(inb(devc -> codec) & 0x80)) 
+			break;
+		
+	sscape_pnp_init_hw(devc);
+
+	for (i = 0; i < sizeof(valid_interrupts); i++)
+	{
+		if (devc->codec_irq == valid_interrupts[i]) {
+			irq_bits = i;
+			break;
+		}
+	}	
+	sscape_write(devc, GA_INTENA_REG, 0x00);
+	sscape_write(devc, GA_DMACFG_REG, 0x50);
+	sscape_write(devc, GA_DMAA_REG, 0x70);
+	sscape_write(devc, GA_DMAB_REG, 0x20);
+	sscape_write(devc, GA_INTCFG_REG, 0xf0);
+	sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1));
+
+	sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20);
+	sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20);
+
+	return 1;	
+}
+
+static int __init probe_sscape(struct address_info *hw_config)
+{
+
+	if (sscape_detected != 0 && sscape_detected != hw_config->io_base)
+		return 0;
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->dma = hw_config->dma;
+	devc->osp = hw_config->osp;
+
+#ifdef SSCAPE_DEBUG1
+	/*
+	 * Temporary debugging aid. Print contents of the registers before
+	 * changing them.
+	 */
+	{
+		int i;
+
+		for (i = 0; i < 13; i++)
+			printk("I%d = %02x (old value)\n", i, sscape_read(devc, i));
+	}
+#endif
+	devc->failed = 1;
+
+	if (!detect_ga(devc)) {
+		if (detect_sscape_pnp(devc)) {		        
+			sscape_detected = hw_config->io_base;
+			return 1;
+		} 
+		else return 0;
+	}
+
+	if (old_hardware)	/* Check that it's really an old Spea/Reveal card. */
+	{
+		unsigned char   tmp;
+		int             cc;
+
+		if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0))
+		{
+			sscape_write(devc, GA_HMCTL_REG, tmp | 0x80);
+			for (cc = 0; cc < 200000; ++cc)
+				inb(devc->base + ODIE_ADDR);
+		}
+	}
+	sscape_detected = hw_config->io_base;
+	return 1;
+}
+
+static int __init probe_ss_ms_sound(struct address_info *hw_config)
+{
+	int i, irq_bits = 0xff;
+	int ad_flags = 0;
+	
+	if (devc->failed)
+	{
+		printk(KERN_ERR "soundscape: Card not detected\n");
+		return 0;
+	}
+	if (devc->ok == 0)
+	{
+		printk(KERN_ERR "soundscape: Invalid initialization order.\n");
+		return 0;
+	}
+	for (i = 0; i < sizeof(valid_interrupts); i++)
+	{
+		if (hw_config->irq == valid_interrupts[i])
+		{
+			irq_bits = i;
+			break;
+		}
+	}
+	if (hw_config->irq > 15 || irq_bits == 0xff)
+	{
+		printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq);
+		return 0;
+	}
+	
+	if (!sscape_is_pnp) {
+		if (old_hardware)
+			ad_flags = 0x12345677;	/* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
+		return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp);
+	} 
+	else {
+		if (old_hardware)
+			ad_flags = 0x12345677;	/* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
+		else
+			ad_flags = 0x87654321;  /* Tell that we have a soundscape pnp with 1845 chip */
+		return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp);
+	}
+}
+
+static void __init attach_ss_ms_sound(struct address_info *hw_config)
+{
+	/*
+	 * This routine configures the SoundScape card for use with the
+	 * Win Sound System driver. The AD1848 codec interface uses the CD-ROM
+	 * config registers of the "ODIE".
+	 */
+
+	int i, irq_bits = 0xff;
+
+ 		
+ 	if (!sscape_is_pnp)  /*pnp is already setup*/
+ 	{
+ 		/*
+     		 * Setup the DMA polarity.
+ 	    	 */
+ 		sscape_write(devc, GA_DMACFG_REG, 0x50);
+ 	
+ 		/*
+ 		 * Take the gate-array off of the DMA channel.
+ 		 */
+ 		sscape_write(devc, GA_DMAB_REG, 0x20);
+ 	
+ 		/*
+ 		 * Init the AD1848 (CD-ROM) config reg.
+ 		 */
+ 		for (i = 0; i < sizeof(valid_interrupts); i++)
+ 		{
+ 			if (hw_config->irq == valid_interrupts[i])
+ 			{
+ 				irq_bits = i;
+ 				break;
+ 			}
+ 		}	
+ 		sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1));
+ 	}
+ 	
+ 	if (hw_config->irq == devc->irq)
+ 		printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n");
+ 				
+	hw_config->slots[0] = ad1848_init(
+			sscape_is_pnp ? "SoundScape" : "SoundScape PNP",
+			hw_config->io_base,
+			hw_config->irq,
+			hw_config->dma,
+			hw_config->dma,
+			0,
+			devc->osp,
+			THIS_MODULE);
+
+ 					  
+	if (hw_config->slots[0] != -1)	/* The AD1848 driver installed itself */
+	{
+		audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations;
+		devc->codec_audiodev = hw_config->slots[0];
+		devc->my_audiodev = hw_config->slots[0];
+
+		/* Set proper routings here (what are they) */
+		AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+	}
+		
+#ifdef SSCAPE_DEBUG5
+	/*
+	 * Temporary debugging aid. Print contents of the registers
+	 * after the AD1848 device has been initialized.
+	 */
+	{
+		int i;
+
+		for (i = 0; i < 13; i++)
+			printk("I%d = %02x\n", i, sscape_read(devc, i));
+	}
+#endif
+
+}
+
+static void __exit unload_sscape(struct address_info *hw_config)
+{
+	release_region(devc->base + 2, 6);
+	unload_mpu401(hw_config);
+}
+
+static void __exit unload_ss_ms_sound(struct address_info *hw_config)
+{
+	ad1848_unload(hw_config->io_base,
+		      hw_config->irq,
+		      devc->dma,
+		      devc->dma,
+		      0);
+	sound_unload_audiodev(hw_config->slots[0]);
+}
+
+static struct address_info cfg;
+static struct address_info cfg_mpu;
+
+static int __initdata spea = -1;
+static int __initdata mss = 0;
+static int __initdata dma = -1;
+static int __initdata irq = -1;
+static int __initdata io = -1;
+static int __initdata mpu_irq = -1;
+static int __initdata mpu_io = -1;
+
+MODULE_PARM(dma, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(io, "i");
+MODULE_PARM(spea, "i");		/* spea=0/1 set the old_hardware */
+MODULE_PARM(mpu_irq, "i");
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(mss, "i");
+
+static int __init init_sscape(void)
+{
+	printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+	
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.io_base = io;
+
+	cfg_mpu.irq = mpu_irq;
+	cfg_mpu.io_base = mpu_io;
+	/* WEH - Try to get right dma channel */
+        cfg_mpu.dma = dma;
+	
+	devc->codec = cfg.io_base;
+	devc->codec_irq = cfg.irq;
+	devc->codec_type = 0;
+	devc->ic_type = 0;
+	devc->raw_buf = NULL;
+
+	if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) {
+		printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n");
+		return -EINVAL;
+	}
+	
+	if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) {
+		printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n");
+		return -EINVAL;
+	}
+	
+	if(spea != -1) {
+		old_hardware = spea;
+		printk(KERN_INFO "Forcing %s hardware support.\n",
+			spea?"new":"old");
+	}	
+	if (probe_sscape(&cfg_mpu) == 0)
+		return -ENODEV;
+
+	attach_sscape(&cfg_mpu);
+	
+	mss = probe_ss_ms_sound(&cfg);
+
+	if (mss)
+		attach_ss_ms_sound(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_sscape(void)
+{
+	if (mss)
+		unload_ss_ms_sound(&cfg);
+	unload_sscape(&cfg_mpu);
+}
+
+module_init(init_sscape);
+module_exit(cleanup_sscape);
+
+#ifndef MODULE
+static int __init setup_sscape(char *str)
+{
+	/* io, irq, dma, mpu_io, mpu_irq */
+	int ints[6];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	mpu_io	= ints[4];
+	mpu_irq	= ints[5];
+
+	return 1;
+}
+
+__setup("sscape=", setup_sscape);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/sys_timer.c linux-2.4.19-pre5-mjc/sound/oss/sys_timer.c
--- linux/sound/oss/sys_timer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/sys_timer.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,287 @@
+/*
+ * sound/sys_timer.c
+ *
+ * The default timer for the Level 2 sequencer interface
+ * Uses the (1/HZ sec) timer of kernel.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Andrew Veliath  : adapted tmr2ticks from level 1 sequencer (avoid overflow)
+ */
+#include "sound_config.h"
+
+static volatile int opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+
+static void     poll_def_tmr(unsigned long dummy);
+
+
+static struct timer_list def_tmr =
+{function: poll_def_tmr};
+
+static unsigned long
+tmr2ticks(int tmr_value)
+{
+	/*
+	 *    Convert timer ticks to MIDI ticks
+	 */
+
+	unsigned long tmp;
+	unsigned long scale;
+
+	/* tmr_value (ticks per sec) *
+	   1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
+	tmp = tmr_value * (1000000 / HZ);
+	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
+	return (tmp + scale / 2) / scale;
+}
+
+static void
+poll_def_tmr(unsigned long dummy)
+{
+
+	if (opened)
+	  {
+
+		  {
+			  def_tmr.expires = (1) + jiffies;
+			  add_timer(&def_tmr);
+		  };
+
+		  if (tmr_running)
+		    {
+			    tmr_ctr++;
+			    curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
+
+			    if (curr_ticks >= next_event_time)
+			      {
+				      next_event_time = (unsigned long) -1;
+				      sequencer_timer(0);
+			      }
+		    }
+	  }
+}
+
+static void
+tmr_reset(void)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	tmr_offs = 0;
+	ticks_offs = 0;
+	tmr_ctr = 0;
+	next_event_time = (unsigned long) -1;
+	prev_event_time = 0;
+	curr_ticks = 0;
+	restore_flags(flags);
+}
+
+static int
+def_tmr_open(int dev, int mode)
+{
+	if (opened)
+		return -EBUSY;
+
+	tmr_reset();
+	curr_tempo = 60;
+	curr_timebase = 100;
+	opened = 1;
+
+	;
+
+	{
+		def_tmr.expires = (1) + jiffies;
+		add_timer(&def_tmr);
+	};
+
+	return 0;
+}
+
+static void
+def_tmr_close(int dev)
+{
+	opened = tmr_running = 0;
+	del_timer(&def_tmr);;
+}
+
+static int
+def_tmr_event(int dev, unsigned char *event)
+{
+	unsigned char   cmd = event[1];
+	unsigned long   parm = *(int *) &event[4];
+
+	switch (cmd)
+	  {
+	  case TMR_WAIT_REL:
+		  parm += prev_event_time;
+	  case TMR_WAIT_ABS:
+		  if (parm > 0)
+		    {
+			    long            time;
+
+			    if (parm <= curr_ticks)	/* It's the time */
+				    return TIMER_NOT_ARMED;
+
+			    time = parm;
+			    next_event_time = prev_event_time = time;
+
+			    return TIMER_ARMED;
+		    }
+		  break;
+
+	  case TMR_START:
+		  tmr_reset();
+		  tmr_running = 1;
+		  break;
+
+	  case TMR_STOP:
+		  tmr_running = 0;
+		  break;
+
+	  case TMR_CONTINUE:
+		  tmr_running = 1;
+		  break;
+
+	  case TMR_TEMPO:
+		  if (parm)
+		    {
+			    if (parm < 8)
+				    parm = 8;
+			    if (parm > 360)
+				    parm = 360;
+			    tmr_offs = tmr_ctr;
+			    ticks_offs += tmr2ticks(tmr_ctr);
+			    tmr_ctr = 0;
+			    curr_tempo = parm;
+		    }
+		  break;
+
+	  case TMR_ECHO:
+		  seq_copy_to_input(event, 8);
+		  break;
+
+	  default:;
+	  }
+
+	return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+def_tmr_get_time(int dev)
+{
+	if (!opened)
+		return 0;
+
+	return curr_ticks;
+}
+
+/* same as sound_timer.c:timer_ioctl!? */
+static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int val;
+
+	switch (cmd) {
+	case SNDCTL_TMR_SOURCE:
+		return __put_user(TMR_INTERNAL, (int *)arg);
+
+	case SNDCTL_TMR_START:
+		tmr_reset();
+		tmr_running = 1;
+		return 0;
+
+	case SNDCTL_TMR_STOP:
+		tmr_running = 0;
+		return 0;
+
+	case SNDCTL_TMR_CONTINUE:
+		tmr_running = 1;
+		return 0;
+
+	case SNDCTL_TMR_TIMEBASE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val) {
+			if (val < 1)
+				val = 1;
+			if (val > 1000)
+				val = 1000;
+			curr_timebase = val;
+		}
+		return __put_user(curr_timebase, (int *)arg);
+
+	case SNDCTL_TMR_TEMPO:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val) {
+			if (val < 8)
+				val = 8;
+			if (val > 250)
+				val = 250;
+			tmr_offs = tmr_ctr;
+			ticks_offs += tmr2ticks(tmr_ctr);
+			tmr_ctr = 0;
+			curr_tempo = val;
+			reprogram_timer();
+		}
+		return __put_user(curr_tempo, (int *)arg);
+
+	case SNDCTL_SEQ_CTRLRATE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0)	/* Can't change */
+			return -EINVAL;
+		val = ((curr_tempo * curr_timebase) + 30) / 60;
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_SEQ_GETTIME:
+		return __put_user(curr_ticks, (int *)arg);
+		
+	case SNDCTL_TMR_METRONOME:
+		/* NOP */
+		break;
+		
+	default:;
+	}
+	return -EINVAL;
+}
+
+static void
+def_tmr_arm(int dev, long time)
+{
+	if (time < 0)
+		time = curr_ticks + 1;
+	else if (time <= curr_ticks)	/* It's the time */
+		return;
+
+	next_event_time = prev_event_time = time;
+
+	return;
+}
+
+struct sound_timer_operations default_sound_timer =
+{
+	owner:		THIS_MODULE,
+	info:		{"System clock", 0},
+	priority:	0,	/* Priority */
+	devlink:	0,	/* Local device link */
+	open:		def_tmr_open,
+	close:		def_tmr_close,
+	event:		def_tmr_event,
+	get_time:	def_tmr_get_time,
+	ioctl:		def_tmr_ioctl,
+	arm_timer:	def_tmr_arm
+};
diff -Nru linux/sound/oss/trident.c linux-2.4.19-pre5-mjc/sound/oss/trident.c
--- linux/sound/oss/trident.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/trident.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,4231 @@
+/*
+ *	OSS driver for Linux 2.4.x for
+ *
+ *	Trident 4D-Wave
+ *	SiS 7018
+ *	ALi 5451
+ *	Tvia/IGST CyberPro 5050
+ *
+ *	Driver: Alan Cox <alan@redhat.com>
+ *
+ *  Built from:
+ *	Low level code: <audio@tridentmicro.com> from ALSA
+ *	Framework: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *	Extended by: Zach Brown <zab@redhat.com>  
+ *
+ *  Hacked up by:
+ *	Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *	Ollie Lho <ollie@sis.com.tw> SiS 7018 Audio Core Support
+ *	Ching-Ling Lee <cling-li@ali.com.tw> ALi 5451 Audio Core Support 
+ *	Matt Wu <mattwu@acersoftech.com.cn> ALi 5451 Audio Core Support
+ *	Peter Wchtler <pwaechtler@loewe-komp.de> CyberPro5050 support
+ *
+ *
+ *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  History
+ *  v0.14.9d
+ *  	October 8 2001 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *	use set_current_state, properly release resources on failure in
+ *	trident_probe, get rid of check_region
+ *  v0.14.9c
+ *	August 10 2001 Peter Wchtler <pwaechtler@loewe-komp.de>
+ *	added support for Tvia (formerly Integraphics/IGST) CyberPro5050
+ *	this chip is often found in settop boxes (combined video+audio)
+ *  v0.14.9b
+ *	Switch to static inline not extern inline (gcc 3)
+ *  v0.14.9a
+ *	Aug 6 2001 Alan Cox
+ *	0.14.9 crashed on rmmod due to a timer/bh left running. Simplified
+ *	the existing logic (the BH doesnt help as ac97 is lock_irqsave)
+ *	and used del_timer_sync to clean up
+ *	Fixed a problem where the ALi change broke my generic card
+ *  v0.14.9
+ *	Jul 10 2001 Matt Wu
+ *	Add H/W Volume Control
+ *  v0.14.8a
+ *	July 7 2001 Alan Cox
+ *	Moved Matt Wu's ac97 register cache into the card structure
+ *  v0.14.8
+ *	Apr 30 2001 Matt Wu
+ *	Set EBUF1 and EBUF2 to still mode
+ *	Add dc97/ac97 reset function
+ *	Fix power management: ali_restore_regs
+ *  unreleased 
+ *	Mar 09 2001 Matt Wu
+ *	Add cache for ac97 access
+ *  v0.14.7
+ *	Feb 06 2001 Matt Wu
+ *	Fix ac97 initialization
+ *	Fix bug: an extra tail will be played when playing
+ *	Jan 05 2001 Matt Wu
+ *	Implement multi-channels and S/PDIF in support for ALi 1535+
+ *  v0.14.6 
+ *	Nov 1 2000 Ching-Ling Lee
+ *	Fix the bug of memory leak when switching 5.1-channels to 2 channels.
+ *	Add lock protection into dynamic changing format of data.
+ *	Oct 18 2000 Ching-Ling Lee
+ *	5.1-channels support for ALi
+ *	June 28 2000 Ching-Ling Lee
+ *	S/PDIF out/in(playback/record) support for ALi 1535+, using /proc to be selected by user
+ *	Simple Power Management support for ALi
+ *  v0.14.5 May 23 2000 Ollie Lho
+ *  	Misc bug fix from the Net
+ *  v0.14.4 May 20 2000 Aaron Holtzman
+ *  	Fix kfree'd memory access in release
+ *  	Fix race in open while looking for a free virtual channel slot
+ *  	remove open_wait wq (which appears to be unused)
+ *  v0.14.3 May 10 2000 Ollie Lho
+ *	fixed a small bug in trident_update_ptr, xmms 1.0.1 no longer uses 100% CPU
+ *  v0.14.2 Mar 29 2000 Ching-Ling Lee
+ *	Add clear to silence advance in trident_update_ptr 
+ *	fix invalid data of the end of the sound
+ *  v0.14.1 Mar 24 2000 Ching-Ling Lee
+ *	ALi 5451 support added, playback and recording O.K.
+ *	ALi 5451 originally developed and structured based on sonicvibes, and
+ *	suggested to merge into this file by Alan Cox.
+ *  v0.14 Mar 15 2000 Ollie Lho
+ *	5.1 channel output support with channel binding. What's the Matrix ?
+ *  v0.13.1 Mar 10 2000 Ollie Lho
+ *	few minor bugs on dual codec support, needs more testing
+ *  v0.13 Mar 03 2000 Ollie Lho
+ *	new pci_* for 2.4 kernel, back ported to 2.2
+ *  v0.12 Feb 23 2000 Ollie Lho
+ *	Preliminary Recording support
+ *  v0.11.2 Feb 19 2000 Ollie Lho
+ *	removed incomplete full-dulplex support
+ *  v0.11.1 Jan 28 2000 Ollie Lho
+ *	small bug in setting sample rate for 4d-nx (reported by Aaron)
+ *  v0.11 Jan 27 2000 Ollie Lho
+ *	DMA bug, scheduler latency, second try
+ *  v0.10 Jan 24 2000 Ollie Lho
+ *	DMA bug fixed, found kernel scheduling problem
+ *  v0.09 Jan 20 2000 Ollie Lho
+ *	Clean up of channel register access routine (prepare for channel binding)
+ *  v0.08 Jan 14 2000 Ollie Lho
+ *	Isolation of AC97 codec code
+ *  v0.07 Jan 13 2000 Ollie Lho
+ *	Get rid of ugly old low level access routines (e.g. CHRegs.lp****)
+ *  v0.06 Jan 11 2000 Ollie Lho
+ *	Preliminary support for dual (more ?) AC97 codecs
+ *  v0.05 Jan 08 2000 Luca Montecchiani <m.luca@iname.com>
+ *	adapt to 2.3.x new __setup/__init call
+ *  v0.04 Dec 31 1999 Ollie Lho
+ *	Multiple Open, using Middle Loop Interrupt to smooth playback
+ *  v0.03 Dec 24 1999 Ollie Lho
+ *	mem leak in prog_dmabuf and dealloc_dmabuf removed
+ *  v0.02 Dec 15 1999 Ollie Lho
+ *	SiS 7018 support added, playback O.K.
+ *  v0.01 Alan Cox et. al.
+ *	Initial Release in kernel 2.3.30, does not work
+ * 
+ *  ToDo
+ *	Clean up of low level channel register access code. (done)
+ *	Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done)
+ *	Dual AC97 codecs support (done)
+ *	Recording support (done)
+ *	Mmap support
+ *	"Channel Binding" ioctl extension (done)
+ *	new pci device driver interface for 2.4 kernel (done)
+ *
+ *	Lock order (high->low)
+ *		lock	-	hardware lock
+ *		open_sem - 	guard opens
+ *		sem	-	guard dmabuf, write re-entry etc
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <linux/wrapper.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+
+#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC
+#include <asm/hwrpb.h>
+#endif
+
+#include "trident.h"
+
+#include <linux/pm.h>
+
+#define DRIVER_VERSION "0.14.9d"
+
+/* magic numbers to protect our data structures */
+#define TRIDENT_CARD_MAGIC	0x5072696E /* "Prin" */
+#define TRIDENT_STATE_MAGIC	0x63657373 /* "cess" */
+
+#define TRIDENT_DMA_MASK	0x3fffffff /* DMA buffer mask for pci_alloc_consist */
+#define ALI_DMA_MASK		0xffffffff /* ALI Tridents lack the 30-bit limitation */
+
+#define NR_HW_CH		32
+
+/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only
+   have 2 SDATA_IN lines (currently) */
+#define NR_AC97		2	
+
+/* minor number of /dev/swmodem (temporary, experimental) */
+#define SND_DEV_SWMODEM	7
+
+static const unsigned ali_multi_channels_5_1[] = { /*ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL,*/ ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL};
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n";
+
+enum {
+	TRIDENT_4D_DX = 0,
+	TRIDENT_4D_NX,
+	SIS_7018,
+	ALI_5451,
+	CYBER5050
+};
+
+static char * card_names[] = {
+	"Trident 4DWave DX",
+	"Trident 4DWave NX",
+	"SiS 7018 PCI Audio",
+	"ALi Audio Accelerator",
+	"Tvia/IGST CyberPro 5050"
+};
+
+static struct pci_device_id trident_pci_tbl [] __devinitdata = {
+	{PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX},
+	{PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX},
+	{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018},
+	{PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451},
+	{ PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050, 
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CYBER5050},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE (pci, trident_pci_tbl);
+
+/* "software" or virtual channel, an instance of opened /dev/dsp */
+struct trident_state {
+	unsigned int magic;
+	struct trident_card *card;	/* Card info */
+
+	/* file mode */
+	mode_t open_mode;
+
+	/* virtual channel number */
+	int virt;
+
+	struct dmabuf {
+		/* wave sample stuff */
+		unsigned int rate;
+		unsigned char fmt, enable;
+
+		/* hardware channel */
+		struct trident_channel *channel;
+
+		/* OSS buffer management stuff */
+		void *rawbuf;
+		dma_addr_t dma_handle;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+
+		/* our buffer acts like a circular ring */
+		unsigned hwptr;		/* where dma last started, updated by update_ptr */
+		unsigned swptr;		/* where driver last clear/filled, updated by read/write */
+		int count;		/* bytes to be comsumed or been generated by dma machine */
+		unsigned total_bytes;	/* total bytes dmaed by hardware */
+
+		unsigned error;		/* number of over/underruns */
+		wait_queue_head_t wait;	/* put process on wait queue when no more space in buffer */
+
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned update_flag;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+		
+	} dmabuf;
+
+	/* 5.1channels */	
+	struct trident_state *other_states[4];
+	int multi_channels_adjust_count;
+	unsigned chans_num;
+	unsigned fmt_flag:1;
+	/* Guard against mmap/write/read races */
+	struct semaphore sem;
+
+};
+
+/* hardware channels */
+struct trident_channel {
+	int  num;	/* channel number */
+	u32 lba;	/* Loop Begine Address, where dma buffer starts */
+	u32 eso;	/* End Sample Offset, wehre dma buffer ends (in the unit of samples) */
+	u32 delta;	/* delta value, sample rate / 48k for playback, 48k/sample rate for recording */
+	u16 attribute;	/* control where PCM data go and come  */
+	u16 fm_vol;
+	u32 control;	/* signed/unsigned, 8/16 bits, mono/stereo */
+};
+
+struct trident_pcm_bank_address {
+	u32 start;
+	u32 stop;
+	u32 aint;
+	u32 aint_en;
+};
+static struct trident_pcm_bank_address bank_a_addrs =
+{
+	T4D_START_A,
+	T4D_STOP_A,
+	T4D_AINT_A,
+	T4D_AINTEN_A
+};
+static struct trident_pcm_bank_address bank_b_addrs =
+{
+	T4D_START_B,
+	T4D_STOP_B,
+	T4D_AINT_B,
+	T4D_AINTEN_B
+};
+struct trident_pcm_bank {
+	/* register addresses to control bank operations */
+	struct trident_pcm_bank_address *addresses;
+	/* each bank has 32 channels */
+	u32 bitmap; /* channel allocation bitmap */
+	struct trident_channel channels[32];
+};
+
+struct trident_card {
+	unsigned int magic;
+
+	/* We keep trident cards in a linked list */
+	struct trident_card *next;
+
+	/* single open lock mechanism, only used for recording */
+	struct semaphore open_sem;
+
+	/* The trident has a certain amount of cross channel interaction
+	   so we use a single per card lock */
+	spinlock_t lock;
+	
+	/* PCI device stuff */
+	struct pci_dev * pci_dev;
+	u16 pci_id;
+	u8 revision;
+
+	/* soundcore stuff */
+	int dev_audio;
+
+	/* structures for abstraction of hardware facilities, codecs, banks and channels*/
+	struct ac97_codec *ac97_codec[NR_AC97];
+	struct trident_pcm_bank banks[NR_BANKS];
+	struct trident_state *states[NR_HW_CH];
+
+	/* hardware resources */
+	unsigned long iobase;
+	u32 irq;
+	
+	/* Function support */
+	struct trident_channel *(*alloc_pcm_channel)(struct trident_card *);
+	struct trident_channel *(*alloc_rec_pcm_channel)(struct trident_card *);
+	void (*free_pcm_channel)(struct trident_card *, unsigned int chan);
+	void (*address_interrupt)(struct trident_card *);
+	
+	/* Added by Matt Wu 01-05-2001 for spdif in */
+	int multi_channel_use_count;
+	int rec_channel_use_count;
+	u16 mixer_regs[64][NR_AC97];	/* Made card local by Alan */
+	int mixer_regs_ready;
+
+	/* Added for hardware volume control */
+	int hwvolctl;
+	struct timer_list timer;
+};
+
+/* table to map from CHANNELMASK to channel attribute for SiS 7018 */
+static u16 mask2attr [] =
+{
+	PCM_LR, PCM_LR, SURR_LR, CENTER_LFE,
+	HSET, MIC, MODEM_LINE1, MODEM_LINE2,
+	I2S_LR, SPDIF_LR
+};
+/* table to map from channel attribute to CHANNELMASK for SiS 7018 */
+static int attr2mask [] = {
+	DSP_BIND_MODEM1, DSP_BIND_MODEM2, DSP_BIND_FRONT, DSP_BIND_HANDSET,
+	DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF
+};
+
+/* Added by Matt Wu 01-05-2001 for spdif in */
+static int ali_close_multi_channels(void);
+static void ali_delay(struct trident_card *card,int interval);
+static void ali_detect_spdif_rate(struct trident_card *card);
+
+static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val);
+static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg);
+
+static struct trident_card *devs;
+
+static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
+static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg);
+
+static int trident_open_mixdev(struct inode *inode, struct file *file);
+static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+				unsigned long arg);
+
+static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val);
+static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg);
+static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate);
+static void ali_enable_special_channel(struct trident_state *stat);
+static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card);
+static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card);
+static void ali_restore_regs(struct trident_card *card);
+static void ali_save_regs(struct trident_card *card);
+static int trident_suspend(struct pci_dev *dev, u32 unused);
+static int trident_resume(struct pci_dev *dev);
+static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel);
+static int ali_setup_multi_channels(struct trident_card *card, int chan_nums);
+static unsigned int ali_get_spdif_in_rate(struct trident_card *card);
+static void ali_setup_spdif_in(struct trident_card *card);
+static void ali_disable_spdif_in(struct trident_card *card);
+static void ali_disable_special_channel(struct trident_card *card, int ch);
+static void ali_setup_spdif_out(struct trident_card *card, int flag);
+static int ali_write_5_1(struct trident_state *state, const char *buffer,int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt);
+static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums);
+static void ali_free_other_states_resources(struct trident_state *state);
+
+
+/* save registers for ALi Power Management */
+static struct ali_saved_registers {
+	unsigned long global_regs[ALI_GLOBAL_REGS];
+	unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+	unsigned mixer_regs[ALI_MIXER_REGS];
+} ali_registers;
+
+#define seek_offset(dma_ptr, buffer, cnt, offset, copy_count)	(dma_ptr) += (offset);	\
+							(buffer) += (offset);	\
+							(cnt) -= (offset);	\
+							(copy_count) += (offset);
+
+#define lock_set_fmt(state)	{spin_lock_irqsave(&state->card->lock, flags);			\
+				if (state->fmt_flag) {						\
+					spin_unlock_irqrestore(&state->card->lock, flags);	\
+					return -EFAULT;						\
+				}								\
+				state->fmt_flag = 1;						\
+				spin_unlock_irqrestore(&state->card->lock, flags);}
+				
+#define unlock_set_fmt(state)	{spin_lock_irqsave(&state->card->lock, flags);		\
+				state->fmt_flag = 0;					\
+				spin_unlock_irqrestore(&state->card->lock, flags);}
+
+static int trident_enable_loop_interrupts(struct trident_card * card)
+{
+	u32 global_control;
+
+	global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR));
+
+	switch (card->pci_id)
+	{
+	case PCI_DEVICE_ID_SI_7018:
+		global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN);
+		break;
+	case PCI_DEVICE_ID_ALI_5451:
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+	case PCI_DEVICE_ID_INTERG_5050:
+		global_control |= (ENDLP_IE | MIDLP_IE);
+		break;
+	default:
+		return FALSE;
+	}
+
+	outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR));
+
+#ifdef DEBUG
+	printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n",
+			inl(TRID_REG(card, T4D_LFO_GC_CIR)));
+#endif
+	return (TRUE);
+}
+
+static int trident_disable_loop_interrupts(struct trident_card * card)
+{
+	u32 global_control;
+
+	global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR));
+	global_control &= ~(ENDLP_IE | MIDLP_IE);
+	outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR));
+
+#ifdef DEBUG
+	printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n",
+	       global_control);
+#endif
+	return (TRUE);
+}
+
+static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+	u32 reg, addr = bank->addresses->aint_en;
+
+	reg = inl(TRID_REG(card, addr));
+	reg |= mask;
+	outl(reg, TRID_REG(card, addr));
+
+#ifdef DEBUG
+	reg = inl(TRID_REG(card, addr));
+	printk("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n",
+		channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr);
+#endif
+}
+
+static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+	u32 reg, addr = bank->addresses->aint_en;
+	
+	reg = inl(TRID_REG(card, addr));
+	reg &= ~mask;
+	outl(reg, TRID_REG(card, addr));
+	
+	/* Ack the channel in case the interrupt was set before we disable it. */
+	outl(mask, TRID_REG(card, bank->addresses->aint));
+
+#ifdef DEBUG
+	reg = inl(TRID_REG(card, addr));
+	printk("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n",
+		channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr);
+#endif
+}
+
+static void trident_start_voice(struct trident_card * card, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+	u32 addr = bank->addresses->start;
+
+#ifdef DEBUG
+	u32 reg;
+#endif
+
+	outl(mask, TRID_REG(card, addr));
+
+#ifdef DEBUG
+	reg = inl(TRID_REG(card, addr));
+	printk("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n",
+		channel, addr==T4D_START_B? "START_B":"START_A",reg,addr);
+#endif
+}
+
+static void trident_stop_voice(struct trident_card * card, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+	u32 addr = bank->addresses->stop;
+
+#ifdef DEBUG
+	u32 reg;
+#endif
+
+	outl(mask, TRID_REG(card, addr));
+
+#ifdef DEBUG
+	reg = inl(TRID_REG(card, addr));
+	printk("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n",
+		channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr);
+#endif
+}
+
+static u32 trident_get_interrupt_mask (struct trident_card * card, unsigned int channel)
+{
+	struct trident_pcm_bank *bank = &card->banks[channel];
+	u32 addr = bank->addresses->aint;
+	return inl(TRID_REG(card, addr));
+}
+
+static int trident_check_channel_interrupt(struct trident_card * card, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	u32 reg = trident_get_interrupt_mask (card, channel >> 5);
+
+#ifdef DEBUG
+	if (reg & mask)
+		printk("trident: channel %d has interrupt, %s = 0x%08x\n",
+			channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg);
+#endif
+	return (reg & mask) ? TRUE : FALSE;
+}
+
+static void trident_ack_channel_interrupt(struct trident_card * card, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	struct trident_pcm_bank *bank = &card->banks[channel >> 5];
+	u32 reg, addr = bank->addresses->aint;
+
+	reg = inl(TRID_REG(card, addr));
+	reg &= mask;
+	outl(reg, TRID_REG(card, addr));
+
+#ifdef DEBUG
+	reg = inl(TRID_REG(card, T4D_AINT_B));
+	printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n",
+	       channel, reg);
+#endif
+}
+
+static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card)
+{
+	struct trident_pcm_bank *bank;
+	int idx;
+
+	bank = &card->banks[BANK_B];
+
+	for (idx = 31; idx >= 0; idx--) {
+		if (!(bank->bitmap & (1 << idx))) {
+			struct trident_channel *channel = &bank->channels[idx];
+			bank->bitmap |= 1 << idx;
+			channel->num = idx + 32;
+			return channel;
+		}
+	}
+
+	/* no more free channels available */
+	printk(KERN_ERR "trident: no more channels available on Bank B.\n");
+	return NULL;
+}
+
+static void trident_free_pcm_channel(struct trident_card *card, unsigned int channel)
+{
+	int bank;
+        unsigned char b;
+
+	if (channel < 31 || channel > 63)
+		return;
+
+	if (card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_DX ||
+            card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) {
+          b = inb (TRID_REG(card, T4D_REC_CH));
+          if ((b & ~0x80) == channel)
+            outb(0x0, TRID_REG(card, T4D_REC_CH));
+        }
+            
+	bank = channel >> 5;
+	channel = channel & 0x1f;
+        
+	card->banks[bank].bitmap &= ~(1 << (channel));
+}
+
+static struct trident_channel * cyber_alloc_pcm_channel(struct trident_card *card)
+{
+	struct trident_pcm_bank *bank;
+	int idx;
+
+	/* The cyberpro 5050 has only 32 voices and one bank */
+	/* .. at least they are not documented (if you want to call that 
+	 * crap documentation), perhaps broken ? */
+
+	bank = &card->banks[BANK_A];
+
+	for (idx = 31; idx >= 0; idx--) {
+		if (!(bank->bitmap & (1 << idx))) {
+			struct trident_channel *channel = &bank->channels[idx];
+			bank->bitmap |= 1 << idx;
+			channel->num = idx;
+			return channel;
+		}
+	}
+
+	/* no more free channels available */
+	printk(KERN_ERR "cyberpro5050: no more channels available on Bank A.\n");
+	return NULL;
+}
+
+static void cyber_free_pcm_channel(struct trident_card *card, unsigned int channel)
+{
+	if (channel > 31)
+		return;
+	card->banks[BANK_A].bitmap &= ~(1 << (channel));
+}
+
+static inline void cyber_outidx(int port,int idx,int data)
+{
+	outb(idx,port);
+	outb(data,port+1);
+}
+
+static inline int cyber_inidx(int port,int idx)
+{
+	outb(idx,port);
+	return inb(port+1);
+}
+
+static int cyber_init_ritual(struct trident_card *card)
+{
+	/* some black magic, taken from SDK samples */
+	/* remove this and nothing will work */
+	int portDat;
+	int ret = 0;
+	unsigned long flags;
+
+	/*
+ 	 *	Keep interrupts off for the configure - we don't want to
+ 	 *	clash with another cyberpro config event
+ 	 */
+ 
+	save_flags(flags);
+	cli();
+	portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE);
+	/* enable, if it was disabled */
+	if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) {
+		printk(KERN_INFO "cyberpro5050: enabling audio controller\n" );
+		cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE,
+			portDat | CYBER_BMSK_AUENZ_ENABLE );
+		/* check again if hardware is enabled now */
+		portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE);
+	}
+	if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE )
+	{
+		printk(KERN_ERR "cyberpro5050: initAudioAccess: no success\n" );
+		ret = -1;
+	}
+	else
+	{
+		cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_IRQ_ENABLE, CYBER_BMSK_AUDIO_INT_ENABLE );
+		cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x01 );
+		cyber_outidx( CYBER_PORT_AUDIO, 0xba, 0x20 );
+		cyber_outidx( CYBER_PORT_AUDIO, 0xbb, 0x08 );
+		cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x02 );
+		cyber_outidx( CYBER_PORT_AUDIO, 0xb3, 0x06 );
+		cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x00 );
+	}
+	restore_flags(flags);
+	return ret;
+}
+
+/*  called with spin lock held */
+
+static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel)
+{
+	int i;
+
+	if (channel > 63)
+		return FALSE;
+
+	/* select hardware channel to write */
+	outb(channel, TRID_REG(card, T4D_LFO_GC_CIR));
+
+	/* Output the channel registers, but don't write register
+	   three to an ALI chip. */
+	for (i = 0; i < CHANNEL_REGS; i++) {
+		if (i == 3 && card->pci_id == PCI_DEVICE_ID_ALI_5451)
+			continue;
+		outl(data[i], TRID_REG(card, CHANNEL_START + 4*i));
+	}
+	if (card->pci_id == PCI_DEVICE_ID_ALI_5451 ||
+		card->pci_id == PCI_DEVICE_ID_INTERG_5050) {
+		outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF1));
+		outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF2));
+	}
+	return TRUE;
+}
+
+/* called with spin lock held */
+static int trident_write_voice_regs(struct trident_state *state)
+{
+	unsigned int data[CHANNEL_REGS + 1];
+	struct trident_channel *channel;
+
+	channel = state->dmabuf.channel;
+
+	data[1] = channel->lba;
+	data[4] = channel->control;
+
+	switch (state->card->pci_id)
+	{
+	case PCI_DEVICE_ID_ALI_5451:
+		data[0] = 0; /* Current Sample Offset */
+		data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
+		data[3] = 0;
+		break;	
+	case PCI_DEVICE_ID_SI_7018:
+	case PCI_DEVICE_ID_INTERG_5050:
+		data[0] = 0; /* Current Sample Offset */
+		data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
+		data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff);
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+		data[0] = 0; /* Current Sample Offset */
+		data[2] = (channel->eso << 16) | (channel->delta & 0xffff);
+		data[3] = channel->fm_vol & 0xffff;
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+		data[0] = (channel->delta << 24);
+		data[2] = ((channel->delta << 16) & 0xff000000) | (channel->eso & 0x00ffffff);
+		data[3] = channel->fm_vol & 0xffff;
+		break;
+	default:
+		return FALSE;
+	}
+
+	return trident_load_channel_registers(state->card, data, channel->num);
+}
+
+static int compute_rate_play(u32 rate)
+{
+	int delta;
+	/* We special case 44100 and 8000 since rounding with the equation
+	   does not give us an accurate enough value. For 11025 and 22050
+	   the equation gives us the best answer. All other frequencies will
+	   also use the equation. JDW */
+	if (rate == 44100)
+		delta = 0xeb3;
+	else if (rate == 8000)
+		delta = 0x2ab;
+	else if (rate == 48000)
+		delta = 0x1000;
+	else
+		delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
+	return delta;
+}
+
+static int compute_rate_rec(u32 rate)
+{
+	int delta;
+
+	if (rate == 44100)
+		delta = 0x116a;
+	else if (rate == 8000)
+		delta = 0x6000;
+	else if (rate == 48000)
+		delta = 0x1000;
+	else
+		delta = ((48000 << 12) / rate) & 0x0000ffff;
+
+	return delta;
+}
+/* set playback sample rate */
+static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate)
+{	
+	struct dmabuf *dmabuf = &state->dmabuf;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+
+	dmabuf->rate = rate;
+	dmabuf->channel->delta = compute_rate_play(rate);
+
+	trident_write_voice_regs(state);
+
+#ifdef DEBUG
+	printk("trident: called trident_set_dac_rate : rate = %d\n", rate);
+#endif
+
+	return rate;
+}
+
+/* set recording sample rate */
+static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+
+	dmabuf->rate = rate;
+	dmabuf->channel->delta = compute_rate_rec(rate);
+
+	trident_write_voice_regs(state);
+
+#ifdef DEBUG
+	printk("trident: called trident_set_adc_rate : rate = %d\n", rate);
+#endif
+	return rate;
+}
+
+/* prepare channel attributes for playback */ 
+static void trident_play_setup(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct trident_channel *channel = dmabuf->channel;
+
+	channel->lba = dmabuf->dma_handle;
+	channel->delta = compute_rate_play(dmabuf->rate);
+
+	channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
+	channel->eso -= 1;
+
+	if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
+		channel->attribute = 0;
+		if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+			if ((channel->num == ALI_SPDIF_IN_CHANNEL) || (channel->num == ALI_PCM_IN_CHANNEL))
+				ali_disable_special_channel(state->card, channel->num);
+			else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE) 
+								&& (channel->num == ALI_SPDIF_OUT_CHANNEL))
+			{
+				ali_set_spdif_out_rate(state->card, state->dmabuf.rate);
+				state->dmabuf.channel->delta = 0x1000;
+			}
+		}
+	}
+
+	channel->fm_vol = 0x0;
+	
+	channel->control = CHANNEL_LOOP;
+	if (dmabuf->fmt & TRIDENT_FMT_16BIT) {
+		/* 16-bits */
+		channel->control |= CHANNEL_16BITS;
+		/* signed */
+		channel->control |= CHANNEL_SIGNED;
+	}
+	if (dmabuf->fmt & TRIDENT_FMT_STEREO)
+		/* stereo */
+		channel->control |= CHANNEL_STEREO;
+#ifdef DEBUG
+	printk("trident: trident_play_setup, LBA = 0x%08x, "
+	       "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n",
+	       channel->lba, channel->delta, channel->eso, channel->control);
+#endif
+	trident_write_voice_regs(state);
+}
+
+/* prepare channel attributes for recording */
+static void trident_rec_setup(struct trident_state *state)
+{
+	u16 w;
+	u8  bval;
+
+	struct trident_card *card = state->card;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct trident_channel *channel = dmabuf->channel;
+	unsigned int rate;
+
+	/* Enable AC-97 ADC (capture) */
+	switch (card->pci_id) 
+	{
+	case PCI_DEVICE_ID_ALI_5451:
+		ali_enable_special_channel(state);
+		break;
+	case PCI_DEVICE_ID_SI_7018:
+		/* for 7018, the ac97 is always in playback/record (duplex) mode */
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+		w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+		outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+		/* enable and set record channel */
+		outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+		w = inw(TRID_REG(card, T4D_MISCINT));
+		outw(w | 0x1000, TRID_REG(card, T4D_MISCINT));
+		/* enable and set record channel */
+		outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH));
+		break;
+	case PCI_DEVICE_ID_INTERG_5050:
+		/* don't know yet, using special channel 22 in GC1(0xd4)? */
+		break;
+	default:
+		return;
+	}
+
+	channel->lba = dmabuf->dma_handle;
+	channel->delta = compute_rate_rec(dmabuf->rate);
+	if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) {
+		rate = ali_get_spdif_in_rate(card);
+		if (rate == 0)
+		{
+			printk(KERN_WARNING "trident: ALi 5451 S/PDIF input setup error!\n");
+			rate = 48000;
+		}
+		bval = inb(TRID_REG(card,ALI_SPDIF_CTRL));
+		if (bval & 0x10)
+		{
+			outb(bval,TRID_REG(card,ALI_SPDIF_CTRL));
+			printk(KERN_WARNING "trident: cleared ALi 5451 S/PDIF parity error flag.\n");
+		}
+
+		if (rate != 48000)
+			channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff;
+	}
+	
+	channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
+	channel->eso -= 1;
+
+	if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
+		channel->attribute = 0;
+	}
+
+	channel->fm_vol = 0x0;
+	
+	channel->control = CHANNEL_LOOP;
+	if (dmabuf->fmt & TRIDENT_FMT_16BIT) {
+		/* 16-bits */
+		channel->control |= CHANNEL_16BITS;
+		/* signed */
+		channel->control |= CHANNEL_SIGNED;
+	}
+	if (dmabuf->fmt & TRIDENT_FMT_STEREO)
+		/* stereo */
+		channel->control |= CHANNEL_STEREO;
+#ifdef DEBUG
+	printk("trident: trident_rec_setup, LBA = 0x%08x, "
+	       "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n",
+	       channel->lba, channel->delta, channel->eso, channel->control);
+#endif
+	trident_write_voice_regs(state);
+}
+
+/* get current playback/recording dma buffer pointer (byte offset from LBA),
+   called with spinlock held! */
+static inline unsigned trident_get_dma_addr(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	u32 cso;
+
+	if (!dmabuf->enable)
+		return 0;
+
+	outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR));
+
+	switch (state->card->pci_id) 
+	{
+	case PCI_DEVICE_ID_ALI_5451:
+	case PCI_DEVICE_ID_SI_7018:
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+	case PCI_DEVICE_ID_INTERG_5050:
+		/* 16 bits ESO, CSO for 7018 and DX */
+		cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2));
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+		/* 24 bits ESO, CSO for NX */
+		cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
+		break;
+	default:
+		return 0;
+	}
+
+#ifdef DEBUG
+	printk("trident: trident_get_dma_addr: chip reported channel: %d, "
+	       "cso = 0x%04x\n",
+	       dmabuf->channel->num, cso);
+#endif
+	/* ESO and CSO are in units of Samples, convert to byte offset */
+	cso <<= sample_shift[dmabuf->fmt];
+
+	return (cso % dmabuf->dmasize);
+}
+
+/* Stop recording (lock held) */
+static inline void __stop_adc(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned int chan_num = dmabuf->channel->num;
+	struct trident_card *card = state->card;
+
+	dmabuf->enable &= ~ADC_RUNNING;
+	trident_stop_voice(card, chan_num);
+	trident_disable_voice_irq(card, chan_num);
+}
+
+static void stop_adc(struct trident_state *state)
+{
+	struct trident_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__stop_adc(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void start_adc(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned int chan_num = dmabuf->channel->num;
+	struct trident_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) {
+		dmabuf->enable |= ADC_RUNNING;
+		trident_enable_voice_irq(card, chan_num);
+		trident_start_voice(card, chan_num);
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* stop playback (lock held) */
+static inline void __stop_dac(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned int chan_num = dmabuf->channel->num;
+	struct trident_card *card = state->card;
+
+	dmabuf->enable &= ~DAC_RUNNING;
+	trident_stop_voice(card, chan_num);
+	if (state->chans_num == 6) {
+		trident_stop_voice(card, state->other_states[0]->dmabuf.channel->num);
+		trident_stop_voice(card, state->other_states[1]->dmabuf.channel->num);
+		trident_stop_voice(card, state->other_states[2]->dmabuf.channel->num);
+		trident_stop_voice(card, state->other_states[3]->dmabuf.channel->num);
+	}
+	trident_disable_voice_irq(card, chan_num);
+}
+
+static void stop_dac(struct trident_state *state)
+{
+	struct trident_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__stop_dac(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}	
+
+static void start_dac(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned int chan_num = dmabuf->channel->num;
+	struct trident_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
+		dmabuf->enable |= DAC_RUNNING;
+		trident_enable_voice_irq(card, chan_num);
+		trident_start_voice(card, chan_num);
+		if (state->chans_num == 6) {
+			trident_start_voice(card, state->other_states[0]->dmabuf.channel->num);
+			trident_start_voice(card, state->other_states[1]->dmabuf.channel->num);
+			trident_start_voice(card, state->other_states[2]->dmabuf.channel->num);
+			trident_start_voice(card, state->other_states[3]->dmabuf.channel->num);
+		}
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+/* allocate DMA buffer, playback and recording buffer should be allocated seperately */
+static int alloc_dmabuf(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	void *rawbuf = NULL;
+	int order;
+	struct page *page, *pend;
+
+	/* alloc as big a chunk as we can, FIXME: is this necessary ?? */
+	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+		if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
+						   PAGE_SIZE << order,
+						   &dmabuf->dma_handle)))
+			break;
+	if (!rawbuf)
+		return -ENOMEM;
+
+#ifdef DEBUG
+	printk("trident: allocated %ld (order = %d) bytes at %p\n",
+	       PAGE_SIZE << order, order, rawbuf);
+#endif
+
+	dmabuf->ready  = dmabuf->mapped = 0;
+	dmabuf->rawbuf = rawbuf;
+	dmabuf->buforder = order;
+	
+	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+	pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
+	for (page = virt_to_page(rawbuf); page <= pend; page++)
+		mem_map_reserve(page);
+
+	return 0;
+}
+
+/* free DMA buffer */
+static void dealloc_dmabuf(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct page *page, *pend;
+
+	if (dmabuf->rawbuf) {
+		/* undo marking the pages as reserved */
+		pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
+		for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
+			mem_map_unreserve(page);
+		pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,
+				    dmabuf->rawbuf, dmabuf->dma_handle);
+	}
+	dmabuf->rawbuf = NULL;
+	dmabuf->mapped = dmabuf->ready = 0;
+}
+
+static int prog_dmabuf(struct trident_state *state, unsigned rec)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned bytepersec;
+	struct trident_state *s = state;
+	unsigned bufsize, dma_nums;
+	unsigned long flags;
+	int ret, i, order;
+	struct page *page, *pend;
+	
+	lock_set_fmt(state);
+	if (state->chans_num == 6)
+		dma_nums = 5;
+	else 	dma_nums = 1;
+	
+	for (i = 0; i < dma_nums; i++) {
+		if (i > 0) {
+			s = state->other_states[i - 1];			
+			dmabuf = &s->dmabuf;
+			dmabuf->fmt = state->dmabuf.fmt;
+			dmabuf->rate = state->dmabuf.rate;
+		}
+
+		spin_lock_irqsave(&s->card->lock, flags);
+		dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0;
+		dmabuf->count = dmabuf->error = 0;
+		spin_unlock_irqrestore(&s->card->lock, flags);
+
+		/* allocate DMA buffer if not allocated yet */
+		if (!dmabuf->rawbuf) {
+			if (i == 0) {
+				if ((ret = alloc_dmabuf(state))) {
+					unlock_set_fmt(state);
+					return ret;
+				}
+			}
+			else {
+				if ((order = state->dmabuf.buforder - 1) >= DMABUF_MINORDER) {
+					dmabuf->rawbuf = pci_alloc_consistent(state->card->pci_dev,
+									      PAGE_SIZE << order,
+									      &dmabuf->dma_handle);
+				}
+				if (!dmabuf->rawbuf) {
+					free_pages((unsigned long)state->dmabuf.rawbuf, state->dmabuf.buforder);
+					state->dmabuf.rawbuf = NULL;
+					i-=2;
+					for (; i >= 0; i--) {
+						pci_free_consistent(state->card->pci_dev,
+								    PAGE_SIZE << state->other_states[i]->dmabuf.buforder,
+								    state->other_states[i]->dmabuf.rawbuf,
+								    state->other_states[i]->dmabuf.dma_handle);
+					}
+					unlock_set_fmt(state);
+					return -ENOMEM;
+				}
+				dmabuf->ready  = dmabuf->mapped = 0;
+				dmabuf->buforder = order;
+				pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << order) - 1);
+				for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
+					mem_map_reserve(page);
+			}
+		}
+		/* FIXME: figure out all this OSS fragment stuff */
+		bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+		bufsize = PAGE_SIZE << dmabuf->buforder;
+		if (dmabuf->ossfragshift) {
+			if ((1000 << dmabuf->ossfragshift) < bytepersec)
+				dmabuf->fragshift = ld2(bytepersec/1000);
+			else
+				dmabuf->fragshift = dmabuf->ossfragshift;
+		} else {
+			/* lets hand out reasonable big ass buffers by default */
+			dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
+		}
+		dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+		while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
+			dmabuf->fragshift--;
+			dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+		}
+		dmabuf->fragsize = 1 << dmabuf->fragshift;
+		if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
+			dmabuf->numfrag = dmabuf->ossmaxfrags;
+		dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
+		dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+
+		memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80,
+		       dmabuf->dmasize);
+
+		spin_lock_irqsave(&s->card->lock, flags);
+		if (rec) {
+			trident_rec_setup(s);
+		} else {
+			trident_play_setup(s);
+		}
+		spin_unlock_irqrestore(&s->card->lock, flags);
+
+		/* set the ready flag for the dma buffer */
+		dmabuf->ready = 1;
+
+#ifdef DEBUG
+	printk("trident: prog_dmabuf(%d), sample rate = %d, format = %d, numfrag = %d, "
+	       "fragsize = %d dmasize = %d\n",
+	       dmabuf->channel->num, dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
+	       dmabuf->fragsize, dmabuf->dmasize);
+#endif
+	}
+	unlock_set_fmt(state);
+	return 0;
+}
+
+/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.
+   |------------|------------|   or   |xxxxxxxxxxxx|------------|   or   |xxxxxxxxxxxx|xxxxxxxxxxxx|
+   but we almost always get this
+   |xxxxxx------|------------|   or   |xxxxxxxxxxxx|xxxxx-------|
+   so we have to clear the tail space to "silence"
+   |xxxxxx000000|------------|   or   |xxxxxxxxxxxx|xxxxxx000000|
+*/
+static void trident_clear_tail(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned swptr;
+	unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80;
+	unsigned int len;
+	unsigned long flags;
+
+	spin_lock_irqsave(&state->card->lock, flags);
+	swptr = dmabuf->swptr;
+	spin_unlock_irqrestore(&state->card->lock, flags);
+
+	if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
+		return;
+
+	if (swptr < dmabuf->dmasize/2)
+		len = dmabuf->dmasize/2 - swptr;
+	else
+		len = dmabuf->dmasize - swptr;
+
+	memset(dmabuf->rawbuf + swptr, silence, len);
+	if(state->card->pci_id != PCI_DEVICE_ID_ALI_5451)
+	{
+		spin_lock_irqsave(&state->card->lock, flags);
+		dmabuf->swptr += len;
+		dmabuf->count += len;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+	}
+
+	/* restart the dma machine in case it is halted */
+	start_dac(state);
+}
+
+static int drain_dac(struct trident_state *state, int nonblock)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	unsigned long tmo;
+	int count;
+	unsigned long diff = 0;
+
+	if (dmabuf->mapped || !dmabuf->ready)
+		return 0;
+
+	add_wait_queue(&dmabuf->wait, &wait);
+	for (;;) {
+		/* It seems that we have to set the current state to TASK_INTERRUPTIBLE
+		   every time to make the process really go to sleep */
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		count = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (count <= 0)
+			break;
+
+		if (signal_pending(current))
+			break;
+
+		if (nonblock) {
+			remove_wait_queue(&dmabuf->wait, &wait);
+			set_current_state(TASK_RUNNING);
+			return -EBUSY;
+		}
+
+		/* No matter how much data is left in the buffer, we have to wait until
+		   CSO == ESO/2 or CSO == ESO when address engine interrupts */
+	 	if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451 ||
+		    state->card->pci_id == PCI_DEVICE_ID_INTERG_5050)
+		{	
+			diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize ;
+			diff = diff % (dmabuf->dmasize);
+			tmo  = (diff * HZ) / dmabuf->rate;
+		}
+		else
+		{
+			tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
+		}
+		tmo >>= sample_shift[dmabuf->fmt];
+		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
+			break;
+		}
+	}
+	remove_wait_queue(&dmabuf->wait, &wait);
+	set_current_state(TASK_RUNNING);
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+
+	return 0;
+}
+
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
+static void trident_update_ptr(struct trident_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned hwptr, swptr;
+	int clear_cnt = 0;
+	int diff;
+	unsigned char silence;
+	unsigned half_dmasize;
+
+	/* update hardware pointer */
+	hwptr = trident_get_dma_addr(state);
+	diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
+	dmabuf->hwptr = hwptr;
+	dmabuf->total_bytes += diff;
+
+	/* error handling and process wake up for ADC */
+	if (dmabuf->enable == ADC_RUNNING) {
+		if (dmabuf->mapped) {
+			dmabuf->count -= diff;
+			if (dmabuf->count >= (signed)dmabuf->fragsize)
+				wake_up(&dmabuf->wait);
+		} else {
+			dmabuf->count += diff;
+
+			if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
+				/* buffer underrun or buffer overrun, we have no way to recover
+				   it here, just stop the machine and let the process force hwptr
+				   and swptr to sync */
+				__stop_adc(state);
+				dmabuf->error++;
+			}
+			if (dmabuf->count < (signed)dmabuf->dmasize/2)
+				wake_up(&dmabuf->wait);
+		}
+	}
+
+	/* error handling and process wake up for DAC */
+	if (dmabuf->enable == DAC_RUNNING) {
+		if (dmabuf->mapped) {
+			dmabuf->count += diff;
+			if (dmabuf->count >= (signed)dmabuf->fragsize)
+				wake_up(&dmabuf->wait);
+		} else {
+			dmabuf->count -= diff;
+
+			if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
+				/* buffer underrun or buffer overrun, we have no way to recover
+				   it here, just stop the machine and let the process force hwptr
+				   and swptr to sync */
+				__stop_dac(state);
+				dmabuf->error++;
+			}
+			else if (!dmabuf->endcleared) {
+				swptr = dmabuf->swptr;
+				silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80);
+				if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) {
+					/* We must clear end data of 1/2 dmabuf if needed.
+					   According to 1/2 algorithm of Address Engine Interrupt,
+					   check the validation of the data of half dmasize. */
+					half_dmasize = dmabuf->dmasize / 2;
+					if ((diff = hwptr - half_dmasize) < 0 )
+						diff = hwptr;
+					if ((dmabuf->count + diff) < half_dmasize) {
+						//there is invalid data in the end of half buffer
+						if ((clear_cnt = half_dmasize - swptr) < 0)
+							clear_cnt += half_dmasize;
+						//clear the invalid data
+						memset (dmabuf->rawbuf + swptr,
+							silence, clear_cnt);
+						if (state->chans_num == 6) {
+						clear_cnt = clear_cnt / 2;
+						swptr = swptr / 2;
+							memset (state->other_states[0]->dmabuf.rawbuf + swptr,
+								silence, clear_cnt);
+							memset (state->other_states[1]->dmabuf.rawbuf + swptr,
+								silence, clear_cnt);
+							memset (state->other_states[2]->dmabuf.rawbuf + swptr,
+								silence, clear_cnt);
+							memset (state->other_states[3]->dmabuf.rawbuf + swptr,
+								silence, clear_cnt);
+						}
+						dmabuf->endcleared = 1;
+					}
+				} else if (dmabuf->count < (signed) dmabuf->fragsize) {
+					clear_cnt = dmabuf->fragsize;
+					if ((swptr + clear_cnt) > dmabuf->dmasize)
+						clear_cnt = dmabuf->dmasize - swptr;
+					memset (dmabuf->rawbuf + swptr, silence, clear_cnt);
+					if (state->chans_num == 6) {
+						clear_cnt = clear_cnt / 2;
+						swptr = swptr / 2;
+						memset (state->other_states[0]->dmabuf.rawbuf + swptr,
+							silence, clear_cnt);
+						memset (state->other_states[1]->dmabuf.rawbuf + swptr,
+							silence, clear_cnt);
+						memset (state->other_states[2]->dmabuf.rawbuf + swptr,
+							silence, clear_cnt);
+						memset (state->other_states[3]->dmabuf.rawbuf + swptr,
+							silence, clear_cnt);
+					}
+					dmabuf->endcleared = 1;
+				}
+			}
+			/* trident_update_ptr is called by interrupt handler or by process via
+			   ioctl/poll, we only wake up the waiting process when we have more
+			   than 1/2 buffer free (always true for interrupt handler) */
+			if (dmabuf->count < (signed)dmabuf->dmasize/2)
+				wake_up(&dmabuf->wait);
+		}
+	}
+	dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE;
+}
+
+static void trident_address_interrupt(struct trident_card *card)
+{
+	int i;
+	struct trident_state *state;
+	
+	/* Update the pointers for all channels we are running. */
+	/* FIXME: should read interrupt status only once */
+	for (i = 0; i < NR_HW_CH; i++) {
+		if (trident_check_channel_interrupt(card, 63 - i)) {
+			trident_ack_channel_interrupt(card, 63 - i);
+			if ((state = card->states[i]) != NULL) {
+				trident_update_ptr(state);
+			} else {
+				printk("trident: spurious channel irq %d.\n",
+				       63 - i);
+				trident_stop_voice(card, 63 - i);
+				trident_disable_voice_irq(card, 63 - i);
+			}
+		}
+	}
+}
+
+static void ali_hwvol_control(struct trident_card *card, int opt)
+{
+	u16 dwTemp, volume[2], mute, diff, *pVol[2];
+
+	dwTemp = ali_ac97_read(card->ac97_codec[0], 0x02);
+	mute = dwTemp & 0x8000;
+	volume[0] = dwTemp & 0x001f;
+	volume[1] = (dwTemp & 0x1f00) >> 8;
+	if (volume[0] < volume [1]) {
+		pVol[0] = &volume[0];
+		pVol[1] = &volume[1];
+	} else {
+		pVol[1] = &volume[0];
+		pVol[0] = &volume[1];
+	}
+	diff = *(pVol[1]) - *(pVol[0]);
+
+	if (opt == 1) {                     // MUTE
+		dwTemp ^= 0x8000;
+		ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);
+	} else if (opt == 2) {   // Down
+		if (mute)
+			return;
+		if (*(pVol[1]) < 0x001f) {
+			(*pVol[1])++;
+			*(pVol[0]) = *(pVol[1]) - diff;
+		}
+		dwTemp &= 0xe0e0;
+		dwTemp |= (volume[0]) | (volume[1] << 8);
+		ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);
+		card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8);
+	} else if (opt == 4) {   // Up
+		if (mute)
+			return;
+		if (*(pVol[0]) >0) {
+			(*pVol[0])--;
+			*(pVol[1]) = *(pVol[0]) + diff;
+		}
+		dwTemp &= 0xe0e0;
+		dwTemp |= (volume[0]) | (volume[1] << 8);
+		ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);
+		card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8);
+	} 
+	else 
+	{
+		/* Nothing needs doing */
+	}
+}
+
+/*
+ *	Re-enable reporting of vol change after 0.1 seconds
+ */
+
+static void ali_timeout(unsigned long ptr)
+{
+	struct trident_card *card = (struct trident_card *)ptr;
+	u16 temp = 0;
+
+	/* Enable GPIO IRQ (MISCINT bit 18h)*/
+	temp = inw(TRID_REG(card, T4D_MISCINT + 2));
+	temp |= 0x0004;
+	outw(temp, TRID_REG(card, T4D_MISCINT + 2));
+}
+
+/*
+ *	Set up the timer to clear the vol change notification
+ */
+ 
+static void ali_set_timer(struct trident_card *card)
+{
+	/* Add Timer Routine to Enable GPIO IRQ */
+	del_timer(&card->timer);	/* Never queue twice */
+	card->timer.function = ali_timeout;
+	card->timer.data = (unsigned long) card;
+	card->timer.expires = jiffies + HZ/10;
+	add_timer(&card->timer);
+}
+
+/*
+ *	Process a GPIO event
+ */
+ 
+static void ali_queue_task(struct trident_card *card, int opt)
+{
+	u16 temp;
+
+	/* Disable GPIO IRQ (MISCINT bit 18h)*/
+	temp = inw(TRID_REG(card, T4D_MISCINT + 2));
+	temp &= (u16)(~0x0004);
+	outw(temp, TRID_REG(card, T4D_MISCINT + 2));
+
+	/* Adjust the volume */
+	ali_hwvol_control(card, opt);
+	
+	/* Set the timer for 1/10th sec */
+	ali_set_timer(card);
+}
+
+static void cyber_address_interrupt(struct trident_card *card)
+{
+	int i,irq_status;
+	struct trident_state *state;
+
+	/* Update the pointers for all channels we are running. */
+	/* FIXED: read interrupt status only once */
+	irq_status=inl(TRID_REG(card, T4D_AINT_A) );
+#ifdef DEBUG	
+	printk("cyber_address_interrupt: irq_status 0x%X\n",irq_status);
+#endif
+	for (i = 0; i < NR_HW_CH; i++) {
+		if (irq_status & ( 1 << (31 - i)) ) {
+
+			/* clear bit by writing a 1, zeroes are ignored */ 		
+			outl( (1 <<(31-i)), TRID_REG(card, T4D_AINT_A));
+		
+#ifdef DEBUG	
+	printk("cyber_interrupt: channel %d\n", 31-i);
+#endif
+			if ((state = card->states[i]) != NULL) {
+				trident_update_ptr(state);
+			} else {
+				printk("cyber5050: spurious channel irq %d.\n",
+				       31 - i);
+				trident_stop_voice(card, 31 - i);
+				trident_disable_voice_irq(card, 31 - i);
+			}
+		}
+	}
+}
+
+static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct trident_card *card = (struct trident_card *)dev_id;
+	u32 event;
+	u32 gpio;
+
+	spin_lock(&card->lock);
+	event = inl(TRID_REG(card, T4D_MISCINT));
+
+#ifdef DEBUG
+	printk("trident: trident_interrupt called, MISCINT = 0x%08x\n", event);
+#endif
+
+	if (event & ADDRESS_IRQ) {
+		card->address_interrupt(card);
+	}
+
+	if(card->pci_id == PCI_DEVICE_ID_ALI_5451)
+	{
+		/* GPIO IRQ (H/W Volume Control) */
+		event = inl(TRID_REG(card, T4D_MISCINT));
+		if (event & (1<<25)) {
+			gpio = inl(TRID_REG(card, ALI_GPIO));
+			if (!timer_pending(&card->timer)) 
+				ali_queue_task(card, gpio&0x07);
+		}
+		event = inl(TRID_REG(card, T4D_MISCINT));
+		outl(event | (ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(card, T4D_MISCINT));
+		spin_unlock(&card->lock);
+		return;
+	}
+
+	/* manually clear interrupt status, bad hardware design, blame T^2 */
+	outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
+	     TRID_REG(card, T4D_MISCINT));
+	spin_unlock(&card->lock);
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
+   the user's buffer.  it is filled by the dma machine and drained by this loop. */
+static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct trident_state *state = (struct trident_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+#ifdef DEBUG
+	printk("trident: trident_read called, count = %d\n", count);
+#endif
+
+	VALIDATE_STATE(state);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+		
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+		
+	down(&state->sem);
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+		goto out;
+
+	while (count > 0) {
+		spin_lock_irqsave(&state->card->lock, flags);
+		if (dmabuf->count > (signed) dmabuf->dmasize) {
+			/* buffer overrun, we are recovering from sleep_on_timeout,
+			   resync hwptr and swptr, make process flush the buffer */
+			dmabuf->count = dmabuf->dmasize;
+			dmabuf->swptr = dmabuf->hwptr;
+		}
+		swptr = dmabuf->swptr;
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count < cnt)
+			cnt = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			unsigned long tmo;
+			/* buffer is empty, start the dma machine and wait for data to be
+			   recorded */
+			start_adc(state);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				goto out;
+			}
+			
+			up(&state->sem);
+			/* No matter how much space left in the buffer, we have to wait until
+			   CSO == ESO/2 or CSO == ESO when address engine interrupts */
+			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
+			tmo >>= sample_shift[dmabuf->fmt];
+			/* There are two situations when sleep_on_timeout returns, one is when
+			   the interrupt is serviced correctly and the process is waked up by
+			   ISR ON TIME. Another is when timeout is expired, which means that
+			   either interrupt is NOT serviced correctly (pending interrupt) or it
+			   is TOO LATE for the process to be scheduled to run (scheduler latency)
+			   which results in a (potential) buffer overrun. And worse, there is
+			   NOTHING we can do to prevent it. */
+			if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
+#ifdef DEBUG
+				printk(KERN_ERR "trident: recording schedule timeout, "
+				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+				       dmabuf->hwptr, dmabuf->swptr);
+#endif
+				/* a buffer overrun, we delay the recovery until next time the
+				   while loop begin and we REALLY have space to record */
+			}
+			if (signal_pending(current)) {
+				if(!ret) ret = -ERESTARTSYS;
+				goto out;
+			}
+			down(&state->sem);
+			if(dmabuf->mapped)
+			{
+				if(!ret)
+					ret = -ENXIO;
+				goto out;
+			}
+			continue;
+		}
+
+		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
+			if (!ret) ret = -EFAULT;
+			goto out;
+		}
+
+		swptr = (swptr + cnt) % dmabuf->dmasize;
+
+		spin_lock_irqsave(&state->card->lock, flags);
+		dmabuf->swptr = swptr;
+		dmabuf->count -= cnt;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(state);
+	}
+out:
+	up(&state->sem);
+	return ret;
+}
+
+/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to
+   the soundcard.  it is drained by the dma machine and filled by this loop. */
+
+static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct trident_state *state = (struct trident_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+	unsigned int state_cnt;
+	unsigned int copy_count;
+
+#ifdef DEBUG
+	printk("trident: trident_write called, count = %d\n", count);
+#endif
+	VALIDATE_STATE(state);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	
+	/*
+	 *	Guard against an mmap or ioctl while writing
+	 */	
+	 
+	down(&state->sem);
+	
+	if (dmabuf->mapped)
+	{
+		ret = -ENXIO;
+		goto out;
+	}
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+		goto out;
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+	{
+		ret= -EFAULT;
+		goto out;
+	}
+	
+	ret = 0;
+
+	while (count > 0) {
+		spin_lock_irqsave(&state->card->lock, flags);
+		if (dmabuf->count < 0) {
+			/* buffer underrun, we are recovering from sleep_on_timeout,
+			   resync hwptr and swptr */
+			dmabuf->count = 0;
+			dmabuf->swptr = dmabuf->hwptr;
+		}
+		swptr = dmabuf->swptr;
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count + cnt > dmabuf->dmasize)
+			cnt = dmabuf->dmasize - dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			unsigned long tmo;
+			/* buffer is full, start the dma machine and wait for data to be
+			   played */
+			start_dac(state);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				goto out;
+			}
+			/* No matter how much data left in the buffer, we have to wait until
+			   CSO == ESO/2 or CSO == ESO when address engine interrupts */
+			lock_set_fmt(state);
+			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
+			tmo >>= sample_shift[dmabuf->fmt];
+			unlock_set_fmt(state);
+			up(&state->sem);
+			
+			/* There are two situations when sleep_on_timeout returns, one is when
+			   the interrupt is serviced correctly and the process is waked up by
+			   ISR ON TIME. Another is when timeout is expired, which means that
+			   either interrupt is NOT serviced correctly (pending interrupt) or it
+			   is TOO LATE for the process to be scheduled to run (scheduler latency)
+			   which results in a (potential) buffer underrun. And worse, there is
+			   NOTHING we can do to prevent it. */
+			if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
+#ifdef DEBUG
+				printk(KERN_ERR "trident: playback schedule timeout, "
+				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+				       dmabuf->hwptr, dmabuf->swptr);
+#endif
+				/* a buffer underrun, we delay the recovery until next time the
+				   while loop begin and we REALLY have data to play */
+			}
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				goto out;
+			}
+			down(&state->sem);
+			if(dmabuf->mapped)
+			{
+				if(!ret)
+					ret = -ENXIO;
+				goto out;
+			}
+			continue;
+		}
+		lock_set_fmt(state);
+		if (state->chans_num == 6) {
+			copy_count = 0;
+			state_cnt = 0;
+			if (ali_write_5_1(state, buffer, cnt, &copy_count, &state_cnt) == -EFAULT) {
+				if (state_cnt){
+					swptr = (swptr + state_cnt) % dmabuf->dmasize;
+					spin_lock_irqsave(&state->card->lock, flags);
+					dmabuf->swptr = swptr;
+					dmabuf->count += state_cnt;
+					dmabuf->endcleared = 0;
+					spin_unlock_irqrestore(&state->card->lock, flags);
+				}
+				ret += copy_count;
+				if (!ret) ret = -EFAULT;
+				unlock_set_fmt(state);
+				goto out;
+			}
+		}
+		else {
+			if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+				if (!ret) ret = -EFAULT;
+				unlock_set_fmt(state);
+				goto out;
+			}
+			state_cnt = cnt;
+		}
+		unlock_set_fmt(state);
+		
+		swptr = (swptr + state_cnt) % dmabuf->dmasize;		
+		
+		spin_lock_irqsave(&state->card->lock, flags);
+		dmabuf->swptr = swptr;
+		dmabuf->count += state_cnt;
+		dmabuf->endcleared = 0;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+
+		count -= cnt;
+		buffer += cnt;	
+		ret += cnt;
+		start_dac(state);
+	}
+out:
+	up(&state->sem);
+	return ret;
+}
+
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct trident_state *state = (struct trident_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(state);
+
+	/*
+	 *	Guard against a parallel poll and write causing multiple
+	 *	prog_dmabuf events
+	 */
+	 
+	down(&state->sem);
+
+	if (file->f_mode & FMODE_WRITE) {
+		if (!dmabuf->ready && prog_dmabuf(state, 0))
+		{
+			up(&state->sem);
+			return 0;
+		}
+		poll_wait(file, &dmabuf->wait, wait);
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!dmabuf->ready && prog_dmabuf(state, 1))
+		{
+			up(&state->sem);
+			return 0;
+		}
+		poll_wait(file, &dmabuf->wait, wait);
+	}
+
+	up(&state->sem);
+	
+	spin_lock_irqsave(&state->card->lock, flags);
+	trident_update_ptr(state);
+	if (file->f_mode & FMODE_READ) {
+		if (dmabuf->count >= (signed)dmabuf->fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (dmabuf->mapped) {
+			if (dmabuf->count >= (signed)dmabuf->fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&state->card->lock, flags);
+
+	return mask;
+}
+
+static int trident_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct trident_state *state = (struct trident_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int ret = -EINVAL;
+	unsigned long size;
+
+	VALIDATE_STATE(state);
+	lock_kernel();
+	
+	/*
+	 *	Lock against poll read write or mmap creating buffers. Also lock
+	 *	a read or write against an mmap.
+	 */
+	 
+	down(&state->sem);
+	
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(state, 0)) != 0)
+			goto out;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(state, 1)) != 0)
+			goto out;
+	} else
+		goto out;
+
+	ret = -EINVAL;
+	if (vma->vm_pgoff != 0)
+		goto out;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << dmabuf->buforder))
+		goto out;
+	ret = -EAGAIN;
+	if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+			     size, vma->vm_page_prot))
+		goto out;
+	dmabuf->mapped = 1;
+	ret = 0;
+out:
+	up(&state->sem);
+	unlock_kernel();
+	return ret;
+}
+
+static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct trident_state *state = (struct trident_state *)file->private_data;
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int val, mapped, ret = 0;
+
+	struct trident_card *card = state->card;
+
+	VALIDATE_STATE(state);
+	mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
+		((file->f_mode & FMODE_READ) && dmabuf->mapped);
+#ifdef DEBUG
+	printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
+	       _IOC_NR(cmd), arg ? *(int *)arg : 0);
+#endif
+
+	switch (cmd) 
+	{
+	case OSS_GETVERSION:
+		ret = put_user(SOUND_VERSION, (int *)arg);
+		break;
+		
+	case SNDCTL_DSP_RESET:
+		/* FIXME: spin_lock ? */
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(state);
+			synchronize_irq();
+			dmabuf->ready = 0;
+			dmabuf->swptr = dmabuf->hwptr = 0;
+			dmabuf->count = dmabuf->total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(state);
+			synchronize_irq();
+			dmabuf->ready = 0;
+			dmabuf->swptr = dmabuf->hwptr = 0;
+			dmabuf->count = dmabuf->total_bytes = 0;
+		}
+		break;
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			ret = drain_dac(state, file->f_flags & O_NONBLOCK);
+		break;
+
+	case SNDCTL_DSP_SPEED: /* set smaple rate */
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		if (val >= 0) {
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(state);
+				dmabuf->ready = 0;
+				spin_lock_irqsave(&state->card->lock, flags);
+				trident_set_dac_rate(state, val);
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			}
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+				dmabuf->ready = 0;
+				spin_lock_irqsave(&state->card->lock, flags);
+				trident_set_adc_rate(state, val);
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			}
+		}
+		ret = put_user(dmabuf->rate, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		lock_set_fmt(state);
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(state);
+			dmabuf->ready = 0;
+			if (val)
+				dmabuf->fmt |= TRIDENT_FMT_STEREO;
+			else
+				dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(state);
+			dmabuf->ready = 0;
+			if (val)
+				dmabuf->fmt |= TRIDENT_FMT_STEREO;
+			else
+				dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
+		}
+		unlock_set_fmt(state);
+		break;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(state, 0)))
+				ret = val;
+			else
+				ret = put_user(dmabuf->fragsize, (int *)arg);
+			break;
+		}
+		if (file->f_mode & FMODE_READ) {
+			if ((val = prog_dmabuf(state, 1)))
+				ret = val;
+			else
+				ret = put_user(dmabuf->fragsize, (int *)arg);
+			break;
+		}
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+		ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_SETFMT: /* Select sample format */
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		lock_set_fmt(state);
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(state);
+				dmabuf->ready = 0;
+				if (val == AFMT_S16_LE)
+					dmabuf->fmt |= TRIDENT_FMT_16BIT;
+				else
+					dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
+			}
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+				dmabuf->ready = 0;
+				if (val == AFMT_S16_LE)
+					dmabuf->fmt |= TRIDENT_FMT_16BIT;
+				else
+					dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
+			}
+		}
+		unlock_set_fmt(state);
+		ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+				AFMT_S16_LE : AFMT_U8, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		if (val != 0) {
+			lock_set_fmt(state);
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(state);
+				dmabuf->ready = 0;
+								
+				//prevent from memory leak
+				if ((state->chans_num > 2) && (state->chans_num != val)) {
+					ali_free_other_states_resources(state);
+					state->chans_num = 1;
+				}
+				
+				if (val >= 2)
+				{
+
+					dmabuf->fmt |= TRIDENT_FMT_STEREO;
+					if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) {
+
+						if( card->rec_channel_use_count > 0 )
+						{
+							printk(KERN_ERR "trident: Record is working on the card!\n");
+							ret = -EBUSY;
+							break;
+						}
+
+						ret = ali_setup_multi_channels(state->card, 6);
+						if (ret < 0) {
+							unlock_set_fmt(state);
+							break;
+						}
+						down(&state->card->open_sem);
+						ret = ali_allocate_other_states_resources(state, 6);
+						if (ret < 0) {
+							up(&state->card->open_sem);
+							unlock_set_fmt(state);
+							break;
+						}
+						state->card->multi_channel_use_count ++;
+						up(&state->card->open_sem);
+					}
+					else val = 2;	/*yield to 2-channels*/
+				}
+				else
+					dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
+				state->chans_num = val;
+			}
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(state);
+				dmabuf->ready = 0;
+				if (val >= 2) {
+					if (!((file->f_mode & FMODE_WRITE) && (val == 6)))
+						val = 2;
+					dmabuf->fmt |= TRIDENT_FMT_STEREO;
+				}
+				else
+					dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
+				state->chans_num = val;
+			}
+			unlock_set_fmt(state);
+		}
+		ret = put_user(val, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_POST:
+		/* Cause the working fragment to be output */
+		break;
+
+	case SNDCTL_DSP_SUBDIVIDE:
+		if (dmabuf->subdivision)
+		{
+			ret = -EINVAL;
+			break;
+		}
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		if (val != 1 && val != 2 && val != 4)
+		{
+			ret = -EINVAL;
+			break;
+		}
+		dmabuf->subdivision = val;
+		break;
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+
+		dmabuf->ossfragshift = val & 0xffff;
+		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+		if (dmabuf->ossfragshift < 4)
+			dmabuf->ossfragshift = 4;
+		if (dmabuf->ossfragshift > 15)
+			dmabuf->ossfragshift = 15;
+		if (dmabuf->ossmaxfrags < 4)
+			dmabuf->ossmaxfrags = 4;
+
+		break;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+		{
+			ret = -EINVAL;
+			break;
+		}
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+		{ 
+			ret = val;
+			break;
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		trident_update_ptr(state);
+		abinfo.fragsize = dmabuf->fragsize;
+		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+		abinfo.fragstotal = dmabuf->numfrag;
+		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		break;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+		{
+			ret = -EINVAL;
+			break;
+		}
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+		{
+			ret = val;
+			break;
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		trident_update_ptr(state);
+		abinfo.fragsize = dmabuf->fragsize;
+		abinfo.bytes = dmabuf->count;
+		abinfo.fragstotal = dmabuf->numfrag;
+		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		break;
+
+	case SNDCTL_DSP_NONBLOCK:
+		file->f_flags |= O_NONBLOCK;
+		break;
+
+	case SNDCTL_DSP_GETCAPS:
+		ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
+			    (int *)arg);
+		break;
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if ((file->f_mode & FMODE_READ) && dmabuf->enable)
+			val |= PCM_ENABLE_INPUT;
+		if ((file->f_mode & FMODE_WRITE) && dmabuf->enable)
+			val |= PCM_ENABLE_OUTPUT;
+		ret = put_user(val, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+					break;
+				start_adc(state);
+			} else
+				stop_adc(state);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+					break;
+				start_dac(state);
+			} else
+				stop_dac(state);
+		}
+		break;
+
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+		{
+			ret = -EINVAL;
+			break;
+		}
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+		{
+			ret = val;
+			break;
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		trident_update_ptr(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.ptr = dmabuf->hwptr;
+		if (dmabuf->mapped)
+			dmabuf->count &= dmabuf->fragsize-1;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+		break;
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+		{
+			ret = -EINVAL;
+			break;
+		}
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+		{
+			ret = val;
+			break;
+		}
+		
+		spin_lock_irqsave(&state->card->lock, flags);
+		trident_update_ptr(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.ptr = dmabuf->hwptr;
+		if (dmabuf->mapped)
+			dmabuf->count &= dmabuf->fragsize-1;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0;
+		break;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		ret = -EINVAL;
+		break;
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+		{
+			ret = -EINVAL;
+			break;
+		}
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+		{
+			ret = val;
+			break;
+		}
+		spin_lock_irqsave(&state->card->lock, flags);
+		trident_update_ptr(state);
+		val = dmabuf->count;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+		ret = put_user(val, (int *)arg);
+		break;
+
+	case SOUND_PCM_READ_RATE:
+		ret = put_user(dmabuf->rate, (int *)arg);
+		break;
+
+	case SOUND_PCM_READ_CHANNELS:
+		ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
+				(int *)arg);
+		break;
+
+	case SOUND_PCM_READ_BITS:
+		ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
+				AFMT_S16_LE : AFMT_U8, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_GETCHANNELMASK:
+		ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE,
+				(int *)arg);
+		break;
+
+	case SNDCTL_DSP_BIND_CHANNEL:
+		if (state->card->pci_id != PCI_DEVICE_ID_SI_7018)
+		{
+			ret = -EINVAL;
+			break;
+		}
+
+		if (get_user(val, (int *)arg))
+		{
+			ret = -EFAULT;
+			break;
+		}
+		if (val == DSP_BIND_QUERY) {
+			val = dmabuf->channel->attribute | 0x3c00;
+			val = attr2mask[val >> 8];
+		} else {
+			dmabuf->ready = 0;
+			if (file->f_mode & FMODE_READ)
+				dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE);
+			if (file->f_mode & FMODE_WRITE)
+				dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE);
+			dmabuf->channel->attribute |= mask2attr[ffs(val)];
+		}
+		ret = put_user(val, (int *)arg);
+		break;
+
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_WRITE_FILTER:
+	case SOUND_PCM_READ_FILTER:
+	default:
+		ret = -EINVAL;
+		break;
+		
+	}
+	return ret;
+}
+
+static int trident_open(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	int minor = minor(inode->i_rdev);
+	struct trident_card *card = devs;
+	struct trident_state *state = NULL;
+	struct dmabuf *dmabuf = NULL;
+	
+	/* Added by Matt Wu 01-05-2001 */
+	if(file->f_mode & FMODE_READ)
+	{
+		if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+			if (card->multi_channel_use_count > 0)
+				return -EBUSY;
+		}
+	}
+
+	/* find an available virtual channel (instance of /dev/dsp) */
+	while (card != NULL) {
+		down(&card->open_sem);
+		if(file->f_mode & FMODE_READ)
+		{
+			/* Skip opens on cards that are in 6 channel mode */
+			if (card->multi_channel_use_count > 0)
+			{
+				up(&card->open_sem);
+				card = card->next;
+				continue;
+			}
+		}
+		for (i = 0; i < NR_HW_CH; i++) {
+			if (card->states[i] == NULL) {
+				state = card->states[i] = (struct trident_state *)
+					kmalloc(sizeof(struct trident_state), GFP_KERNEL);
+				if (state == NULL) {
+					return -ENOMEM;
+				}
+				memset(state, 0, sizeof(struct trident_state));
+				init_MUTEX(&state->sem);
+				dmabuf = &state->dmabuf;
+				goto found_virt;
+			}
+		}
+		up(&card->open_sem);
+		card = card->next;
+	}
+	/* no more virtual channel avaiable */
+	if (!state) {
+		return -ENODEV;
+	}
+ found_virt:
+	/* found a free virtual channel, allocate hardware channels */
+	if(file->f_mode & FMODE_READ)
+		dmabuf->channel = card->alloc_rec_pcm_channel(card);
+	else
+		dmabuf->channel = card->alloc_pcm_channel(card);
+		
+	if (dmabuf->channel == NULL) {
+		kfree (card->states[i]);
+		card->states[i] = NULL;
+		return -ENODEV;
+	}
+
+	/* initialize the virtual channel */
+	state->virt = i;
+	state->card = card;
+	state->magic = TRIDENT_STATE_MAGIC;
+	init_waitqueue_head(&dmabuf->wait);
+	file->private_data = state;
+
+	/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
+	   should be default to unsigned 8-bits, mono, with sample rate 8kHz and
+	   /dev/dspW will accept 16-bits sample */
+	if (file->f_mode & FMODE_WRITE) {
+		dmabuf->fmt &= ~TRIDENT_FMT_MASK;
+		if ((minor & 0x0f) == SND_DEV_DSP16)
+			dmabuf->fmt |= TRIDENT_FMT_16BIT;
+		dmabuf->ossfragshift = 0;
+		dmabuf->ossmaxfrags  = 0;
+		dmabuf->subdivision  = 0;
+		if (card->pci_id == PCI_DEVICE_ID_SI_7018) {
+			/* set default channel attribute to normal playback */
+			dmabuf->channel->attribute = CHANNEL_PB;
+		}
+		trident_set_dac_rate(state, 8000);
+	}
+
+	if (file->f_mode & FMODE_READ) {
+		/* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample,
+		   to be dealed with in trident_set_adc_rate() ?? */
+		dmabuf->fmt &= ~TRIDENT_FMT_MASK;
+		if ((minor & 0x0f) == SND_DEV_DSP16)
+			dmabuf->fmt |= TRIDENT_FMT_16BIT;
+		dmabuf->ossfragshift = 0;
+		dmabuf->ossmaxfrags  = 0;
+		dmabuf->subdivision  = 0;
+		if (card->pci_id == PCI_DEVICE_ID_SI_7018) {
+			/* set default channel attribute to 0x8a80, record from
+			   PCM L/R FIFO and mono = (left + right + 1)/2*/
+			dmabuf->channel->attribute =
+				(CHANNEL_REC|PCM_LR|MONO_MIX);
+		}
+		trident_set_adc_rate(state, 8000);
+	
+		/* Added by Matt Wu 01-05-2001 */
+		if(card->pci_id == PCI_DEVICE_ID_ALI_5451)
+			card->rec_channel_use_count ++;
+	}
+
+	state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&card->open_sem);
+
+#ifdef DEBUG
+       printk(KERN_ERR "trident: open virtual channel %d, hard channel %d\n", 
+              state->virt, dmabuf->channel->num);
+#endif
+
+	return 0;
+}
+
+static int trident_release(struct inode *inode, struct file *file)
+{
+	struct trident_state *state = (struct trident_state *)file->private_data;
+	struct trident_card *card;
+	struct dmabuf *dmabuf;
+	unsigned long flags;
+
+	lock_kernel();
+	card = state->card;
+	dmabuf = &state->dmabuf;
+	VALIDATE_STATE(state);
+
+	if (file->f_mode & FMODE_WRITE) {
+		trident_clear_tail(state);
+		drain_dac(state, file->f_flags & O_NONBLOCK);
+	}
+
+#ifdef DEBUG
+	printk(KERN_ERR "trident: closing virtual channel %d, hard channel %d\n", 
+		state->virt, dmabuf->channel->num);
+#endif
+
+	/* stop DMA state machine and free DMA buffers/channels */
+	down(&card->open_sem);
+
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(state);
+		lock_set_fmt(state);
+
+		unlock_set_fmt(state);
+		dealloc_dmabuf(state);
+		state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+
+		/* Added by Matt Wu */
+		if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+			if (state->chans_num > 2) {
+				if (card->multi_channel_use_count-- < 0)
+					card->multi_channel_use_count = 0;
+				if (card->multi_channel_use_count == 0)
+					ali_close_multi_channels();
+				ali_free_other_states_resources(state);
+			}
+		}
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(state);
+		dealloc_dmabuf(state);
+		state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+
+		/* Added by Matt Wu */
+		if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+			if( card->rec_channel_use_count-- < 0 )
+				card->rec_channel_use_count = 0;
+		}
+	}
+
+	card->states[state->virt] = NULL;
+	kfree(state);
+
+	/* we're covered by the open_sem */
+	up(&card->open_sem);
+	unlock_kernel();
+
+	return 0;
+}
+
+static /*const*/ struct file_operations trident_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		trident_read,
+	write:		trident_write,
+	poll:		trident_poll,
+	ioctl:		trident_ioctl,
+	mmap:		trident_mmap,
+	open:		trident_open,
+	release:	trident_release,
+};
+
+/* trident specific AC97 functions */
+/* Write AC97 codec registers */
+static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val)
+{
+	struct trident_card *card = (struct trident_card *)codec->private_data;
+	unsigned int address, mask, busy;
+	unsigned short count  = 0xffff;
+	unsigned long flags;
+	u32 data;
+
+	data = ((u32) val) << 16;
+
+	switch (card->pci_id)
+	{
+	default:
+	case PCI_DEVICE_ID_SI_7018:
+		address = SI_AC97_WRITE;
+		mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY;
+		if (codec->id)
+			mask |= SI_AC97_SECONDARY;
+		busy = SI_AC97_BUSY_WRITE;
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+		address = DX_ACR0_AC97_W;
+		mask = busy = DX_AC97_BUSY_WRITE;
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+		address = NX_ACR1_AC97_W;
+		mask = NX_AC97_BUSY_WRITE;
+		if (codec->id)
+			mask |= NX_AC97_WRITE_SECONDARY;
+		busy = NX_AC97_BUSY_WRITE;
+		break;
+	case PCI_DEVICE_ID_INTERG_5050:
+		address = SI_AC97_WRITE;
+		mask = busy = SI_AC97_BUSY_WRITE;
+		if (codec->id)
+			mask |= SI_AC97_SECONDARY;
+		break;
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	do {
+		if ((inw(TRID_REG(card, address)) & busy) == 0)
+			break;
+	} while (count--);
+
+
+	data |= (mask | (reg & AC97_REG_ADDR));
+
+	if (count == 0) {
+		printk(KERN_ERR "trident: AC97 CODEC write timed out.\n");
+		spin_unlock_irqrestore(&card->lock, flags);
+		return;
+	}
+
+	outl(data, TRID_REG(card, address));
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* Read AC97 codec registers */
+static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg)
+{
+	struct trident_card *card = (struct trident_card *)codec->private_data;
+	unsigned int address, mask, busy;
+	unsigned short count = 0xffff;
+	unsigned long flags;
+	u32 data;
+
+	switch (card->pci_id)
+	{
+	default:
+	case PCI_DEVICE_ID_SI_7018:
+		address = SI_AC97_READ;
+		mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY;
+		if (codec->id)
+			mask |= SI_AC97_SECONDARY;
+		busy = SI_AC97_BUSY_READ;
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+		address = DX_ACR1_AC97_R;
+		mask = busy = DX_AC97_BUSY_READ;
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+		if (codec->id)
+			address = NX_ACR3_AC97_R_SECONDARY;
+		else
+			address = NX_ACR2_AC97_R_PRIMARY;
+		mask = NX_AC97_BUSY_READ;
+		busy = NX_AC97_BUSY_READ | NX_AC97_BUSY_DATA;
+		break;
+	case PCI_DEVICE_ID_INTERG_5050:
+		address = SI_AC97_READ;
+		mask = busy = SI_AC97_BUSY_READ;
+		if (codec->id)
+			mask |= SI_AC97_SECONDARY;
+		break;
+	}
+
+	data = (mask | (reg & AC97_REG_ADDR));
+
+	spin_lock_irqsave(&card->lock, flags);
+	outl(data, TRID_REG(card, address));
+	do {
+		data = inl(TRID_REG(card, address));
+		if ((data & busy) == 0)
+			break;
+	} while (count--);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	if (count == 0) {
+		printk(KERN_ERR "trident: AC97 CODEC read timed out.\n");
+		data = 0;
+	}
+	return ((u16) (data >> 16));
+}
+
+/* Write AC97 codec registers for ALi*/
+static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val)
+{
+	unsigned int address, mask;
+	unsigned int wCount1 = 0xffff;
+	unsigned int wCount2= 0xffff;
+	unsigned long chk1, chk2;
+	unsigned long flags;
+	u32 data;
+
+	data = ((u32) val) << 16;
+
+	if(!card)
+		BUG();
+		
+	address = ALI_AC97_WRITE;
+	mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
+	if (secondary)
+		mask |= ALI_AC97_SECONDARY;
+	if (card->revision == ALI_5451_V02)
+		mask |= ALI_AC97_WRITE_MIXER_REGISTER;
+		
+	spin_lock_irqsave(&card->lock, flags);
+	while (wCount1--) {
+		if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_WRITE) == 0) {
+			data |= (mask | (reg & AC97_REG_ADDR));
+			
+			chk1 = inl(TRID_REG(card,  ALI_STIMER));
+			chk2 = inl(TRID_REG(card,  ALI_STIMER));
+			while (wCount2-- && (chk1 == chk2))
+				chk2 = inl(TRID_REG(card,  ALI_STIMER));
+			if (wCount2 == 0) {
+				spin_unlock_irqrestore(&card->lock, flags);
+				return;
+			}
+			outl(data, TRID_REG(card, address));	//write!
+			spin_unlock_irqrestore(&card->lock, flags);
+			return;	//success
+		}
+		inw(TRID_REG(card, address));	//wait for a read cycle
+	}
+
+	printk(KERN_ERR "ali: AC97 CODEC write timed out.\n");
+	spin_unlock_irqrestore(&card->lock, flags);
+	return;
+}
+
+/* Read AC97 codec registers for ALi*/
+static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg)
+{
+	unsigned int address, mask;
+        unsigned int wCount1 = 0xffff;
+        unsigned int wCount2= 0xffff;
+        unsigned long chk1, chk2;
+	unsigned long flags;
+	u32 data;
+
+	if(!card)
+		BUG();
+
+	address = ALI_AC97_READ;
+	if (card->revision == ALI_5451_V02) {
+		address = ALI_AC97_WRITE;
+	}
+	mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY;
+	if (secondary)
+		mask |= ALI_AC97_SECONDARY;
+
+	spin_lock_irqsave(&card->lock, flags);
+	data = (mask | (reg & AC97_REG_ADDR));
+	while (wCount1--) {
+		if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) {
+			chk1 = inl(TRID_REG(card,  ALI_STIMER));
+			chk2 = inl(TRID_REG(card,  ALI_STIMER));
+			while (wCount2-- && (chk1 == chk2))
+				chk2 = inl(TRID_REG(card,  ALI_STIMER));
+			if (wCount2 == 0) {
+				printk(KERN_ERR "ali: AC97 CODEC read timed out.\n");
+				spin_unlock_irqrestore(&card->lock, flags);
+				return 0;
+			}
+			outl(data, TRID_REG(card, address));	//read!
+			wCount2 = 0xffff;
+			while (wCount2--) {
+				if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) {
+					data = inl(TRID_REG(card, address));
+					spin_unlock_irqrestore(&card->lock, flags);
+					return ((u16) (data >> 16));
+				}
+			}
+		}
+		inw(TRID_REG(card, address));	//wait a read cycle
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+	printk(KERN_ERR "ali: AC97 CODEC read timed out.\n");
+	return 0;
+}
+
+static void ali_enable_special_channel(struct trident_state *stat)
+{
+	struct trident_card *card = stat->card;
+	unsigned long s_channels;
+	
+	s_channels = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+	s_channels |= (1<<stat->dmabuf.channel->num);
+	outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL));
+}
+
+static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg)
+{
+	int id;
+	u16 data;
+	struct trident_card *card = NULL;
+
+	/* Added by Matt Wu */
+	if (!codec)
+		BUG();
+		
+	card = (struct trident_card *)codec->private_data;
+
+	if(!card->mixer_regs_ready)
+		return ali_ac97_get(card, codec->id, reg);
+
+	if(codec->id)
+		id = 1;
+	else
+		id = 0;
+
+	data = card->mixer_regs[reg/2][id];
+	return data;
+}
+
+static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
+{
+	int id;
+	struct trident_card *card;
+
+	/*  Added by Matt Wu */
+	if (!codec)
+		BUG();
+		
+	card = (struct trident_card *)codec->private_data;
+
+	if (!card->mixer_regs_ready)
+	{
+		ali_ac97_set(card, codec->id, reg, val);
+		return;
+	}
+
+	if(codec->id)
+		id = 1;
+	else
+		id = 0;
+
+	card->mixer_regs[reg/2][id] = val;
+	ali_ac97_set(card, codec->id, reg, val);
+}
+
+/*
+flag:	ALI_SPDIF_OUT_TO_SPDIF_OUT
+	ALI_PCM_TO_SPDIF_OUT
+*/
+
+static void ali_setup_spdif_out(struct trident_card *card, int flag)
+{
+	unsigned long spdif;
+	unsigned char ch;
+
+        char temp;
+        struct pci_dev *pci_dev = NULL;
+
+        pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
+        if (pci_dev == NULL)
+                return;
+        pci_read_config_byte(pci_dev, 0x61, &temp);
+        temp |= 0x40;
+        pci_write_config_byte(pci_dev, 0x61, temp);
+        pci_read_config_byte(pci_dev, 0x7d, &temp);
+        temp |= 0x01;
+        pci_write_config_byte(pci_dev, 0x7d, temp);
+        pci_read_config_byte(pci_dev, 0x7e, &temp);
+        temp &= (~0x20);
+        temp |= 0x10;
+        pci_write_config_byte(pci_dev, 0x7e, temp);
+
+	ch = inb(TRID_REG(card, ALI_SCTRL));
+	outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL));
+	ch = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+	outb(ch & ALI_SPDIF_OUT_CH_STATUS, TRID_REG(card, ALI_SPDIF_CTRL));
+   
+	if (flag & ALI_SPDIF_OUT_TO_SPDIF_OUT) {
+  		spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
+   		spdif |= ALI_SPDIF_OUT_CH_ENABLE;
+   		spdif &= ALI_SPDIF_OUT_SEL_SPDIF;
+   		outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+		spdif = inw(TRID_REG(card, ALI_SPDIF_CS));
+		if (flag & ALI_SPDIF_OUT_NON_PCM)
+   			spdif |= 0x0002;
+		else	spdif &= (~0x0002);
+   		outw(spdif, TRID_REG(card, ALI_SPDIF_CS));
+   	}
+   	else {
+   		spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
+   		spdif |= ALI_SPDIF_OUT_SEL_PCM;
+   		outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+   	}
+}
+
+static void ali_disable_special_channel(struct trident_card *card, int ch)
+{
+	unsigned long sc;
+	
+	sc = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+	sc &= ~(1 << ch);
+	outl(sc, TRID_REG(card, ALI_GLOBAL_CONTROL));
+}
+
+static void ali_disable_spdif_in(struct trident_card *card)
+{
+	unsigned long spdif;
+	
+	spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+	spdif &= (~ALI_SPDIF_IN_SUPPORT);
+	outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+	
+	ali_disable_special_channel(card, ALI_SPDIF_IN_CHANNEL);	
+}
+
+static void ali_setup_spdif_in(struct trident_card *card)
+{	
+	unsigned long spdif;
+
+	//Set SPDIF IN Supported
+	spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+	spdif |= ALI_SPDIF_IN_SUPPORT;
+	outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+
+	//Set SPDIF IN Rec
+	spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
+	spdif |= ALI_SPDIF_IN_CH_ENABLE;
+	outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
+
+	spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+	spdif |= ALI_SPDIF_IN_CH_STATUS;
+	outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+/*
+	spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+	spdif |= ALI_SPDIF_IN_FUNC_ENABLE;
+	outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+*/
+}
+
+static void ali_delay(struct trident_card *card,int interval)
+{
+	unsigned long  begintimer,currenttimer;
+
+	begintimer   = inl(TRID_REG(card,  ALI_STIMER));
+	currenttimer = inl(TRID_REG(card,  ALI_STIMER));
+
+	while (currenttimer < begintimer + interval)
+		currenttimer = inl(TRID_REG(card,  ALI_STIMER));
+}
+
+static void ali_detect_spdif_rate(struct trident_card *card)
+{
+	u16 wval  = 0;
+	u16 count = 0;
+	u8  bval = 0, R1 = 0, R2 = 0;
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CTRL));
+	bval |= 0x02;
+	outb(bval,TRID_REG(card,ALI_SPDIF_CTRL));
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1));
+	bval |= 0x1F;
+	outb(bval,TRID_REG(card,ALI_SPDIF_CTRL + 1));
+
+	while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000)
+	{
+		count ++;
+
+		ali_delay(card, 6);
+
+		bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1));
+		R1 = bval & 0x1F;
+	}
+
+	if (count > 50000)
+	{
+		printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n");
+		return;
+	}
+
+	count = 0;
+
+	while (count <= 50000)
+	{
+		count ++;
+
+		ali_delay(card, 6);
+
+		bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1));
+		R2 = bval & 0x1F;
+
+		if(R2 != R1)
+			R1 = R2;
+		else
+			break;
+	}
+
+	if (count > 50000)
+	{
+		printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n");
+		return;
+	}
+
+	switch (R2)
+	{
+	case 0x0b:
+	case 0x0c:
+	case 0x0d:
+	case 0x0e:
+		wval  = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2));
+		wval &= 0xE0F0;
+		wval |= (u16)0x09 << 8 | (u16)0x05;
+		outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0;
+		outb(bval|0x02,TRID_REG(card,ALI_SPDIF_CS + 3));
+		break;
+
+	case 0x12:
+		wval  = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2));
+		wval &= 0xE0F0;
+		wval |= (u16)0x0E << 8 | (u16)0x08;
+		outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0;
+		outb(bval|0x03,TRID_REG(card,ALI_SPDIF_CS + 3));
+		break;
+
+	default:
+		break;
+	}
+
+}
+
+static unsigned int ali_get_spdif_in_rate(struct trident_card *card)
+{
+	u32	dwRate = 0;
+	u8	bval = 0;
+
+	ali_detect_spdif_rate(card);
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CTRL));
+	bval &= 0x7F;
+	bval |= 0x40;
+	outb(bval,TRID_REG(card,ALI_SPDIF_CTRL));
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CS + 3));
+	bval &= 0x0F;
+
+	switch (bval)
+	{
+	case 0:
+		dwRate = 44100;
+		break;
+	case 1:
+		dwRate = 48000;
+		break;
+	case 2:
+		dwRate = 32000;
+		break;
+	default:
+		// Error occurs
+		break;
+	}
+
+	return dwRate;
+	
+}
+
+static int ali_close_multi_channels(void)
+{
+	char temp = 0;
+	struct pci_dev *pci_dev = NULL;
+
+        pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev);
+        if (pci_dev == NULL)
+                return -1;
+	temp = 0x80;
+	pci_write_config_byte(pci_dev, 0x59, ~temp);
+	
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev);
+	if (pci_dev == NULL)
+                return -1;
+
+	temp = 0x20;
+	pci_write_config_byte(pci_dev, 0xB8, ~temp);
+
+	return 0;
+}
+
+static int ali_setup_multi_channels(struct trident_card *card, int chan_nums)
+{
+	unsigned long dwValue;
+	char temp = 0;
+	struct pci_dev *pci_dev = NULL;
+
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
+	if (pci_dev == NULL)
+                return -1;
+	temp = 0x80;
+	pci_write_config_byte(pci_dev, 0x59, temp);
+	
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev);
+ 	if (pci_dev == NULL)
+                return -1;
+	temp = 0x20;
+	pci_write_config_byte(pci_dev, (int)0xB8,(u8) temp);
+	if (chan_nums == 6) {
+		dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000;
+		outl(dwValue, TRID_REG(card, ALI_SCTRL));
+		mdelay(4);
+		dwValue = inl(TRID_REG(card, ALI_SCTRL));
+		if (dwValue & 0x2000000) {
+			ali_ac97_write(card->ac97_codec[0], 0x02, 8080);
+			ali_ac97_write(card->ac97_codec[0], 0x36, 0);
+			ali_ac97_write(card->ac97_codec[0], 0x38, 0);
+			ali_ac97_write(card->ac97_codec[1], 0x36, 0);
+			ali_ac97_write(card->ac97_codec[1], 0x38, 0);
+			ali_ac97_write(card->ac97_codec[1], 0x02, 0x0606);
+			ali_ac97_write(card->ac97_codec[1], 0x18, 0x0303);
+			ali_ac97_write(card->ac97_codec[1], 0x74, 0x3);
+			return 1;
+		}
+	}
+	return -EINVAL;
+}
+
+static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel)
+{
+	int bank;
+
+	if (channel > 31)
+		return;
+
+	bank = channel >> 5;
+	channel = channel & 0x1f;
+
+	card->banks[bank].bitmap &= ~(1 << (channel));
+}
+
+static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums)
+{
+	struct trident_card *card = state->card;
+	struct trident_state *s;
+	int i, state_count = 0;
+	struct trident_pcm_bank *bank;
+	struct trident_channel *channel;
+	
+	bank = &card->banks[BANK_A];
+	
+	if (chan_nums == 6) {
+		for(i = 0;(i < ALI_CHANNELS) && (state_count != 4); i++) {
+			if (!card->states[i]) {
+				if (!(bank->bitmap & (1 << ali_multi_channels_5_1[state_count]))) {
+					bank->bitmap |= (1 << ali_multi_channels_5_1[state_count]);
+					channel = &bank->channels[ali_multi_channels_5_1[state_count]];
+					channel->num = ali_multi_channels_5_1[state_count];
+				}
+				else {
+					state_count--;
+					for (; state_count >= 0; state_count--) {
+						kfree(state->other_states[state_count]);
+						ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]);
+					}
+					return -EBUSY;
+				}
+				s = card->states[i] = (struct trident_state *)
+						kmalloc(sizeof(struct trident_state), GFP_KERNEL);
+				if (!s) {
+					ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]);
+					state_count--;
+					for (; state_count >= 0; state_count--) {						
+						ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]);
+						kfree(state->other_states[state_count]);
+					}
+					return -ENOMEM;
+				}
+				memset(s, 0, sizeof(struct trident_state));
+				
+				s->dmabuf.channel = channel;
+				s->dmabuf.ossfragshift = s->dmabuf.ossmaxfrags = s->dmabuf.subdivision = 0;
+				init_waitqueue_head(&s->dmabuf.wait);
+				s->magic = card->magic;
+				s->card = card;
+				s->virt = i;
+				ali_enable_special_channel(s);
+				state->other_states[state_count++] = s;
+			}
+		}
+		
+		if (state_count != 4) {
+			state_count--;
+			for (; state_count >= 0; state_count--) {
+				kfree(state->other_states[state_count]);
+				ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]);
+			}
+			return -EBUSY;
+		}
+	}
+	return 0;
+}
+
+static void ali_save_regs(struct trident_card *card)
+{
+	unsigned long flags;
+	int i, j;
+
+	save_flags(flags); 
+	cli();
+	
+	ali_registers.global_regs[0x2c] = inl(TRID_REG(card,T4D_MISCINT));
+	//ali_registers.global_regs[0x20] = inl(TRID_REG(card,T4D_START_A));	
+	ali_registers.global_regs[0x21] = inl(TRID_REG(card,T4D_STOP_A));
+	
+	//disable all IRQ bits
+	outl(ALI_DISABLE_ALL_IRQ, TRID_REG(card, T4D_MISCINT));
+	
+	for (i = 1; i < ALI_MIXER_REGS; i++)
+		ali_registers.mixer_regs[i] = ali_ac97_read (card->ac97_codec[0], i*2);
+	
+	for (i = 0; i < ALI_GLOBAL_REGS; i++)
+	{	
+		if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A))
+			continue;
+		ali_registers.global_regs[i] = inl(TRID_REG(card, i*4));
+	}
+	
+	for (i = 0; i < ALI_CHANNELS; i++) 
+	{
+		outb(i,TRID_REG(card, T4D_LFO_GC_CIR));
+		for (j = 0; j < ALI_CHANNEL_REGS; j++) 
+			ali_registers.channel_regs[i][j] = inl(TRID_REG(card, j*4 + 0xe0));
+	}
+
+	//Stop all HW channel
+	outl(ALI_STOP_ALL_CHANNELS, TRID_REG(card, T4D_STOP_A));
+
+	restore_flags(flags);
+}
+
+static void ali_restore_regs(struct trident_card *card)
+{
+	unsigned long flags;
+	int i, j;
+
+	save_flags(flags); 
+	cli();
+	
+	for (i = 1; i < ALI_MIXER_REGS; i++)
+		ali_ac97_write(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]);
+	
+	for (i = 0; i < ALI_CHANNELS; i++) 
+	{
+		outb(i,TRID_REG(card, T4D_LFO_GC_CIR));
+		for (j = 0; j < ALI_CHANNEL_REGS; j++) 
+			outl(ali_registers.channel_regs[i][j], TRID_REG(card, j*4 + 0xe0));
+	}
+	
+	for (i = 0; i < ALI_GLOBAL_REGS; i++)
+	{	
+		if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A) || (i*4 == T4D_START_A))
+			continue;
+		outl(ali_registers.global_regs[i], TRID_REG(card, i*4));
+	}
+	
+	//start HW channel
+	outl(ali_registers.global_regs[0x20], TRID_REG(card,T4D_START_A));
+	//restore IRQ enable bits
+	outl(ali_registers.global_regs[0x2c], TRID_REG(card,T4D_MISCINT));
+	
+	restore_flags(flags);
+}
+
+static int trident_suspend(struct pci_dev *dev, u32 unused)
+{
+	struct trident_card *card = (struct trident_card *) dev;
+
+	if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		ali_save_regs(card);
+	}
+	return 0;
+}
+
+static int trident_resume(struct pci_dev *dev)
+{
+	struct trident_card *card = (struct trident_card *) dev;
+
+	if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		ali_restore_regs(card);
+	}
+	return 0;
+}
+
+static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card)
+{
+	struct trident_pcm_bank *bank;
+	int idx;
+
+	bank = &card->banks[BANK_A];
+	
+	if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & (ALI_SPDIF_OUT_CH_ENABLE)) {
+		idx = ALI_SPDIF_OUT_CHANNEL;
+		if (!(bank->bitmap & (1 << idx))) {
+			struct trident_channel *channel = &bank->channels[idx];
+			bank->bitmap |= 1 << idx;
+			channel->num = idx;
+			return channel;
+		}
+	}
+	
+	for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST ; idx++) {
+		if (!(bank->bitmap & (1 << idx))) {
+			struct trident_channel *channel = &bank->channels[idx];
+			bank->bitmap |= 1 << idx;
+			channel->num = idx;
+			return channel;
+		}
+	}
+
+	/* no more free channels avaliable */
+//	printk(KERN_ERR "ali: no more channels available on Bank A.\n");
+	return NULL;
+}
+
+static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card)
+{
+	struct trident_pcm_bank *bank;
+	int idx;
+	
+	if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT)
+		idx = ALI_SPDIF_IN_CHANNEL;
+	else	idx = ALI_PCM_IN_CHANNEL;
+
+	bank = &card->banks[BANK_A];
+	
+	if (!(bank->bitmap & (1 << idx))) {
+		struct trident_channel *channel = &bank->channels[idx];
+		bank->bitmap |= 1 << idx;
+		channel->num = idx;
+		return channel;
+	}
+	
+	/* no free recordable channels avaliable */
+//	printk(KERN_ERR "ali: no recordable channels available on Bank A.\n");
+	return NULL;
+}
+
+static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate)
+{
+	unsigned char ch_st_sel;
+	unsigned short status_rate;
+	
+	switch(rate) {
+	case 44100:
+		status_rate = 0;
+		break;
+	case 32000:
+		status_rate = 0x300;
+		break;
+	case 48000:
+	default:
+		status_rate = 0x200;
+		break;
+	}
+	
+	ch_st_sel = inb(TRID_REG(card, ALI_SPDIF_CTRL)) & ALI_SPDIF_OUT_CH_STATUS;	//select spdif_out
+	
+	ch_st_sel |= 0x80;	//select right
+	outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
+	outb(status_rate | 0x20, TRID_REG(card, ALI_SPDIF_CS + 2));
+	
+	ch_st_sel &= (~0x80);	//select left
+	outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
+	outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2));
+}
+
+static void ali_address_interrupt(struct trident_card *card)
+{
+	int i, channel;
+	struct trident_state *state;
+	u32 mask, channel_mask;
+	
+	mask = trident_get_interrupt_mask (card, 0);
+	for (i = 0; i < NR_HW_CH; i++) {
+		if ((state = card->states[i]) == NULL)
+			continue;		
+		channel = state->dmabuf.channel->num;
+		if ((channel_mask = 1 << channel) & mask) {
+			mask &= ~channel_mask;
+			trident_ack_channel_interrupt(card, channel);
+			udelay(100);
+			state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE;
+			trident_update_ptr(state);
+		}
+	}
+	if (mask) {
+		for (i = 0; i < NR_HW_CH; i++) {
+			if (mask & (1 << i)) {
+				printk("ali: spurious channel irq %d.\n", i);
+				trident_ack_channel_interrupt(card, i);
+				trident_stop_voice(card, i);
+				trident_disable_voice_irq(card, i);
+			}
+		}
+	}
+}
+
+/* Updating the values of counters of other_states' DMAs without lock 
+protection is no harm because all DMAs of multi-channels and interrupt
+depend on a master state's DMA, and changing the counters of the master
+state DMA is protected by a spinlock.
+*/
+static int ali_write_5_1(struct trident_state *state,  const char *buf, int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt)
+{
+	
+	struct dmabuf *dmabuf = &state->dmabuf;
+	struct dmabuf *dmabuf_temp;
+	const char *buffer = buf;
+	unsigned swptr, other_dma_nums, sample_s;
+	unsigned int i, loop;
+	
+	other_dma_nums = 4;
+	sample_s = sample_size[dmabuf->fmt] >> 1;
+	swptr = dmabuf->swptr;
+
+	if ((i = state->multi_channels_adjust_count) > 0) {
+		if (i == 1) {
+			if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+			i--;
+			(*state_cnt) += sample_s;
+			state->multi_channels_adjust_count++;
+		}
+		else	i = i - (state->chans_num - other_dma_nums);
+		for (; (i < other_dma_nums) && (cnt_for_multi_channel > 0); i++) {
+			dmabuf_temp = &state->other_states[i]->dmabuf;
+			if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+		}
+		if (cnt_for_multi_channel == 0)
+			state->multi_channels_adjust_count += i;
+	}
+	if (cnt_for_multi_channel > 0) {
+		loop = cnt_for_multi_channel / (state->chans_num * sample_s);
+		for (i = 0; i < loop; i++) {
+			if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s * 2))
+				return -EFAULT;
+			seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s * 2, *copy_count);
+			(*state_cnt) += (sample_s * 2);
+		
+			dmabuf_temp = &state->other_states[0]->dmabuf;
+			if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+		
+			dmabuf_temp = &state->other_states[1]->dmabuf;
+			if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+		
+			dmabuf_temp = &state->other_states[2]->dmabuf;
+			if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+				
+			dmabuf_temp = &state->other_states[3]->dmabuf;
+			if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+		}
+		
+		if (cnt_for_multi_channel > 0) {
+			state->multi_channels_adjust_count = cnt_for_multi_channel / sample_s;
+			
+			if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s))
+				return -EFAULT;
+			seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);			
+			(*state_cnt) += sample_s;
+			
+			if (cnt_for_multi_channel > 0) {
+				if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s))
+					return -EFAULT;
+				seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+				(*state_cnt) += sample_s;
+			
+				if (cnt_for_multi_channel > 0) {
+					loop = state->multi_channels_adjust_count - (state->chans_num - other_dma_nums);
+					for (i = 0; i < loop; i++) {
+						dmabuf_temp = &state->other_states[i]->dmabuf;
+						if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s))
+							return -EFAULT;
+						seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count);
+					}
+				}
+			}
+		}
+		else
+			state->multi_channels_adjust_count = 0;
+	}
+	for (i = 0; i < other_dma_nums; i++) {
+		dmabuf_temp = &state->other_states[i]->dmabuf;
+		dmabuf_temp->swptr = dmabuf_temp->swptr % dmabuf_temp->dmasize;
+	}
+	return *state_cnt;
+}
+
+static void ali_free_other_states_resources(struct trident_state *state)
+{
+	int i;
+	struct trident_card *card = state->card;
+	struct trident_state *s;
+	unsigned other_states_count;
+	
+	other_states_count = state->chans_num - 2;	/* except PCM L/R channels*/
+	for ( i = 0; i < other_states_count; i++) {
+		s = state->other_states[i];
+		dealloc_dmabuf(s);
+		ali_disable_special_channel(s->card, s->dmabuf.channel->num);
+		state->card->free_pcm_channel(s->card, s->dmabuf.channel->num);
+		card->states[s->virt] = NULL;
+		kfree(s);
+	}
+}
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry *res;
+static int ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	struct trident_card *card = (struct trident_card *)data;
+	unsigned long flags;
+	char c;
+
+	if (count<0)
+		return -EINVAL;
+	if (count == 0)
+		return 0;
+	if (get_user(c, buffer))
+		return -EFAULT;
+	
+	spin_lock_irqsave(&card->lock, flags);
+	switch (c) {
+	    case '0':
+		ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
+		ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL);
+		break;
+	    case '1':
+		ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_PCM);
+		break;
+	    case '2':
+		ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_NON_PCM);
+		break;
+	    case '3':
+		ali_disable_spdif_in(card);	//default
+		break;
+	    case '4':
+		ali_setup_spdif_in(card);
+		break;
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return count;
+}
+#endif
+
+/* OSS /dev/mixer file operation methods */
+static int trident_open_mixdev(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	int minor = minor(inode->i_rdev);
+	struct trident_card *card = devs;
+
+	for (card = devs; card != NULL; card = card->next)
+		for (i = 0; i < NR_AC97; i++)
+			if (card->ac97_codec[i] != NULL &&
+			    card->ac97_codec[i]->dev_mixer == minor)
+				goto match;
+
+	if (!card) {
+		return -ENODEV;
+	}
+ match:
+	file->private_data = card->ac97_codec[i];
+
+
+	return 0;
+}
+
+static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static /*const*/ struct file_operations trident_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		trident_ioctl_mixdev,
+	open:		trident_open_mixdev,
+};
+
+static int ali_reset_5451(struct trident_card *card)
+{
+	struct pci_dev *pci_dev = NULL;
+	unsigned int   dwVal;
+	unsigned short wCount, wReg;
+
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
+	if (pci_dev == NULL)
+		return -1;
+	
+	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+	pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+	udelay(5000);
+	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+	pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+	udelay(5000);
+
+	pci_dev = card->pci_dev;
+	if (pci_dev == NULL)
+		return -1;
+
+	pci_read_config_dword(pci_dev, 0x44, &dwVal);
+	pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
+	udelay(500);
+	pci_read_config_dword(pci_dev, 0x44, &dwVal);
+	pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
+	udelay(5000);
+
+	wCount = 200;
+	while(wCount--) {
+		wReg = ali_ac97_get(card, 0, AC97_POWER_CONTROL);
+		if((wReg & 0x000f) == 0x000f)
+			return 0;
+		udelay(500);
+	}
+	return 0;
+}
+
+/* AC97 codec initialisation. */
+static int __init trident_ac97_init(struct trident_card *card)
+{
+	int num_ac97 = 0;
+	unsigned long ready_2nd = 0;
+	struct ac97_codec *codec;
+	int i = 0;
+	
+
+	/* initialize controller side of AC link, and find out if secondary codes
+	   really exist */
+	switch (card->pci_id)
+	{
+	case PCI_DEVICE_ID_ALI_5451:
+		if (ali_reset_5451(card)) 
+		{
+			printk(KERN_ERR "trident_ac97_init: error resetting 5451.\n");
+			return -1;
+		}
+		outl(0x80000001,TRID_REG(card, ALI_GLOBAL_CONTROL));
+		outl(0x00000000,TRID_REG(card, T4D_AINTEN_A));
+		outl(0xffffffff,TRID_REG(card, T4D_AINT_A));
+		outl(0x00000000,TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
+		outb(0x10,	TRID_REG(card, ALI_MPUR2));
+		ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
+		ready_2nd &= 0x3fff;
+		outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL));
+		ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); 
+		ready_2nd &= SI_AC97_SECONDARY_READY;
+		if (card->revision < ALI_5451_V02)
+			ready_2nd = 0;
+		break;
+	case PCI_DEVICE_ID_SI_7018:
+		/* disable AC97 GPIO interrupt */
+		outl(0x00, TRID_REG(card, SI_AC97_GPIO));
+		/* when power up the AC link is in cold reset mode so stop it */
+		outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID,
+		     TRID_REG(card, SI_SERIAL_INTF_CTRL));
+		/* it take a long time to recover from a cold reset (especially when you have
+		   more than one codec) */
+		udelay(2000);
+		ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
+		ready_2nd &= SI_AC97_SECONDARY_READY;
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
+		/* playback on */
+		outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
+		break;
+	case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX:
+		/* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */
+		outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT));
+		ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT));
+		ready_2nd &= NX_AC97_SECONDARY_READY;
+		break;
+	case PCI_DEVICE_ID_INTERG_5050:
+		/* disable AC97 GPIO interrupt */
+		outl(0x00, TRID_REG(card, SI_AC97_GPIO));
+		/* when power up, the AC link is in cold reset mode, so stop it */
+		outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT,
+		     TRID_REG(card, SI_SERIAL_INTF_CTRL));
+		/* it take a long time to recover from a cold reset (especially when you have
+		   more than one codec) */
+		udelay(2000);
+		ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
+		ready_2nd &= SI_AC97_SECONDARY_READY;
+		break;
+	}
+
+	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+		if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		memset(codec, 0, sizeof(struct ac97_codec));
+
+		/* initialize some basic codec information, other fields will be filled
+		   in ac97_probe_codec */
+		codec->private_data = card;
+		codec->id = num_ac97;
+
+		if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+			codec->codec_read = ali_ac97_read;
+			codec->codec_write = ali_ac97_write;
+		}
+		else {
+			codec->codec_read = trident_ac97_get;
+			codec->codec_write = trident_ac97_set;
+		}
+	
+		if (ac97_probe_codec(codec) == 0)
+			break;
+
+		if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) {
+			printk(KERN_ERR "trident: couldn't register mixer!\n");
+			kfree(codec);
+			break;
+		}
+
+		card->ac97_codec[num_ac97] = codec;
+
+		/* if there is no secondary codec at all, don't probe any more */
+		if (!ready_2nd)
+			break;
+	}
+
+	if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+			if (card->ac97_codec[num_ac97] == NULL)
+				break;
+			for (i=0; i<64;i++)
+				card->mixer_regs[i][num_ac97] = ali_ac97_get(card, num_ac97,i*2);
+		}
+	}
+	return num_ac97+1;
+}
+
+/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered 
+   until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
+{
+	unsigned long iobase;
+	struct trident_card *card;
+	u8 bits;
+	u8 revision;
+	int i = 0;
+	u16 temp;
+	struct pci_dev *pci_dev_m1533 = NULL;
+	int rc = -ENODEV;
+	u64 dma_mask;
+
+	if (pci_enable_device(pci_dev))
+		goto out;
+
+	if (pci_dev->device == PCI_DEVICE_ID_ALI_5451)
+		dma_mask = ALI_DMA_MASK;
+	else
+		dma_mask = TRIDENT_DMA_MASK;
+	if (pci_set_dma_mask(pci_dev, dma_mask)) {
+		printk(KERN_ERR "trident: architecture does not support"
+		       " %s PCI busmaster DMA\n",
+		       pci_dev->device == PCI_DEVICE_ID_ALI_5451 ?
+		       "32-bit" : "30-bit");
+		goto out;
+	}
+	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
+
+	if (pci_id->device == PCI_DEVICE_ID_INTERG_5050)
+		iobase = pci_resource_start(pci_dev, 1);
+	else
+		iobase = pci_resource_start(pci_dev, 0);
+
+	if (!request_region(iobase, 256, card_names[pci_id->driver_data])) {
+		printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n",
+		       iobase);
+		goto out;
+	}
+
+	rc = -ENOMEM;
+	if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "trident: out of memory\n");
+		goto out_release_region;
+	}
+	memset(card, 0, sizeof(*card));
+
+	card->iobase = iobase;
+	card->pci_dev = pci_dev;
+	card->pci_id = pci_id->device;
+	card->revision = revision;
+	card->irq = pci_dev->irq;
+	card->next = devs;
+	card->magic = TRIDENT_CARD_MAGIC;
+	card->banks[BANK_A].addresses = &bank_a_addrs;
+	card->banks[BANK_A].bitmap = 0UL;
+	card->banks[BANK_B].addresses = &bank_b_addrs;
+	card->banks[BANK_B].bitmap = 0UL;
+
+	init_MUTEX(&card->open_sem);
+	spin_lock_init(&card->lock);
+	init_timer(&card->timer);
+
+	devs = card;
+
+	pci_set_master(pci_dev);
+
+	printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n",
+	       card_names[pci_id->driver_data], card->iobase, card->irq);
+
+	if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		/* ALi channel Management */	
+		card->alloc_pcm_channel = ali_alloc_pcm_channel;
+		card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel;
+		card->free_pcm_channel = ali_free_pcm_channel;
+		
+		card->address_interrupt = ali_address_interrupt;
+
+		/* Added by Matt Wu 01-05-2001 for spdif in */
+		card->multi_channel_use_count = 0;
+		card->rec_channel_use_count = 0;
+
+		/* ALi SPDIF OUT function */
+		if(card->revision == ALI_5451_V02) {
+			ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);		
+#ifdef CONFIG_PROC_FS
+			res = create_proc_entry("ALi5451", 0, NULL);
+			if (res) {
+				res->write_proc = ali_write_proc;
+				res->data = card;
+			}
+#endif
+		}
+
+		/* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */
+		card->hwvolctl = 0;
+		pci_dev_m1533 = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev_m1533);
+		rc = -ENODEV;
+		if (pci_dev_m1533 == NULL)
+			goto out_proc_fs;
+		pci_read_config_byte(pci_dev_m1533, 0x63, &bits);
+		if (bits & (1<<5))
+			card->hwvolctl = 1;
+		if (card->hwvolctl) 
+		{
+			/* Clear m1533 pci cfg 78h bit 30 to zero, which makes
+			   GPIO11/12/13 work as ACGP_UP/DOWN/MUTE. */
+			pci_read_config_byte(pci_dev_m1533, 0x7b, &bits);
+			bits &= 0xbf; /*clear bit 6 */
+			pci_write_config_byte(pci_dev_m1533, 0x7b, bits);
+		}
+	}
+	else if(card->pci_id == PCI_DEVICE_ID_INTERG_5050)
+	{
+		card->alloc_pcm_channel = cyber_alloc_pcm_channel;
+		card->alloc_rec_pcm_channel = cyber_alloc_pcm_channel;
+		card->free_pcm_channel = cyber_free_pcm_channel;
+		card->address_interrupt = cyber_address_interrupt;
+		cyber_init_ritual(card);
+	}
+	else
+	{
+		card->alloc_pcm_channel = trident_alloc_pcm_channel;
+		card->alloc_rec_pcm_channel = trident_alloc_pcm_channel;
+		card->free_pcm_channel = trident_free_pcm_channel;
+		card->address_interrupt = trident_address_interrupt;
+	}
+
+	/* claim our irq */
+	rc = -ENODEV;
+	if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ,
+			card_names[pci_id->driver_data], card)) {
+		printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq);
+		goto out_proc_fs;
+	}
+	/* register /dev/dsp */
+	if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) {
+		printk(KERN_ERR "trident: couldn't register DSP device!\n");
+		goto out_free_irq;
+	}
+	card->mixer_regs_ready = 0;
+	/* initialize AC97 codec and register /dev/mixer */
+	if (trident_ac97_init(card) <= 0) {
+		/* unregister audio devices */
+		for (i = 0; i < NR_AC97; i++) {
+			if (card->ac97_codec[i] != NULL) {
+				unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+				kfree (card->ac97_codec[i]);
+			}
+		}
+		goto out_unregister_sound_dsp;
+	}
+	card->mixer_regs_ready = 1;
+	outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
+
+	if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		/* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */
+		if(card->hwvolctl) 
+		{
+			/* Enable GPIO IRQ (MISCINT bit 18h)*/
+			temp = inw(TRID_REG(card, T4D_MISCINT + 2));
+			temp |= 0x0004;
+			outw(temp, TRID_REG(card, T4D_MISCINT + 2));
+
+			/* Enable H/W Volume Control GLOVAL CONTROL bit 0*/
+			temp = inw(TRID_REG(card, ALI_GLOBAL_CONTROL));
+			temp |= 0x0001;
+			outw(temp, TRID_REG(card, ALI_GLOBAL_CONTROL));
+
+		}
+		if(card->revision == ALI_5451_V02)
+			ali_close_multi_channels();
+		/* edited by HMSEO for GT sound */
+#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC
+		{
+			u16 ac97_data;
+			extern struct hwrpb_struct *hwrpb;
+		
+			if ((hwrpb->sys_type) == 201) {
+				printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n");
+				ac97_data = ali_ac97_get(card, 0, AC97_POWER_CONTROL);
+				ali_ac97_set(card, 0, AC97_POWER_CONTROL, ac97_data | ALI_EAPD_POWER_DOWN);
+			}
+		}
+#endif
+		/* edited by HMSEO for GT sound*/
+	}
+	rc = 0;
+	pci_set_drvdata(pci_dev, card);
+
+	/* Enable Address Engine Interrupts */
+	trident_enable_loop_interrupts(card);
+out:	return rc;
+out_unregister_sound_dsp:
+	unregister_sound_dsp(card->dev_audio);
+out_free_irq:
+	free_irq(card->irq, card);
+out_proc_fs:
+#ifdef CONFIG_PROC_FS
+	if (res) {
+		remove_proc_entry("ALi5451", NULL);
+		res = NULL;
+	}
+#endif
+	kfree(card);
+	devs = NULL;
+out_release_region:
+	release_region(iobase, 256);
+	goto out;
+}
+
+static void __devexit trident_remove(struct pci_dev *pci_dev)
+{
+	int i;
+	struct trident_card *card = pci_get_drvdata(pci_dev);
+
+	/*
+ 	 *	Kill running timers before unload. We can't have them
+ 	 *	going off after rmmod!
+ 	 */
+	if(card->hwvolctl)
+		del_timer_sync(&card->timer);
+		
+	/* ALi S/PDIF and Power Management */
+	if(card->pci_id == PCI_DEVICE_ID_ALI_5451) {
+		ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);
+                ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL);
+                ali_disable_spdif_in(card);
+#ifdef CONFIG_PROC_FS
+		remove_proc_entry("ALi5451", NULL);
+#endif
+	}
+
+	/* Kill interrupts, and SP/DIF */
+	trident_disable_loop_interrupts(card);
+
+	/* free hardware resources */
+	free_irq(card->irq, card);
+	release_region(card->iobase, 256);
+
+	/* unregister audio devices */
+	for (i = 0; i < NR_AC97; i++)
+		if (card->ac97_codec[i] != NULL) {
+			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
+			kfree (card->ac97_codec[i]);
+		}
+	unregister_sound_dsp(card->dev_audio);
+
+	kfree(card);
+
+	pci_set_drvdata(pci_dev, NULL);
+}
+
+MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho, Ching Ling Lee");
+MODULE_DESCRIPTION("Trident 4DWave/SiS 7018/ALi 5451 and Tvia/IGST CyberPro5050 PCI Audio Driver");
+MODULE_LICENSE("GPL");
+
+
+#define TRIDENT_MODULE_NAME "trident"
+
+static struct pci_driver trident_pci_driver = {
+	name:		TRIDENT_MODULE_NAME,
+	id_table:	trident_pci_tbl,
+	probe:		trident_probe,
+	remove:		__devexit_p(trident_remove),
+	suspend:	trident_suspend,
+	resume:		trident_resume
+};
+
+static int __init trident_init_module (void)
+{
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+
+	printk(KERN_INFO "Trident 4DWave/SiS 7018/ALi 5451,Tvia CyberPro 5050 PCI Audio, version "
+	       DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
+
+	if (!pci_register_driver(&trident_pci_driver)) {
+		pci_unregister_driver(&trident_pci_driver);
+                return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit trident_cleanup_module (void)
+{
+	pci_unregister_driver(&trident_pci_driver);
+}
+
+module_init(trident_init_module);
+module_exit(trident_cleanup_module);
diff -Nru linux/sound/oss/trident.h linux-2.4.19-pre5-mjc/sound/oss/trident.h
--- linux/sound/oss/trident.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/trident.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,362 @@
+#ifndef __TRID4DWAVE_H
+#define __TRID4DWAVE_H
+
+/*
+ *  audio@tridentmicro.com
+ *  Fri Feb 19 15:55:28 MST 1999
+ *  Definitions for Trident 4DWave DX/NX chips
+ *
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* PCI vendor and device ID */ 
+#ifndef PCI_VENDOR_ID_TRIDENT
+#define PCI_VENDOR_ID_TRIDENT		0x1023
+#endif
+
+#ifndef PCI_VENDOR_ID_SI
+#define PCI_VENDOR_ID_SI			0x1039
+#endif
+
+#ifndef PCI_VENDOR_ID_ALI
+#define PCI_VENDOR_ID_ALI			0x10b9
+#endif
+
+#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX	0x2000
+#endif
+
+#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX	0x2001
+#endif
+
+#ifndef PCI_DEVICE_ID_SI_7018
+#define PCI_DEVICE_ID_SI_7018		0x7018
+#endif
+
+#ifndef PCI_DEVICE_ID_ALI_5451
+#define PCI_DEVICE_ID_ALI_5451		0x5451
+#endif
+
+#ifndef PCI_DEVICE_ID_ALI_1533
+#define PCI_DEVICE_ID_ALI_1533		0x1533
+#endif
+
+#ifndef FALSE
+#define FALSE 		0
+#define TRUE  		1
+#endif
+
+#define CHANNEL_REGS	5
+#define CHANNEL_START	0xe0   // The first bytes of the contiguous register space.
+
+#define BANK_A 		0
+#define BANK_B 		1
+#define NR_BANKS		2
+
+#define TRIDENT_FMT_STEREO     0x01
+#define TRIDENT_FMT_16BIT      0x02
+#define TRIDENT_FMT_MASK       0x03
+
+#define DAC_RUNNING	0x01
+#define ADC_RUNNING	0x02
+
+/* Register Addresses */
+
+/* operational registers common to DX, NX, 7018 */
+enum trident_op_registers {
+	T4D_REC_CH	= 0x70,
+	T4D_START_A     = 0x80, T4D_STOP_A      = 0x84,
+	T4D_DLY_A       = 0x88, T4D_SIGN_CSO_A  = 0x8c,
+	T4D_CSPF_A      = 0x90, T4D_CEBC_A      = 0x94,
+	T4D_AINT_A      = 0x98, T4D_EINT_A	= 0x9c,
+	T4D_LFO_GC_CIR	= 0xa0, T4D_AINTEN_A    = 0xa4,
+	T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac,
+	T4D_MISCINT	= 0xb0, T4D_START_B     = 0xb4,
+	T4D_STOP_B      = 0xb8, T4D_CSPF_B	= 0xbc,
+	T4D_SBBL_SBCL	= 0xc0, T4D_SBCTRL_SBE2R_SBDD    = 0xc4,
+	T4D_STIMER	= 0xc8, T4D_LFO_B_I2S_DELTA      = 0xcc,
+	T4D_AINT_B	= 0xd8, T4D_AINTEN_B	= 0xdc,
+	ALI_MPUR2	= 0x22,	ALI_GPIO	= 0x7c,
+	ALI_EBUF1 = 0xf4,
+	ALI_EBUF2 = 0xf8
+};
+
+enum ali_op_registers {
+	ALI_SCTRL		= 0x48,
+	ALI_GLOBAL_CONTROL	= 0xd4,
+	ALI_STIMER		= 0xc8,
+	ALI_SPDIF_CS		= 0x70,
+	ALI_SPDIF_CTRL		= 0x74
+};
+
+enum ali_registers_number {
+	ALI_GLOBAL_REGS		= 56,
+	ALI_CHANNEL_REGS	= 8,
+	ALI_MIXER_REGS		= 20
+};
+
+enum ali_sctrl_control_bit {
+	ALI_SPDIF_OUT_ENABLE	= 0x20
+};
+
+enum ali_global_control_bit {
+	ALI_SPDIF_OUT_SEL_PCM	= 0x00000400,
+	ALI_SPDIF_IN_SUPPORT	= 0x00000800,
+	ALI_SPDIF_OUT_CH_ENABLE	= 0x00008000,
+	ALI_SPDIF_IN_CH_ENABLE	= 0x00080000,
+	ALI_PCM_IN_DISABLE	= 0x7fffffff,
+	ALI_PCM_IN_ENABLE	= 0x80000000,
+	ALI_SPDIF_IN_CH_DISABLE	= 0xfff7ffff,
+	ALI_SPDIF_OUT_CH_DISABLE = 0xffff7fff,
+	ALI_SPDIF_OUT_SEL_SPDIF	= 0xfffffbff
+	
+};
+
+enum ali_spdif_control_bit {
+	ALI_SPDIF_IN_FUNC_ENABLE	= 0x02,
+	ALI_SPDIF_IN_CH_STATUS		= 0x40,
+	ALI_SPDIF_OUT_CH_STATUS		= 0xbf
+	
+};
+
+enum ali_control_all {
+	ALI_DISABLE_ALL_IRQ	= 0,
+	ALI_CHANNELS		= 32,
+	ALI_STOP_ALL_CHANNELS	= 0xffffffff,
+	ALI_MULTI_CHANNELS_START_STOP	= 0x07800000
+};
+
+enum ali_EMOD_control_bit {
+	ALI_EMOD_DEC	= 0x00000000,
+	ALI_EMOD_INC	= 0x10000000,
+	ALI_EMOD_Delay	= 0x20000000,
+	ALI_EMOD_Still	= 0x30000000
+};
+
+enum ali_pcm_in_channel_num {
+	ALI_NORMAL_CHANNEL	= 0,
+	ALI_SPDIF_OUT_CHANNEL	= 15,
+	ALI_SPDIF_IN_CHANNEL    = 19,
+	ALI_LEF_CHANNEL		= 23,
+	ALI_CENTER_CHANNEL	= 24,
+	ALI_SURR_RIGHT_CHANNEL	= 25,
+	ALI_SURR_LEFT_CHANNEL	= 26,
+	ALI_PCM_IN_CHANNEL	= 31
+};
+
+enum ali_pcm_out_channel_num {
+	ALI_PCM_OUT_CHANNEL_FIRST = 0,
+	ALI_PCM_OUT_CHANNEL_LAST = 31
+};
+
+enum ali_ac97_power_control_bit {
+	ALI_EAPD_POWER_DOWN	= 0x8000
+};
+
+enum ali_update_ptr_flags {
+	ALI_ADDRESS_INT_UPDATE	= 0x01
+};
+
+enum ali_revision {
+	ALI_5451_V02	= 0x02
+};
+
+enum ali_spdif_out_control {
+	ALI_PCM_TO_SPDIF_OUT		= 0,
+	ALI_SPDIF_OUT_TO_SPDIF_OUT	= 1,
+	ALI_SPDIF_OUT_PCM		= 0,
+	ALI_SPDIF_OUT_NON_PCM		= 2
+};
+
+/* S/PDIF Operational Registers for 4D-NX */
+enum nx_spdif_registers {
+	NX_SPCTRL_SPCSO	= 0x24, NX_SPLBA = 0x28,
+	NX_SPESO	= 0x2c, NX_SPCSTATUS = 0x64
+};
+
+/* OP registers to access each hardware channel */
+enum channel_registers {
+	CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8,
+	CH_DX_FMC_RVOL_CVOL = 0xec,
+	CH_NX_DELTA_CSO     = 0xe0, CH_NX_DELTA_ESO = 0xe8,
+	CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec,
+	CH_LBA              = 0xe4,
+	CH_GVSEL_PAN_VOL_CTRL_EC      = 0xf0
+};
+
+/* registers to read/write/control AC97 codec */
+enum dx_ac97_registers {
+	DX_ACR0_AC97_W        = 0x40, DX_ACR1_AC97_R = 0x44,
+	DX_ACR2_AC97_COM_STAT = 0x48
+};
+
+enum nx_ac97_registers {
+	NX_ACR0_AC97_COM_STAT  = 0x40, NX_ACR1_AC97_W           = 0x44,
+	NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY	= 0x4c
+};
+
+enum si_ac97_registers {
+	SI_AC97_WRITE       = 0x40, SI_AC97_READ = 0x44,
+	SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c
+};
+
+enum ali_ac97_registers {
+	ALI_AC97_WRITE       = 0x40, ALI_AC97_READ = 0x44
+};
+
+/* Bit mask for operational registers */
+#define AC97_REG_ADDR      0x000000ff
+
+enum ali_ac97_bits {
+	ALI_AC97_BUSY_WRITE = 0x8000, ALI_AC97_BUSY_READ = 0x8000,
+	ALI_AC97_WRITE_ACTION = 0x8000, ALI_AC97_READ_ACTION = 0x8000,
+	ALI_AC97_AUDIO_BUSY = 0x4000, ALI_AC97_SECONDARY  = 0x0080,
+	ALI_AC97_READ_MIXER_REGISTER = 0xfeff,
+	ALI_AC97_WRITE_MIXER_REGISTER = 0x0100
+};
+
+enum sis7018_ac97_bits {
+	SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000,
+	SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000,
+	SI_AC97_SECONDARY  = 0x0080
+};
+
+enum trident_dx_ac97_bits {
+	DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000,
+	DX_AC97_READY      = 0x0010, DX_AC97_RECORD    = 0x0008,
+	DX_AC97_PLAYBACK   = 0x0002
+};
+
+enum trident_nx_ac97_bits {
+	/* ACR1-3 */
+	NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800,
+	NX_AC97_BUSY_DATA  = 0x0400, NX_AC97_WRITE_SECONDARY = 0x0100,
+	/* ACR0 */
+	NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020,
+	NX_AC97_SURROUND_OUTPUT = 0x0010,
+	NX_AC97_PRIMARY_READY   = 0x0008, NX_AC97_PRIMARY_RECORD   = 0x0004,
+	NX_AC97_PCM_OUTPUT      = 0x0002,
+	NX_AC97_WARM_RESET      = 0x0001
+};
+
+enum serial_intf_ctrl_bits {
+	WARM_REST   = 0x00000001, COLD_RESET  = 0x00000002,
+	I2S_CLOCK   = 0x00000004, PCM_SEC_AC97= 0x00000008,
+	AC97_DBL_RATE = 0x00000010, SPDIF_EN  = 0x00000020,
+	I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080,
+	PCMIN       = 0x00000100, LINE1IN     = 0x00000200,
+	MICIN       = 0x00000400, LINE2IN     = 0x00000800,
+	HEAD_SET_IN = 0x00001000, GPIOIN      = 0x00002000,
+	/* 7018 spec says id = 01 but the demo board routed to 10 
+	   SECONDARY_ID= 0x00004000, */
+	SECONDARY_ID= 0x00004000,
+	PCMOUT      = 0x00010000, SURROUT     = 0x00020000,
+	CENTEROUT   = 0x00040000, LFEOUT      = 0x00080000,
+	LINE1OUT    = 0x00100000, LINE2OUT    = 0x00200000,
+	GPIOOUT     = 0x00400000,
+	SI_AC97_PRIMARY_READY   = 0x01000000,
+	SI_AC97_SECONDARY_READY = 0x02000000,
+};
+
+enum global_control_bits {
+	CHANNLE_IDX = 0x0000003f, PB_RESET    = 0x00000100,
+	PAUSE_ENG   = 0x00000200,
+	OVERRUN_IE  = 0x00000400, UNDERRUN_IE = 0x00000800,
+	ENDLP_IE    = 0x00001000, MIDLP_IE    = 0x00002000,
+	ETOG_IE     = 0x00004000,
+	EDROP_IE    = 0x00008000, BANK_B_EN   = 0x00010000
+};
+
+enum channel_control_bits {
+	CHANNEL_LOOP   = 0x00001000, CHANNEL_SIGNED = 0x00002000,
+	CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000,
+};
+
+enum channel_attribute {
+	/* playback/record select */
+	CHANNEL_PB     = 0x0000, CHANNEL_SPC_PB = 0x4000,
+	CHANNEL_REC    = 0x8000, CHANNEL_REC_PB = 0xc000,
+	/* playback destination/record source select */
+	MODEM_LINE1    = 0x0000, MODEM_LINE2    = 0x0400,
+	PCM_LR         = 0x0800, HSET           = 0x0c00,
+	I2S_LR         = 0x1000, CENTER_LFE     = 0x1400,
+	SURR_LR        = 0x1800, SPDIF_LR       = 0x1c00,
+	MIC            = 0x1400,
+	/* mist stuff */
+	MONO_LEFT      = 0x0000, MONO_RIGHT     = 0x0100,
+	MONO_MIX       = 0x0200, SRC_ENABLE     = 0x0080,
+};
+
+enum miscint_bits {
+	PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002,
+	SB_IRQ          = 0x00000004, MPU401_IRQ      = 0x00000008,
+	OPL3_IRQ        = 0x00000010, ADDRESS_IRQ     = 0x00000020,
+	ENVELOPE_IRQ    = 0x00000040, ST_IRQ          = 0x00000080,
+	PB_UNDERRUN     = 0x00000100, REC_OVERRUN     = 0x00000200,
+	MIXER_UNDERFLOW = 0x00000400, MIXER_OVERFLOW  = 0x00000800,
+	ST_TARGET_REACHED = 0x00008000, PB_24K_MODE   = 0x00010000, 
+	ST_IRQ_EN       = 0x00800000, ACGPIO_IRQ      = 0x01000000
+};
+
+#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) )
+
+#define		CYBER_PORT_AUDIO		0x3CE
+#define		CYBER_IDX_AUDIO_ENABLE          0x7B
+#define		CYBER_BMSK_AUDIO_INT_ENABLE	0x09
+#define		CYBER_BMSK_AUENZ		0x01
+#define		CYBER_BMSK_AUENZ_ENABLE		0x00
+#define		CYBER_IDX_IRQ_ENABLE		0x12
+      
+#define VALIDATE_MAGIC(FOO,MAG)				\
+({						  	\
+	if (!(FOO) || (FOO)->magic != MAG) { 		\
+		printk(invalid_magic,__FUNCTION__);	\
+		return -ENXIO;			  	\
+	}					  	\
+})
+
+#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC)
+#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC)
+
+extern __inline__ unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+#endif /* __TRID4DWAVE_H */
+
diff -Nru linux/sound/oss/trix.c linux-2.4.19-pre5-mjc/sound/oss/trix.c
--- linux/sound/oss/trix.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/trix.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,546 @@
+/*
+ * sound/trix.c
+ *
+ * Low level driver for the MediaTrix AudioTrix Pro
+ * (MT-0002-PC Control Chip)
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes
+ *	Alan Cox		Modularisation, cleanup.
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	Got rid of attach_uart401
+ */
+ 
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+#include "sb.h"
+#include "sound_firmware.h"
+
+#include "ad1848.h"
+#include "mpu401.h"
+
+#include "trix_boot.h"
+
+static int kilroy_was_here = 0;	/* Don't detect twice */
+static int sb_initialized = 0;
+static int mpu_initialized = 0;
+
+static int *trix_osp = NULL;
+
+static int mpu = 0;
+
+static int joystick=0;
+
+static unsigned char trix_read(int addr)
+{
+	outb(((unsigned char) addr), 0x390);	/* MT-0002-PC ASIC address */
+	return inb(0x391);	/* MT-0002-PC ASIC data */
+}
+
+static void trix_write(int addr, int data)
+{
+	outb(((unsigned char) addr), 0x390);	/* MT-0002-PC ASIC address */
+	outb(((unsigned char) data), 0x391);	/* MT-0002-PC ASIC data */
+}
+
+static void download_boot(int base)
+{
+	int i = 0, n = trix_boot_len;
+
+	if (trix_boot_len == 0)
+		return;
+
+	trix_write(0xf8, 0x00);	/* ??????? */
+	outb((0x01), base + 6);	/* Clear the internal data pointer */
+	outb((0x00), base + 6);	/* Restart */
+
+	/*
+	   *  Write the boot code to the RAM upload/download register.
+	   *  Each write increments the internal data pointer.
+	 */
+	outb((0x01), base + 6);	/* Clear the internal data pointer */
+	outb((0x1A), 0x390);	/* Select RAM download/upload port */
+
+	for (i = 0; i < n; i++)
+		outb((trix_boot[i]), 0x391);
+	for (i = n; i < 10016; i++)	/* Clear up to first 16 bytes of data RAM */
+		outb((0x00), 0x391);
+	outb((0x00), base + 6);	/* Reset */
+	outb((0x50), 0x390);	/* ?????? */
+
+}
+
+static int trix_set_wss_port(struct address_info *hw_config)
+{
+	unsigned char   addr_bits;
+
+	if (check_region(0x390, 2))
+	{
+		printk(KERN_ERR "AudioTrix: Config port I/O conflict\n");
+		return 0;
+	}
+	if (kilroy_was_here)	/* Already initialized */
+		return 0;
+
+	if (trix_read(0x15) != 0x71)	/* No ASIC signature */
+	{
+		MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n"));
+		return 0;
+	}
+	kilroy_was_here = 1;
+
+	/*
+	 * Reset some registers.
+	 */
+
+	trix_write(0x13, 0);
+	trix_write(0x14, 0);
+
+	/*
+	 * Configure the ASIC to place the codec to the proper I/O location
+	 */
+
+	switch (hw_config->io_base)
+	{
+		case 0x530:
+			addr_bits = 0;
+			break;
+		case 0x604:
+			addr_bits = 1;
+			break;
+		case 0xE80:
+			addr_bits = 2;
+			break;
+		case 0xF40:
+			addr_bits = 3;
+			break;
+		default:
+			return 0;
+	}
+
+	trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits);
+	return 1;
+}
+
+/*
+ *    Probe and attach routines for the Windows Sound System mode of
+ *      AudioTrix Pro
+ */
+
+static int __init probe_trix_wss(struct address_info *hw_config)
+{
+	int ret;
+
+	/*
+	 * Check if the IO port returns valid signature. The original MS Sound
+	 * system returns 0x04 while some cards (AudioTrix Pro for example)
+	 * return 0x00.
+	 */
+	if (check_region(hw_config->io_base, 8))
+	{
+		printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
+		return 0;
+	}
+	trix_osp = hw_config->osp;
+
+	if (!trix_set_wss_port(hw_config))
+		return 0;
+
+	if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00)
+	{
+		MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base));
+		return 0;
+	}
+	if (hw_config->irq > 11)
+	{
+		printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+	{
+		printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", hw_config->dma);
+		return 0;
+	}
+	if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
+		if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3)
+		{
+			  printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", hw_config->dma2);
+			  return 0;
+		}
+	/*
+	 * Check that DMA0 is not in use with a 8 bit board.
+	 */
+
+	if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+	{
+		printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
+		return 0;
+	}
+	if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+	{
+		printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
+		return 0;
+	}
+	ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
+
+	if (ret)
+	{
+		if(joystick==1)
+			trix_write(0x15, 0x80);
+		request_region(0x390, 2, "AudioTrix");
+	}
+	return ret;
+}
+
+static void __init attach_trix_wss(struct address_info *hw_config)
+{
+	static unsigned char interrupt_bits[12] = {
+		0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20
+	};
+	char bits;
+
+	static unsigned char dma_bits[4] = {
+		1, 2, 0, 3
+	};
+
+	int config_port = hw_config->io_base + 0;
+	int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+	int old_num_mixers = num_mixers;
+
+	trix_osp = hw_config->osp;
+
+	if (!kilroy_was_here)
+	{
+		DDB(printk("AudioTrix: Attach called but not probed yet???\n"));
+		return;
+	}
+	
+	/*
+	 * Set the IRQ and DMA addresses.
+	 */
+
+	bits = interrupt_bits[hw_config->irq];
+	if (bits == 0)
+	{
+		printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq);
+		return;
+	}
+	outb((bits | 0x40), config_port);
+
+	if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma)
+	{
+		  bits |= dma_bits[dma1];
+		  dma2 = dma1;
+	}
+	else
+	{
+		unsigned char tmp;
+
+		tmp = trix_read(0x13) & ~30;
+		trix_write(0x13, tmp | 0x80 | (dma1 << 4));
+
+		tmp = trix_read(0x14) & ~30;
+		trix_write(0x14, tmp | 0x80 | (dma2 << 4));
+	}
+
+	outb((bits), config_port);	/* Write IRQ+DMA setup */
+
+	hw_config->slots[0] = ad1848_init("AudioTrix Pro", hw_config->io_base + 4,
+					  hw_config->irq,
+					  dma1,
+					  dma2,
+					  0,
+					  hw_config->osp,
+					  THIS_MODULE);
+	request_region(hw_config->io_base, 4, "MSS config");
+
+	if (num_mixers > old_num_mixers)	/* Mixer got installed */
+	{
+		AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);	/* Line in */
+		AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+		AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH);		/* OPL4 */
+		AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM);	/* SB */
+	}
+}
+
+static int __init probe_trix_sb(struct address_info *hw_config)
+{
+
+	int tmp;
+	unsigned char conf;
+	static signed char irq_translate[] = {
+		-1, -1, -1, 0, 1, 2, -1, 3
+	};
+
+	if (trix_boot_len == 0)
+		return 0;	/* No boot code -> no fun */
+
+	if (!kilroy_was_here)
+		return 0;	/* AudioTrix Pro has not been detected earlier */
+
+	if (sb_initialized)
+		return 0;
+
+	if (check_region(hw_config->io_base, 16))
+	{
+		printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
+		return 0;
+	}
+	if ((hw_config->io_base & 0xffffff8f) != 0x200)
+		return 0;
+
+	tmp = hw_config->irq;
+	if (tmp > 7)
+		return 0;
+	if (irq_translate[tmp] == -1)
+		return 0;
+
+	tmp = hw_config->dma;
+	if (tmp != 1 && tmp != 3)
+		return 0;
+
+	conf = 0x84;		/* DMA and IRQ enable */
+	conf |= hw_config->io_base & 0x70;	/* I/O address bits */
+	conf |= irq_translate[hw_config->irq];
+	if (hw_config->dma == 3)
+		conf |= 0x08;
+	trix_write(0x1b, conf);
+
+	download_boot(hw_config->io_base);
+	sb_initialized = 1;
+
+	hw_config->name = "AudioTrix SB";
+	return sb_dsp_detect(hw_config, 0, 0, NULL);
+}
+
+static void __init attach_trix_sb(struct address_info *hw_config)
+{
+	extern int sb_be_quiet;
+	int old_quiet;
+
+	hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
+
+	/* Prevent false alarms */
+	old_quiet = sb_be_quiet;
+	sb_be_quiet = 1;
+
+	sb_dsp_init(hw_config, THIS_MODULE);
+
+	sb_be_quiet = old_quiet;
+}
+
+static int __init probe_trix_mpu(struct address_info *hw_config)
+{
+	unsigned char conf;
+	static int irq_bits[] = {
+		-1, -1, -1, 1, 2, 3, -1, 4, -1, 5
+	};
+
+	if (!kilroy_was_here)
+	{
+		DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n"));
+		return 0;	/* AudioTrix Pro has not been detected earlier */
+	}
+	if (!sb_initialized)
+	{
+		DDB(printk("Trix: SB mode must be initialized before MPU\n"));
+		return 0;
+	}
+	if (mpu_initialized)
+	{
+		DDB(printk("Trix: MPU mode already initialized\n"));
+		return 0;
+	}
+	if (hw_config->irq > 9)
+	{
+		printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	if (irq_bits[hw_config->irq] == -1)
+	{
+		printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
+		return 0;
+	}
+	switch (hw_config->io_base)
+	{
+		case 0x330:
+			conf = 0x00;
+			break;
+		case 0x370:
+			conf = 0x04;
+			break;
+		case 0x3b0:
+			conf = 0x08;
+			break;
+		case 0x3f0:
+			conf = 0x0c;
+			break;
+		default:
+			return 0;	/* Invalid port */
+	}
+
+	conf |= irq_bits[hw_config->irq] << 4;
+	trix_write(0x19, (trix_read(0x19) & 0x83) | conf);
+	mpu_initialized = 1;
+	hw_config->name = "AudioTrix Pro";
+	return probe_uart401(hw_config, THIS_MODULE);
+}
+
+static void __exit unload_trix_wss(struct address_info *hw_config)
+{
+	int dma2 = hw_config->dma2;
+
+	if (dma2 == -1)
+		dma2 = hw_config->dma;
+
+	release_region(0x390, 2);
+	release_region(hw_config->io_base, 4);
+
+	ad1848_unload(hw_config->io_base + 4,
+		      hw_config->irq,
+		      hw_config->dma,
+		      dma2,
+		      0);
+	sound_unload_audiodev(hw_config->slots[0]);
+}
+
+static inline void __exit unload_trix_mpu(struct address_info *hw_config)
+{
+	unload_uart401(hw_config);
+}
+
+static inline void __exit unload_trix_sb(struct address_info *hw_config)
+{
+	sb_dsp_unload(hw_config, mpu);
+}
+
+static struct address_info cfg;
+static struct address_info cfg2;
+static struct address_info cfg_mpu;
+
+static int sb = 0;
+static int fw_load;
+
+static int __initdata io	= -1;
+static int __initdata irq	= -1;
+static int __initdata dma	= -1;
+static int __initdata dma2	= -1;	/* Set this for modules that need it */
+static int __initdata sb_io	= -1;
+static int __initdata sb_dma	= -1;
+static int __initdata sb_irq	= -1;
+static int __initdata mpu_io	= -1;
+static int __initdata mpu_irq	= -1;
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(dma2,"i");
+MODULE_PARM(sb_io,"i");
+MODULE_PARM(sb_dma,"i");
+MODULE_PARM(sb_irq,"i");
+MODULE_PARM(mpu_io,"i");
+MODULE_PARM(mpu_irq,"i");
+MODULE_PARM(joystick, "i");
+
+static int __init init_trix(void)
+{
+	printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma2;
+
+	cfg2.io_base = sb_io;
+	cfg2.irq = sb_irq;
+	cfg2.dma = sb_dma;
+
+	cfg_mpu.io_base = mpu_io;
+	cfg_mpu.irq = mpu_irq;
+
+	if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
+		printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
+		return -EINVAL;
+	}
+
+	if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) {
+		printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n");
+		return -EINVAL;
+	}
+	if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) {
+		printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n");
+		return -EINVAL;
+	}
+	if (!trix_boot)
+	{
+		fw_load = 1;
+		trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin",
+						    (char **) &trix_boot);
+	}
+	if (!probe_trix_wss(&cfg))
+		return -ENODEV;
+	attach_trix_wss(&cfg);
+
+	/*
+	 *    We must attach in the right order to get the firmware
+	 *      loaded up in time.
+	 */
+
+	if (cfg2.io_base != -1) {
+		sb = probe_trix_sb(&cfg2);
+		if (sb)
+			attach_trix_sb(&cfg2);
+	}
+	
+	if (cfg_mpu.io_base != -1)
+		mpu = probe_trix_mpu(&cfg_mpu);
+
+	return 0;
+}
+
+static void __exit cleanup_trix(void)
+{
+	if (fw_load && trix_boot)
+		vfree(trix_boot);
+	if (sb)
+		unload_trix_sb(&cfg2);
+	if (mpu)
+		unload_trix_mpu(&cfg_mpu);
+	unload_trix_wss(&cfg);
+}
+
+module_init(init_trix);
+module_exit(cleanup_trix);
+
+#ifndef MODULE
+static int __init setup_trix (char *str)
+{
+	/* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */
+	int ints[9];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+	sb_io	= ints[5];
+	sb_irq	= ints[6];
+	sb_dma	= ints[6];
+	mpu_io	= ints[7];
+	mpu_irq	= ints[8];
+
+	return 1;
+}
+
+__setup("trix=", setup_trix);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/tuning.h linux-2.4.19-pre5-mjc/sound/oss/tuning.h
--- linux/sound/oss/tuning.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/tuning.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,29 @@
+#ifdef SEQUENCER_C
+
+unsigned short semitone_tuning[24] = 
+{
+/*   0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, 
+/*   8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, 
+/*  16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+unsigned short cent_tuning[100] =
+{
+/*   0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, 
+/*   8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, 
+/*  16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, 
+/*  24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, 
+/*  32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, 
+/*  40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, 
+/*  48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, 
+/*  56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, 
+/*  64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, 
+/*  72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, 
+/*  80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, 
+/*  88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, 
+/*  96 */ 10570, 10576, 10582, 10589
+};
+#else
+extern unsigned short semitone_tuning[24];
+extern unsigned short cent_tuning[100];
+#endif
diff -Nru linux/sound/oss/uart401.c linux-2.4.19-pre5-mjc/sound/oss/uart401.c
--- linux/sound/oss/uart401.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/uart401.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,481 @@
+/*
+ * sound/uart401.c
+ *
+ * MPU-401 UART driver (formerly uart401_midi.c)
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ *	Alan Cox		Reformatted, removed sound_mem usage, use normal Linux
+ *				interrupt allocation. Protect against bogus unload
+ *				Fixed to allow IRQ > 15
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *	Arnaldo C. de Melo	got rid of check_region
+ *
+ * Status:
+ *		Untested
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+
+#include "mpu401.h"
+
+typedef struct uart401_devc
+{
+	int             base;
+	int             irq;
+	int            *osp;
+	void            (*midi_input_intr) (int dev, unsigned char data);
+	int             opened, disabled;
+	volatile unsigned char input_byte;
+	int             my_dev;
+	int             share_irq;
+}
+uart401_devc;
+
+#define	DATAPORT   (devc->base)
+#define	COMDPORT   (devc->base+1)
+#define	STATPORT   (devc->base+1)
+
+static int uart401_status(uart401_devc * devc)
+{
+	return inb(STATPORT);
+}
+
+#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
+#define output_ready(devc)	(!(uart401_status(devc)&OUTPUT_READY))
+
+static void uart401_cmd(uart401_devc * devc, unsigned char cmd)
+{
+	outb((cmd), COMDPORT);
+}
+
+static int uart401_read(uart401_devc * devc)
+{
+	return inb(DATAPORT);
+}
+
+static void uart401_write(uart401_devc * devc, unsigned char byte)
+{
+	outb((byte), DATAPORT);
+}
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	MPU_RESET	0xFF
+#define	UART_MODE_ON	0x3F
+
+static int      reset_uart401(uart401_devc * devc);
+static void     enter_uart_mode(uart401_devc * devc);
+
+static void uart401_input_loop(uart401_devc * devc)
+{
+	int work_limit=30000;
+	
+	while (input_avail(devc) && --work_limit)
+	{
+		unsigned char   c = uart401_read(devc);
+
+		if (c == MPU_ACK)
+			devc->input_byte = c;
+		else if (devc->opened & OPEN_READ && devc->midi_input_intr)
+			devc->midi_input_intr(devc->my_dev, c);
+	}
+	if(work_limit==0)
+		printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base);
+}
+
+void uart401intr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	uart401_devc *devc = dev_id;
+
+	if (devc == NULL)
+	{
+		printk(KERN_ERR "uart401: bad devc\n");
+		return;
+	}
+
+	if (input_avail(devc))
+		uart401_input_loop(devc);
+}
+
+static int
+uart401_open(int dev, int mode,
+	     void            (*input) (int dev, unsigned char data),
+	     void            (*output) (int dev)
+)
+{
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+	if (devc->opened)
+		return -EBUSY;
+
+	/* Flush the UART */
+	
+	while (input_avail(devc))
+		uart401_read(devc);
+
+	devc->midi_input_intr = input;
+	devc->opened = mode;
+	enter_uart_mode(devc);
+	devc->disabled = 0;
+
+	return 0;
+}
+
+static void uart401_close(int dev)
+{
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+	reset_uart401(devc);
+	devc->opened = 0;
+}
+
+static int uart401_out(int dev, unsigned char midi_byte)
+{
+	int timeout;
+	unsigned long flags;
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+	if (devc->disabled)
+		return 1;
+	/*
+	 * Test for input since pending input seems to block the output.
+	 */
+
+	save_flags(flags);
+	cli();
+
+	if (input_avail(devc))
+		uart401_input_loop(devc);
+
+	restore_flags(flags);
+
+	/*
+	 * Sometimes it takes about 13000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+	if (!output_ready(devc))
+	{
+		  printk(KERN_WARNING "uart401: Timeout - Device not responding\n");
+		  devc->disabled = 1;
+		  reset_uart401(devc);
+		  enter_uart_mode(devc);
+		  return 1;
+	}
+	uart401_write(devc, midi_byte);
+	return 1;
+}
+
+static inline int uart401_start_read(int dev)
+{
+	return 0;
+}
+
+static inline int uart401_end_read(int dev)
+{
+	return 0;
+}
+
+static inline void uart401_kick(int dev)
+{
+}
+
+static inline int uart401_buffer_status(int dev)
+{
+	return 0;
+}
+
+#define MIDI_SYNTH_NAME	"MPU-401 UART"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static const struct midi_operations uart401_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		uart401_open,
+	close:		uart401_close,
+	outputc:	uart401_out,
+	start_read:	uart401_start_read,
+	end_read:	uart401_end_read,
+	kick:		uart401_kick,
+	buffer_status:	uart401_buffer_status,
+};
+
+static void enter_uart_mode(uart401_devc * devc)
+{
+	int ok, timeout;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+	devc->input_byte = 0;
+	uart401_cmd(devc, UART_MODE_ON);
+
+	ok = 0;
+	for (timeout = 50000; timeout > 0 && !ok; timeout--)
+		if (devc->input_byte == MPU_ACK)
+			ok = 1;
+		else if (input_avail(devc))
+			if (uart401_read(devc) == MPU_ACK)
+				ok = 1;
+
+	restore_flags(flags);
+}
+
+static int reset_uart401(uart401_devc * devc)
+{
+	int ok, timeout, n;
+
+	/*
+	 * Send the RESET command. Try again if no success at the first time.
+	 */
+
+	ok = 0;
+
+	for (n = 0; n < 2 && !ok; n++)
+	{
+		for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+		devc->input_byte = 0;
+		uart401_cmd(devc, MPU_RESET);
+
+		/*
+		 * Wait at least 25 msec. This method is not accurate so let's make the
+		 * loop bit longer. Cannot sleep since this is called during boot.
+		 */
+
+		for (timeout = 50000; timeout > 0 && !ok; timeout--)
+		{
+			if (devc->input_byte == MPU_ACK)	/* Interrupt */
+				ok = 1;
+			else if (input_avail(devc))
+			{
+				if (uart401_read(devc) == MPU_ACK)
+					ok = 1;
+			}
+		}
+	}
+
+
+	if (ok)
+	{
+		DEB(printk("Reset UART401 OK\n"));
+	}
+	else
+		DDB(printk("Reset UART401 failed - No hardware detected.\n"));
+
+	if (ok)
+		uart401_input_loop(devc);	/*
+						 * Flush input before enabling interrupts
+						 */
+
+	return ok;
+}
+
+int probe_uart401(struct address_info *hw_config, struct module *owner)
+{
+	uart401_devc *devc;
+	char *name = "MPU-401 (UART) MIDI";
+	int ok = 0;
+	unsigned long flags;
+
+	DDB(printk("Entered probe_uart401()\n"));
+
+	/* Default to "not found" */
+	hw_config->slots[4] = -1;
+
+	if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) {
+		printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base);
+		return 0;
+	}
+
+	devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL);
+	if (!devc) {
+		printk(KERN_WARNING "uart401: Can't allocate memory\n");
+		goto cleanup_region;
+	}
+
+	devc->base = hw_config->io_base;
+	devc->irq = hw_config->irq;
+	devc->osp = hw_config->osp;
+	devc->midi_input_intr = NULL;
+	devc->opened = 0;
+	devc->input_byte = 0;
+	devc->my_dev = 0;
+	devc->share_irq = 0;
+
+	save_flags(flags);
+	cli();
+	ok = reset_uart401(devc);
+	restore_flags(flags);
+
+	if (!ok)
+		goto cleanup_devc;
+
+	if (hw_config->name)
+		name = hw_config->name;
+
+	if (devc->irq < 0) {
+		devc->share_irq = 1;
+		devc->irq *= -1;
+	} else
+		devc->share_irq = 0;
+
+	if (!devc->share_irq)
+		if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) {
+			printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
+			devc->share_irq = 1;
+		}
+	devc->my_dev = sound_alloc_mididev();
+	enter_uart_mode(devc);
+
+	if (devc->my_dev == -1) {
+		printk(KERN_INFO "uart401: Too many midi devices detected\n");
+		goto cleanup_irq;
+	}
+	conf_printf(name, hw_config);
+	midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
+	if (!midi_devs[devc->my_dev]) {
+		printk(KERN_ERR "uart401: Failed to allocate memory\n");
+		goto cleanup_unload_mididev;
+	}
+	memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations));
+
+	if (owner)
+		midi_devs[devc->my_dev]->owner = owner;
+	
+	midi_devs[devc->my_dev]->devc = devc;
+	midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+	if (!midi_devs[devc->my_dev]->converter) {
+		printk(KERN_WARNING "uart401: Failed to allocate memory\n");
+		goto cleanup_midi_devs;
+	}
+	memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations));
+	strcpy(midi_devs[devc->my_dev]->info.name, name);
+	midi_devs[devc->my_dev]->converter->id = "UART401";
+	midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev;
+
+	if (owner)
+		midi_devs[devc->my_dev]->converter->owner = owner;
+
+	hw_config->slots[4] = devc->my_dev;
+	sequencer_init();
+	devc->opened = 0;
+	return 1;
+cleanup_midi_devs:
+	kfree(midi_devs[devc->my_dev]);
+cleanup_unload_mididev:
+	sound_unload_mididev(devc->my_dev);
+cleanup_irq:
+	if (!devc->share_irq)
+		free_irq(devc->irq, devc);
+cleanup_devc:
+	kfree(devc);
+cleanup_region:
+	release_region(hw_config->io_base, 4);
+	return 0;
+}
+
+void unload_uart401(struct address_info *hw_config)
+{
+	uart401_devc *devc;
+	int n=hw_config->slots[4];
+	
+	/* Not set up */
+	if(n==-1 || midi_devs[n]==NULL)
+		return;
+		
+	/* Not allocated (erm ??) */
+	
+	devc = midi_devs[hw_config->slots[4]]->devc;
+	if (devc == NULL)
+		return;
+
+	reset_uart401(devc);
+	release_region(hw_config->io_base, 4);
+
+	if (!devc->share_irq)
+		free_irq(devc->irq, devc);
+	if (devc)
+	{
+		kfree(midi_devs[devc->my_dev]->converter);
+		kfree(midi_devs[devc->my_dev]);
+		kfree(devc);
+		devc = NULL;
+	}
+	/* This kills midi_devs[x] */
+	sound_unload_mididev(hw_config->slots[4]);
+}
+
+EXPORT_SYMBOL(probe_uart401);
+EXPORT_SYMBOL(unload_uart401);
+EXPORT_SYMBOL(uart401intr);
+
+static struct address_info cfg_mpu;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+
+
+static int __init init_uart401(void)
+{
+	cfg_mpu.irq = irq;
+	cfg_mpu.io_base = io;
+
+	/* Can be loaded either for module use or to provide functions
+	   to others */
+	if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) {
+		printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
+		if (!probe_uart401(&cfg_mpu, THIS_MODULE))
+			return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_uart401(void)
+{
+	if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1)
+		unload_uart401(&cfg_mpu);
+}
+
+module_init(init_uart401);
+module_exit(cleanup_uart401);
+
+#ifndef MODULE
+static int __init setup_uart401(char *str)
+{
+	/* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+
+	io = ints[1];
+	irq = ints[2];
+	
+	return 1;
+}
+
+__setup("uart401=", setup_uart401);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/uart6850.c linux-2.4.19-pre5-mjc/sound/oss/uart6850.c
--- linux/sound/oss/uart6850.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/uart6850.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,364 @@
+/*
+ * sound/uart6850.c
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
+ * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
+ *
+ * Alan Cox:		Updated for new modular code. Removed snd_* irq handling. Now
+ *			uses native linux resources
+ * Christoph Hellwig:	Adapted to module_init/module_exit
+ * Jeff Garzik:		Made it work again, in theory
+ *			FIXME: If the request_irq() succeeds, the probe succeeds. Ug.
+ *
+ *	Status: Testing required (no shit -jgarzik)
+ *
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
+ *      added 6850 support, used with COVOX SoundMaster II and custom cards.
+ */
+
+#include "sound_config.h"
+
+static int uart6850_base = 0x330;
+
+static int *uart6850_osp;
+
+#define	DATAPORT   (uart6850_base)
+#define	COMDPORT   (uart6850_base+1)
+#define	STATPORT   (uart6850_base+1)
+
+static int uart6850_status(void)
+{
+	return inb(STATPORT);
+}
+
+#define input_avail()		(uart6850_status()&INPUT_AVAIL)
+#define output_ready()		(uart6850_status()&OUTPUT_READY)
+
+static void uart6850_cmd(unsigned char cmd)
+{
+	outb(cmd, COMDPORT);
+}
+
+static int uart6850_read(void)
+{
+	return inb(DATAPORT);
+}
+
+static void uart6850_write(unsigned char byte)
+{
+	outb(byte, DATAPORT);
+}
+
+#define	OUTPUT_READY	0x02	/* Mask for data ready Bit */
+#define	INPUT_AVAIL	0x01	/* Mask for Data Send Ready Bit */
+
+#define	UART_RESET	0x95
+#define	UART_MODE_ON	0x03
+
+static int uart6850_opened;
+static int uart6850_irq;
+static int uart6850_detected;
+static int my_dev;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+static void poll_uart6850(unsigned long dummy);
+
+
+static struct timer_list uart6850_timer = {
+	function: poll_uart6850
+};
+
+static void uart6850_input_loop(void)
+{
+	int count = 10;
+
+	while (count)
+	{
+		/*
+		 * Not timed out
+		 */
+		if (input_avail())
+		{
+			unsigned char c = uart6850_read();
+			count = 100;
+			if (uart6850_opened & OPEN_READ)
+				midi_input_intr(my_dev, c);
+		}
+		else
+		{
+			while (!input_avail() && count)
+				count--;
+		}
+	}
+}
+
+void m6850intr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	if (input_avail())
+		uart6850_input_loop();
+}
+
+/*
+ *	It looks like there is no input interrupts in the UART mode. Let's try
+ *	polling.
+ */
+
+static void poll_uart6850(unsigned long dummy)
+{
+	unsigned long flags;
+
+	if (!(uart6850_opened & OPEN_READ))
+		return;		/* Device has been closed */
+
+	save_flags(flags);
+	cli();
+
+	if (input_avail())
+		uart6850_input_loop();
+
+	uart6850_timer.expires = 1 + jiffies;
+	add_timer(&uart6850_timer);
+	
+	/*
+	 *	Come back later
+	 */
+
+	restore_flags(flags);
+}
+
+static int uart6850_open(int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+	if (uart6850_opened)
+	{
+/*		  printk("Midi6850: Midi busy\n");*/
+		  return -EBUSY;
+	};
+
+	uart6850_cmd(UART_RESET);
+	uart6850_input_loop();
+	midi_input_intr = input;
+	uart6850_opened = mode;
+	poll_uart6850(0);	/*
+				 * Enable input polling
+				 */
+
+	return 0;
+}
+
+static void uart6850_close(int dev)
+{
+	uart6850_cmd(UART_MODE_ON);
+	del_timer(&uart6850_timer);
+	uart6850_opened = 0;
+}
+
+static int uart6850_out(int dev, unsigned char midi_byte)
+{
+	int timeout;
+	unsigned long flags;
+
+	/*
+	 * Test for input since pending input seems to block the output.
+	 */
+
+	save_flags(flags);
+	cli();
+
+	if (input_avail())
+		uart6850_input_loop();
+
+	restore_flags(flags);
+
+	/*
+	 * Sometimes it takes about 13000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	for (timeout = 30000; timeout > 0 && !output_ready(); timeout--);	/*
+										 * Wait
+										 */
+	if (!output_ready())
+	{
+		printk(KERN_WARNING "Midi6850: Timeout\n");
+		return 0;
+	}
+	uart6850_write(midi_byte);
+	return 1;
+}
+
+static inline int uart6850_command(int dev, unsigned char *midi_byte)
+{
+	return 1;
+}
+
+static inline int uart6850_start_read(int dev)
+{
+	return 0;
+}
+
+static inline int uart6850_end_read(int dev)
+{
+	return 0;
+}
+
+static inline void uart6850_kick(int dev)
+{
+}
+
+static inline int uart6850_buffer_status(int dev)
+{
+	return 0;		/*
+				 * No data in buffers
+				 */
+}
+
+#define MIDI_SYNTH_NAME	"6850 UART Midi"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations uart6850_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"6850 UART", 0, 0, SNDCARD_UART6850},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		uart6850_open,
+	close:		uart6850_close,
+	outputc:	uart6850_out,
+	start_read:	uart6850_start_read,
+	end_read:	uart6850_end_read,
+	kick:		uart6850_kick,
+	command:	uart6850_command,
+	buffer_status:	uart6850_buffer_status
+};
+
+
+static void __init attach_uart6850(struct address_info *hw_config)
+{
+	int ok, timeout;
+	unsigned long   flags;
+
+	if (!uart6850_detected)
+		return;
+
+	if ((my_dev = sound_alloc_mididev()) == -1)
+	{
+		printk(KERN_INFO "uart6850: Too many midi devices detected\n");
+		return;
+	}
+	uart6850_base = hw_config->io_base;
+	uart6850_osp = hw_config->osp;
+	uart6850_irq = hw_config->irq;
+
+	save_flags(flags);
+	cli();
+
+	for (timeout = 30000; timeout > 0 && !output_ready(); timeout--);	/*
+										 * Wait
+										 */
+	uart6850_cmd(UART_MODE_ON);
+	ok = 1;
+	restore_flags(flags);
+
+	conf_printf("6850 Midi Interface", hw_config);
+
+	std_midi_synth.midi_dev = my_dev;
+	hw_config->slots[4] = my_dev;
+	midi_devs[my_dev] = &uart6850_operations;
+	sequencer_init();
+}
+
+static inline int reset_uart6850(void)
+{
+	uart6850_read();
+	return 1;		/*
+				 * OK
+				 */
+}
+
+static int __init probe_uart6850(struct address_info *hw_config)
+{
+	int ok;
+
+	uart6850_osp = hw_config->osp;
+	uart6850_base = hw_config->io_base;
+	uart6850_irq = hw_config->irq;
+
+	if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0)
+		return 0;
+
+	ok = reset_uart6850();
+	uart6850_detected = ok;
+	return ok;
+}
+
+static void __exit unload_uart6850(struct address_info *hw_config)
+{
+	free_irq(hw_config->irq, NULL);
+	sound_unload_mididev(hw_config->slots[4]);
+}
+
+static struct address_info cfg_mpu;
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+
+static int __init init_uart6850(void)
+{
+	cfg_mpu.io_base = io;
+	cfg_mpu.irq = irq;
+
+	if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) {
+		printk(KERN_INFO "uart6850: irq and io must be set.\n");
+		return -EINVAL;
+	}
+
+	if (probe_uart6850(&cfg_mpu))
+		return -ENODEV;
+	attach_uart6850(&cfg_mpu);
+
+	return 0;
+}
+
+static void __exit cleanup_uart6850(void)
+{
+	unload_uart6850(&cfg_mpu);
+}
+
+module_init(init_uart6850);
+module_exit(cleanup_uart6850);
+
+#ifndef MODULE
+static int __init setup_uart6850(char *str)
+{
+	/* io, irq */
+	int ints[3];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io = ints[1];
+	irq = ints[2];
+
+	return 1;
+}
+__setup("uart6850=", setup_uart6850);
+#endif
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/ulaw.h linux-2.4.19-pre5-mjc/sound/oss/ulaw.h
--- linux/sound/oss/ulaw.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ulaw.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,69 @@
+static unsigned char ulaw_dsp[] = {
+     3,    7,   11,   15,   19,   23,   27,   31,
+    35,   39,   43,   47,   51,   55,   59,   63,
+    66,   68,   70,   72,   74,   76,   78,   80,
+    82,   84,   86,   88,   90,   92,   94,   96,
+    98,   99,  100,  101,  102,  103,  104,  105,
+   106,  107,  108,  109,  110,  111,  112,  113,
+   113,  114,  114,  115,  115,  116,  116,  117,
+   117,  118,  118,  119,  119,  120,  120,  121,
+   121,  121,  122,  122,  122,  122,  123,  123,
+   123,  123,  124,  124,  124,  124,  125,  125,
+   125,  125,  125,  125,  126,  126,  126,  126,
+   126,  126,  126,  126,  127,  127,  127,  127,
+   127,  127,  127,  127,  127,  127,  127,  127,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   253,  249,  245,  241,  237,  233,  229,  225,
+   221,  217,  213,  209,  205,  201,  197,  193,
+   190,  188,  186,  184,  182,  180,  178,  176,
+   174,  172,  170,  168,  166,  164,  162,  160,
+   158,  157,  156,  155,  154,  153,  152,  151,
+   150,  149,  148,  147,  146,  145,  144,  143,
+   143,  142,  142,  141,  141,  140,  140,  139,
+   139,  138,  138,  137,  137,  136,  136,  135,
+   135,  135,  134,  134,  134,  134,  133,  133,
+   133,  133,  132,  132,  132,  132,  131,  131,
+   131,  131,  131,  131,  130,  130,  130,  130,
+   130,  130,  130,  130,  129,  129,  129,  129,
+   129,  129,  129,  129,  129,  129,  129,  129,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+};
+
+static unsigned char dsp_ulaw[] = {
+     0,    0,    0,    0,    0,    1,    1,    1,
+     1,    2,    2,    2,    2,    3,    3,    3,
+     3,    4,    4,    4,    4,    5,    5,    5,
+     5,    6,    6,    6,    6,    7,    7,    7,
+     7,    8,    8,    8,    8,    9,    9,    9,
+     9,   10,   10,   10,   10,   11,   11,   11,
+    11,   12,   12,   12,   12,   13,   13,   13,
+    13,   14,   14,   14,   14,   15,   15,   15,
+    15,   16,   16,   17,   17,   18,   18,   19,
+    19,   20,   20,   21,   21,   22,   22,   23,
+    23,   24,   24,   25,   25,   26,   26,   27,
+    27,   28,   28,   29,   29,   30,   30,   31,
+    31,   32,   33,   34,   35,   36,   37,   38,
+    39,   40,   41,   42,   43,   44,   45,   46,
+    47,   49,   51,   53,   55,   57,   59,   61,
+    63,   66,   70,   74,   78,   84,   92,  104,
+   254,  231,  219,  211,  205,  201,  197,  193,
+   190,  188,  186,  184,  182,  180,  178,  176,
+   175,  174,  173,  172,  171,  170,  169,  168,
+   167,  166,  165,  164,  163,  162,  161,  160,
+   159,  159,  158,  158,  157,  157,  156,  156,
+   155,  155,  154,  154,  153,  153,  152,  152,
+   151,  151,  150,  150,  149,  149,  148,  148,
+   147,  147,  146,  146,  145,  145,  144,  144,
+   143,  143,  143,  143,  142,  142,  142,  142,
+   141,  141,  141,  141,  140,  140,  140,  140,
+   139,  139,  139,  139,  138,  138,  138,  138,
+   137,  137,  137,  137,  136,  136,  136,  136,
+   135,  135,  135,  135,  134,  134,  134,  134,
+   133,  133,  133,  133,  132,  132,  132,  132,
+   131,  131,  131,  131,  130,  130,  130,  130,
+   129,  129,  129,  129,  128,  128,  128,  128,
+};
diff -Nru linux/sound/oss/v_midi.c linux-2.4.19-pre5-mjc/sound/oss/v_midi.c
--- linux/sound/oss/v_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/v_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,290 @@
+/*
+ * sound/v_midi.c
+ *
+ * The low level driver for the Sound Blaster DS chips.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1996
+ *
+ * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ * ??
+ *
+ * Changes
+ *	Alan Cox		Modularisation, changed memory allocations
+ *	Christoph Hellwig	Adapted to module_init/module_exit
+ *
+ * Status
+ *	Untested
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+
+#include "v_midi.h"
+
+static vmidi_devc *v_devc[2] = { NULL, NULL};
+static int midi1,midi2;
+static void *midi_mem = NULL;
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+
+void            (*midi_input_intr) (int dev, unsigned char data);
+
+static int v_midi_open (int dev, int mode,
+	      void            (*input) (int dev, unsigned char data),
+	      void            (*output) (int dev)
+)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return -(ENXIO);
+
+	save_flags (flags);
+	cli();
+	if (devc->opened)
+	{
+		restore_flags (flags);
+		return -(EBUSY);
+	}
+	devc->opened = 1;
+	restore_flags (flags);
+
+	devc->intr_active = 1;
+
+	if (mode & OPEN_READ)
+	{
+		devc->input_opened = 1;
+		devc->midi_input_intr = input;
+	}
+
+	return 0;
+}
+
+static void v_midi_close (int dev)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	unsigned long flags;
+
+	if (devc == NULL)
+		return;
+
+	save_flags (flags);
+	cli ();
+	devc->intr_active = 0;
+	devc->input_opened = 0;
+	devc->opened = 0;
+	restore_flags (flags);
+}
+
+static int v_midi_out (int dev, unsigned char midi_byte)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc;
+
+	if (devc == NULL)
+		return -(ENXIO);
+
+	if (pdevc->input_opened > 0){
+		if (MIDIbuf_avail(pdevc->my_mididev) > 500)
+			return 0;
+		pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
+	}
+	return 1;
+}
+
+static inline int v_midi_start_read (int dev)
+{
+	return 0;
+}
+
+static int v_midi_end_read (int dev)
+{
+	vmidi_devc *devc = midi_devs[dev]->devc;
+	if (devc == NULL)
+		return -ENXIO;
+
+	devc->intr_active = 0;
+	return 0;
+}
+
+/* why -EPERM and not -EINVAL?? */
+
+static inline int v_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+{
+	return -EPERM;
+}
+
+
+#define MIDI_SYNTH_NAME	"Loopback MIDI"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+
+#include "midi_synth.h"
+
+static struct midi_operations v_midi_operations =
+{
+	owner:		THIS_MODULE,
+	info:		{"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		v_midi_open,
+	close:		v_midi_close,
+	ioctl:		v_midi_ioctl,
+	outputc:	v_midi_out,
+	start_read:	v_midi_start_read,
+	end_read:	v_midi_end_read,
+};
+
+static struct midi_operations v_midi_operations2 =
+{
+	owner:		THIS_MODULE,
+	info:		{"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
+	converter:	&std_midi_synth,
+	in_info:	{0},
+	open:		v_midi_open,
+	close:		v_midi_close,
+	ioctl:		v_midi_ioctl,
+	outputc:	v_midi_out,
+	start_read:	v_midi_start_read,
+	end_read:	v_midi_end_read,
+};
+
+/*
+ *	We kmalloc just one of these - it makes life simpler and the code
+ *	cleaner and the memory handling far more efficient
+ */
+ 
+struct vmidi_memory
+{
+	/* Must be first */
+	struct midi_operations m_ops[2];
+	struct synth_operations s_ops[2];
+	struct vmidi_devc v_ops[2];
+};
+
+static void __init attach_v_midi (struct address_info *hw_config)
+{
+	struct vmidi_memory *m;
+	/* printk("Attaching v_midi device.....\n"); */
+
+	midi1 = sound_alloc_mididev();
+	if (midi1 == -1)
+	{
+		printk(KERN_ERR "v_midi: Too many midi devices detected\n");
+		return;
+	}
+	
+	m=(struct vmidi_memory *)kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
+	if (m == NULL)
+	{
+		printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
+		sound_unload_mididev(midi1);
+		return;
+	}
+	
+	midi_mem = m;
+	
+	midi_devs[midi1] = &m->m_ops[0];
+	
+
+	midi2 = sound_alloc_mididev();
+	if (midi2 == -1)
+	{
+		printk (KERN_ERR "v_midi: Too many midi devices detected\n");
+		kfree(m);
+		sound_unload_mididev(midi1);
+		return;
+	}
+
+	midi_devs[midi2] = &m->m_ops[1];
+
+	/* printk("VMIDI1: %d   VMIDI2: %d\n",midi1,midi2); */
+
+	/* for MIDI-1 */
+	v_devc[0] = &m->v_ops[0];
+	memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
+		sizeof (struct midi_operations));
+
+	v_devc[0]->my_mididev = midi1;
+	v_devc[0]->pair_mididev = midi2;
+	v_devc[0]->opened = v_devc[0]->input_opened = 0;
+	v_devc[0]->intr_active = 0;
+	v_devc[0]->midi_input_intr = NULL;
+
+	midi_devs[midi1]->devc = v_devc[0];
+
+	midi_devs[midi1]->converter = &m->s_ops[0];
+	std_midi_synth.midi_dev = midi1;
+	memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
+		sizeof (struct synth_operations));
+	midi_devs[midi1]->converter->id = "V_MIDI 1";
+
+	/* for MIDI-2 */
+	v_devc[1] = &m->v_ops[1];
+
+	memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
+		sizeof (struct midi_operations));
+
+	v_devc[1]->my_mididev = midi2;
+	v_devc[1]->pair_mididev = midi1;
+	v_devc[1]->opened = v_devc[1]->input_opened = 0;
+	v_devc[1]->intr_active = 0;
+	v_devc[1]->midi_input_intr = NULL;
+
+	midi_devs[midi2]->devc = v_devc[1];
+	midi_devs[midi2]->converter = &m->s_ops[1];
+
+	std_midi_synth.midi_dev = midi2;
+	memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
+		sizeof (struct synth_operations));
+	midi_devs[midi2]->converter->id = "V_MIDI 2";
+
+	sequencer_init();
+	/* printk("Attached v_midi device\n"); */
+}
+
+static inline int __init probe_v_midi(struct address_info *hw_config)
+{
+	return(1);	/* always OK */
+}
+
+
+static void __exit unload_v_midi(struct address_info *hw_config)
+{
+	sound_unload_mididev(midi1);
+	sound_unload_mididev(midi2);
+	kfree(midi_mem);
+}
+
+static struct address_info cfg; /* dummy */
+
+static int __init init_vmidi(void)
+{
+	printk("MIDI Loopback device driver\n");
+	if (!probe_v_midi(&cfg))
+		return -ENODEV;
+	attach_v_midi(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_vmidi(void)
+{
+	unload_v_midi(&cfg);
+}
+
+module_init(init_vmidi);
+module_exit(cleanup_vmidi);
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/v_midi.h linux-2.4.19-pre5-mjc/sound/oss/v_midi.h
--- linux/sound/oss/v_midi.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/v_midi.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,15 @@
+typedef struct vmidi_devc {
+	   int dev;
+
+	/* State variables */
+ 	   int opened;
+
+	
+	/* MIDI fields */
+	   int my_mididev;
+	   int pair_mididev;
+	   int input_opened;
+	   int intr_active;
+	   void (*midi_input_intr) (int dev, unsigned char data);
+	} vmidi_devc;
+
diff -Nru linux/sound/oss/via82cxxx_audio.c linux-2.4.19-pre5-mjc/sound/oss/via82cxxx_audio.c
--- linux/sound/oss/via82cxxx_audio.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/via82cxxx_audio.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,3539 @@
+/*
+ * Support for VIA 82Cxxx Audio Codecs
+ * Copyright 1999,2000 Jeff Garzik
+ *
+ * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2.
+ * See the "COPYING" file distributed with this software for more info.
+ *
+ * For a list of known bugs (errata) and documentation,
+ * see via-audio.pdf in linux/Documentation/DocBook.
+ * If this documentation does not exist, run "make pdfdocs".
+ *
+ */
+
+
+#define VIA_VERSION	"1.9.1"
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/sound.h>
+#include <linux/poll.h>
+#include <linux/soundcard.h>
+#include <linux/ac97_codec.h>
+#include <linux/smp_lock.h>
+#include <linux/ioport.h>
+#include <linux/wrapper.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/semaphore.h>
+#include "sound_config.h"
+#include "dev_table.h"
+#include "mpu401.h"
+
+
+#undef VIA_DEBUG	/* define to enable debugging output and checks */
+#ifdef VIA_DEBUG
+/* note: prints function name for you */
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#undef VIA_NDEBUG	/* define to disable lightweight runtime checks */
+#ifdef VIA_NDEBUG
+#define assert(expr)
+#else
+#define assert(expr) \
+        if(!(expr)) {					\
+        printk( "Assertion failed! %s,%s,%s,line=%d\n",	\
+        #expr,__FILE__,__FUNCTION__,__LINE__);		\
+        }
+#endif
+
+#if defined(CONFIG_PROC_FS) && \
+    defined(CONFIG_SOUND_ALSA_VIA82CXXX_PROCFS)
+#define VIA_PROC_FS 1
+#endif
+
+#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */
+
+#define MAX_CARDS	1
+
+#define VIA_CARD_NAME	"VIA 82Cxxx Audio driver " VIA_VERSION
+#define VIA_MODULE_NAME "via82cxxx"
+#define PFX		VIA_MODULE_NAME ": "
+
+#define VIA_COUNTER_LIMIT	100000
+
+/* size of DMA buffers */
+#define VIA_MAX_BUFFER_DMA_PAGES	32
+
+/* buffering default values in ms */
+#define VIA_DEFAULT_FRAG_TIME		20
+#define VIA_DEFAULT_BUFFER_TIME		500
+
+#define VIA_MAX_FRAG_SIZE		PAGE_SIZE
+#define VIA_MIN_FRAG_SIZE		64
+
+#define VIA_MIN_FRAG_NUMBER		2
+
+/* 82C686 function 5 (audio codec) PCI configuration registers */
+#define VIA_ACLINK_STATUS	0x40
+#define VIA_ACLINK_CTRL		0x41
+#define VIA_FUNC_ENABLE		0x42
+#define VIA_PNP_CONTROL		0x43
+#define VIA_FM_NMI_CTRL		0x48
+
+/*
+ * controller base 0 (scatter-gather) registers
+ *
+ * NOTE: Via datasheet lists first channel as "read"
+ * channel and second channel as "write" channel.
+ * I changed the naming of the constants to be more
+ * clear than I felt the datasheet to be.
+ */
+
+#define VIA_BASE0_PCM_OUT_CHAN	0x00 /* output PCM to user */
+#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00
+#define VIA_BASE0_PCM_OUT_CHAN_CTRL	0x01
+#define VIA_BASE0_PCM_OUT_CHAN_TYPE	0x02
+
+#define VIA_BASE0_PCM_IN_CHAN		0x10 /* input PCM from user */
+#define VIA_BASE0_PCM_IN_CHAN_STATUS	0x10
+#define VIA_BASE0_PCM_IN_CHAN_CTRL	0x11
+#define VIA_BASE0_PCM_IN_CHAN_TYPE	0x12
+
+/* offsets from base */
+#define VIA_PCM_STATUS			0x00
+#define VIA_PCM_CONTROL			0x01
+#define VIA_PCM_TYPE			0x02
+#define VIA_PCM_TABLE_ADDR		0x04
+#define VIA_PCM_BLOCK_COUNT		0x0C
+
+/* XXX unused DMA channel for FM PCM data */
+#define VIA_BASE0_FM_OUT_CHAN		0x20
+#define VIA_BASE0_FM_OUT_CHAN_STATUS	0x20
+#define VIA_BASE0_FM_OUT_CHAN_CTRL	0x21
+#define VIA_BASE0_FM_OUT_CHAN_TYPE	0x22
+
+#define VIA_BASE0_AC97_CTRL		0x80
+#define VIA_BASE0_SGD_STATUS_SHADOW	0x84
+#define VIA_BASE0_GPI_INT_ENABLE	0x8C
+#define VIA_INTR_OUT			((1<<0) |  (1<<4) |  (1<<8))
+#define VIA_INTR_IN			((1<<1) |  (1<<5) |  (1<<9))
+#define VIA_INTR_FM			((1<<2) |  (1<<6) | (1<<10))
+#define VIA_INTR_MASK		(VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM)
+
+/* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */
+#define VIA_IRQ_ON_FLAG			(1<<0)	/* int on each flagged scatter block */
+#define VIA_IRQ_ON_EOL			(1<<1)	/* int at end of scatter list */
+#define VIA_INT_SEL_PCI_LAST_LINE_READ	(0)	/* int at PCI read of last line */
+#define VIA_INT_SEL_LAST_SAMPLE_SENT	(1<<2)	/* int at last sample sent */
+#define VIA_INT_SEL_ONE_LINE_LEFT	(1<<3)	/* int at less than one line to send */
+#define VIA_PCM_FMT_STEREO		(1<<4)	/* PCM stereo format (bit clear == mono) */
+#define VIA_PCM_FMT_16BIT		(1<<5)	/* PCM 16-bit format (bit clear == 8-bit) */
+#define VIA_PCM_REC_FIFO		(1<<6)	/* PCM Recording FIFO */
+#define VIA_RESTART_SGD_ON_EOL		(1<<7)	/* restart scatter-gather at EOL */
+#define VIA_PCM_FMT_MASK		(VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT)
+#define VIA_CHAN_TYPE_MASK		(VIA_RESTART_SGD_ON_EOL | \
+					 VIA_IRQ_ON_FLAG | \
+					 VIA_IRQ_ON_EOL)
+#define VIA_CHAN_TYPE_INT_SELECT	(VIA_INT_SEL_LAST_SAMPLE_SENT)
+
+/* PCI configuration register bits and masks */
+#define VIA_CR40_AC97_READY	0x01
+#define VIA_CR40_AC97_LOW_POWER	0x02
+#define VIA_CR40_SECONDARY_READY 0x04
+
+#define VIA_CR41_AC97_ENABLE	0x80 /* enable AC97 codec */
+#define VIA_CR41_AC97_RESET	0x40 /* clear bit to reset AC97 */
+#define VIA_CR41_AC97_WAKEUP	0x20 /* wake up from power-down mode */
+#define VIA_CR41_AC97_SDO	0x10 /* force Serial Data Out (SDO) high */
+#define VIA_CR41_VRA		0x08 /* enable variable sample rate */
+#define VIA_CR41_PCM_ENABLE	0x04 /* AC Link SGD Read Channel PCM Data Output */
+#define VIA_CR41_FM_PCM_ENABLE	0x02 /* AC Link FM Channel PCM Data Out */
+#define VIA_CR41_SB_PCM_ENABLE	0x01 /* AC Link SB PCM Data Output */
+#define VIA_CR41_BOOT_MASK	(VIA_CR41_AC97_ENABLE | \
+				 VIA_CR41_AC97_WAKEUP | \
+				 VIA_CR41_AC97_SDO)
+#define VIA_CR41_RUN_MASK	(VIA_CR41_AC97_ENABLE | \
+				 VIA_CR41_AC97_RESET | \
+				 VIA_CR41_VRA | \
+				 VIA_CR41_PCM_ENABLE)
+
+#define VIA_CR42_SB_ENABLE	0x01
+#define VIA_CR42_MIDI_ENABLE	0x02
+#define VIA_CR42_FM_ENABLE	0x04
+#define VIA_CR42_GAME_ENABLE	0x08
+#define VIA_CR42_MIDI_IRQMASK   0x40
+#define VIA_CR42_MIDI_PNP	0x80
+
+#define VIA_CR44_SECOND_CODEC_SUPPORT	(1 << 6)
+#define VIA_CR44_AC_LINK_ACCESS		(1 << 7)
+
+#define VIA_CR48_FM_TRAP_TO_NMI		(1 << 2)
+
+/* controller base 0 register bitmasks */
+#define VIA_INT_DISABLE_MASK		(~(0x01|0x02))
+#define VIA_SGD_STOPPED			(1 << 2)
+#define VIA_SGD_PAUSED			(1 << 6)
+#define VIA_SGD_ACTIVE			(1 << 7)
+#define VIA_SGD_TERMINATE		(1 << 6)
+#define VIA_SGD_FLAG			(1 << 0)
+#define VIA_SGD_EOL			(1 << 1)
+#define VIA_SGD_START			(1 << 7)
+
+#define VIA_CR80_FIRST_CODEC		0
+#define VIA_CR80_SECOND_CODEC		(1 << 30)
+#define VIA_CR80_FIRST_CODEC_VALID	(1 << 25)
+#define VIA_CR80_VALID			(1 << 25)
+#define VIA_CR80_SECOND_CODEC_VALID	(1 << 27)
+#define VIA_CR80_BUSY			(1 << 24)
+#define VIA_CR83_BUSY			(1)
+#define VIA_CR83_FIRST_CODEC_VALID	(1 << 1)
+#define VIA_CR80_READ			(1 << 23)
+#define VIA_CR80_WRITE_MODE		0
+#define VIA_CR80_REG_IDX(idx)		((((idx) & 0xFF) >> 1) << 16)
+
+/* capabilities we announce */
+#ifdef VIA_SUPPORT_MMAP
+#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \
+		     DSP_CAP_TRIGGER | DSP_CAP_REALTIME)
+#else
+#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \
+		     DSP_CAP_TRIGGER | DSP_CAP_REALTIME)
+#endif
+
+/* scatter-gather DMA table entry, exactly as passed to hardware */
+struct via_sgd_table {
+	u32 addr;
+	u32 count;	/* includes additional VIA_xxx bits also */
+};
+
+#define VIA_EOL (1 << 31)
+#define VIA_FLAG (1 << 30)
+#define VIA_STOP (1 << 29)
+
+
+enum via_channel_states {
+	sgd_stopped = 0,
+	sgd_in_progress = 1,
+};
+
+
+struct via_buffer_pgtbl {
+	dma_addr_t handle;
+	void *cpuaddr;
+};
+
+
+struct via_channel {
+	atomic_t n_frags;
+	atomic_t hw_ptr;
+	wait_queue_head_t wait;
+
+	unsigned int sw_ptr;
+	unsigned int slop_len;
+	unsigned int n_irqs;
+	int bytes;
+
+	unsigned is_active : 1;
+	unsigned is_record : 1;
+	unsigned is_mapped : 1;
+	unsigned is_enabled : 1;
+	u8 pcm_fmt;		/* VIA_PCM_FMT_xxx */
+
+	unsigned rate;		/* sample rate */
+	unsigned int frag_size;
+	unsigned int frag_number;
+
+	volatile struct via_sgd_table *sgtable;
+	dma_addr_t sgt_handle;
+
+	unsigned int page_number;
+	struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES];
+
+	long iobase;
+
+	const char *name;
+};
+
+
+/* data stored for each chip */
+struct via_info {
+	struct pci_dev *pdev;
+	long baseaddr;
+
+	struct ac97_codec ac97;
+	spinlock_t lock;
+	int card_num;		/* unique card number, from 0 */
+
+	int dev_dsp;		/* /dev/dsp index from register_sound_dsp() */
+
+	unsigned rev_h : 1;
+
+	int locked_rate : 1;
+
+	struct semaphore syscall_sem;
+	struct semaphore open_sem;
+
+	struct via_channel ch_in;
+	struct via_channel ch_out;
+	struct via_channel ch_fm;
+
+#ifdef CONFIG_MIDI_VIA82CXXX
+        void *midi_devc;
+        struct address_info midi_info;
+#endif
+};
+
+
+/* number of cards, used for assigning unique numbers to cards */
+static unsigned via_num_cards = 0;
+
+
+
+/****************************************************************
+ *
+ * prototypes
+ *
+ *
+ */
+
+static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id);
+static void __devexit via_remove_one (struct pci_dev *pdev);
+
+static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos);
+static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos);
+static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait);
+static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int via_dsp_open (struct inode *inode, struct file *file);
+static int via_dsp_release(struct inode *inode, struct file *file);
+static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma);
+
+static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg);
+static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value);
+static u8 via_ac97_wait_idle (struct via_info *card);
+
+static void via_chan_free (struct via_info *card, struct via_channel *chan);
+static void via_chan_clear (struct via_info *card, struct via_channel *chan);
+static void via_chan_pcm_fmt (struct via_channel *chan, int reset);
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan);
+
+#ifdef VIA_PROC_FS
+static int via_init_proc (void);
+static void via_cleanup_proc (void);
+static int via_card_init_proc (struct via_info *card);
+static void via_card_cleanup_proc (struct via_info *card);
+#else
+static inline int via_init_proc (void) { return 0; }
+static inline void via_cleanup_proc (void) {}
+static inline int via_card_init_proc (struct via_info *card) { return 0; }
+static inline void via_card_cleanup_proc (struct via_info *card) {}
+#endif
+
+
+/****************************************************************
+ *
+ * Various data the driver needs
+ *
+ *
+ */
+
+
+static struct pci_device_id via_pci_tbl[] __initdata = {
+	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci,via_pci_tbl);
+
+
+static struct pci_driver via_driver = {
+	name:		VIA_MODULE_NAME,
+	id_table:	via_pci_tbl,
+	probe:		via_init_one,
+	remove:		__devexit_p(via_remove_one),
+};
+
+
+/****************************************************************
+ *
+ * Low-level base 0 register read/write helpers
+ *
+ *
+ */
+
+/**
+ *	via_chan_stop - Terminate DMA on specified PCM channel
+ *	@iobase: PCI base address for SGD channel registers
+ *
+ *	Terminate scatter-gather DMA operation for given
+ *	channel (derived from @iobase), if DMA is active.
+ *
+ *	Note that @iobase is not the PCI base address,
+ *	but the PCI base address plus an offset to
+ *	one of three PCM channels supported by the chip.
+ *
+ */
+
+static inline void via_chan_stop (long iobase)
+{
+	if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE)
+		outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL);
+}
+
+
+/**
+ *	via_chan_status_clear - Clear status flags on specified DMA channel
+ *	@iobase: PCI base address for SGD channel registers
+ *
+ *	Clear any pending status flags for the given
+ *	DMA channel (derived from @iobase), if any
+ *	flags are asserted.
+ *
+ *	Note that @iobase is not the PCI base address,
+ *	but the PCI base address plus an offset to
+ *	one of three PCM channels supported by the chip.
+ *
+ */
+
+static inline void via_chan_status_clear (long iobase)
+{
+	u8 tmp = inb (iobase + VIA_PCM_STATUS);
+
+	if (tmp != 0)
+		outb (tmp, iobase + VIA_PCM_STATUS);
+}
+
+
+/**
+ *	sg_begin - Begin recording or playback on a PCM channel
+ *	@chan: Channel for which DMA operation shall begin
+ *
+ *	Start scatter-gather DMA for the given channel.
+ *
+ */
+
+static inline void sg_begin (struct via_channel *chan)
+{
+	outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL);
+}
+
+
+static int sg_active (long iobase)
+{
+	u8 tmp = inb (iobase + VIA_PCM_STATUS);
+	if ((tmp & VIA_SGD_STOPPED) || (tmp & VIA_SGD_PAUSED)) {
+		printk(KERN_WARNING "via82cxxx warning: SG stopped or paused\n");
+		return 0;
+	}
+	if (tmp & VIA_SGD_ACTIVE)
+		return 1;
+	return 0;
+}
+
+
+/****************************************************************
+ *
+ * Miscellaneous debris
+ *
+ *
+ */
+
+
+/**
+ *	via_syscall_down - down the card-specific syscell semaphore
+ *	@card: Private info for specified board
+ *	@nonblock: boolean, non-zero if O_NONBLOCK is set
+ *
+ *	Encapsulates standard method of acquiring the syscall sem.
+ *
+ *	Returns negative errno on error, or zero for success.
+ */
+
+static inline int via_syscall_down (struct via_info *card, int nonblock)
+{
+	/* Thomas Sailer:
+	 * EAGAIN is supposed to be used if IO is pending,
+	 * not if there is contention on some internal
+	 * synchronization primitive which should be
+	 * held only for a short time anyway
+	 */
+	nonblock = 0;
+
+	if (nonblock) {
+		if (down_trylock (&card->syscall_sem))
+			return -EAGAIN;
+	} else {
+		if (down_interruptible (&card->syscall_sem))
+			return -ERESTARTSYS;
+	}
+
+	return 0;
+}
+
+
+/**
+ *	via_stop_everything - Stop all audio operations
+ *	@card: Private info for specified board
+ *
+ *	Stops all DMA operations and interrupts, and clear
+ *	any pending status bits resulting from those operations.
+ */
+
+static void via_stop_everything (struct via_info *card)
+{
+	u8 tmp, new_tmp;
+
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+
+	/*
+	 * terminate any existing operations on audio read/write channels
+	 */
+	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
+	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
+	via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+
+	/*
+	 * clear any existing stops / flags (sanity check mainly)
+	 */
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+
+	/*
+	 * clear any enabled interrupt bits
+	 */
+	tmp = inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE);
+	new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);
+	if (tmp != new_tmp)
+		outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE);
+
+	tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE);
+	new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);
+	if (tmp != new_tmp)
+		outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE);
+
+	tmp = inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);
+	new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);
+	if (tmp != new_tmp)
+		outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);
+
+	udelay(10);
+
+	/*
+	 * clear any existing flags
+	 */
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
+	via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/**
+ *	via_set_rate - Set PCM rate for given channel
+ *	@ac97: Pointer to generic codec info struct
+ *	@chan: Private info for specified channel
+ *	@rate: Desired PCM sample rate, in Khz
+ *
+ *	Sets the PCM sample rate for a channel.
+ *
+ *	Values for @rate are clamped to a range of 4000 Khz through 48000 Khz,
+ *	due to hardware constraints.
+ */
+
+static int via_set_rate (struct ac97_codec *ac97,
+			 struct via_channel *chan, unsigned rate)
+{
+	struct via_info *card = ac97->private_data;
+	int rate_reg;
+
+	DPRINTK ("ENTER, rate = %d\n", rate);
+
+	if (chan->rate == rate)
+		goto out;
+	if (card->locked_rate) {
+		chan->rate = 48000;
+		goto out;
+	}
+
+	if (rate > 48000)		rate = 48000;
+	if (rate < 4000) 		rate = 4000;
+
+	rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE :
+			    AC97_PCM_FRONT_DAC_RATE;
+
+	via_ac97_write_reg (ac97, AC97_POWER_CONTROL,
+		(via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) |
+		0x0200);
+
+	via_ac97_write_reg (ac97, rate_reg, rate);
+
+	via_ac97_write_reg (ac97, AC97_POWER_CONTROL,
+		via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200);
+
+	udelay (10);
+
+	/* the hardware might return a value different than what we
+	 * passed to it, so read the rate value back from hardware
+	 * to see what we came up with
+	 */
+	chan->rate = via_ac97_read_reg (ac97, rate_reg);
+
+	if (chan->rate == 0) {
+		card->locked_rate = 1;
+		chan->rate = 48000;
+		printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n");
+	}
+
+out:
+	DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate);
+	return chan->rate;
+}
+
+
+/****************************************************************
+ *
+ * Channel-specific operations
+ *
+ *
+ */
+
+
+/**
+ *	via_chan_init_defaults - Initialize a struct via_channel
+ *	@card: Private audio chip info
+ *	@chan: Channel to be initialized
+ *
+ *	Zero @chan, and then set all static defaults for the structure.
+ */
+
+static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan)
+{
+	memset (chan, 0, sizeof (*chan));
+
+	if (chan == &card->ch_out) {
+		chan->name = "PCM-OUT";
+		chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN;
+	} else if (chan == &card->ch_in) {
+		chan->name = "PCM-IN";
+		chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN;
+		chan->is_record = 1;
+	} else if (chan == &card->ch_fm) {
+		chan->name = "PCM-OUT-FM";
+		chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN;
+	} else {
+		BUG();
+	}
+
+	init_waitqueue_head (&chan->wait);
+
+	chan->pcm_fmt = VIA_PCM_FMT_MASK;
+	chan->is_enabled = 1;
+
+	chan->frag_number = 0;
+        chan->frag_size = 0;
+	atomic_set(&chan->n_frags, 0);
+	atomic_set (&chan->hw_ptr, 0);
+}
+
+/**
+ *      via_chan_init - Initialize PCM channel
+ *      @card: Private audio chip info
+ *      @chan: Channel to be initialized
+ *
+ *      Performs some of the preparations necessary to begin
+ *      using a PCM channel.
+ *
+ *      Currently the preparations consist in
+ *      setting the
+ *      PCM channel to a known state.
+ */
+
+
+static void via_chan_init (struct via_info *card, struct via_channel *chan)
+{
+
+        DPRINTK ("ENTER\n");
+
+	/* bzero channel structure, and init members to defaults */
+        via_chan_init_defaults (card, chan);
+
+        /* stop any existing channel output */
+        via_chan_clear (card, chan);
+        via_chan_status_clear (chan->iobase);
+        via_chan_pcm_fmt (chan, 1);
+
+	DPRINTK ("EXIT\n");
+}
+
+/**
+ *	via_chan_buffer_init - Initialize PCM channel buffer
+ *	@card: Private audio chip info
+ *	@chan: Channel to be initialized
+ *
+ *	Performs some of the preparations necessary to begin
+ *	using a PCM channel.
+ *
+ *	Currently the preparations include allocating the
+ *	scatter-gather DMA table and buffers,
+ *	and passing the
+ *	address of the DMA table to the hardware.
+ *
+ *	Note that special care is taken when passing the
+ *	DMA table address to hardware, because it was found
+ *	during driver development that the hardware did not
+ *	always "take" the address.
+ */
+
+static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan)
+{
+	int page, offset;
+	int i;
+
+	DPRINTK ("ENTER\n");
+
+	if (chan->sgtable != NULL) {
+		DPRINTK ("EXIT\n");
+		return 0;
+	}
+
+	/* alloc DMA-able memory for scatter-gather table */
+	chan->sgtable = pci_alloc_consistent (card->pdev,
+		(sizeof (struct via_sgd_table) * chan->frag_number),
+		&chan->sgt_handle);
+	if (!chan->sgtable) {
+		printk (KERN_ERR PFX "DMA table alloc fail, aborting\n");
+		DPRINTK ("EXIT\n");
+		return -ENOMEM;
+	}
+
+	memset ((void*)chan->sgtable, 0,
+		(sizeof (struct via_sgd_table) * chan->frag_number));
+
+	/* alloc DMA-able memory for scatter-gather buffers */
+
+	chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE +
+			    (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0);
+
+	for (i = 0; i < chan->page_number; i++) {
+		chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE,
+					      &chan->pgtbl[i].handle);
+
+		if (!chan->pgtbl[i].cpuaddr) {
+			chan->page_number = i;
+			goto err_out_nomem;
+		}
+
+#ifndef VIA_NDEBUG
+                memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size);
+#endif
+
+#if 1
+                DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n",
+			i, (long)chan->pgtbl[i].handle,
+			virt_to_phys(chan->pgtbl[i].cpuaddr),
+			chan->pgtbl[i].cpuaddr);
+#endif
+	}
+
+	for (i = 0; i < chan->frag_number; i++) {
+
+		page = i / (PAGE_SIZE / chan->frag_size);
+		offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size;
+
+		chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
+		chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset);
+
+#if 1
+		DPRINTK ("dmabuf #%d (32(h)=%lx)\n",
+			 i,
+			 (long)chan->sgtable[i].addr);
+#endif
+	}
+
+	/* overwrite the last buffer information */
+	chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
+
+	/* set location of DMA-able scatter-gather info table */
+	DPRINTK ("outl (0x%X, 0x%04lX)\n",
+		chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR);
+
+	via_ac97_wait_idle (card);
+	outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR);
+	udelay (20);
+	via_ac97_wait_idle (card);
+
+	DPRINTK ("inl (0x%lX) = %x\n",
+		chan->iobase + VIA_PCM_TABLE_ADDR,
+		inl(chan->iobase + VIA_PCM_TABLE_ADDR));
+
+	DPRINTK ("EXIT\n");
+	return 0;
+
+err_out_nomem:
+	printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n");
+	via_chan_buffer_free (card, chan);
+	DPRINTK ("EXIT\n");
+	return -ENOMEM;
+}
+
+
+/**
+ *	via_chan_free - Release a PCM channel
+ *	@card: Private audio chip info
+ *	@chan: Channel to be released
+ *
+ *	Performs all the functions necessary to clean up
+ *	an initialized channel.
+ *
+ *	Currently these functions include disabled any
+ *	active DMA operations, setting the PCM channel
+ *	back to a known state, and releasing any allocated
+ *	sound buffers.
+ */
+
+static void via_chan_free (struct via_info *card, struct via_channel *chan)
+{
+	DPRINTK ("ENTER\n");
+
+	spin_lock_irq (&card->lock);
+
+	/* stop any existing channel output */
+	via_chan_status_clear (chan->iobase);
+	via_chan_stop (chan->iobase);
+	via_chan_status_clear (chan->iobase);
+
+	spin_unlock_irq (&card->lock);
+
+	synchronize_irq();
+
+	DPRINTK ("EXIT\n");
+}
+
+static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan)
+{
+	int i;
+
+        DPRINTK ("ENTER\n");
+
+	/* zero location of DMA-able scatter-gather info table */
+	via_ac97_wait_idle(card);
+	outl (0, chan->iobase + VIA_PCM_TABLE_ADDR);
+
+	for (i = 0; i < chan->page_number; i++)
+		if (chan->pgtbl[i].cpuaddr) {
+			pci_free_consistent (card->pdev, PAGE_SIZE,
+					     chan->pgtbl[i].cpuaddr,
+					     chan->pgtbl[i].handle);
+			chan->pgtbl[i].cpuaddr = NULL;
+			chan->pgtbl[i].handle = 0;
+		}
+
+	chan->page_number = 0;
+
+	if (chan->sgtable) {
+		pci_free_consistent (card->pdev,
+			(sizeof (struct via_sgd_table) * chan->frag_number),
+			(void*)chan->sgtable, chan->sgt_handle);
+		chan->sgtable = NULL;
+	}
+
+	DPRINTK ("EXIT\n");
+}
+
+
+/**
+ *	via_chan_pcm_fmt - Update PCM channel settings
+ *	@chan: Channel to be updated
+ *	@reset: Boolean.  If non-zero, channel will be reset
+ *		to 8-bit mono mode.
+ *
+ *	Stores the settings of the current PCM format,
+ *	8-bit or 16-bit, and mono/stereo, into the
+ *	hardware settings for the specified channel.
+ *	If @reset is non-zero, the channel is reset
+ *	to 8-bit mono mode.  Otherwise, the channel
+ *	is set to the values stored in the channel
+ *	information struct @chan.
+ */
+
+static void via_chan_pcm_fmt (struct via_channel *chan, int reset)
+{
+	DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n",
+		 chan->pcm_fmt, reset ? "yes" : "no");
+
+	assert (chan != NULL);
+
+	if (reset)
+		/* reset to 8-bit mono mode */
+		chan->pcm_fmt = 0;
+
+	/* enable interrupts on FLAG and EOL */
+	chan->pcm_fmt |= VIA_CHAN_TYPE_MASK;
+
+	/* if we are recording, enable recording fifo bit */
+	if (chan->is_record)
+		chan->pcm_fmt |= VIA_PCM_REC_FIFO;
+	/* set interrupt select bits where applicable (PCM in & out channels) */
+	if (!chan->is_record)
+		chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
+
+	outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE);
+
+	DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n",
+		 chan->pcm_fmt,
+		 inb (chan->iobase + VIA_PCM_TYPE));
+}
+
+
+/**
+ *	via_chan_clear - Stop DMA channel operation, and reset pointers
+ *	@card: the chip to accessed
+ *	@chan: Channel to be cleared
+ *
+ *	Call via_chan_stop to halt DMA operations, and then resets
+ *	all software pointers which track DMA operation.
+ */
+
+static void via_chan_clear (struct via_info *card, struct via_channel *chan)
+{
+	DPRINTK ("ENTER\n");
+	via_chan_stop (chan->iobase);
+	via_chan_buffer_free(card, chan);
+	chan->is_active = 0;
+	chan->is_mapped = 0;
+	chan->is_enabled = 1;
+	chan->slop_len = 0;
+	chan->sw_ptr = 0;
+	chan->n_irqs = 0;
+	atomic_set (&chan->hw_ptr, 0);
+	DPRINTK ("EXIT\n");
+}
+
+
+/**
+ *	via_chan_set_speed - Set PCM sample rate for given channel
+ *	@card: Private info for specified board
+ *	@chan: Channel whose sample rate will be adjusted
+ *	@val: New sample rate, in Khz
+ *
+ *	Helper function for the %SNDCTL_DSP_SPEED ioctl.  OSS semantics
+ *	demand that all audio operations halt (if they are not already
+ *	halted) when the %SNDCTL_DSP_SPEED is given.
+ *
+ *	This function halts all audio operations for the given channel
+ *	@chan, and then calls via_set_rate to set the audio hardware
+ *	to the new rate.
+ */
+
+static int via_chan_set_speed (struct via_info *card,
+			       struct via_channel *chan, int val)
+{
+	DPRINTK ("ENTER, requested rate = %d\n", val);
+
+	via_chan_clear (card, chan);
+
+	val = via_set_rate (&card->ac97, chan, val);
+
+	DPRINTK ("EXIT, returning %d\n", val);
+	return val;
+}
+
+
+/**
+ *	via_chan_set_fmt - Set PCM sample size for given channel
+ *	@card: Private info for specified board
+ *	@chan: Channel whose sample size will be adjusted
+ *	@val: New sample size, use the %AFMT_xxx constants
+ *
+ *	Helper function for the %SNDCTL_DSP_SETFMT ioctl.  OSS semantics
+ *	demand that all audio operations halt (if they are not already
+ *	halted) when the %SNDCTL_DSP_SETFMT is given.
+ *
+ *	This function halts all audio operations for the given channel
+ *	@chan, and then calls via_chan_pcm_fmt to set the audio hardware
+ *	to the new sample size, either 8-bit or 16-bit.
+ */
+
+static int via_chan_set_fmt (struct via_info *card,
+			     struct via_channel *chan, int val)
+{
+	DPRINTK ("ENTER, val=%s\n",
+		 val == AFMT_U8 ? "AFMT_U8" :
+	 	 val == AFMT_S16_LE ? "AFMT_S16_LE" :
+		 "unknown");
+
+	via_chan_clear (card, chan);
+
+	assert (val != AFMT_QUERY); /* this case is handled elsewhere */
+
+	switch (val) {
+	case AFMT_S16_LE:
+		if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) {
+			chan->pcm_fmt |= VIA_PCM_FMT_16BIT;
+			via_chan_pcm_fmt (chan, 0);
+		}
+		break;
+
+	case AFMT_U8:
+		if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) {
+			chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT;
+			via_chan_pcm_fmt (chan, 0);
+		}
+		break;
+
+	default:
+		DPRINTK ("unknown AFMT: 0x%X\n", val);
+		val = AFMT_S16_LE;
+	}
+
+	DPRINTK ("EXIT\n");
+	return val;
+}
+
+
+/**
+ *	via_chan_set_stereo - Enable or disable stereo for a DMA channel
+ *	@card: Private info for specified board
+ *	@chan: Channel whose stereo setting will be adjusted
+ *	@val: New sample size, use the %AFMT_xxx constants
+ *
+ *	Helper function for the %SNDCTL_DSP_CHANNELS and %SNDCTL_DSP_STEREO ioctls.  OSS semantics
+ *	demand that all audio operations halt (if they are not already
+ *	halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO is given.
+ *
+ *	This function halts all audio operations for the given channel
+ *	@chan, and then calls via_chan_pcm_fmt to set the audio hardware
+ *	to enable or disable stereo.
+ */
+
+static int via_chan_set_stereo (struct via_info *card,
+			        struct via_channel *chan, int val)
+{
+	DPRINTK ("ENTER, channels = %d\n", val);
+
+	via_chan_clear (card, chan);
+
+	switch (val) {
+
+	/* mono */
+	case 1:
+		chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO;
+		via_chan_pcm_fmt (chan, 0);
+		break;
+
+	/* stereo */
+	case 2:
+		chan->pcm_fmt |= VIA_PCM_FMT_STEREO;
+		via_chan_pcm_fmt (chan, 0);
+		break;
+
+	/* unknown */
+	default:
+		printk (KERN_WARNING PFX "unknown number of channels\n");
+		val = -EINVAL;
+		break;
+	}
+
+	DPRINTK ("EXIT, returning %d\n", val);
+	return val;
+}
+
+static int via_chan_set_buffering (struct via_info *card,
+                                struct via_channel *chan, int val)
+{
+	int shift;
+
+        DPRINTK ("ENTER\n");
+
+	/* in both cases the buffer cannot be changed */
+	if (chan->is_active || chan->is_mapped) {
+		DPRINTK ("EXIT\n");
+		return -EINVAL;
+	}
+
+	/* called outside SETFRAGMENT */
+	/* set defaults or do nothing */
+	if (val < 0) {
+
+		if (chan->frag_size && chan->frag_number)
+			goto out;
+
+		DPRINTK ("\n");
+
+		chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate *
+				   ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) *
+				   ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1;
+
+		shift = 0;
+		while (chan->frag_size) {
+			chan->frag_size >>= 1;
+			shift++;
+		}
+		chan->frag_size = 1 << shift;
+
+		chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME);
+
+		DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number);
+	} else {
+		chan->frag_size = 1 << (val & 0xFFFF);
+		chan->frag_number = (val >> 16) & 0xFFFF;
+
+		DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number);
+	}
+
+	/* quake3 wants frag_number to be a power of two */
+	shift = 0;
+	while (chan->frag_number) {
+		chan->frag_number >>= 1;
+		shift++;
+	}
+	chan->frag_number = 1 << shift;
+
+	if (chan->frag_size > VIA_MAX_FRAG_SIZE)
+		chan->frag_size = VIA_MAX_FRAG_SIZE;
+	else if (chan->frag_size < VIA_MIN_FRAG_SIZE)
+		chan->frag_size = VIA_MIN_FRAG_SIZE;
+
+	if (chan->frag_number < VIA_MIN_FRAG_NUMBER)
+                chan->frag_number = VIA_MIN_FRAG_NUMBER;
+
+	if ((chan->frag_number * chan->frag_size) / PAGE_SIZE > VIA_MAX_BUFFER_DMA_PAGES)
+		chan->frag_number = (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE) / chan->frag_size;
+
+out:
+	if (chan->is_record)
+		atomic_set (&chan->n_frags, 0);
+	else
+		atomic_set (&chan->n_frags, chan->frag_number);
+
+	DPRINTK ("EXIT\n");
+
+	return 0;
+}
+
+#ifdef VIA_CHAN_DUMP_BUFS
+/**
+ *	via_chan_dump_bufs - Display DMA table contents
+ *	@chan: Channel whose DMA table will be displayed
+ *
+ *	Debugging function which displays the contents of the
+ *	scatter-gather DMA table for the given channel @chan.
+ */
+
+static void via_chan_dump_bufs (struct via_channel *chan)
+{
+	int i;
+
+	for (i = 0; i < chan->frag_number; i++) {
+		DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
+			 i, chan->sgtable[i].addr,
+			 chan->sgtable[i].count & 0x00FFFFFF,
+			 chan->sgtable[i].count & VIA_FLAG ? 1 : 0,
+			 chan->sgtable[i].count & VIA_EOL ? 1 : 0);
+	}
+	DPRINTK ("buf_in_use = %d, nextbuf = %d\n",
+		 atomic_read (&chan->buf_in_use),
+		 atomic_read (&chan->sw_ptr));
+}
+#endif /* VIA_CHAN_DUMP_BUFS */
+
+
+/**
+ *	via_chan_flush_frag - Flush partially-full playback buffer to hardware
+ *	@chan: Channel whose DMA table will be displayed
+ *
+ *	Flushes partially-full playback buffer to hardware.
+ */
+
+static void via_chan_flush_frag (struct via_channel *chan)
+{
+	DPRINTK ("ENTER\n");
+
+	assert (chan->slop_len > 0);
+
+	if (chan->sw_ptr == (chan->frag_number - 1))
+		chan->sw_ptr = 0;
+	else
+		chan->sw_ptr++;
+
+	chan->slop_len = 0;
+
+	assert (atomic_read (&chan->n_frags) > 0);
+	atomic_dec (&chan->n_frags);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+
+/**
+ *	via_chan_maybe_start - Initiate audio hardware DMA operation
+ *	@chan: Channel whose DMA is to be started
+ *
+ *	Initiate DMA operation, if the DMA engine for the given
+ *	channel @chan is not already active.
+ */
+
+static inline void via_chan_maybe_start (struct via_channel *chan)
+{
+	assert (chan->is_active == sg_active(chan->iobase));
+
+	if (!chan->is_active && chan->is_enabled) {
+		chan->is_active = 1;
+		sg_begin (chan);
+		DPRINTK ("starting channel %s\n", chan->name);
+	}
+}
+
+
+/****************************************************************
+ *
+ * Interface to ac97-codec module
+ *
+ *
+ */
+
+/**
+ *	via_ac97_wait_idle - Wait until AC97 codec is not busy
+ *	@card: Private info for specified board
+ *
+ *	Sleep until the AC97 codec is no longer busy.
+ *	Returns the final value read from the SGD
+ *	register being polled.
+ */
+
+static u8 via_ac97_wait_idle (struct via_info *card)
+{
+	u8 tmp8;
+	int counter = VIA_COUNTER_LIMIT;
+
+	DPRINTK ("ENTER/EXIT\n");
+
+	assert (card != NULL);
+	assert (card->pdev != NULL);
+
+	do {
+		udelay (15);
+
+		tmp8 = inb (card->baseaddr + 0x83);
+	} while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0));
+
+	if (tmp8 & VIA_CR83_BUSY)
+		printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n");
+	return tmp8;
+}
+
+
+/**
+ *	via_ac97_read_reg - Read AC97 standard register
+ *	@codec: Pointer to generic AC97 codec info
+ *	@reg: Index of AC97 register to be read
+ *
+ *	Read the value of a single AC97 codec register,
+ *	as defined by the Intel AC97 specification.
+ *
+ *	Defines the standard AC97 read-register operation
+ *	required by the kernel's ac97_codec interface.
+ *
+ *	Returns the 16-bit value stored in the specified
+ *	register.
+ */
+
+static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg)
+{
+	unsigned long data;
+	struct via_info *card;
+	int counter;
+
+	DPRINTK ("ENTER\n");
+
+	assert (codec != NULL);
+	assert (codec->private_data != NULL);
+
+	card = codec->private_data;
+
+	/* Every time we write to register 80 we cause a transaction.
+	   The only safe way to clear the valid bit is to write it at
+	   the same time as the command */
+	data = (reg << 16) | VIA_CR80_READ | VIA_CR80_VALID;
+
+	outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL);
+	udelay (20);
+
+	for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
+		udelay (1);
+		if ((((data = inl(card->baseaddr + VIA_BASE0_AC97_CTRL)) &
+		      (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID))
+			goto out;
+	}
+
+	printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data);
+	goto err_out;
+
+out:
+	/* Once the valid bit has become set, we must wait a complete AC97
+	   frame before the data has settled. */
+	udelay(25);
+	data = (unsigned long) inl (card->baseaddr + VIA_BASE0_AC97_CTRL);
+
+	outb (0x02, card->baseaddr + 0x83);
+
+	if (((data & 0x007F0000) >> 16) == reg) {
+		DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n",
+			 data, data & 0x0000FFFF);
+		return data & 0x0000FFFF;
+	}
+
+	printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n",
+		reg, ((data & 0x007F0000) >> 16));
+
+err_out:
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+/**
+ *	via_ac97_write_reg - Write AC97 standard register
+ *	@codec: Pointer to generic AC97 codec info
+ *	@reg: Index of AC97 register to be written
+ *	@value: Value to be written to AC97 register
+ *
+ *	Write the value of a single AC97 codec register,
+ *	as defined by the Intel AC97 specification.
+ *
+ *	Defines the standard AC97 write-register operation
+ *	required by the kernel's ac97_codec interface.
+ */
+
+static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value)
+{
+	u32 data;
+	struct via_info *card;
+	int counter;
+
+	DPRINTK ("ENTER\n");
+
+	assert (codec != NULL);
+	assert (codec->private_data != NULL);
+
+	card = codec->private_data;
+
+	data = (reg << 16) + value;
+	outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL);
+	udelay (10);
+
+	for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) {
+		if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0)
+			goto out;
+
+		udelay (15);
+	}
+
+	printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value);
+
+out:
+	DPRINTK ("EXIT\n");
+}
+
+
+static int via_mixer_open (struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct via_info *card;
+	struct pci_dev *pdev;
+	struct pci_driver *drvr;
+
+	DPRINTK ("ENTER\n");
+
+	pci_for_each_dev(pdev) {
+		drvr = pci_dev_driver (pdev);
+		if (drvr == &via_driver) {
+			assert (pci_get_drvdata (pdev) != NULL);
+
+			card = pci_get_drvdata (pdev);
+			if (card->ac97.dev_mixer == minor)
+				goto match;
+		}
+	}
+
+	DPRINTK ("EXIT, returning -ENODEV\n");
+	return -ENODEV;
+
+match:
+	file->private_data = &card->ac97;
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
+			    unsigned long arg)
+{
+	struct ac97_codec *codec = file->private_data;
+	struct via_info *card;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+	int rc;
+
+	DPRINTK ("ENTER\n");
+
+	assert (codec != NULL);
+	card = codec->private_data;
+	assert (card != NULL);
+
+	rc = via_syscall_down (card, nonblock);
+	if (rc) goto out;
+
+	rc = codec->mixer_ioctl(codec, cmd, arg);
+
+	up (&card->syscall_sem);
+
+out:
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static struct file_operations via_mixer_fops = {
+	owner:		THIS_MODULE,
+	open:		via_mixer_open,
+	llseek:		no_llseek,
+	ioctl:		via_mixer_ioctl,
+};
+
+
+static int __init via_ac97_reset (struct via_info *card)
+{
+	struct pci_dev *pdev = card->pdev;
+	u8 tmp8;
+	u16 tmp16;
+
+	DPRINTK ("ENTER\n");
+
+	assert (pdev != NULL);
+
+#ifndef NDEBUG
+	{
+		u8 r40,r41,r42,r43,r44,r48;
+		pci_read_config_byte (card->pdev, 0x40, &r40);
+		pci_read_config_byte (card->pdev, 0x41, &r41);
+		pci_read_config_byte (card->pdev, 0x42, &r42);
+		pci_read_config_byte (card->pdev, 0x43, &r43);
+		pci_read_config_byte (card->pdev, 0x44, &r44);
+		pci_read_config_byte (card->pdev, 0x48, &r48);
+		DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
+			r40,r41,r42,r43,r44,r48);
+
+		spin_lock_irq (&card->lock);
+		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+			 inb (card->baseaddr + 0x00),
+			 inb (card->baseaddr + 0x01),
+			 inb (card->baseaddr + 0x02),
+			 inl (card->baseaddr + 0x04),
+			 inl (card->baseaddr + 0x0C),
+			 inl (card->baseaddr + 0x80),
+			 inl (card->baseaddr + 0x84));
+		spin_unlock_irq (&card->lock);
+
+	}
+#endif
+
+        /*
+         * Reset AC97 controller: enable, disable, enable,
+         * pausing after each command for good luck.  Only
+	 * do this if the codec is not ready, because it causes
+	 * loud pops and such due to such a hard codec reset.
+         */
+	pci_read_config_byte (pdev, VIA_ACLINK_STATUS, &tmp8);
+	if ((tmp8 & VIA_CR40_AC97_READY) == 0) {
+        	pci_write_config_byte (pdev, VIA_ACLINK_CTRL,
+				       VIA_CR41_AC97_ENABLE |
+                		       VIA_CR41_AC97_RESET |
+				       VIA_CR41_AC97_WAKEUP);
+        	udelay (100);
+
+        	pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0);
+        	udelay (100);
+
+        	pci_write_config_byte (pdev, VIA_ACLINK_CTRL,
+				       VIA_CR41_AC97_ENABLE |
+				       VIA_CR41_PCM_ENABLE |
+                		       VIA_CR41_VRA | VIA_CR41_AC97_RESET);
+        	udelay (100);
+	}
+
+	/* Make sure VRA is enabled, in case we didn't do a
+	 * complete codec reset, above
+	 */
+	pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8);
+	if (((tmp8 & VIA_CR41_VRA) == 0) ||
+	    ((tmp8 & VIA_CR41_AC97_ENABLE) == 0) ||
+	    ((tmp8 & VIA_CR41_PCM_ENABLE) == 0) ||
+	    ((tmp8 & VIA_CR41_AC97_RESET) == 0)) {
+        	pci_write_config_byte (pdev, VIA_ACLINK_CTRL,
+				       VIA_CR41_AC97_ENABLE |
+				       VIA_CR41_PCM_ENABLE |
+                		       VIA_CR41_VRA | VIA_CR41_AC97_RESET);
+        	udelay (100);
+	}
+
+#if 0 /* this breaks on K7M */
+	/* disable legacy stuff */
+	pci_write_config_byte (pdev, 0x42, 0x00);
+	udelay(10);
+#endif
+
+	/* route FM trap to IRQ, disable FM trap */
+	pci_write_config_byte (pdev, 0x48, 0x05);
+	udelay(10);
+
+	/* disable all codec GPI interrupts */
+	outl (0, pci_resource_start (pdev, 0) + 0x8C);
+
+	/* WARNING: this line is magic.  Remove this
+	 * and things break. */
+	/* enable variable rate */
+ 	tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS);
+ 	if ((tmp16 & 1) == 0)
+ 		via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1);
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static void via_ac97_codec_wait (struct ac97_codec *codec)
+{
+	assert (codec->private_data != NULL);
+	via_ac97_wait_idle (codec->private_data);
+}
+
+
+static int __init via_ac97_init (struct via_info *card)
+{
+	int rc;
+	u16 tmp16;
+
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+
+	memset (&card->ac97, 0, sizeof (card->ac97));
+	card->ac97.private_data = card;
+	card->ac97.codec_read = via_ac97_read_reg;
+	card->ac97.codec_write = via_ac97_write_reg;
+	card->ac97.codec_wait = via_ac97_codec_wait;
+
+	card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
+	if (card->ac97.dev_mixer < 0) {
+		printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
+		DPRINTK ("EXIT, returning -EIO\n");
+		return -EIO;
+	}
+
+	rc = via_ac97_reset (card);
+	if (rc) {
+		printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n");
+		goto err_out;
+	}
+
+	if (ac97_probe_codec (&card->ac97) == 0) {
+		printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n");
+		rc = -EIO;
+		goto err_out;
+	}
+
+	/* enable variable rate */
+	tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS);
+	via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1);
+
+ 	/*
+ 	 * If we cannot enable VRA, we have a locked-rate codec.
+ 	 * We try again to enable VRA before assuming so, however.
+ 	 */
+ 	tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS);
+ 	if ((tmp16 & 1) == 0) {
+ 		via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1);
+ 		tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS);
+ 		if ((tmp16 & 1) == 0) {
+ 			card->locked_rate = 1;
+ 			printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n");
+ 		}
+ 	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out:
+	unregister_sound_mixer (card->ac97.dev_mixer);
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static void via_ac97_cleanup (struct via_info *card)
+{
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+	assert (card->ac97.dev_mixer >= 0);
+
+	unregister_sound_mixer (card->ac97.dev_mixer);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+
+/****************************************************************
+ *
+ * Interrupt-related code
+ *
+ */
+
+/**
+ *	via_intr_channel - handle an interrupt for a single channel
+ *	@chan: handle interrupt for this channel
+ *
+ *	This is the "meat" of the interrupt handler,
+ *	containing the actions taken each time an interrupt
+ *	occurs.  All communication and coordination with
+ *	userspace takes place here.
+ *
+ *	Locking: inside card->lock
+ */
+
+static void via_intr_channel (struct via_channel *chan)
+{
+	u8 status;
+	int n;
+
+	/* check pertinent bits of status register for action bits */
+	status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED);
+	if (!status)
+		return;
+
+	/* acknowledge any flagged bits ASAP */
+	outb (status, chan->iobase);
+
+	if (!chan->sgtable) /* XXX: temporary solution */
+		return;
+
+	/* grab current h/w ptr value */
+	n = atomic_read (&chan->hw_ptr);
+
+	/* sanity check: make sure our h/w ptr doesn't have a weird value */
+	assert (n >= 0);
+	assert (n < chan->frag_number);
+
+	/* reset SGD data structure in memory to reflect a full buffer,
+	 * and advance the h/w ptr, wrapping around to zero if needed
+	 */
+	if (n == (chan->frag_number - 1)) {
+		chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_EOL);
+		atomic_set (&chan->hw_ptr, 0);
+	} else {
+		chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_FLAG);
+		atomic_inc (&chan->hw_ptr);
+	}
+
+	/* accounting crap for SNDCTL_DSP_GETxPTR */
+	chan->n_irqs++;
+	chan->bytes += chan->frag_size;
+	if (chan->bytes < 0) /* handle overflow of 31-bit value */
+		chan->bytes = chan->frag_size;
+
+	/* wake up anyone listening to see when interrupts occur */
+	if (waitqueue_active (&chan->wait))
+		wake_up_all (&chan->wait);
+
+	DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n",
+		 chan->name, status, (long) inl (chan->iobase + 0x04),
+		 atomic_read (&chan->hw_ptr));
+
+	/* all following checks only occur when not in mmap(2) mode */
+	if (chan->is_mapped)
+		return;
+
+	/* If we are recording, then n_frags represents the number
+	 * of fragments waiting to be handled by userspace.
+	 * If we are playback, then n_frags represents the number
+	 * of fragments remaining to be filled by userspace.
+	 * We increment here.  If we reach max number of fragments,
+	 * this indicates an underrun/overrun.  For this case under OSS,
+	 * we stop the record/playback process.
+	 */
+	if (atomic_read (&chan->n_frags) < chan->frag_number)
+		atomic_inc (&chan->n_frags);
+	assert (atomic_read (&chan->n_frags) <= chan->frag_number);
+
+	if (atomic_read (&chan->n_frags) == chan->frag_number) {
+		chan->is_active = 0;
+		via_chan_stop (chan->iobase);
+	}
+
+	DPRINTK ("%s intr, channel n_frags == %d\n", chan->name,
+		 atomic_read (&chan->n_frags));
+}
+
+
+static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct via_info *card = dev_id;
+	u32 status32;
+
+	/* to minimize interrupt sharing costs, we use the SGD status
+	 * shadow register to check the status of all inputs and
+	 * outputs with a single 32-bit bus read.  If no interrupt
+	 * conditions are flagged, we exit immediately
+	 */
+	status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW);
+	if (!(status32 & VIA_INTR_MASK))
+        {
+#ifdef CONFIG_MIDI_VIA82CXXX
+	    	 if (card->midi_devc)
+                    	uart401intr(irq, card->midi_devc, regs);
+#endif
+		return;
+    	}
+	DPRINTK ("intr, status32 == 0x%08X\n", status32);
+
+	/* synchronize interrupt handling under SMP.  this spinlock
+	 * goes away completely on UP
+	 */
+	spin_lock (&card->lock);
+
+	if (status32 & VIA_INTR_OUT)
+		via_intr_channel (&card->ch_out);
+	if (status32 & VIA_INTR_IN)
+		via_intr_channel (&card->ch_in);
+	if (status32 & VIA_INTR_FM)
+		via_intr_channel (&card->ch_fm);
+
+	spin_unlock (&card->lock);
+}
+
+
+/**
+ *	via_interrupt_init - Initialize interrupt handling
+ *	@card: Private info for specified board
+ *
+ *	Obtain and reserve IRQ for using in handling audio events.
+ *	Also, disable any IRQ-generating resources, to make sure
+ *	we don't get interrupts before we want them.
+ */
+
+static int via_interrupt_init (struct via_info *card)
+{
+	u8 tmp8;
+
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+	assert (card->pdev != NULL);
+
+	/* check for sane IRQ number. can this ever happen? */
+	if (card->pdev->irq < 2) {
+		printk (KERN_ERR PFX "insane IRQ %d, aborting\n",
+			card->pdev->irq);
+		DPRINTK ("EXIT, returning -EIO\n");
+		return -EIO;
+	}
+
+	/* make sure FM irq is not routed to us */
+	pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8);
+	if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) {
+		tmp8 |= VIA_CR48_FM_TRAP_TO_NMI;
+		pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8);
+	}
+
+	if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
+		printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
+			card->pdev->irq);
+		DPRINTK ("EXIT, returning -EBUSY\n");
+		return -EBUSY;
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+/****************************************************************
+ *
+ * OSS DSP device
+ *
+ */
+
+static struct file_operations via_dsp_fops = {
+	owner:		THIS_MODULE,
+	open:		via_dsp_open,
+	release:	via_dsp_release,
+	read:		via_dsp_read,
+	write:		via_dsp_write,
+	poll:		via_dsp_poll,
+	llseek: 	no_llseek,
+	ioctl:		via_dsp_ioctl,
+	mmap:		via_dsp_mmap,
+};
+
+
+static int __init via_dsp_init (struct via_info *card)
+{
+	u8 tmp8;
+
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+
+	/* turn off legacy features, if not already */
+	pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8);
+	if (tmp8 & (VIA_CR42_SB_ENABLE |  VIA_CR42_FM_ENABLE)) {
+		tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE);
+		pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);
+	}
+
+	via_stop_everything (card);
+
+	card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1);
+	if (card->dev_dsp < 0) {
+		DPRINTK ("EXIT, returning -ENODEV\n");
+		return -ENODEV;
+	}
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static void via_dsp_cleanup (struct via_info *card)
+{
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+	assert (card->dev_dsp >= 0);
+
+	via_stop_everything (card);
+
+	unregister_sound_dsp (card->dev_dsp);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+static struct page * via_mm_nopage (struct vm_area_struct * vma,
+				    unsigned long address, int write_access)
+{
+	struct via_info *card = vma->vm_private_data;
+	struct via_channel *chan = &card->ch_out;
+	struct page *dmapage;
+	unsigned long pgoff;
+	int rd, wr;
+
+	DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n",
+		 vma->vm_start,
+		 address - vma->vm_start,
+		 (address - vma->vm_start) >> PAGE_SHIFT,
+		 address,
+		 write_access);
+
+        if (address > vma->vm_end) {
+		DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n");
+		return NOPAGE_SIGBUS; /* Disallow mremap */
+	}
+        if (!card) {
+		DPRINTK ("EXIT, returning NOPAGE_OOM\n");
+		return NOPAGE_OOM;	/* Nothing allocated */
+	}
+
+	pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT);
+	rd = card->ch_in.is_mapped;
+	wr = card->ch_out.is_mapped;
+
+#ifndef VIA_NDEBUG
+	{
+	unsigned long max_bufs = chan->frag_number;
+	if (rd && wr) max_bufs *= 2;
+	/* via_dsp_mmap() should ensure this */
+	assert (pgoff < max_bufs);
+	}
+#endif
+
+	/* if full-duplex (read+write) and we have two sets of bufs,
+	 * then the playback buffers come first, sez soundcard.c */
+	if (pgoff >= chan->page_number) {
+		pgoff -= chan->page_number;
+		chan = &card->ch_in;
+	} else if (!wr)
+		chan = &card->ch_in;
+
+	assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0);
+
+	dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr);
+	DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n",
+		 dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr);
+	get_page (dmapage);
+	return dmapage;
+}
+
+
+#ifndef VM_RESERVED
+static int via_mm_swapout (struct page *page, struct file *filp)
+{
+	return 0;
+}
+#endif /* VM_RESERVED */
+
+
+struct vm_operations_struct via_mm_ops = {
+	nopage:		via_mm_nopage,
+
+#ifndef VM_RESERVED
+	swapout:	via_mm_swapout,
+#endif
+};
+
+
+static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct via_info *card;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+	int rc = -EINVAL, rd=0, wr=0;
+	unsigned long max_size, size, start, offset;
+
+	assert (file != NULL);
+	assert (vma != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n",
+		 vma->vm_start,
+		 vma->vm_end - vma->vm_start,
+		 vma->vm_pgoff);
+
+	max_size = 0;
+	if (vma->vm_flags & VM_READ) {
+		rd = 1;
+		via_chan_set_buffering(card, &card->ch_in, -1);
+		via_chan_buffer_init (card, &card->ch_in);
+		max_size += card->ch_in.page_number << PAGE_SHIFT;
+	}
+	if (vma->vm_flags & VM_WRITE) {
+		wr = 1;
+		via_chan_set_buffering(card, &card->ch_out, -1);
+		via_chan_buffer_init (card, &card->ch_out);
+		max_size += card->ch_out.page_number << PAGE_SHIFT;
+	}
+
+	start = vma->vm_start;
+	offset = (vma->vm_pgoff << PAGE_SHIFT);
+	size = vma->vm_end - vma->vm_start;
+
+	/* some basic size/offset sanity checks */
+	if (size > max_size)
+		goto out;
+	if (offset > max_size - size)
+		goto out;
+
+	rc = via_syscall_down (card, nonblock);
+	if (rc) goto out;
+
+	vma->vm_ops = &via_mm_ops;
+	vma->vm_private_data = card;
+
+#ifdef VM_RESERVED
+	vma->vm_flags |= VM_RESERVED;
+#endif
+
+	if (rd)
+		card->ch_in.is_mapped = 1;
+	if (wr)
+		card->ch_out.is_mapped = 1;
+
+	up (&card->syscall_sem);
+	rc = 0;
+
+out:
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static ssize_t via_dsp_do_read (struct via_info *card,
+				char *userbuf, size_t count,
+				int nonblock)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	const char *orig_userbuf = userbuf;
+	struct via_channel *chan = &card->ch_in;
+	size_t size;
+	int n, tmp;
+	ssize_t ret = 0;
+
+	/* if SGD has not yet been started, start it */
+	via_chan_maybe_start (chan);
+
+handle_one_block:
+	/* just to be a nice neighbor */
+	/* Thomas Sailer:
+	 * But also to ourselves, release semaphore if we do so */
+	if (current->need_resched) {
+		up(&card->syscall_sem);
+		schedule ();
+		ret = via_syscall_down (card, nonblock);
+		if (ret)
+			goto out;
+	}
+
+	/* grab current channel software pointer.  In the case of
+	 * recording, this is pointing to the next buffer that
+	 * will receive data from the audio hardware.
+	 */
+	n = chan->sw_ptr;
+
+	/* n_frags represents the number of fragments waiting
+	 * to be copied to userland.  sleep until at least
+	 * one buffer has been read from the audio hardware.
+	 */
+	add_wait_queue(&chan->wait, &wait);
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		tmp = atomic_read (&chan->n_frags);
+		assert (tmp >= 0);
+		assert (tmp <= chan->frag_number);
+		if (tmp)
+			break;
+		if (nonblock || !chan->is_active) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		up(&card->syscall_sem);
+
+		DPRINTK ("Sleeping on block %d\n", n);
+		schedule();
+
+		ret = via_syscall_down (card, nonblock);
+		if (ret)
+			break;
+
+		if (signal_pending (current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&chan->wait, &wait);
+	if (ret)
+		goto out;
+
+	/* Now that we have a buffer we can read from, send
+	 * as much as sample data possible to userspace.
+	 */
+	while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+		size_t slop_left = chan->frag_size - chan->slop_len;
+		void *base = chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr;
+		unsigned ofs = n % (PAGE_SIZE / chan->frag_size);
+
+		size = (count < slop_left) ? count : slop_left;
+		if (copy_to_user (userbuf,
+				  base + ofs + chan->slop_len,
+				  size)) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		count -= size;
+		chan->slop_len += size;
+		userbuf += size;
+	}
+
+	/* If we didn't copy the buffer completely to userspace,
+	 * stop now.
+	 */
+	if (chan->slop_len < chan->frag_size)
+		goto out;
+
+	/*
+	 * If we get to this point, we copied one buffer completely
+	 * to userspace, give the buffer back to the hardware.
+	 */
+
+	/* advance channel software pointer to point to
+	 * the next buffer from which we will copy
+	 */
+	if (chan->sw_ptr == (chan->frag_number - 1))
+		chan->sw_ptr = 0;
+	else
+		chan->sw_ptr++;
+
+	/* mark one less buffer waiting to be processed */
+	assert (atomic_read (&chan->n_frags) > 0);
+	atomic_dec (&chan->n_frags);
+
+	/* we are at a block boundary, there is no fragment data */
+	chan->slop_len = 0;
+
+	DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+		n, chan->sw_ptr, atomic_read (&chan->n_frags));
+
+	DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+		 inb (card->baseaddr + 0x00),
+		 inb (card->baseaddr + 0x01),
+		 inb (card->baseaddr + 0x02),
+		 inl (card->baseaddr + 0x04),
+		 inl (card->baseaddr + 0x0C),
+		 inl (card->baseaddr + 0x80),
+		 inl (card->baseaddr + 0x84));
+
+	if (count > 0)
+		goto handle_one_block;
+
+out:
+	return (userbuf != orig_userbuf) ? (userbuf - orig_userbuf) : ret;
+}
+
+
+static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct via_info *card;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+	int rc;
+
+	DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n",
+		 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
+
+	assert (file != NULL);
+	assert (buffer != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if (ppos != &file->f_pos) {
+		DPRINTK ("EXIT, returning -ESPIPE\n");
+		return -ESPIPE;
+	}
+
+	rc = via_syscall_down (card, nonblock);
+	if (rc) goto out;
+
+	if (card->ch_in.is_mapped) {
+		rc = -ENXIO;
+		goto out_up;
+	}
+
+	via_chan_set_buffering(card, &card->ch_in, -1);
+        rc = via_chan_buffer_init (card, &card->ch_in);
+
+	if (rc)
+		goto out_up;
+
+	rc = via_dsp_do_read (card, buffer, count, nonblock);
+
+out_up:
+	up (&card->syscall_sem);
+out:
+	DPRINTK ("EXIT, returning %ld\n",(long) rc);
+	return rc;
+}
+
+
+static ssize_t via_dsp_do_write (struct via_info *card,
+				 const char *userbuf, size_t count,
+				 int nonblock)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	const char *orig_userbuf = userbuf;
+	struct via_channel *chan = &card->ch_out;
+	volatile struct via_sgd_table *sgtable = chan->sgtable;
+	size_t size;
+	int n, tmp;
+	ssize_t ret = 0;
+
+handle_one_block:
+	/* just to be a nice neighbor */
+	/* Thomas Sailer:
+	 * But also to ourselves, release semaphore if we do so */
+	if (current->need_resched) {
+		up(&card->syscall_sem);
+		schedule ();
+		ret = via_syscall_down (card, nonblock);
+		if (ret)
+			goto out;
+	}
+
+	/* grab current channel fragment pointer.  In the case of
+	 * playback, this is pointing to the next fragment that
+	 * should receive data from userland.
+	 */
+	n = chan->sw_ptr;
+
+	/* n_frags represents the number of fragments remaining
+	 * to be filled by userspace.  Sleep until
+	 * at least one fragment is available for our use.
+	 */
+	add_wait_queue(&chan->wait, &wait);
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		tmp = atomic_read (&chan->n_frags);
+		assert (tmp >= 0);
+		assert (tmp <= chan->frag_number);
+		if (tmp)
+			break;
+		if (nonblock || !chan->is_active) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		up(&card->syscall_sem);
+
+		DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record);
+		schedule();
+
+		ret = via_syscall_down (card, nonblock);
+		if (ret)
+			break;
+
+		if (signal_pending (current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&chan->wait, &wait);
+	if (ret)
+		goto out;
+
+	/* Now that we have at least one fragment we can write to, fill the buffer
+	 * as much as possible with data from userspace.
+	 */
+	while ((count > 0) && (chan->slop_len < chan->frag_size)) {
+		size_t slop_left = chan->frag_size - chan->slop_len;
+
+		size = (count < slop_left) ? count : slop_left;
+		if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len,
+				    userbuf, size)) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		count -= size;
+		chan->slop_len += size;
+		userbuf += size;
+	}
+
+	/* If we didn't fill up the buffer with data, stop now.
+         * Put a 'stop' marker in the DMA table too, to tell the
+         * audio hardware to stop if it gets here.
+         */
+	if (chan->slop_len < chan->frag_size) {
+		sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP);
+		goto out;
+	}
+
+	/*
+         * If we get to this point, we have filled a buffer with
+         * audio data, flush the buffer to audio hardware.
+         */
+
+	/* Record the true size for the audio hardware to notice */
+        if (n == (chan->frag_number - 1))
+                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL);
+        else
+                sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);
+
+	/* advance channel software pointer to point to
+	 * the next buffer we will fill with data
+	 */
+	if (chan->sw_ptr == (chan->frag_number - 1))
+		chan->sw_ptr = 0;
+	else
+		chan->sw_ptr++;
+
+	/* mark one less buffer as being available for userspace consumption */
+	assert (atomic_read (&chan->n_frags) > 0);
+	atomic_dec (&chan->n_frags);
+
+	/* we are at a block boundary, there is no fragment data */
+	chan->slop_len = 0;
+
+	/* if SGD has not yet been started, start it */
+	via_chan_maybe_start (chan);
+
+	DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
+		n, chan->sw_ptr, atomic_read (&chan->n_frags));
+
+	DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+		 inb (card->baseaddr + 0x00),
+		 inb (card->baseaddr + 0x01),
+		 inb (card->baseaddr + 0x02),
+		 inl (card->baseaddr + 0x04),
+		 inl (card->baseaddr + 0x0C),
+		 inl (card->baseaddr + 0x80),
+		 inl (card->baseaddr + 0x84));
+
+	if (count > 0)
+		goto handle_one_block;
+
+out:
+	return userbuf - orig_userbuf;
+}
+
+
+static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct via_info *card;
+	ssize_t rc;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+
+	DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n",
+		 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
+
+	assert (file != NULL);
+	assert (buffer != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if (ppos != &file->f_pos) {
+		DPRINTK ("EXIT, returning -ESPIPE\n");
+		return -ESPIPE;
+	}
+
+	rc = via_syscall_down (card, nonblock);
+	if (rc) goto out;
+
+	if (card->ch_out.is_mapped) {
+		rc = -ENXIO;
+		goto out_up;
+	}
+
+	via_chan_set_buffering(card, &card->ch_out, -1);
+	rc = via_chan_buffer_init (card, &card->ch_out);
+
+	if (rc)
+		goto out_up;
+
+	rc = via_dsp_do_write (card, buffer, count, nonblock);
+
+out_up:
+	up (&card->syscall_sem);
+out:
+	DPRINTK ("EXIT, returning %ld\n",(long) rc);
+	return rc;
+}
+
+
+static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct via_info *card;
+	struct via_channel *chan;
+	unsigned int mask = 0;
+
+	DPRINTK ("ENTER\n");
+
+	assert (file != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if (file->f_mode & FMODE_READ) {
+		chan = &card->ch_in;
+		if (sg_active (chan->iobase))
+	                poll_wait(file, &chan->wait, wait);
+		if (atomic_read (&chan->n_frags) > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+
+	if (file->f_mode & FMODE_WRITE) {
+		chan = &card->ch_out;
+		if (sg_active (chan->iobase))
+	                poll_wait(file, &chan->wait, wait);
+		if (atomic_read (&chan->n_frags) > 0)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+
+	DPRINTK ("EXIT, returning %u\n", mask);
+	return mask;
+}
+
+
+/**
+ *	via_dsp_drain_playback - sleep until all playback samples are flushed
+ *	@card: Private info for specified board
+ *	@chan: Channel to drain
+ *	@nonblock: boolean, non-zero if O_NONBLOCK is set
+ *
+ *	Sleeps until all playback has been flushed to the audio
+ *	hardware.
+ *
+ *	Locking: inside card->syscall_sem
+ */
+
+static int via_dsp_drain_playback (struct via_info *card,
+				   struct via_channel *chan, int nonblock)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
+
+	DPRINTK ("ENTER, nonblock = %d\n", nonblock);
+
+	if (chan->slop_len > 0)
+		via_chan_flush_frag (chan);
+
+	if (atomic_read (&chan->n_frags) == chan->frag_number)
+		goto out;
+
+	via_chan_maybe_start (chan);
+
+	add_wait_queue(&chan->wait, &wait);
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if (atomic_read (&chan->n_frags) >= chan->frag_number)
+			break;
+
+		if (nonblock) {
+			DPRINTK ("EXIT, returning -EAGAIN\n");
+			ret = -EAGAIN;
+			break;
+		}
+
+#ifdef VIA_DEBUG
+		{
+		u8 r40,r41,r42,r43,r44,r48;
+		pci_read_config_byte (card->pdev, 0x40, &r40);
+		pci_read_config_byte (card->pdev, 0x41, &r41);
+		pci_read_config_byte (card->pdev, 0x42, &r42);
+		pci_read_config_byte (card->pdev, 0x43, &r43);
+		pci_read_config_byte (card->pdev, 0x44, &r44);
+		pci_read_config_byte (card->pdev, 0x48, &r48);
+		DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
+			r40,r41,r42,r43,r44,r48);
+
+		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+			 inb (card->baseaddr + 0x00),
+			 inb (card->baseaddr + 0x01),
+			 inb (card->baseaddr + 0x02),
+			 inl (card->baseaddr + 0x04),
+			 inl (card->baseaddr + 0x0C),
+			 inl (card->baseaddr + 0x80),
+			 inl (card->baseaddr + 0x84));
+		}
+
+		if (!chan->is_active)
+			printk (KERN_ERR "sleeping but not active\n");
+#endif
+
+		up(&card->syscall_sem);
+
+		DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags));
+		schedule();
+
+		if ((ret = via_syscall_down (card, nonblock)))
+			break;
+
+		if (signal_pending (current)) {
+			DPRINTK ("EXIT, returning -ERESTARTSYS\n");
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&chan->wait, &wait);
+
+#ifdef VIA_DEBUG
+	{
+		u8 r40,r41,r42,r43,r44,r48;
+		pci_read_config_byte (card->pdev, 0x40, &r40);
+		pci_read_config_byte (card->pdev, 0x41, &r41);
+		pci_read_config_byte (card->pdev, 0x42, &r42);
+		pci_read_config_byte (card->pdev, 0x43, &r43);
+		pci_read_config_byte (card->pdev, 0x44, &r44);
+		pci_read_config_byte (card->pdev, 0x48, &r48);
+		DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n",
+			r40,r41,r42,r43,r44,r48);
+
+		DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+			 inb (card->baseaddr + 0x00),
+			 inb (card->baseaddr + 0x01),
+			 inb (card->baseaddr + 0x02),
+			 inl (card->baseaddr + 0x04),
+			 inl (card->baseaddr + 0x0C),
+			 inl (card->baseaddr + 0x80),
+			 inl (card->baseaddr + 0x84));
+
+		DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags));
+	}
+#endif
+
+out:
+	DPRINTK ("EXIT, returning %d\n", ret);
+	return ret;
+}
+
+
+/**
+ *	via_dsp_ioctl_space - get information about channel buffering
+ *	@card: Private info for specified board
+ *	@chan: pointer to channel-specific info
+ *	@arg: user buffer for returned information
+ *
+ *	Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE.
+ *
+ *	Locking: inside card->syscall_sem
+ */
+
+static int via_dsp_ioctl_space (struct via_info *card,
+				struct via_channel *chan,
+				void *arg)
+{
+	audio_buf_info info;
+
+	via_chan_set_buffering(card, chan, -1);
+
+	info.fragstotal = chan->frag_number;
+	info.fragsize = chan->frag_size;
+
+	/* number of full fragments we can read/write without blocking */
+	info.fragments = atomic_read (&chan->n_frags);
+
+	if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0))
+		info.fragments--;
+
+	/* number of bytes that can be read or written immediately
+	 * without blocking.
+	 */
+	info.bytes = (info.fragments * chan->frag_size);
+	if (chan->slop_len % chan->frag_size > 0)
+		info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size);
+
+	DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n",
+		info.fragstotal,
+		info.fragsize,
+		info.fragments,
+		info.bytes);
+
+	return copy_to_user (arg, &info, sizeof (info));
+}
+
+
+/**
+ *	via_dsp_ioctl_ptr - get information about hardware buffer ptr
+ *	@card: Private info for specified board
+ *	@chan: pointer to channel-specific info
+ *	@arg: user buffer for returned information
+ *
+ *	Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR.
+ *
+ *	Locking: inside card->syscall_sem
+ */
+
+static int via_dsp_ioctl_ptr (struct via_info *card,
+				struct via_channel *chan,
+				void *arg)
+{
+	count_info info;
+
+	spin_lock_irq (&card->lock);
+
+	info.bytes = chan->bytes;
+	info.blocks = chan->n_irqs;
+	chan->n_irqs = 0;
+
+	spin_unlock_irq (&card->lock);
+
+	if (chan->is_active) {
+		unsigned long extra;
+		info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size;
+		extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+		info.ptr += extra;
+		info.bytes += extra;
+	} else {
+		info.ptr = 0;
+	}
+
+	DPRINTK ("EXIT, returning bytes=%d, blocks=%d, ptr=%d\n",
+		info.bytes,
+		info.blocks,
+		info.ptr);
+
+	return copy_to_user (arg, &info, sizeof (info));
+}
+
+
+static int via_dsp_ioctl_trigger (struct via_channel *chan, int val)
+{
+	int enable, do_something;
+
+	if (chan->is_record)
+		enable = (val & PCM_ENABLE_INPUT);
+	else
+		enable = (val & PCM_ENABLE_OUTPUT);
+
+	if (!chan->is_enabled && enable) {
+		do_something = 1;
+	} else if (chan->is_enabled && !enable) {
+		do_something = -1;
+	} else {
+		do_something = 0;
+	}
+
+	DPRINTK ("enable=%d, do_something=%d\n",
+		 enable, do_something);
+
+	if (chan->is_active && do_something)
+		return -EINVAL;
+
+	if (do_something == 1) {
+		chan->is_enabled = 1;
+		via_chan_maybe_start (chan);
+		DPRINTK ("Triggering input\n");
+	}
+
+	else if (do_something == -1) {
+		chan->is_enabled = 0;
+		DPRINTK ("Setup input trigger\n");
+	}
+
+	return 0;
+}
+
+
+static int via_dsp_ioctl (struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	int rc, rd=0, wr=0, val=0;
+	struct via_info *card;
+	struct via_channel *chan;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+
+	assert (file != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	if (file->f_mode & FMODE_WRITE)
+		wr = 1;
+	if (file->f_mode & FMODE_READ)
+		rd = 1;
+
+	rc = via_syscall_down (card, nonblock);
+	if (rc)
+		return rc;
+	rc = -EINVAL;
+
+	switch (cmd) {
+
+	/* OSS API version.  XXX unverified */
+	case OSS_GETVERSION:
+		DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n");
+		rc = put_user (SOUND_VERSION, (int *)arg);
+		break;
+
+	/* list of supported PCM data formats */
+	case SNDCTL_DSP_GETFMTS:
+		DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n");
+                rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg);
+		break;
+
+	/* query or set current channel's PCM data format */
+	case SNDCTL_DSP_SETFMT:
+		if (get_user(val, (int *)arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		DPRINTK ("DSP_SETFMT, val==%d\n", val);
+		if (val != AFMT_QUERY) {
+			rc = 0;
+
+			if (rd)
+				rc = via_chan_set_fmt (card, &card->ch_in, val);
+
+			if (rc >= 0 && wr)
+				rc = via_chan_set_fmt (card, &card->ch_out, val);
+
+			if (rc < 0)
+				break;
+
+			val = rc;
+		} else {
+			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) ||
+			    (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT)))
+				val = AFMT_S16_LE;
+			else
+				val = AFMT_U8;
+		}
+		DPRINTK ("SETFMT EXIT, returning %d\n", val);
+                rc = put_user (val, (int *)arg);
+		break;
+
+	/* query or set number of channels (1=mono, 2=stereo) */
+        case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		DPRINTK ("DSP_CHANNELS, val==%d\n", val);
+		if (val != 0) {
+			rc = 0;
+
+			if (rd)
+				rc = via_chan_set_stereo (card, &card->ch_in, val);
+
+			if (rc >= 0 && wr)
+				rc = via_chan_set_stereo (card, &card->ch_out, val);
+
+			if (rc < 0)
+				break;
+
+			val = rc;
+		} else {
+			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
+			    (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO)))
+				val = 2;
+			else
+				val = 1;
+		}
+		DPRINTK ("CHANNELS EXIT, returning %d\n", val);
+                rc = put_user (val, (int *)arg);
+		break;
+
+	/* enable (val is not zero) or disable (val == 0) stereo */
+        case SNDCTL_DSP_STEREO:
+		if (get_user(val, (int *)arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		DPRINTK ("DSP_STEREO, val==%d\n", val);
+		rc = 0;
+
+		if (rd)
+			rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1);
+		if (rc >= 0 && wr)
+			rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1);
+
+		if (rc < 0)
+			break;
+
+		val = rc - 1;
+
+		DPRINTK ("STEREO EXIT, returning %d\n", val);
+		rc = put_user(val, (int *) arg);
+		break;
+
+	/* query or set sampling rate */
+        case SNDCTL_DSP_SPEED:
+		if (get_user(val, (int *)arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		DPRINTK ("DSP_SPEED, val==%d\n", val);
+		if (val < 0) {
+			rc = -EINVAL;
+			break;
+		}
+		if (val > 0) {
+			rc = 0;
+
+			if (rd)
+				rc = via_chan_set_speed (card, &card->ch_in, val);
+			if (rc >= 0 && wr)
+				rc = via_chan_set_speed (card, &card->ch_out, val);
+
+			if (rc < 0)
+				break;
+
+			val = rc;
+		} else {
+			if (rd)
+				val = card->ch_in.rate;
+			else if (wr)
+				val = card->ch_out.rate;
+			else
+				val = 0;
+		}
+		DPRINTK ("SPEED EXIT, returning %d\n", val);
+                rc = put_user (val, (int *)arg);
+		break;
+
+	/* wait until all buffers have been played, and then stop device */
+	case SNDCTL_DSP_SYNC:
+		DPRINTK ("DSP_SYNC\n");
+		rc = 0;
+		if (wr) {
+			DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n");
+			rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
+		}
+		break;
+
+	/* stop recording/playback immediately */
+        case SNDCTL_DSP_RESET:
+		DPRINTK ("DSP_RESET\n");
+		if (rd) {
+			via_chan_clear (card, &card->ch_in);
+			card->ch_in.frag_number = 0;
+			card->ch_in.frag_size = 0;
+			atomic_set(&card->ch_in.n_frags, 0);
+		}
+
+		if (wr) {
+			via_chan_clear (card, &card->ch_out);
+			card->ch_out.frag_number = 0;
+			card->ch_out.frag_size = 0;
+			atomic_set(&card->ch_out.n_frags, 0);
+		}
+
+		rc = 0;
+		break;
+
+	case SNDCTL_DSP_NONBLOCK:
+		file->f_flags |= O_NONBLOCK;
+		rc = 0;
+		break;
+
+	/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
+	case SNDCTL_DSP_GETCAPS:
+		DPRINTK ("DSP_GETCAPS\n");
+		rc = put_user(VIA_DSP_CAP, (int *)arg);
+		break;
+
+	/* obtain buffer fragment size */
+	case SNDCTL_DSP_GETBLKSIZE:
+		DPRINTK ("DSP_GETBLKSIZE\n");
+
+		if (rd) {
+			via_chan_set_buffering(card, &card->ch_in, -1);
+			rc = put_user(card->ch_in.frag_size, (int *)arg);
+		} else if (wr) {
+			via_chan_set_buffering(card, &card->ch_out, -1);
+			rc = put_user(card->ch_out.frag_size, (int *)arg);
+		}
+		break;
+
+	/* obtain information about input buffering */
+	case SNDCTL_DSP_GETISPACE:
+		DPRINTK ("DSP_GETISPACE\n");
+		if (rd)
+			rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg);
+		break;
+
+	/* obtain information about output buffering */
+	case SNDCTL_DSP_GETOSPACE:
+		DPRINTK ("DSP_GETOSPACE\n");
+		if (wr)
+			rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg);
+		break;
+
+	/* obtain information about input hardware pointer */
+	case SNDCTL_DSP_GETIPTR:
+		DPRINTK ("DSP_GETIPTR\n");
+		if (rd)
+			rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg);
+		break;
+
+	/* obtain information about output hardware pointer */
+	case SNDCTL_DSP_GETOPTR:
+		DPRINTK ("DSP_GETOPTR\n");
+		if (wr)
+			rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg);
+		break;
+
+	/* return number of bytes remaining to be played by DMA engine */
+	case SNDCTL_DSP_GETODELAY:
+		{
+		DPRINTK ("DSP_GETODELAY\n");
+
+		chan = &card->ch_out;
+
+		if (!wr)
+			break;
+
+		if (chan->is_active) {
+
+			val = chan->frag_number - atomic_read (&chan->n_frags);
+
+			if (val > 0) {
+				val *= chan->frag_size;
+				val -= chan->frag_size -
+				       inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+			}
+			val += chan->slop_len % chan->frag_size;
+		} else
+			val = 0;
+
+		assert (val <= (chan->frag_size * chan->frag_number));
+
+		DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val);
+                rc = put_user (val, (int *)arg);
+		break;
+		}
+
+	/* handle the quick-start of a channel,
+	 * or the notification that a quick-start will
+	 * occur in the future
+	 */
+	case SNDCTL_DSP_SETTRIGGER:
+		if (get_user(val, (int *)arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n",
+			rd, wr, card->ch_in.is_active, card->ch_out.is_active,
+			card->ch_in.is_enabled, card->ch_out.is_enabled);
+
+		rc = 0;
+
+		if (rd)
+			rc = via_dsp_ioctl_trigger (&card->ch_in, val);
+
+		if (!rc && wr)
+			rc = via_dsp_ioctl_trigger (&card->ch_out, val);
+
+		break;
+
+	case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if ((file->f_mode & FMODE_READ) && card->ch_in.is_enabled)
+			val |= PCM_ENABLE_INPUT;
+		if ((file->f_mode & FMODE_WRITE) && card->ch_out.is_enabled)
+			val |= PCM_ENABLE_OUTPUT;
+		rc = put_user(val, (int *)arg);
+		break;
+
+	/* Enable full duplex.  Since we do this as soon as we are opened
+	 * with O_RDWR, this is mainly a no-op that always returns success.
+	 */
+	case SNDCTL_DSP_SETDUPLEX:
+		DPRINTK ("DSP_SETDUPLEX\n");
+		if (!rd || !wr)
+			break;
+		rc = 0;
+		break;
+
+	/* set fragment size.  implemented as a successful no-op for now */
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val);
+
+		if (rd)
+			rc = via_chan_set_buffering(card, &card->ch_in, val);
+
+		if (wr)
+			rc = via_chan_set_buffering(card, &card->ch_out, val);
+
+		DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
+			 val & 0xFFFF,
+			 val & 0xFFFF,
+			 (val >> 16) & 0xFFFF,
+			 (val >> 16) & 0xFFFF);
+
+		rc = 0;
+		break;
+
+	/* inform device of an upcoming pause in input (or output). */
+	case SNDCTL_DSP_POST:
+		DPRINTK ("DSP_POST\n");
+		if (wr) {
+			if (card->ch_out.slop_len > 0)
+				via_chan_flush_frag (&card->ch_out);
+			via_chan_maybe_start (&card->ch_out);
+		}
+
+		rc = 0;
+		break;
+
+	/* not implemented */
+	default:
+		DPRINTK ("unhandled ioctl, cmd==%u, arg==%p\n",
+			 cmd, (void*) arg);
+		break;
+	}
+
+	up (&card->syscall_sem);
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static int via_dsp_open (struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct via_info *card;
+	struct pci_dev *pdev;
+	struct via_channel *chan;
+	struct pci_driver *drvr;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+
+	DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode);
+
+	if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) {
+		DPRINTK ("EXIT, returning -EINVAL\n");
+		return -EINVAL;
+	}
+
+	card = NULL;
+	pci_for_each_dev(pdev) {
+		drvr = pci_dev_driver (pdev);
+		if (drvr == &via_driver) {
+			assert (pci_get_drvdata (pdev) != NULL);
+
+			card = pci_get_drvdata (pdev);
+			DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n",
+				 card->dev_dsp, minor,
+				 (card->dev_dsp ^ minor) & ~0xf);
+
+			if (((card->dev_dsp ^ minor) & ~0xf) == 0)
+				goto match;
+		}
+	}
+
+	DPRINTK ("no matching %s found\n", card ? "minor" : "driver");
+	return -ENODEV;
+
+match:
+	if (nonblock) {
+		if (down_trylock (&card->open_sem)) {
+			DPRINTK ("EXIT, returning -EAGAIN\n");
+			return -EAGAIN;
+		}
+	} else {
+		if (down_interruptible (&card->open_sem)) {
+			DPRINTK ("EXIT, returning -ERESTARTSYS\n");
+			return -ERESTARTSYS;
+		}
+	}
+
+	file->private_data = card;
+	DPRINTK ("file->f_mode == 0x%x\n", file->f_mode);
+
+	/* handle input from analog source */
+	if (file->f_mode & FMODE_READ) {
+		chan = &card->ch_in;
+
+		via_chan_init (card, chan);
+
+		/* why is this forced to 16-bit stereo in all drivers? */
+		chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+
+		via_chan_pcm_fmt (chan, 0);
+		via_set_rate (&card->ac97, chan, 44100);
+	}
+
+	/* handle output to analog source */
+	if (file->f_mode & FMODE_WRITE) {
+		chan = &card->ch_out;
+
+		via_chan_init (card, chan);
+
+		if (file->f_mode & FMODE_READ) {
+			/* if in duplex mode make the recording and playback channels
+			   have the same settings */
+			chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+			via_chan_pcm_fmt (chan, 0);
+                        via_set_rate (&card->ac97, chan, 44100);
+		} else {
+			 if ((minor & 0xf) == SND_DEV_DSP16) {
+				chan->pcm_fmt = VIA_PCM_FMT_16BIT;
+				via_chan_pcm_fmt (chan, 0);
+				via_set_rate (&card->ac97, chan, 44100);
+			} else {
+				via_chan_pcm_fmt (chan, 1);
+				via_set_rate (&card->ac97, chan, 8000);
+			}
+		}
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static int via_dsp_release(struct inode *inode, struct file *file)
+{
+	struct via_info *card;
+	int nonblock = (file->f_flags & O_NONBLOCK);
+	int rc;
+
+	DPRINTK ("ENTER\n");
+
+	assert (file != NULL);
+	card = file->private_data;
+	assert (card != NULL);
+
+	rc = via_syscall_down (card, nonblock);
+	if (rc) {
+		DPRINTK ("EXIT (syscall_down error), rc=%d\n", rc);
+		return rc;
+	}
+
+	if (file->f_mode & FMODE_WRITE) {
+		rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
+		if (rc)
+			printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc);
+
+		via_chan_free (card, &card->ch_out);
+		via_chan_buffer_free(card, &card->ch_out);
+	}
+
+	if (file->f_mode & FMODE_READ) {
+		via_chan_free (card, &card->ch_in);
+		via_chan_buffer_free (card, &card->ch_in);
+	}
+
+	up (&card->syscall_sem);
+	up (&card->open_sem);
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+/****************************************************************
+ *
+ * Chip setup and kernel registration
+ *
+ *
+ */
+
+static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+#ifdef CONFIG_MIDI_VIA82CXXX
+	u8 r42;
+#endif
+	int rc;
+	struct via_info *card;
+	static int printed_version = 0;
+
+	DPRINTK ("ENTER\n");
+
+	if (printed_version++ == 0)
+		printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n");
+
+	rc = pci_enable_device (pdev);
+	if (rc)
+		goto err_out;
+
+	rc = pci_request_regions (pdev, "via82cxxx_audio");
+	if (rc)
+		goto err_out_disable;
+
+	card = kmalloc (sizeof (*card), GFP_KERNEL);
+	if (!card) {
+		printk (KERN_ERR PFX "out of memory, aborting\n");
+		rc = -ENOMEM;
+		goto err_out_res;
+	}
+
+	pci_set_drvdata (pdev, card);
+
+	memset (card, 0, sizeof (*card));
+	card->pdev = pdev;
+	card->baseaddr = pci_resource_start (pdev, 0);
+	card->card_num = via_num_cards++;
+	spin_lock_init (&card->lock);
+	init_MUTEX (&card->syscall_sem);
+	init_MUTEX (&card->open_sem);
+
+	/* we must init these now, in case the intr handler needs them */
+	via_chan_init_defaults (card, &card->ch_out);
+	via_chan_init_defaults (card, &card->ch_in);
+	via_chan_init_defaults (card, &card->ch_fm);
+
+	/* if BAR 2 is present, chip is Rev H or later,
+	 * which means it has a few extra features */
+	if (pci_resource_start (pdev, 2) > 0)
+		card->rev_h = 1;
+
+	if (pdev->irq < 1) {
+		printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq);
+		rc = -ENODEV;
+		goto err_out_kfree;
+	}
+
+	if (!(pci_resource_flags (pdev, 0) & IORESOURCE_IO)) {
+		printk (KERN_ERR PFX "unable to locate I/O resources, aborting\n");
+		rc = -ENODEV;
+		goto err_out_kfree;
+	}
+
+	/*
+	 * init AC97 mixer and codec
+	 */
+	rc = via_ac97_init (card);
+	if (rc) {
+		printk (KERN_ERR PFX "AC97 init failed, aborting\n");
+		goto err_out_kfree;
+	}
+
+	/*
+	 * init DSP device
+	 */
+	rc = via_dsp_init (card);
+	if (rc) {
+		printk (KERN_ERR PFX "DSP device init failed, aborting\n");
+		goto err_out_have_mixer;
+	}
+
+	/*
+	 * per-card /proc info
+	 */
+	rc = via_card_init_proc (card);
+	if (rc) {
+		printk (KERN_ERR PFX "card-specific /proc init failed, aborting\n");
+		goto err_out_have_dsp;
+	}
+
+	/*
+	 * init and turn on interrupts, as the last thing we do
+	 */
+	rc = via_interrupt_init (card);
+	if (rc) {
+		printk (KERN_ERR PFX "interrupt init failed, aborting\n");
+		goto err_out_have_proc;
+	}
+
+	printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n",
+		card->card_num + 1, card->baseaddr, pdev->irq);
+
+#ifdef CONFIG_MIDI_VIA82CXXX
+	/* Disable by default */
+	card->midi_info.io_base = 0;
+
+	pci_read_config_byte (pdev, 0x42, &r42);
+	/* Disable MIDI interrupt */
+	pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK);
+	if (r42 & VIA_CR42_MIDI_ENABLE)
+	{
+		if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */
+			card->midi_info.io_base = pci_resource_start (pdev, 2);
+		else /* Address selected by byte 0x43 */
+		{
+			u8 r43;
+			pci_read_config_byte (pdev, 0x43, &r43);
+			card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2);
+		}
+
+		card->midi_info.irq = -pdev->irq;
+		if (probe_uart401(& card->midi_info, THIS_MODULE))
+		{
+			card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc;
+			pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK);
+			printk("Enabled Via MIDI\n");
+		}
+	}
+#endif
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out_have_proc:
+	via_card_cleanup_proc (card);
+
+err_out_have_dsp:
+	via_dsp_cleanup (card);
+
+err_out_have_mixer:
+	via_ac97_cleanup (card);
+
+err_out_kfree:
+#ifndef VIA_NDEBUG
+	memset (card, 0xAB, sizeof (*card)); /* poison memory */
+#endif
+	kfree (card);
+
+err_out_res:
+	pci_release_regions (pdev);
+
+err_out_disable:
+	pci_disable_device (pdev);
+
+err_out:
+	pci_set_drvdata (pdev, NULL);
+	DPRINTK ("EXIT - returning %d\n", rc);
+	return rc;
+}
+
+
+static void __devexit via_remove_one (struct pci_dev *pdev)
+{
+	struct via_info *card;
+
+	DPRINTK ("ENTER\n");
+
+	assert (pdev != NULL);
+	card = pci_get_drvdata (pdev);
+	assert (card != NULL);
+
+#ifdef CONFIG_MIDI_VIA82CXXX
+	if (card->midi_info.io_base)
+		unload_uart401(&card->midi_info);
+#endif
+
+	free_irq (card->pdev->irq, card);
+	via_card_cleanup_proc (card);
+	via_dsp_cleanup (card);
+	via_ac97_cleanup (card);
+
+#ifndef VIA_NDEBUG
+	memset (card, 0xAB, sizeof (*card)); /* poison memory */
+#endif
+	kfree (card);
+
+	pci_set_drvdata (pdev, NULL);
+
+	pci_release_regions (pdev);
+	pci_disable_device (pdev);
+	pci_set_power_state (pdev, 3); /* ...zzzzzz */
+
+	DPRINTK ("EXIT\n");
+	return;
+}
+
+
+/****************************************************************
+ *
+ * Driver initialization and cleanup
+ *
+ *
+ */
+
+static int __init init_via82cxxx_audio(void)
+{
+	int rc;
+
+	DPRINTK ("ENTER\n");
+
+	rc = via_init_proc ();
+	if (rc) {
+		DPRINTK ("EXIT, returning %d\n", rc);
+		return rc;
+	}
+
+	rc = pci_register_driver (&via_driver);
+	if (rc < 1) {
+		if (rc == 0)
+			pci_unregister_driver (&via_driver);
+		via_cleanup_proc ();
+		DPRINTK ("EXIT, returning -ENODEV\n");
+		return -ENODEV;
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static void __exit cleanup_via82cxxx_audio(void)
+{
+	DPRINTK ("ENTER\n");
+
+	pci_unregister_driver (&via_driver);
+	via_cleanup_proc ();
+
+	DPRINTK ("EXIT\n");
+}
+
+
+module_init(init_via82cxxx_audio);
+module_exit(cleanup_via82cxxx_audio);
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("DSP audio and mixer driver for Via 82Cxxx audio devices");
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
+
+
+
+#ifdef VIA_PROC_FS
+
+/****************************************************************
+ *
+ * /proc/driver/via/info
+ *
+ *
+ */
+
+static int via_info_read_proc (char *page, char **start, off_t off,
+			       int count, int *eof, void *data)
+{
+#define YN(val,bit) (((val) & (bit)) ? "yes" : "no")
+#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable")
+
+	int len = 0;
+	u8 r40, r41, r42, r44;
+	struct via_info *card = data;
+
+	DPRINTK ("ENTER\n");
+
+	assert (card != NULL);
+
+	len += sprintf (page+len, VIA_CARD_NAME "\n\n");
+
+	pci_read_config_byte (card->pdev, 0x40, &r40);
+	pci_read_config_byte (card->pdev, 0x41, &r41);
+	pci_read_config_byte (card->pdev, 0x42, &r42);
+	pci_read_config_byte (card->pdev, 0x44, &r44);
+
+	len += sprintf (page+len,
+		"Via 82Cxxx PCI registers:\n"
+		"\n"
+		"40  Codec Ready: %s\n"
+		"    Codec Low-power: %s\n"
+		"    Secondary Codec Ready: %s\n"
+		"\n"
+		"41  Interface Enable: %s\n"
+		"    De-Assert Reset: %s\n"
+		"    Force SYNC high: %s\n"
+		"    Force SDO high: %s\n"
+		"    Variable Sample Rate On-Demand Mode: %s\n"
+		"    SGD Read Channel PCM Data Out: %s\n"
+		"    FM Channel PCM Data Out: %s\n"
+		"    SB PCM Data Out: %s\n"
+		"\n"
+		"42  Game port enabled: %s\n"
+		"    SoundBlaster enabled: %s\n"
+		"    FM enabled: %s\n"
+		"    MIDI enabled: %s\n"
+		"\n"
+		"44  AC-Link Interface Access: %s\n"
+		"    Secondary Codec Support: %s\n"
+
+		"\n",
+
+		YN (r40, VIA_CR40_AC97_READY),
+		YN (r40, VIA_CR40_AC97_LOW_POWER),
+		YN (r40, VIA_CR40_SECONDARY_READY),
+
+		ED (r41, VIA_CR41_AC97_ENABLE),
+		YN (r41, (1 << 6)),
+		YN (r41, (1 << 5)),
+		YN (r41, (1 << 4)),
+		ED (r41, (1 << 3)),
+		ED (r41, (1 << 2)),
+		ED (r41, (1 << 1)),
+		ED (r41, (1 << 0)),
+
+		YN (r42, VIA_CR42_GAME_ENABLE),
+		YN (r42, VIA_CR42_SB_ENABLE),
+		YN (r42, VIA_CR42_FM_ENABLE),
+		YN (r42, VIA_CR42_MIDI_ENABLE),
+
+		YN (r44, VIA_CR44_AC_LINK_ACCESS),
+		YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT)
+
+		);
+
+	DPRINTK ("EXIT, returning %d\n", len);
+	return len;
+
+#undef YN
+#undef ED
+}
+
+
+/****************************************************************
+ *
+ * /proc/driver/via/... setup and cleanup
+ *
+ *
+ */
+
+static int __init via_init_proc (void)
+{
+	DPRINTK ("ENTER\n");
+
+	if (!proc_mkdir ("driver/via", 0))
+		return -EIO;
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+static void via_cleanup_proc (void)
+{
+	DPRINTK ("ENTER\n");
+
+	remove_proc_entry ("driver/via", NULL);
+
+	DPRINTK ("EXIT\n");
+}
+
+
+static int __init via_card_init_proc (struct via_info *card)
+{
+	char s[32];
+	int rc;
+
+	DPRINTK ("ENTER\n");
+
+	sprintf (s, "driver/via/%d", card->card_num);
+	if (!proc_mkdir (s, 0)) {
+		rc = -EIO;
+		goto err_out_none;
+	}
+
+	sprintf (s, "driver/via/%d/info", card->card_num);
+	if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) {
+		rc = -EIO;
+		goto err_out_dir;
+	}
+
+	sprintf (s, "driver/via/%d/ac97", card->card_num);
+	if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) {
+		rc = -EIO;
+		goto err_out_info;
+	}
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out_info:
+	sprintf (s, "driver/via/%d/info", card->card_num);
+	remove_proc_entry (s, NULL);
+
+err_out_dir:
+	sprintf (s, "driver/via/%d", card->card_num);
+	remove_proc_entry (s, NULL);
+
+err_out_none:
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
+
+
+static void via_card_cleanup_proc (struct via_info *card)
+{
+	char s[32];
+
+	DPRINTK ("ENTER\n");
+
+	sprintf (s, "driver/via/%d/ac97", card->card_num);
+	remove_proc_entry (s, NULL);
+
+	sprintf (s, "driver/via/%d/info", card->card_num);
+	remove_proc_entry (s, NULL);
+
+	sprintf (s, "driver/via/%d", card->card_num);
+	remove_proc_entry (s, NULL);
+
+	DPRINTK ("EXIT\n");
+}
+
+#endif /* VIA_PROC_FS */
diff -Nru linux/sound/oss/vidc.c linux-2.4.19-pre5-mjc/sound/oss/vidc.c
--- linux/sound/oss/vidc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/vidc.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,549 @@
+/*
+ *  linux/drivers/sound/vidc.c
+ *
+ *  Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  VIDC20 audio driver.
+ *
+ * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
+ * engine.  The DMA transfers fixed-format (16-bit little-endian linear)
+ * samples to the VIDC20, which then transfers this data serially to the
+ * DACs.  The samplerate is controlled by the VIDC.
+ *
+ * We currently support a mixer device, but it is currently non-functional.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/hardware.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware/iomd.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "sound_config.h"
+#include "vidc.h"
+
+#ifndef _SIOC_TYPE
+#define _SIOC_TYPE(x)	_IOC_TYPE(x)
+#endif
+#ifndef _SIOC_NR
+#define _SIOC_NR(x)	_IOC_NR(x)
+#endif
+
+#define VIDC_SOUND_CLOCK	(250000)
+
+/*
+ * When using SERIAL SOUND mode (external DAC), the number of physical
+ * channels is fixed at 2.
+ */
+static int		vidc_busy;
+static int		vidc_adev;
+static int		vidc_audio_rate;
+static char		vidc_audio_format;
+static char		vidc_audio_channels;
+
+static unsigned char	vidc_level_l[SOUND_MIXER_NRDEVICES] = {
+	85,		/* master	*/
+	50,		/* bass		*/
+	50,		/* treble	*/
+	0,		/* synth	*/
+	75,		/* pcm		*/
+	0,		/* speaker	*/
+	100,		/* ext line	*/
+	0,		/* mic		*/
+	100,		/* CD		*/
+	0,
+};
+
+static unsigned char	vidc_level_r[SOUND_MIXER_NRDEVICES] = {
+	85,		/* master	*/
+	50,		/* bass		*/
+	50,		/* treble	*/
+	0,		/* synth	*/
+	75,		/* pcm		*/
+	0,		/* speaker	*/
+	100,		/* ext line	*/
+	0,		/* mic		*/
+	100,		/* CD		*/
+	0,
+};
+
+static unsigned int	vidc_audio_volume_l;	/* left PCM vol, 0 - 65536 */
+static unsigned int	vidc_audio_volume_r;	/* right PCM vol, 0 - 65536 */
+
+static void	(*old_mksound)(unsigned int hz, unsigned int ticks);
+extern void	(*kd_mksound)(unsigned int hz, unsigned int ticks);
+extern void	vidc_update_filler(int bits, int channels);
+extern int	softoss_dev;
+
+static void
+vidc_mksound(unsigned int hz, unsigned int ticks)
+{
+//	printk("BEEP - %d %d!\n", hz, ticks);
+}
+
+static void
+vidc_mixer_set(int mdev, unsigned int level)
+{
+	unsigned int lev_l = level & 0x007f;
+	unsigned int lev_r = (level & 0x7f00) >> 8;
+	unsigned int mlev_l, mlev_r;
+
+	if (lev_l > 100)
+		lev_l = 100;
+	if (lev_r > 100)
+		lev_r = 100;
+
+#define SCALE(lev,master)	((lev) * (master) * 65536 / 10000)
+
+	mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
+	mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
+
+	switch (mdev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_PCM:
+		vidc_level_l[mdev] = lev_l;
+		vidc_level_r[mdev] = lev_r;
+
+		vidc_audio_volume_l = SCALE(lev_l, mlev_l);
+		vidc_audio_volume_r = SCALE(lev_r, mlev_r);
+/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
+		break;
+	}
+#undef SCALE
+}
+
+static int vidc_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	unsigned int val;
+	unsigned int mdev;
+
+	if (_SIOC_TYPE(cmd) != 'M')
+		return -EINVAL;
+
+	mdev = _SIOC_NR(cmd);
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		if (get_user(val, (unsigned int *)arg))
+			return -EFAULT;
+
+		if (mdev < SOUND_MIXER_NRDEVICES)
+			vidc_mixer_set(mdev, val);
+		else
+			return -EINVAL;
+	}
+
+	/*
+	 * Return parameters
+	 */
+	switch (mdev) {
+	case SOUND_MIXER_RECSRC:
+		val = 0;
+		break;
+
+	case SOUND_MIXER_DEVMASK:
+		val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
+		break;
+
+	case SOUND_MIXER_STEREODEVS:
+		val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
+		break;
+
+	case SOUND_MIXER_RECMASK:
+		val = 0;
+		break;
+
+	case SOUND_MIXER_CAPS:
+		val = 0;
+		break;
+
+	default:
+		if (mdev < SOUND_MIXER_NRDEVICES)
+			val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
+		else
+			return -EINVAL;
+	}
+
+	return put_user(val, (unsigned int *)arg) ? -EFAULT : 0;
+}
+
+static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
+{
+	switch (fmt) {
+	default:
+		fmt = AFMT_S16_LE;
+	case AFMT_U8:
+	case AFMT_S8:
+	case AFMT_S16_LE:
+		vidc_audio_format = fmt;
+		vidc_update_filler(vidc_audio_format, vidc_audio_channels);
+	case AFMT_QUERY:
+		break;
+	}
+	return vidc_audio_format;
+}
+
+static int vidc_audio_set_speed(int dev, int rate)
+{
+	if (rate) {
+		unsigned int hwctrl, hwrate;
+		unsigned int newsize, new2size;
+
+		/*
+		 * If we have selected 44.1kHz, use the DAC clock.
+		 */
+		if (0 && rate == 44100) {
+			hwctrl = 0x00000002;
+			hwrate = 3;
+		} else {
+			hwctrl = 0x00000003;
+
+			hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
+			if (hwrate < 3)
+				hwrate = 3;
+			if (hwrate > 255)
+				hwrate = 255;
+
+			rate = VIDC_SOUND_CLOCK / hwrate;
+		}
+
+		vidc_writel(0xb0000000 | (hwrate - 2));
+		vidc_writel(0xb1000000 | hwctrl);
+
+		newsize = (10000 / hwrate) & ~3;
+		if (newsize < 208)
+			newsize = 208;
+		if (newsize > 4096)
+			newsize = 4096;
+		for (new2size = 128; new2size < newsize; new2size <<= 1);
+			if (new2size - newsize > newsize - (new2size >> 1))
+				new2size >>= 1;
+		if (new2size > 4096) {
+			printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
+				newsize, new2size);
+			new2size = 4096;
+		}
+		dma_bufsize = new2size;
+		vidc_audio_rate = rate;
+	}
+	return vidc_audio_rate;
+}
+
+static short vidc_audio_set_channels(int dev, short channels)
+{
+	switch (channels) {
+	default:
+		channels = 2;
+	case 1:
+	case 2:
+		vidc_audio_channels = channels;
+		vidc_update_filler(vidc_audio_format, vidc_audio_channels);
+	case 0:
+		break;
+	}
+	return vidc_audio_channels;
+}
+
+/*
+ * Open the device
+ */
+static int vidc_audio_open(int dev, int mode)
+{
+	/* This audio device does not have recording capability */
+	if (mode == OPEN_READ)
+		return -EPERM;
+
+	if (vidc_busy)
+		return -EBUSY;
+
+	vidc_busy = 1;
+	return 0;
+}
+
+/*
+ * Close the device
+ */
+static void vidc_audio_close(int dev)
+{
+	vidc_busy = 0;
+}
+
+/*
+ * Output a block via DMA to sound device.
+ *
+ * We just set the DMA start and count; the DMA interrupt routine
+ * will take care of formatting the samples (via the appropriate
+ * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
+ * more data is required.
+ */
+static void
+vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
+{
+	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
+	dma_count = total_count;
+	local_irq_restore(flags);
+}
+
+static void
+vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
+{
+}
+
+static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	return -EINVAL;
+}
+
+static void vidc_audio_dma_interrupt(void)
+{
+	DMAbuf_outputintr(vidc_adev, 1);
+}
+
+/*
+ * Prepare for outputting samples.
+ *
+ * Each buffer that will be passed will be `bsize' bytes long,
+ * with a total of `bcount' buffers.
+ */
+static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	dma_interrupt = NULL;
+	adev->dmap_out->flags |= DMA_NODMA;
+
+	return 0;
+}
+
+/*
+ * Stop our current operation.
+ */
+static void vidc_audio_reset(int dev)
+{
+	dma_interrupt = NULL;
+}
+
+static int vidc_audio_local_qlen(int dev)
+{
+	return /*dma_count !=*/ 0;
+}
+
+static void vidc_audio_trigger(int dev, int enable_bits)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (enable_bits & PCM_ENABLE_OUTPUT) {
+		if (!(adev->flags & DMA_ACTIVE)) {
+			unsigned long flags;
+
+			local_irq_save(flags);
+
+			/* prevent recusion */
+			adev->flags |= DMA_ACTIVE;
+
+			dma_interrupt = vidc_audio_dma_interrupt;
+			vidc_sound_dma_irq(0, NULL, NULL);
+			iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
+
+			local_irq_restore(flags);
+		}
+	}
+}
+
+static struct audio_driver vidc_audio_driver =
+{
+	owner:			THIS_MODULE,
+	open:			vidc_audio_open,
+	close:			vidc_audio_close,
+	output_block:		vidc_audio_output_block,
+	start_input:		vidc_audio_start_input,
+	prepare_for_input:	vidc_audio_prepare_for_input,
+	prepare_for_output:	vidc_audio_prepare_for_output,
+	halt_io:		vidc_audio_reset,
+	local_qlen:		vidc_audio_local_qlen,
+	trigger:		vidc_audio_trigger,
+	set_speed:		vidc_audio_set_speed,
+	set_bits:		vidc_audio_set_format,
+	set_channels:		vidc_audio_set_channels
+};
+
+static struct mixer_operations vidc_mixer_operations = {
+	owner:		THIS_MODULE,
+	id:		"VIDC",
+	name:		"VIDCsound",
+	ioctl:		vidc_mixer_ioctl
+};
+
+void vidc_update_filler(int format, int channels)
+{
+#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
+
+	switch (TYPE(format, channels)) {
+	default:
+	case TYPE(AFMT_U8, 1):
+		vidc_filler = vidc_fill_1x8_u;
+		break;
+
+	case TYPE(AFMT_U8, 2):
+		vidc_filler = vidc_fill_2x8_u;
+		break;
+
+	case TYPE(AFMT_S8, 1):
+		vidc_filler = vidc_fill_1x8_s;
+		break;
+
+	case TYPE(AFMT_S8, 2):
+		vidc_filler = vidc_fill_2x8_s;
+		break;
+
+	case TYPE(AFMT_S16_LE, 1):
+		vidc_filler = vidc_fill_1x16_s;
+		break;
+
+	case TYPE(AFMT_S16_LE, 2):
+		vidc_filler = vidc_fill_2x16_s;
+		break;
+	}
+}
+
+static void __init attach_vidc(struct address_info *hw_config)
+{
+	char name[32];
+	int i, adev;
+
+	sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
+	conf_printf(name, hw_config);
+	memset(dma_buf, 0, sizeof(dma_buf));
+
+	adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
+			&vidc_audio_driver, sizeof(vidc_audio_driver),
+			DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
+			NULL, hw_config->dma, hw_config->dma2);
+
+	if (adev < 0)
+		goto audio_failed;
+
+	/*
+	 * 1024 bytes => 64 buffers
+	 */
+	audio_devs[adev]->min_fragment = 10;
+	audio_devs[adev]->mixer_dev = num_mixers;
+
+	audio_devs[adev]->mixer_dev =
+		sound_install_mixer(MIXER_DRIVER_VERSION,
+				name, &vidc_mixer_operations,
+				sizeof(vidc_mixer_operations), NULL);
+
+	if (audio_devs[adev]->mixer_dev < 0)
+		goto mixer_failed;
+
+	for (i = 0; i < 2; i++) {
+		dma_buf[i] = get_free_page(GFP_KERNEL);
+		if (!dma_buf[i]) {
+			printk(KERN_ERR "%s: can't allocate required buffers\n",
+				name);
+			goto mem_failed;
+		}
+		dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
+	}
+
+	if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
+		printk(KERN_ERR "%s: DMA %d is in  use\n", name, hw_config->dma);
+		goto dma_failed;
+	}
+
+	if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
+			hw_config->name, &dma_start)) {
+		printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
+		goto irq_failed;
+	}
+	old_mksound = kd_mksound;
+	kd_mksound = vidc_mksound;
+	vidc_adev = adev;
+	vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
+
+#if defined(CONFIG_SOUND_ALSA_SOFTOSS) || defined(CONFIG_SOUND_ALSA_SOFTOSS_MODULE)
+	softoss_dev = adev;
+#endif
+	return;
+
+irq_failed:
+	sound_free_dma(hw_config->dma);
+dma_failed:
+mem_failed:
+	for (i = 0; i < 2; i++)
+		free_page(dma_buf[i]);
+	sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
+mixer_failed:
+	sound_unload_audiodev(adev);
+audio_failed:
+	return;
+}
+
+static int __init probe_vidc(struct address_info *hw_config)
+{
+	hw_config->irq		= IRQ_DMAS0;
+	hw_config->dma		= DMA_VIRTUAL_SOUND;
+	hw_config->dma2		= -1;
+	hw_config->card_subtype	= 16;
+	hw_config->name		= "VIDC20";
+	return 1;
+}
+
+static void __exit unload_vidc(struct address_info *hw_config)
+{
+	int i, adev = vidc_adev;
+
+	vidc_adev = -1;
+
+	if (old_mksound)
+		kd_mksound = old_mksound;
+
+	free_irq(hw_config->irq, &dma_start);
+	sound_free_dma(hw_config->dma);
+
+	if (adev >= 0) {
+		sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
+		sound_unload_audiodev(adev);
+		for (i = 0; i < 2; i++)
+			free_page(dma_buf[i]);
+	}
+}
+
+static struct address_info cfg;
+
+static int __init init_vidc(void)
+{
+	if (probe_vidc(&cfg) == 0)
+		return -ENODEV;
+
+	attach_vidc(&cfg);
+
+	return 0;
+}
+
+static void __exit cleanup_vidc(void)
+{
+	unload_vidc(&cfg);
+}
+
+module_init(init_vidc);
+module_exit(cleanup_vidc);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("VIDC20 audio driver");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
diff -Nru linux/sound/oss/vidc.h linux-2.4.19-pre5-mjc/sound/oss/vidc.h
--- linux/sound/oss/vidc.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/vidc.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,67 @@
+/*
+ *  linux/drivers/sound/vidc.h
+ *
+ *  Copyright (C) 1997 Russell King <rmk@arm.linux.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  VIDC sound function prototypes
+ */
+
+/* vidc.c */
+
+extern int vidc_busy;
+
+/* vidc_fill.S */
+
+/*
+ * Filler routines for different channels and sample sizes
+ */
+
+extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend,
+				      unsigned long obuf, int mask);
+extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend,
+				      unsigned long obuf, int mask);
+
+/*
+ * DMA Interrupt handler
+ */
+
+extern void vidc_sound_dma_irq(int irqnr, void *ref, struct pt_regs *regs);
+
+/*
+ * Filler routine pointer
+ */
+
+extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend,
+				     unsigned long obuf, int mask);
+
+/*
+ * Virtual DMA buffer exhausted
+ */
+
+extern void     (*dma_interrupt) (void);
+
+/*
+ * Virtual DMA buffer addresses
+ */
+
+extern unsigned long dma_start, dma_count, dma_bufsize;
+extern unsigned long dma_buf[2], dma_pbuf[2];
+
+/* vidc_synth.c */
+
+extern void     vidc_synth_init(struct address_info *hw_config);
+extern void	vidc_synth_exit(struct address_info *hw_config);
+extern int      vidc_synth_get_volume(void);
+extern int      vidc_synth_set_volume(int vol);
diff -Nru linux/sound/oss/vidc_fill.S linux-2.4.19-pre5-mjc/sound/oss/vidc_fill.S
--- linux/sound/oss/vidc_fill.S	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/vidc_fill.S	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,218 @@
+/*
+ *  linux/drivers/sound/vidc_fill.S
+ *
+ *  Copyright (C) 1997 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Filler routines for DMA buffers
+ */
+#define __ASSEMBLY__
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+#include <asm/hardware/iomd.h>
+
+		.text
+
+ENTRY(vidc_fill_1x8_u)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	SYMBOL_NAME(vidc_clear)
+		ldrb	r4, [r0], #1
+		eor	r4, r4, #0x80
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_2x8_u)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	SYMBOL_NAME(vidc_clear)
+		ldr	r4, [r0], #2
+		and	r5, r4, ip
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r5, lsl #16
+		orr	r4, r4, r4, lsr #8
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_1x8_s)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	SYMBOL_NAME(vidc_clear)
+		ldrb	r4, [r0], #1
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_2x8_s)
+		mov	ip, #0xff00
+1:		cmp	r0, r1
+		bge	SYMBOL_NAME(vidc_clear)
+		ldr	r4, [r0], #2
+		and	r5, r4, ip
+		and	r4, ip, r4, lsl #8
+		orr	r4, r4, r5, lsl #16
+		orr	r4, r4, r4, lsr #8
+		str	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_1x16_s)
+		mov	ip, #0xff00
+		orr	ip, ip, ip, lsr #8
+1:		cmp	r0, r1
+		bge	SYMBOL_NAME(vidc_clear)
+		ldr	r5, [r0], #2
+		and	r4, r5, ip
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r2], #4
+		cmp	r0, r1
+		addlt	r0, r0, #2
+		andlt	r4, r5, ip, lsl #16
+		orrlt	r4, r4, r4, lsr #16
+		strlt	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_2x16_s)
+		mov	ip, #0xff00
+		orr	ip, ip, ip, lsr #8
+1:		cmp	r0, r1
+		bge	SYMBOL_NAME(vidc_clear)
+		ldr	r4, [r0], #4
+		str	r4, [r2], #4
+		cmp	r0, r1
+		ldrlt	r4, [r0], #4
+		strlt	r4, [r2], #4
+		cmp	r2, r3
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_fill_noaudio)
+		mov	r0, #0
+		mov	r1, #0
+2:		mov	r4, #0
+		mov	r5, #0
+1:		cmp	r2, r3
+		stmltia	r2!, {r0, r1, r4, r5}
+		blt	1b
+		mov	pc, lr
+
+ENTRY(vidc_clear)
+		mov	r0, #0
+		mov	r1, #0
+		tst	r2, #4
+		str	r0, [r2], #4
+		tst	r2, #8
+		stmia	r2!, {r0, r1}
+		b	2b
+
+/*
+ * Call filler routines with:
+ *  r0 = phys address
+ *  r1 = phys end
+ *  r2 = buffer
+ * Returns:
+ *  r0 = new buffer address
+ *  r2 = new buffer finish
+ *  r4 = corrupted
+ *  r5 = corrupted
+ *  ip = corrupted
+ */
+
+ENTRY(vidc_sound_dma_irq)
+		stmfd	sp!, {r4 - r8, lr}
+		ldr	r8, =SYMBOL_NAME(dma_start)
+		ldmia	r8, {r0, r1, r2, r3, r4, r5}
+		teq	r1, #0
+		adreq	r4, SYMBOL_NAME(vidc_fill_noaudio)
+		moveq	r7, #1 << 31
+		movne	r7, #0
+		mov	ip, #IOMD_BASE & 0xff000000
+		orr	ip, ip, #IOMD_BASE & 0x00ff0000
+		ldrb	r6, [ip, #IOMD_SD0ST]
+		tst	r6, #DMA_ST_OFL			@ Check for overrun
+		eorne	r6, r6, #DMA_ST_AB
+		tst	r6, #DMA_ST_AB
+		moveq	r2, r3				@ DMAing A, update B
+		add	r3, r2, r5			@ End of DMA buffer
+		add	r1, r1, r0			@ End of virtual DMA buffer
+		mov	lr, pc
+		mov	pc, r4				@ Call fill routine (uses r4, ip)
+		sub	r1, r1, r0			@ Remaining length
+		stmia	r8, {r0, r1}
+		mov	r0, #0
+		tst	r2, #4				@ Round buffer up to 4 words
+		strne	r0, [r2], #4
+		tst	r2, #8
+		strne	r0, [r2], #4
+		strne	r0, [r2], #4
+		sub	r2, r2, #16
+		mov	r2, r2, lsl #20
+		movs	r2, r2, lsr #20
+		orreq	r2, r2, #1 << 30		@ Set L bit
+		orr	r2, r2, r7
+		ldmdb	r8, {r3, r4, r5}
+		tst	r6, #DMA_ST_AB
+		mov	ip, #IOMD_BASE & 0xff000000
+		orr	ip, ip, #IOMD_BASE & 0x00ff0000
+		streq	r4, [ip, #IOMD_SD0CURB]
+		strne	r5, [ip, #IOMD_SD0CURA]
+		streq	r2, [ip, #IOMD_SD0ENDB]
+		strne	r2, [ip, #IOMD_SD0ENDA]
+		ldr	lr, [ip, #IOMD_SD0ST]
+		tst	lr, #DMA_ST_OFL
+		bne	1f
+		tst	r6, #DMA_ST_AB
+		strne	r4, [ip, #IOMD_SD0CURB]
+		streq	r5, [ip, #IOMD_SD0CURA]
+		strne	r2, [ip, #IOMD_SD0ENDB]
+		streq	r2, [ip, #IOMD_SD0ENDA]
+1:		teq	r7, #0
+		mov	r0, #0x10
+		strneb	r0, [ip, #IOMD_SD0CR]
+		ldmfd	sp!, {r4 - r8, lr}
+		teq	r1, #0				@ If we have no more
+		movne	pc, lr
+		teq	r3, #0
+		movne	pc, r3				@ Call interrupt routine
+		mov	pc, lr
+
+		.data
+		.globl	SYMBOL_NAME(dma_interrupt)
+SYMBOL_NAME(dma_interrupt):
+		.long	0				@ r3
+		.globl	SYMBOL_NAME(dma_pbuf)
+SYMBOL_NAME(dma_pbuf):
+		.long	0				@ r4
+		.long	0				@ r5
+		.globl	SYMBOL_NAME(dma_start)
+SYMBOL_NAME(dma_start):
+		.long	0				@ r0
+		.globl	SYMBOL_NAME(dma_count)
+SYMBOL_NAME(dma_count):
+		.long	0				@ r1
+		.globl	SYMBOL_NAME(dma_buf)
+SYMBOL_NAME(dma_buf):
+		.long	0				@ r2
+		.long	0				@ r3
+		.globl	SYMBOL_NAME(vidc_filler)
+SYMBOL_NAME(vidc_filler):
+		.long	SYMBOL_NAME(vidc_fill_noaudio)	@ r4
+		.globl	SYMBOL_NAME(dma_bufsize)
+SYMBOL_NAME(dma_bufsize):
+		.long	0x1000				@ r5
diff -Nru linux/sound/oss/vwsnd.c linux-2.4.19-pre5-mjc/sound/oss/vwsnd.c
--- linux/sound/oss/vwsnd.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/vwsnd.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3477 @@
+/*
+ * Sound driver for Silicon Graphics 320 and 540 Visual Workstations'
+ * onboard audio.  See notes in ../../Documentation/sound/vwsnd .
+ *
+ * Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#undef VWSND_DEBUG			/* define for debugging */
+
+/*
+ * XXX to do -
+ *
+ *	External sync.
+ *	Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational.
+ *	Bug - if select() called before read(), pcm_setup() not called.
+ *	Bug - output doesn't stop soon enough if process killed.
+ */
+
+/*
+ * Things to test -
+ *
+ *	Will readv/writev work?  Write a test.
+ *
+ *	insmod/rmmod 100 million times.
+ *
+ *	Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT
+ *	rate).
+ *
+ *	Concurrent threads banging on mixer simultaneously, both UP
+ *	and SMP kernels.  Especially, watch for thread A changing
+ *	OUTSRC while thread B changes gain -- both write to the same
+ *	ad1843 register.
+ *
+ *	What happens if a client opens /dev/audio then forks?
+ *	Do two procs have /dev/audio open?  Test.
+ *
+ *	Pump audio through the CD, MIC and line inputs and verify that
+ *	they mix/mute into the output.
+ *
+ *	Apps:
+ *		amp
+ *		mpg123
+ *		x11amp
+ *		mxv
+ *		kmedia
+ *		esound
+ *		need more input apps
+ *
+ *	Run tests while bombarding with signals.  setitimer(2) will do it...  */
+
+/*
+ * This driver is organized in nine sections.
+ * The nine sections are:
+ *
+ *	debug stuff
+ * 	low level lithium access
+ *	high level lithium access
+ *	AD1843 access
+ *	PCM I/O
+ *	audio driver
+ *	mixer driver
+ *	probe/attach/unload
+ *	initialization and loadable kernel module interface
+ *
+ * That is roughly the order of increasing abstraction, so forward
+ * dependencies are minimal.
+ */
+
+/*
+ * Locking Notes
+ *
+ *	INC_USE_COUNT and DEC_USE_COUNT keep track of the number of
+ *	open descriptors to this driver. They store it in vwsnd_use_count.
+ * 	The global device list, vwsnd_dev_list,	is immutable when the IN_USE
+ *	is true.
+ *
+ *	devc->open_lock is a semaphore that is used to enforce the
+ *	single reader/single writer rule for /dev/audio.  The rule is
+ *	that each device may have at most one reader and one writer.
+ *	Open will block until the previous client has closed the
+ *	device, unless O_NONBLOCK is specified.
+ *
+ *	The semaphore devc->io_sema serializes PCM I/O syscalls.  This
+ *	is unnecessary in Linux 2.2, because the kernel lock
+ *	serializes read, write, and ioctl globally, but it's there,
+ *	ready for the brave, new post-kernel-lock world.
+ *
+ *	Locking between interrupt and baselevel is handled by the
+ *	"lock" spinlock in vwsnd_port (one lock each for read and
+ *	write).  Each half holds the lock just long enough to see what
+ *	area it owns and update its pointers.  See pcm_output() and
+ *	pcm_input() for most of the gory stuff.
+ *
+ *	devc->mix_sema serializes all mixer ioctls.  This is also
+ *	redundant because of the kernel lock.
+ *
+ *	The lowest level lock is lith->lithium_lock.  It is a
+ *	spinlock which is held during the two-register tango of
+ *	reading/writing an AD1843 register.  See
+ *	li_{read,write}_ad1843_reg().
+ */
+
+/*
+ * Sample Format Notes
+ *
+ *	Lithium's DMA engine has two formats: 16-bit 2's complement
+ *	and 8-bit unsigned .  16-bit transfers the data unmodified, 2
+ *	bytes per sample.  8-bit unsigned transfers 1 byte per sample
+ *	and XORs each byte with 0x80.  Lithium can input or output
+ *	either mono or stereo in either format.
+ *
+ *	The AD1843 has four formats: 16-bit 2's complement, 8-bit
+ *	unsigned, 8-bit mu-Law and 8-bit A-Law.
+ *
+ *	This driver supports five formats: AFMT_S8, AFMT_U8,
+ *	AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE.
+ *
+ *	For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and
+ *	rely on Lithium's XOR to translate between U8 and S8.
+ *
+ *	For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR
+ *	the 0x80 bit in software to compensate for Lithium's XOR.
+ *	This happens in pcm_copy_{in,out}().
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added some __init/__exit
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/stddef.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <asm/fixmap.h>
+#include <asm/cobalt.h>
+#include <asm/semaphore.h>
+
+#include "sound_config.h"
+
+/*****************************************************************************/
+/* debug stuff */
+
+#ifdef VWSND_DEBUG
+
+#include <linux/interrupt.h>		/* for in_interrupt() */
+
+static int shut_up = 1;
+
+/*
+ * dbgassert - called when an assertion fails.
+ */
+
+static void dbgassert(const char *fcn, int line, const char *expr)
+{
+	if (in_interrupt())
+		panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n",
+		      __FILE__, fcn, line, expr);
+	else {
+		int x;
+		printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n",
+		       __FILE__, fcn, line, expr);
+		x = * (volatile int *) 0; /* force proc to exit */
+	}
+}
+
+/*
+ * Bunch of useful debug macros:
+ *
+ *	ASSERT	- print unless e nonzero (panic if in interrupt)
+ *	DBGDO	- include arbitrary code if debugging
+ *	DBGX	- debug print raw (w/o function name)
+ *	DBGP	- debug print w/ function name
+ *	DBGE	- debug print function entry
+ *	DBGC	- debug print function call
+ *	DBGR	- debug print function return
+ *	DBGXV	- debug print raw when verbose
+ *	DBGPV	- debug print when verbose
+ *	DBGEV	- debug print function entry when verbose
+ *	DBGRV	- debug print function return when verbose
+ */
+
+#define ASSERT(e)      ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e))
+#define DBGDO(x)            x
+#define DBGX(fmt, args...)  (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
+#define DBGP(fmt, args...)  (DBGX(__FUNCTION__ ": " fmt, ##args))
+#define DBGE(fmt, args...)  (DBGX(__FUNCTION__ fmt, ##args))
+#define DBGC(rtn)           (DBGP("calling %s\n", rtn))
+#define DBGR()              (DBGP("returning\n"))
+#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
+#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args))
+#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args))
+#define DBGCV(rtn)          (shut_up ? 0 : DBGC(rtn))
+#define DBGRV()             (shut_up ? 0 : DBGR())
+
+#else /* !VWSND_DEBUG */
+
+#define ASSERT(e)           ((void) 0)
+#define DBGDO(x)            /* don't */
+#define DBGX(fmt, args...)  ((void) 0)
+#define DBGP(fmt, args...)  ((void) 0)
+#define DBGE(fmt, args...)  ((void) 0)
+#define DBGC(rtn)           ((void) 0)
+#define DBGR()              ((void) 0)
+#define DBGPV(fmt, args...) ((void) 0)
+#define DBGXV(fmt, args...) ((void) 0)
+#define DBGEV(fmt, args...) ((void) 0)
+#define DBGCV(rtn)          ((void) 0)
+#define DBGRV()             ((void) 0)
+
+#endif /* !VWSND_DEBUG */
+
+/*****************************************************************************/
+/* low level lithium access */
+
+/*
+ * We need to talk to Lithium registers on three pages.  Here are
+ * the pages' offsets from the base address (0xFF001000).
+ */
+
+enum {
+	LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */
+	LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */
+	LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */
+};
+
+/* low-level lithium data */
+
+typedef struct lithium {
+	caddr_t		page0;		/* virtual addresses */
+	caddr_t		page1;
+	caddr_t		page2;
+	spinlock_t	lock;		/* protects codec and UST/MSC access */
+} lithium_t;
+
+/*
+ * li_create initializes the lithium_t structure and sets up vm mappings
+ * to access the registers.
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int li_create(lithium_t *lith, unsigned long baseaddr)
+{
+	static void li_destroy(lithium_t *);
+
+	lith->lock = SPIN_LOCK_UNLOCKED;
+	lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE);
+	lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE);
+	lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE);
+	if (!lith->page0 || !lith->page1 || !lith->page2) {
+		li_destroy(lith);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * li_destroy destroys the lithium_t structure and vm mappings.
+ */
+
+static void li_destroy(lithium_t *lith)
+{
+	if (lith->page0) {
+		iounmap(lith->page0);
+		lith->page0 = NULL;
+	}
+	if (lith->page1) {
+		iounmap(lith->page1);
+		lith->page1 = NULL;
+	}
+	if (lith->page2) {
+		iounmap(lith->page2);
+		lith->page2 = NULL;
+	}
+}
+
+/*
+ * basic register accessors - read/write long/byte
+ */
+
+static __inline__ unsigned long li_readl(lithium_t *lith, int off)
+{
+	return * (volatile unsigned long *) (lith->page0 + off);
+}
+
+static __inline__ unsigned char li_readb(lithium_t *lith, int off)
+{
+	return * (volatile unsigned char *) (lith->page0 + off);
+}
+
+static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val)
+{
+	* (volatile unsigned long *) (lith->page0 + off) = val;
+}
+
+static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val)
+{
+	* (volatile unsigned char *) (lith->page0 + off) = val;
+}
+
+/*****************************************************************************/
+/* High Level Lithium Access */
+
+/*
+ * Lithium DMA Notes
+ *
+ * Lithium has two dedicated DMA channels for audio.  They are known
+ * as comm1 and comm2 (communication areas 1 and 2).  Comm1 is for
+ * input, and comm2 is for output.  Each is controlled by three
+ * registers: BASE (base address), CFG (config) and CCTL
+ * (config/control).
+ *
+ * Each DMA channel points to a physically contiguous ring buffer in
+ * main memory of up to 8 Kbytes.  (This driver always uses 8 Kb.)
+ * There are three pointers into the ring buffer: read, write, and
+ * trigger.  The pointers are 8 bits each.  Each pointer points to
+ * 32-byte "chunks" of data.  The DMA engine moves 32 bytes at a time,
+ * so there is no finer-granularity control.
+ *
+ * In comm1, the hardware updates the write ptr, and software updates
+ * the read ptr.  In comm2, it's the opposite: hardware updates the
+ * read ptr, and software updates the write ptr.  I designate the
+ * hardware-updated ptr as the hwptr, and the software-updated ptr as
+ * the swptr.
+ *
+ * The trigger ptr and trigger mask are used to trigger interrupts.
+ * From the Lithium spec, section 5.6.8, revision of 12/15/1998:
+ *
+ *	Trigger Mask Value
+ *
+ *	A three bit wide field that represents a power of two mask
+ *	that is used whenever the trigger pointer is compared to its
+ *	respective read or write pointer.  A value of zero here
+ *	implies a mask of 0xFF and a value of seven implies a mask
+ *	0x01.  This value can be used to sub-divide the ring buffer
+ *	into pie sections so that interrupts monitor the progress of
+ *	hardware from section to section.
+ *
+ * My interpretation of that is, whenever the hw ptr is updated, it is
+ * compared with the trigger ptr, and the result is masked by the
+ * trigger mask.  (Actually, by the complement of the trigger mask.)
+ * If the result is zero, an interrupt is triggered.  I.e., interrupt
+ * if ((hwptr & ~mask) == (trptr & ~mask)).  The mask is formed from
+ * the trigger register value as mask = (1 << (8 - tmreg)) - 1.
+ *
+ * In yet different words, setting tmreg to 0 causes an interrupt after
+ * every 256 DMA chunks (8192 bytes) or once per traversal of the
+ * ring buffer.  Setting it to 7 caues an interrupt every 2 DMA chunks
+ * (64 bytes) or 128 times per traversal of the ring buffer.
+ */
+
+/* Lithium register offsets and bit definitions */
+
+#define LI_HOST_CONTROLLER	0x000
+# define LI_HC_RESET		 0x00008000
+# define LI_HC_LINK_ENABLE	 0x00004000
+# define LI_HC_LINK_FAILURE	 0x00000004
+# define LI_HC_LINK_CODEC	 0x00000002
+# define LI_HC_LINK_READY	 0x00000001
+
+#define LI_INTR_STATUS		0x010
+#define LI_INTR_MASK		0x014
+# define LI_INTR_LINK_ERR	 0x00008000
+# define LI_INTR_COMM2_TRIG	 0x00000008
+# define LI_INTR_COMM2_UNDERFLOW 0x00000004
+# define LI_INTR_COMM1_TRIG	 0x00000002
+# define LI_INTR_COMM1_OVERFLOW  0x00000001
+
+#define LI_CODEC_COMMAND	0x018
+# define LI_CC_BUSY		 0x00008000
+# define LI_CC_DIR		 0x00000080
+#  define LI_CC_DIR_RD		  LI_CC_DIR
+#  define LI_CC_DIR_WR		(!LI_CC_DIR)
+# define LI_CC_ADDR_MASK	 0x0000007F
+
+#define LI_CODEC_DATA		0x01C
+
+#define LI_COMM1_BASE		0x100
+#define LI_COMM1_CTL		0x104
+# define LI_CCTL_RESET		 0x80000000
+# define LI_CCTL_SIZE		 0x70000000
+# define LI_CCTL_DMA_ENABLE	 0x08000000
+# define LI_CCTL_TMASK		 0x07000000 /* trigger mask */
+# define LI_CCTL_TPTR		 0x00FF0000 /* trigger pointer */
+# define LI_CCTL_RPTR		 0x0000FF00
+# define LI_CCTL_WPTR		 0x000000FF
+#define LI_COMM1_CFG		0x108
+# define LI_CCFG_LOCK		 0x00008000
+# define LI_CCFG_SLOT		 0x00000070
+# define LI_CCFG_DIRECTION	 0x00000008
+#  define LI_CCFG_DIR_IN	(!LI_CCFG_DIRECTION)
+#  define LI_CCFG_DIR_OUT	  LI_CCFG_DIRECTION
+# define LI_CCFG_MODE		 0x00000004
+#  define LI_CCFG_MODE_MONO	(!LI_CCFG_MODE)
+#  define LI_CCFG_MODE_STEREO	  LI_CCFG_MODE
+# define LI_CCFG_FORMAT		 0x00000003
+#  define LI_CCFG_FMT_8BIT	  0x00000000
+#  define LI_CCFG_FMT_16BIT	  0x00000001
+#define LI_COMM2_BASE		0x10C
+#define LI_COMM2_CTL		0x110
+ /* bit definitions are the same as LI_COMM1_CTL */
+#define LI_COMM2_CFG		0x114
+ /* bit definitions are the same as LI_COMM1_CFG */
+
+#define LI_UST_LOW		0x200	/* 64-bit Unadjusted System Time is */
+#define LI_UST_HIGH		0x204	/* microseconds since boot */
+
+#define LI_AUDIO1_UST		0x300	/* UST-MSC pairs */
+#define LI_AUDIO1_MSC		0x304	/* MSC (Media Stream Counter) */
+#define LI_AUDIO2_UST		0x308	/* counts samples actually */
+#define LI_AUDIO2_MSC		0x30C	/* processed as of time UST */
+
+/* 
+ * Lithium's DMA engine operates on chunks of 32 bytes.  We call that
+ * a DMACHUNK.
+ */
+
+#define DMACHUNK_SHIFT 5
+#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT)
+#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT)
+#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT)
+
+/*
+ * Two convenient macros to shift bitfields into/out of position.
+ *
+ * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)).
+ * As long as mask is constant, we trust the compiler will change the
+ * multipy and divide into shifts.
+ */
+
+#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask))
+#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask)))
+
+/*
+ * dma_chan_desc is invariant information about a Lithium
+ * DMA channel.  There are two instances, li_comm1 and li_comm2.
+ *
+ * Note that the CCTL register fields are write ptr and read ptr, but what
+ * we care about are which pointer is updated by software and which by
+ * hardware.
+ */
+
+typedef struct dma_chan_desc {
+	int basereg;
+	int cfgreg;
+	int ctlreg;
+	int hwptrreg;
+	int swptrreg;
+	int ustreg;
+	int mscreg;
+	unsigned long swptrmask;
+	int ad1843_slot;
+	int direction;			/* LI_CCTL_DIR_IN/OUT */
+} dma_chan_desc_t;
+
+static const dma_chan_desc_t li_comm1 = {
+	LI_COMM1_BASE,			/* base register offset */
+	LI_COMM1_CFG,			/* config register offset */
+	LI_COMM1_CTL,			/* control register offset */
+	LI_COMM1_CTL + 0,		/* hw ptr reg offset (write ptr) */
+	LI_COMM1_CTL + 1,		/* sw ptr reg offset (read ptr) */
+	LI_AUDIO1_UST,			/* ust reg offset */
+	LI_AUDIO1_MSC,			/* msc reg offset */
+	LI_CCTL_RPTR,			/* sw ptr bitmask in ctlval */
+	2,				/* ad1843 serial slot */
+	LI_CCFG_DIR_IN			/* direction */
+};
+
+static const dma_chan_desc_t li_comm2 = {
+	LI_COMM2_BASE,			/* base register offset */
+	LI_COMM2_CFG,			/* config register offset */
+	LI_COMM2_CTL,			/* control register offset */
+	LI_COMM2_CTL + 1,		/* hw ptr reg offset (read ptr) */
+	LI_COMM2_CTL + 0,		/* sw ptr reg offset (writr ptr) */
+	LI_AUDIO2_UST,			/* ust reg offset */
+	LI_AUDIO2_MSC,			/* msc reg offset */
+	LI_CCTL_WPTR,			/* sw ptr bitmask in ctlval */
+	2,				/* ad1843 serial slot */
+	LI_CCFG_DIR_OUT			/* direction */
+};
+
+/*
+ * dma_chan is variable information about a Lithium DMA channel.
+ *
+ * The desc field points to invariant information.
+ * The lith field points to a lithium_t which is passed
+ * to li_read* and li_write* to access the registers.
+ * The *val fields shadow the lithium registers' contents.
+ */
+
+typedef struct dma_chan {
+	const dma_chan_desc_t *desc;
+	lithium_t      *lith;
+	unsigned long   baseval;
+	unsigned long	cfgval;
+	unsigned long	ctlval;
+} dma_chan_t;
+
+/*
+ * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter).
+ * UST is time in microseconds since the system booted, and MSC is a
+ * counter that increments with every audio sample.
+ */
+
+typedef struct ustmsc {
+	unsigned long long ust;
+	unsigned long msc;
+} ustmsc_t;
+
+/*
+ * li_ad1843_wait waits until lithium says the AD1843 register
+ * exchange is not busy.  Returns 0 on success, -EBUSY on timeout.
+ *
+ * Locking: must be called with lithium_lock held.
+ */
+
+static int li_ad1843_wait(lithium_t *lith)
+{
+	unsigned long later = jiffies + 2;
+	while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY)
+		if (jiffies >= later)
+			return -EBUSY;
+	return 0;
+}
+
+/*
+ * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
+ *
+ * Returns unsigned register value on success, -errno on failure.
+ */
+
+static int li_read_ad1843_reg(lithium_t *lith, int reg)
+{
+	int val;
+
+	ASSERT(!in_interrupt());
+	spin_lock(&lith->lock);
+	{
+		val = li_ad1843_wait(lith);
+		if (val == 0) {
+			li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg);
+			val = li_ad1843_wait(lith);
+		}
+		if (val == 0)
+			val = li_readl(lith, LI_CODEC_DATA);
+	}
+	spin_unlock(&lith->lock);
+
+	DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n",
+	      lith, reg, val);
+
+	return val;
+}
+
+/*
+ * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
+ */
+
+static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval)
+{
+	spin_lock(&lith->lock);
+	{
+		if (li_ad1843_wait(lith) == 0) {
+			li_writel(lith, LI_CODEC_DATA, newval);
+			li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg);
+		}
+	}
+	spin_unlock(&lith->lock);
+}
+
+/*
+ * li_setup_dma calculates all the register settings for DMA in a particular
+ * mode.  It takes too many arguments.
+ */
+
+static void li_setup_dma(dma_chan_t *chan,
+			 const dma_chan_desc_t *desc,
+			 lithium_t *lith,
+			 unsigned long buffer_paddr,
+			 int bufshift,
+			 int fragshift,
+			 int channels,
+			 int sampsize)
+{
+	unsigned long mode, format;
+	unsigned long size, tmask;
+
+	DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, "
+	     "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n",
+	     chan, desc, lith, buffer_paddr,
+	     bufshift, fragshift, channels, sampsize);
+
+	/* Reset the channel first. */
+
+	li_writel(lith, desc->ctlreg, LI_CCTL_RESET);
+
+	ASSERT(channels == 1 || channels == 2);
+	if (channels == 2)
+		mode = LI_CCFG_MODE_STEREO;
+	else
+		mode = LI_CCFG_MODE_MONO;
+	ASSERT(sampsize == 1 || sampsize == 2);
+	if (sampsize == 2)
+		format = LI_CCFG_FMT_16BIT;
+	else
+		format = LI_CCFG_FMT_8BIT;
+	chan->desc = desc;
+	chan->lith = lith;
+
+	/*
+	 * Lithium DMA address register takes a 40-bit physical
+	 * address, right-shifted by 8 so it fits in 32 bits.  Bit 37
+	 * must be set -- it enables cache coherence.
+	 */
+
+	ASSERT(!(buffer_paddr & 0xFF));
+	chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
+
+	chan->cfgval = (!LI_CCFG_LOCK |
+			SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
+			desc->direction |
+			mode |
+			format);
+
+	size = bufshift - 6;
+	tmask = 13 - fragshift;		/* See Lithium DMA Notes above. */
+	ASSERT(size >= 2 && size <= 7);
+	ASSERT(tmask >= 1 && tmask <= 7);
+	chan->ctlval = (!LI_CCTL_RESET |
+			SHIFT_FIELD(size, LI_CCTL_SIZE) |
+			!LI_CCTL_DMA_ENABLE |
+			SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
+			SHIFT_FIELD(0, LI_CCTL_TPTR));
+
+	DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval);
+	DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval);
+	DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval);
+
+	li_writel(lith, desc->basereg, chan->baseval);
+	li_writel(lith, desc->cfgreg, chan->cfgval);
+	li_writel(lith, desc->ctlreg, chan->ctlval);
+
+	DBGRV();
+}
+
+static void li_shutdown_dma(dma_chan_t *chan)
+{
+	lithium_t *lith = chan->lith;
+	caddr_t lith1 = lith->page1;
+
+	DBGEV("(chan=0x%p)\n", chan);
+	
+	chan->ctlval &= ~LI_CCTL_DMA_ENABLE;
+	DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
+	li_writel(lith, chan->desc->ctlreg, chan->ctlval);
+
+	/*
+	 * Offset 0x500 on Lithium page 1 is an undocumented,
+	 * unsupported register that holds the zero sample value.
+	 * Lithium is supposed to output zero samples when DMA is
+	 * inactive, and repeat the last sample when DMA underflows.
+	 * But it has a bug, where, after underflow occurs, the zero
+	 * sample is not reset.
+	 *
+	 * I expect this to break in a future rev of Lithium.
+	 */
+
+	if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT)
+		* (volatile unsigned long *) (lith1 + 0x500) = 0;
+}
+
+/*
+ * li_activate_dma always starts dma at the beginning of the buffer.
+ *
+ * N.B., these may be called from interrupt.
+ */
+
+static __inline__ void li_activate_dma(dma_chan_t *chan)
+{
+	chan->ctlval |= LI_CCTL_DMA_ENABLE;
+	DBGPV("ctlval = 0x%lx\n", chan->ctlval);
+	li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval);
+}
+
+static void li_deactivate_dma(dma_chan_t *chan)
+{
+	lithium_t *lith = chan->lith;
+	caddr_t lith2 = lith->page2;
+
+	chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR);
+	DBGPV("ctlval = 0x%lx\n", chan->ctlval);
+	DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
+	li_writel(lith, chan->desc->ctlreg, chan->ctlval);
+
+	/*
+	 * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented,
+	 * unsupported registers that are internal copies of the DMA
+	 * read and write pointers.  Because of a Lithium bug, these
+	 * registers aren't zeroed correctly when DMA is shut off.  So
+	 * we whack them directly.
+	 *
+	 * I expect this to break in a future rev of Lithium.
+	 */
+
+	if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) {
+		* (volatile unsigned long *) (lith2 + 0x98) = 0;
+		* (volatile unsigned long *) (lith2 + 0x9C) = 0;
+	}
+}
+
+/*
+ * read/write the ring buffer pointers.  These routines' arguments and results
+ * are byte offsets from the beginning of the ring buffer.
+ */
+
+static __inline__ int li_read_swptr(dma_chan_t *chan)
+{
+	const unsigned long mask = chan->desc->swptrmask;
+
+	return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask));
+}
+
+static __inline__ int li_read_hwptr(dma_chan_t *chan)
+{
+	return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg));
+}
+
+static __inline__ void li_write_swptr(dma_chan_t *chan, int val)
+{
+	const unsigned long mask = chan->desc->swptrmask;
+
+	ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF)));
+	val = BYTES_TO_CHUNKS(val);
+	chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask);
+	li_writeb(chan->lith, chan->desc->swptrreg, val);
+}
+
+/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */
+
+static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc)
+{
+	lithium_t *lith = chan->lith;
+	const dma_chan_desc_t *desc = chan->desc;
+	unsigned long now_low, now_high0, now_high1, chan_ust;
+
+	spin_lock(&lith->lock);
+	{
+		/*
+		 * retry until we do all five reads without the
+		 * high word changing.  (High word increments
+		 * every 2^32 microseconds, i.e., not often)
+		 */
+		do {
+			now_high0 = li_readl(lith, LI_UST_HIGH);
+			now_low = li_readl(lith, LI_UST_LOW);
+
+			/*
+			 * Lithium guarantees these two reads will be
+			 * atomic -- ust will not increment after msc
+			 * is read.
+			 */
+
+			ustmsc->msc = li_readl(lith, desc->mscreg);
+			chan_ust = li_readl(lith, desc->ustreg);
+
+			now_high1 = li_readl(lith, LI_UST_HIGH);
+		} while (now_high0 != now_high1);
+	}	
+	spin_unlock(&lith->lock);
+	ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust);
+}
+
+static void li_enable_interrupts(lithium_t *lith, unsigned int mask)
+{
+	DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
+
+	/* clear any already-pending interrupts. */
+
+	li_writel(lith, LI_INTR_STATUS, mask);
+
+	/* enable the interrupts. */
+
+	mask |= li_readl(lith, LI_INTR_MASK);
+	li_writel(lith, LI_INTR_MASK, mask);
+}
+
+static void li_disable_interrupts(lithium_t *lith, unsigned int mask)
+{
+	unsigned int keepmask;
+
+	DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
+
+	/* disable the interrupts */
+
+	keepmask = li_readl(lith, LI_INTR_MASK) & ~mask;
+	li_writel(lith, LI_INTR_MASK, keepmask);
+
+	/* clear any pending interrupts. */
+
+	li_writel(lith, LI_INTR_STATUS, mask);
+}
+
+/* Get the interrupt status and clear all pending interrupts. */
+
+static unsigned int li_get_clear_intr_status(lithium_t *lith)
+{
+	unsigned int status;
+
+	status = li_readl(lith, LI_INTR_STATUS);
+	li_writel(lith, LI_INTR_STATUS, ~0);
+	return status & li_readl(lith, LI_INTR_MASK);
+}
+
+static int li_init(lithium_t *lith)
+{
+	/* 1. System power supplies stabilize. */
+
+	/* 2. Assert the ~RESET signal. */
+
+	li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET);
+	udelay(1);
+
+	/* 3. Deassert the ~RESET signal and enter a wait period to allow
+	   the AD1843 internal clocks and the external crystal oscillator
+	   to stabilize. */
+
+	li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
+	udelay(1);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* AD1843 access */
+
+/*
+ * AD1843 bitfield definitions.  All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+typedef struct ad1843_bitfield {
+	char reg;
+	char lo_bit;
+	char nbits;
+} ad1843_bitfield_t;
+
+static const ad1843_bitfield_t
+	ad1843_PDNO   = {  0, 14,  1 },	/* Converter Power-Down Flag */
+	ad1843_INIT   = {  0, 15,  1 },	/* Clock Initialization Flag */
+	ad1843_RIG    = {  2,  0,  4 },	/* Right ADC Input Gain */
+	ad1843_RMGE   = {  2,  4,  1 },	/* Right ADC Mic Gain Enable */
+	ad1843_RSS    = {  2,  5,  3 },	/* Right ADC Source Select */
+	ad1843_LIG    = {  2,  8,  4 },	/* Left ADC Input Gain */
+	ad1843_LMGE   = {  2, 12,  1 },	/* Left ADC Mic Gain Enable */
+	ad1843_LSS    = {  2, 13,  3 },	/* Left ADC Source Select */
+	ad1843_RX1M   = {  4,  0,  5 },	/* Right Aux 1 Mix Gain/Atten */
+	ad1843_RX1MM  = {  4,  7,  1 },	/* Right Aux 1 Mix Mute */
+	ad1843_LX1M   = {  4,  8,  5 },	/* Left Aux 1 Mix Gain/Atten */
+	ad1843_LX1MM  = {  4, 15,  1 },	/* Left Aux 1 Mix Mute */
+	ad1843_RX2M   = {  5,  0,  5 },	/* Right Aux 2 Mix Gain/Atten */
+	ad1843_RX2MM  = {  5,  7,  1 },	/* Right Aux 2 Mix Mute */
+	ad1843_LX2M   = {  5,  8,  5 },	/* Left Aux 2 Mix Gain/Atten */
+	ad1843_LX2MM  = {  5, 15,  1 },	/* Left Aux 2 Mix Mute */
+	ad1843_RMCM   = {  7,  0,  5 },	/* Right Mic Mix Gain/Atten */
+	ad1843_RMCMM  = {  7,  7,  1 },	/* Right Mic Mix Mute */
+	ad1843_LMCM   = {  7,  8,  5 },	/* Left Mic Mix Gain/Atten */
+	ad1843_LMCMM  = {  7, 15,  1 },	/* Left Mic Mix Mute */
+	ad1843_HPOS   = {  8,  4,  1 },	/* Headphone Output Voltage Swing */
+	ad1843_HPOM   = {  8,  5,  1 },	/* Headphone Output Mute */
+	ad1843_RDA1G  = {  9,  0,  6 },	/* Right DAC1 Analog/Digital Gain */
+	ad1843_RDA1GM = {  9,  7,  1 },	/* Right DAC1 Analog Mute */
+	ad1843_LDA1G  = {  9,  8,  6 },	/* Left DAC1 Analog/Digital Gain */
+	ad1843_LDA1GM = {  9, 15,  1 },	/* Left DAC1 Analog Mute */
+	ad1843_RDA1AM = { 11,  7,  1 },	/* Right DAC1 Digital Mute */
+	ad1843_LDA1AM = { 11, 15,  1 },	/* Left DAC1 Digital Mute */
+	ad1843_ADLC   = { 15,  0,  2 },	/* ADC Left Sample Rate Source */
+	ad1843_ADRC   = { 15,  2,  2 },	/* ADC Right Sample Rate Source */
+	ad1843_DA1C   = { 15,  8,  2 },	/* DAC1 Sample Rate Source */
+	ad1843_C1C    = { 17,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_C2C    = { 20,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_DAADL  = { 25,  4,  2 },	/* Digital ADC Left Source Select */
+	ad1843_DAADR  = { 25,  6,  2 },	/* Digital ADC Right Source Select */
+	ad1843_DRSFLT = { 25, 15,  1 },	/* Digital Reampler Filter Mode */
+	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
+	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
+	ad1843_ADTLK  = { 26,  4,  1 },	/* ADC Transmit Lock Mode Select */
+	ad1843_SCF    = { 26,  7,  1 },	/* SCLK Frequency Select */
+	ad1843_DA1F   = { 26,  8,  2 },	/* DAC1 Data Format Select */
+	ad1843_DA1SM  = { 26, 14,  1 },	/* DAC1 Stereo/Mono Mode Select */
+	ad1843_ADLEN  = { 27,  0,  1 },	/* ADC Left Channel Enable */
+	ad1843_ADREN  = { 27,  1,  1 },	/* ADC Right Channel Enable */
+	ad1843_AAMEN  = { 27,  4,  1 },	/* Analog to Analog Mix Enable */
+	ad1843_ANAEN  = { 27,  7,  1 },	/* Analog Channel Enable */
+	ad1843_DA1EN  = { 27,  8,  1 },	/* DAC1 Enable */
+	ad1843_DA2EN  = { 27,  9,  1 },	/* DAC2 Enable */
+	ad1843_C1EN   = { 28, 11,  1 },	/* Clock Generator 1 Enable */
+	ad1843_C2EN   = { 28, 12,  1 },	/* Clock Generator 2 Enable */
+	ad1843_PDNI   = { 28, 15,  1 };	/* Converter Power Down */
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain.  The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+typedef struct ad1843_gain {
+
+	int	negative;		/* nonzero if gain is negative. */
+	const ad1843_bitfield_t *lfield;
+	const ad1843_bitfield_t *rfield;
+
+} ad1843_gain_t;
+
+static const ad1843_gain_t ad1843_gain_RECLEV
+				= { 0, &ad1843_LIG,   &ad1843_RIG };
+static const ad1843_gain_t ad1843_gain_LINE
+				= { 1, &ad1843_LX1M,  &ad1843_RX1M };
+static const ad1843_gain_t ad1843_gain_CD
+				= { 1, &ad1843_LX2M,  &ad1843_RX2M };
+static const ad1843_gain_t ad1843_gain_MIC
+				= { 1, &ad1843_LMCM,  &ad1843_RMCM };
+static const ad1843_gain_t ad1843_gain_PCM
+				= { 1, &ad1843_LDA1G, &ad1843_RDA1G };
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field)
+{
+	int w = li_read_ad1843_reg(lith, field->reg);
+	int val = w >> field->lo_bit & ((1 << field->nbits) - 1);
+
+	DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n",
+	      lith, field->reg, field->lo_bit, field->nbits, val);
+
+	return val;
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(lithium_t *lith,
+			     const ad1843_bitfield_t *field,
+			     int newval)
+{
+	int w = li_read_ad1843_reg(lith, field->reg);
+	int mask = ((1 << field->nbits) - 1) << field->lo_bit;
+	int oldval = (w & mask) >> field->lo_bit;
+	int newbits = (newval << field->lo_bit) & mask;
+	w = (w & ~mask) | newbits;
+	(void) li_write_ad1843_reg(lith, field->reg, w);
+
+	DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) "
+	      "returns 0x%x\n",
+	      lith, field->reg, field->lo_bit, field->nbits, newval,
+	      oldval);
+
+	return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register.  It uses a single read cycle to do it.  (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called ike this.
+ *
+ *  ad1843_read_multi(lith, nfields,
+ *		      &ad1843_FIELD1, &val1,
+ *		      &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(lithium_t *lith, int argcount, ...)
+{
+	va_list ap;
+	const ad1843_bitfield_t *fp;
+	int w = 0, mask, *value, reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const ad1843_bitfield_t *);
+		value = va_arg(ap, int *);
+		if (reg == -1) {
+			reg = fp->reg;
+			w = li_read_ad1843_reg(lith, reg);
+		}
+		ASSERT(reg == fp->reg);
+		mask = (1 << fp->nbits) - 1;
+		*value = w >> fp->lo_bit & mask;
+	}
+	va_end(ap);
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register.  It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ *  ad1843_write_multi(lith, nfields,
+ *		       &ad1843_FIELD1, val1,
+ *		       &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(lithium_t *lith, int argcount, ...)
+{
+	va_list ap;
+	int reg;
+	const ad1843_bitfield_t *fp;
+	int value;
+	int w, m, mask, bits;
+
+	mask = 0;
+	bits = 0;
+	reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const ad1843_bitfield_t *);
+		value = va_arg(ap, int);
+		if (reg == -1)
+			reg = fp->reg;
+		ASSERT(fp->reg == reg);
+		m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+		mask |= m;
+		bits |= (value << fp->lo_bit) & m;
+	}
+	va_end(ap);
+	ASSERT(!(bits & ~mask));
+	if (~mask & 0xFFFF)
+		w = li_read_ad1843_reg(lith, reg);
+	else
+		w = 0;
+	w = (w & ~mask) | bits;
+	(void) li_write_ad1843_reg(lith, reg, w);
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.  It returns the gain in OSS format.
+ */
+
+static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp)
+{
+	int lg, rg;
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg);
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	lg = (lg * 100 + (mask >> 1)) / mask;
+	rg = (rg * 100 + (mask >> 1)) / mask;
+	return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain. Converts from OSS format to AD1843's
+ * format.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+static int ad1843_set_gain(lithium_t *lith,
+			   const ad1843_gain_t *gp,
+			   int newval)
+{
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	int lg = newval >> 0 & 0xFF;
+	int rg = newval >> 8;
+	if (lg < 0 || lg > 100 || rg < 0 || rg > 100)
+		return -EINVAL;
+	lg = (lg * mask + (mask >> 1)) / 100;
+	rg = (rg * mask + (mask >> 1)) / 100;
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg);
+	return ad1843_get_gain(lith, gp);
+}
+
+/* Returns the current recording source, in OSS format. */
+
+static int ad1843_get_recsrc(lithium_t *lith)
+{
+	int ls = ad1843_read_bits(lith, &ad1843_LSS);
+
+	switch (ls) {
+	case 1:
+		return SOUND_MASK_MIC;
+	case 2:
+		return SOUND_MASK_LINE;
+	case 3:
+		return SOUND_MASK_CD;
+	case 6:
+		return SOUND_MASK_PCM;
+	default:
+		ASSERT(0);
+		return -1;
+	}
+}
+
+/*
+ * Enable/disable digital resample mode in the AD1843.
+ *
+ * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down
+ * while switching modes.  So we save DA1's state (DA2's state is not
+ * interesting), power them down, switch into/out of resample mode,
+ * power them up, and restore state.
+ *
+ * This will cause audible glitches if D/A or A/D is going on, so the
+ * driver disallows that (in mixer_write_ioctl()).
+ *
+ * The open question is, is this worth doing?  I'm leaving it in,
+ * because it's written, but...
+ */
+
+static void ad1843_set_resample_mode(lithium_t *lith, int onoff)
+{
+	/* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */
+	int save_da1 = li_read_ad1843_reg(lith, 9);
+
+	/* Power down A/D and D/A. */
+	ad1843_write_multi(lith, 4,
+			   &ad1843_DA1EN, 0,
+			   &ad1843_DA2EN, 0,
+			   &ad1843_ADLEN, 0,
+			   &ad1843_ADREN, 0);
+
+	/* Switch mode */
+	ASSERT(onoff == 0 || onoff == 1);
+	ad1843_write_bits(lith, &ad1843_DRSFLT, onoff);
+
+ 	/* Power up A/D and D/A. */
+	ad1843_write_multi(lith, 3,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* Restore DA1 mute and gain. */
+	li_write_ad1843_reg(lith, 9, save_da1);
+}
+
+/*
+ * Set recording source.  Arg newsrc specifies an OSS channel mask.
+ *
+ * The complication is that when we switch into/out of loopback mode
+ * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of
+ * digital resampling mode.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+static int ad1843_set_recsrc(lithium_t *lith, int newsrc)
+{
+	int bits;
+	int oldbits;
+
+	switch (newsrc) {
+	case SOUND_MASK_PCM:
+		bits = 6;
+		break;
+
+	case SOUND_MASK_MIC:
+		bits = 1;
+		break;
+
+	case SOUND_MASK_LINE:
+		bits = 2;
+		break;
+
+	case SOUND_MASK_CD:
+		bits = 3;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	oldbits = ad1843_read_bits(lith, &ad1843_LSS);
+	if (newsrc == SOUND_MASK_PCM && oldbits != 6) {
+		DBGP("enabling digital resample mode\n");
+		ad1843_set_resample_mode(lith, 1);
+		ad1843_write_multi(lith, 2,
+				   &ad1843_DAADL, 2,
+				   &ad1843_DAADR, 2);
+	} else if (newsrc != SOUND_MASK_PCM && oldbits == 6) {
+		DBGP("disabling digital resample mode\n");
+		ad1843_set_resample_mode(lith, 0);
+		ad1843_write_multi(lith, 2,
+				   &ad1843_DAADL, 0,
+				   &ad1843_DAADR, 0);
+	}
+	ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits);
+	return newsrc;
+}
+
+/*
+ * Return current output sources, in OSS format.
+ */
+
+static int ad1843_get_outsrc(lithium_t *lith)
+{
+	int pcm, line, mic, cd;
+
+	pcm  = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM;
+	line = ad1843_read_bits(lith, &ad1843_LX1MM)  ? 0 : SOUND_MASK_LINE;
+	cd   = ad1843_read_bits(lith, &ad1843_LX2MM)  ? 0 : SOUND_MASK_CD;
+	mic  = ad1843_read_bits(lith, &ad1843_LMCMM)  ? 0 : SOUND_MASK_MIC;
+
+	return pcm | line | cd | mic;
+}
+
+/*
+ * Set output sources.  Arg is a mask of active sources in OSS format.
+ *
+ * Returns source mask on success, -errno on failure.
+ */
+
+static int ad1843_set_outsrc(lithium_t *lith, int mask)
+{
+	int pcm, line, mic, cd;
+
+	if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE |
+		     SOUND_MASK_CD | SOUND_MASK_MIC))
+		return -EINVAL;
+	pcm  = (mask & SOUND_MASK_PCM)  ? 0 : 1;
+	line = (mask & SOUND_MASK_LINE) ? 0 : 1;
+	mic  = (mask & SOUND_MASK_MIC)  ? 0 : 1;
+	cd   = (mask & SOUND_MASK_CD)   ? 0 : 1;
+
+	ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm);
+	ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line);
+	ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd,   &ad1843_RX2MM, cd);
+	ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic,  &ad1843_RMCMM, mic);
+
+	return mask;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+static void ad1843_setup_dac(lithium_t *lith,
+			     int framerate,
+			     int fmt,
+			     int channels)
+{
+	int ad_fmt = 0, ad_mode = 0;
+
+	DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
+	      lith, framerate, fmt, channels);
+
+	switch (fmt) {
+	case AFMT_S8:		ad_fmt = 1; break;
+	case AFMT_U8:		ad_fmt = 1; break;
+	case AFMT_S16_LE:	ad_fmt = 1; break;
+	case AFMT_MU_LAW:	ad_fmt = 2; break;
+	case AFMT_A_LAW:	ad_fmt = 3; break;
+	default:		ASSERT(0);
+	}
+
+	switch (channels) {
+	case 2:			ad_mode = 0; break;
+	case 1:			ad_mode = 1; break;
+	default:		ASSERT(0);
+	}
+		
+	DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt);
+	ASSERT(framerate >= 4000 && framerate <= 49000);
+	ad1843_write_bits(lith, &ad1843_C1C, framerate);
+	ad1843_write_multi(lith, 2,
+			   &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt);
+}
+
+static void ad1843_shutdown_dac(lithium_t *lith)
+{
+	ad1843_write_bits(lith, &ad1843_DA1F, 1);
+}
+
+static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels)
+{
+	int da_fmt = 0;
+
+	DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
+	      lith, framerate, fmt, channels);
+
+	switch (fmt) {
+	case AFMT_S8:		da_fmt = 1; break;
+	case AFMT_U8:		da_fmt = 1; break;
+	case AFMT_S16_LE:	da_fmt = 1; break;
+	case AFMT_MU_LAW:	da_fmt = 2; break;
+	case AFMT_A_LAW:	da_fmt = 3; break;
+	default:		ASSERT(0);
+	}
+
+	DBGPV("da_fmt = %d\n", da_fmt);
+	ASSERT(framerate >= 4000 && framerate <= 49000);
+	ad1843_write_bits(lith, &ad1843_C2C, framerate);
+	ad1843_write_multi(lith, 2,
+			   &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+static void ad1843_shutdown_adc(lithium_t *lith)
+{
+	/* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843.  As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE".  The numbered comments are
+ * subsection headings from the data sheet.  See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure.  */
+
+static int __init ad1843_init(lithium_t *lith)
+{
+	unsigned long later;
+	int err;
+
+	err = li_init(lith);
+	if (err)
+		return err;
+
+	if (ad1843_read_bits(lith, &ad1843_INIT) != 0) {
+		printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n");
+		return -EIO;
+	}
+
+	ad1843_write_bits(lith, &ad1843_SCF, 1);
+
+	/* 4. Put the conversion resources into standby. */
+
+	ad1843_write_bits(lith, &ad1843_PDNI, 0);
+	later = jiffies + HZ / 2;	/* roughly half a second */
+	DBGDO(shut_up++);
+	while (ad1843_read_bits(lith, &ad1843_PDNO)) {
+		if (jiffies > later) {
+			printk(KERN_ERR
+			       "vwsnd audio: AD1843 won't power up\n");
+			return -EIO;
+		}
+		schedule();
+	}
+	DBGDO(shut_up--);
+
+	/* 5. Power up the clock generators and enable clock output pins. */
+
+	ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1);
+
+	/* 6. Configure conversion resources while they are in standby. */
+
+ 	/* DAC1 uses clock 1 as source, ADC uses clock 2.  Always. */
+
+	ad1843_write_multi(lith, 3,
+			   &ad1843_DA1C, 1,
+			   &ad1843_ADLC, 2,
+			   &ad1843_ADRC, 2);
+
+	/* 7. Enable conversion resources. */
+
+	ad1843_write_bits(lith, &ad1843_ADTLK, 1);
+	ad1843_write_multi(lith, 5,
+			   &ad1843_ANAEN, 1,
+			   &ad1843_AAMEN, 1,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* 8. Configure conversion resources while they are enabled. */
+
+	ad1843_write_bits(lith, &ad1843_DA1C, 1);
+
+	/* Unmute all channels. */
+
+	ad1843_set_outsrc(lith,
+			  (SOUND_MASK_PCM | SOUND_MASK_LINE |
+			   SOUND_MASK_MIC | SOUND_MASK_CD));
+	ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);
+
+	/* Set default recording source to Line In and set
+	 * mic gain to +20 dB.
+	 */
+
+	ad1843_set_recsrc(lith, SOUND_MASK_LINE);
+	ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+	/* Set Speaker Out level to +/- 4V and unmute it. */
+
+	ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* PCM I/O */
+
+#define READ_INTR_MASK  (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)
+#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)
+
+typedef enum vwsnd_port_swstate {	/* software state */
+	SW_OFF,
+	SW_INITIAL,
+	SW_RUN,
+	SW_DRAIN,
+} vwsnd_port_swstate_t;
+
+typedef enum vwsnd_port_hwstate {	/* hardware state */
+	HW_STOPPED,
+	HW_RUNNING,
+} vwsnd_port_hwstate_t;
+
+/*
+ * These flags are read by ISR, but only written at baseline.
+ */
+
+typedef enum vwsnd_port_flags {
+	DISABLED = 1 << 0,
+	ERFLOWN  = 1 << 1,		/* overflown or underflown */
+	HW_BUSY  = 1 << 2,
+} vwsnd_port_flags_t;
+
+/*
+ * vwsnd_port is the per-port data structure.  Each device has two
+ * ports, one for input and one for output.
+ *
+ * Locking:
+ *
+ *	port->lock protects: hwstate, flags, swb_[iu]_avail.
+ *
+ *	devc->io_sema protects: swstate, sw_*, swb_[iu]_idx.
+ *
+ *	everything else is only written by open/release or
+ *	pcm_{setup,shutdown}(), which are serialized by a
+ *	combination of devc->open_sema and devc->io_sema.
+ */
+
+typedef struct vwsnd_port {
+
+	spinlock_t	lock;
+	wait_queue_head_t queue;
+	vwsnd_port_swstate_t swstate;
+	vwsnd_port_hwstate_t hwstate;
+	vwsnd_port_flags_t flags;
+
+	int		sw_channels;
+	int		sw_samplefmt;
+	int		sw_framerate;
+	int		sample_size;
+	int		frame_size;
+	unsigned int	zero_word;	/* zero for the sample format */
+
+	int		sw_fragshift;
+	int		sw_fragcount;
+	int		sw_subdivshift;
+
+	unsigned int	hw_fragshift;
+	unsigned int	hw_fragsize;
+	unsigned int	hw_fragcount;
+
+	int		hwbuf_size;
+	unsigned long	hwbuf_paddr;
+	unsigned long	hwbuf_vaddr;
+	caddr_t		hwbuf;		/* hwbuf == hwbuf_vaddr */
+	int		hwbuf_max;	/* max bytes to preload */
+
+	caddr_t		swbuf;
+	unsigned int	swbuf_size;	/* size in bytes */
+	unsigned int	swb_u_idx;	/* index of next user byte */
+	unsigned int	swb_i_idx;	/* index of next intr byte */
+	unsigned int	swb_u_avail;	/* # bytes avail to user */
+	unsigned int	swb_i_avail;	/* # bytes avail to intr */
+
+	dma_chan_t	chan;
+
+	/* Accounting */
+
+	int		byte_count;
+	int		frag_count;
+	int		MSC_offset;
+
+} vwsnd_port_t;
+
+/* vwsnd_dev is the per-device data structure. */
+
+typedef struct vwsnd_dev {
+	struct vwsnd_dev *next_dev;
+	int		audio_minor;	/* minor number of audio device */
+	int		mixer_minor;	/* minor number of mixer device */
+
+	struct semaphore open_sema;
+	struct semaphore io_sema;
+	struct semaphore mix_sema;
+	mode_t		open_mode;
+	wait_queue_head_t open_wait;
+
+	lithium_t	lith;
+
+	vwsnd_port_t	rport;
+	vwsnd_port_t	wport;
+} vwsnd_dev_t;
+
+static vwsnd_dev_t *vwsnd_dev_list;	/* linked list of all devices */
+
+static atomic_t vwsnd_use_count = ATOMIC_INIT(0);
+
+# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))
+# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))
+# define IN_USE        (atomic_read(&vwsnd_use_count) != 0)
+
+/*
+ * Lithium can only DMA multiples of 32 bytes.  Its DMA buffer may
+ * be up to 8 Kb.  This driver always uses 8 Kb.
+ *
+ * Memory bug workaround -- I'm not sure what's going on here, but
+ * somehow pcm_copy_out() was triggering segv's going on to the next
+ * page of the hw buffer.  So, I make the hw buffer one size bigger
+ * than we actually use.  That way, the following page is allocated
+ * and mapped, and no error.  I suspect that something is broken
+ * in Cobalt, but haven't really investigated.  HBO is the actual
+ * size of the buffer, and HWBUF_ORDER is what we allocate.
+ */
+
+#define HWBUF_SHIFT 13
+#define HWBUF_SIZE (1 << HWBUF_SHIFT)
+# define HBO         (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)
+# define HWBUF_ORDER (HBO + 1)		/* next size bigger */
+#define MIN_SPEED 4000
+#define MAX_SPEED 49000
+
+#define MIN_FRAGSHIFT			(DMACHUNK_SHIFT + 1)
+#define MAX_FRAGSHIFT			(PAGE_SHIFT)
+#define MIN_FRAGSIZE			(1 << MIN_FRAGSHIFT)
+#define MAX_FRAGSIZE			(1 << MAX_FRAGSHIFT)
+#define MIN_FRAGCOUNT(fragsize)		3
+#define MAX_FRAGCOUNT(fragsize)		(32 * PAGE_SIZE / (fragsize))
+#define DEFAULT_FRAGSHIFT		12
+#define DEFAULT_FRAGCOUNT		16
+#define DEFAULT_SUBDIVSHIFT		0
+
+/*
+ * The software buffer (swbuf) is a ring buffer shared between user
+ * level and interrupt level.  Each level owns some of the bytes in
+ * the buffer, and may give bytes away by calling swb_inc_{u,i}().
+ * User level calls _u for user, and interrupt level calls _i for
+ * interrupt.
+ *
+ * port->swb_{u,i}_avail is the number of bytes available to that level.
+ *
+ * port->swb_{u,i}_idx is the index of the first available byte in the
+ * buffer.
+ *
+ * Each level calls swb_inc_{u,i}() to atomically increment its index,
+ * recalculate the number of bytes available for both sides, and
+ * return the number of bytes available.  Since each side can only
+ * give away bytes, the other side can only increase the number of
+ * bytes available to this side.  Each side updates its own index
+ * variable, swb_{u,i}_idx, so no lock is needed to read it.
+ *
+ * To query the number of bytes available, call swb_inc_{u,i} with an
+ * increment of zero.
+ */
+
+static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc)
+{
+	if (inc) {
+		port->swb_u_idx += inc;
+		port->swb_u_idx %= port->swbuf_size;
+		port->swb_u_avail -= inc;
+		port->swb_i_avail += inc;
+	}
+	return port->swb_u_avail;
+}
+
+static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	{
+		ret = __swb_inc_u(port, inc);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc)
+{
+	if (inc) {
+		port->swb_i_idx += inc;
+		port->swb_i_idx %= port->swbuf_size;
+		port->swb_i_avail -= inc;
+		port->swb_u_avail += inc;
+	}
+	return port->swb_i_avail;
+}
+
+static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	{
+		ret = __swb_inc_i(port, inc);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+/*
+ * pcm_setup - this routine initializes all port state after
+ * mode-setting ioctls have been done, but before the first I/O is
+ * done.
+ *
+ * Locking: called with devc->io_sema held.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int pcm_setup(vwsnd_dev_t *devc,
+		     vwsnd_port_t *rport,
+		     vwsnd_port_t *wport)
+{
+	vwsnd_port_t *aport = rport ? rport : wport;
+	int sample_size;
+	unsigned int zero_word;
+
+	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
+
+	ASSERT(aport != NULL);
+	if (aport->swbuf != NULL)
+		return 0;
+	switch (aport->sw_samplefmt) {
+	case AFMT_MU_LAW:
+		sample_size = 1;
+		zero_word = 0xFFFFFFFF ^ 0x80808080;
+		break;
+
+	case AFMT_A_LAW:
+		sample_size = 1;
+		zero_word = 0xD5D5D5D5 ^ 0x80808080;
+		break;
+
+	case AFMT_U8:
+		sample_size = 1;
+		zero_word = 0x80808080;
+		break;
+
+	case AFMT_S8:
+		sample_size = 1;
+		zero_word = 0x00000000;
+		break;
+
+	case AFMT_S16_LE:
+		sample_size = 2;
+		zero_word = 0x00000000;
+		break;
+
+	default:
+		sample_size = 0;	/* prevent compiler warning */
+		zero_word = 0;
+		ASSERT(0);
+	}
+	aport->sample_size  = sample_size;
+	aport->zero_word    = zero_word;
+	aport->frame_size   = aport->sw_channels * aport->sample_size;
+	aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift;
+	aport->hw_fragsize  = 1 << aport->hw_fragshift;
+	aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift;
+	ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE);
+	ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE);
+	ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize));
+	ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize));
+	if (rport) {
+		int hwfrags, swfrags;
+		rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
+		hwfrags = rport->hwbuf_max >> aport->hw_fragshift;
+		swfrags = aport->hw_fragcount - hwfrags;
+		if (swfrags < 2)
+			swfrags = 2;
+		rport->swbuf_size = swfrags * aport->hw_fragsize;
+		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
+		DBGPV("read hwbuf_max = %d, swbuf_size = %d\n",
+		     rport->hwbuf_max, rport->swbuf_size);
+	}
+	if (wport) {
+		int hwfrags, swfrags;
+		int total_bytes = aport->hw_fragcount * aport->hw_fragsize;
+		wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
+		if (wport->hwbuf_max > total_bytes)
+			wport->hwbuf_max = total_bytes;
+		hwfrags = wport->hwbuf_max >> aport->hw_fragshift;
+		DBGPV("hwfrags = %d\n", hwfrags);
+		swfrags = aport->hw_fragcount - hwfrags;
+		if (swfrags < 2)
+			swfrags = 2;
+		wport->swbuf_size = swfrags * aport->hw_fragsize;
+		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
+		DBGPV("write hwbuf_max = %d, swbuf_size = %d\n",
+		     wport->hwbuf_max, wport->swbuf_size);
+	}
+
+	aport->swb_u_idx    = 0;
+	aport->swb_i_idx    = 0;
+	aport->byte_count   = 0;
+
+	/*
+	 * Is this a Cobalt bug?  We need to make this buffer extend
+	 * one page further than we actually use -- somehow memcpy
+	 * causes an exceptoin otherwise.  I suspect there's a bug in
+	 * Cobalt (or somewhere) where it's generating a fault on a
+	 * speculative load or something.  Obviously, I haven't taken
+	 * the time to track it down.
+	 */
+
+	aport->swbuf        = vmalloc(aport->swbuf_size + PAGE_SIZE);
+	if (!aport->swbuf)
+		return -ENOMEM;
+	if (rport && wport) {
+		ASSERT(aport == rport);
+		ASSERT(wport->swbuf == NULL);
+		/* One extra page - see comment above. */
+		wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
+		if (!wport->swbuf) {
+			vfree(aport->swbuf);
+			aport->swbuf = NULL;
+			return -ENOMEM;
+		}
+		wport->sample_size  = rport->sample_size;
+		wport->zero_word    = rport->zero_word;
+		wport->frame_size   = rport->frame_size;
+		wport->hw_fragshift = rport->hw_fragshift;
+		wport->hw_fragsize  = rport->hw_fragsize;
+		wport->hw_fragcount = rport->hw_fragcount;
+		wport->swbuf_size   = rport->swbuf_size;
+		wport->hwbuf_max    = rport->hwbuf_max;
+		wport->swb_u_idx    = rport->swb_u_idx;
+		wport->swb_i_idx    = rport->swb_i_idx;
+		wport->byte_count   = rport->byte_count;
+	}
+	if (rport) {
+		rport->swb_u_avail = 0;
+		rport->swb_i_avail = rport->swbuf_size;
+		rport->swstate = SW_RUN;
+		li_setup_dma(&rport->chan,
+			     &li_comm1,
+			     &devc->lith,
+			     rport->hwbuf_paddr,
+			     HWBUF_SHIFT,
+			     rport->hw_fragshift,
+			     rport->sw_channels,
+			     rport->sample_size);
+		ad1843_setup_adc(&devc->lith,
+				 rport->sw_framerate,
+				 rport->sw_samplefmt,
+				 rport->sw_channels);
+		li_enable_interrupts(&devc->lith, READ_INTR_MASK);
+		if (!(rport->flags & DISABLED)) {
+			ustmsc_t ustmsc;
+			rport->hwstate = HW_RUNNING;
+			li_activate_dma(&rport->chan);
+			li_read_USTMSC(&rport->chan, &ustmsc);
+			rport->MSC_offset = ustmsc.msc;
+		}
+	}
+	if (wport) {
+		if (wport->hwbuf_max > wport->swbuf_size)
+			wport->hwbuf_max = wport->swbuf_size;
+		wport->flags &= ~ERFLOWN;
+		wport->swb_u_avail = wport->swbuf_size;
+		wport->swb_i_avail = 0;
+		wport->swstate = SW_RUN;
+		li_setup_dma(&wport->chan,
+			     &li_comm2,
+			     &devc->lith,
+			     wport->hwbuf_paddr,
+			     HWBUF_SHIFT,
+			     wport->hw_fragshift,
+			     wport->sw_channels,
+			     wport->sample_size);
+		ad1843_setup_dac(&devc->lith,
+				 wport->sw_framerate,
+				 wport->sw_samplefmt,
+				 wport->sw_channels);
+		li_enable_interrupts(&devc->lith, WRITE_INTR_MASK);
+	}
+	DBGRV();
+	return 0;
+}
+
+/*
+ * pcm_shutdown_port - shut down one port (direction) for PCM I/O.
+ * Only called from pcm_shutdown.
+ */
+
+static void pcm_shutdown_port(vwsnd_dev_t *devc,
+			      vwsnd_port_t *aport,
+			      unsigned int mask)
+{
+	unsigned long flags;
+	vwsnd_port_hwstate_t hwstate;
+	DECLARE_WAITQUEUE(wait, current);
+
+	aport->swstate = SW_INITIAL;
+	add_wait_queue(&aport->queue, &wait);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		spin_lock_irqsave(&aport->lock, flags);
+		{
+			hwstate = aport->hwstate;
+		}		
+		spin_unlock_irqrestore(&aport->lock, flags);
+		if (hwstate == HW_STOPPED)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&aport->queue, &wait);
+	li_disable_interrupts(&devc->lith, mask);
+	if (aport == &devc->rport)
+		ad1843_shutdown_adc(&devc->lith);
+	else /* aport == &devc->wport) */
+		ad1843_shutdown_dac(&devc->lith);
+	li_shutdown_dma(&aport->chan);
+	vfree(aport->swbuf);
+	aport->swbuf = NULL;
+	aport->byte_count = 0;
+}
+
+/*
+ * pcm_shutdown undoes what pcm_setup did.
+ * Also sets the ports' swstate to newstate.
+ */
+
+static void pcm_shutdown(vwsnd_dev_t *devc,
+			 vwsnd_port_t *rport,
+			 vwsnd_port_t *wport)
+{
+	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
+
+	if (rport && rport->swbuf) {
+		DBGPV("shutting down rport\n");
+		pcm_shutdown_port(devc, rport, READ_INTR_MASK);
+	}
+	if (wport && wport->swbuf) {
+		DBGPV("shutting down wport\n");
+		pcm_shutdown_port(devc, wport, WRITE_INTR_MASK);
+	}
+	DBGRV();
+}
+
+static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb)
+{
+	char *src = rport->hwbuf + hwidx;
+	char *dst = rport->swbuf + swidx;
+	int fmt = rport->sw_samplefmt;
+
+	DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx);
+	ASSERT(rport->hwbuf != NULL);
+	ASSERT(rport->swbuf != NULL);
+	ASSERT(nb > 0 && (nb % 32) == 0);
+	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
+	ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size);
+	ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size);
+
+	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
+
+		/* See Sample Format Notes above. */
+
+		char *end = src + nb;
+		while (src < end)
+			*dst++ = *src++ ^ 0x80;
+	} else
+		memcpy(dst, src, nb);
+}
+
+static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb)
+{
+	char *src = wport->swbuf + swidx;
+	char *dst = wport->hwbuf + hwidx;
+	int fmt = wport->sw_samplefmt;
+
+	ASSERT(nb > 0 && (nb % 32) == 0);
+	ASSERT(wport->hwbuf != NULL);
+	ASSERT(wport->swbuf != NULL);
+	ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
+	ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size);
+	ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size);
+	if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
+
+		/* See Sample Format Notes above. */
+
+		char *end = src + nb;
+		while (src < end)
+			*dst++ = *src++ ^ 0x80;
+	} else
+		memcpy(dst, src, nb);
+}
+
+/*
+ * pcm_output() is called both from baselevel and from interrupt level.
+ * This is where audio frames are copied into the hardware-accessible
+ * ring buffer.
+ *
+ * Locking note: The part of this routine that figures out what to do
+ * holds wport->lock.  The longer part releases wport->lock, but sets
+ * wport->flags & HW_BUSY.  Afterward, it reacquires wport->lock, and
+ * checks for more work to do.
+ *
+ * If another thread calls pcm_output() while HW_BUSY is set, it
+ * returns immediately, knowing that the thread that set HW_BUSY will
+ * look for more work to do before returning.
+ *
+ * This has the advantage that port->lock is held for several short
+ * periods instead of one long period.  Also, when pcm_output is
+ * called from base level, it reenables interrupts.
+ */
+
+static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb)
+{
+	vwsnd_port_t *wport = &devc->wport;
+	const int hwmax  = wport->hwbuf_max;
+	const int hwsize = wport->hwbuf_size;
+	const int swsize = wport->swbuf_size;
+	const int fragsize = wport->hw_fragsize;
+	unsigned long iflags;
+
+	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
+	spin_lock_irqsave(&wport->lock, iflags);
+	if (erflown)
+		wport->flags |= ERFLOWN;
+	(void) __swb_inc_u(wport, nb);
+	if (wport->flags & HW_BUSY) {
+		spin_unlock_irqrestore(&wport->lock, iflags);
+		DBGPV("returning: HW BUSY\n");
+		return;
+	}
+	if (wport->flags & DISABLED) {
+		spin_unlock_irqrestore(&wport->lock, iflags);
+		DBGPV("returning: DISABLED\n");
+		return;
+	}
+	wport->flags |= HW_BUSY;
+	while (1) {
+		int swptr, hwptr, hw_avail, sw_avail, swidx;
+		vwsnd_port_hwstate_t hwstate = wport->hwstate;
+		vwsnd_port_swstate_t swstate = wport->swstate;
+		int hw_unavail;
+		ustmsc_t ustmsc;
+
+		hwptr = li_read_hwptr(&wport->chan);
+		swptr = li_read_swptr(&wport->chan);
+		hw_unavail = (swptr - hwptr + hwsize) % hwsize;
+		hw_avail = (hwmax - hw_unavail) & -fragsize;
+		sw_avail = wport->swb_i_avail & -fragsize;
+		if (sw_avail && swstate == SW_RUN) {
+			if (wport->flags & ERFLOWN) {
+				wport->flags &= ~ERFLOWN;
+			}
+		} else if (swstate == SW_INITIAL ||
+			 swstate == SW_OFF ||
+			 (swstate == SW_DRAIN &&
+			  !sw_avail &&
+			  (wport->flags & ERFLOWN))) {
+			DBGP("stopping.  hwstate = %d\n", hwstate);
+			if (hwstate != HW_STOPPED) {
+				li_deactivate_dma(&wport->chan);
+				wport->hwstate = HW_STOPPED;
+			}
+			wake_up(&wport->queue);
+			break;
+		}
+		if (!sw_avail || !hw_avail)
+			break;
+		spin_unlock_irqrestore(&wport->lock, iflags);
+
+		/*
+		 * We gave up the port lock, but we have the HW_BUSY flag.
+		 * Proceed without accessing any nonlocal state.
+		 * Do not exit the loop -- must check for more work.
+		 */
+
+		swidx = wport->swb_i_idx;
+		nb = hw_avail;
+		if (nb > sw_avail)
+			nb = sw_avail;
+		if (nb > hwsize - swptr)
+			nb = hwsize - swptr; /* don't overflow hwbuf */
+		if (nb > swsize - swidx)
+			nb = swsize - swidx; /* don't overflow swbuf */
+		ASSERT(nb > 0);
+		if (nb % fragsize) {
+			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
+			DBGP("hw_avail = %d\n", hw_avail);
+			DBGP("sw_avail = %d\n", sw_avail);
+			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
+			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
+		}
+		ASSERT(!(nb % fragsize));
+		DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n",
+		      swidx, swidx + nb, swptr, swptr + nb);
+		pcm_copy_out(wport, swidx, swptr, nb);
+		li_write_swptr(&wport->chan, (swptr + nb) % hwsize);
+		spin_lock_irqsave(&wport->lock, iflags);
+		if (hwstate == HW_STOPPED) {
+			DBGPV("starting\n");
+			li_activate_dma(&wport->chan);
+			wport->hwstate = HW_RUNNING;
+			li_read_USTMSC(&wport->chan, &ustmsc);
+			ASSERT(wport->byte_count % wport->frame_size == 0);
+			wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size;
+		}
+		__swb_inc_i(wport, nb);
+		wport->byte_count += nb;
+		wport->frag_count += nb / fragsize;
+		ASSERT(nb % fragsize == 0);
+		wake_up(&wport->queue);
+	}
+	wport->flags &= ~HW_BUSY;
+	spin_unlock_irqrestore(&wport->lock, iflags);
+	DBGRV();
+}
+
+/*
+ * pcm_input() is called both from baselevel and from interrupt level.
+ * This is where audio frames are copied out of the hardware-accessible
+ * ring buffer.
+ *
+ * Locking note: The part of this routine that figures out what to do
+ * holds rport->lock.  The longer part releases rport->lock, but sets
+ * rport->flags & HW_BUSY.  Afterward, it reacquires rport->lock, and
+ * checks for more work to do.
+ *
+ * If another thread calls pcm_input() while HW_BUSY is set, it
+ * returns immediately, knowing that the thread that set HW_BUSY will
+ * look for more work to do before returning.
+ *
+ * This has the advantage that port->lock is held for several short
+ * periods instead of one long period.  Also, when pcm_input is
+ * called from base level, it reenables interrupts.
+ */
+
+static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb)
+{
+	vwsnd_port_t *rport = &devc->rport;
+	const int hwmax  = rport->hwbuf_max;
+	const int hwsize = rport->hwbuf_size;
+	const int swsize = rport->swbuf_size;
+	const int fragsize = rport->hw_fragsize;
+	unsigned long iflags;
+
+	DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
+
+	spin_lock_irqsave(&rport->lock, iflags);
+	if (erflown)
+		rport->flags |= ERFLOWN;
+	(void) __swb_inc_u(rport, nb);
+	if (rport->flags & HW_BUSY || !rport->swbuf) {
+		spin_unlock_irqrestore(&rport->lock, iflags);
+		DBGPV("returning: HW BUSY or !swbuf\n");
+		return;
+	}
+	if (rport->flags & DISABLED) {
+		spin_unlock_irqrestore(&rport->lock, iflags);
+		DBGPV("returning: DISABLED\n");
+		return;
+	}
+	rport->flags |= HW_BUSY;
+	while (1) {
+		int swptr, hwptr, hw_avail, sw_avail, swidx;
+		vwsnd_port_hwstate_t hwstate = rport->hwstate;
+		vwsnd_port_swstate_t swstate = rport->swstate;
+
+		hwptr = li_read_hwptr(&rport->chan);
+		swptr = li_read_swptr(&rport->chan);
+		hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize;
+		if (hw_avail > hwmax)
+			hw_avail = hwmax;
+		sw_avail = rport->swb_i_avail & -fragsize;
+		if (swstate != SW_RUN) {
+			DBGP("stopping.  hwstate = %d\n", hwstate);
+			if (hwstate != HW_STOPPED) {
+				li_deactivate_dma(&rport->chan);
+				rport->hwstate = HW_STOPPED;
+			}
+			wake_up(&rport->queue);
+			break;
+		}
+		if (!sw_avail || !hw_avail)
+			break;
+		spin_unlock_irqrestore(&rport->lock, iflags);
+
+		/*
+		 * We gave up the port lock, but we have the HW_BUSY flag.
+		 * Proceed without accessing any nonlocal state.
+		 * Do not exit the loop -- must check for more work.
+		 */
+
+		swidx = rport->swb_i_idx;
+		nb = hw_avail;
+		if (nb > sw_avail)
+			nb = sw_avail;
+		if (nb > hwsize - swptr)
+			nb = hwsize - swptr; /* don't overflow hwbuf */
+		if (nb > swsize - swidx)
+			nb = swsize - swidx; /* don't overflow swbuf */
+		ASSERT(nb > 0);
+		if (nb % fragsize) {
+			DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
+			DBGP("hw_avail = %d\n", hw_avail);
+			DBGP("sw_avail = %d\n", sw_avail);
+			DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
+			DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
+		}
+		ASSERT(!(nb % fragsize));
+		DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n",
+		      swptr, swptr + nb, swidx, swidx + nb);
+		pcm_copy_in(rport, swidx, swptr, nb);
+		li_write_swptr(&rport->chan, (swptr + nb) % hwsize);
+		spin_lock_irqsave(&rport->lock, iflags);
+		__swb_inc_i(rport, nb);
+		rport->byte_count += nb;
+		rport->frag_count += nb / fragsize;
+		ASSERT(nb % fragsize == 0);
+		wake_up(&rport->queue);
+	}
+	rport->flags &= ~HW_BUSY;
+	spin_unlock_irqrestore(&rport->lock, iflags);
+	DBGRV();
+}
+
+/*
+ * pcm_flush_frag() writes zero samples to fill the current fragment,
+ * then flushes it to the hardware.
+ *
+ * It is only meaningful to flush output, not input.
+ */
+
+static void pcm_flush_frag(vwsnd_dev_t *devc)
+{
+	vwsnd_port_t *wport = &devc->wport;
+
+	DBGPV("swstate = %d\n", wport->swstate);
+	if (wport->swstate == SW_RUN) {
+		int idx = wport->swb_u_idx;
+		int end = (idx + wport->hw_fragsize - 1)
+			>> wport->hw_fragshift
+			<< wport->hw_fragshift;
+		int nb = end - idx;
+		DBGPV("clearing %d bytes\n", nb);
+		if (nb)
+			memset(wport->swbuf + idx,
+			       (char) wport->zero_word,
+			       nb);
+		wport->swstate = SW_DRAIN;
+		pcm_output(devc, 0, nb);
+	}
+	DBGRV();
+}
+
+/*
+ * Wait for output to drain.  This sleeps uninterruptibly because
+ * there is nothing intelligent we can do if interrupted.  This
+ * means the process will be delayed in responding to the signal.
+ */
+
+static void pcm_write_sync(vwsnd_dev_t *devc)
+{
+	vwsnd_port_t *wport = &devc->wport;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	vwsnd_port_hwstate_t hwstate;
+
+	DBGEV("(devc=0x%p)\n", devc);
+	add_wait_queue(&wport->queue, &wait);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			hwstate = wport->hwstate;
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		if (hwstate == HW_STOPPED)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&wport->queue, &wait);
+	DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate);
+	DBGRV();
+}
+
+/*****************************************************************************/
+/* audio driver */
+
+/*
+ * seek on an audio device always fails.
+ */
+
+static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status)
+{
+	int overflown = status & LI_INTR_COMM1_OVERFLOW;
+
+	if (status & READ_INTR_MASK)
+		pcm_input(devc, overflown, 0);
+}
+
+static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status)
+{
+	int underflown = status & LI_INTR_COMM2_UNDERFLOW;
+
+	if (status & WRITE_INTR_MASK)
+		pcm_output(devc, underflown, 0);
+}
+
+static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id;
+	unsigned int status;
+
+	DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs);
+
+	status = li_get_clear_intr_status(&devc->lith);
+	vwsnd_audio_read_intr(devc, status);
+	vwsnd_audio_write_intr(devc, status);
+}
+
+static ssize_t vwsnd_audio_do_read(struct file *file,
+				   char *buffer,
+				   size_t count,
+				   loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ?
+			       &devc->rport : NULL);
+	int ret, nb;
+
+	DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
+	     file, buffer, count, ppos);
+
+	if (!rport)
+		return -EINVAL;
+
+	if (rport->swbuf == NULL) {
+		vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+			&devc->wport : NULL;
+		ret = pcm_setup(devc, rport, wport);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count) {
+		DECLARE_WAITQUEUE(wait, current);
+		add_wait_queue(&rport->queue, &wait);
+		while ((nb = swb_inc_u(rport, 0)) == 0) {
+			DBGPV("blocking\n");
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (rport->flags & DISABLED ||
+			    file->f_flags & O_NONBLOCK) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&rport->queue, &wait);
+				return ret ? ret : -EAGAIN;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&rport->queue, &wait);
+				return ret ? ret : -ERESTARTSYS;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&rport->queue, &wait);
+		pcm_input(devc, 0, 0);
+		/* nb bytes are available in userbuf. */
+		if (nb > count)
+			nb = count;
+		DBGPV("nb = %d\n", nb);
+		copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb);
+		(void) swb_inc_u(rport, nb);
+		buffer += nb;
+		count -= nb;
+		ret += nb;
+	}
+	DBGPV("returning %d\n", ret);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_read(struct file *file,
+				char *buffer,
+				size_t count,
+				loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	ssize_t ret;
+
+	down(&devc->io_sema);
+	ret = vwsnd_audio_do_read(file, buffer, count, ppos);
+	up(&devc->io_sema);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_do_write(struct file *file,
+				    const char *buffer,
+				    size_t count,
+				    loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ?
+			       &devc->wport : NULL);
+	int ret, nb;
+
+	DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
+	      file, buffer, count, ppos);
+
+	if (!wport)
+		return -EINVAL;
+
+	if (wport->swbuf == NULL) {
+		vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+			&devc->rport : NULL;
+		ret = pcm_setup(devc, rport, wport);
+		if (ret < 0)
+			return ret;
+	}
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count) {
+		DECLARE_WAITQUEUE(wait, current);
+		add_wait_queue(&wport->queue, &wait);
+		while ((nb = swb_inc_u(wport, 0)) == 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (wport->flags & DISABLED ||
+			    file->f_flags & O_NONBLOCK) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&wport->queue, &wait);
+				return ret ? ret : -EAGAIN;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				current->state = TASK_RUNNING;
+				remove_wait_queue(&wport->queue, &wait);
+				return ret ? ret : -ERESTARTSYS;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&wport->queue, &wait);
+		/* nb bytes are available in userbuf. */
+		if (nb > count)
+			nb = count;
+		DBGPV("nb = %d\n", nb);
+		copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb);
+		pcm_output(devc, 0, nb);
+		buffer += nb;
+		count -= nb;
+		ret += nb;
+	}
+	DBGPV("returning %d\n", ret);
+	return ret;
+}
+
+static ssize_t vwsnd_audio_write(struct file *file,
+				 const char *buffer,
+				 size_t count,
+				 loff_t *ppos)
+{
+	vwsnd_dev_t *devc = file->private_data;
+	ssize_t ret;
+
+	down(&devc->io_sema);
+	ret = vwsnd_audio_do_write(file, buffer, count, ppos);
+	up(&devc->io_sema);
+	return ret;
+}
+
+/* No kernel lock - fine */
+static unsigned int vwsnd_audio_poll(struct file *file,
+				     struct poll_table_struct *wait)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+		&devc->rport : NULL;
+	vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+		&devc->wport : NULL;
+	unsigned int mask = 0;
+
+	DBGEV("(file=0x%p, wait=0x%p)\n", file, wait);
+
+	ASSERT(rport || wport);
+	if (rport) {
+		poll_wait(file, &rport->queue, wait);
+		if (swb_inc_u(rport, 0))
+			mask |= (POLLIN | POLLRDNORM);
+	}
+	if (wport) {
+		poll_wait(file, &wport->queue, wait);
+		if (wport->swbuf == NULL || swb_inc_u(wport, 0))
+			mask |= (POLLOUT | POLLWRNORM);
+	}
+
+	DBGPV("returning 0x%x\n", mask);
+	return mask;
+}
+
+static int vwsnd_audio_do_ioctl(struct inode *inode,
+				struct file *file,
+				unsigned int cmd,
+				unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
+		&devc->rport : NULL;
+	vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
+		&devc->wport : NULL;
+	vwsnd_port_t *aport = rport ? rport : wport;
+	struct audio_buf_info buf_info;
+	struct count_info info;
+	unsigned long flags;
+	int ival;
+
+	
+	DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n",
+	      inode, file, cmd, arg);
+	switch (cmd) {
+	case OSS_GETVERSION:		/* _SIOR ('M', 118, int) */
+		DBGX("OSS_GETVERSION\n");
+		ival = SOUND_VERSION;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETCAPS:	/* _SIOR ('P',15, int) */
+		DBGX("SNDCTL_DSP_GETCAPS\n");
+		ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETFMTS:	/* _SIOR ('P',11, int) */
+		DBGX("SNDCTL_DSP_GETFMTS\n");
+		ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW |
+			AFMT_U8 | AFMT_S8);
+		return put_user(ival, (int *) arg);
+		break;
+
+	case SOUND_PCM_READ_RATE:	/* _SIOR ('P', 2, int) */
+		DBGX("SOUND_PCM_READ_RATE\n");
+		ival = aport->sw_framerate;
+		return put_user(ival, (int *) arg);
+
+	case SOUND_PCM_READ_CHANNELS:	/* _SIOR ('P', 6, int) */
+		DBGX("SOUND_PCM_READ_CHANNELS\n");
+		ival = aport->sw_channels;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SPEED:		/* _SIOWR('P', 2, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SPEED %d\n", ival);
+		if (ival) {
+			if (aport->swstate != SW_INITIAL) {
+				DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n",
+				     aport->swstate);
+				return -EINVAL;
+			}
+			if (ival < MIN_SPEED)
+				ival = MIN_SPEED;
+			if (ival > MAX_SPEED)
+				ival = MAX_SPEED;
+			if (rport)
+				rport->sw_framerate = ival;
+			if (wport)
+				wport->sw_framerate = ival;
+		} else
+			ival = aport->sw_framerate;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_STEREO:		/* _SIOWR('P', 3, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_STEREO %d\n", ival);
+		if (ival != 0 && ival != 1)
+			return -EINVAL;
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		if (rport)
+			rport->sw_channels = ival + 1;
+		if (wport)
+			wport->sw_channels = ival + 1;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_CHANNELS:	/* _SIOWR('P', 6, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_CHANNELS %d\n", ival);
+		if (ival != 1 && ival != 2)
+			return -EINVAL;
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		if (rport)
+			rport->sw_channels = ival;
+		if (wport)
+			wport->sw_channels = ival;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETBLKSIZE:	/* _SIOWR('P', 4, int) */
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0) {
+			DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival);
+			return ival;
+		}
+		ival = 1 << aport->sw_fragshift;
+		DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:	/* _SIOWR('P',10, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n",
+		     ival >> 16, ival & 0xFFFF);
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		{
+			int sw_fragshift = ival & 0xFFFF;
+			int sw_subdivshift = aport->sw_subdivshift;
+			int hw_fragshift = sw_fragshift - sw_subdivshift;
+			int sw_fragcount = (ival >> 16) & 0xFFFF;
+			int hw_fragsize;
+			if (hw_fragshift < MIN_FRAGSHIFT)
+				hw_fragshift = MIN_FRAGSHIFT;
+			if (hw_fragshift > MAX_FRAGSHIFT)
+				hw_fragshift = MAX_FRAGSHIFT;
+			sw_fragshift = hw_fragshift + aport->sw_subdivshift;
+			hw_fragsize = 1 << hw_fragshift;
+			if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize))
+				sw_fragcount = MIN_FRAGCOUNT(hw_fragsize);
+			if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
+				sw_fragcount = MAX_FRAGCOUNT(hw_fragsize);
+			DBGPV("sw_fragshift = %d\n", sw_fragshift);
+			DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport);
+			if (rport) {
+				rport->sw_fragshift = sw_fragshift;
+				rport->sw_fragcount = sw_fragcount;
+			}
+			if (wport) {
+				wport->sw_fragshift = sw_fragshift;
+				wport->sw_fragcount = sw_fragcount;
+			}
+			ival = sw_fragcount << 16 | sw_fragshift;
+		}
+		DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n",
+		      ival >> 16, ival & 0xFFFF);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SUBDIVIDE:	/* _SIOWR('P', 9, int) */
+                if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival);
+		if (aport->swstate != SW_INITIAL)
+			return -EINVAL;
+		{
+			int subdivshift;
+			int hw_fragshift, hw_fragsize, hw_fragcount;
+			switch (ival) {
+			case 1: subdivshift = 0; break;
+			case 2: subdivshift = 1; break;
+			case 4: subdivshift = 2; break;
+			default: return -EINVAL;
+			}
+			hw_fragshift = aport->sw_fragshift - subdivshift;
+			if (hw_fragshift < MIN_FRAGSHIFT ||
+			    hw_fragshift > MAX_FRAGSHIFT)
+				return -EINVAL;
+			hw_fragsize = 1 << hw_fragshift;
+			hw_fragcount = aport->sw_fragcount >> subdivshift;
+			if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) ||
+			    hw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
+				return -EINVAL;
+			if (rport)
+				rport->sw_subdivshift = subdivshift;
+			if (wport)
+				wport->sw_subdivshift = subdivshift;
+		}
+		return 0;
+
+	case SNDCTL_DSP_SETFMT:		/* _SIOWR('P',5, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SETFMT %d\n", ival);
+		if (ival != AFMT_QUERY) {
+			if (aport->swstate != SW_INITIAL) {
+				DBGP("SETFMT failed, swstate = %d\n",
+				     aport->swstate);
+				return -EINVAL;
+			}
+			switch (ival) {
+			case AFMT_MU_LAW:
+			case AFMT_A_LAW:
+			case AFMT_U8:
+			case AFMT_S8:
+			case AFMT_S16_LE:
+				if (rport)
+					rport->sw_samplefmt = ival;
+				if (wport)
+					wport->sw_samplefmt = ival;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+		ival = aport->sw_samplefmt;
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_GETOSPACE:	/* _SIOR ('P',12, audio_buf_info) */
+		DBGXV("SNDCTL_DSP_GETOSPACE\n");
+		if (!wport)
+			return -EINVAL;
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0)
+			return ival;
+		ival = swb_inc_u(wport, 0);
+		buf_info.fragments = ival >> wport->sw_fragshift;
+		buf_info.fragstotal = wport->sw_fragcount;
+		buf_info.fragsize = 1 << wport->sw_fragshift;
+		buf_info.bytes = ival;
+		DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n",
+		     buf_info.fragments, buf_info.fragstotal,
+		     buf_info.fragsize, buf_info.bytes);
+		return copy_to_user((void *) arg, &buf_info, sizeof buf_info);
+
+	case SNDCTL_DSP_GETISPACE:	/* _SIOR ('P',13, audio_buf_info) */
+		DBGX("SNDCTL_DSP_GETISPACE\n");
+		if (!rport)
+			return -EINVAL;
+		ival = pcm_setup(devc, rport, wport);
+		if (ival < 0)
+			return ival;
+		ival = swb_inc_u(rport, 0);
+		buf_info.fragments = ival >> rport->sw_fragshift;
+		buf_info.fragstotal = rport->sw_fragcount;
+		buf_info.fragsize = 1 << rport->sw_fragshift;
+		buf_info.bytes = ival;
+		DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n",
+		     buf_info.fragments, buf_info.fragstotal,
+		     buf_info.fragsize, buf_info.bytes);
+		return copy_to_user((void *) arg, &buf_info, sizeof buf_info);
+
+	case SNDCTL_DSP_NONBLOCK:	/* _SIO  ('P',14) */
+		DBGX("SNDCTL_DSP_NONBLOCK\n");
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_RESET:		/* _SIO  ('P', 0) */
+		DBGX("SNDCTL_DSP_RESET\n");
+		/*
+		 * Nothing special needs to be done for input.  Input
+		 * samples sit in swbuf, but it will be reinitialized
+		 * to empty when pcm_setup() is called.
+		 */
+		if (wport && wport->swbuf) {
+			wport->swstate = SW_INITIAL;
+			pcm_output(devc, 0, 0);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		return 0;
+
+	case SNDCTL_DSP_SYNC:		/* _SIO  ('P', 1) */
+		DBGX("SNDCTL_DSP_SYNC\n");
+		if (wport) {
+			pcm_flush_frag(devc);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		return 0;
+
+	case SNDCTL_DSP_POST:		/* _SIO  ('P', 8) */
+		DBGX("SNDCTL_DSP_POST\n");
+		if (!wport)
+			return -EINVAL;
+		pcm_flush_frag(devc);
+		return 0;
+
+	case SNDCTL_DSP_GETIPTR:	/* _SIOR ('P', 17, count_info) */
+		DBGX("SNDCTL_DSP_GETIPTR\n");
+		if (!rport)
+			return -EINVAL;
+		spin_lock_irqsave(&rport->lock, flags);
+		{
+			ustmsc_t ustmsc;
+			if (rport->hwstate == HW_RUNNING) {
+				ASSERT(rport->swstate == SW_RUN);
+				li_read_USTMSC(&rport->chan, &ustmsc);
+				info.bytes = ustmsc.msc - rport->MSC_offset;
+				info.bytes *= rport->frame_size;
+			} else {
+				info.bytes = rport->byte_count;
+			}
+			info.blocks = rport->frag_count;
+			info.ptr = 0;	/* not implemented */
+			rport->frag_count = 0;
+		}
+		spin_unlock_irqrestore(&rport->lock, flags);
+		return copy_to_user((void *) arg, &info, sizeof info);
+
+	case SNDCTL_DSP_GETOPTR:	/* _SIOR ('P',18, count_info) */
+		DBGX("SNDCTL_DSP_GETOPTR\n");
+		if (!wport)
+			return -EINVAL;
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			ustmsc_t ustmsc;
+			if (wport->hwstate == HW_RUNNING) {
+				ASSERT(wport->swstate == SW_RUN);
+				li_read_USTMSC(&wport->chan, &ustmsc);
+				info.bytes = ustmsc.msc - wport->MSC_offset;
+				info.bytes *= wport->frame_size;
+			} else {
+				info.bytes = wport->byte_count;
+			}
+			info.blocks = wport->frag_count;
+			info.ptr = 0;	/* not implemented */
+			wport->frag_count = 0;
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		return copy_to_user((void *) arg, &info, sizeof info);
+
+	case SNDCTL_DSP_GETODELAY:	/* _SIOR ('P', 23, int) */
+		DBGX("SNDCTL_DSP_GETODELAY\n");
+		if (!wport)
+			return -EINVAL;
+		spin_lock_irqsave(&wport->lock, flags);
+		{
+			int fsize = wport->frame_size;
+			ival = wport->swb_i_avail / fsize;
+			if (wport->hwstate == HW_RUNNING) {
+				int swptr, hwptr, hwframes, hwbytes, hwsize;
+				int totalhwbytes;
+				ustmsc_t ustmsc;
+
+				hwsize = wport->hwbuf_size;
+				swptr = li_read_swptr(&wport->chan);
+				li_read_USTMSC(&wport->chan, &ustmsc);
+				hwframes = ustmsc.msc - wport->MSC_offset;
+				totalhwbytes = hwframes * fsize;
+				hwptr = totalhwbytes % hwsize;
+				hwbytes = (swptr - hwptr + hwsize) % hwsize;
+				ival += hwbytes / fsize;
+			}
+		}
+		spin_unlock_irqrestore(&wport->lock, flags);
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_PROFILE:	/* _SIOW ('P', 23, int) */
+		DBGX("SNDCTL_DSP_PROFILE\n");
+
+		/*
+		 * Thomas Sailer explains SNDCTL_DSP_PROFILE
+		 * (private email, March 24, 1999):
+		 *
+		 *     This gives the sound driver a hint on what it
+		 *     should do with partial fragments
+		 *     (i.e. fragments partially filled with write).
+		 *     This can direct the driver to zero them or
+		 *     leave them alone.  But don't ask me what this
+		 *     is good for, my driver just zeroes the last
+		 *     fragment before the receiver stops, no idea
+		 *     what good for any other behaviour could
+		 *     be. Implementing it as NOP seems safe.
+		 */
+
+		break;
+
+	case SNDCTL_DSP_GETTRIGGER:	/* _SIOR ('P',16, int) */
+		DBGX("SNDCTL_DSP_GETTRIGGER\n");
+		ival = 0;
+		if (rport) {
+			spin_lock_irqsave(&rport->lock, flags);
+			{
+				if (!(rport->flags & DISABLED))
+					ival |= PCM_ENABLE_INPUT;
+			}
+			spin_unlock_irqrestore(&rport->lock, flags);
+		}
+		if (wport) {
+			spin_lock_irqsave(&wport->lock, flags);
+			{
+				if (!(wport->flags & DISABLED))
+					ival |= PCM_ENABLE_OUTPUT;
+			}
+			spin_unlock_irqrestore(&wport->lock, flags);
+		}
+		return put_user(ival, (int *) arg);
+
+	case SNDCTL_DSP_SETTRIGGER:	/* _SIOW ('P',16, int) */
+		if (get_user(ival, (int *) arg))
+			return -EFAULT;
+		DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival);
+
+		/*
+		 * If user is disabling I/O and port is not in initial
+		 * state, fail with EINVAL.
+		 */
+
+		if (((rport && !(ival & PCM_ENABLE_INPUT)) ||
+		     (wport && !(ival & PCM_ENABLE_OUTPUT))) &&
+		    aport->swstate != SW_INITIAL)
+			return -EINVAL;
+
+		if (rport) {
+			vwsnd_port_hwstate_t hwstate;
+			spin_lock_irqsave(&rport->lock, flags);
+			{
+				hwstate = rport->hwstate;
+				if (ival & PCM_ENABLE_INPUT)
+					rport->flags &= ~DISABLED;
+				else
+					rport->flags |= DISABLED;
+			}
+			spin_unlock_irqrestore(&rport->lock, flags);
+			if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) {
+
+				if (rport->swstate == SW_INITIAL)
+					pcm_setup(devc, rport, wport);
+				else
+					li_activate_dma(&rport->chan);
+			}
+		}
+		if (wport) {
+			vwsnd_port_flags_t pflags;
+			spin_lock_irqsave(&wport->lock, flags);
+			{
+				pflags = wport->flags;
+				if (ival & PCM_ENABLE_OUTPUT)
+					wport->flags &= ~DISABLED;
+				else
+					wport->flags |= DISABLED;
+			}
+			spin_unlock_irqrestore(&wport->lock, flags);
+			if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) {
+				if (wport->swstate == SW_RUN)
+					pcm_output(devc, 0, 0);
+			}
+		}
+		return 0;
+
+	default:
+		DBGP("unknown ioctl 0x%x\n", cmd);
+		return -EINVAL;
+	}
+	DBGP("unimplemented ioctl 0x%x\n", cmd);
+	return -EINVAL;
+}
+
+static int vwsnd_audio_ioctl(struct inode *inode,
+				struct file *file,
+				unsigned int cmd,
+				unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	int ret;
+
+	down(&devc->io_sema);
+	ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg);
+	up(&devc->io_sema);
+	return ret;
+}
+
+/* No mmap. */
+
+static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	DBGE("(file=0x%p, vma=0x%p)\n", file, vma);
+	return -ENODEV;
+}
+
+/*
+ * Open the audio device for read and/or write.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+
+static int vwsnd_audio_open(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc;
+	dev_t minor = MINOR(inode->i_rdev);
+	int sw_samplefmt;
+
+	DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	INC_USE_COUNT;
+	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
+		if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
+			break;
+
+	if (devc == NULL) {
+		DEC_USE_COUNT;
+		return -ENODEV;
+	}
+
+	down(&devc->open_sema);
+	while (devc->open_mode & file->f_mode) {
+		up(&devc->open_sema);
+		if (file->f_flags & O_NONBLOCK) {
+			DEC_USE_COUNT;
+			return -EBUSY;
+		}
+		interruptible_sleep_on(&devc->open_wait);
+		if (signal_pending(current)) {
+			DEC_USE_COUNT;
+			return -ERESTARTSYS;
+		}
+		down(&devc->open_sema);
+	}
+	devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&devc->open_sema);
+
+	/* get default sample format from minor number. */
+
+	sw_samplefmt = 0;
+	if ((minor & 0xF) == SND_DEV_DSP)
+		sw_samplefmt = AFMT_U8;
+	else if ((minor & 0xF) == SND_DEV_AUDIO)
+		sw_samplefmt = AFMT_MU_LAW;
+	else if ((minor & 0xF) == SND_DEV_DSP16)
+		sw_samplefmt = AFMT_S16_LE;
+	else
+		ASSERT(0);
+
+	/* Initialize vwsnd_ports. */
+
+	down(&devc->io_sema);
+	{
+		if (file->f_mode & FMODE_READ) {
+			devc->rport.swstate        = SW_INITIAL;
+			devc->rport.flags          = 0;
+			devc->rport.sw_channels    = 1;
+			devc->rport.sw_samplefmt   = sw_samplefmt;
+			devc->rport.sw_framerate   = 8000;
+			devc->rport.sw_fragshift   = DEFAULT_FRAGSHIFT;
+			devc->rport.sw_fragcount   = DEFAULT_FRAGCOUNT;
+			devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
+			devc->rport.byte_count     = 0;
+			devc->rport.frag_count     = 0;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			devc->wport.swstate        = SW_INITIAL;
+			devc->wport.flags          = 0;
+			devc->wport.sw_channels    = 1;
+			devc->wport.sw_samplefmt   = sw_samplefmt;
+			devc->wport.sw_framerate   = 8000;
+			devc->wport.sw_fragshift   = DEFAULT_FRAGSHIFT;
+			devc->wport.sw_fragcount   = DEFAULT_FRAGCOUNT;
+			devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
+			devc->wport.byte_count     = 0;
+			devc->wport.frag_count     = 0;
+		}
+	}
+	up(&devc->io_sema);
+
+	file->private_data = devc;
+	DBGRV();
+	return 0;
+}
+
+/*
+ * Release (close) the audio device.
+ */
+
+static int vwsnd_audio_release(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	vwsnd_port_t *wport = NULL, *rport = NULL;
+	int err = 0;
+
+	lock_kernel();
+	down(&devc->io_sema);
+	{
+		DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+		if (file->f_mode & FMODE_READ)
+			rport = &devc->rport;
+		if (file->f_mode & FMODE_WRITE) {
+			wport = &devc->wport;
+			pcm_flush_frag(devc);
+			pcm_write_sync(devc);
+		}
+		pcm_shutdown(devc, rport, wport);
+		if (rport)
+			rport->swstate = SW_OFF;
+		if (wport)
+			wport->swstate = SW_OFF;
+	}
+	up(&devc->io_sema);
+
+	down(&devc->open_sema);
+	{
+		devc->open_mode &= ~file->f_mode;
+	}
+	up(&devc->open_sema);
+	wake_up(&devc->open_wait);
+	DEC_USE_COUNT;
+	DBGR();
+	unlock_kernel();
+	return err;
+}
+
+static struct file_operations vwsnd_audio_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		vwsnd_audio_read,
+	write:		vwsnd_audio_write,
+	poll:		vwsnd_audio_poll,
+	ioctl:		vwsnd_audio_ioctl,
+	mmap:		vwsnd_audio_mmap,
+	open:		vwsnd_audio_open,
+	release:	vwsnd_audio_release,
+};
+
+/*****************************************************************************/
+/* mixer driver */
+
+/* open the mixer device. */
+
+static int vwsnd_mixer_open(struct inode *inode, struct file *file)
+{
+	vwsnd_dev_t *devc;
+
+	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+
+	INC_USE_COUNT;
+	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
+		if (devc->mixer_minor == MINOR(inode->i_rdev))
+			break;
+
+	if (devc == NULL) {
+		DEC_USE_COUNT;
+		return -ENODEV;
+	}
+	file->private_data = devc;
+	return 0;
+}
+
+/* release (close) the mixer device. */
+
+static int vwsnd_mixer_release(struct inode *inode, struct file *file)
+{
+	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
+	DEC_USE_COUNT;
+	return 0;
+}
+
+/* mixer_read_ioctl handles all read ioctls on the mixer device. */
+
+static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg)
+{
+	int val = -1;
+
+	DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
+
+	switch (nr) {
+	case SOUND_MIXER_CAPS:
+		val = SOUND_CAP_EXCL_INPUT;
+		break;
+
+	case SOUND_MIXER_DEVMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
+		break;
+
+	case SOUND_MIXER_STEREODEVS:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
+		break;
+
+	case SOUND_MIXER_OUTMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD);
+		break;
+
+	case SOUND_MIXER_RECMASK:
+		val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
+		       SOUND_MASK_MIC | SOUND_MASK_CD);
+		break;
+
+	case SOUND_MIXER_PCM:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM);
+		break;
+
+	case SOUND_MIXER_LINE:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE);
+		break;
+
+	case SOUND_MIXER_MIC:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC);
+		break;
+
+	case SOUND_MIXER_CD:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD);
+		break;
+
+	case SOUND_MIXER_RECLEV:
+		val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV);
+		break;
+
+	case SOUND_MIXER_RECSRC:
+		val = ad1843_get_recsrc(&devc->lith);
+		break;
+
+	case SOUND_MIXER_OUTSRC:
+		val = ad1843_get_outsrc(&devc->lith);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return put_user(val, (int *) arg);
+}
+
+/* mixer_write_ioctl handles all write ioctls on the mixer device. */
+
+static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg)
+{
+	int val;
+	int err;
+
+	DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
+
+	err = get_user(val, (int *) arg);
+	if (err)
+		return -EFAULT;
+	switch (nr) {
+	case SOUND_MIXER_PCM:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val);
+		break;
+
+	case SOUND_MIXER_LINE:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val);
+		break;
+
+	case SOUND_MIXER_MIC:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val);
+		break;
+
+	case SOUND_MIXER_CD:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val);
+		break;
+
+	case SOUND_MIXER_RECLEV:
+		val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val);
+		break;
+
+	case SOUND_MIXER_RECSRC:
+		if (devc->rport.swbuf || devc->wport.swbuf)
+			return -EBUSY;	/* can't change recsrc while running */
+		val = ad1843_set_recsrc(&devc->lith, val);
+		break;
+
+	case SOUND_MIXER_OUTSRC:
+		val = ad1843_set_outsrc(&devc->lith, val);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (val < 0)
+		return val;
+	return put_user(val, (int *) arg);
+}
+
+/* This is the ioctl entry to the mixer driver. */
+
+static int vwsnd_mixer_ioctl(struct inode *ioctl,
+			      struct file *file,
+			      unsigned int cmd,
+			      unsigned long arg)
+{
+	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
+	const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT;
+	const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT;
+	int retval;
+
+	DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
+
+	down(&devc->mix_sema);
+	{
+		if ((cmd & ~nrmask) == MIXER_READ(0))
+			retval = mixer_read_ioctl(devc, nr, (caddr_t) arg);
+		else if ((cmd & ~nrmask) == MIXER_WRITE(0))
+			retval = mixer_write_ioctl(devc, nr, (caddr_t) arg);
+		else
+			retval = -EINVAL;
+	}
+	up(&devc->mix_sema);
+	return retval;
+}
+
+static struct file_operations vwsnd_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		vwsnd_mixer_ioctl,
+	open:		vwsnd_mixer_open,
+	release:	vwsnd_mixer_release,
+};
+
+/*****************************************************************************/
+/* probe/attach/unload */
+
+/* driver probe routine.  Return nonzero if hardware is found. */
+
+static int __init probe_vwsnd(struct address_info *hw_config)
+{
+	lithium_t lith;
+	int w;
+	unsigned long later;
+
+	DBGEV("(hw_config=0x%p)\n", hw_config);
+
+	/* XXX verify lithium present (to prevent crash on non-vw) */
+
+	if (li_create(&lith, hw_config->io_base) != 0) {
+		printk(KERN_WARNING "probe_vwsnd: can't map lithium\n");
+		return 0;
+	}
+	later = jiffies + 2;
+	li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
+	do {
+		w = li_readl(&lith, LI_HOST_CONTROLLER);
+	} while (w == LI_HC_LINK_ENABLE && jiffies < later);
+	
+	li_destroy(&lith);
+
+	DBGPV("HC = 0x%04x\n", w);
+
+	if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) {
+
+		/* This may indicate a beta machine with no audio,
+		 * or a future machine with different audio.
+		 * On beta-release 320 w/ no audio, HC == 0x4000 */
+
+		printk(KERN_WARNING "probe_vwsnd: audio codec not found\n");
+		return 0;
+	}
+
+	if (w & LI_HC_LINK_FAILURE) {
+		printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n");
+		return 0;
+	}
+
+	printk(KERN_INFO "probe_vwsnd: lithium audio found\n");
+
+	return 1;
+}
+
+/*
+ * driver attach routine.  Initialize driver data structures and
+ * initialize hardware.  A new vwsnd_dev_t is allocated and put
+ * onto the global list, vwsnd_dev_list.
+ *
+ * Return +minor_dev on success, -errno on failure.
+ */
+
+static int __init attach_vwsnd(struct address_info *hw_config)
+{
+	vwsnd_dev_t *devc = NULL;
+	int err = -ENOMEM;
+
+	DBGEV("(hw_config=0x%p)\n", hw_config);
+
+	devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL);
+	if (devc == NULL)
+		goto fail0;
+
+	err = li_create(&devc->lith, hw_config->io_base);
+	if (err)
+		goto fail1;
+
+	init_waitqueue(&devc->open_wait);
+
+	devc->rport.hwbuf_size = HWBUF_SIZE;
+	devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
+	if (!devc->rport.hwbuf_vaddr)
+		goto fail2;
+	devc->rport.hwbuf = (caddr_t) devc->rport.hwbuf_vaddr;
+	devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf);
+
+	/*
+	 * Quote from the NT driver:
+	 *
+	 * // WARNING!!! HACK to setup output dma!!!
+	 * // This is required because even on output there is some data
+	 * // trickling into the input DMA channel.  This is a bug in the
+	 * // Lithium microcode.
+	 * // --sde
+	 *
+	 * We set the input side's DMA base address here.  It will remain
+	 * valid until the driver is unloaded.
+	 */
+
+	li_writel(&devc->lith, LI_COMM1_BASE,
+		  devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8));
+
+	devc->wport.hwbuf_size = HWBUF_SIZE;
+	devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
+	if (!devc->wport.hwbuf_vaddr)
+		goto fail3;
+	devc->wport.hwbuf = (caddr_t) devc->wport.hwbuf_vaddr;
+	devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf);
+	DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf);
+
+	DBGDO(shut_up++);
+	err = ad1843_init(&devc->lith);
+	DBGDO(shut_up--);
+	if (err)
+		goto fail4;
+
+	/* install interrupt handler */
+
+	err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc);
+	if (err)
+		goto fail5;
+
+	/* register this device's drivers. */
+
+	devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1);
+	if ((err = devc->audio_minor) < 0) {
+		DBGDO(printk(KERN_WARNING
+			     "attach_vwsnd: register_sound_dsp error %d\n",
+			     err));
+		goto fail6;
+	}
+	devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops,
+						 devc->audio_minor >> 4);
+	if ((err = devc->mixer_minor) < 0) {
+		DBGDO(printk(KERN_WARNING
+			     "attach_vwsnd: register_sound_mixer error %d\n",
+			     err));
+		goto fail7;
+	}
+
+	/* Squirrel away device indices for unload routine. */
+
+	hw_config->slots[0] = devc->audio_minor;
+
+	/* Initialize as much of *devc as possible */
+
+	devc->open_sema = MUTEX;
+	devc->io_sema = MUTEX;
+	devc->mix_sema = MUTEX;
+	devc->open_mode = 0;
+	devc->rport.lock = SPIN_LOCK_UNLOCKED;
+	init_waitqueue(&devc->rport.queue);
+	devc->rport.swstate = SW_OFF;
+	devc->rport.hwstate = HW_STOPPED;
+	devc->rport.flags = 0;
+	devc->rport.swbuf = NULL;
+	devc->wport.lock = SPIN_LOCK_UNLOCKED;
+	init_waitqueue(&devc->wport.queue);
+	devc->wport.swstate = SW_OFF;
+	devc->wport.hwstate = HW_STOPPED;
+	devc->wport.flags = 0;
+	devc->wport.swbuf = NULL;
+
+	/* Success.  Link us onto the local device list. */
+
+	devc->next_dev = vwsnd_dev_list;
+	vwsnd_dev_list = devc;
+	return devc->audio_minor;
+
+	/* So many ways to fail.  Undo what we did. */
+
+ fail7:
+	unregister_sound_dsp(devc->audio_minor);
+ fail6:
+	free_irq(hw_config->irq, devc);
+ fail5:
+ fail4:
+	free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
+ fail3:
+	free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
+ fail2:
+	li_destroy(&devc->lith);
+ fail1:
+	kfree(devc);
+ fail0:
+	return err;
+}
+
+static int __exit unload_vwsnd(struct address_info *hw_config)
+{
+	vwsnd_dev_t *devc, **devcp;
+
+	DBGE("()\n");
+
+	devcp = &vwsnd_dev_list;
+	while ((devc = *devcp)) {
+		if (devc->audio_minor == hw_config->slots[0]) {
+			*devcp = devc->next_dev;
+			break;
+		}
+		devcp = &devc->next_dev;
+	}
+
+	if (!devc)
+		return -ENODEV;
+
+	unregister_sound_mixer(devc->mixer_minor);
+	unregister_sound_dsp(devc->audio_minor);
+	free_irq(hw_config->irq, devc);
+	free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
+	free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
+	li_destroy(&devc->lith);
+	kfree(devc);
+
+	return 0;
+}
+
+/*****************************************************************************/
+/* initialization and loadable kernel module interface */
+
+static struct address_info the_hw_config = {
+	0xFF001000,			/* lithium phys addr  */
+	CO_IRQ(CO_APIC_LI_AUDIO)	/* irq */
+};
+
+MODULE_DESCRIPTION("SGI Visual Workstation sound module");
+MODULE_AUTHOR("Bob Miller <kbob@sgi.com>");
+MODULE_LICENSE("GPL");
+
+static int __init init_vwsnd(void)
+{
+	int err;
+
+	DBGXV("\n");
+	DBGXV("sound::vwsnd::init_module()\n");
+
+	if(!probe_vwsnd(&the_hw_config))
+		return -ENODEV;
+	err = attach_vwsnd(&the_hw_config);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static void __exit cleanup_vwsnd(void)
+{
+	DBGX("sound::vwsnd::cleanup_module()\n");
+
+	unload_vwsnd(&the_hw_config);
+}
+
+module_init(init_vwsnd);
+module_exit(cleanup_vwsnd);
diff -Nru linux/sound/oss/waveartist.c linux-2.4.19-pre5-mjc/sound/oss/waveartist.c
--- linux/sound/oss/waveartist.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/waveartist.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2033 @@
+/*
+ * linux/drivers/sound/waveartist.c
+ *
+ * The low level driver for the RWA010 Rockwell Wave Artist
+ * codec chip used in the Rebel.com NetWinder.
+ *
+ * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
+ * and Pat Beirne (patb@corel.ca)
+ *
+ *
+ * Copyright (C) by Rebel.com 1998-1999
+ *
+ * RWA010 specs received under NDA from Rockwell
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added __init to waveartist_init()
+ */
+
+/* Debugging */
+#define DEBUG_CMD	1
+#define DEBUG_OUT	2
+#define DEBUG_IN	4
+#define DEBUG_INTR	8
+#define DEBUG_MIXER	16
+#define DEBUG_TRIGGER	32
+
+#define debug_flg (0)
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+
+#include "sound_config.h"
+#include "waveartist.h"
+
+#ifdef CONFIG_ARM
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#endif
+
+#ifndef NO_DMA
+#define NO_DMA	255
+#endif
+
+#define SUPPORTED_MIXER_DEVICES		(SOUND_MASK_SYNTH      |\
+					 SOUND_MASK_PCM        |\
+					 SOUND_MASK_LINE       |\
+					 SOUND_MASK_MIC        |\
+					 SOUND_MASK_LINE1      |\
+					 SOUND_MASK_RECLEV     |\
+					 SOUND_MASK_VOLUME     |\
+					 SOUND_MASK_IMIX)
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] = {
+	0x5555,		/* Master Volume	 */
+	0x0000,		/* Bass			 */
+	0x0000,		/* Treble		 */
+	0x2323,		/* Synth (FM)		 */
+	0x4b4b,		/* PCM			 */
+	0x6464,		/* PC Speaker		 */
+	0x0000,		/* Ext Line		 */
+	0x0000,		/* Mic			 */
+	0x0000,		/* CD			 */
+	0x6464,		/* Recording monitor	 */
+	0x0000,		/* SB PCM (ALT PCM)	 */
+	0x0000,		/* Recording level	 */
+	0x6464,		/* Input gain		 */
+	0x6464,		/* Output gain		 */
+	0x0000,		/* Line1 (Aux1)		 */
+	0x0000,		/* Line2 (Aux2)		 */
+	0x0000,		/* Line3 (Aux3)		 */
+	0x0000,		/* Digital1		 */
+	0x0000,		/* Digital2		 */
+	0x0000,		/* Digital3		 */
+	0x0000,		/* Phone In		 */
+	0x6464,		/* Phone Out		 */
+	0x0000,		/* Video		 */
+	0x0000,		/* Radio		 */
+	0x0000		/* Monitor		 */
+};
+
+typedef struct {
+	struct address_info  hw;	/* hardware */
+	char		*chip_name;
+
+	int		xfer_count;
+	int		audio_mode;
+	int		open_mode;
+	int		audio_flags;
+	int		record_dev;
+	int		playback_dev;
+	int		dev_no;
+
+	/* Mixer parameters */
+	const struct waveartist_mixer_info *mix;
+
+	unsigned short	*levels;	   /* cache of volume settings   */
+	int		recmask;	   /* currently enabled recording device! */
+
+#ifdef CONFIG_ARCH_NETWINDER
+	signed int	slider_vol;	   /* hardware slider volume     */
+	unsigned int	handset_detect	:1;
+	unsigned int	telephone_detect:1;
+	unsigned int	no_autoselect	:1;/* handset/telephone autoselects a path */
+	unsigned int	spkr_mute_state	:1;/* set by ioctl or autoselect */
+	unsigned int	line_mute_state	:1;/* set by ioctl or autoselect */
+	unsigned int	use_slider	:1;/* use slider setting for o/p vol */
+#endif
+} wavnc_info;
+
+/*
+ * This is the implementation specific mixer information.
+ */
+struct waveartist_mixer_info {
+	unsigned int	supported_devs;	   /* Supported devices */
+	unsigned int	recording_devs;	   /* Recordable devies */
+	unsigned int	stereo_devs;	   /* Stereo devices	*/
+
+	unsigned int	(*select_input)(wavnc_info *, unsigned int,
+					unsigned char *, unsigned char *);
+	int		(*decode_mixer)(wavnc_info *, int,
+					unsigned char, unsigned char);
+	int		(*get_mixer)(wavnc_info *, int);
+};
+
+typedef struct wavnc_port_info {
+	int		open_mode;
+	int		speed;
+	int		channels;
+	int		audio_format;
+} wavnc_port_info;
+
+static int		nr_waveartist_devs;
+static wavnc_info	adev_info[MAX_AUDIO_DEV];
+static spinlock_t	waveartist_lock = SPIN_LOCK_UNLOCKED;
+
+#ifndef CONFIG_ARCH_NETWINDER
+#define machine_is_netwinder() 0
+#else
+static struct timer_list vnc_timer;
+static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask);
+static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg);
+static void vnc_slider_tick(unsigned long data);
+#endif
+
+static inline void
+waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
+{
+	unsigned int ctlr_port = hw->io_base + CTLR;
+
+	clear = ~clear & inb(ctlr_port);
+
+	outb(clear | set, ctlr_port);
+}
+
+/* Toggle IRQ acknowledge line
+ */
+static inline void
+waveartist_iack(wavnc_info *devc)
+{
+	unsigned int ctlr_port = devc->hw.io_base + CTLR;
+	int old_ctlr;
+
+	old_ctlr = inb(ctlr_port) & ~IRQ_ACK;
+
+	outb(old_ctlr | IRQ_ACK, ctlr_port);
+	outb(old_ctlr, ctlr_port);
+}
+
+static inline int
+waveartist_sleep(int timeout_ms)
+{
+	unsigned int timeout = timeout_ms * 10 * HZ / 100;
+
+	do {
+		set_current_state(TASK_INTERRUPTIBLE);
+		timeout = schedule_timeout(timeout);
+	} while (timeout);
+
+	return 0;
+}
+
+static int
+waveartist_reset(wavnc_info *devc)
+{
+	struct address_info *hw = &devc->hw;
+	unsigned int timeout, res = -1;
+
+	waveartist_set_ctlr(hw, -1, RESET);
+	waveartist_sleep(2);
+	waveartist_set_ctlr(hw, RESET, 0);
+
+	timeout = 500;
+	do {
+		mdelay(2);
+
+		if (inb(hw->io_base + STATR) & CMD_RF) {
+			res = inw(hw->io_base + CMDR);
+			if (res == 0x55aa)
+				break;
+		}
+	} while (--timeout);
+
+	if (timeout == 0) {
+		printk(KERN_WARNING "WaveArtist: reset timeout ");
+		if (res != (unsigned int)-1)
+			printk("(res=%04X)", res);
+		printk("\n");
+		return 1;
+	}
+	return 0;
+}
+
+/* Helper function to send and receive words
+ * from WaveArtist.  It handles all the handshaking
+ * and can send or receive multiple words.
+ */
+static int
+waveartist_cmd(wavnc_info *devc,
+		int nr_cmd, unsigned int *cmd,
+		int nr_resp, unsigned int *resp)
+{
+	unsigned int io_base = devc->hw.io_base;
+	unsigned int timed_out = 0;
+	unsigned int i;
+
+	if (debug_flg & DEBUG_CMD) {
+		printk("waveartist_cmd: cmd=");
+
+		for (i = 0; i < nr_cmd; i++)
+			printk("%04X ", cmd[i]);
+
+		printk("\n");
+	}
+
+	if (inb(io_base + STATR) & CMD_RF) {
+		int old_data;
+
+		/* flush the port
+		 */
+
+		old_data = inw(io_base + CMDR);
+
+		if (debug_flg & DEBUG_CMD)
+			printk("flushed %04X...", old_data);
+
+		udelay(10);
+	}
+
+	for (i = 0; !timed_out && i < nr_cmd; i++) {
+		int count;
+
+		for (count = 5000; count; count--)
+			if (inb(io_base + STATR) & CMD_WE)
+				break;
+
+		if (!count)
+			timed_out = 1;
+		else
+			outw(cmd[i], io_base + CMDR);
+	}
+
+	for (i = 0; !timed_out && i < nr_resp; i++) {
+		int count;
+
+		for (count = 5000; count; count--)
+			if (inb(io_base + STATR) & CMD_RF)
+				break;
+
+		if (!count)
+			timed_out = 1;
+		else
+			resp[i] = inw(io_base + CMDR);
+	}
+
+	if (debug_flg & DEBUG_CMD) {
+		if (!timed_out) {
+			printk("waveartist_cmd: resp=");
+
+			for (i = 0; i < nr_resp; i++)
+				printk("%04X ", resp[i]);
+
+			printk("\n");
+		} else
+			printk("waveartist_cmd: timed out\n");
+	}
+
+	return timed_out ? 1 : 0;
+}
+
+/*
+ * Send one command word
+ */
+static inline int
+waveartist_cmd1(wavnc_info *devc, unsigned int cmd)
+{
+	return waveartist_cmd(devc, 1, &cmd, 0, NULL);
+}
+
+/*
+ * Send one command, receive one word
+ */
+static inline unsigned int
+waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd)
+{
+	unsigned int ret;
+
+	waveartist_cmd(devc, 1, &cmd, 1, &ret);
+
+	return ret;
+}
+
+/*
+ * Send a double command, receive one
+ * word (and throw it away)
+ */
+static inline int
+waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
+{
+	unsigned int vals[2];
+
+	vals[0] = cmd;
+	vals[1] = arg;
+
+	return waveartist_cmd(devc, 2, vals, 1, vals);
+}
+
+/*
+ * Send a triple command
+ */
+static inline int
+waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
+		unsigned int arg1, unsigned int arg2)
+{
+	unsigned int vals[3];
+
+	vals[0] = cmd;
+	vals[1] = arg1;
+	vals[2] = arg2;
+
+	return waveartist_cmd(devc, 3, vals, 0, NULL);
+}
+
+static int
+waveartist_getrev(wavnc_info *devc, char *rev)
+{
+	unsigned int temp[2];
+	unsigned int cmd = WACMD_GETREV;
+
+	waveartist_cmd(devc, 1, &cmd, 2, temp);
+
+	rev[0] = temp[0] >> 8;
+	rev[1] = temp[0] & 255;
+	rev[2] = '\0';
+
+	return temp[0];
+}
+
+static void waveartist_halt_output(int dev);
+static void waveartist_halt_input(int dev);
+static void waveartist_halt(int dev);
+static void waveartist_trigger(int dev, int state);
+
+static int
+waveartist_open(int dev, int mode)
+{
+	wavnc_info	*devc;
+	wavnc_port_info	*portc;
+	unsigned long	flags;
+
+	if (dev < 0 || dev >= num_audiodevs)
+		return -ENXIO;
+
+	devc  = (wavnc_info *) audio_devs[dev]->devc;
+	portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+	if (portc->open_mode || (devc->open_mode & mode)) {
+		spin_unlock_irqrestore(&waveartist_lock, flags);
+		return -EBUSY;
+	}
+
+	devc->audio_mode  = 0;
+	devc->open_mode  |= mode;
+	portc->open_mode  = mode;
+	waveartist_trigger(dev, 0);
+
+	if (mode & OPEN_READ)
+		devc->record_dev = dev;
+	if (mode & OPEN_WRITE)
+		devc->playback_dev = dev;
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+
+	return 0;
+}
+
+static void
+waveartist_close(int dev)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	waveartist_halt(dev);
+
+	devc->audio_mode = 0;
+	devc->open_mode &= ~portc->open_mode;
+	portc->open_mode = 0;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag)
+{
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+	unsigned int	count = __count; 
+
+	if (debug_flg & DEBUG_OUT)
+		printk("waveartist: output block, buf=0x%lx, count=0x%x...\n",
+			buf, count);
+	/*
+	 * 16 bit data
+	 */
+	if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))
+		count >>= 1;
+
+	if (portc->channels > 1)
+		count >>= 1;
+
+	count -= 1;
+
+	if (devc->audio_mode & PCM_ENABLE_OUTPUT &&
+	    audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    count == devc->xfer_count) {
+		devc->audio_mode |= PCM_ENABLE_OUTPUT;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	/*
+	 * set sample count
+	 */
+	waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count);
+
+	devc->xfer_count = count;
+	devc->audio_mode |= PCM_ENABLE_OUTPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+	unsigned int	count = __count;
+
+	if (debug_flg & DEBUG_IN)
+		printk("waveartist: start input, buf=0x%lx, count=0x%x...\n",
+			buf, count);
+
+	if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+		count >>= 1;
+
+	if (portc->channels > 1)
+		count >>= 1;
+
+	count -= 1;
+
+	if (devc->audio_mode & PCM_ENABLE_INPUT &&
+	    audio_devs[dev]->flags & DMA_AUTOMODE &&
+	    intrflag &&
+	    count == devc->xfer_count) {
+		devc->audio_mode |= PCM_ENABLE_INPUT;
+		return;	/*
+			 * Auto DMA mode on. No need to react
+			 */
+	}
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	/*
+	 * set sample count
+	 */
+	waveartist_cmd2(devc, WACMD_INPUTSIZE, count);
+
+	devc->xfer_count = count;
+	devc->audio_mode |= PCM_ENABLE_INPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static int
+waveartist_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	return -EINVAL;
+}
+
+static unsigned int
+waveartist_get_speed(wavnc_port_info *portc)
+{
+	unsigned int speed;
+
+	/*
+	 * program the speed, channels, bits
+	 */
+	if (portc->speed == 8000)
+		speed = 0x2E71;
+	else if (portc->speed == 11025)
+		speed = 0x4000;
+	else if (portc->speed == 22050)
+		speed = 0x8000;
+	else if (portc->speed == 44100)
+		speed = 0x0;
+	else {
+		/*
+		 * non-standard - just calculate
+		 */
+		speed = portc->speed << 16;
+
+		speed = (speed / 44100) & 65535;
+	}
+
+	return speed;
+}
+
+static unsigned int
+waveartist_get_bits(wavnc_port_info *portc)
+{
+	unsigned int bits;
+
+	if (portc->audio_format == AFMT_S16_LE)
+		bits = 1;
+	else if (portc->audio_format == AFMT_S8)
+		bits = 0;
+	else
+		bits = 2;	//default AFMT_U8
+
+	return bits;
+}
+
+static int
+waveartist_prepare_for_input(int dev, int bsize, int bcount)
+{
+	unsigned long	flags;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned int	speed, bits;
+
+	if (devc->audio_mode)
+		return 0;
+
+	speed = waveartist_get_speed(portc);
+	bits  = waveartist_get_bits(portc);
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
+		printk(KERN_WARNING "waveartist: error setting the "
+		       "record format to %d\n", portc->audio_format);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels))
+		printk(KERN_WARNING "waveartist: error setting record "
+		       "to %d channels\n", portc->channels);
+
+	/*
+	 * write cmd SetSampleSpeedTimeConstant
+	 */
+	if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed))
+		printk(KERN_WARNING "waveartist: error setting the record "
+		       "speed to %dHz.\n", portc->speed);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1))
+		printk(KERN_WARNING "waveartist: error setting the record "
+		       "data path to 0x%X\n", 1);
+
+	if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
+		printk(KERN_WARNING "waveartist: error setting the record "
+		       "format to %d\n", portc->audio_format);
+
+	devc->xfer_count = 0;
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+	waveartist_halt_input(dev);
+
+	if (debug_flg & DEBUG_INTR) {
+		printk("WA CTLR reg: 0x%02X.\n",
+		       inb(devc->hw.io_base + CTLR));
+		printk("WA STAT reg: 0x%02X.\n",
+		       inb(devc->hw.io_base + STATR));
+		printk("WA IRQS reg: 0x%02X.\n",
+		       inb(devc->hw.io_base + IRQSTAT));
+	}
+
+	return 0;
+}
+
+static int
+waveartist_prepare_for_output(int dev, int bsize, int bcount)
+{
+	unsigned long	flags;
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned int	speed, bits;
+
+	/*
+	 * program the speed, channels, bits
+	 */
+	speed = waveartist_get_speed(portc);
+	bits  = waveartist_get_bits(portc);
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) &&
+	    waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "speed to %dHz.\n", portc->speed);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "to %d channels\n", portc->channels);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "data path to 0x%X\n", 0);
+
+	if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits))
+		printk(KERN_WARNING "waveartist: error setting the playback "
+		       "format to %d\n", portc->audio_format);
+
+	devc->xfer_count = 0;
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+	waveartist_halt_output(dev);
+
+	if (debug_flg & DEBUG_INTR) {
+		printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR));
+		printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR));
+		printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT));
+	}
+
+	return 0;
+}
+
+static void
+waveartist_halt(int dev)
+{
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	wavnc_info	*devc;
+
+	if (portc->open_mode & OPEN_WRITE)
+		waveartist_halt_output(dev);
+
+	if (portc->open_mode & OPEN_READ)
+		waveartist_halt_input(dev);
+
+	devc = (wavnc_info *) audio_devs[dev]->devc;
+	devc->audio_mode = 0;
+}
+
+static void
+waveartist_halt_input(int dev)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	/*
+	 * Stop capture
+	 */
+	waveartist_cmd1(devc, WACMD_INPUTSTOP);
+
+	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+
+	/*
+	 * Clear interrupt by toggling
+	 * the IRQ_ACK bit in CTRL
+	 */
+	if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
+		waveartist_iack(devc);
+
+//	devc->audio_mode &= ~PCM_ENABLE_INPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_halt_output(int dev)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	waveartist_cmd1(devc, WACMD_OUTPUTSTOP);
+
+	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+
+	/*
+	 * Clear interrupt by toggling
+	 * the IRQ_ACK bit in CTRL
+	 */
+	if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
+		waveartist_iack(devc);
+
+//	devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static void
+waveartist_trigger(int dev, int state)
+{
+	wavnc_info	*devc = (wavnc_info *) audio_devs[dev]->devc;
+	wavnc_port_info	*portc = (wavnc_port_info *) audio_devs[dev]->portc;
+	unsigned long	flags;
+
+	if (debug_flg & DEBUG_TRIGGER) {
+		printk("wavnc: audio trigger ");
+		if (state & PCM_ENABLE_INPUT)
+			printk("in ");
+		if (state & PCM_ENABLE_OUTPUT)
+			printk("out");
+		printk("\n");
+	}
+
+	spin_lock_irqsave(&waveartist_lock, flags);
+
+	state &= devc->audio_mode;
+
+	if (portc->open_mode & OPEN_READ &&
+	    state & PCM_ENABLE_INPUT)
+		/*
+		 * enable ADC Data Transfer to PC
+		 */
+		waveartist_cmd1(devc, WACMD_INPUTSTART);
+
+	if (portc->open_mode & OPEN_WRITE &&
+	    state & PCM_ENABLE_OUTPUT)
+		/*
+		 * enable DAC data transfer from PC
+		 */
+		waveartist_cmd1(devc, WACMD_OUTPUTSTART);
+
+	spin_unlock_irqrestore(&waveartist_lock, flags);
+}
+
+static int
+waveartist_set_speed(int dev, int arg)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	if (arg <= 0)
+		return portc->speed;
+
+	if (arg < 5000)
+		arg = 5000;
+	if (arg > 44100)
+		arg = 44100;
+
+	portc->speed = arg;
+	return portc->speed;
+
+}
+
+static short
+waveartist_set_channels(int dev, short arg)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	if (arg != 1 && arg != 2)
+		return portc->channels;
+
+	portc->channels = arg;
+	return arg;
+}
+
+static unsigned int
+waveartist_set_bits(int dev, unsigned int arg)
+{
+	wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+
+	if (arg == 0)
+		return portc->audio_format;
+
+	if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8))
+		arg = AFMT_U8;
+
+	portc->audio_format = arg;
+
+	return arg;
+}
+
+static struct audio_driver waveartist_audio_driver = {
+	owner:			THIS_MODULE,
+	open:			waveartist_open,
+	close:			waveartist_close,
+	output_block:		waveartist_output_block,
+	start_input:		waveartist_start_input,
+	ioctl:			waveartist_ioctl,
+	prepare_for_input:	waveartist_prepare_for_input,
+	prepare_for_output:	waveartist_prepare_for_output,
+	halt_io:		waveartist_halt,
+	halt_input:		waveartist_halt_input,
+	halt_output:		waveartist_halt_output,
+	trigger:		waveartist_trigger,
+	set_speed:		waveartist_set_speed,
+	set_bits:		waveartist_set_bits,
+	set_channels:		waveartist_set_channels
+};
+
+
+static void
+waveartist_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	wavnc_info *devc = (wavnc_info *)dev_id;
+	int	   irqstatus, status;
+
+	irqstatus = inb(devc->hw.io_base + IRQSTAT);
+	status    = inb(devc->hw.io_base + STATR);
+
+	if (debug_flg & DEBUG_INTR)
+		printk("waveartist_intr: stat=%02x, irqstat=%02x\n",
+		       status, irqstatus);
+
+	if (status & IRQ_REQ)	/* Clear interrupt */
+		waveartist_iack(devc);
+	else
+		printk(KERN_WARNING "waveartist: unexpected interrupt\n");
+
+	if (irqstatus & 0x01) {
+		int temp = 1;
+
+		/* PCM buffer done
+		 */
+		if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) {
+			DMAbuf_outputintr(devc->playback_dev, 1);
+			temp = 0;
+		}
+		if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) {
+			DMAbuf_inputintr(devc->record_dev);
+			temp = 0;
+		}
+		if (temp)	//default:
+			printk(KERN_WARNING "waveartist: Unknown interrupt\n");
+	}
+	if (irqstatus & 0x2)
+		// We do not use SB mode natively...
+		printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n");
+}
+
+/* -------------------------------------------------------------------------
+ * Mixer stuff
+ */
+struct mix_ent {
+	unsigned char	reg_l;
+	unsigned char	reg_r;
+	unsigned char	shift;
+	unsigned char	max;
+};
+
+static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = {
+	{ 2, 6, 1,  7 }, /* SOUND_MIXER_VOLUME   */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_BASS     */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_TREBLE   */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_SYNTH    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_PCM      */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_SPEAKER  */
+	{ 0, 4, 6, 31 }, /* SOUND_MIXER_LINE     */
+	{ 2, 6, 4,  3 }, /* SOUND_MIXER_MIC      */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_CD       */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_IMIX     */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_ALTPCM   */
+#if 0
+	{ 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV   */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_IGAIN    */
+#else
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_RECLEV   */
+	{ 3, 7, 0,  7 }, /* SOUND_MIXER_IGAIN    */
+#endif
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_OGAIN    */
+	{ 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1    */
+	{ 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_LINE3    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_DIGITAL1 */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_DIGITAL2 */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_DIGITAL3 */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_PHONEIN  */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_PHONEOUT */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_VIDEO    */
+	{ 0, 0, 0,  0 }, /* SOUND_MIXER_RADIO    */
+	{ 0, 0, 0,  0 }  /* SOUND_MIXER_MONITOR  */
+};
+
+static void
+waveartist_mixer_update(wavnc_info *devc, int whichDev)
+{
+	unsigned int lev_left, lev_right;
+
+	lev_left  = devc->levels[whichDev] & 0xff;
+	lev_right = devc->levels[whichDev] >> 8;
+
+	if (lev_left > 100)
+		lev_left = 100;
+	if (lev_right > 100)
+		lev_right = 100;
+
+#define SCALE(lev,max)	((lev) * (max) / 100)
+
+	if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT)
+		whichDev = SOUND_MIXER_VOLUME;
+
+	if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) {
+		const struct mix_ent *mix = mix_devs + whichDev;
+		unsigned int mask, left, right;
+
+		mask = mix->max << mix->shift;
+		lev_left  = SCALE(lev_left,  mix->max) << mix->shift;
+		lev_right = SCALE(lev_right, mix->max) << mix->shift;
+
+		/* read left setting */
+		left  = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
+					       mix->reg_l << 8);
+
+		/* read right setting */
+		right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
+						mix->reg_r << 8);
+
+		left  = (left  & ~mask) | (lev_left  & mask);
+		right = (right & ~mask) | (lev_right & mask);
+
+		/* write left,right back */
+		waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
+	} else {
+		switch(whichDev) {
+		case SOUND_MIXER_PCM:
+			waveartist_cmd3(devc, WACMD_SET_LEVEL,
+					SCALE(lev_left,  32767),
+					SCALE(lev_right, 32767));
+			break;
+
+		case SOUND_MIXER_SYNTH:
+			waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL,
+					SCALE(lev_left,  32767),
+					SCALE(lev_right, 32767));
+			break;
+		}
+	}
+}
+
+/*
+ * Set the ADC MUX to the specified values.  We do NOT do any
+ * checking of the values passed, since we assume that the
+ * relevant *_select_input function has done that for us.
+ */
+static void
+waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev)
+{
+	unsigned int reg_08, reg_09;
+
+	reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800);
+	reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900);
+
+	reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev;
+
+	waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09);
+}
+
+/*
+ * Decode a recording mask into a mixer selection as follows:
+ *
+ *     OSS Source	WA Source	Actual source
+ *  SOUND_MASK_IMIX	Mixer		Mixer output (same as AD1848)
+ *  SOUND_MASK_LINE	Line		Line in
+ *  SOUND_MASK_LINE1	Aux 1		Aux 1 in
+ *  SOUND_MASK_LINE2	Aux 2		Aux 2 in
+ *  SOUND_MASK_MIC	Mic		Microphone
+ */
+static unsigned int
+waveartist_select_input(wavnc_info *devc, unsigned int recmask,
+			unsigned char *dev_l, unsigned char *dev_r)
+{
+	unsigned int recdev = ADC_MUX_NONE;
+
+	if (recmask & SOUND_MASK_IMIX) {
+		recmask = SOUND_MASK_IMIX;
+		recdev = ADC_MUX_MIXER;
+	} else if (recmask & SOUND_MASK_LINE2) {
+		recmask = SOUND_MASK_LINE2;
+		recdev = ADC_MUX_AUX2;
+	} else if (recmask & SOUND_MASK_LINE1) {
+		recmask = SOUND_MASK_LINE1;
+		recdev = ADC_MUX_AUX1;
+	} else if (recmask & SOUND_MASK_LINE) {
+		recmask = SOUND_MASK_LINE;
+		recdev = ADC_MUX_LINE;
+	} else if (recmask & SOUND_MASK_MIC) {
+		recmask = SOUND_MASK_MIC;
+		recdev = ADC_MUX_MIC;
+	}
+
+	*dev_l = *dev_r = recdev;
+
+	return recmask;
+}
+
+static int
+waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
+			unsigned char lev_r)
+{
+	switch (dev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_SYNTH:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_MIC:
+	case SOUND_MIXER_IGAIN:
+	case SOUND_MIXER_LINE1:
+	case SOUND_MIXER_LINE2:
+		devc->levels[dev] = lev_l | lev_r << 8;
+		break;
+
+	case SOUND_MIXER_IMIX:
+		break;
+
+	default:
+		dev = -EINVAL;
+		break;
+	}
+
+	return dev;
+}
+
+static int waveartist_get_mixer(wavnc_info *devc, int dev)
+{
+	return devc->levels[dev];
+}
+
+static const struct waveartist_mixer_info waveartist_mixer = {
+	supported_devs:	SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN,
+	recording_devs:	SOUND_MASK_LINE  | SOUND_MASK_MIC   |
+			SOUND_MASK_LINE1 | SOUND_MASK_LINE2 |
+			SOUND_MASK_IMIX,
+	stereo_devs:	(SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~
+			(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX),
+	select_input:	waveartist_select_input,
+	decode_mixer:	waveartist_decode_mixer,
+	get_mixer:	waveartist_get_mixer,
+};
+
+static void
+waveartist_set_recmask(wavnc_info *devc, unsigned int recmask)
+{
+	unsigned char dev_l, dev_r;
+
+	recmask &= devc->mix->recording_devs;
+
+	/*
+	 * If more than one recording device selected,
+	 * disable the device that is currently in use.
+	 */
+	if (hweight32(recmask) > 1)
+		recmask &= ~devc->recmask;
+
+	/*
+	 * Translate the recording device mask into
+	 * the ADC multiplexer settings.
+	 */
+	devc->recmask = devc->mix->select_input(devc, recmask,
+						&dev_l, &dev_r);
+
+	waveartist_set_adc_mux(devc, dev_l, dev_r);
+}
+
+static int
+waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level)
+{
+	unsigned int lev_left  = level & 0x00ff;
+	unsigned int lev_right = (level & 0xff00) >> 8;
+
+	if (lev_left > 100)
+		lev_left = 100;
+	if (lev_right > 100)
+		lev_right = 100;
+
+	/*
+	 * Mono devices have their right volume forced to their
+	 * left volume.  (from ALSA driver OSS emulation).
+	 */
+	if (!(devc->mix->stereo_devs & (1 << dev)))
+		lev_right = lev_left;
+
+	dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right);
+
+	if (dev >= 0)
+		waveartist_mixer_update(devc, dev);
+
+	return dev < 0 ? dev : 0;
+}
+
+static int
+waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int ret = 0, val, nr;
+
+	/*
+	 * All SOUND_MIXER_* ioctls use type 'M'
+	 */
+	if (((cmd >> 8) & 255) != 'M')
+		return -ENOIOCTLCMD;
+
+#ifdef CONFIG_ARCH_NETWINDER
+	if (machine_is_netwinder()) {
+		ret = vnc_private_ioctl(dev, cmd, arg);
+		if (ret != -ENOIOCTLCMD)
+			return ret;
+		else
+			ret = 0;
+	}
+#endif
+
+	nr = cmd & 0xff;
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		switch (nr) {
+		case SOUND_MIXER_RECSRC:
+			waveartist_set_recmask(devc, val);
+			break;
+
+		default:
+			ret = -EINVAL;
+			if (nr < SOUND_MIXER_NRDEVICES &&
+			    devc->mix->supported_devs & (1 << nr))
+				ret = waveartist_set_mixer(devc, nr, val);
+		}
+	}
+
+	if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) {
+		ret = -EINVAL;
+
+		switch (nr) {
+		case SOUND_MIXER_RECSRC:
+			ret = devc->recmask;
+			break;
+
+		case SOUND_MIXER_DEVMASK:
+			ret = devc->mix->supported_devs;
+			break;
+
+		case SOUND_MIXER_STEREODEVS:
+			ret = devc->mix->stereo_devs;
+			break;
+
+		case SOUND_MIXER_RECMASK:
+			ret = devc->mix->recording_devs;
+			break;
+
+		case SOUND_MIXER_CAPS:
+			ret = SOUND_CAP_EXCL_INPUT;
+			break;
+
+		default:
+			if (nr < SOUND_MIXER_NRDEVICES)
+				ret = devc->mix->get_mixer(devc, nr);
+			break;
+		}
+
+		if (ret >= 0)
+			ret = put_user(ret, (int *)arg) ? -EFAULT : 0;
+	}
+
+	return ret;
+}
+
+static struct mixer_operations waveartist_mixer_operations =
+{
+	owner:	THIS_MODULE,
+	id:	"WaveArtist",
+	name:	"WaveArtist",
+	ioctl:	waveartist_mixer_ioctl
+};
+
+static void
+waveartist_mixer_reset(wavnc_info *devc)
+{
+	int i;
+
+	if (debug_flg & DEBUG_MIXER)
+		printk("%s: mixer_reset\n", devc->hw.name);
+
+	/*
+	 * reset mixer cmd
+	 */
+	waveartist_cmd1(devc, WACMD_RST_MIXER);
+
+	/*
+	 * set input for ADC to come from 'quiet'
+	 * turn on default modes
+	 */
+	waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836);
+
+	/*
+	 * set mixer input select to none, RX filter gains 0 dB
+	 */
+	waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00);
+
+	/*
+	 * set bit 0 reg 2 to 1 - unmute MonoOut
+	 */
+	waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800);
+
+	/* set default input device = internal mic
+	 * current recording device = none
+	 */
+	waveartist_set_recmask(devc, 0);
+
+	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		waveartist_mixer_update(devc, i);
+}
+
+static int __init waveartist_init(wavnc_info *devc)
+{
+	wavnc_port_info *portc;
+	char rev[3], dev_name[64];
+	int my_dev;
+
+	if (waveartist_reset(devc))
+		return -ENODEV;
+
+	sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name);
+
+	if (waveartist_getrev(devc, rev)) {
+		strcat(dev_name, " rev. ");
+		strcat(dev_name, rev);
+	}
+	strcat(dev_name, ")");
+
+	conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq,
+		     devc->hw.dma, devc->hw.dma2);
+
+	portc = (wavnc_port_info *)kmalloc(sizeof(wavnc_port_info), GFP_KERNEL);
+	if (portc == NULL)
+		goto nomem;
+
+	memset(portc, 0, sizeof(wavnc_port_info));
+
+	my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name,
+			&waveartist_audio_driver, sizeof(struct audio_driver),
+			devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8,
+			devc, devc->hw.dma, devc->hw.dma2);
+
+	if (my_dev < 0)
+		goto free;
+
+	audio_devs[my_dev]->portc = portc;
+
+	waveartist_mixer_reset(devc);
+
+	/*
+	 * clear any pending interrupt
+	 */
+	waveartist_iack(devc);
+
+	if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {
+		printk(KERN_ERR "%s: IRQ %d in use\n",
+			devc->hw.name, devc->hw.irq);
+		goto uninstall;
+	}
+
+	if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {
+		printk(KERN_ERR "%s: Can't allocate DMA%d\n",
+			devc->hw.name, devc->hw.dma);
+		goto uninstall_irq;
+	}
+
+	if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)
+		if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {
+			printk(KERN_ERR "%s: can't allocate DMA%d\n",
+				devc->hw.name, devc->hw.dma2);
+			goto uninstall_dma;
+		}
+
+	waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE);
+
+	audio_devs[my_dev]->mixer_dev =
+		sound_install_mixer(MIXER_DRIVER_VERSION,
+				dev_name,
+				&waveartist_mixer_operations,
+				sizeof(struct mixer_operations),
+				devc);
+
+	return my_dev;
+
+uninstall_dma:
+	sound_free_dma(devc->hw.dma);
+
+uninstall_irq:
+	free_irq(devc->hw.irq, devc);
+
+uninstall:
+	sound_unload_audiodev(my_dev);
+
+free:
+	kfree(portc);
+
+nomem:
+	return -1;
+}
+
+static int __init probe_waveartist(struct address_info *hw_config)
+{
+	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+
+	if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
+		printk(KERN_WARNING "waveartist: too many audio devices\n");
+		return 0;
+	}
+
+	if (check_region(hw_config->io_base, 15))  {
+		printk(KERN_WARNING "WaveArtist: I/O port conflict\n");
+		return 0;
+	}
+
+	if (hw_config->irq > 15 || hw_config->irq < 0) {
+		printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",
+		       hw_config->irq);
+		return 0;
+	}
+
+	if (hw_config->dma != 3) {
+		printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",
+		       hw_config->dma);
+		return 0;
+	}
+
+	hw_config->name = "WaveArtist";
+	devc->hw = *hw_config;
+	devc->open_mode = 0;
+	devc->chip_name = "RWA-010";
+
+	return 1;
+}
+
+static void __init
+attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix)
+{
+	wavnc_info *devc = &adev_info[nr_waveartist_devs];
+
+	/*
+	 * NOTE! If irq < 0, there is another driver which has allocated the
+	 *   IRQ so that this driver doesn't need to allocate/deallocate it.
+	 *   The actually used IRQ is ABS(irq).
+	 */
+	devc->hw = *hw;
+	devc->hw.irq = (hw->irq > 0) ? hw->irq : 0;
+	devc->open_mode = 0;
+	devc->playback_dev = 0;
+	devc->record_dev = 0;
+	devc->audio_flags = DMA_AUTOMODE;
+	devc->levels = levels;
+
+	if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA)
+		devc->audio_flags |= DMA_DUPLEX;
+
+	request_region(hw->io_base, 15, devc->hw.name);
+
+	devc->mix = mix;
+	devc->dev_no = waveartist_init(devc);
+
+	if (devc->dev_no < 0)
+		release_region(hw->io_base, 15);
+	else {
+#ifdef CONFIG_ARCH_NETWINDER
+		if (machine_is_netwinder()) {
+			init_timer(&vnc_timer);
+			vnc_timer.function = vnc_slider_tick;
+			vnc_timer.expires  = jiffies;
+			vnc_timer.data     = nr_waveartist_devs;
+			add_timer(&vnc_timer);
+
+			vnc_configure_mixer(devc, 0);
+
+			devc->no_autoselect = 1;
+		}
+#endif
+		nr_waveartist_devs += 1;
+	}
+}
+
+static void __exit unload_waveartist(struct address_info *hw)
+{
+	wavnc_info *devc = NULL;
+	int i;
+
+	for (i = 0; i < nr_waveartist_devs; i++)
+		if (hw->io_base == adev_info[i].hw.io_base) {
+			devc = adev_info + i;
+			break;
+		}
+
+	if (devc != NULL) {
+		int mixer;
+
+#ifdef CONFIG_ARCH_NETWINDER
+		if (machine_is_netwinder())
+			del_timer(&vnc_timer);
+#endif
+
+		release_region(devc->hw.io_base, 15);
+
+		waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);
+
+		if (devc->hw.irq >= 0)
+			free_irq(devc->hw.irq, devc);
+
+		sound_free_dma(devc->hw.dma);
+
+		if (devc->hw.dma != devc->hw.dma2 &&
+		    devc->hw.dma2 != NO_DMA)
+			sound_free_dma(devc->hw.dma2);
+
+		mixer = audio_devs[devc->dev_no]->mixer_dev;
+
+		if (mixer >= 0)
+			sound_unload_mixerdev(mixer);
+
+		if (devc->dev_no >= 0)
+			sound_unload_audiodev(devc->dev_no);
+
+		nr_waveartist_devs -= 1;
+
+		for (; i < nr_waveartist_devs; i++)
+			adev_info[i] = adev_info[i + 1];
+	} else
+		printk(KERN_WARNING "waveartist: can't find device "
+		       "to unload\n");
+}
+
+#ifdef CONFIG_ARCH_NETWINDER
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
+
+#include <asm/hardware/dec21285.h>
+ 
+#define	VNC_TIMER_PERIOD (HZ/4)	//check slider 4 times/sec
+
+#define	MIXER_PRIVATE3_RESET	0x53570000
+#define	MIXER_PRIVATE3_READ	0x53570001
+#define	MIXER_PRIVATE3_WRITE	0x53570002
+
+#define	VNC_MUTE_INTERNAL_SPKR	0x01	//the sw mute on/off control bit
+#define	VNC_MUTE_LINE_OUT	0x10
+#define VNC_PHONE_DETECT	0x20
+#define VNC_HANDSET_DETECT	0x40
+#define VNC_DISABLE_AUTOSWITCH	0x80
+
+extern spinlock_t gpio_lock;
+
+static inline void
+vnc_mute_spkr(wavnc_info *devc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static void
+vnc_mute_lout(wavnc_info *devc)
+{
+	unsigned int left, right;
+
+	left  = waveartist_cmd1_r(devc, WACMD_GET_LEVEL);
+	right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400);
+
+	if (devc->line_mute_state) {
+		left &= ~1;
+		right &= ~1;
+	} else {
+		left |= 1;
+		right |= 1;
+	}
+	waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
+		
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+	static signed int old_slider_volume;
+	unsigned long flags;
+	signed int volume = 255;
+
+	*CSR_TIMER1_LOAD = 0x00ffffff;
+
+	save_flags(flags);
+	cli();
+
+	outb(0xFF, 0x201);
+	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+	while (volume && (inb(0x201) & 0x01))
+		volume--;
+
+	*CSR_TIMER1_CNTL = 0;
+
+	restore_flags(flags);
+	
+	volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+	volume = 150 - (volume >> 5);
+#else
+	volume = (volume >> 6) - 25;
+#endif
+
+	if (volume < 0)
+		volume = 0;
+
+	if (volume > 100)
+		volume = 100;
+
+	/*
+	 * slider quite often reads +-8, so debounce this random noise
+	 */
+	if (abs(volume - old_slider_volume) > 7) {
+		old_slider_volume = volume;
+
+		if (debug_flg & DEBUG_MIXER)
+			printk(KERN_DEBUG "Slider volume: %d.\n", volume);
+	}
+
+	return old_slider_volume;
+}
+
+/*
+ * Decode a recording mask into a mixer selection on the NetWinder
+ * as follows:
+ *
+ *     OSS Source	WA Source	Actual source
+ *  SOUND_MASK_IMIX	Mixer		Mixer output (same as AD1848)
+ *  SOUND_MASK_LINE	Line		Line in
+ *  SOUND_MASK_LINE1	Left Mic	Handset
+ *  SOUND_MASK_PHONEIN	Left Aux	Telephone microphone
+ *  SOUND_MASK_MIC	Right Mic	Builtin microphone
+ */
+static unsigned int
+netwinder_select_input(wavnc_info *devc, unsigned int recmask,
+		       unsigned char *dev_l, unsigned char *dev_r)
+{
+	unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE;
+
+	if (recmask & SOUND_MASK_IMIX) {
+		recmask = SOUND_MASK_IMIX;
+		recdev_l = ADC_MUX_MIXER;
+		recdev_r = ADC_MUX_MIXER;
+	} else if (recmask & SOUND_MASK_LINE) {
+		recmask = SOUND_MASK_LINE;
+		recdev_l = ADC_MUX_LINE;
+		recdev_r = ADC_MUX_LINE;
+	} else if (recmask & SOUND_MASK_LINE1) {
+		recmask = SOUND_MASK_LINE1;
+		waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
+		recdev_l = ADC_MUX_MIC;
+		recdev_r = ADC_MUX_NONE;
+	} else if (recmask & SOUND_MASK_PHONEIN) {
+		recmask = SOUND_MASK_PHONEIN;
+		waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
+		recdev_l = ADC_MUX_AUX1;
+		recdev_r = ADC_MUX_NONE;
+	} else if (recmask & SOUND_MASK_MIC) {
+		recmask = SOUND_MASK_MIC;
+		waveartist_cmd1(devc, WACMD_SET_MONO | 0x100);	/* right */
+		recdev_l = ADC_MUX_NONE;
+		recdev_r = ADC_MUX_MIC;
+	}
+
+	*dev_l = recdev_l;
+	*dev_r = recdev_r;
+
+	return recmask;
+}
+
+static int
+netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
+		       unsigned char lev_r)
+{
+	switch (dev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_SYNTH:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_IGAIN:
+		devc->levels[dev] = lev_l | lev_r << 8;
+		break;
+
+	case SOUND_MIXER_MIC:		/* right mic only */
+		devc->levels[SOUND_MIXER_MIC] &= 0xff;
+		devc->levels[SOUND_MIXER_MIC] |= lev_l << 8;
+		break;
+
+	case SOUND_MIXER_LINE1:		/* left mic only  */
+		devc->levels[SOUND_MIXER_MIC] &= 0xff00;
+		devc->levels[SOUND_MIXER_MIC] |= lev_l;
+		dev = SOUND_MIXER_MIC;
+		break;
+
+	case SOUND_MIXER_PHONEIN:	/* left aux only  */
+		devc->levels[SOUND_MIXER_LINE1] = lev_l;
+		dev = SOUND_MIXER_LINE1;
+		break;
+
+	case SOUND_MIXER_IMIX:
+	case SOUND_MIXER_PHONEOUT:
+		break;
+
+	default:
+		dev = -EINVAL;
+		break;
+	}
+	return dev;
+}
+
+static int netwinder_get_mixer(wavnc_info *devc, int dev)
+{
+	int levels;
+
+	switch (dev) {
+	case SOUND_MIXER_VOLUME:
+	case SOUND_MIXER_SYNTH:
+	case SOUND_MIXER_PCM:
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_IGAIN:
+		levels = devc->levels[dev];
+		break;
+
+	case SOUND_MIXER_MIC:		/* builtin mic: right mic only */
+		levels = devc->levels[SOUND_MIXER_MIC] >> 8;
+		levels |= levels << 8;
+		break;
+
+	case SOUND_MIXER_LINE1:		/* handset mic: left mic only */
+		levels = devc->levels[SOUND_MIXER_MIC] & 0xff;
+		levels |= levels << 8;
+		break;
+
+	case SOUND_MIXER_PHONEIN:	/* phone mic: left aux1 only */
+		levels = devc->levels[SOUND_MIXER_LINE1] & 0xff;
+		levels |= levels << 8;
+		break;
+
+	default:
+		levels = 0;
+	}
+
+	return levels;
+}
+
+/*
+ * Waveartist specific mixer information.
+ */
+static const struct waveartist_mixer_info netwinder_mixer = {
+	supported_devs:	SOUND_MASK_VOLUME  | SOUND_MASK_SYNTH   |
+			SOUND_MASK_PCM     | SOUND_MASK_SPEAKER |
+			SOUND_MASK_LINE    | SOUND_MASK_MIC     |
+			SOUND_MASK_IMIX    | SOUND_MASK_LINE1   |
+			SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT|
+			SOUND_MASK_IGAIN,
+
+	recording_devs:	SOUND_MASK_LINE    | SOUND_MASK_MIC     |
+			SOUND_MASK_IMIX    | SOUND_MASK_LINE1   |
+			SOUND_MASK_PHONEIN,
+
+	stereo_devs:	SOUND_MASK_VOLUME  | SOUND_MASK_SYNTH   |
+			SOUND_MASK_PCM     | SOUND_MASK_LINE    |
+			SOUND_MASK_IMIX    | SOUND_MASK_IGAIN,
+
+	select_input:	netwinder_select_input,
+	decode_mixer:	netwinder_decode_mixer,
+	get_mixer:	netwinder_get_mixer,
+};
+
+static void
+vnc_configure_mixer(wavnc_info *devc, unsigned int recmask)
+{
+	if (!devc->no_autoselect) {
+		if (devc->handset_detect) {
+			recmask = SOUND_MASK_LINE1;
+			devc->spkr_mute_state = devc->line_mute_state = 1;
+		} else if (devc->telephone_detect) {
+			recmask = SOUND_MASK_PHONEIN;
+			devc->spkr_mute_state = devc->line_mute_state = 1;
+		} else {
+			/* unless someone has asked for LINE-IN,
+			 * we default to MIC
+			 */
+			if ((devc->recmask & SOUND_MASK_LINE) == 0)
+				devc->recmask = SOUND_MASK_MIC;
+			devc->spkr_mute_state = devc->line_mute_state = 0;
+		}
+		vnc_mute_spkr(devc);
+		vnc_mute_lout(devc);
+
+		if (recmask != devc->recmask)
+			waveartist_set_recmask(devc, recmask);
+	}
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+	signed int slider_volume;
+	unsigned int temp, old_hs, old_td;
+
+	/*
+	 * read the "buttons" state.
+	 *  Bit 4 = 0 means handset present
+	 *  Bit 5 = 1 means phone offhook
+	 */
+	temp = inb(0x201);
+
+	old_hs = devc->handset_detect;
+	old_td = devc->telephone_detect;
+
+	devc->handset_detect = !(temp & 0x10);
+	devc->telephone_detect = !!(temp & 0x20);
+
+	if (!devc->no_autoselect &&
+	    (old_hs != devc->handset_detect ||
+	     old_td != devc->telephone_detect))
+		vnc_configure_mixer(devc, devc->recmask);
+
+	slider_volume = vnc_volume_slider(devc);
+
+	/*
+	 * If we're using software controlled volume, and
+	 * the slider moves by more than 20%, then we
+	 * switch back to slider controlled volume.
+	 */
+	if (abs(devc->slider_vol - slider_volume) > 20)
+		devc->use_slider = 1;
+
+	/*
+	 * use only left channel
+	 */
+	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+	if (slider_volume != temp && devc->use_slider) {
+		devc->slider_vol = slider_volume;
+
+		waveartist_set_mixer(devc, SOUND_MIXER_VOLUME,
+			slider_volume | slider_volume << 8);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+	int next_timeout;
+
+	if (vnc_slider(adev_info + data))
+		next_timeout = 5;	// mixer reported change
+	else
+		next_timeout = VNC_TIMER_PERIOD;
+
+	mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+	int val;
+
+	switch (cmd) {
+	case SOUND_MIXER_PRIVATE1:
+	{
+		u_int prev_spkr_mute, prev_line_mute, prev_auto_state;
+		int val;
+
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		/* check if parameter is logical */
+		if (val & ~(VNC_MUTE_INTERNAL_SPKR |
+			    VNC_MUTE_LINE_OUT |
+			    VNC_DISABLE_AUTOSWITCH))
+			return -EINVAL;
+
+		prev_auto_state = devc->no_autoselect;
+		prev_spkr_mute  = devc->spkr_mute_state;
+		prev_line_mute  = devc->line_mute_state;
+
+		devc->no_autoselect   = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;
+		devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;
+		devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;
+
+		if (prev_spkr_mute != devc->spkr_mute_state)
+			vnc_mute_spkr(devc);
+
+		if (prev_line_mute != devc->line_mute_state)
+			vnc_mute_lout(devc);
+
+		if (prev_auto_state != devc->no_autoselect)
+			vnc_configure_mixer(devc, devc->recmask);
+
+		return 0;
+	}
+
+	case SOUND_MIXER_PRIVATE2:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+
+		switch (val) {
+#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
+#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
+		case VNC_SOUND_PAUSE:
+			waveartist_cmd1(devc, 0x16);
+			break;
+
+		case VNC_SOUND_RESUME:
+			waveartist_cmd1(devc, 0x18);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		return 0;
+
+	/* private ioctl to allow bulk access to waveartist */
+	case SOUND_MIXER_PRIVATE3:
+	{
+		unsigned long	flags;
+		int		mixer_reg[15], i, val;
+
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg)))
+			return -EFAULT;
+
+		switch (mixer_reg[14]) {
+		case MIXER_PRIVATE3_RESET:
+			waveartist_mixer_reset(devc);
+			break;
+
+		case MIXER_PRIVATE3_WRITE:
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);
+			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);
+
+			waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);
+			waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);
+			break;
+
+		case MIXER_PRIVATE3_READ:
+			spin_lock_irqsave(&waveartist_lock, flags);
+
+			for (i = 0x30; i < 14 << 8; i += 1 << 8)
+				waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));
+
+			spin_unlock_irqrestore(&waveartist_lock, flags);
+
+			if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg)))
+				return -EFAULT;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	/* read back the state from PRIVATE1 */
+	case SOUND_MIXER_PRIVATE4:
+		val = (devc->spkr_mute_state  ? VNC_MUTE_INTERNAL_SPKR : 0) |
+		      (devc->line_mute_state  ? VNC_MUTE_LINE_OUT      : 0) |
+		      (devc->handset_detect   ? VNC_HANDSET_DETECT     : 0) |
+		      (devc->telephone_detect ? VNC_PHONE_DETECT       : 0) |
+		      (devc->no_autoselect    ? VNC_DISABLE_AUTOSWITCH : 0);
+
+		return put_user(val, (int *)arg) ? -EFAULT : 0;
+	}
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		/*
+		 * special case for master volume: if we
+		 * received this call - switch from hw
+		 * volume control to a software volume
+		 * control, till the hw volume is modified
+		 * to signal that user wants to be back in
+		 * hardware...
+		 */
+		if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
+			devc->use_slider = 0;
+
+		/* speaker output            */
+		if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) {
+			unsigned int val, l, r;
+
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+
+			l = val & 0x7f;
+			r = (val & 0x7f00) >> 8;
+			val = (l + r) / 2;
+			devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8);
+			devc->spkr_mute_state = (val <= 50);
+			vnc_mute_spkr(devc);
+			return 0;
+		}
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+#endif
+
+static struct address_info cfg;
+
+static int attached;
+
+static int __initdata io = 0;
+static int __initdata irq = 0;
+static int __initdata dma = 0;
+static int __initdata dma2 = 0;
+
+
+static int __init init_waveartist(void)
+{
+	const struct waveartist_mixer_info *mix;
+
+	if (!io && machine_is_netwinder()) {
+		/*
+		 * The NetWinder WaveArtist is at a fixed address.
+		 * If the user does not supply an address, use the
+		 * well-known parameters.
+		 */
+		io   = 0x250;
+		irq  = 12;
+		dma  = 3;
+		dma2 = 7;
+	}
+
+	mix = &waveartist_mixer;
+#ifdef CONFIG_ARCH_NETWINDER
+	if (machine_is_netwinder())
+		mix = &netwinder_mixer;
+#endif
+
+	cfg.io_base = io;
+	cfg.irq = irq;
+	cfg.dma = dma;
+	cfg.dma2 = dma2;
+
+	if (!probe_waveartist(&cfg))
+		return -ENODEV;
+
+	attach_waveartist(&cfg, mix);
+	attached = 1;
+
+	return 0;
+}
+
+static void __exit cleanup_waveartist(void)
+{
+	if (attached)
+		unload_waveartist(&cfg);
+}
+
+module_init(init_waveartist);
+module_exit(cleanup_waveartist);
+
+#ifndef MODULE
+static int __init setup_waveartist(char *str)
+{
+	/* io, irq, dma, dma2 */
+	int ints[5];
+	
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	
+	io	= ints[1];
+	irq	= ints[2];
+	dma	= ints[3];
+	dma2	= ints[4];
+
+	return 1;
+}
+__setup("waveartist=", setup_waveartist);
+#endif
+
+MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver");
+MODULE_PARM(io, "i");		/* IO base */
+MODULE_PARM(irq, "i");		/* IRQ */
+MODULE_PARM(dma, "i");		/* DMA */
+MODULE_PARM(dma2, "i");		/* DMA2 */
+MODULE_LICENSE("GPL");
diff -Nru linux/sound/oss/waveartist.h linux-2.4.19-pre5-mjc/sound/oss/waveartist.h
--- linux/sound/oss/waveartist.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/waveartist.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,92 @@
+/*
+ * linux/drivers/sound/waveartist.h
+ *
+ * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
+ */
+
+//registers
+#define CMDR	0
+#define DATR	2
+#define CTLR	4
+#define	STATR	5
+#define	IRQSTAT	12
+
+//bit defs
+//reg STATR
+#define	CMD_WE	0x80
+#define	CMD_RF	0x40
+#define	DAT_WE	0x20
+#define	DAT_RF	0x10
+
+#define	IRQ_REQ	0x08
+#define	DMA1	0x04
+#define	DMA0	0x02
+
+//bit defs
+//reg CTLR
+#define	CMD_WEIE	0x80
+#define	CMD_RFIE	0x40
+#define	DAT_WEIE	0x20
+#define	DAT_RFIE	0x10
+
+#define	RESET	0x08
+#define	DMA1_IE	0x04
+#define	DMA0_IE	0x02
+#define	IRQ_ACK	0x01
+
+//commands
+
+#define	WACMD_SYSTEMID		0x00
+#define WACMD_GETREV		0x00
+#define	WACMD_INPUTFORMAT	0x10	//0-8S, 1-16S, 2-8U
+#define	WACMD_INPUTCHANNELS	0x11	//1-Mono, 2-Stereo
+#define	WACMD_INPUTSPEED	0x12	//sampling rate
+#define	WACMD_INPUTDMA		0x13	//0-8bit, 1-16bit, 2-PIO
+#define	WACMD_INPUTSIZE		0x14	//samples to interrupt
+#define	WACMD_INPUTSTART	0x15	//start ADC
+#define	WACMD_INPUTPAUSE	0x16	//pause ADC
+#define	WACMD_INPUTSTOP		0x17	//stop ADC
+#define	WACMD_INPUTRESUME	0x18	//resume ADC
+#define	WACMD_INPUTPIO		0x19	//PIO ADC
+
+#define	WACMD_OUTPUTFORMAT	0x20	//0-8S, 1-16S, 2-8U
+#define	WACMD_OUTPUTCHANNELS	0x21	//1-Mono, 2-Stereo
+#define	WACMD_OUTPUTSPEED	0x22	//sampling rate
+#define	WACMD_OUTPUTDMA		0x23	//0-8bit, 1-16bit, 2-PIO
+#define	WACMD_OUTPUTSIZE	0x24	//samples to interrupt
+#define	WACMD_OUTPUTSTART	0x25	//start ADC
+#define	WACMD_OUTPUTPAUSE	0x26	//pause ADC
+#define	WACMD_OUTPUTSTOP	0x27	//stop ADC
+#define	WACMD_OUTPUTRESUME	0x28	//resume ADC
+#define	WACMD_OUTPUTPIO		0x29	//PIO ADC
+
+#define	WACMD_GET_LEVEL		0x30
+#define	WACMD_SET_LEVEL		0x31
+#define	WACMD_SET_MIXER		0x32
+#define	WACMD_RST_MIXER		0x33
+#define	WACMD_SET_MONO		0x34
+
+/*
+ * Definitions for left/right recording input mux
+ */
+#define ADC_MUX_NONE	0
+#define ADC_MUX_MIXER	1
+#define ADC_MUX_LINE	2
+#define ADC_MUX_AUX2	3
+#define ADC_MUX_AUX1	4
+#define ADC_MUX_MIC	5
+
+/*
+ * Definitions for mixer gain settings
+ */
+#define MIX_GAIN_LINE	0	/* line in	 */
+#define MIX_GAIN_AUX1	1	/* aux1		 */
+#define MIX_GAIN_AUX2	2	/* aux2		 */
+#define MIX_GAIN_XMIC	3	/* crossover mic */
+#define MIX_GAIN_MIC	4	/* normal mic	 */
+#define MIX_GAIN_PREMIC	5	/* preamp mic	 */
+#define MIX_GAIN_OUT	6	/* output	 */
+#define MIX_GAIN_MONO	7	/* mono in	 */
+
+int wa_sendcmd(unsigned int cmd);
+int wa_writecmd(unsigned int cmd, unsigned int arg);
diff -Nru linux/sound/oss/wavfront.c linux-2.4.19-pre5-mjc/sound/oss/wavfront.c
--- linux/sound/oss/wavfront.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/wavfront.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,3529 @@
+/*  -*- linux-c -*-
+ *
+ * sound/wavfront.c
+ *
+ * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus)
+ *
+ * This driver supports the onboard wavetable synthesizer (an ICS2115),
+ * including patch, sample and program loading and unloading, conversion
+ * of GUS patches during loading, and full user-level access to all
+ * WaveFront commands. It tries to provide semi-intelligent patch and
+ * sample management as well.
+ *
+ * It also provides support for the ICS emulation of an MPU-401.  Full
+ * support for the ICS emulation's "virtual MIDI mode" is provided in
+ * wf_midi.c.
+ *
+ * Support is also provided for the Tropez Plus' onboard FX processor,
+ * a Yamaha YSS225. Currently, code exists to configure the YSS225,
+ * and there is an interface allowing tweaking of any of its memory
+ * addresses. However, I have been unable to decipher the logical
+ * positioning of the configuration info for various effects, so for
+ * now, you just get the YSS225 in the same state as Turtle Beach's
+ * "SETUPSND.EXE" utility leaves it.
+ *
+ * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co],
+ * This chip also controls the configuration of the card: the wavefront
+ * synth is logical unit 4.
+ *
+ *
+ * Supported devices:
+ *
+ *   /dev/dsp                      - using cs4232+ad1848 modules, OSS compatible
+ *   /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible
+ *   /dev/synth00                  - raw synth interface
+ * 
+ **********************************************************************
+ *
+ * Copyright (C) by Paul Barton-Davis 1998
+ *
+ * Some portions of this file are taken from work that is
+ * copyright (C) by Hannu Savolainen 1993-1996
+ *
+ * Although the relevant code here is all new, the handling of
+ * sample/alias/multi- samples is entirely based on a driver by Matt
+ * Martin and Rutger Nijlunsing which demonstrated how to get things
+ * to work correctly. The GUS patch loading code has been almost
+ * unaltered by me, except to fit formatting and function names in the
+ * rest of the file. Many thanks to them.
+ *
+ * Appreciation and thanks to Hannu Savolainen for his early work on the Maui
+ * driver, and answering a few questions while this one was developed.
+ *
+ * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their
+ * complete lack of help in developing this driver, and in particular
+ * for their utter silence in response to questions about undocumented
+ * aspects of configuring a WaveFront soundcard, particularly the
+ * effects processor.
+ *
+ * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $
+ *
+ * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Changes:
+ * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
+ *		Added some __init and __initdata to entries in yss225.c
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>    
+
+#include <linux/interrupt.h>
+#include <linux/config.h>
+
+#include <linux/delay.h>
+
+#include "sound_config.h"
+
+#include <linux/wavefront.h>
+
+#define _MIDI_SYNTH_C_
+#define MIDI_SYNTH_NAME	"WaveFront MIDI"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+/* Compile-time control of the extent to which OSS is supported.
+
+   I consider /dev/sequencer to be an anachronism, but given its
+   widespread usage by various Linux MIDI software, it seems worth
+   offering support to it if its not too painful. Instead of using
+   /dev/sequencer, I recommend:
+
+     for synth programming and patch loading: /dev/synthNN
+     for kernel-synchronized MIDI sequencing: the ALSA sequencer
+     for direct MIDI control: /dev/midiNN
+
+   I have never tried static compilation into the kernel. The #if's
+   for this are really just notes to myself about what the code is
+   for.
+*/
+
+#define OSS_SUPPORT_SEQ            0x1  /* use of /dev/sequencer */
+#define OSS_SUPPORT_STATIC_INSTALL 0x2  /* static compilation into kernel */
+
+#define OSS_SUPPORT_LEVEL          0x1  /* just /dev/sequencer for now */
+
+#if    OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+static int (*midi_load_patch) (int devno, int format, const char *addr,
+			       int offs, int count, int pmgr_flag) = NULL;
+#endif /* OSS_SUPPORT_SEQ */
+
+/* if WF_DEBUG not defined, no run-time debugging messages will
+   be available via the debug flag setting. Given the current
+   beta state of the driver, this will remain set until a future 
+   version.
+*/
+
+#define WF_DEBUG 1
+
+#ifdef WF_DEBUG
+
+/* Thank goodness for gcc's preprocessor ... */
+
+#define DPRINT(cond, format, args...) \
+       if ((dev.debug & (cond)) == (cond)) { \
+	     printk (KERN_DEBUG LOGNAME format, ## args); \
+       }
+#else
+#define DPRINT(cond, format, args...)
+#endif
+
+#define LOGNAME "WaveFront: "
+
+/* bitmasks for WaveFront status port value */
+
+#define STAT_RINTR_ENABLED	0x01
+#define STAT_CAN_READ		0x02
+#define STAT_INTR_READ		0x04
+#define STAT_WINTR_ENABLED	0x10
+#define STAT_CAN_WRITE		0x20
+#define STAT_INTR_WRITE		0x40
+
+/*** Module-accessible parameters ***************************************/
+
+int wf_raw;     /* we normally check for "raw state" to firmware
+		   loading. if set, then during driver loading, the
+		   state of the board is ignored, and we reset the
+		   board and load the firmware anyway.
+		*/
+		   
+int fx_raw = 1; /* if this is zero, we'll leave the FX processor in
+		   whatever state it is when the driver is loaded.
+		   The default is to download the microprogram and
+		   associated coefficients to set it up for "default"
+		   operation, whatever that means.
+		*/
+
+int debug_default;      /* you can set this to control debugging
+			      during driver loading. it takes any combination
+			      of the WF_DEBUG_* flags defined in
+			      wavefront.h
+			   */
+
+/* XXX this needs to be made firmware and hardware version dependent */
+
+char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed
+					     version of the WaveFront OS
+					  */
+
+int wait_polls = 2000;	/* This is a number of tries we poll the status register
+			   before resorting to sleeping. WaveFront being an ISA
+			   card each poll takes about 1.2us. So before going to
+			   sleep we wait up to 2.4ms in a loop.
+			*/
+
+int sleep_length = HZ/100; /* This says how long we're going to sleep between polls.
+			      10ms sounds reasonable for fast response.
+			   */
+
+int sleep_tries = 50;       /* Wait for status 0.5 seconds total. */
+
+int reset_time = 2;        /* hundreths of a second we wait after a HW reset for
+			      the expected interrupt.
+			   */
+
+int ramcheck_time = 20;    /* time in seconds to wait while ROM code
+			      checks on-board RAM.
+			   */
+
+int osrun_time = 10;       /* time in seconds we wait for the OS to
+			      start running.
+			   */
+
+MODULE_PARM(wf_raw,"i");
+MODULE_PARM(fx_raw,"i");
+MODULE_PARM(debug_default,"i");
+MODULE_PARM(wait_polls,"i");
+MODULE_PARM(sleep_length,"i");
+MODULE_PARM(sleep_tries,"i");
+MODULE_PARM(ospath,"s");
+MODULE_PARM(reset_time,"i");
+MODULE_PARM(ramcheck_time,"i");
+MODULE_PARM(osrun_time,"i");
+
+/***************************************************************************/
+
+/* Note: because this module doesn't export any symbols, this really isn't
+   a global variable, even if it looks like one. I was quite confused by
+   this when I started writing this as a (newer) module -- pbd.
+*/
+
+struct wf_config {
+	int devno;            /* device number from kernel */
+	int irq;              /* "you were one, one of the few ..." */
+	int base;             /* low i/o port address */
+
+#define mpu_data_port    base 
+#define mpu_command_port base + 1 /* write semantics */
+#define mpu_status_port  base + 1 /* read semantics */
+#define data_port        base + 2 
+#define status_port      base + 3 /* read semantics */
+#define control_port     base + 3 /* write semantics  */
+#define block_port       base + 4 /* 16 bit, writeonly */
+#define last_block_port  base + 6 /* 16 bit, writeonly */
+
+	/* FX ports. These are mapped through the ICS2115 to the YS225.
+	   The ICS2115 takes care of flipping the relevant pins on the
+	   YS225 so that access to each of these ports does the right
+	   thing. Note: these are NOT documented by Turtle Beach.
+	*/
+
+#define fx_status       base + 8 
+#define fx_op           base + 8 
+#define fx_lcr          base + 9 
+#define fx_dsp_addr     base + 0xa
+#define fx_dsp_page     base + 0xb 
+#define fx_dsp_lsb      base + 0xc 
+#define fx_dsp_msb      base + 0xd 
+#define fx_mod_addr     base + 0xe
+#define fx_mod_data     base + 0xf 
+
+	volatile int irq_ok;               /* set by interrupt handler */
+        volatile int irq_cnt;              /* ditto */
+	int opened;                        /* flag, holds open(2) mode */
+	char debug;                        /* debugging flags */
+	int freemem;                       /* installed RAM, in bytes */ 
+
+	int synth_dev;                     /* devno for "raw" synth */
+	int mididev;                       /* devno for internal MIDI */
+	int ext_mididev;                   /* devno for external MIDI */ 
+        int fx_mididev;                    /* devno for FX MIDI interface */
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	int oss_dev;                      /* devno for OSS sequencer synth */
+#endif /* OSS_SUPPORT_SEQ */
+
+	char fw_version[2];                /* major = [0], minor = [1] */
+	char hw_version[2];                /* major = [0], minor = [1] */
+	char israw;                        /* needs Motorola microcode */
+	char has_fx;                       /* has FX processor (Tropez+) */
+	char prog_status[WF_MAX_PROGRAM];  /* WF_SLOT_* */
+	char patch_status[WF_MAX_PATCH];   /* WF_SLOT_* */
+	char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */
+	int samples_used;                  /* how many */
+	char interrupts_on;                /* h/w MPU interrupts enabled ? */
+	char rom_samples_rdonly;           /* can we write on ROM samples */
+	wait_queue_head_t interrupt_sleeper; 
+} dev;
+
+static int  detect_wffx(void);
+static int  wffx_ioctl (wavefront_fx_info *);
+static int  wffx_init (void);
+
+static int wavefront_delete_sample (int sampnum);
+static int wavefront_find_free_sample (void);
+
+/* From wf_midi.c */
+
+extern int  virtual_midi_enable  (void);
+extern int  virtual_midi_disable (void);
+extern int  detect_wf_mpu (int, int);
+extern int  install_wf_mpu (void);
+extern int  uninstall_wf_mpu (void);
+
+typedef struct {
+	int cmd;
+	char *action;
+	unsigned int read_cnt;
+	unsigned int write_cnt;
+	int need_ack;
+} wavefront_command;
+
+static struct {
+	int errno;
+	const char *errstr;
+} wavefront_errors[] = {
+	{ 0x01, "Bad sample number" },
+	{ 0x02, "Out of sample memory" },
+	{ 0x03, "Bad patch number" },
+	{ 0x04, "Error in number of voices" },
+	{ 0x06, "Sample load already in progress" },
+	{ 0x0B, "No sample load request pending" },
+	{ 0x0E, "Bad MIDI channel number" },
+	{ 0x10, "Download Record Error" },
+	{ 0x80, "Success" },
+	{ 0x0, 0x0 }
+};
+
+#define NEEDS_ACK 1
+
+static wavefront_command wavefront_commands[] = {
+	{ WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK },
+	{ WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0},
+	{ WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK },
+	{ WFC_GET_NVOICES, "get number of voices", 1, 0, 0 },
+	{ WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK },
+	{ WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 },
+	{ WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK },
+	{ WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK },
+	{ WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 },
+	{ WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK },
+	{ WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK },
+	{ WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK },
+	{ WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK },
+	{ WFC_MIDI_STATUS, "report midi status", 1, 0, 0 },
+	{ WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 },
+	{ WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 },
+	{ WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 },
+	{ WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 },
+	{ WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 },
+	{ WFC_DOWNLOAD_SAMPLE, "download sample",
+	  0, WF_SAMPLE_BYTES, NEEDS_ACK },
+	{ WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK},
+	{ WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header",
+	  0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK },
+	{ WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 },
+
+	/* This command requires a variable number of bytes to be written.
+	   There is a hack in wavefront_cmd() to support this. The actual
+	   count is passed in as the read buffer ptr, cast appropriately.
+	   Ugh.
+	*/
+
+	{ WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK },
+
+	/* This one is a hack as well. We just read the first byte of the
+	   response, don't fetch an ACK, and leave the rest to the 
+	   calling function. Ugly, ugly, ugly.
+	*/
+
+	{ WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 },
+	{ WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias",
+	  0, WF_ALIAS_BYTES, NEEDS_ACK },
+	{ WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0},
+	{ WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK },
+	{ WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 },
+	{ WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" },
+	{ WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 },
+	{ WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK },
+	{ WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 },
+	{ WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK },
+	{ WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 },
+	{ WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9,
+	  NEEDS_ACK},
+	{ WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0},
+	{ WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel",
+	  0, 1, NEEDS_ACK },
+	{ WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK },
+	{ WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers",
+	  32, 0, 0 },
+	{ WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK },
+	{ 0x00 }
+};
+
+static const char *
+wavefront_errorstr (int errnum)
+
+{
+	int i;
+
+	for (i = 0; wavefront_errors[i].errstr; i++) {
+		if (wavefront_errors[i].errno == errnum) {
+			return wavefront_errors[i].errstr;
+		}
+	}
+
+	return "Unknown WaveFront error";
+}
+
+static wavefront_command *
+wavefront_get_command (int cmd) 
+
+{
+	int i;
+
+	for (i = 0; wavefront_commands[i].cmd != 0; i++) {
+		if (cmd == wavefront_commands[i].cmd) {
+			return &wavefront_commands[i];
+		}
+	}
+
+	return (wavefront_command *) 0;
+}
+
+static inline int
+wavefront_status (void) 
+
+{
+	return inb (dev.status_port);
+}
+
+static int
+wavefront_wait (int mask)
+
+{
+	int i;
+
+	for (i = 0; i < wait_polls; i++)
+		if (wavefront_status() & mask)
+			return 1;
+
+	for (i = 0; i < sleep_tries; i++) {
+
+		if (wavefront_status() & mask) {
+			set_current_state(TASK_RUNNING);
+			return 1;
+		}
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(sleep_length);
+		if (signal_pending(current))
+			break;
+	}
+
+	set_current_state(TASK_RUNNING);
+	return 0;
+}
+
+static int
+wavefront_read (void)
+
+{
+	if (wavefront_wait (STAT_CAN_READ))
+		return inb (dev.data_port);
+
+	DPRINT (WF_DEBUG_DATA, "read timeout.\n");
+
+	return -1;
+}
+
+static int
+wavefront_write (unsigned char data)
+
+{
+	if (wavefront_wait (STAT_CAN_WRITE)) {
+		outb (data, dev.data_port);
+		return 0;
+	}
+
+	DPRINT (WF_DEBUG_DATA, "write timeout.\n");
+
+	return -1;
+}
+
+static int
+wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf)
+
+{
+	int ack;
+	int i;
+	int c;
+	wavefront_command *wfcmd;
+
+	if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) {
+		printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n",
+			cmd);
+		return 1;
+	}
+
+	/* Hack to handle the one variable-size write command. See
+	   wavefront_send_multisample() for the other half of this
+	   gross and ugly strategy.
+	*/
+
+	if (cmd == WFC_DOWNLOAD_MULTISAMPLE) {
+		wfcmd->write_cnt = (unsigned int) rbuf;
+		rbuf = 0;
+	}
+
+	DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n",
+			       cmd, wfcmd->action, wfcmd->read_cnt,
+			       wfcmd->write_cnt, wfcmd->need_ack);
+    
+	if (wavefront_write (cmd)) { 
+		DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request "
+						     "0x%x [%s].\n",
+						     cmd, wfcmd->action);
+		return 1;
+	} 
+
+	if (wfcmd->write_cnt > 0) {
+		DPRINT (WF_DEBUG_DATA, "writing %d bytes "
+					"for 0x%x\n",
+					wfcmd->write_cnt, cmd);
+
+		for (i = 0; i < wfcmd->write_cnt; i++) {
+			if (wavefront_write (wbuf[i])) {
+				DPRINT (WF_DEBUG_IO, "bad write for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
+				return 1;
+			}
+
+			DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n",
+						i, wbuf[i]);
+		}
+	}
+
+	if (wfcmd->read_cnt > 0) {
+		DPRINT (WF_DEBUG_DATA, "reading %d ints "
+					"for 0x%x\n",
+					wfcmd->read_cnt, cmd);
+
+		for (i = 0; i < wfcmd->read_cnt; i++) {
+
+			if ((c = wavefront_read()) == -1) {
+				DPRINT (WF_DEBUG_IO, "bad read for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
+				return 1;
+			}
+
+			/* Now handle errors. Lots of special cases here */
+	    
+			if (c == 0xff) { 
+				if ((c = wavefront_read ()) == -1) {
+					DPRINT (WF_DEBUG_IO, "bad read for "
+							      "error byte at "
+							      "read byte %d "
+							      "of 0x%x [%s].\n",
+							      i, cmd,
+							      wfcmd->action);
+					return 1;
+				}
+
+				/* Can you believe this madness ? */
+
+				if (c == 1 &&
+				    wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) {
+					rbuf[0] = WF_ST_EMPTY;
+					return (0);
+
+				} else if (c == 3 &&
+					   wfcmd->cmd == WFC_UPLOAD_PATCH) {
+
+					return 3;
+
+				} else if (c == 1 &&
+					   wfcmd->cmd == WFC_UPLOAD_PROGRAM) {
+
+					return 1;
+
+				} else {
+
+					DPRINT (WF_DEBUG_IO, "error %d (%s) "
+							      "during "
+							      "read for byte "
+							      "%d of 0x%x "
+							      "[%s].\n",
+							      c,
+							      wavefront_errorstr (c),
+							      i, cmd,
+							      wfcmd->action);
+					return 1;
+
+				}
+		
+		} else {
+				rbuf[i] = c;
+			}
+			
+			DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]);
+		}
+	}
+	
+	if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) {
+
+		DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd);
+
+		/* Some commands need an ACK, but return zero instead
+		   of the standard value.
+		*/
+	    
+		if ((ack = wavefront_read()) == 0) {
+			ack = WF_ACK;
+		}
+	
+		if (ack != WF_ACK) {
+			if (ack == -1) {
+				DPRINT (WF_DEBUG_IO, "cannot read ack for "
+						      "0x%x [%s].\n",
+						      cmd, wfcmd->action);
+				return 1;
+		
+			} else {
+				int err = -1; /* something unknown */
+
+				if (ack == 0xff) { /* explicit error */
+		    
+					if ((err = wavefront_read ()) == -1) {
+						DPRINT (WF_DEBUG_DATA,
+							"cannot read err "
+							"for 0x%x [%s].\n",
+							cmd, wfcmd->action);
+					}
+				}
+				
+				DPRINT (WF_DEBUG_IO, "0x%x [%s] "
+					"failed (0x%x, 0x%x, %s)\n",
+					cmd, wfcmd->action, ack, err,
+					wavefront_errorstr (err));
+				
+				return -err;
+			}
+		}
+		
+		DPRINT (WF_DEBUG_DATA, "ack received "
+					"for 0x%x [%s]\n",
+					cmd, wfcmd->action);
+	} else {
+
+		DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need "
+				       "ACK (%d,%d,%d)\n",
+				       cmd, wfcmd->action, wfcmd->read_cnt,
+				       wfcmd->write_cnt, wfcmd->need_ack);
+	}
+
+	return 0;
+	
+}
+
+/***********************************************************************
+WaveFront: data munging   
+
+Things here are weird. All data written to the board cannot 
+have its most significant bit set. Any data item with values 
+potentially > 0x7F (127) must be split across multiple bytes.
+
+Sometimes, we need to munge numeric values that are represented on
+the x86 side as 8-32 bit values. Sometimes, we need to munge data
+that is represented on the x86 side as an array of bytes. The most
+efficient approach to handling both cases seems to be to use 2
+different functions for munging and 2 for de-munging. This avoids
+weird casting and worrying about bit-level offsets.
+
+**********************************************************************/
+
+static 
+unsigned char *
+munge_int32 (unsigned int src,
+	     unsigned char *dst,
+	     unsigned int dst_size)
+{
+	int i;
+
+	for (i = 0;i < dst_size; i++) {
+		*dst = src & 0x7F;  /* Mask high bit of LSB */
+		src = src >> 7;     /* Rotate Right 7 bits  */
+	                            /* Note: we leave the upper bits in place */ 
+
+		dst++;
+ 	};
+	return dst;
+};
+
+static int 
+demunge_int32 (unsigned char* src, int src_size)
+
+{
+	int i;
+ 	int outval = 0;
+	
+ 	for (i = src_size - 1; i >= 0; i--) {
+		outval=(outval<<7)+src[i];
+	}
+
+	return outval;
+};
+
+static 
+unsigned char *
+munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size)
+
+{
+	int i;
+	unsigned int last = dst_size / 2;
+
+	for (i = 0; i < last; i++) {
+		*dst++ = src[i] & 0x7f;
+		*dst++ = src[i] >> 7;
+	}
+	return dst;
+}
+
+static 
+unsigned char *
+demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes)
+
+{
+	int i;
+	unsigned char *end = src + src_bytes;
+    
+	end = src + src_bytes;
+
+	/* NOTE: src and dst *CAN* point to the same address */
+
+	for (i = 0; src != end; i++) {
+		dst[i] = *src++;
+		dst[i] |= (*src++)<<7;
+	}
+
+	return dst;
+}
+
+/***********************************************************************
+WaveFront: sample, patch and program management.
+***********************************************************************/
+
+static int
+wavefront_delete_sample (int sample_num)
+
+{
+	unsigned char wbuf[2];
+	int x;
+
+	wbuf[0] = sample_num & 0x7f;
+	wbuf[1] = sample_num >> 7;
+
+	if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, 0, wbuf)) == 0) {
+		dev.sample_status[sample_num] = WF_ST_EMPTY;
+	}
+
+	return x;
+}
+
+static int
+wavefront_get_sample_status (int assume_rom)
+
+{
+	int i;
+	unsigned char rbuf[32], wbuf[32];
+	unsigned int    sc_real, sc_alias, sc_multi;
+
+	/* check sample status */
+    
+	if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) {
+		printk (KERN_WARNING LOGNAME "cannot request sample count.\n");
+		return -1;
+	} 
+    
+	sc_real = sc_alias = sc_multi = dev.samples_used = 0;
+    
+	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+	
+		wbuf[0] = i & 0x7f;
+		wbuf[1] = i >> 7;
+
+		if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
+			printk (KERN_WARNING LOGNAME
+				"cannot identify sample "
+				"type of slot %d\n", i);
+			dev.sample_status[i] = WF_ST_EMPTY;
+			continue;
+		}
+
+		dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]);
+
+		if (assume_rom) {
+			dev.sample_status[i] |= WF_SLOT_ROM;
+		}
+
+		switch (rbuf[0] & WF_ST_MASK) {
+		case WF_ST_SAMPLE:
+			sc_real++;
+			break;
+		case WF_ST_MULTISAMPLE:
+			sc_multi++;
+			break;
+		case WF_ST_ALIAS:
+			sc_alias++;
+			break;
+		case WF_ST_EMPTY:
+			break;
+
+		default:
+			printk (KERN_WARNING LOGNAME "unknown sample type for "
+				"slot %d (0x%x)\n", 
+				i, rbuf[0]);
+		}
+
+		if (rbuf[0] != WF_ST_EMPTY) {
+			dev.samples_used++;
+		} 
+	}
+
+	printk (KERN_INFO LOGNAME
+		"%d samples used (%d real, %d aliases, %d multi), "
+		"%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi,
+		WF_MAX_SAMPLE - dev.samples_used);
+
+
+	return (0);
+
+}
+
+static int
+wavefront_get_patch_status (void)
+
+{
+	unsigned char patchbuf[WF_PATCH_BYTES];
+	unsigned char patchnum[2];
+	wavefront_patch *p;
+	int i, x, cnt, cnt2;
+
+	for (i = 0; i < WF_MAX_PATCH; i++) {
+		patchnum[0] = i & 0x7f;
+		patchnum[1] = i >> 7;
+
+		if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf,
+					patchnum)) == 0) {
+
+			dev.patch_status[i] |= WF_SLOT_FILLED;
+			p = (wavefront_patch *) patchbuf;
+			dev.sample_status
+				[p->sample_number|(p->sample_msb<<7)] |=
+				WF_SLOT_USED;
+	    
+		} else if (x == 3) { /* Bad patch number */
+			dev.patch_status[i] = 0;
+		} else {
+			printk (KERN_ERR LOGNAME "upload patch "
+				"error 0x%x\n", x);
+			dev.patch_status[i] = 0;
+			return 1;
+		}
+	}
+
+	/* program status has already filled in slot_used bits */
+
+	for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) {
+		if (dev.patch_status[i] & WF_SLOT_FILLED) {
+			cnt++;
+		}
+		if (dev.patch_status[i] & WF_SLOT_USED) {
+			cnt2++;
+		}
+	
+	}
+	printk (KERN_INFO LOGNAME
+		"%d patch slots filled, %d in use\n", cnt, cnt2);
+
+	return (0);
+}
+
+static int
+wavefront_get_program_status (void)
+
+{
+	unsigned char progbuf[WF_PROGRAM_BYTES];
+	wavefront_program prog;
+	unsigned char prognum;
+	int i, x, l, cnt;
+
+	for (i = 0; i < WF_MAX_PROGRAM; i++) {
+		prognum = i;
+
+		if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf,
+					&prognum)) == 0) {
+
+			dev.prog_status[i] |= WF_SLOT_USED;
+
+			demunge_buf (progbuf, (unsigned char *) &prog,
+				     WF_PROGRAM_BYTES);
+
+			for (l = 0; l < WF_NUM_LAYERS; l++) {
+				if (prog.layer[l].mute) {
+					dev.patch_status
+						[prog.layer[l].patch_number] |=
+						WF_SLOT_USED;
+				}
+			}
+		} else if (x == 1) { /* Bad program number */
+			dev.prog_status[i] = 0;
+		} else {
+			printk (KERN_ERR LOGNAME "upload program "
+				"error 0x%x\n", x);
+			dev.prog_status[i] = 0;
+		}
+	}
+
+	for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) {
+		if (dev.prog_status[i]) {
+			cnt++;
+		}
+	}
+
+	printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt);
+
+	return (0);
+}
+
+static int
+wavefront_send_patch (wavefront_patch_info *header)
+
+{
+	unsigned char buf[WF_PATCH_BYTES+2];
+	unsigned char *bptr;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
+				      header->number);
+
+	dev.patch_status[header->number] |= WF_SLOT_FILLED;
+
+	bptr = buf;
+	bptr = munge_int32 (header->number, buf, 2);
+	munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
+    
+	if (wavefront_cmd (WFC_DOWNLOAD_PATCH, 0, buf)) {
+		printk (KERN_ERR LOGNAME "download patch failed\n");
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int
+wavefront_send_program (wavefront_patch_info *header)
+
+{
+	unsigned char buf[WF_PROGRAM_BYTES+1];
+	int i;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
+		header->number);
+
+	dev.prog_status[header->number] = WF_SLOT_USED;
+
+	/* XXX need to zero existing SLOT_USED bit for program_status[i]
+	   where `i' is the program that's being (potentially) overwritten.
+	*/
+    
+	for (i = 0; i < WF_NUM_LAYERS; i++) {
+		if (header->hdr.pr.layer[i].mute) {
+			dev.patch_status[header->hdr.pr.layer[i].patch_number] |=
+				WF_SLOT_USED;
+
+			/* XXX need to mark SLOT_USED for sample used by
+			   patch_number, but this means we have to load it. Ick.
+			*/
+		}
+	}
+
+	buf[0] = header->number;
+	munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
+    
+	if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, 0, buf)) {
+		printk (KERN_WARNING LOGNAME "download patch failed\n");	
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int
+wavefront_freemem (void)
+
+{
+	char rbuf[8];
+
+	if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, 0)) {
+		printk (KERN_WARNING LOGNAME "can't get memory stats.\n");
+		return -1;
+	} else {
+		return demunge_int32 (rbuf, 4);
+	}
+}
+
+static int
+wavefront_send_sample (wavefront_patch_info *header,
+		       UINT16 *dataptr,
+		       int data_is_unsigned)
+
+{
+	/* samples are downloaded via a 16-bit wide i/o port
+	   (you could think of it as 2 adjacent 8-bit wide ports
+	   but its less efficient that way). therefore, all
+	   the blocksizes and so forth listed in the documentation,
+	   and used conventionally to refer to sample sizes,
+	   which are given in 8-bit units (bytes), need to be
+	   divided by 2.
+        */
+
+	UINT16 sample_short;
+	UINT32 length;
+	UINT16 *data_end = 0;
+	unsigned int i;
+	const int max_blksize = 4096/2;
+	unsigned int written;
+	unsigned int blocksize;
+	int dma_ack;
+	int blocknum;
+	unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES];
+	unsigned char *shptr;
+	int skip = 0;
+	int initial_skip = 0;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, "
+				      "type %d, %d bytes from 0x%x\n",
+				      header->size ? "" : "header ", 
+				      header->number, header->subkey,
+				      header->size,
+				      (int) header->dataptr);
+
+	if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
+		int x;
+
+		if ((x = wavefront_find_free_sample ()) < 0) {
+			return -ENOMEM;
+		}
+		printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x);
+		header->number = x;
+	}
+
+	if (header->size) {
+
+		/* XXX its a debatable point whether or not RDONLY semantics
+		   on the ROM samples should cover just the sample data or
+		   the sample header. For now, it only covers the sample data,
+		   so anyone is free at all times to rewrite sample headers.
+
+		   My reason for this is that we have the sample headers
+		   available in the WFB file for General MIDI, and so these
+		   can always be reset if needed. The sample data, however,
+		   cannot be recovered without a complete reset and firmware
+		   reload of the ICS2115, which is a very expensive operation.
+
+		   So, doing things this way allows us to honor the notion of
+		   "RESETSAMPLES" reasonably cheaply. Note however, that this
+		   is done purely at user level: there is no WFB parser in
+		   this driver, and so a complete reset (back to General MIDI,
+		   or theoretically some other configuration) is the
+		   responsibility of the user level library. 
+
+		   To try to do this in the kernel would be a little
+		   crazy: we'd need 158K of kernel space just to hold
+		   a copy of the patch/program/sample header data.
+		*/
+
+		if (dev.rom_samples_rdonly) {
+			if (dev.sample_status[header->number] & WF_SLOT_ROM) {
+				printk (KERN_ERR LOGNAME "sample slot %d "
+					"write protected\n",
+					header->number);
+				return -EACCES;
+			}
+		}
+
+		wavefront_delete_sample (header->number);
+	}
+
+	if (header->size) {
+		dev.freemem = wavefront_freemem ();
+
+		if (dev.freemem < header->size) {
+			printk (KERN_ERR LOGNAME
+				"insufficient memory to "
+				"load %d byte sample.\n",
+				header->size);
+			return -ENOMEM;
+		}
+	
+	}
+
+	skip = WF_GET_CHANNEL(&header->hdr.s);
+
+	if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
+		printk (KERN_ERR LOGNAME "channel selection only "
+			"possible on 16-bit samples");
+		return -(EINVAL);
+	}
+
+	switch (skip) {
+	case 0:
+		initial_skip = 0;
+		skip = 1;
+		break;
+	case 1:
+		initial_skip = 0;
+		skip = 2;
+		break;
+	case 2:
+		initial_skip = 1;
+		skip = 2;
+		break;
+	case 3:
+		initial_skip = 2;
+		skip = 3;
+		break;
+	case 4:
+		initial_skip = 3;
+		skip = 4;
+		break;
+	case 5:
+		initial_skip = 4;
+		skip = 5;
+		break;
+	case 6:
+		initial_skip = 5;
+		skip = 6;
+		break;
+	}
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => "
+				      "initial skip = %d, skip = %d\n",
+				      WF_GET_CHANNEL (&header->hdr.s),
+				      initial_skip, skip);
+    
+	/* Be safe, and zero the "Unused" bits ... */
+
+	WF_SET_CHANNEL(&header->hdr.s, 0);
+
+	/* adjust size for 16 bit samples by dividing by two.  We always
+	   send 16 bits per write, even for 8 bit samples, so the length
+	   is always half the size of the sample data in bytes.
+	*/
+
+	length = header->size / 2;
+
+	/* the data we're sent has not been munged, and in fact, the
+	   header we have to send isn't just a munged copy either.
+	   so, build the sample header right here.
+	*/
+
+	shptr = &sample_hdr[0];
+
+	shptr = munge_int32 (header->number, shptr, 2);
+
+	if (header->size) {
+		shptr = munge_int32 (length, shptr, 4);
+	}
+
+	/* Yes, a 4 byte result doesn't contain all of the offset bits,
+	   but the offset only uses 24 bits.
+	*/
+
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset),
+			     shptr, 4);
+	
+	/* This one is truly weird. What kind of weirdo decided that in
+	   a system dominated by 16 and 32 bit integers, they would use
+	   a just 12 bits ?
+	*/
+	
+	shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3);
+	
+	/* Why is this nybblified, when the MSB is *always* zero ? 
+	   Anyway, we can't take address of bitfield, so make a
+	   good-faith guess at where it starts.
+	*/
+	
+	shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1),
+			     shptr, 2);
+
+	if (wavefront_cmd (header->size ?
+			   WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
+			   0, sample_hdr)) {
+		printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n",
+			header->size ? "" : "header ");
+		return -(EIO);
+	}
+
+	if (header->size == 0) {
+		goto sent; /* Sorry. Just had to have one somewhere */
+	}
+    
+	data_end = dataptr + length;
+
+	/* Do any initial skip over an unused channel's data */
+
+	dataptr += initial_skip;
+    
+	for (written = 0, blocknum = 0;
+	     written < length; written += max_blksize, blocknum++) {
+	
+		if ((length - written) > max_blksize) {
+			blocksize = max_blksize;
+		} else {
+			/* round to nearest 16-byte value */
+			blocksize = ((length-written+7)&~0x7);
+		}
+
+		if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, 0, 0)) {
+			printk (KERN_WARNING LOGNAME "download block "
+				"request refused.\n");
+			return -(EIO);
+		}
+
+		for (i = 0; i < blocksize; i++) {
+
+			if (dataptr < data_end) {
+		
+				__get_user (sample_short, dataptr);
+				dataptr += skip;
+		
+				if (data_is_unsigned) { /* GUS ? */
+
+					if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) {
+			
+						/* 8 bit sample
+						 resolution, sign
+						 extend both bytes.
+						*/
+			
+						((unsigned char*)
+						 &sample_short)[0] += 0x7f;
+						((unsigned char*)
+						 &sample_short)[1] += 0x7f;
+			
+					} else {
+			
+						/* 16 bit sample
+						 resolution, sign
+						 extend the MSB.
+						*/
+			
+						sample_short += 0x7fff;
+					}
+				}
+
+			} else {
+
+				/* In padding section of final block:
+
+				   Don't fetch unsupplied data from
+				   user space, just continue with
+				   whatever the final value was.
+				*/
+			}
+	    
+			if (i < blocksize - 1) {
+				outw (sample_short, dev.block_port);
+			} else {
+				outw (sample_short, dev.last_block_port);
+			}
+		}
+
+		/* Get "DMA page acknowledge", even though its really
+		   nothing to do with DMA at all.
+		*/
+	
+		if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) {
+			if (dma_ack == -1) {
+				printk (KERN_ERR LOGNAME "upload sample "
+					"DMA ack timeout\n");
+				return -(EIO);
+			} else {
+				printk (KERN_ERR LOGNAME "upload sample "
+					"DMA ack error 0x%x\n",
+					dma_ack);
+				return -(EIO);
+			}
+		}
+	}
+
+	dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE);
+
+	/* Note, label is here because sending the sample header shouldn't
+	   alter the sample_status info at all.
+	*/
+
+ sent:
+	return (0);
+}
+
+static int
+wavefront_send_alias (wavefront_patch_info *header)
+
+{
+	unsigned char alias_hdr[WF_ALIAS_BYTES];
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is "
+				      "alias for %d\n",
+				      header->number,
+				      header->hdr.a.OriginalSample);
+    
+	munge_int32 (header->number, &alias_hdr[0], 2);
+	munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
+	munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
+		     &alias_hdr[4], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset),
+		     &alias_hdr[8], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset),
+		     &alias_hdr[12], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset),
+		     &alias_hdr[16], 4);
+	munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3);
+	munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
+
+	if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) {
+		printk (KERN_ERR LOGNAME "download alias failed.\n");
+		return -(EIO);
+	}
+
+	dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
+
+	return (0);
+}
+
+static int
+wavefront_send_multisample (wavefront_patch_info *header)
+{
+	int i;
+	int num_samples;
+	unsigned char msample_hdr[WF_MSAMPLE_BYTES];
+
+	munge_int32 (header->number, &msample_hdr[0], 2);
+
+	/* You'll recall at this point that the "number of samples" value
+	   in a wavefront_multisample struct is actually the log2 of the
+	   real number of samples.
+	*/
+
+	num_samples = (1<<(header->hdr.ms.NumberOfSamples&7));
+	msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n",
+				      header->number,
+				      header->hdr.ms.NumberOfSamples,
+				      num_samples);
+
+	for (i = 0; i < num_samples; i++) {
+		DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n",
+		       i, header->hdr.ms.SampleNumber[i]);
+		munge_int32 (header->hdr.ms.SampleNumber[i],
+		     &msample_hdr[3+(i*2)], 2);
+	}
+    
+	/* Need a hack here to pass in the number of bytes
+	   to be written to the synth. This is ugly, and perhaps
+	   one day, I'll fix it.
+	*/
+
+	if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, 
+			   (unsigned char *) ((num_samples*2)+3),
+			   msample_hdr)) {
+		printk (KERN_ERR LOGNAME "download of multisample failed.\n");
+		return -(EIO);
+	}
+
+	dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
+
+	return (0);
+}
+
+static int
+wavefront_fetch_multisample (wavefront_patch_info *header)
+{
+	int i;
+	unsigned char log_ns[1];
+	unsigned char number[2];
+	int num_samples;
+
+	munge_int32 (header->number, number, 2);
+    
+	if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
+		printk (KERN_ERR LOGNAME "upload multisample failed.\n");
+		return -(EIO);
+	}
+    
+	DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
+				header->number, log_ns[0]);
+
+	header->hdr.ms.NumberOfSamples = log_ns[0];
+
+	/* get the number of samples ... */
+
+	num_samples = (1 << log_ns[0]);
+    
+	for (i = 0; i < num_samples; i++) {
+		char d[2];
+	
+		if ((d[0] = wavefront_read ()) == -1) {
+			printk (KERN_ERR LOGNAME "upload multisample failed "
+				"during sample loop.\n");
+			return -(EIO);
+		}
+
+		if ((d[1] = wavefront_read ()) == -1) {
+			printk (KERN_ERR LOGNAME "upload multisample failed "
+				"during sample loop.\n");
+			return -(EIO);
+		}
+	
+		header->hdr.ms.SampleNumber[i] =
+			demunge_int32 ((unsigned char *) d, 2);
+	
+		DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n",
+					i, header->hdr.ms.SampleNumber[i]);
+	}
+
+	return (0);
+}
+
+
+static int
+wavefront_send_drum (wavefront_patch_info *header)
+
+{
+	unsigned char drumbuf[WF_DRUM_BYTES];
+	wavefront_drum *drum = &header->hdr.d;
+	int i;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI "
+		"note %d, patch = %d\n", 
+		header->number, drum->PatchNumber);
+
+	drumbuf[0] = header->number & 0x7f;
+
+	for (i = 0; i < 4; i++) {
+		munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2);
+	}
+
+	if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) {
+		printk (KERN_ERR LOGNAME "download drum failed.\n");
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int 
+wavefront_find_free_sample (void)
+
+{
+	int i;
+
+	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+		if (!(dev.sample_status[i] & WF_SLOT_FILLED)) {
+			return i;
+		}
+	}
+	printk (KERN_WARNING LOGNAME "no free sample slots!\n");
+	return -1;
+}
+
+static int 
+wavefront_find_free_patch (void)
+
+{
+	int i;
+
+	for (i = 0; i < WF_MAX_PATCH; i++) {
+		if (!(dev.patch_status[i] & WF_SLOT_FILLED)) {
+			return i;
+		}
+	}
+	printk (KERN_WARNING LOGNAME "no free patch slots!\n");
+	return -1;
+}
+
+static int 
+log2_2048(int n)
+
+{
+	int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143,
+		   6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192,
+		   8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390,
+		   9510, 9626, 9738, 9845, 9949, 10049, 10146};
+	int i;
+
+	/* Returns 2048*log2(n) */
+
+	/* FIXME: this is like doing integer math
+	   on quantum particles (RuN) */
+
+	i=0;
+	while(n>=32*256) {
+		n>>=8;
+		i+=2048*8;
+	}
+	while(n>=32) {
+		n>>=1;
+		i+=2048;
+	}
+	i+=tbl[n];
+	return(i);
+}
+
+static int
+wavefront_load_gus_patch (int devno, int format, const char *addr,
+			  int offs, int count, int pmgr_flag)
+{
+	struct patch_info guspatch;
+	wavefront_patch_info samp, pat, prog;
+	wavefront_patch *patp;
+	wavefront_sample *sampp;
+	wavefront_program *progp;
+
+	int i,base_note;
+	long sizeof_patch;
+
+	/* Copy in the header of the GUS patch */
+
+	sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; 
+	copy_from_user (&((char *) &guspatch)[offs],
+			&(addr)[offs], sizeof_patch - offs);
+
+	if ((i = wavefront_find_free_patch ()) == -1) {
+		return -EBUSY;
+	}
+	pat.number = i;
+	pat.subkey = WF_ST_PATCH;
+	patp = &pat.hdr.p;
+
+	if ((i = wavefront_find_free_sample ()) == -1) {
+		return -EBUSY;
+	}
+	samp.number = i;
+	samp.subkey = WF_ST_SAMPLE;
+	samp.size = guspatch.len;
+	sampp = &samp.hdr.s;
+
+	prog.number = guspatch.instr_no;
+	progp = &prog.hdr.pr;
+
+	/* Setup the patch structure */
+
+	patp->amplitude_bias=guspatch.volume;
+	patp->portamento=0;
+	patp->sample_number= samp.number & 0xff;
+	patp->sample_msb= samp.number>>8;
+	patp->pitch_bend= /*12*/ 0;
+	patp->mono=1;
+	patp->retrigger=1;
+	patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1;
+	patp->frequency_bias=0;
+	patp->restart=0;
+	patp->reuse=0;
+	patp->reset_lfo=1;
+	patp->fm_src2=0;
+	patp->fm_src1=WF_MOD_MOD_WHEEL;
+	patp->am_src=WF_MOD_PRESSURE;
+	patp->am_amount=127;
+	patp->fc1_mod_amount=0;
+	patp->fc2_mod_amount=0; 
+	patp->fm_amount1=0;
+	patp->fm_amount2=0;
+	patp->envelope1.attack_level=127;
+	patp->envelope1.decay1_level=127;
+	patp->envelope1.decay2_level=127;
+	patp->envelope1.sustain_level=127;
+	patp->envelope1.release_level=0;
+	patp->envelope2.attack_velocity=127;
+	patp->envelope2.attack_level=127;
+	patp->envelope2.decay1_level=127;
+	patp->envelope2.decay2_level=127;
+	patp->envelope2.sustain_level=127;
+	patp->envelope2.release_level=0;
+	patp->envelope2.attack_velocity=127;
+	patp->randomizer=0;
+
+	/* Program for this patch */
+
+	progp->layer[0].patch_number= pat.number; /* XXX is this right ? */
+	progp->layer[0].mute=1;
+	progp->layer[0].pan_or_mod=1;
+	progp->layer[0].pan=7;
+	progp->layer[0].mix_level=127  /* guspatch.volume */;
+	progp->layer[0].split_type=0;
+	progp->layer[0].split_point=0;
+	progp->layer[0].play_below=0;
+
+	for (i = 1; i < 4; i++) {
+		progp->layer[i].mute=0;
+	}
+
+	/* Sample data */
+
+	sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1);
+
+	for (base_note=0;
+	     note_to_freq (base_note) < guspatch.base_note;
+	     base_note++);
+
+	if ((guspatch.base_note-note_to_freq(base_note))
+	    >(note_to_freq(base_note)-guspatch.base_note))
+		base_note++;
+
+	printk(KERN_DEBUG "ref freq=%d,base note=%d\n",
+	       guspatch.base_freq,
+	       base_note);
+
+	sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq)
+				+ base_note*171);
+	printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias);
+	sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0;
+	sampp->sampleStartOffset.Fraction=0;
+	sampp->sampleStartOffset.Integer=0;
+	sampp->loopStartOffset.Fraction=0;
+	sampp->loopStartOffset.Integer=guspatch.loop_start
+		>>((guspatch.mode&WAVE_16_BITS) ? 1:0);
+	sampp->loopEndOffset.Fraction=0;
+	sampp->loopEndOffset.Integer=guspatch.loop_end
+		>>((guspatch.mode&WAVE_16_BITS) ? 1:0);
+	sampp->sampleEndOffset.Fraction=0;
+	sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1);
+	sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0;
+	sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0;
+
+	/* Now ship it down */
+
+	wavefront_send_sample (&samp, 
+			       (unsigned short *) &(addr)[sizeof_patch],
+			       (guspatch.mode & WAVE_UNSIGNED) ? 1:0);
+	wavefront_send_patch (&pat);
+	wavefront_send_program (&prog);
+
+	/* Now pan as best we can ... use the slave/internal MIDI device
+	   number if it exists (since it talks to the WaveFront), or the
+	   master otherwise.
+	*/
+
+	if (dev.mididev > 0) {
+		midi_synth_controller (dev.mididev, guspatch.instr_no, 10,
+				       ((guspatch.panning << 4) > 127) ?
+				       127 : (guspatch.panning << 4));
+	}
+
+	return(0);
+}
+
+static int
+wavefront_load_patch (const char *addr)
+
+
+{
+	wavefront_patch_info header;
+	
+	if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) -
+			    sizeof(wavefront_any))) {
+		printk (KERN_WARNING LOGNAME "bad address for load patch.\n");
+		return -(EINVAL);
+	}
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download "
+				      "Sample type: %d "
+				      "Sample number: %d "
+				      "Sample size: %d\n",
+				      header.subkey,
+				      header.number,
+				      header.size);
+
+	switch (header.subkey) {
+	case WF_ST_SAMPLE:  /* sample or sample_header, based on patch->size */
+
+		copy_from_user ((unsigned char *) &header.hdr.s,
+				(unsigned char *) header.hdrptr,
+				sizeof (wavefront_sample));
+
+		return wavefront_send_sample (&header, header.dataptr, 0);
+
+	case WF_ST_MULTISAMPLE:
+
+		copy_from_user ((unsigned char *) &header.hdr.s,
+				(unsigned char *) header.hdrptr,
+				sizeof (wavefront_multisample));
+
+		return wavefront_send_multisample (&header);
+
+
+	case WF_ST_ALIAS:
+
+		copy_from_user ((unsigned char *) &header.hdr.a,
+				(unsigned char *) header.hdrptr,
+				sizeof (wavefront_alias));
+
+		return wavefront_send_alias (&header);
+
+	case WF_ST_DRUM:
+		copy_from_user ((unsigned char *) &header.hdr.d, 
+				(unsigned char *) header.hdrptr,
+				sizeof (wavefront_drum));
+
+		return wavefront_send_drum (&header);
+
+	case WF_ST_PATCH:
+		copy_from_user ((unsigned char *) &header.hdr.p, 
+				(unsigned char *) header.hdrptr,
+				sizeof (wavefront_patch));
+
+		return wavefront_send_patch (&header);
+
+	case WF_ST_PROGRAM:
+		copy_from_user ((unsigned char *) &header.hdr.pr, 
+				(unsigned char *) header.hdrptr,
+				sizeof (wavefront_program));
+
+		return wavefront_send_program (&header);
+
+	default:
+		printk (KERN_ERR LOGNAME "unknown patch type %d.\n",
+			header.subkey);
+		return -(EINVAL);
+	}
+
+	return 0;
+}
+
+/***********************************************************************
+WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces
+***********************************************************************/
+
+static void
+process_sample_hdr (UCHAR8 *buf)
+
+{
+	wavefront_sample s;
+	UCHAR8 *ptr;
+
+	ptr = buf;
+
+	/* The board doesn't send us an exact copy of a "wavefront_sample"
+	   in response to an Upload Sample Header command. Instead, we 
+	   have to convert the data format back into our data structure,
+	   just as in the Download Sample command, where we have to do
+	   something very similar in the reverse direction.
+	*/
+
+	*((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3;
+
+	s.SampleResolution = *ptr & 0x3;
+	s.Loop = *ptr & 0x8;
+	s.Bidirectional = *ptr & 0x10;
+	s.Reverse = *ptr & 0x40;
+
+	/* Now copy it back to where it came from */
+
+	memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample));
+}
+
+static int
+wavefront_synth_control (int cmd, wavefront_control *wc)
+
+{
+	unsigned char patchnumbuf[2];
+	int i;
+
+	DPRINT (WF_DEBUG_CMD, "synth control with "
+		"cmd 0x%x\n", wc->cmd);
+
+	/* Pre-handling of or for various commands */
+
+	switch (wc->cmd) {
+	case WFC_DISABLE_INTERRUPTS:
+		printk (KERN_INFO LOGNAME "interrupts disabled.\n");
+		outb (0x80|0x20, dev.control_port);
+		dev.interrupts_on = 0;
+		return 0;
+
+	case WFC_ENABLE_INTERRUPTS:
+		printk (KERN_INFO LOGNAME "interrupts enabled.\n");
+		outb (0x80|0x40|0x20, dev.control_port);
+		dev.interrupts_on = 1;
+		return 0;
+
+	case WFC_INTERRUPT_STATUS:
+		wc->rbuf[0] = dev.interrupts_on;
+		return 0;
+
+	case WFC_ROMSAMPLES_RDONLY:
+		dev.rom_samples_rdonly = wc->wbuf[0];
+		wc->status = 0;
+		return 0;
+
+	case WFC_IDENTIFY_SLOT_TYPE:
+		i = wc->wbuf[0] | (wc->wbuf[1] << 7);
+		if (i <0 || i >= WF_MAX_SAMPLE) {
+			printk (KERN_WARNING LOGNAME "invalid slot ID %d\n",
+				i);
+			wc->status = EINVAL;
+			return 0;
+		}
+		wc->rbuf[0] = dev.sample_status[i];
+		wc->status = 0;
+		return 0;
+
+	case WFC_DEBUG_DRIVER:
+		dev.debug = wc->wbuf[0];
+		printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug);
+		return 0;
+
+	case WFC_FX_IOCTL:
+		wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]);
+		return 0;
+
+	case WFC_UPLOAD_PATCH:
+		munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2);
+		memcpy (wc->wbuf, patchnumbuf, 2);
+		break;
+
+	case WFC_UPLOAD_MULTISAMPLE:
+		/* multisamples have to be handled differently, and
+		   cannot be dealt with properly by wavefront_cmd() alone.
+		*/
+		wc->status = wavefront_fetch_multisample
+			((wavefront_patch_info *) wc->rbuf);
+		return 0;
+
+	case WFC_UPLOAD_SAMPLE_ALIAS:
+		printk (KERN_INFO LOGNAME "support for sample alias upload "
+			"being considered.\n");
+		wc->status = EINVAL;
+		return -EINVAL;
+	}
+
+	wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf);
+
+	/* Post-handling of certain commands.
+
+	   In particular, if the command was an upload, demunge the data
+	   so that the user-level doesn't have to think about it.
+	*/
+
+	if (wc->status == 0) {
+		switch (wc->cmd) {
+			/* intercept any freemem requests so that we know
+			   we are always current with the user-level view
+			   of things.
+			*/
+
+		case WFC_REPORT_FREE_MEMORY:
+			dev.freemem = demunge_int32 (wc->rbuf, 4);
+			break;
+
+		case WFC_UPLOAD_PATCH:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES);
+			break;
+
+		case WFC_UPLOAD_PROGRAM:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES);
+			break;
+
+		case WFC_UPLOAD_EDRUM_PROGRAM:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1);
+			break;
+
+		case WFC_UPLOAD_SAMPLE_HEADER:
+			process_sample_hdr (wc->rbuf);
+			break;
+
+		case WFC_UPLOAD_SAMPLE_ALIAS:
+			printk (KERN_INFO LOGNAME "support for "
+				"sample aliases still "
+				"being considered.\n");
+			break;
+
+		case WFC_VMIDI_OFF:
+			if (virtual_midi_disable () < 0) {
+				return -(EIO);
+			}
+			break;
+
+		case WFC_VMIDI_ON:
+			if (virtual_midi_enable () < 0) {
+				return -(EIO);
+			}
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+/***********************************************************************/
+/* WaveFront: Linux file system interface (for access via raw synth)    */
+/***********************************************************************/
+
+static int 
+wavefront_open (struct inode *inode, struct file *file)
+{
+	/* XXX fix me */
+	dev.opened = file->f_flags;
+	return 0;
+}
+
+static int
+wavefront_release(struct inode *inode, struct file *file)
+{
+	lock_kernel();
+	dev.opened = 0;
+	dev.debug = 0;
+	unlock_kernel();
+	return 0;
+}
+
+static int
+wavefront_ioctl(struct inode *inode, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	wavefront_control wc;
+	int err;
+
+	switch (cmd) {
+
+	case WFCTL_WFCMD:
+		copy_from_user (&wc, (void *) arg, sizeof (wc));
+		
+		if ((err = wavefront_synth_control (cmd, &wc)) == 0) {
+			copy_to_user ((void *) arg, &wc, sizeof (wc));
+		}
+
+		return err;
+		
+	case WFCTL_LOAD_SPP:
+		return wavefront_load_patch ((const char *) arg);
+		
+	default:
+		printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd);
+		return -(EINVAL);
+
+	}
+	return 0;
+}
+
+static /*const*/ struct file_operations wavefront_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		wavefront_ioctl,
+	open:		wavefront_open,
+	release:	wavefront_release,
+};
+
+
+/***********************************************************************/
+/* WaveFront: OSS installation and support interface                   */
+/***********************************************************************/
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+
+static struct synth_info wavefront_info =
+{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT,
+ 0, 32, 0, 0, SYNTH_CAP_INPUT};
+
+static int
+wavefront_oss_open (int devno, int mode)
+
+{
+	dev.opened = mode;
+	return 0;
+}
+
+static void
+wavefront_oss_close (int devno)
+
+{
+	dev.opened = 0;
+	dev.debug = 0;
+	return;
+}
+
+static int
+wavefront_oss_ioctl (int devno, unsigned int cmd, caddr_t arg)
+
+{
+	wavefront_control wc;
+	int err;
+
+	switch (cmd) {
+	case SNDCTL_SYNTH_INFO:
+		if(copy_to_user(&((char *) arg)[0], &wavefront_info,
+			sizeof (wavefront_info)))
+			return -EFAULT;
+		return 0;
+
+	case SNDCTL_SEQ_RESETSAMPLES:
+//		printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n");
+		return 0; /* don't force an error */
+
+	case SNDCTL_SEQ_PERCMODE:
+		return 0; /* don't force an error */
+
+	case SNDCTL_SYNTH_MEMAVL:
+		if ((dev.freemem = wavefront_freemem ()) < 0) {
+			printk (KERN_ERR LOGNAME "cannot get memory size\n");
+			return -EIO;
+		} else {
+			return dev.freemem;
+		}
+		break;
+
+	case SNDCTL_SYNTH_CONTROL:
+		if(copy_from_user (&wc, arg, sizeof (wc)))
+			err = -EFAULT;
+		else if ((err = wavefront_synth_control (cmd, &wc)) == 0) {
+			if(copy_to_user (arg, &wc, sizeof (wc)))
+				err = -EFAULT;
+		}
+
+		return err;
+
+	default:
+		return -(EINVAL);
+	}
+}
+
+int
+wavefront_oss_load_patch (int devno, int format, const char *addr,
+			  int offs, int count, int pmgr_flag)
+{
+
+	if (format == SYSEX_PATCH) {	/* Handled by midi_synth.c */
+		if (midi_load_patch == NULL) {
+			printk (KERN_ERR LOGNAME
+				"SYSEX not loadable: "
+				"no midi patch loader!\n");
+			return -(EINVAL);
+		}
+
+		return midi_load_patch (devno, format, addr,
+					offs, count, pmgr_flag);
+
+	} else if (format == GUS_PATCH) {
+		return wavefront_load_gus_patch (devno, format,
+						 addr, offs, count, pmgr_flag);
+
+	} else if (format != WAVEFRONT_PATCH) {
+		printk (KERN_ERR LOGNAME "unknown patch format %d\n", format);
+		return -(EINVAL);
+	}
+
+	if (count < sizeof (wavefront_patch_info)) {
+		printk (KERN_ERR LOGNAME "sample header too short\n");
+		return -(EINVAL);
+	}
+
+	/* "addr" points to a user-space wavefront_patch_info */
+
+	return wavefront_load_patch (addr);
+}	
+
+static struct synth_operations wavefront_operations =
+{
+	owner:		THIS_MODULE,
+	id:		"WaveFront",
+	info:		&wavefront_info,
+	midi_dev:	0,
+	synth_type:	SYNTH_TYPE_SAMPLE,
+	synth_subtype:	SAMPLE_TYPE_WAVEFRONT,
+	open:		wavefront_oss_open,
+	close:		wavefront_oss_close,
+	ioctl:		wavefront_oss_ioctl,
+	kill_note:	midi_synth_kill_note,
+	start_note:	midi_synth_start_note,
+	set_instr:	midi_synth_set_instr,
+	reset:		midi_synth_reset,
+	load_patch:	midi_synth_load_patch,
+	aftertouch:	midi_synth_aftertouch,
+	controller:	midi_synth_controller,
+	panning:	midi_synth_panning,
+	bender:		midi_synth_bender,
+	setup_voice:	midi_synth_setup_voice
+};
+#endif /* OSS_SUPPORT_SEQ */
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL
+
+static void __init attach_wavefront (struct address_info *hw_config)
+{
+    (void) install_wavefront ();
+}
+
+static int __init probe_wavefront (struct address_info *hw_config)
+{
+    return !detect_wavefront (hw_config->irq, hw_config->io_base);
+}
+
+static void __exit unload_wavefront (struct address_info *hw_config) 
+{
+    (void) uninstall_wavefront ();
+}
+
+#endif /* OSS_SUPPORT_STATIC_INSTALL */
+
+/***********************************************************************/
+/* WaveFront: Linux modular sound kernel installation interface        */
+/***********************************************************************/
+
+void
+wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy)
+{
+	struct wf_config *hw = dev_id;
+
+	/*
+	   Some comments on interrupts. I attempted a version of this
+	   driver that used interrupts throughout the code instead of
+	   doing busy and/or sleep-waiting. Alas, it appears that once
+	   the Motorola firmware is downloaded, the card *never*
+	   generates an RX interrupt. These are successfully generated
+	   during firmware loading, and after that wavefront_status()
+	   reports that an interrupt is pending on the card from time
+	   to time, but it never seems to be delivered to this
+	   driver. Note also that wavefront_status() continues to
+	   report that RX interrupts are enabled, suggesting that I
+	   didn't goof up and disable them by mistake.
+
+	   Thus, I stepped back to a prior version of
+	   wavefront_wait(), the only place where this really
+	   matters. Its sad, but I've looked through the code to check
+	   on things, and I really feel certain that the Motorola
+	   firmware prevents RX-ready interrupts.
+	*/
+
+	if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) {
+		return;
+	}
+
+	hw->irq_ok = 1;
+	hw->irq_cnt++;
+	wake_up_interruptible (&hw->interrupt_sleeper);
+}
+
+/* STATUS REGISTER 
+
+0 Host Rx Interrupt Enable (1=Enabled)
+1 Host Rx Register Full (1=Full)
+2 Host Rx Interrupt Pending (1=Interrupt)
+3 Unused
+4 Host Tx Interrupt (1=Enabled)
+5 Host Tx Register empty (1=Empty)
+6 Host Tx Interrupt Pending (1=Interrupt)
+7 Unused
+*/
+
+int
+wavefront_interrupt_bits (int irq)
+
+{
+	int bits;
+
+	switch (irq) {
+	case 9:
+		bits = 0x00;
+		break;
+	case 5:
+		bits = 0x08;
+		break;
+	case 12:
+		bits = 0x10;
+		break;
+	case 15:
+		bits = 0x18;
+		break;
+	
+	default:
+		printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq);
+		bits = -1;
+	}
+
+	return bits;
+}
+
+void
+wavefront_should_cause_interrupt (int val, int port, int timeout)
+
+{
+	unsigned long flags;
+
+	save_flags (flags);
+	cli();
+	dev.irq_ok = 0;
+	outb (val,port);
+	interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout);
+	restore_flags (flags);
+}
+
+static int __init wavefront_hw_reset (void)
+{
+	int bits;
+	int hwv[2];
+	unsigned long irq_mask;
+	short reported_irq;
+
+	/* IRQ already checked in init_module() */
+
+	bits = wavefront_interrupt_bits (dev.irq);
+
+	printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n");
+
+	sti ();
+
+	irq_mask = probe_irq_on ();
+
+	outb (0x0, dev.control_port); 
+	outb (0x80 | 0x40 | bits, dev.data_port);	
+	wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1,
+					 dev.control_port,
+					 (reset_time*HZ)/100);
+
+	reported_irq = probe_irq_off (irq_mask);
+
+	if (reported_irq != dev.irq) {
+		if (reported_irq == 0) {
+			printk (KERN_ERR LOGNAME
+				"No unassigned interrupts detected "
+				"after h/w reset\n");
+		} else if (reported_irq < 0) {
+			printk (KERN_ERR LOGNAME
+				"Multiple unassigned interrupts detected "
+				"after h/w reset\n");
+		} else {
+			printk (KERN_ERR LOGNAME "autodetected IRQ %d not the "
+				"value provided (%d)\n", reported_irq,
+				dev.irq);
+		}
+		dev.irq = -1;
+		return 1;
+	} else {
+		printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n",
+			reported_irq);
+	}
+
+	if (request_irq (dev.irq, wavefrontintr,
+			 SA_INTERRUPT|SA_SHIRQ,
+			 "wavefront synth", &dev) < 0) {
+		printk (KERN_WARNING LOGNAME "IRQ %d not available!\n",
+			dev.irq);
+		return 1;
+	}
+
+	/* try reset of port */
+      
+	outb (0x0, dev.control_port); 
+  
+	/* At this point, the board is in reset, and the H/W initialization
+	   register is accessed at the same address as the data port.
+     
+	   Bit 7 - Enable IRQ Driver	
+	   0 - Tri-state the Wave-Board drivers for the PC Bus IRQs
+	   1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus.
+     
+	   Bit 6 - MIDI Interface Select
+
+	   0 - Use the MIDI Input from the 26-pin WaveBlaster
+	   compatible header as the serial MIDI source
+	   1 - Use the MIDI Input from the 9-pin D connector as the
+	   serial MIDI source.
+     
+	   Bits 5:3 - IRQ Selection
+	   0 0 0 - IRQ 2/9
+	   0 0 1 - IRQ 5
+	   0 1 0 - IRQ 12
+	   0 1 1 - IRQ 15
+	   1 0 0 - Reserved
+	   1 0 1 - Reserved
+	   1 1 0 - Reserved
+	   1 1 1 - Reserved
+     
+	   Bits 2:1 - Reserved
+	   Bit 0 - Disable Boot ROM
+	   0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM
+	   1 - memory accesses to 03FC30-03FFFFH are directed to external 
+	   storage.
+     
+	*/
+
+	/* configure hardware: IRQ, enable interrupts, 
+	   plus external 9-pin MIDI interface selected
+	*/
+
+	outb (0x80 | 0x40 | bits, dev.data_port);	
+  
+	/* CONTROL REGISTER
+
+	   0 Host Rx Interrupt Enable (1=Enabled)      0x1
+	   1 Unused                                    0x2
+	   2 Unused                                    0x4
+	   3 Unused                                    0x8
+	   4 Host Tx Interrupt Enable                 0x10
+	   5 Mute (0=Mute; 1=Play)                    0x20
+	   6 Master Interrupt Enable (1=Enabled)      0x40
+	   7 Master Reset (0=Reset; 1=Run)            0x80
+
+	   Take us out of reset, mute output, master + TX + RX interrupts on.
+	   
+	   We'll get an interrupt presumably to tell us that the TX
+	   register is clear.
+	*/
+
+	wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1,
+					 dev.control_port,
+					 (reset_time*HZ)/100);
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+	 */
+
+	if (!dev.irq_ok) {
+		printk (KERN_WARNING LOGNAME
+			"intr not received after h/w un-reset.\n");
+		goto gone_bad;
+	} 
+
+	dev.interrupts_on = 1;
+	
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+
+	   At this point, only "HW VERSION" or "DOWNLOAD OS" commands
+	   will work. So, issue one of them, and wait for TX
+	   interrupt. This can take a *long* time after a cold boot,
+	   while the ISC ROM does its RAM test. The SDK says up to 4
+	   seconds - with 12MB of RAM on a Tropez+, it takes a lot
+	   longer than that (~16secs). Note that the card understands
+	   the difference between a warm and a cold boot, so
+	   subsequent ISC2115 reboots (say, caused by module
+	   reloading) will get through this much faster.
+
+	   XXX Interesting question: why is no RX interrupt received first ?
+	*/
+
+	wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, 
+					 dev.data_port, ramcheck_time*HZ);
+
+	if (!dev.irq_ok) {
+		printk (KERN_WARNING LOGNAME
+			"post-RAM-check interrupt not received.\n");
+		goto gone_bad;
+	} 
+
+	if (!wavefront_wait (STAT_CAN_READ)) {
+		printk (KERN_WARNING LOGNAME
+			"no response to HW version cmd.\n");
+		goto gone_bad;
+	}
+	
+	if ((hwv[0] = wavefront_read ()) == -1) {
+		printk (KERN_WARNING LOGNAME
+			"board not responding correctly.\n");
+		goto gone_bad;
+	}
+
+	if (hwv[0] == 0xFF) { /* NAK */
+
+		/* Board's RAM test failed. Try to read error code,
+		   and tell us about it either way.
+		*/
+		
+		if ((hwv[0] = wavefront_read ()) == -1) {
+			printk (KERN_WARNING LOGNAME "on-board RAM test failed "
+				"(bad error code).\n");
+		} else {
+			printk (KERN_WARNING LOGNAME "on-board RAM test failed "
+				"(error code: 0x%x).\n",
+				hwv[0]);
+		}
+		goto gone_bad;
+	}
+
+	/* We're OK, just get the next byte of the HW version response */
+
+	if ((hwv[1] = wavefront_read ()) == -1) {
+		printk (KERN_WARNING LOGNAME "incorrect h/w response.\n");
+		goto gone_bad;
+	}
+
+	printk (KERN_INFO LOGNAME "hardware version %d.%d\n",
+		hwv[0], hwv[1]);
+
+	return 0;
+
+
+     gone_bad:
+	if (dev.irq >= 0) {
+		free_irq (dev.irq, &dev);
+		dev.irq = -1;
+	}
+	return (1);
+}
+
+static int __init detect_wavefront (int irq, int io_base)
+{
+	unsigned char   rbuf[4], wbuf[4];
+
+	/* TB docs say the device takes up 8 ports, but we know that
+	   if there is an FX device present (i.e. a Tropez+) it really
+	   consumes 16.
+	*/
+
+	if (check_region (io_base, 16)) {
+		printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x "
+			"already in use - ignored\n", dev.base,
+			dev.base+15);
+		return -1;
+	}
+  
+	dev.irq = irq;
+	dev.base = io_base;
+	dev.israw = 0;
+	dev.debug = debug_default;
+	dev.interrupts_on = 0;
+	dev.irq_cnt = 0;
+	dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */
+
+	if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
+
+		dev.fw_version[0] = rbuf[0];
+		dev.fw_version[1] = rbuf[1];
+		printk (KERN_INFO LOGNAME
+			"firmware %d.%d already loaded.\n",
+			rbuf[0], rbuf[1]);
+
+		/* check that a command actually works */
+      
+		if (wavefront_cmd (WFC_HARDWARE_VERSION,
+				   rbuf, wbuf) == 0) {
+			dev.hw_version[0] = rbuf[0];
+			dev.hw_version[1] = rbuf[1];
+		} else {
+			printk (KERN_WARNING LOGNAME "not raw, but no "
+				"hardware version!\n");
+			return 0;
+		}
+
+		if (!wf_raw) {
+			return 1;
+		} else {
+			printk (KERN_INFO LOGNAME
+				"reloading firmware anyway.\n");
+			dev.israw = 1;
+		}
+
+	} else {
+
+		dev.israw = 1;
+		printk (KERN_INFO LOGNAME
+			"no response to firmware probe, assume raw.\n");
+
+	}
+
+	init_waitqueue_head (&dev.interrupt_sleeper);
+
+	if (wavefront_hw_reset ()) {
+		printk (KERN_WARNING LOGNAME "hardware reset failed\n");
+		return 0;
+	}
+
+	/* Check for FX device, present only on Tropez+ */
+
+	dev.has_fx = (detect_wffx () == 0);
+
+	return 1;
+}
+
+#include "os.h"
+#define __KERNEL_SYSCALLS__
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <asm/uaccess.h>
+
+static int errno; 
+
+static int
+wavefront_download_firmware (char *path)
+
+{
+	unsigned char section[WF_SECTION_MAX];
+	char section_length; /* yes, just a char; max value is WF_SECTION_MAX */
+	int section_cnt_downloaded = 0;
+	int fd;
+	int c;
+	int i;
+	mm_segment_t fs;
+
+	/* This tries to be a bit cleverer than the stuff Alan Cox did for
+	   the generic sound firmware, in that it actually knows
+	   something about the structure of the Motorola firmware. In
+	   particular, it uses a version that has been stripped of the
+	   20K of useless header information, and had section lengths
+	   added, making it possible to load the entire OS without any
+	   [kv]malloc() activity, since the longest entity we ever read is
+	   42 bytes (well, WF_SECTION_MAX) long.
+	*/
+
+	fs = get_fs();
+	set_fs (get_ds());
+
+	if ((fd = open (path, 0, 0)) < 0) {
+		printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n",
+			path);
+		return 1;
+	}
+
+	while (1) {
+		int x;
+
+		if ((x = read (fd, &section_length, sizeof (section_length))) !=
+		    sizeof (section_length)) {
+			printk (KERN_ERR LOGNAME "firmware read error.\n");
+			goto failure;
+		}
+
+		if (section_length == 0) {
+			break;
+		}
+
+		if (read (fd, section, section_length) != section_length) {
+			printk (KERN_ERR LOGNAME "firmware section "
+				"read error.\n");
+			goto failure;
+		}
+
+		/* Send command */
+	
+		if (wavefront_write (WFC_DOWNLOAD_OS)) {
+			goto failure;
+		}
+	
+		for (i = 0; i < section_length; i++) {
+			if (wavefront_write (section[i])) {
+				goto failure;
+			}
+		}
+	
+		/* get ACK */
+	
+		if (wavefront_wait (STAT_CAN_READ)) {
+
+			if ((c = inb (dev.data_port)) != WF_ACK) {
+
+				printk (KERN_ERR LOGNAME "download "
+					"of section #%d not "
+					"acknowledged, ack = 0x%x\n",
+					section_cnt_downloaded + 1, c);
+				goto failure;
+		
+			}
+
+		} else {
+			printk (KERN_ERR LOGNAME "time out for firmware ACK.\n");
+			goto failure;
+		}
+
+	}
+
+	close (fd);
+	set_fs (fs);
+	return 0;
+
+ failure:
+	close (fd);
+	set_fs (fs);
+	printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n");
+	return 1;
+}
+
+static int __init wavefront_config_midi (void)
+{
+	unsigned char rbuf[4], wbuf[4];
+    
+	if (detect_wf_mpu (dev.irq, dev.base) < 0) {
+		printk (KERN_WARNING LOGNAME
+			"could not find working MIDI device\n");
+		return -1;
+	} 
+
+	if ((dev.mididev = install_wf_mpu ()) < 0) {
+		printk (KERN_WARNING LOGNAME
+			"MIDI interfaces not configured\n");
+		return -1;
+	}
+
+	/* Route external MIDI to WaveFront synth (by default) */
+    
+	if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) {
+		printk (KERN_WARNING LOGNAME
+			"cannot enable MIDI-IN to synth routing.\n");
+		/* XXX error ? */
+	}
+
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	/* Get the regular MIDI patch loading function, so we can
+	   use it if we ever get handed a SYSEX patch. This is
+	   unlikely, because its so damn slow, but we may as well
+	   leave this functionality from maui.c behind, since it
+	   could be useful for sequencer applications that can
+	   only use MIDI to do patch loading.
+	*/
+
+	if (midi_devs[dev.mididev]->converter != NULL) {
+		midi_load_patch = midi_devs[dev.mididev]->converter->load_patch;
+		midi_devs[dev.mididev]->converter->load_patch =
+		    &wavefront_oss_load_patch;
+	}
+
+#endif /* OSS_SUPPORT_SEQ */
+	
+	/* Turn on Virtual MIDI, but first *always* turn it off,
+	   since otherwise consectutive reloads of the driver will
+	   never cause the hardware to generate the initial "internal" or 
+	   "external" source bytes in the MIDI data stream. This
+	   is pretty important, since the internal hardware generally will
+	   be used to generate none or very little MIDI output, and
+	   thus the only source of MIDI data is actually external. Without
+	   the switch bytes, the driver will think it all comes from
+	   the internal interface. Duh.
+	*/
+
+	if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { 
+		printk (KERN_WARNING LOGNAME
+			"virtual MIDI mode not disabled\n");
+		return 0; /* We're OK, but missing the external MIDI dev */
+	}
+
+	if ((dev.ext_mididev = virtual_midi_enable ()) < 0) {
+		printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n");
+	} else {
+		if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) {
+			printk (KERN_WARNING LOGNAME
+				"cannot enable virtual MIDI mode.\n");
+			virtual_midi_disable ();
+		} 
+	}
+    
+	return 0;
+}
+
+static int __init wavefront_do_reset (int atboot)
+{
+	char voices[1];
+
+	if (!atboot && wavefront_hw_reset ()) {
+		printk (KERN_WARNING LOGNAME "hw reset failed.\n");
+		goto gone_bad;
+	}
+
+	if (dev.israw) {
+		if (wavefront_download_firmware (ospath)) {
+			goto gone_bad;
+		}
+
+		dev.israw = 0;
+
+		/* Wait for the OS to get running. The protocol for
+		   this is non-obvious, and was determined by
+		   using port-IO tracing in DOSemu and some
+		   experimentation here.
+		   
+		   Rather than using timed waits, use interrupts creatively.
+		*/
+
+		wavefront_should_cause_interrupt (WFC_NOOP,
+						  dev.data_port,
+						  (osrun_time*HZ));
+
+		if (!dev.irq_ok) {
+			printk (KERN_WARNING LOGNAME
+				"no post-OS interrupt.\n");
+			goto gone_bad;
+		}
+		
+		/* Now, do it again ! */
+		
+		wavefront_should_cause_interrupt (WFC_NOOP,
+						  dev.data_port, (10*HZ));
+		
+		if (!dev.irq_ok) {
+			printk (KERN_WARNING LOGNAME
+				"no post-OS interrupt(2).\n");
+			goto gone_bad;
+		}
+
+		/* OK, no (RX/TX) interrupts any more, but leave mute
+		   in effect. 
+		*/
+		
+		outb (0x80|0x40, dev.control_port); 
+
+		/* No need for the IRQ anymore */
+		
+		free_irq (dev.irq, &dev);
+
+	}
+
+	if (dev.has_fx && fx_raw) {
+		wffx_init ();
+	}
+
+	/* SETUPSND.EXE asks for sample memory config here, but since i
+	   have no idea how to interpret the result, we'll forget
+	   about it.
+	*/
+	
+	if ((dev.freemem = wavefront_freemem ()) < 0) {
+		goto gone_bad;
+	}
+		
+	printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024);
+
+	if (wavefront_write (0xf0) ||
+	    wavefront_write (1) ||
+	    (wavefront_read () < 0)) {
+		dev.debug = 0;
+		printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n");
+		goto gone_bad;
+	}
+
+	voices[0] = 32;
+
+	if (wavefront_cmd (WFC_SET_NVOICES, 0, voices)) {
+		printk (KERN_WARNING LOGNAME
+			"cannot set number of voices to 32.\n");
+		goto gone_bad;
+	}
+
+
+	return 0;
+
+ gone_bad:
+	/* reset that sucker so that it doesn't bother us. */
+
+	outb (0x0, dev.control_port);
+	dev.interrupts_on = 0;
+	if (dev.irq >= 0) {
+		free_irq (dev.irq, &dev);
+	}
+	return 1;
+}
+
+static int __init wavefront_init (int atboot)
+{
+	int samples_are_from_rom;
+
+	if (dev.israw) {
+		samples_are_from_rom = 1;
+	} else {
+		/* XXX is this always true ? */
+		samples_are_from_rom = 0;
+	}
+
+	if (dev.israw || fx_raw) {
+		if (wavefront_do_reset (atboot)) {
+			return -1;
+		}
+	}
+
+	wavefront_get_sample_status (samples_are_from_rom);
+	wavefront_get_program_status ();
+	wavefront_get_patch_status ();
+
+	/* Start normal operation: unreset, master interrupt enabled, no mute
+	*/
+
+	outb (0x80|0x40|0x20, dev.control_port); 
+
+	return (0);
+}
+
+static int __init install_wavefront (void)
+
+{
+	if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) {
+		printk (KERN_ERR LOGNAME "cannot register raw synth\n");
+		return -1;
+	}
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	if ((dev.oss_dev = sound_alloc_synthdev()) == -1) {
+		printk (KERN_ERR LOGNAME "Too many sequencers\n");
+		return -1;
+	} else {
+		synth_devs[dev.oss_dev] = &wavefront_operations;
+	}
+#endif /* OSS_SUPPORT_SEQ */
+
+	if (wavefront_init (1) < 0) {
+		printk (KERN_WARNING LOGNAME "initialization failed.\n");
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+		sound_unload_synthdev (dev.oss_dev);
+#endif /* OSS_SUPPORT_SEQ */ 
+
+		return -1;
+	}
+    
+	request_region (dev.base+2, 6, "wavefront synth");
+
+	if (dev.has_fx) {
+		request_region (dev.base+8, 8, "wavefront fx");
+	}
+
+	if (wavefront_config_midi ()) {
+		printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n");
+	}
+
+	return dev.oss_dev;
+}
+
+static void __exit uninstall_wavefront (void)
+{
+	/* the first two i/o addresses are freed by the wf_mpu code */
+	release_region (dev.base+2, 6);
+
+	if (dev.has_fx) {
+		release_region (dev.base+8, 8);
+	}
+
+	unregister_sound_synth (dev.synth_dev);
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	sound_unload_synthdev (dev.oss_dev);
+#endif /* OSS_SUPPORT_SEQ */ 
+	uninstall_wf_mpu ();
+}
+
+/***********************************************************************/
+/*   WaveFront FX control                                              */
+/***********************************************************************/
+
+#include "yss225.h"
+
+/* Control bits for the Load Control Register
+ */
+
+#define FX_LSB_TRANSFER 0x01    /* transfer after DSP LSB byte written */
+#define FX_MSB_TRANSFER 0x02    /* transfer after DSP MSB byte written */
+#define FX_AUTO_INCR    0x04    /* auto-increment DSP address after transfer */
+
+static int
+wffx_idle (void) 
+    
+{
+	int i;
+	unsigned int x = 0x80;
+    
+	for (i = 0; i < 1000; i++) {
+		x = inb (dev.fx_status);
+		if ((x & 0x80) == 0) {
+			break;
+		}
+	}
+    
+	if (x & 0x80) {
+		printk (KERN_ERR LOGNAME "FX device never idle.\n");
+		return 0;
+	}
+    
+	return (1);
+}
+
+int __init detect_wffx (void)
+{
+	/* This is a crude check, but its the best one I have for now.
+	   Certainly on the Maui and the Tropez, wffx_idle() will
+	   report "never idle", which suggests that this test should
+	   work OK.
+	*/
+
+	if (inb (dev.fx_status) & 0x80) {
+		printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n");
+		return -1;
+	}
+
+	return 0;
+}	
+
+int __init attach_wffx (void)
+{
+	if ((dev.fx_mididev = sound_alloc_mididev ()) < 0) {
+		printk (KERN_WARNING LOGNAME "cannot install FX Midi driver\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void
+wffx_mute (int onoff)
+    
+{
+	if (!wffx_idle()) {
+		return;
+	}
+    
+	outb (onoff ? 0x02 : 0x00, dev.fx_op);
+}
+
+static int
+wffx_memset (int page,
+	     int addr, int cnt, unsigned short *data)
+{
+	if (page < 0 || page > 7) {
+		printk (KERN_ERR LOGNAME "FX memset: "
+			"page must be >= 0 and <= 7\n");
+		return -(EINVAL);
+	}
+
+	if (addr < 0 || addr > 0x7f) {
+		printk (KERN_ERR LOGNAME "FX memset: "
+			"addr must be >= 0 and <= 7f\n");
+		return -(EINVAL);
+	}
+
+	if (cnt == 1) {
+
+		outb (FX_LSB_TRANSFER, dev.fx_lcr);
+		outb (page, dev.fx_dsp_page);
+		outb (addr, dev.fx_dsp_addr);
+		outb ((data[0] >> 8), dev.fx_dsp_msb);
+		outb ((data[0] & 0xff), dev.fx_dsp_lsb);
+
+		printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n",
+			page, addr, data[0]);
+	
+	} else {
+		int i;
+
+		outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+		outb (page, dev.fx_dsp_page);
+		outb (addr, dev.fx_dsp_addr);
+
+		for (i = 0; i < cnt; i++) {
+			outb ((data[i] >> 8), dev.fx_dsp_msb);
+			outb ((data[i] & 0xff), dev.fx_dsp_lsb);
+			if (!wffx_idle ()) {
+				break;
+			}
+		}
+
+		if (i != cnt) {
+			printk (KERN_WARNING LOGNAME
+				"FX memset "
+				"(0x%x, 0x%x, 0x%x, %d) incomplete\n",
+				page, addr, (int) data, cnt);
+			return -(EIO);
+		}
+	}
+
+	return 0;
+}
+
+static int
+wffx_ioctl (wavefront_fx_info *r)
+
+{
+	unsigned short page_data[256];
+	unsigned short *pd;
+
+	switch (r->request) {
+	case WFFX_MUTE:
+		wffx_mute (r->data[0]);
+		return 0;
+
+	case WFFX_MEMSET:
+
+		if (r->data[2] <= 0) {
+			printk (KERN_ERR LOGNAME "cannot write "
+				"<= 0 bytes to FX\n");
+			return -(EINVAL);
+		} else if (r->data[2] == 1) {
+			pd = (unsigned short *) &r->data[3];
+		} else {
+			if (r->data[2] > sizeof (page_data)) {
+				printk (KERN_ERR LOGNAME "cannot write "
+					"> 255 bytes to FX\n");
+				return -(EINVAL);
+			}
+			copy_from_user (page_data, (unsigned char *) r->data[3],
+					r->data[2]);
+			pd = page_data;
+		}
+
+		return wffx_memset (r->data[0], /* page */
+				    r->data[1], /* addr */
+				    r->data[2], /* cnt */
+				    pd);
+
+	default:
+		printk (KERN_WARNING LOGNAME
+			"FX: ioctl %d not yet supported\n",
+			r->request);
+		return -(EINVAL);
+	}
+}
+
+/* YSS225 initialization.
+
+   This code was developed using DOSEMU. The Turtle Beach SETUPSND
+   utility was run with I/O tracing in DOSEMU enabled, and a reconstruction
+   of the port I/O done, using the Yamaha faxback document as a guide
+   to add more logic to the code. Its really pretty weird.
+
+   There was an alternative approach of just dumping the whole I/O
+   sequence as a series of port/value pairs and a simple loop
+   that output it. However, I hope that eventually I'll get more
+   control over what this code does, and so I tried to stick with
+   a somewhat "algorithmic" approach.
+*/
+
+static int __init wffx_init (void)
+{
+	int i;
+	int j;
+
+	/* Set all bits for all channels on the MOD unit to zero */
+	/* XXX But why do this twice ? */
+
+	for (j = 0; j < 2; j++) {
+		for (i = 0x10; i <= 0xff; i++) {
+	    
+			if (!wffx_idle ()) {
+				return (-1);
+			}
+	    
+			outb (i, dev.fx_mod_addr);
+			outb (0x0, dev.fx_mod_data);
+		}
+	}
+
+	if (!wffx_idle()) return (-1);
+	outb (0x02, dev.fx_op);                        /* mute on */
+
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x44, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x42, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x43, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x7c, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x7e, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x46, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x49, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x47, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x4a, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+
+	/* either because of stupidity by TB's programmers, or because it
+	   actually does something, rezero the MOD page.
+	*/
+	for (i = 0x10; i <= 0xff; i++) {
+	
+		if (!wffx_idle ()) {
+			return (-1);
+		}
+	
+		outb (i, dev.fx_mod_addr);
+		outb (0x0, dev.fx_mod_data);
+	}
+	/* load page zero */
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x00, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_zero); i += 2) {
+		outb (page_zero[i], dev.fx_dsp_msb);
+		outb (page_zero[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	/* Now load page one */
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x01, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_one); i += 2) {
+		outb (page_one[i], dev.fx_dsp_msb);
+		outb (page_one[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x02, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_two); i++) {
+		outb (page_two[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x03, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_three); i++) {
+		outb (page_three[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x04, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_four); i++) {
+		outb (page_four[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	/* Load memory area (page six) */
+    
+	outb (FX_LSB_TRANSFER, dev.fx_lcr); 
+	outb (0x06, dev.fx_dsp_page); 
+
+	for (i = 0; i < sizeof (page_six); i += 3) {
+		outb (page_six[i], dev.fx_dsp_addr);
+		outb (page_six[i+1], dev.fx_dsp_msb);
+		outb (page_six[i+2], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_seven); i += 2) {
+		outb (page_seven[i], dev.fx_dsp_msb);
+		outb (page_seven[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	/* Now setup the MOD area. We do this algorithmically in order to
+	   save a little data space. It could be done in the same fashion
+	   as the "pages".
+	*/
+
+	for (i = 0x00; i <= 0x0f; i++) {
+		outb (0x01, dev.fx_mod_addr);
+		outb (i, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+		outb (0x02, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0xb0; i <= 0xbf; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x20, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0xf0; i <= 0xff; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x20, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0x10; i <= 0x1d; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0xff, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x1e, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	for (i = 0x1f; i <= 0x2d; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0xff, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x2e, dev.fx_mod_addr);
+	outb (0x00, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	for (i = 0x2f; i <= 0x3e; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x3f, dev.fx_mod_addr);
+	outb (0x20, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	for (i = 0x40; i <= 0x4d; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x4e, dev.fx_mod_addr);
+	outb (0x0e, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0x4f, dev.fx_mod_addr);
+	outb (0x0e, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+
+	for (i = 0x50; i <= 0x6b; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x6c, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	outb (0x6d, dev.fx_mod_addr);
+	outb (0x00, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	outb (0x6e, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	outb (0x6f, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	for (i = 0x70; i <= 0x7f; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0xc0, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	for (i = 0x80; i <= 0xaf; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0xc0; i <= 0xdd; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0xde, dev.fx_mod_addr);
+	outb (0x10, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0xdf, dev.fx_mod_addr);
+	outb (0x10, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	for (i = 0xe0; i <= 0xef; i++) {
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0x00; i <= 0x0f; i++) {
+		outb (0x01, dev.fx_mod_addr);
+		outb (i, dev.fx_mod_data);
+		outb (0x02, dev.fx_mod_addr);
+		outb (0x01, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x02, dev.fx_op); /* mute on */
+
+	/* Now set the coefficients and so forth for the programs above */
+
+	for (i = 0; i < sizeof (coefficients); i += 4) {
+		outb (coefficients[i], dev.fx_dsp_page);
+		outb (coefficients[i+1], dev.fx_dsp_addr);
+		outb (coefficients[i+2], dev.fx_dsp_msb);
+		outb (coefficients[i+3], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	/* Some settings (?) that are too small to bundle into loops */
+
+	if (!wffx_idle()) return (-1);
+	outb (0x1e, dev.fx_mod_addr);
+	outb (0x14, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0xde, dev.fx_mod_addr);
+	outb (0x20, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0xdf, dev.fx_mod_addr);
+	outb (0x20, dev.fx_mod_data);
+    
+	/* some more coefficients */
+
+	if (!wffx_idle()) return (-1);
+	outb (0x06, dev.fx_dsp_page);
+	outb (0x78, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x40, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x03, dev.fx_dsp_addr);
+	outb (0x0f, dev.fx_dsp_msb);
+	outb (0xff, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x0b, dev.fx_dsp_addr);
+	outb (0x0f, dev.fx_dsp_msb);
+	outb (0xff, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x02, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x0a, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x46, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x49, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+    
+	/* Now, for some strange reason, lets reload every page
+	   and all the coefficients over again. I have *NO* idea
+	   why this is done. I do know that no sound is produced
+	   is this phase is omitted.
+	*/
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x00, dev.fx_dsp_page);  
+	outb (0x10, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_zero_v2); i += 2) {
+		outb (page_zero_v2[i], dev.fx_dsp_msb);
+		outb (page_zero_v2[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x01, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_one_v2); i += 2) {
+		outb (page_one_v2[i], dev.fx_dsp_msb);
+		outb (page_one_v2[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	if (!wffx_idle()) return (-1);
+	if (!wffx_idle()) return (-1);
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x02, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_two_v2); i++) {
+		outb (page_two_v2[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x03, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_three_v2); i++) {
+		outb (page_three_v2[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x04, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_four_v2); i++) {
+		outb (page_four_v2[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+    
+	outb (FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x06, dev.fx_dsp_page);
+
+	/* Page six v.2 is algorithmic */
+    
+	for (i = 0x10; i <= 0x3e; i += 2) {
+		outb (i, dev.fx_dsp_addr);
+		outb (0x00, dev.fx_dsp_msb);
+		outb (0x00, dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_seven_v2); i += 2) {
+		outb (page_seven_v2[i], dev.fx_dsp_msb);
+		outb (page_seven_v2[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0x00; i < sizeof(mod_v2); i += 2) {
+		outb (mod_v2[i], dev.fx_mod_addr);
+		outb (mod_v2[i+1], dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0; i < sizeof (coefficients2); i += 4) {
+		outb (coefficients2[i], dev.fx_dsp_page);
+		outb (coefficients2[i+1], dev.fx_dsp_addr);
+		outb (coefficients2[i+2], dev.fx_dsp_msb);
+		outb (coefficients2[i+3], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
+	}
+
+	for (i = 0; i < sizeof (coefficients3); i += 2) {
+		int x;
+
+		outb (0x07, dev.fx_dsp_page);
+		x = (i % 4) ? 0x4e : 0x4c;
+		outb (x, dev.fx_dsp_addr);
+		outb (coefficients3[i], dev.fx_dsp_msb);
+		outb (coefficients3[i+1], dev.fx_dsp_lsb);
+	}
+
+	outb (0x00, dev.fx_op); /* mute off */
+	if (!wffx_idle()) return (-1);
+
+	return (0);
+}
+
+static int io = -1;
+static int irq = -1;
+
+MODULE_AUTHOR      ("Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_PARM        (io,"i");
+MODULE_PARM        (irq,"i");
+
+static int __init init_wavfront (void)
+{
+	printk ("Turtle Beach WaveFront Driver\n"
+		"Copyright (C) by Hannu Solvainen, "
+		"Paul Barton-Davis 1993-1998.\n");
+
+	/* XXX t'would be lovely to ask the CS4232 for these values, eh ? */
+
+	if (io == -1 || irq == -1) {
+		printk (KERN_INFO LOGNAME "irq and io options must be set.\n");
+		return -EINVAL;
+	}
+
+	if (wavefront_interrupt_bits (irq) < 0) {
+		printk (KERN_INFO LOGNAME
+			"IRQ must be 9, 5, 12 or 15 (not %d)\n", irq);
+		return -ENODEV;
+	}
+
+	if (detect_wavefront (irq, io) < 0) {
+		return -ENODEV;
+	} 
+
+	if (install_wavefront () < 0) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_wavfront (void)
+{
+	uninstall_wavefront ();
+}
+
+module_init(init_wavfront);
+module_exit(cleanup_wavfront);
diff -Nru linux/sound/oss/wf_midi.c linux-2.4.19-pre5-mjc/sound/oss/wf_midi.c
--- linux/sound/oss/wf_midi.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/wf_midi.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,880 @@
+/*
+ * sound/wf_midi.c
+ *
+ * The low level driver for the WaveFront ICS2115 MIDI interface(s)
+ * Note that there is also an MPU-401 emulation (actually, a UART-401
+ * emulation) on the CS4232 on the Tropez Plus. This code has nothing
+ * to do with that interface at all.
+ *
+ * The interface is essentially just a UART-401, but is has the
+ * interesting property of supporting what Turtle Beach called
+ * "Virtual MIDI" mode. In this mode, there are effectively *two*
+ * MIDI buses accessible via the interface, one that is routed
+ * solely to/from the external WaveFront synthesizer and the other
+ * corresponding to the pin/socket connector used to link external
+ * MIDI devices to the board.
+ *
+ * This driver fully supports this mode, allowing two distinct
+ * midi devices (/dev/midiNN and /dev/midiNN+1) to be used
+ * completely independently, giving 32 channels of MIDI routing,
+ * 16 to the WaveFront synth and 16 to the external MIDI bus.
+ *
+ * Switching between the two is accomplished externally by the driver
+ * using the two otherwise unused MIDI bytes. See the code for more details.
+ *
+ * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c)
+ *
+ * The main reason to turn off Virtual MIDI mode is when you want to
+ * tightly couple the WaveFront synth with an external MIDI
+ * device. You won't be able to distinguish the source of any MIDI
+ * data except via SysEx ID, but thats probably OK, since for the most
+ * part, the WaveFront won't be sending any MIDI data at all.
+ *  
+ * The main reason to turn on Virtual MIDI Mode is to provide two
+ * completely independent 16-channel MIDI buses, one to the
+ * WaveFront and one to any external MIDI devices. Given the 32
+ * voice nature of the WaveFront, its pretty easy to find a use
+ * for all 16 channels driving just that synth.
+ *
+ */
+
+/*
+ * Copyright (C) by Paul Barton-Davis 1998
+ * Some portions of this file are derived from work that is:
+ *
+ *    CopyriGht (C) by Hannu Savolainen 1993-1996
+ *
+ * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+#include <linux/init.h>
+#include "sound_config.h"
+
+#include <linux/wavefront.h>
+
+#ifdef MODULE
+
+struct wf_mpu_config {
+	int             base;
+#define	DATAPORT(d)   (d)->base
+#define	COMDPORT(d)   (d)->base+1
+#define	STATPORT(d)   (d)->base+1
+
+	int             irq;
+	int             opened;
+	int             devno;
+	int             synthno;
+	int             mode;
+#define MODE_MIDI	1
+#define MODE_SYNTH	2
+
+	void            (*inputintr) (int dev, unsigned char data);
+	char isvirtual;                /* do virtual I/O stuff */
+};
+
+static struct wf_mpu_config  devs[2];
+static struct wf_mpu_config *phys_dev = &devs[0];
+static struct wf_mpu_config *virt_dev = &devs[1];
+
+static void start_uart_mode (void);
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	UART_MODE_ON	0x3F
+
+static inline int wf_mpu_status (void)
+{
+	return inb (STATPORT (phys_dev));
+}
+
+static inline int input_avail (void)
+{
+	return !(wf_mpu_status() & INPUT_AVAIL);
+}
+
+static inline int output_ready (void)
+{
+	return !(wf_mpu_status() & OUTPUT_READY);
+}
+
+static inline int  read_data (void)
+{
+	return inb (DATAPORT (phys_dev));
+}
+
+static inline void write_data (unsigned char byte)
+{
+	outb (byte, DATAPORT (phys_dev));
+}
+
+/*
+ * States for the input scanner (should be in dev_table.h)
+ */
+
+#define MST_SYSMSG		100	/* System message (sysx etc). */
+#define MST_MTC			102	/* Midi Time Code (MTC) qframe msg */
+#define MST_SONGSEL		103	/* Song select */
+#define MST_SONGPOS		104	/* Song position pointer */
+#define MST_TIMED		105	/* Leading timing byte rcvd */
+
+/* buffer space check for input scanner */
+
+#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \
+{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \
+	mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;}
+
+static unsigned char len_tab[] =	/* # of data bytes following a status
+					 */
+{
+	2,				/* 8x */
+	2,				/* 9x */
+	2,				/* Ax */
+	2,				/* Bx */
+	1,				/* Cx */
+	1,				/* Dx */
+	2,				/* Ex */
+	0				/* Fx */
+};
+
+static int
+wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic)
+
+{
+	struct midi_input_info *mi = &midi_devs[devno]->in_info;
+
+	switch (mi->m_state) {
+	case MST_INIT:
+		switch (midic) {
+		case 0xf8:
+			/* Timer overflow */
+			break;
+		
+		case 0xfc:
+			break;
+		
+		case 0xfd:
+			/* XXX do something useful with this. If there is
+			   an external MIDI timer (e.g. a hardware sequencer,
+			   a useful timer can be derived ...
+		   
+			   For now, no timer support.
+			*/
+			break;
+		
+		case 0xfe:
+			return MPU_ACK;
+			break;
+		
+		case 0xf0:
+		case 0xf1:
+		case 0xf2:
+		case 0xf3:
+		case 0xf4:
+		case 0xf5:
+		case 0xf6:
+		case 0xf7:
+			break;
+		
+		case 0xf9:
+			break;
+		
+		case 0xff:
+			mi->m_state = MST_SYSMSG;
+			break;
+		
+		default:
+			if (midic <= 0xef) {
+				mi->m_state = MST_TIMED;
+			}
+			else
+				printk (KERN_ERR "<MPU: Unknown event %02x> ",
+					midic);
+		}
+		break;
+	  
+	case MST_TIMED:
+	{
+		int             msg = ((int) (midic & 0xf0) >> 4);
+	  
+		mi->m_state = MST_DATA;
+	  
+		if (msg < 8) {	/* Data byte */
+	      
+			msg = ((int) (mi->m_prev_status & 0xf0) >> 4);
+			msg -= 8;
+			mi->m_left = len_tab[msg] - 1;
+	      
+			mi->m_ptr = 2;
+			mi->m_buf[0] = mi->m_prev_status;
+			mi->m_buf[1] = midic;
+
+			if (mi->m_left <= 0) {
+				mi->m_state = MST_INIT;
+				do_midi_msg (synthdev, mi->m_buf, mi->m_ptr);
+				mi->m_ptr = 0;
+			}
+		} else if (msg == 0xf) {	/* MPU MARK */
+	      
+			mi->m_state = MST_INIT;
+
+			switch (midic) {
+			case 0xf8:
+				break;
+		    
+			case 0xf9:
+				break;
+		    
+			case 0xfc:
+				break;
+		    
+			default:
+				break;
+			}
+		} else {
+			mi->m_prev_status = midic;
+			msg -= 8;
+			mi->m_left = len_tab[msg];
+	      
+			mi->m_ptr = 1;
+			mi->m_buf[0] = midic;
+	      
+			if (mi->m_left <= 0) {
+				mi->m_state = MST_INIT;
+				do_midi_msg (synthdev, mi->m_buf, mi->m_ptr);
+				mi->m_ptr = 0;
+			}
+		}
+	}
+	break;
+
+	case MST_SYSMSG:
+		switch (midic) {
+		case 0xf0:
+			mi->m_state = MST_SYSEX;
+			break;
+	    
+		case 0xf1:
+			mi->m_state = MST_MTC;
+			break;
+
+		case 0xf2:
+			mi->m_state = MST_SONGPOS;
+			mi->m_ptr = 0;
+			break;
+	    
+		case 0xf3:
+			mi->m_state = MST_SONGSEL;
+			break;
+	    
+		case 0xf6:
+			mi->m_state = MST_INIT;
+	    
+			/*
+			 *    Real time messages
+			 */
+		case 0xf8:
+			/* midi clock */
+			mi->m_state = MST_INIT;
+			/* XXX need ext MIDI timer support */
+			break;
+	    
+		case 0xfA:
+			mi->m_state = MST_INIT;
+			/* XXX need ext MIDI timer support */
+			break;
+	    
+		case 0xFB:
+			mi->m_state = MST_INIT;
+			/* XXX need ext MIDI timer support */
+			break;
+	    
+		case 0xFC:
+			mi->m_state = MST_INIT;
+			/* XXX need ext MIDI timer support */
+			break;
+	    
+		case 0xFE:
+			/* active sensing */
+			mi->m_state = MST_INIT;
+			break;
+	    
+		case 0xff:
+			mi->m_state = MST_INIT;
+			break;
+
+		default:
+			printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic);
+			mi->m_state = MST_INIT;
+		}
+		break;
+
+	case MST_MTC:
+		mi->m_state = MST_INIT;
+		break;
+
+	case MST_SYSEX:
+		if (midic == 0xf7) {
+			mi->m_state = MST_INIT;
+		} else {
+			/* XXX fix me */
+		}
+		break;
+
+	case MST_SONGPOS:
+		BUFTEST (mi);
+		mi->m_buf[mi->m_ptr++] = midic;
+		if (mi->m_ptr == 2) {
+			mi->m_state = MST_INIT;
+			mi->m_ptr = 0;
+			/* XXX need ext MIDI timer support */
+		}
+		break;
+
+	case MST_DATA:
+		BUFTEST (mi);
+		mi->m_buf[mi->m_ptr++] = midic;
+		if ((--mi->m_left) <= 0) {
+			mi->m_state = MST_INIT;
+			do_midi_msg (synthdev, mi->m_buf, mi->m_ptr);
+			mi->m_ptr = 0;
+		}
+		break;
+
+	default:
+		printk (KERN_ERR "Bad state %d ", mi->m_state);
+		mi->m_state = MST_INIT;
+	}
+
+	return 1;
+}
+
+void
+wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy)
+
+{
+	struct wf_mpu_config *physical_dev = dev_id;
+	static struct wf_mpu_config *input_dev = 0;
+	struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info;
+	int n;
+
+	if (!input_avail()) { /* not for us */
+		return;
+	}
+
+	if (mi->m_busy) return;
+	mi->m_busy = 1;
+	sti (); 
+
+	if (!input_dev) {
+		input_dev = physical_dev;
+	}
+
+	n = 50; /* XXX why ? */
+
+	do {
+		unsigned char c = read_data ();
+      
+		if (phys_dev->isvirtual) {
+
+			if (c == WF_EXTERNAL_SWITCH) {
+				input_dev = virt_dev;
+				continue;
+			} else if (c == WF_INTERNAL_SWITCH) { 
+				input_dev = phys_dev;
+				continue;
+			} /* else just leave it as it is */
+
+		} else {
+			input_dev = phys_dev;
+		}
+
+		if (input_dev->mode == MODE_SYNTH) {
+	  
+			wf_mpu_input_scanner (input_dev->devno,
+					      input_dev->synthno, c);
+	  
+		} else if (input_dev->opened & OPEN_READ) {
+	  
+			if (input_dev->inputintr) {
+				input_dev->inputintr (input_dev->devno, c);
+			} 
+		}
+
+	} while (input_avail() && n-- > 0);
+
+	mi->m_busy = 0;
+}
+
+static int
+wf_mpu_open (int dev, int mode,
+	     void            (*input) (int dev, unsigned char data),
+	     void            (*output) (int dev)
+	)
+{
+	struct wf_mpu_config *devc;
+
+	if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL)
+		return -(ENXIO);
+
+	if (phys_dev->devno == dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return -(EINVAL);
+	}
+
+	if (devc->opened) {
+		return -(EBUSY);
+	}
+
+	devc->mode = MODE_MIDI;
+	devc->opened = mode;
+	devc->synthno = 0;
+
+	devc->inputintr = input;
+	return 0;
+}
+ 
+static void
+wf_mpu_close (int dev)
+{
+	struct wf_mpu_config *devc;
+
+	if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL)
+		return;
+
+	if (phys_dev->devno == dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return;
+	}
+
+	devc->mode = 0;
+	devc->inputintr = NULL;
+	devc->opened = 0;
+}
+
+static int
+wf_mpu_out (int dev, unsigned char midi_byte)
+{
+	int             timeout;
+	unsigned long   flags;
+	static int lastoutdev = -1;
+	unsigned char switchch;
+
+	if (phys_dev->isvirtual && lastoutdev != dev) {
+      
+		if (dev == phys_dev->devno) { 
+			switchch = WF_INTERNAL_SWITCH;
+		} else if (dev == virt_dev->devno) { 
+			switchch = WF_EXTERNAL_SWITCH;
+		} else {
+			printk (KERN_ERR "WF-MPU: bad device number %d", dev);
+			return (0);
+		}
+
+		/* XXX fix me */
+      
+		for (timeout = 30000; timeout > 0 && !output_ready ();
+		     timeout--);
+      
+		save_flags (flags);
+		cli ();
+      
+		if (!output_ready ()) {
+			printk (KERN_WARNING "WF-MPU: Send switch "
+				"byte timeout\n");
+			restore_flags (flags);
+			return 0;
+		}
+      
+		write_data (switchch);
+		restore_flags (flags);
+	} 
+
+	lastoutdev = dev;
+
+	/*
+	 * Sometimes it takes about 30000 loops before the output becomes ready
+	 * (After reset). Normally it takes just about 10 loops.
+	 */
+
+	/* XXX fix me */
+
+	for (timeout = 30000; timeout > 0 && !output_ready (); timeout--);
+
+	save_flags (flags);
+	cli ();
+	if (!output_ready ()) {
+		printk (KERN_WARNING "WF-MPU: Send data timeout\n");
+		restore_flags (flags);
+		return 0;
+	}
+
+	write_data (midi_byte);
+	restore_flags (flags);
+
+	return 1;
+}
+
+static inline int wf_mpu_start_read (int dev) {
+	return 0;
+}
+
+static inline int wf_mpu_end_read (int dev) {
+	return 0;
+}
+
+static int wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg)
+{
+	printk (KERN_WARNING
+		"WF-MPU: Intelligent mode not supported by hardware.\n");
+	return -(EINVAL);
+}
+
+static int wf_mpu_buffer_status (int dev)
+{
+	return 0;
+}
+
+static struct synth_operations wf_mpu_synth_operations[2];
+static struct midi_operations  wf_mpu_midi_operations[2];
+
+static struct midi_operations wf_mpu_midi_proto =
+{
+	owner:		THIS_MODULE,
+	info:		{"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+	in_info:	{0},   /* in_info */
+	open:		wf_mpu_open,
+	close:		wf_mpu_close,
+	ioctl:		wf_mpu_ioctl,
+	outputc:	wf_mpu_out,
+	start_read:	wf_mpu_start_read,
+	end_read:	wf_mpu_end_read,
+	buffer_status:	wf_mpu_buffer_status,
+};
+
+static struct synth_info wf_mpu_synth_info_proto =
+{"WaveFront MPU-401 interface", 0,
+ SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT};
+
+static struct synth_info wf_mpu_synth_info[2];
+
+static int
+wf_mpu_synth_ioctl (int dev,
+		    unsigned int cmd, caddr_t arg)
+{
+	int             midi_dev;
+	int index;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL)
+		return -(ENXIO);
+
+	if (midi_dev == phys_dev->devno) {
+		index = 0;
+	} else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) {
+		index = 1;
+	} else {
+		return -(EINVAL);
+	}
+
+	switch (cmd) {
+
+	case SNDCTL_SYNTH_INFO:
+		if(copy_to_user (&((char *) arg)[0],
+			      &wf_mpu_synth_info[index],
+			      sizeof (struct synth_info)))
+			return -EFAULT;
+		return 0;
+	
+	case SNDCTL_SYNTH_MEMAVL:
+		return 0x7fffffff;
+	
+	default:
+		return -EINVAL;
+	}
+}
+
+static int
+wf_mpu_synth_open (int dev, int mode)
+{
+	int             midi_dev;
+	struct wf_mpu_config *devc;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) {
+		return -(ENXIO);
+	}
+  
+	if (phys_dev->devno == midi_dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return -(EINVAL);
+	}
+
+	if (devc->opened) {
+		return -(EBUSY);
+	}
+  
+	devc->mode = MODE_SYNTH;
+	devc->synthno = dev;
+	devc->opened = mode;
+	devc->inputintr = NULL;
+	return 0;
+}
+
+static void
+wf_mpu_synth_close (int dev)
+{
+	int             midi_dev;
+	struct wf_mpu_config *devc;
+
+	midi_dev = synth_devs[dev]->midi_dev;
+
+	if (phys_dev->devno == midi_dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return;
+	}
+
+	devc->inputintr = NULL;
+	devc->opened = 0;
+	devc->mode = 0;
+}
+
+#define _MIDI_SYNTH_C_
+#define MIDI_SYNTH_NAME	"WaveFront (MIDI)"
+#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct synth_operations wf_mpu_synth_proto =
+{
+	owner:		THIS_MODULE,
+	id:		"WaveFront (ICS2115)",
+	info:		NULL,  /* info field, filled in during configuration */
+	midi_dev:	0,     /* MIDI dev XXX should this be -1 ? */
+	synth_type:	SYNTH_TYPE_MIDI,
+	synth_subtype:	SAMPLE_TYPE_WAVEFRONT,
+	open:		wf_mpu_synth_open,
+	close:		wf_mpu_synth_close,
+	ioctl:		wf_mpu_synth_ioctl,
+	kill_note:	midi_synth_kill_note,
+	start_note:	midi_synth_start_note,
+	set_instr:	midi_synth_set_instr,
+	reset:		midi_synth_reset,
+	hw_control:	midi_synth_hw_control,
+	load_patch:	midi_synth_load_patch,
+	aftertouch:	midi_synth_aftertouch,
+	controller:	midi_synth_controller,
+	panning:	midi_synth_panning,
+	bender:		midi_synth_bender,
+	setup_voice:	midi_synth_setup_voice,
+	send_sysex:	midi_synth_send_sysex
+};
+
+static int
+config_wf_mpu (struct wf_mpu_config *dev)
+
+{
+	int is_external;
+	char *name;
+	int index;
+
+	if (dev == phys_dev) {
+		name = "WaveFront internal MIDI";
+		is_external = 0;
+		index = 0;
+		memcpy ((char *) &wf_mpu_synth_operations[index],
+			(char *) &wf_mpu_synth_proto,
+			sizeof (struct synth_operations));
+	} else {
+		name = "WaveFront external MIDI";
+		is_external = 1;
+		index = 1;
+		/* no synth operations for an external MIDI interface */
+	}
+
+	memcpy ((char *) &wf_mpu_synth_info[dev->devno],
+		(char *) &wf_mpu_synth_info_proto,
+		sizeof (struct synth_info));
+
+	strcpy (wf_mpu_synth_info[index].name, name);
+
+	wf_mpu_synth_operations[index].midi_dev = dev->devno;
+	wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index];
+
+	memcpy ((char *) &wf_mpu_midi_operations[index],
+		(char *) &wf_mpu_midi_proto,
+		sizeof (struct midi_operations));
+  
+	if (is_external) {
+		wf_mpu_midi_operations[index].converter = NULL;
+	} else {
+		wf_mpu_midi_operations[index].converter =
+			&wf_mpu_synth_operations[index];
+	}
+
+	strcpy (wf_mpu_midi_operations[index].info.name, name);
+
+	midi_devs[dev->devno] = &wf_mpu_midi_operations[index];
+	midi_devs[dev->devno]->in_info.m_busy = 0;
+	midi_devs[dev->devno]->in_info.m_state = MST_INIT;
+	midi_devs[dev->devno]->in_info.m_ptr = 0;
+	midi_devs[dev->devno]->in_info.m_left = 0;
+	midi_devs[dev->devno]->in_info.m_prev_status = 0;
+
+	devs[index].opened = 0;
+	devs[index].mode = 0;
+
+	return (0);
+}
+
+int virtual_midi_enable (void)
+
+{
+	if ((virt_dev->devno < 0) &&
+	    (virt_dev->devno = sound_alloc_mididev()) == -1) {
+		printk (KERN_ERR
+			"WF-MPU: too many midi devices detected\n");
+		return -1;
+	}
+
+	config_wf_mpu (virt_dev);
+
+	phys_dev->isvirtual = 1;
+	return virt_dev->devno;
+}
+
+int
+virtual_midi_disable (void)
+
+{
+	unsigned long flags;
+
+	save_flags (flags);
+	cli();
+
+	wf_mpu_close (virt_dev->devno);
+	/* no synth on virt_dev, so no need to call wf_mpu_synth_close() */
+	phys_dev->isvirtual = 0;
+
+	restore_flags (flags);
+
+	return 0;
+}
+
+int __init detect_wf_mpu (int irq, int io_base)
+{
+	if (check_region (io_base, 2)) {
+		printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n",
+			io_base);
+		return -1;
+	}
+
+	phys_dev->base = io_base;
+	phys_dev->irq = irq;
+	phys_dev->devno = -1;
+	virt_dev->devno = -1;
+
+	return 0;
+}
+
+int __init install_wf_mpu (void)
+{
+	if ((phys_dev->devno = sound_alloc_mididev()) < 0){
+
+		printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n");
+		return -1;
+
+	}
+
+	request_region (phys_dev->base, 2, "wavefront midi");
+	phys_dev->isvirtual = 0;
+
+	if (config_wf_mpu (phys_dev)) {
+
+		printk (KERN_WARNING
+			"WF-MPU: configuration for MIDI device %d failed\n",
+			phys_dev->devno);
+		sound_unload_mididev (phys_dev->devno);
+
+	}
+
+	/* OK, now we're configured to handle an interrupt ... */
+
+	if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ,
+			 "wavefront midi", phys_dev) < 0) {
+
+		printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n",
+			phys_dev->irq);
+		return -1;
+
+	}
+
+	/* This being a WaveFront (ICS-2115) emulated MPU-401, we have
+	   to switch it into UART (dumb) mode, because otherwise, it
+	   won't do anything at all.
+	*/
+  
+	start_uart_mode ();
+
+	return phys_dev->devno;
+}
+ 
+void
+uninstall_wf_mpu (void)
+
+{
+	release_region (phys_dev->base, 2); 
+	free_irq (phys_dev->irq, phys_dev);
+	sound_unload_mididev (phys_dev->devno);
+
+	if (virt_dev->devno >= 0) {
+		sound_unload_mididev (virt_dev->devno);
+	}
+}
+
+static void
+start_uart_mode (void)
+
+{
+	int             ok, i;
+	unsigned long   flags;
+
+	save_flags (flags);
+	cli ();
+
+	/* XXX fix me */
+
+	for (i = 0; i < 30000 && !output_ready (); i++);
+
+	outb (UART_MODE_ON, COMDPORT(phys_dev));
+
+	for (ok = 0, i = 50000; i > 0 && !ok; i--) {
+		if (input_avail ()) {
+			if (read_data () == MPU_ACK) {
+				ok = 1;
+			}
+		}
+	}
+
+	restore_flags (flags);
+}
+#endif
diff -Nru linux/sound/oss/ymfpci.c linux-2.4.19-pre5-mjc/sound/oss/ymfpci.c
--- linux/sound/oss/ymfpci.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ymfpci.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2671 @@
+/*
+ *  Copyright 1999 Jaroslav Kysela <perex@suse.cz>
+ *  Copyright 2000 Alan Cox <alan@redhat.com>
+ *  Copyright 2001 Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
+ *  Copyright 2002 Pete Zaitcev <zaitcev@yahoo.com>
+ *
+ *  Yamaha YMF7xx driver.
+ *
+ *  This code is a result of high-speed collision
+ *  between ymfpci.c of ALSA and cs46xx.c of Linux.
+ *  -- Pete Zaitcev <zaitcev@yahoo.com>; 2000/09/18
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO:
+ *  - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot).
+ *  - 96KHz playback for DVD - use pitch of 2.0.
+ *  - Retain DMA buffer on close, do not wait the end of frame.
+ *  - Resolve XXX tagged questions.
+ *  - Cannot play 5133Hz.
+ *  - 2001/01/07 Consider if we can remove voice_lock, like so:
+ *     : Allocate/deallocate voices in open/close under semafore.
+ *     : We access voices in interrupt, that only for pcms that open.
+ *    voice_lock around playback_prepare closes interrupts for insane duration.
+ *  - Revisit the way voice_alloc is done - too confusing, overcomplicated.
+ *    Should support various channel types, however.
+ *  - Remove prog_dmabuf from read/write, leave it in open.
+ *  - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_ALSA_YMFPCI_LEGACY code with
+ *    native synthesizer through a playback slot.
+ *  - 2001/11/29 ac97_save_state
+ *    Talk to Kai to remove ac97_save_state before it's too late!
+ *  - Second AC97
+ *  - Restore S/PDIF - Toshibas have it.
+ *
+ * Kai used pci_alloc_consistent for DMA buffer, which sounds a little
+ * unconventional. However, given how small our fragments can be,
+ * a little uncached access is perhaps better than endless flushing.
+ * On i386 and other I/O-coherent architectures pci_alloc_consistent
+ * is entirely harmless.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/soundcard.h>
+#include <linux/ac97_codec.h>
+#include <linux/sound.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+# include "sound_config.h"
+# include "mpu401.h"
+#endif
+#include "ymfpci.h"
+
+/*
+ * I do not believe in debug levels as I never can guess what
+ * part of the code is going to be problematic in the future.
+ * Don't forget to run your klogd with -c 8.
+ *
+ * Example (do not remove):
+ * #define YMFDBG(fmt, arg...)  do{ printk(KERN_DEBUG fmt, ##arg); }while(0)
+ */
+#define YMFDBGW(fmt, arg...)  /* */	/* write counts */
+#define YMFDBGI(fmt, arg...)  /* */	/* interrupts */
+#define YMFDBGX(fmt, arg...)  /* */	/* ioctl */
+
+static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);
+static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd);
+static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice);
+static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank);
+static int ymf_playback_prepare(struct ymf_state *state);
+static int ymf_capture_prepare(struct ymf_state *state);
+static struct ymf_state *ymf_state_alloc(ymfpci_t *unit);
+
+static void ymfpci_aclink_reset(struct pci_dev * pci);
+static void ymfpci_disable_dsp(ymfpci_t *unit);
+static void ymfpci_download_image(ymfpci_t *codec);
+static void ymf_memload(ymfpci_t *unit);
+
+static LIST_HEAD(ymf_devs);
+
+/*
+ *  constants
+ */
+
+static struct pci_device_id ymf_id_tbl[] __devinitdata = {
+#define DEV(v, d, data) \
+  { PCI_VENDOR_ID_##v, PCI_DEVICE_ID_##v##_##d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)data }
+	DEV (YAMAHA, 724,  "YMF724"),
+	DEV (YAMAHA, 724F, "YMF724F"),
+	DEV (YAMAHA, 740,  "YMF740"),
+	DEV (YAMAHA, 740C, "YMF740C"),
+	DEV (YAMAHA, 744,  "YMF744"),
+	DEV (YAMAHA, 754,  "YMF754"),
+#undef DEV
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, ymf_id_tbl);
+
+/*
+ *  common I/O routines
+ */
+
+static inline u8 ymfpci_readb(ymfpci_t *codec, u32 offset)
+{
+	return readb(codec->reg_area_virt + offset);
+}
+
+static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val)
+{
+	writeb(val, codec->reg_area_virt + offset);
+}
+
+static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset)
+{
+	return readw(codec->reg_area_virt + offset);
+}
+
+static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val)
+{
+	writew(val, codec->reg_area_virt + offset);
+}
+
+static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset)
+{
+	return readl(codec->reg_area_virt + offset);
+}
+
+static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val)
+{
+	writel(val, codec->reg_area_virt + offset);
+}
+
+static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched)
+{
+	signed long end_time;
+	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
+	
+	end_time = jiffies + 3 * (HZ / 4);
+	do {
+		if ((ymfpci_readw(codec, reg) & 0x8000) == 0)
+			return 0;
+		if (sched) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		}
+	} while (end_time - (signed long)jiffies >= 0);
+	printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n",
+	    secondary, ymfpci_readw(codec, reg));
+	return -EBUSY;
+}
+
+static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val)
+{
+	ymfpci_t *codec = dev->private_data;
+	u32 cmd;
+
+	/* XXX Do make use of dev->id */
+	ymfpci_codec_ready(codec, 0, 0);
+	cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
+	ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd);
+}
+
+static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg)
+{
+	ymfpci_t *unit = dev->private_data;
+	int i;
+
+	if (ymfpci_codec_ready(unit, 0, 0))
+		return ~0;
+	ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);
+	if (ymfpci_codec_ready(unit, 0, 0))
+		return ~0;
+	if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) {
+		for (i = 0; i < 600; i++)
+			ymfpci_readw(unit, YDSXGR_PRISTATUSDATA);
+	}
+	return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA);
+}
+
+/*
+ *  Misc routines
+ */
+
+/*
+ * Calculate the actual sampling rate relatetively to the base clock (48kHz).
+ */
+static u32 ymfpci_calc_delta(u32 rate)
+{
+	switch (rate) {
+	case 8000:	return 0x02aaab00;
+	case 11025:	return 0x03accd00;
+	case 16000:	return 0x05555500;
+	case 22050:	return 0x07599a00;
+	case 32000:	return 0x0aaaab00;
+	case 44100:	return 0x0eb33300;
+	default:	return ((rate << 16) / 48000) << 12;
+	}
+}
+
+static u32 def_rate[8] = {
+	100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
+};
+
+static u32 ymfpci_calc_lpfK(u32 rate)
+{
+	u32 i;
+	static u32 val[8] = {
+		0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
+		0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
+	};
+	
+	if (rate == 44100)
+		return 0x40000000;	/* FIXME: What's the right value? */
+	for (i = 0; i < 8; i++)
+		if (rate <= def_rate[i])
+			return val[i];
+	return val[0];
+}
+
+static u32 ymfpci_calc_lpfQ(u32 rate)
+{
+	u32 i;
+	static u32 val[8] = {
+		0x35280000, 0x34A70000, 0x32020000, 0x31770000,
+		0x31390000, 0x31C90000, 0x33D00000, 0x40000000
+	};
+	
+	if (rate == 44100)
+		return 0x370A0000;
+	for (i = 0; i < 8; i++)
+		if (rate <= def_rate[i])
+			return val[i];
+	return val[0];
+}
+
+static u32 ymf_calc_lend(u32 rate)
+{
+	return (rate * YMF_SAMPF) / 48000;
+}
+
+/*
+ * We ever allow only a few formats, but let's be generic, for smaller surprise.
+ */
+static int ymf_pcm_format_width(int format)
+{
+	static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE;
+
+	if ((format & (format-1)) != 0) {
+		printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format);
+		return 8;
+	}
+
+	if (format == AFMT_IMA_ADPCM) return 4;
+	if ((format & mask16) != 0) return 16;
+	return 8;
+}
+
+static void ymf_pcm_update_shift(struct ymf_pcm_format *f)
+{
+	f->shift = 0;
+	if (f->voices == 2)
+		f->shift++;
+	if (ymf_pcm_format_width(f->format) == 16)
+		f->shift++;
+}
+
+/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */
+#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+/*
+ * Allocate DMA buffer
+ */
+static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf)
+{
+	void *rawbuf = NULL;
+	dma_addr_t dma_addr;
+	int order;
+	struct page *map, *mapend;
+
+	/* alloc as big a chunk as we can */
+	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
+		rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr);
+		if (rawbuf)
+			break;
+	}
+	if (!rawbuf)
+		return -ENOMEM;
+
+#if 0
+	printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n",
+	       PAGE_SIZE << order, order, rawbuf);
+#endif
+
+	dmabuf->ready  = dmabuf->mapped = 0;
+	dmabuf->rawbuf = rawbuf;
+	dmabuf->dma_addr = dma_addr;
+	dmabuf->buforder = order;
+
+	/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+	mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
+	for (map = virt_to_page(rawbuf); map <= mapend; map++)
+		set_bit(PG_reserved, &map->flags);
+
+	return 0;
+}
+
+/*
+ * Free DMA buffer
+ */
+static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf)
+{
+	struct page *map, *mapend;
+
+	if (dmabuf->rawbuf) {
+		/* undo marking the pages as reserved */
+		mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);
+		for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &map->flags);
+
+		pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder,
+		    dmabuf->rawbuf, dmabuf->dma_addr);
+	}
+	dmabuf->rawbuf = NULL;
+	dmabuf->mapped = dmabuf->ready = 0;
+}
+
+static int prog_dmabuf(struct ymf_state *state, int rec)
+{
+	struct ymf_dmabuf *dmabuf;
+	int w_16;
+	unsigned bufsize;
+	unsigned long flags;
+	int redzone, redfrags;
+	int ret;
+
+	w_16 = ymf_pcm_format_width(state->format.format) == 16;
+	dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf;
+
+	spin_lock_irqsave(&state->unit->reg_lock, flags);
+	dmabuf->hwptr = dmabuf->swptr = 0;
+	dmabuf->total_bytes = 0;
+	dmabuf->count = 0;
+	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+	/* allocate DMA buffer if not allocated yet */
+	if (!dmabuf->rawbuf)
+		if ((ret = alloc_dmabuf(state->unit, dmabuf)))
+			return ret;
+
+	/*
+	 * Create fake fragment sizes and numbers for OSS ioctls.
+	 * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT.
+	 */
+	bufsize = PAGE_SIZE << dmabuf->buforder;
+	/* By default we give 4 big buffers. */
+	dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2);
+	if (dmabuf->ossfragshift > 3 &&
+	    dmabuf->ossfragshift < dmabuf->fragshift) {
+		/* If OSS set smaller fragments, give more smaller buffers. */
+		dmabuf->fragshift = dmabuf->ossfragshift;
+	}
+	dmabuf->fragsize = 1 << dmabuf->fragshift;
+
+	dmabuf->numfrag = bufsize >> dmabuf->fragshift;
+	dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+
+	if (dmabuf->ossmaxfrags >= 2) {
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= state->format.shift;
+		redzone *= 3;
+		redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift;
+
+		if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) {
+			dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags;
+			dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
+		}
+	}
+
+	memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize);
+
+	/*
+	 *	Now set up the ring 
+	 */
+
+	/* XXX   ret = rec? cap_pre(): pbk_pre();  */
+	spin_lock_irqsave(&state->unit->voice_lock, flags);
+	if (rec) {
+		if ((ret = ymf_capture_prepare(state)) != 0) {
+			spin_unlock_irqrestore(&state->unit->voice_lock, flags);
+			return ret;
+		}
+	} else {
+		if ((ret = ymf_playback_prepare(state)) != 0) {
+			spin_unlock_irqrestore(&state->unit->voice_lock, flags);
+			return ret;
+		}
+	}
+	spin_unlock_irqrestore(&state->unit->voice_lock, flags);
+
+	/* set the ready flag for the dma buffer (this comment is not stupid) */
+	dmabuf->ready = 1;
+
+#if 0
+	printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x,"
+	    " numfrag %d fragsize %d dmasize %d\n",
+	       state->format.rate, state->format.format, dmabuf->numfrag,
+	       dmabuf->fragsize, dmabuf->dmasize);
+#endif
+
+	return 0;
+}
+
+static void ymf_start_dac(struct ymf_state *state)
+{
+	ymf_playback_trigger(state->unit, &state->wpcm, 1);
+}
+
+// static void ymf_start_adc(struct ymf_state *state)
+// {
+// 	ymf_capture_trigger(state->unit, &state->rpcm, 1);
+// }
+
+/*
+ * Wait until output is drained.
+ * This does not kill the hardware for the sake of ioctls.
+ */
+static void ymf_wait_dac(struct ymf_state *state)
+{
+	struct ymf_unit *unit = state->unit;
+	struct ymf_pcm *ypcm = &state->wpcm;
+	DECLARE_WAITQUEUE(waita, current);
+	unsigned long flags;
+
+	add_wait_queue(&ypcm->dmabuf.wait, &waita);
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+	if (ypcm->dmabuf.count != 0 && !ypcm->running) {
+		ymf_playback_trigger(unit, ypcm, 1);
+	}
+
+#if 0
+	if (file->f_flags & O_NONBLOCK) {
+		/*
+		 * XXX Our  mistake is to attach DMA buffer to state
+		 * rather than to some per-device structure.
+		 * Cannot skip waiting, can only make it shorter.
+		 */
+	}
+#endif
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	while (ypcm->running) {
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
+		schedule();
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+	}
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&ypcm->dmabuf.wait, &waita);
+
+	/*
+	 * This function may take up to 4 seconds to reach this point
+	 * (32K circular buffer, 8000 Hz). User notices.
+	 */
+}
+
+/* Can just stop, without wait. Or can we? */
+static void ymf_stop_adc(struct ymf_state *state)
+{
+	struct ymf_unit *unit = state->unit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+	ymf_capture_trigger(unit, &state->rpcm, 0);
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+}
+
+/*
+ *  Hardware start management
+ */
+
+static void ymfpci_hw_start(ymfpci_t *unit)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+	if (unit->start_count++ == 0) {
+		ymfpci_writel(unit, YDSXGR_MODE,
+		    ymfpci_readl(unit, YDSXGR_MODE) | 3);
+		unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1;
+	}
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+}
+
+static void ymfpci_hw_stop(ymfpci_t *unit)
+{
+	unsigned long flags;
+	long timeout = 1000;
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+	if (--unit->start_count == 0) {
+		ymfpci_writel(unit, YDSXGR_MODE,
+		    ymfpci_readl(unit, YDSXGR_MODE) & ~3);
+		while (timeout-- > 0) {
+			if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0)
+				break;
+		}
+	}
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+}
+
+/*
+ *  Playback voice management
+ */
+
+static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[])
+{
+	ymfpci_voice_t *voice, *voice2;
+	int idx;
+
+	for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) {
+		voice = &codec->voices[idx];
+		voice2 = pair ? &codec->voices[idx+1] : NULL;
+		if (voice->use || (voice2 && voice2->use))
+			continue;
+		voice->use = 1;
+		if (voice2)
+			voice2->use = 1;
+		switch (type) {
+		case YMFPCI_PCM:
+			voice->pcm = 1;
+			if (voice2)
+				voice2->pcm = 1;
+			break;
+		case YMFPCI_SYNTH:
+			voice->synth = 1;
+			break;
+		case YMFPCI_MIDI:
+			voice->midi = 1;
+			break;
+		}
+		ymfpci_hw_start(codec);
+		rvoice[0] = voice;
+		if (voice2) {
+			ymfpci_hw_start(codec);
+			rvoice[1] = voice2;
+		}
+		return 0;
+	}
+	return -EBUSY;	/* Your audio channel is open by someone else. */
+}
+
+static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice)
+{
+	ymfpci_hw_stop(unit);
+	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+	pvoice->ypcm = NULL;
+}
+
+/*
+ */
+
+static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice)
+{
+	struct ymf_pcm *ypcm;
+	int redzone;
+	int pos, delta, swptr;
+	int played, distance;
+	struct ymf_state *state;
+	struct ymf_dmabuf *dmabuf;
+	char silence;
+
+	if ((ypcm = voice->ypcm) == NULL) {
+		return;
+	}
+	if ((state = ypcm->state) == NULL) {
+		ypcm->running = 0;	// lock it
+		return;
+	}
+	dmabuf = &ypcm->dmabuf;
+	spin_lock(&codec->reg_lock);
+	if (ypcm->running) {
+		YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",
+		   voice->number, codec->active_bank, dmabuf->count,
+		   le32_to_cpu(voice->bank[0].start),
+		   le32_to_cpu(voice->bank[1].start));
+		silence = (ymf_pcm_format_width(state->format.format) == 16) ?
+		    0 : 0x80;
+		/* We need actual left-hand-side redzone size here. */
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= (state->format.shift + 1);
+		swptr = dmabuf->swptr;
+
+		pos = le32_to_cpu(voice->bank[codec->active_bank].start);
+		pos <<= state->format.shift;
+		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */
+			printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n",
+			    codec->dev_audio, voice->number,
+			    dmabuf->hwptr, pos, dmabuf->dmasize);
+			pos = 0;
+		}
+		if (pos < dmabuf->hwptr) {
+			delta = dmabuf->dmasize - dmabuf->hwptr;
+			memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta);
+			delta += pos;
+			memset(dmabuf->rawbuf, silence, pos);
+		} else {
+			delta = pos - dmabuf->hwptr;
+			memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta);
+		}
+		dmabuf->hwptr = pos;
+
+		if (dmabuf->count == 0) {
+			printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n",
+			    codec->dev_audio, voice->number, dmabuf->hwptr);
+			ymf_playback_trigger(codec, ypcm, 0);
+		}
+
+		if (swptr <= pos) {
+			distance = pos - swptr;
+		} else {
+			distance = dmabuf->dmasize - (swptr - pos);
+		}
+		if (distance < redzone) {
+			/*
+			 * hwptr inside redzone => DMA ran out of samples.
+			 */
+			if (delta < dmabuf->count) {
+				/*
+				 * Lost interrupt or other screwage.
+				 */
+				printk(KERN_ERR "ymfpci%d: %d: lost: delta %d"
+				    " hwptr %d swptr %d distance %d count %d\n",
+				    codec->dev_audio, voice->number, delta,
+				    dmabuf->hwptr, swptr, distance, dmabuf->count);
+			} else {
+				/*
+				 * Normal end of DMA.
+				 */
+				YMFDBGI("ymfpci%d: %d: done: delta %d"
+				    " hwptr %d swptr %d distance %d count %d\n",
+				    codec->dev_audio, voice->number, delta,
+				    dmabuf->hwptr, swptr, distance, dmabuf->count);
+			}
+			played = dmabuf->count;
+			if (ypcm->running) {
+				ymf_playback_trigger(codec, ypcm, 0);
+			}
+		} else {
+			/*
+			 * hwptr is chipping away towards a remote swptr.
+			 * Calculate other distance and apply it to count.
+			 */
+			if (swptr >= pos) {
+				distance = swptr - pos;
+			} else {
+				distance = dmabuf->dmasize - (pos - swptr);
+			}
+			if (distance < dmabuf->count) {
+				played = dmabuf->count - distance;
+			} else {
+				played = 0;
+			}
+		}
+
+		dmabuf->total_bytes += played;
+		dmabuf->count -= played;
+		if (dmabuf->count < dmabuf->dmasize / 2) {
+			wake_up(&dmabuf->wait);
+		}
+	}
+	spin_unlock(&codec->reg_lock);
+}
+
+static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap)
+{
+	struct ymf_pcm *ypcm;
+	int redzone;
+	struct ymf_state *state;
+	struct ymf_dmabuf *dmabuf;
+	int pos, delta;
+	int cnt;
+
+	if ((ypcm = cap->ypcm) == NULL) {
+		return;
+	}
+	if ((state = ypcm->state) == NULL) {
+		ypcm->running = 0;	// lock it
+		return;
+	}
+	dmabuf = &ypcm->dmabuf;
+	spin_lock(&unit->reg_lock);
+	if (ypcm->running) {
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= (state->format.shift + 1);
+
+		pos = le32_to_cpu(cap->bank[unit->active_bank].start);
+		// pos <<= state->format.shift;
+		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */
+			printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n",
+			    unit->dev_audio, ypcm->capture_bank_number,
+			    dmabuf->hwptr, pos, dmabuf->dmasize);
+			pos = 0;
+		}
+		if (pos < dmabuf->hwptr) {
+			delta = dmabuf->dmasize - dmabuf->hwptr;
+			delta += pos;
+		} else {
+			delta = pos - dmabuf->hwptr;
+		}
+		dmabuf->hwptr = pos;
+
+		cnt = dmabuf->count;
+		cnt += delta;
+		if (cnt + redzone > dmabuf->dmasize) {
+			/* Overflow - bump swptr */
+			dmabuf->count = dmabuf->dmasize - redzone;
+			dmabuf->swptr = dmabuf->hwptr + redzone;
+			if (dmabuf->swptr >= dmabuf->dmasize) {
+				dmabuf->swptr -= dmabuf->dmasize;
+			}
+		} else {
+			dmabuf->count = cnt;
+		}
+
+		dmabuf->total_bytes += delta;
+		if (dmabuf->count) {		/* && is_sleeping  XXX */
+			wake_up(&dmabuf->wait);
+		}
+	}
+	spin_unlock(&unit->reg_lock);
+}
+
+static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd)
+{
+
+	if (ypcm->voices[0] == NULL) {
+		return -EINVAL;
+	}
+	if (cmd != 0) {
+		codec->ctrl_playback[ypcm->voices[0]->number + 1] =
+		    cpu_to_le32(ypcm->voices[0]->bank_ba);
+		if (ypcm->voices[1] != NULL)
+			codec->ctrl_playback[ypcm->voices[1]->number + 1] =
+			    cpu_to_le32(ypcm->voices[1]->bank_ba);
+		ypcm->running = 1;
+	} else {
+		codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
+		if (ypcm->voices[1] != NULL)
+			codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
+		ypcm->running = 0;
+	}
+	return 0;
+}
+
+static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd)
+{
+	u32 tmp;
+
+	if (cmd != 0) {
+		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
+		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
+		ypcm->running = 1;
+	} else {
+		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
+		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);
+		ypcm->running = 0;
+	}
+}
+
+static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices)
+{
+	struct ymf_unit *unit;
+	int err;
+
+	unit = ypcm->state->unit;
+	if (ypcm->voices[1] != NULL && voices < 2) {
+		ymfpci_voice_free(unit, ypcm->voices[1]);
+		ypcm->voices[1] = NULL;
+	}
+	if (voices == 1 && ypcm->voices[0] != NULL)
+		return 0;		/* already allocated */
+	if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)
+		return 0;		/* already allocated */
+	if (voices > 1) {
+		if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
+			ymfpci_voice_free(unit, ypcm->voices[0]);
+			ypcm->voices[0] = NULL;
+		}		
+		if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0)
+			return err;
+		ypcm->voices[0]->ypcm = ypcm;
+		ypcm->voices[1]->ypcm = ypcm;
+	} else {
+		if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0)
+			return err;
+		ypcm->voices[0]->ypcm = ypcm;
+	}
+	return 0;
+}
+
+static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
+    int rate, int w_16, unsigned long addr, unsigned int end, int spdif)
+{
+	u32 format;
+	u32 delta = ymfpci_calc_delta(rate);
+	u32 lpfQ = ymfpci_calc_lpfQ(rate);
+	u32 lpfK = ymfpci_calc_lpfK(rate);
+	ymfpci_playback_bank_t *bank;
+	int nbank;
+
+	/*
+	 * The gain is a floating point number. According to the manual,
+	 * bit 31 indicates a sign bit, bit 30 indicates an integer part,
+	 * and bits [29:15] indicate a decimal fraction part. Thus,
+	 * for a gain of 1.0 the constant of 0x40000000 is loaded.
+	 */
+	unsigned default_gain = cpu_to_le32(0x40000000);
+
+	format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);
+	if (stereo)
+		end >>= 1;
+	if (w_16)
+		end >>= 1;
+	for (nbank = 0; nbank < 2; nbank++) {
+		bank = &voice->bank[nbank];
+		bank->format = cpu_to_le32(format);
+		bank->loop_default = 0;	/* 0-loops forever, otherwise count */
+		bank->base = cpu_to_le32(addr);
+		bank->loop_start = 0;
+		bank->loop_end = cpu_to_le32(end);
+		bank->loop_frac = 0;
+		bank->eg_gain_end = default_gain;
+		bank->lpfQ = cpu_to_le32(lpfQ);
+		bank->status = 0;
+		bank->num_of_frames = 0;
+		bank->loop_count = 0;
+		bank->start = 0;
+		bank->start_frac = 0;
+		bank->delta =
+		bank->delta_end = cpu_to_le32(delta);
+		bank->lpfK =
+		bank->lpfK_end = cpu_to_le32(lpfK);
+		bank->eg_gain = default_gain;
+		bank->lpfD1 =
+		bank->lpfD2 = 0;
+
+		bank->left_gain = 
+		bank->right_gain =
+		bank->left_gain_end =
+		bank->right_gain_end =
+		bank->eff1_gain =
+		bank->eff2_gain =
+		bank->eff3_gain =
+		bank->eff1_gain_end =
+		bank->eff2_gain_end =
+		bank->eff3_gain_end = 0;
+
+		if (!stereo) {
+			if (!spdif) {
+				bank->left_gain = 
+				bank->right_gain =
+				bank->left_gain_end =
+				bank->right_gain_end = default_gain;
+			} else {
+				bank->eff2_gain =
+				bank->eff2_gain_end =
+				bank->eff3_gain =
+				bank->eff3_gain_end = default_gain;
+			}
+		} else {
+			if (!spdif) {
+				if ((voice->number & 1) == 0) {
+					bank->left_gain =
+					bank->left_gain_end = default_gain;
+				} else {
+					bank->format |= cpu_to_le32(1);
+					bank->right_gain =
+					bank->right_gain_end = default_gain;
+				}
+			} else {
+				if ((voice->number & 1) == 0) {
+					bank->eff2_gain =
+					bank->eff2_gain_end = default_gain;
+				} else {
+					bank->format |= cpu_to_le32(1);
+					bank->eff3_gain =
+					bank->eff3_gain_end = default_gain;
+				}
+			}
+		}
+	}
+}
+
+/*
+ * XXX Capture channel allocation is entirely fake at the moment.
+ * We use only one channel and mark it busy as required.
+ */
+static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank)
+{
+	struct ymf_capture *cap;
+	int cbank;
+
+	cbank = 1;		/* Only ADC slot is used for now. */
+	cap = &unit->capture[cbank];
+	if (cap->use)
+		return -EBUSY;
+	cap->use = 1;
+	*pbank = cbank;
+	return 0;
+}
+
+static int ymf_playback_prepare(struct ymf_state *state)
+{
+	struct ymf_pcm *ypcm = &state->wpcm;
+	int err, nvoice;
+
+	if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
+		/* Somebody started 32 mpg123's in parallel? */
+		printk(KERN_INFO "ymfpci%d: cannot allocate voice\n",
+		    state->unit->dev_audio);
+		return err;
+	}
+
+	for (nvoice = 0; nvoice < state->format.voices; nvoice++) {
+		ymf_pcm_init_voice(ypcm->voices[nvoice],
+		    state->format.voices == 2, state->format.rate,
+		    ymf_pcm_format_width(state->format.format) == 16,
+		    ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize,
+		    ypcm->spdif);
+	}
+	return 0;
+}
+
+static int ymf_capture_prepare(struct ymf_state *state)
+{
+	ymfpci_t *unit = state->unit;
+	struct ymf_pcm *ypcm = &state->rpcm;
+	ymfpci_capture_bank_t * bank;
+	/* XXX This is confusing, gotta rename one of them banks... */
+	int nbank;		/* flip-flop bank */
+	int cbank;		/* input [super-]bank */
+	struct ymf_capture *cap;
+	u32 rate, format;
+
+	if (ypcm->capture_bank_number == -1) {
+		if (ymf_capture_alloc(unit, &cbank) != 0)
+			return -EBUSY;
+
+		ypcm->capture_bank_number = cbank;
+
+		cap = &unit->capture[cbank];
+		cap->bank = unit->bank_capture[cbank][0];
+		cap->ypcm = ypcm;
+		ymfpci_hw_start(unit);
+	}
+
+	// ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream);
+	// frag_size is replaced with nonfragged byte-aligned rolling buffer
+	rate = ((48000 * 4096) / state->format.rate) - 1;
+	format = 0;
+	if (state->format.voices == 2)
+		format |= 2;
+	if (ymf_pcm_format_width(state->format.format) == 8)
+		format |= 1;
+	switch (ypcm->capture_bank_number) {
+	case 0:
+		ymfpci_writel(unit, YDSXGR_RECFORMAT, format);
+		ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate);
+		break;
+	case 1:
+		ymfpci_writel(unit, YDSXGR_ADCFORMAT, format);
+		ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate);
+		break;
+	}
+	for (nbank = 0; nbank < 2; nbank++) {
+		bank = unit->bank_capture[ypcm->capture_bank_number][nbank];
+		bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr);
+		// bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift;
+		bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize);
+		bank->start = 0;
+		bank->num_of_loops = 0;
+	}
+#if 0 /* s/pdif */
+	if (state->digital.dig_valid)
+		/*state->digital.type == SND_PCM_DIG_AES_IEC958*/
+		ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS,
+		    state->digital.dig_status[0] | (state->digital.dig_status[1] << 8));
+#endif
+	return 0;
+}
+
+void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ymfpci_t *codec = dev_id;
+	u32 status, nvoice, mode;
+	struct ymf_voice *voice;
+	struct ymf_capture *cap;
+
+	status = ymfpci_readl(codec, YDSXGR_STATUS);
+	if (status & 0x80000000) {
+		codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1;
+		spin_lock(&codec->voice_lock);
+		for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
+			voice = &codec->voices[nvoice];
+			if (voice->use)
+				ymf_pcm_interrupt(codec, voice);
+		}
+		for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
+			cap = &codec->capture[nvoice];
+			if (cap->use)
+				ymf_cap_interrupt(codec, cap);
+		}
+		spin_unlock(&codec->voice_lock);
+		spin_lock(&codec->reg_lock);
+		ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000);
+		mode = ymfpci_readl(codec, YDSXGR_MODE) | 2;
+		ymfpci_writel(codec, YDSXGR_MODE, mode);
+		spin_unlock(&codec->reg_lock);
+	}
+
+	status = ymfpci_readl(codec, YDSXGR_INTFLAG);
+	if (status & 1) {
+		/* timer handler */
+		ymfpci_writel(codec, YDSXGR_INTFLAG, ~0);
+	}
+}
+
+static void ymf_pcm_free_substream(struct ymf_pcm *ypcm)
+{
+	unsigned long flags;
+	struct ymf_unit *unit;
+
+	unit = ypcm->state->unit;
+
+	if (ypcm->type == PLAYBACK_VOICE) {
+		spin_lock_irqsave(&unit->voice_lock, flags);
+		if (ypcm->voices[1])
+			ymfpci_voice_free(unit, ypcm->voices[1]);
+		if (ypcm->voices[0])
+			ymfpci_voice_free(unit, ypcm->voices[0]);
+		spin_unlock_irqrestore(&unit->voice_lock, flags);
+	} else {
+		if (ypcm->capture_bank_number != -1) {
+			unit->capture[ypcm->capture_bank_number].use = 0;
+			ypcm->capture_bank_number = -1;
+			ymfpci_hw_stop(unit);
+		}
+	}
+}
+
+static struct ymf_state *ymf_state_alloc(ymfpci_t *unit)
+{
+	struct ymf_pcm *ypcm;
+	struct ymf_state *state;
+
+	if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) {
+		goto out0;
+	}
+	memset(state, 0, sizeof(struct ymf_state));
+
+	ypcm = &state->wpcm;
+	ypcm->state = state;
+	ypcm->type = PLAYBACK_VOICE;
+	ypcm->capture_bank_number = -1;
+	init_waitqueue_head(&ypcm->dmabuf.wait);
+
+	ypcm = &state->rpcm;
+	ypcm->state = state;
+	ypcm->type = CAPTURE_AC97;
+	ypcm->capture_bank_number = -1;
+	init_waitqueue_head(&ypcm->dmabuf.wait);
+
+	state->unit = unit;
+
+	state->format.format = AFMT_U8;
+	state->format.rate = 8000;
+	state->format.voices = 1;
+	ymf_pcm_update_shift(&state->format);
+
+	return state;
+
+out0:
+	return NULL;
+}
+
+/* AES/IEC958 channel status bits */
+#define SND_PCM_AES0_PROFESSIONAL	(1<<0)	/* 0 = consumer, 1 = professional */
+#define SND_PCM_AES0_NONAUDIO		(1<<1)	/* 0 = audio, 1 = non-audio */
+#define SND_PCM_AES0_PRO_EMPHASIS	(7<<2)	/* mask - emphasis */
+#define SND_PCM_AES0_PRO_EMPHASIS_NOTID	(0<<2)	/* emphasis not indicated */
+#define SND_PCM_AES0_PRO_EMPHASIS_NONE	(1<<2)	/* none emphasis */
+#define SND_PCM_AES0_PRO_EMPHASIS_5015	(3<<2)	/* 50/15us emphasis */
+#define SND_PCM_AES0_PRO_EMPHASIS_CCITT	(7<<2)	/* CCITT J.17 emphasis */
+#define SND_PCM_AES0_PRO_FREQ_UNLOCKED	(1<<5)	/* source sample frequency: 0 = locked, 1 = unlocked */
+#define SND_PCM_AES0_PRO_FS		(3<<6)	/* mask - sample frequency */
+#define SND_PCM_AES0_PRO_FS_NOTID	(0<<6)	/* fs not indicated */
+#define SND_PCM_AES0_PRO_FS_44100	(1<<6)	/* 44.1kHz */
+#define SND_PCM_AES0_PRO_FS_48000	(2<<6)	/* 48kHz */
+#define SND_PCM_AES0_PRO_FS_32000	(3<<6)	/* 32kHz */
+#define SND_PCM_AES0_CON_NOT_COPYRIGHT	(1<<2)	/* 0 = copyright, 1 = not copyright */
+#define SND_PCM_AES0_CON_EMPHASIS	(7<<3)	/* mask - emphasis */
+#define SND_PCM_AES0_CON_EMPHASIS_NONE	(0<<3)	/* none emphasis */
+#define SND_PCM_AES0_CON_EMPHASIS_5015	(1<<3)	/* 50/15us emphasis */
+#define SND_PCM_AES0_CON_MODE		(3<<6)	/* mask - mode */
+#define SND_PCM_AES1_PRO_MODE		(15<<0)	/* mask - channel mode */
+#define SND_PCM_AES1_PRO_MODE_NOTID	(0<<0)	/* not indicated */
+#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */
+#define SND_PCM_AES1_PRO_MODE_SINGLE	(4<<0)	/* single channel */
+#define SND_PCM_AES1_PRO_MODE_TWO	(8<<0)	/* two channels */
+#define SND_PCM_AES1_PRO_MODE_PRIMARY	(12<<0)	/* primary/secondary */
+#define SND_PCM_AES1_PRO_MODE_BYTE3	(15<<0)	/* vector to byte 3 */
+#define SND_PCM_AES1_PRO_USERBITS	(15<<4)	/* mask - user bits */
+#define SND_PCM_AES1_PRO_USERBITS_NOTID	(0<<4)	/* not indicated */
+#define SND_PCM_AES1_PRO_USERBITS_192	(8<<4)	/* 192-bit structure */
+#define SND_PCM_AES1_PRO_USERBITS_UDEF	(12<<4)	/* user defined application */
+#define SND_PCM_AES1_CON_CATEGORY	0x7f
+#define SND_PCM_AES1_CON_GENERAL	0x00
+#define SND_PCM_AES1_CON_EXPERIMENTAL	0x40
+#define SND_PCM_AES1_CON_SOLIDMEM_MASK	0x0f
+#define SND_PCM_AES1_CON_SOLIDMEM_ID	0x08
+#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07
+#define SND_PCM_AES1_CON_BROADCAST1_ID	0x04
+#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07
+#define SND_PCM_AES1_CON_DIGDIGCONV_ID	0x02
+#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f
+#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06
+#define SND_PCM_AES1_CON_ADC_MASK	0x1f
+#define SND_PCM_AES1_CON_ADC_ID		0x16
+#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f
+#define SND_PCM_AES1_CON_BROADCAST2_ID	0x0e
+#define SND_PCM_AES1_CON_LASEROPT_MASK	0x07
+#define SND_PCM_AES1_CON_LASEROPT_ID	0x01
+#define SND_PCM_AES1_CON_MUSICAL_MASK	0x07
+#define SND_PCM_AES1_CON_MUSICAL_ID	0x05
+#define SND_PCM_AES1_CON_MAGNETIC_MASK	0x07
+#define SND_PCM_AES1_CON_MAGNETIC_ID	0x03
+#define SND_PCM_AES1_CON_IEC908_CD	(SND_PCM_AES1_CON_LASEROPT_ID|0x00)
+#define SND_PCM_AES1_CON_NON_IEC908_CD	(SND_PCM_AES1_CON_LASEROPT_ID|0x08)
+#define SND_PCM_AES1_CON_PCM_CODER	(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00)
+#define SND_PCM_AES1_CON_SAMPLER	(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20)
+#define SND_PCM_AES1_CON_MIXER		(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10)
+#define SND_PCM_AES1_CON_RATE_CONVERTER	(SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18)
+#define SND_PCM_AES1_CON_SYNTHESIZER	(SND_PCM_AES1_CON_MUSICAL_ID|0x00)
+#define SND_PCM_AES1_CON_MICROPHONE	(SND_PCM_AES1_CON_MUSICAL_ID|0x08)
+#define SND_PCM_AES1_CON_DAT		(SND_PCM_AES1_CON_MAGNETIC_ID|0x00)
+#define SND_PCM_AES1_CON_VCR		(SND_PCM_AES1_CON_MAGNETIC_ID|0x08)
+#define SND_PCM_AES1_CON_ORIGINAL	(1<<7)	/* this bits depends on the category code */
+#define SND_PCM_AES2_PRO_SBITS		(7<<0)	/* mask - sample bits */
+#define SND_PCM_AES2_PRO_SBITS_20	(2<<0)	/* 20-bit - coordination */
+#define SND_PCM_AES2_PRO_SBITS_24	(4<<0)	/* 24-bit - main audio */
+#define SND_PCM_AES2_PRO_SBITS_UDEF	(6<<0)	/* user defined application */
+#define SND_PCM_AES2_PRO_WORDLEN	(7<<3)	/* mask - source word length */
+#define SND_PCM_AES2_PRO_WORDLEN_NOTID	(0<<3)	/* not indicated */
+#define SND_PCM_AES2_PRO_WORDLEN_22_18	(2<<3)	/* 22-bit or 18-bit */
+#define SND_PCM_AES2_PRO_WORDLEN_23_19	(4<<3)	/* 23-bit or 19-bit */
+#define SND_PCM_AES2_PRO_WORDLEN_24_20	(5<<3)	/* 24-bit or 20-bit */
+#define SND_PCM_AES2_PRO_WORDLEN_20_16	(6<<3)	/* 20-bit or 16-bit */
+#define SND_PCM_AES2_CON_SOURCE		(15<<0)	/* mask - source number */
+#define SND_PCM_AES2_CON_SOURCE_UNSPEC	(0<<0)	/* unspecified */
+#define SND_PCM_AES2_CON_CHANNEL	(15<<4)	/* mask - channel number */
+#define SND_PCM_AES2_CON_CHANNEL_UNSPEC	(0<<4)	/* unspecified */
+#define SND_PCM_AES3_CON_FS		(15<<0)	/* mask - sample frequency */
+#define SND_PCM_AES3_CON_FS_44100	(0<<0)	/* 44.1kHz */
+#define SND_PCM_AES3_CON_FS_48000	(2<<0)	/* 48kHz */
+#define SND_PCM_AES3_CON_FS_32000	(3<<0)	/* 32kHz */
+#define SND_PCM_AES3_CON_CLOCK		(3<<4)	/* mask - clock accuracy */
+#define SND_PCM_AES3_CON_CLOCK_1000PPM	(0<<4)	/* 1000 ppm */
+#define SND_PCM_AES3_CON_CLOCK_50PPM	(1<<4)	/* 50 ppm */
+#define SND_PCM_AES3_CON_CLOCK_VARIABLE	(2<<4)	/* variable pitch */
+
+/*
+ * User interface
+ */
+
+/*
+ * in this loop, dmabuf.count signifies the amount of data that is
+ * waiting to be copied to the user's buffer.  it is filled by the dma
+ * machine and drained by this loop.
+ */
+static ssize_t
+ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf;
+	struct ymf_unit *unit = state->unit;
+	DECLARE_WAITQUEUE(waita, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned int swptr;
+	int cnt;			/* This many to go in this revolution */
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+		return ret;
+	ret = 0;
+
+	add_wait_queue(&dmabuf->wait, &waita);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (count > 0) {
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (signal_pending(current)) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			continue;
+		}
+		swptr = dmabuf->swptr;
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count < cnt)
+			cnt = dmabuf->count;
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			unsigned long tmo;
+			/* buffer is empty, start the dma machine and wait for data to be
+			   recorded */
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			if (!state->rpcm.running) {
+				ymf_capture_trigger(state->unit, &state->rpcm, 1);
+			}
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			/* This isnt strictly right for the 810  but it'll do */
+			tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2);
+			tmo >>= state->format.shift;
+			/* There are two situations when sleep_on_timeout returns, one is when
+			   the interrupt is serviced correctly and the process is waked up by
+			   ISR ON TIME. Another is when timeout is expired, which means that
+			   either interrupt is NOT serviced correctly (pending interrupt) or it
+			   is TOO LATE for the process to be scheduled to run (scheduler latency)
+			   which results in a (potential) buffer overrun. And worse, there is
+			   NOTHING we can do to prevent it. */
+			tmo = schedule_timeout(tmo);
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (tmo == 0 && dmabuf->count == 0) {
+				printk(KERN_ERR "ymfpci%d: recording schedule timeout, "
+				    "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+				    state->unit->dev_audio,
+				    dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
+				    dmabuf->hwptr, dmabuf->swptr);
+			}
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+
+		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
+			if (!ret) ret = -EFAULT;
+			break;
+		}
+
+		swptr = (swptr + cnt) % dmabuf->dmasize;
+
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			continue;
+		}
+
+		dmabuf->swptr = swptr;
+		dmabuf->count -= cnt;
+		// spin_unlock_irqrestore(&unit->reg_lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		// spin_lock_irqsave(&unit->reg_lock, flags);
+		if (!state->rpcm.running) {
+			ymf_capture_trigger(unit, &state->rpcm, 1);
+		}
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dmabuf->wait, &waita);
+
+	return ret;
+}
+
+static ssize_t
+ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
+	struct ymf_unit *unit = state->unit;
+	DECLARE_WAITQUEUE(waita, current);
+	ssize_t ret;
+	unsigned long flags;
+	unsigned int swptr;
+	int cnt;			/* This many to go in this revolution */
+	int redzone;
+	int delay;
+
+	YMFDBGW("ymf_write: count %d\n", count);
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (dmabuf->mapped)
+		return -ENXIO;
+	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+		return ret;
+	ret = 0;
+
+	/*
+	 * Alan's cs46xx works without a red zone - marvel of ingenuity.
+	 * We are not so brilliant... Red zone does two things:
+	 *  1. allows for safe start after a pause as we have no way
+	 *     to know what the actual, relentlessly advancing, hwptr is.
+	 *  2. makes computations in ymf_pcm_interrupt simpler.
+	 */
+	redzone = ymf_calc_lend(state->format.rate) << state->format.shift;
+	redzone *= 3;	/* 2 redzone + 1 possible uncertainty reserve. */
+
+	add_wait_queue(&dmabuf->wait, &waita);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while (count > 0) {
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (signal_pending(current)) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			continue;
+		}
+		if (dmabuf->count < 0) {
+			printk(KERN_ERR
+			   "ymf_write: count %d, was legal in cs46xx\n",
+			    dmabuf->count);
+			dmabuf->count = 0;
+		}
+		if (dmabuf->count == 0) {
+			swptr = dmabuf->hwptr;
+			if (state->wpcm.running) {
+				/*
+				 * Add uncertainty reserve.
+				 */
+				cnt = ymf_calc_lend(state->format.rate);
+				cnt <<= state->format.shift;
+				if ((swptr += cnt) >= dmabuf->dmasize) {
+					swptr -= dmabuf->dmasize;
+				}
+			}
+			dmabuf->swptr = swptr;
+		} else {
+			/*
+			 * XXX This is not right if dmabuf->count is small -
+			 * about 2*x frame size or less. We cannot count on
+			 * on appending and not causing an artefact.
+			 * Should use a variation of the count==0 case above.
+			 */
+			swptr = dmabuf->swptr;
+		}
+		cnt = dmabuf->dmasize - swptr;
+		if (dmabuf->count + cnt > dmabuf->dmasize - redzone)
+			cnt = (dmabuf->dmasize - redzone) - dmabuf->count;
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
+
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			YMFDBGW("ymf_write: full, count %d swptr %d\n",
+			   dmabuf->count, dmabuf->swptr);
+			/*
+			 * buffer is full, start the dma machine and
+			 * wait for data to be played
+			 */
+			spin_lock_irqsave(&unit->reg_lock, flags);
+			if (!state->wpcm.running) {
+				ymf_playback_trigger(unit, &state->wpcm, 1);
+			}
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret) ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (signal_pending(current)) {
+				if (!ret) ret = -ERESTARTSYS;
+				break;
+			}
+			continue;
+		}
+		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+			if (!ret) ret = -EFAULT;
+			break;
+		}
+
+		if ((swptr += cnt) >= dmabuf->dmasize) {
+			swptr -= dmabuf->dmasize;
+		}
+
+		spin_lock_irqsave(&unit->reg_lock, flags);
+		if (unit->suspended) {
+			spin_unlock_irqrestore(&unit->reg_lock, flags);
+			continue;
+		}
+		dmabuf->swptr = swptr;
+		dmabuf->count += cnt;
+
+		/*
+		 * Start here is a bad idea - may cause startup click
+		 * in /bin/play when dmabuf is not full yet.
+		 * However, some broken applications do not make
+		 * any use of SNDCTL_DSP_SYNC (Doom is the worst).
+		 * One frame is about 5.3ms, Doom write size is 46ms.
+		 */
+		delay = state->format.rate / 20;	/* 50ms */
+		delay <<= state->format.shift;
+		if (dmabuf->count >= delay && !state->wpcm.running) {
+			ymf_playback_trigger(unit, &state->wpcm, 1);
+		}
+
+		spin_unlock_irqrestore(&unit->reg_lock, flags);
+
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dmabuf->wait, &waita);
+
+	YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, dmabuf->count);
+	return ret;
+}
+
+static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf;
+	int redzone;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &state->wpcm.dmabuf.wait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &state->rpcm.dmabuf.wait, wait);
+
+	spin_lock_irqsave(&state->unit->reg_lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		dmabuf = &state->rpcm.dmabuf;
+		if (dmabuf->count >= (signed)dmabuf->fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= state->format.shift;
+		redzone *= 3;
+
+		dmabuf = &state->wpcm.dmabuf;
+		if (dmabuf->mapped) {
+			if (dmabuf->count >= (signed)dmabuf->fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			/*
+			 * Don't select unless a full fragment is available.
+			 * Otherwise artsd does GETOSPACE, sees 0, and loops.
+			 */
+			if (dmabuf->count + redzone + dmabuf->fragsize
+			     <= dmabuf->dmasize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+
+	return mask;
+}
+
+static int ymf_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf;
+	int ret;
+	unsigned long size;
+
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(state, 0)) != 0)
+			return ret;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(state, 1)) != 0)
+			return ret;
+	} else 
+		return -EINVAL;
+
+	if (vma->vm_pgoff != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << dmabuf->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+			     size, vma->vm_page_prot))
+		return -EAGAIN;
+	dmabuf->mapped = 1;
+
+/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n");
+	return 0;
+}
+
+static int ymf_ioctl(struct inode *inode, struct file *file,
+    unsigned int cmd, unsigned long arg)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	struct ymf_dmabuf *dmabuf;
+	unsigned long flags;
+	audio_buf_info abinfo;
+	count_info cinfo;
+	int redzone;
+	int val;
+
+	switch (cmd) {
+	case OSS_GETVERSION:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg);
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_RESET:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd);
+		if (file->f_mode & FMODE_WRITE) {
+			ymf_wait_dac(state);
+			dmabuf = &state->wpcm.dmabuf;
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			dmabuf->ready = 0;
+			dmabuf->swptr = dmabuf->hwptr;
+			dmabuf->count = dmabuf->total_bytes = 0;
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		if (file->f_mode & FMODE_READ) {
+			ymf_stop_adc(state);
+			dmabuf = &state->rpcm.dmabuf;
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			dmabuf->ready = 0;
+			dmabuf->swptr = dmabuf->hwptr;
+			dmabuf->count = dmabuf->total_bytes = 0;
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd);
+		if (file->f_mode & FMODE_WRITE) {
+			dmabuf = &state->wpcm.dmabuf;
+			if (file->f_flags & O_NONBLOCK) {
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				if (dmabuf->count != 0 && !state->wpcm.running) {
+					ymf_start_dac(state);
+				}
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			} else {
+				ymf_wait_dac(state);
+			}
+		}
+		/* XXX What does this do for reading? dmabuf->count=0; ? */
+		return 0;
+
+	case SNDCTL_DSP_SPEED: /* set smaple rate */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val);
+		if (val >= 8000 && val <= 48000) {
+			if (file->f_mode & FMODE_WRITE) {
+				ymf_wait_dac(state);
+				dmabuf = &state->wpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.rate = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			}
+			if (file->f_mode & FMODE_READ) {
+				ymf_stop_adc(state);
+				dmabuf = &state->rpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.rate = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			}
+		}
+		return put_user(state->format.rate, (int *)arg);
+
+	/*
+	 * OSS manual does not mention SNDCTL_DSP_STEREO at all.
+	 * All channels are mono and if you want stereo, you
+	 * play into two channels with SNDCTL_DSP_CHANNELS.
+	 * However, mpg123 calls it. I wonder, why Michael Hipp used it.
+	 */
+	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val);
+		if (file->f_mode & FMODE_WRITE) {
+			ymf_wait_dac(state); 
+			dmabuf = &state->wpcm.dmabuf;
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			dmabuf->ready = 0;
+			state->format.voices = val ? 2 : 1;
+			ymf_pcm_update_shift(&state->format);
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		if (file->f_mode & FMODE_READ) {
+			ymf_stop_adc(state);
+			dmabuf = &state->rpcm.dmabuf;
+			spin_lock_irqsave(&state->unit->reg_lock, flags);
+			dmabuf->ready = 0;
+			state->format.voices = val ? 2 : 1;
+			ymf_pcm_update_shift(&state->format);
+			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETBLKSIZE:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd);
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(state, 0)))
+				return val;
+			val = state->wpcm.dmabuf.fragsize;
+			YMFDBGX("ymf_ioctl: GETBLK w %d\n", val);
+			return put_user(val, (int *)arg);
+		}
+		if (file->f_mode & FMODE_READ) {
+			if ((val = prog_dmabuf(state, 1)))
+				return val;
+			val = state->rpcm.dmabuf.fragsize;
+			YMFDBGX("ymf_ioctl: GETBLK r %d\n", val);
+			return put_user(val, (int *)arg);
+		}
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd);
+		return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT: /* Select sample format */
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val);
+		if (val == AFMT_S16_LE || val == AFMT_U8) {
+			if (file->f_mode & FMODE_WRITE) {
+				ymf_wait_dac(state);
+				dmabuf = &state->wpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.format = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			}
+			if (file->f_mode & FMODE_READ) {
+				ymf_stop_adc(state);
+				dmabuf = &state->rpcm.dmabuf;
+				spin_lock_irqsave(&state->unit->reg_lock, flags);
+				dmabuf->ready = 0;
+				state->format.format = val;
+				ymf_pcm_update_shift(&state->format);
+				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+			}
+		}
+		return put_user(state->format.format, (int *)arg);
+
+	case SNDCTL_DSP_CHANNELS:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, val);
+		if (val != 0) {
+			if (file->f_mode & FMODE_WRITE) {
+				ymf_wait_dac(state);
+				if (val == 1 || val == 2) {
+					spin_lock_irqsave(&state->unit->reg_lock, flags);
+					dmabuf = &state->wpcm.dmabuf;
+					dmabuf->ready = 0;
+					state->format.voices = val;
+					ymf_pcm_update_shift(&state->format);
+					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+				}
+			}
+			if (file->f_mode & FMODE_READ) {
+				ymf_stop_adc(state);
+				if (val == 1 || val == 2) {
+					spin_lock_irqsave(&state->unit->reg_lock, flags);
+					dmabuf = &state->rpcm.dmabuf;
+					dmabuf->ready = 0;
+					state->format.voices = val;
+					ymf_pcm_update_shift(&state->format);
+					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+				}
+			}
+		}
+		return put_user(state->format.voices, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd);
+		/*
+		 * Quoting OSS PG:
+		 *    The ioctl SNDCTL_DSP_POST is a lightweight version of
+		 *    SNDCTL_DSP_SYNC. It just tells to the driver that there
+		 *    is likely to be a pause in the output. This makes it
+		 *    possible for the device to handle the pause more
+		 *    intelligently. This ioctl doesn't block the application.
+		 *
+		 * The paragraph above is a clumsy way to say "flush ioctl".
+		 * This ioctl is used by mpg123.
+		 */
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) {
+			ymf_start_dac(state);
+		}
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return 0;
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n",
+		    cmd,
+		    (val >> 16) & 0xFFFF, val & 0xFFFF,
+		    (val >> 16) & 0xFFFF, val & 0xFFFF);
+		dmabuf = &state->wpcm.dmabuf;
+		dmabuf->ossfragshift = val & 0xffff;
+		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
+		if (dmabuf->ossfragshift < 4)
+			dmabuf->ossfragshift = 4;
+		if (dmabuf->ossfragshift > 15)
+			dmabuf->ossfragshift = 15;
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd);
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		dmabuf = &state->wpcm.dmabuf;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		redzone = ymf_calc_lend(state->format.rate);
+		redzone <<= state->format.shift;
+		redzone *= 3;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		abinfo.fragsize = dmabuf->fragsize;
+		abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone;
+		abinfo.fragstotal = dmabuf->numfrag;
+		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd);
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		dmabuf = &state->rpcm.dmabuf;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		abinfo.fragsize = dmabuf->fragsize;
+		abinfo.bytes = dmabuf->count;
+		abinfo.fragstotal = dmabuf->numfrag;
+		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_NONBLOCK:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd);
+		file->f_flags |= O_NONBLOCK;
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd);
+		/* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
+			    (int *)arg); */
+		return put_user(0, (int *)arg);
+
+	case SNDCTL_DSP_GETIPTR:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd);
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		dmabuf = &state->rpcm.dmabuf;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.ptr = dmabuf->hwptr;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n",
+		    cinfo.ptr, cinfo.bytes);
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETOPTR:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd);
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		dmabuf = &state->wpcm.dmabuf;
+		spin_lock_irqsave(&state->unit->reg_lock, flags);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.ptr = dmabuf->hwptr;
+		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
+		YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n",
+		    cinfo.ptr, cinfo.bytes);
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_SETDUPLEX:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd);
+		return 0;		/* Always duplex */
+
+	case SOUND_PCM_READ_RATE:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd);
+		return put_user(state->format.rate, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd);
+		return put_user(state->format.voices, (int *)arg);
+
+	case SOUND_PCM_READ_BITS:
+		YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd);
+		return put_user(AFMT_S16_LE, (int *)arg);
+
+	case SNDCTL_DSP_MAPINBUF:
+	case SNDCTL_DSP_MAPOUTBUF:
+	case SNDCTL_DSP_SETSYNCRO:
+	case SOUND_PCM_WRITE_FILTER:
+	case SOUND_PCM_READ_FILTER:
+		YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd);
+		return -ENOTTY;
+
+	default:
+		/*
+		 * Some programs mix up audio devices and ioctls
+		 * or perhaps they expect "universal" ioctls,
+		 * for instance we get SNDCTL_TMR_CONTINUE here.
+		 * (mpg123 -g 100 ends here too - to be fixed.)
+		 */
+		YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd);
+		break;
+	}
+	return -ENOTTY;
+}
+
+/*
+ * open(2)
+ * We use upper part of the minor to distinguish between soundcards.
+ * Channels are opened with a clone open.
+ */
+static int ymf_open(struct inode *inode, struct file *file)
+{
+	struct list_head *list;
+	ymfpci_t *unit = NULL;
+	int minor;
+	struct ymf_state *state;
+	int err;
+
+	minor = minor(inode->i_rdev);
+	if ((minor & 0x0F) == 3) {	/* /dev/dspN */
+		;
+	} else {
+		return -ENXIO;
+	}
+
+	unit = NULL;	/* gcc warns */
+	list_for_each(list, &ymf_devs) {
+		unit = list_entry(list, ymfpci_t, ymf_devs);
+		if (((unit->dev_audio ^ minor) & ~0x0F) == 0)
+			break;
+	}
+	if (list == &ymf_devs)
+		return -ENODEV;
+
+	down(&unit->open_sem);
+
+	if ((state = ymf_state_alloc(unit)) == NULL) {
+		up(&unit->open_sem);
+		return -ENOMEM;
+	}
+	list_add_tail(&state->chain, &unit->states);
+
+	file->private_data = state;
+
+	/*
+	 * ymf_read and ymf_write that we borrowed from cs46xx
+	 * allocate buffers with prog_dmabuf(). We call prog_dmabuf
+	 * here so that in case of DMA memory exhaustion open
+	 * fails rather than write.
+	 *
+	 * XXX prog_dmabuf allocates voice. Should allocate explicitly, above.
+	 */
+	if (file->f_mode & FMODE_WRITE) {
+		if (!state->wpcm.dmabuf.ready) {
+			if ((err = prog_dmabuf(state, 0)) != 0) {
+				goto out_nodma;
+			}
+		}
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!state->rpcm.dmabuf.ready) {
+			if ((err = prog_dmabuf(state, 1)) != 0) {
+				goto out_nodma;
+			}
+		}
+	}
+
+#if 0 /* test if interrupts work */
+	ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe);	/* ~ 680ms */
+	ymfpci_writeb(unit, YDSXGR_TIMERCTRL,
+	    (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));
+#endif
+	up(&unit->open_sem);
+
+	return 0;
+
+out_nodma:
+	/*
+	 * XXX Broken custom: "goto out_xxx" in other place is
+	 * a nestable exception, but here it is not nestable due to semaphore.
+	 * XXX Doubtful technique of self-describing objects....
+	 */
+	dealloc_dmabuf(unit, &state->wpcm.dmabuf);
+	dealloc_dmabuf(unit, &state->rpcm.dmabuf);
+	ymf_pcm_free_substream(&state->wpcm);
+	ymf_pcm_free_substream(&state->rpcm);
+
+	list_del(&state->chain);
+	kfree(state);
+
+	up(&unit->open_sem);
+	return err;
+}
+
+static int ymf_release(struct inode *inode, struct file *file)
+{
+	struct ymf_state *state = (struct ymf_state *)file->private_data;
+	ymfpci_t *unit = state->unit;
+
+#if 0 /* test if interrupts work */
+	ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0);
+#endif
+
+	down(&unit->open_sem);
+
+	/*
+	 * XXX Solve the case of O_NONBLOCK close - don't deallocate here.
+	 * Deallocate when unloading the driver and we can wait.
+	 */
+	ymf_wait_dac(state);
+	ymf_stop_adc(state);		/* fortunately, it's immediate */
+	dealloc_dmabuf(unit, &state->wpcm.dmabuf);
+	dealloc_dmabuf(unit, &state->rpcm.dmabuf);
+	ymf_pcm_free_substream(&state->wpcm);
+	ymf_pcm_free_substream(&state->rpcm);
+
+	list_del(&state->chain);
+	file->private_data = NULL;	/* Can you tell I programmed Solaris */
+	kfree(state);
+
+	up(&unit->open_sem);
+
+	return 0;
+}
+
+/*
+ * Mixer operations are based on cs46xx.
+ */
+static int ymf_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = minor(inode->i_rdev);
+	struct list_head *list;
+	ymfpci_t *unit;
+	int i;
+
+	list_for_each(list, &ymf_devs) {
+		unit = list_entry(list, ymfpci_t, ymf_devs);
+		for (i = 0; i < NR_AC97; i++) {
+			if (unit->ac97_codec[i] != NULL &&
+			    unit->ac97_codec[i]->dev_mixer == minor) {
+				goto match;
+			}
+		}
+	}
+	return -ENODEV;
+
+ match:
+	file->private_data = unit->ac97_codec[i];
+
+	return 0;
+}
+
+static int ymf_ioctl_mixdev(struct inode *inode, struct file *file,
+    unsigned int cmd, unsigned long arg)
+{
+	struct ac97_codec *codec = (struct ac97_codec *)file->private_data;
+
+	return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int ymf_release_mixdev(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static /*const*/ struct file_operations ymf_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		ymf_read,
+	write:		ymf_write,
+	poll:		ymf_poll,
+	ioctl:		ymf_ioctl,
+	mmap:		ymf_mmap,
+	open:		ymf_open,
+	release:	ymf_release,
+};
+
+static /*const*/ struct file_operations ymf_mixer_fops = {
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	ioctl:		ymf_ioctl_mixdev,
+	open:		ymf_open_mixdev,
+	release:	ymf_release_mixdev,
+};
+
+/*
+ */
+
+static int ymf_suspend(struct pci_dev *pcidev, u32 unused)
+{
+	struct ymf_unit *unit = pci_get_drvdata(pcidev);
+	unsigned long flags;
+	struct ymf_dmabuf *dmabuf;
+	struct list_head *p;
+	struct ymf_state *state;
+	struct ac97_codec *codec;
+	int i;
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+
+	unit->suspended = 1;
+
+	for (i = 0; i < NR_AC97; i++) {
+		if ((codec = unit->ac97_codec[i]) != NULL)
+			ac97_save_state(codec);
+	}
+
+	list_for_each(p, &unit->states) {
+		state = list_entry(p, struct ymf_state, chain);
+
+		dmabuf = &state->wpcm.dmabuf;
+		dmabuf->hwptr = dmabuf->swptr = 0;
+		dmabuf->total_bytes = 0;
+		dmabuf->count = 0;
+
+		dmabuf = &state->rpcm.dmabuf;
+		dmabuf->hwptr = dmabuf->swptr = 0;
+		dmabuf->total_bytes = 0;
+		dmabuf->count = 0;
+	}
+
+	ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0);
+	ymfpci_disable_dsp(unit);
+
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+	
+	return 0;
+}
+
+static int ymf_resume(struct pci_dev *pcidev)
+{
+	struct ymf_unit *unit = pci_get_drvdata(pcidev);
+	unsigned long flags;
+	struct list_head *p;
+	struct ymf_state *state;
+	struct ac97_codec *codec;
+	int i;
+
+	ymfpci_aclink_reset(unit->pci);
+	ymfpci_codec_ready(unit, 0, 1);		/* prints diag if not ready. */
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+	/* XXX At this time the legacy registers are probably deprogrammed. */
+#endif
+
+	ymfpci_download_image(unit);
+
+	ymf_memload(unit);
+
+	spin_lock_irqsave(&unit->reg_lock, flags);
+
+	if (unit->start_count) {
+		ymfpci_writel(unit, YDSXGR_MODE, 3);
+		unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1;
+	}
+
+	for (i = 0; i < NR_AC97; i++) {
+		if ((codec = unit->ac97_codec[i]) != NULL)
+			ac97_restore_state(codec);
+	}
+
+	unit->suspended = 0;
+	list_for_each(p, &unit->states) {
+		state = list_entry(p, struct ymf_state, chain);
+		wake_up(&state->wpcm.dmabuf.wait);
+		wake_up(&state->rpcm.dmabuf.wait);
+	}
+
+	spin_unlock_irqrestore(&unit->reg_lock, flags);
+	return 0;
+}
+
+/*
+ *  initialization routines
+ */
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+
+static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev)
+{
+	int v;
+	int mpuio = -1, oplio = -1;
+
+	switch (unit->iomidi) {
+	case 0x330:
+		mpuio = 0;
+		break;
+	case 0x300:
+		mpuio = 1;
+		break;
+	case 0x332:
+		mpuio = 2;
+		break;
+	case 0x334:
+		mpuio = 3;
+		break;
+	default: ;
+	}
+
+	switch (unit->iosynth) {
+	case 0x388:
+		oplio = 0;
+		break;
+	case 0x398:
+		oplio = 1;
+		break;
+	case 0x3a0:
+		oplio = 2;
+		break;
+	case 0x3a8:
+		oplio = 3;
+		break;
+	default: ;
+	}
+
+	if (mpuio >= 0 || oplio >= 0) {
+		/* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */
+		v = 0x001e;
+		pci_write_config_word(pcidev, PCIR_LEGCTRL, v);
+
+		switch (pcidev->device) {
+		case PCI_DEVICE_ID_YAMAHA_724:
+		case PCI_DEVICE_ID_YAMAHA_740:
+		case PCI_DEVICE_ID_YAMAHA_724F:
+		case PCI_DEVICE_ID_YAMAHA_740C:
+			v = 0x8800;
+			if (mpuio >= 0) { v |= mpuio<<4; }
+			if (oplio >= 0) { v |= oplio; }
+			pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+			break;
+
+		case PCI_DEVICE_ID_YAMAHA_744:
+		case PCI_DEVICE_ID_YAMAHA_754:
+			v = 0x8800;
+			pci_write_config_word(pcidev, PCIR_ELEGCTRL, v);
+			if (oplio >= 0) {
+				pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth);
+			}
+			if (mpuio >= 0) {
+				pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi);
+			}
+			break;
+
+		default:
+			printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n",
+			    pcidev->device);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */
+
+static void ymfpci_aclink_reset(struct pci_dev * pci)
+{
+	u8 cmd;
+
+	/*
+	 * In the 744, 754 only 0x01 exists, 0x02 is undefined.
+	 * It does not seem to hurt to trip both regardless of revision.
+	 */
+	pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd);
+	pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc);
+	pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03);
+	pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc);
+
+	pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0);
+	pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0);
+}
+
+static void ymfpci_enable_dsp(ymfpci_t *codec)
+{
+	ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001);
+}
+
+static void ymfpci_disable_dsp(ymfpci_t *codec)
+{
+	u32 val;
+	int timeout = 1000;
+
+	val = ymfpci_readl(codec, YDSXGR_CONFIG);
+	if (val)
+		ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000);
+	while (timeout-- > 0) {
+		val = ymfpci_readl(codec, YDSXGR_STATUS);
+		if ((val & 0x00000002) == 0)
+			break;
+	}
+}
+
+#include "ymfpci_image.h"
+
+static void ymfpci_download_image(ymfpci_t *codec)
+{
+	int i, ver_1e;
+	u16 ctrl;
+
+	ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
+	ymfpci_disable_dsp(codec);
+	ymfpci_writel(codec, YDSXGR_MODE, 0x00010000);
+	ymfpci_writel(codec, YDSXGR_MODE, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000);
+	ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000);
+	ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
+	ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+
+	/* setup DSP instruction code */
+	for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
+		ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]);
+
+	switch (codec->pci->device) {
+	case PCI_DEVICE_ID_YAMAHA_724F:
+	case PCI_DEVICE_ID_YAMAHA_740C:
+	case PCI_DEVICE_ID_YAMAHA_744:
+	case PCI_DEVICE_ID_YAMAHA_754:
+		ver_1e = 1;
+		break;
+	default:
+		ver_1e = 0;
+	}
+
+	if (ver_1e) {
+		/* setup control instruction code */
+		for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
+			ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]);
+	} else {
+		for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
+			ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]);
+	}
+
+	ymfpci_enable_dsp(codec);
+
+	/* 0.02s sounds not too bad, we may do schedule_timeout() later. */
+	mdelay(20); /* seems we need some delay after downloading image.. */
+}
+
+static int ymfpci_memalloc(ymfpci_t *codec)
+{
+	unsigned int playback_ctrl_size;
+	unsigned int bank_size_playback;
+	unsigned int bank_size_capture;
+	unsigned int bank_size_effect;
+	unsigned int size;
+	unsigned int off;
+	char *ptr;
+	dma_addr_t pba;
+	int voice, bank;
+
+	playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES;
+	bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2;
+	bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2;
+	bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2;
+	codec->work_size = YDSXG_DEFAULT_WORK_SIZE;
+
+	size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) +
+	    ((bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) +
+	    ((bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) +
+	    ((bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) +
+	    codec->work_size;
+
+	ptr = pci_alloc_consistent(codec->pci, size + 0xff, &pba);
+	if (ptr == NULL)
+		return -ENOMEM;
+	codec->dma_area_va = ptr;
+	codec->dma_area_ba = pba;
+	codec->dma_area_size = size + 0xff;
+
+	if ((off = ((uint) ptr) & 0xff) != 0) {
+		ptr += 0x100 - off;
+		pba += 0x100 - off;
+	}
+
+	/*
+	 * Hardware requires only ptr[playback_ctrl_size] zeroed,
+	 * but in our judgement it is a wrong kind of savings, so clear it all.
+	 */
+	memset(ptr, 0, size);
+
+	codec->ctrl_playback = (u32 *)ptr;
+	codec->ctrl_playback_ba = pba;
+	codec->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
+	ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+	pba += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+
+	off = 0;
+	for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
+		codec->voices[voice].number = voice;
+		codec->voices[voice].bank =
+		    (ymfpci_playback_bank_t *) (ptr + off);
+		codec->voices[voice].bank_ba = pba + off;
+		off += 2 * bank_size_playback;		/* 2 banks */
+	}
+	off = (off + 0xff) & ~0xff;
+	ptr += off;
+	pba += off;
+
+	off = 0;
+	codec->bank_base_capture = pba;
+	for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			codec->bank_capture[voice][bank] =
+			    (ymfpci_capture_bank_t *) (ptr + off);
+			off += bank_size_capture;
+		}
+	off = (off + 0xff) & ~0xff;
+	ptr += off;
+	pba += off;
+
+	off = 0;
+	codec->bank_base_effect = pba;
+	for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			codec->bank_effect[voice][bank] =
+			    (ymfpci_effect_bank_t *) (ptr + off);
+			off += bank_size_effect;
+		}
+	off = (off + 0xff) & ~0xff;
+	ptr += off;
+	pba += off;
+
+	codec->work_base = pba;
+
+	return 0;
+}
+
+static void ymfpci_memfree(ymfpci_t *codec)
+{
+	ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0);
+	ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0);
+	ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0);
+	ymfpci_writel(codec, YDSXGR_WORKBASE, 0);
+	ymfpci_writel(codec, YDSXGR_WORKSIZE, 0);
+	pci_free_consistent(codec->pci,
+	    codec->dma_area_size, codec->dma_area_va, codec->dma_area_ba);
+}
+
+static void ymf_memload(ymfpci_t *unit)
+{
+
+	ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, unit->ctrl_playback_ba);
+	ymfpci_writel(unit, YDSXGR_RECCTRLBASE, unit->bank_base_capture);
+	ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, unit->bank_base_effect);
+	ymfpci_writel(unit, YDSXGR_WORKBASE, unit->work_base);
+	ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2);
+
+	/* S/PDIF output initialization */
+	ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0);
+	ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS,
+		SND_PCM_AES0_CON_EMPHASIS_NONE |
+		(SND_PCM_AES1_CON_ORIGINAL << 8) |
+		(SND_PCM_AES1_CON_PCM_CODER << 8));
+
+	/* S/PDIF input initialization */
+	ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0);
+
+	/* move this volume setup to mixer */
+	ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0);
+	ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
+	ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
+}
+
+static int ymf_ac97_init(ymfpci_t *unit, int num_ac97)
+{
+	struct ac97_codec *codec;
+	u16 eid;
+
+	if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memset(codec, 0, sizeof(struct ac97_codec));
+
+	/* initialize some basic codec information, other fields will be filled
+	   in ac97_probe_codec */
+	codec->private_data = unit;
+	codec->id = num_ac97;
+
+	codec->codec_read = ymfpci_codec_read;
+	codec->codec_write = ymfpci_codec_write;
+
+	if (ac97_probe_codec(codec) == 0) {
+		printk(KERN_ERR "ymfpci: ac97_probe_codec failed\n");
+		goto out_kfree;
+	}
+
+	eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID);
+	if (eid==0xFFFFFF) {
+		printk(KERN_WARNING "ymfpci: no codec attached ?\n");
+		goto out_kfree;
+	}
+
+	unit->ac97_features = eid;
+
+	if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) {
+		printk(KERN_ERR "ymfpci: couldn't register mixer!\n");
+		goto out_kfree;
+	}
+
+	unit->ac97_codec[num_ac97] = codec;
+
+	return 0;
+ out_kfree:
+	kfree(codec);
+	return -ENODEV;
+}
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+# ifdef MODULE
+static int mpu_io     = 0;
+static int synth_io   = 0;
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(synth_io, "i");
+# else
+static int mpu_io     = 0x330;
+static int synth_io   = 0x388;
+# endif
+static int assigned;
+#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */
+
+static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent)
+{
+	u16 ctrl;
+	unsigned long base;
+	ymfpci_t *codec;
+
+	int err;
+
+	if ((err = pci_enable_device(pcidev)) != 0) {
+		printk(KERN_ERR "ymfpci: pci_enable_device failed\n");
+		return err;
+	}
+	base = pci_resource_start(pcidev, 0);
+
+	if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "ymfpci: no core\n");
+		return -ENOMEM;
+	}
+	memset(codec, 0, sizeof(*codec));
+
+	spin_lock_init(&codec->reg_lock);
+	spin_lock_init(&codec->voice_lock);
+	init_MUTEX(&codec->open_sem);
+	INIT_LIST_HEAD(&codec->states);
+	codec->pci = pcidev;
+
+	pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev);
+
+	if (request_mem_region(base, 0x8000, "ymfpci") == NULL) {
+		printk(KERN_ERR "ymfpci: unable to request mem region\n");
+		goto out_free;
+	}
+
+	if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) {
+		printk(KERN_ERR "ymfpci: unable to map registers\n");
+		goto out_release_region;
+	}
+
+	pci_set_master(pcidev);
+
+	printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
+	    (char *)ent->driver_data, base, pcidev->irq);
+
+	ymfpci_aclink_reset(pcidev);
+	if (ymfpci_codec_ready(codec, 0, 1) < 0)
+		goto out_unmap;
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+	if (assigned == 0) {
+		codec->iomidi = mpu_io;
+		codec->iosynth = synth_io;
+		if (ymfpci_setup_legacy(codec, pcidev) < 0)
+			goto out_unmap;
+		assigned = 1;
+	}
+#endif
+
+	ymfpci_download_image(codec);
+
+	if (ymfpci_memalloc(codec) < 0)
+		goto out_disable_dsp;
+	ymf_memload(codec);
+
+	if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
+		printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
+		    pcidev->irq);
+		goto out_memfree;
+	}
+
+	/* register /dev/dsp */
+	if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
+		printk(KERN_ERR "ymfpci: unable to register dsp\n");
+		goto out_free_irq;
+	}
+
+	/*
+	 * Poke just the primary for the moment.
+	 */
+	if ((err = ymf_ac97_init(codec, 0)) != 0)
+		goto out_unregister_sound_dsp;
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+	codec->opl3_data.name = "ymfpci";
+	codec->mpu_data.name  = "ymfpci";
+
+	codec->opl3_data.io_base = codec->iosynth;
+	codec->opl3_data.irq     = -1;
+
+	codec->mpu_data.io_base  = codec->iomidi;
+	codec->mpu_data.irq      = -1;	/* May be different from our PCI IRQ. */
+
+	if (codec->iomidi) {
+		if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) {
+			codec->iomidi = 0;	/* XXX kludge */
+		}
+	}
+#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */
+
+	/* put it into driver list */
+	list_add_tail(&codec->ymf_devs, &ymf_devs);
+	pci_set_drvdata(pcidev, codec);
+
+	return 0;
+
+ out_unregister_sound_dsp:
+	unregister_sound_dsp(codec->dev_audio);
+ out_free_irq:
+	free_irq(pcidev->irq, codec);
+ out_memfree:
+	ymfpci_memfree(codec);
+ out_disable_dsp:
+	ymfpci_disable_dsp(codec);
+	ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
+	ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+	ymfpci_writel(codec, YDSXGR_STATUS, ~0);
+ out_unmap:
+	iounmap(codec->reg_area_virt);
+ out_release_region:
+	release_mem_region(pci_resource_start(pcidev, 0), 0x8000);
+ out_free:
+	kfree(codec);
+	return -ENODEV;
+}
+
+static void __devexit ymf_remove_one(struct pci_dev *pcidev)
+{
+	__u16 ctrl;
+	ymfpci_t *codec = pci_get_drvdata(pcidev);
+
+	/* remove from list of devices */
+	list_del(&codec->ymf_devs);
+
+	unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer);
+	kfree(codec->ac97_codec[0]);
+	unregister_sound_dsp(codec->dev_audio);
+	free_irq(pcidev->irq, codec);
+	ymfpci_memfree(codec);
+	ymfpci_writel(codec, YDSXGR_STATUS, ~0);
+	ymfpci_disable_dsp(codec);
+	ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
+	ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+	iounmap(codec->reg_area_virt);
+	release_mem_region(pci_resource_start(pcidev, 0), 0x8000);
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+	if (codec->iomidi) {
+		unload_uart401(&codec->mpu_data);
+	}
+#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */
+	kfree(codec);
+}
+
+MODULE_AUTHOR("Jaroslav Kysela");
+MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio");
+MODULE_LICENSE("GPL");
+
+static struct pci_driver ymfpci_driver = {
+	name:		"ymfpci",
+	id_table:	ymf_id_tbl,
+	probe:		ymf_probe_one,
+	remove:         __devexit_p(ymf_remove_one),
+	suspend:	ymf_suspend,
+	resume:		ymf_resume
+};
+
+static int __init ymf_init_module(void)
+{
+	return pci_module_init(&ymfpci_driver);
+}
+
+static void __exit ymf_cleanup_module (void)
+{
+	pci_unregister_driver(&ymfpci_driver);
+}
+
+module_init(ymf_init_module);
+module_exit(ymf_cleanup_module);
diff -Nru linux/sound/oss/ymfpci.h linux-2.4.19-pre5-mjc/sound/oss/ymfpci.h
--- linux/sound/oss/ymfpci.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ymfpci.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,359 @@
+#ifndef __YMFPCI_H
+#define __YMFPCI_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for Yahama YMF724/740/744/754 chips
+ *
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/config.h>
+
+/*
+ *  Direct registers
+ */
+
+/* #define YMFREG(codec, reg)		(codec->port + YDSXGR_##reg) */
+
+#define	YDSXGR_INTFLAG			0x0004
+#define	YDSXGR_ACTIVITY			0x0006
+#define	YDSXGR_GLOBALCTRL		0x0008
+#define	YDSXGR_ZVCTRL			0x000A
+#define	YDSXGR_TIMERCTRL		0x0010
+#define	YDSXGR_TIMERCTRL_TEN		 0x0001
+#define	YDSXGR_TIMERCTRL_TIEN		 0x0002
+#define	YDSXGR_TIMERCOUNT		0x0012
+#define	YDSXGR_SPDIFOUTCTRL		0x0018
+#define	YDSXGR_SPDIFOUTSTATUS		0x001C
+#define	YDSXGR_EEPROMCTRL		0x0020
+#define	YDSXGR_SPDIFINCTRL		0x0034
+#define	YDSXGR_SPDIFINSTATUS		0x0038
+#define	YDSXGR_DSPPROGRAMDL		0x0048
+#define	YDSXGR_DLCNTRL			0x004C
+#define	YDSXGR_GPIOININTFLAG		0x0050
+#define	YDSXGR_GPIOININTENABLE		0x0052
+#define	YDSXGR_GPIOINSTATUS		0x0054
+#define	YDSXGR_GPIOOUTCTRL		0x0056
+#define	YDSXGR_GPIOFUNCENABLE		0x0058
+#define	YDSXGR_GPIOTYPECONFIG		0x005A
+#define	YDSXGR_AC97CMDDATA		0x0060
+#define	YDSXGR_AC97CMDADR		0x0062
+#define	YDSXGR_PRISTATUSDATA		0x0064
+#define	YDSXGR_PRISTATUSADR		0x0066
+#define	YDSXGR_SECSTATUSDATA		0x0068
+#define	YDSXGR_SECSTATUSADR		0x006A
+#define	YDSXGR_SECCONFIG		0x0070
+#define	YDSXGR_LEGACYOUTVOL		0x0080
+#define	YDSXGR_LEGACYOUTVOLL		0x0080
+#define	YDSXGR_LEGACYOUTVOLR		0x0082
+#define	YDSXGR_NATIVEDACOUTVOL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLR		0x0086
+#define	YDSXGR_SPDIFOUTVOL		0x0088
+#define	YDSXGR_SPDIFOUTVOLL		0x0088
+#define	YDSXGR_SPDIFOUTVOLR		0x008A
+#define	YDSXGR_AC3OUTVOL		0x008C
+#define	YDSXGR_AC3OUTVOLL		0x008C
+#define	YDSXGR_AC3OUTVOLR		0x008E
+#define	YDSXGR_PRIADCOUTVOL		0x0090
+#define	YDSXGR_PRIADCOUTVOLL		0x0090
+#define	YDSXGR_PRIADCOUTVOLR		0x0092
+#define	YDSXGR_LEGACYLOOPVOL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLR		0x0096
+#define	YDSXGR_NATIVEDACLOOPVOL		0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLL	0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLR	0x009A
+#define	YDSXGR_SPDIFLOOPVOL		0x009C
+#define	YDSXGR_SPDIFLOOPVOLL		0x009E
+#define	YDSXGR_SPDIFLOOPVOLR		0x009E
+#define	YDSXGR_AC3LOOPVOL		0x00A0
+#define	YDSXGR_AC3LOOPVOLL		0x00A0
+#define	YDSXGR_AC3LOOPVOLR		0x00A2
+#define	YDSXGR_PRIADCLOOPVOL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLR		0x00A6
+#define	YDSXGR_NATIVEADCINVOL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLR		0x00AA
+#define	YDSXGR_NATIVEDACINVOL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLR		0x00AE
+#define	YDSXGR_BUF441OUTVOL		0x00B0
+#define	YDSXGR_BUF441OUTVOLL		0x00B0
+#define	YDSXGR_BUF441OUTVOLR		0x00B2
+#define	YDSXGR_BUF441LOOPVOL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLR		0x00B6
+#define	YDSXGR_SPDIFOUTVOL2		0x00B8
+#define	YDSXGR_SPDIFOUTVOL2L		0x00B8
+#define	YDSXGR_SPDIFOUTVOL2R		0x00BA
+#define	YDSXGR_SPDIFLOOPVOL2		0x00BC
+#define	YDSXGR_SPDIFLOOPVOL2L		0x00BC
+#define	YDSXGR_SPDIFLOOPVOL2R		0x00BE
+#define	YDSXGR_ADCSLOTSR		0x00C0
+#define	YDSXGR_RECSLOTSR		0x00C4
+#define	YDSXGR_ADCFORMAT		0x00C8
+#define	YDSXGR_RECFORMAT		0x00CC
+#define	YDSXGR_P44SLOTSR		0x00D0
+#define	YDSXGR_STATUS			0x0100
+#define	YDSXGR_CTRLSELECT		0x0104
+#define	YDSXGR_MODE			0x0108
+#define	YDSXGR_SAMPLECOUNT		0x010C
+#define	YDSXGR_NUMOFSAMPLES		0x0110
+#define	YDSXGR_CONFIG			0x0114
+#define	YDSXGR_PLAYCTRLSIZE		0x0140
+#define	YDSXGR_RECCTRLSIZE		0x0144
+#define	YDSXGR_EFFCTRLSIZE		0x0148
+#define	YDSXGR_WORKSIZE			0x014C
+#define	YDSXGR_MAPOFREC			0x0150
+#define	YDSXGR_MAPOFEFFECT		0x0154
+#define	YDSXGR_PLAYCTRLBASE		0x0158
+#define	YDSXGR_RECCTRLBASE		0x015C
+#define	YDSXGR_EFFCTRLBASE		0x0160
+#define	YDSXGR_WORKBASE			0x0164
+#define	YDSXGR_DSPINSTRAM		0x1000
+#define	YDSXGR_CTRLINSTRAM		0x4000
+
+#define YDSXG_AC97READCMD		0x8000
+#define YDSXG_AC97WRITECMD		0x0000
+
+#define PCIR_LEGCTRL			0x40
+#define PCIR_ELEGCTRL			0x42
+#define PCIR_DSXGCTRL			0x48
+#define PCIR_DSXPWRCTRL1		0x4a
+#define PCIR_DSXPWRCTRL2		0x4e
+#define PCIR_OPLADR			0x60
+#define PCIR_SBADR			0x62
+#define PCIR_MPUADR			0x64
+
+#define YDSXG_DSPLENGTH			0x0080
+#define YDSXG_CTRLLENGTH		0x3000
+
+#define YDSXG_DEFAULT_WORK_SIZE		0x0400
+
+#define YDSXG_PLAYBACK_VOICES		64
+#define YDSXG_CAPTURE_VOICES		2
+#define YDSXG_EFFECT_VOICES		5
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define NR_AC97		2
+
+#define YMF_SAMPF			256	/* Samples per frame @48000 */
+
+/*
+ * The slot/voice control bank (2 of these per voice)
+ */
+
+typedef struct stru_ymfpci_playback_bank {
+	u32 format;
+	u32 loop_default;
+	u32 base;			/* 32-bit address */
+	u32 loop_start;			/* 32-bit offset */
+	u32 loop_end;			/* 32-bit offset */
+	u32 loop_frac;			/* 8-bit fraction - loop_start */
+	u32 delta_end;			/* pitch delta end */
+	u32 lpfK_end;
+	u32 eg_gain_end;
+	u32 left_gain_end;
+	u32 right_gain_end;
+	u32 eff1_gain_end;
+	u32 eff2_gain_end;
+	u32 eff3_gain_end;
+	u32 lpfQ;
+	u32 status;		/* P3: Always 0 for some reason. */
+	u32 num_of_frames;
+	u32 loop_count;
+	u32 start;		/* P3: J. reads this to know where chip is. */
+	u32 start_frac;
+	u32 delta;
+	u32 lpfK;
+	u32 eg_gain;
+	u32 left_gain;
+	u32 right_gain;
+	u32 eff1_gain;
+	u32 eff2_gain;
+	u32 eff3_gain;
+	u32 lpfD1;
+	u32 lpfD2;
+} ymfpci_playback_bank_t;
+
+typedef struct stru_ymfpci_capture_bank {
+	u32 base;			/* 32-bit address (aligned at 4) */
+	u32 loop_end;			/* size in BYTES (aligned at 4) */
+	u32 start;			/* 32-bit offset */
+	u32 num_of_loops;		/* counter */
+} ymfpci_capture_bank_t;
+
+typedef struct stru_ymfpci_effect_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 temp;
+} ymfpci_effect_bank_t;
+
+typedef struct ymf_voice ymfpci_voice_t;
+/*
+ * Throughout the code Yaroslav names YMF unit pointer "codec"
+ * even though it does not correspond to any codec. Must be historic.
+ * We replace it with "unit" over time.
+ * AC97 parts use "codec" to denote a codec, naturally.
+ */
+typedef struct ymf_unit ymfpci_t;
+
+typedef enum {
+	YMFPCI_PCM,
+	YMFPCI_SYNTH,
+	YMFPCI_MIDI
+} ymfpci_voice_type_t;
+
+struct ymf_voice {
+	// ymfpci_t *codec;
+	int number;
+	char use, pcm, synth, midi;	// bool
+	ymfpci_playback_bank_t *bank;
+	struct ymf_pcm *ypcm;
+	dma_addr_t bank_ba;
+};
+
+struct ymf_capture {
+	// struct ymf_unit *unit;
+	int use;
+	ymfpci_capture_bank_t *bank;
+	struct ymf_pcm *ypcm;
+};
+
+struct ymf_unit {
+	u8 rev;				/* PCI revision */
+	void *reg_area_virt;
+	void *dma_area_va;
+	dma_addr_t dma_area_ba;
+	unsigned int dma_area_size;
+
+	dma_addr_t bank_base_capture;
+	dma_addr_t bank_base_effect;
+	dma_addr_t work_base;
+	unsigned int work_size;
+
+	u32 *ctrl_playback;
+	dma_addr_t ctrl_playback_ba;
+	ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
+	ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2];
+	ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2];
+
+	int start_count;
+	int suspended;
+
+	u32 active_bank;
+	struct ymf_voice voices[YDSXG_PLAYBACK_VOICES];
+	struct ymf_capture capture[YDSXG_CAPTURE_VOICES];
+
+	struct ac97_codec *ac97_codec[NR_AC97];
+	u16 ac97_features;
+
+	struct pci_dev *pci;
+
+#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY
+	/* legacy hardware resources */
+	unsigned int iosynth, iomidi;
+	struct address_info opl3_data, mpu_data;
+#endif
+
+	spinlock_t reg_lock;
+	spinlock_t voice_lock;
+
+	/* soundcore stuff */
+	int dev_audio;
+	struct semaphore open_sem;
+
+	struct list_head ymf_devs;
+	struct list_head states;	/* List of states for this unit */
+};
+
+struct ymf_dmabuf {
+	dma_addr_t dma_addr;
+	void *rawbuf;
+	unsigned buforder;
+
+	/* OSS buffer management stuff */
+	unsigned numfrag;
+	unsigned fragshift;
+
+	/* our buffer acts like a circular ring */
+	unsigned hwptr;		/* where dma last started */
+	unsigned swptr;		/* where driver last clear/filled */
+	int count;		/* fill count */
+	unsigned total_bytes;	/* total bytes dmaed by hardware */
+
+	wait_queue_head_t wait;	/* put process on wait queue when no more space in buffer */
+
+	/* redundant, but makes calculations easier */
+	unsigned fragsize;
+	unsigned dmasize;	/* Total rawbuf[] size */
+
+	/* OSS stuff */
+	unsigned mapped:1;
+	unsigned ready:1;
+	unsigned ossfragshift;
+	int ossmaxfrags;
+	unsigned subdivision;
+};
+
+struct ymf_pcm_format {
+	int format;			/* OSS format */
+	int rate;			/* rate in Hz */
+	int voices;			/* number of voices */
+	int shift;			/* redundant, computed from the above */
+};
+
+typedef enum {
+	PLAYBACK_VOICE,
+	CAPTURE_REC,
+	CAPTURE_AC97,
+	EFFECT_DRY_LEFT,
+	EFFECT_DRY_RIGHT,
+	EFFECT_EFF1,
+	EFFECT_EFF2,
+	EFFECT_EFF3
+} ymfpci_pcm_type_t;
+
+/* This is variant record, but we hate unions. Little waste on pointers []. */
+struct ymf_pcm {
+	ymfpci_pcm_type_t type;
+	struct ymf_state *state;
+
+	ymfpci_voice_t *voices[2];
+	int capture_bank_number;
+
+	struct ymf_dmabuf dmabuf;
+	int running;
+	int spdif;
+};
+
+/*
+ * "Software" or virtual channel, an instance of opened /dev/dsp.
+ * It may have two physical channels (pcms) for duplex operations.
+ */
+
+struct ymf_state {
+	struct list_head chain;
+	struct ymf_unit *unit;			/* backpointer */
+	struct ymf_pcm rpcm, wpcm;
+	struct ymf_pcm_format format;
+};
+
+#endif				/* __YMFPCI_H */
diff -Nru linux/sound/oss/ymfpci_image.h linux-2.4.19-pre5-mjc/sound/oss/ymfpci_image.h
--- linux/sound/oss/ymfpci_image.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/ymfpci_image.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1565 @@
+#ifndef _HWMCODE_
+#define _HWMCODE_
+
+static u32 DspInst[YDSXG_DSPLENGTH / 4] = {
+	0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+	0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+	0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+	0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x001A82, 0x032D0D, 0x000810, 0x10043A,
+	0x02D38D, 0x000810, 0x18043A, 0x00010D,
+	0x020015, 0x0000FD, 0x000020, 0x038860,
+	0x039060, 0x038060, 0x038040, 0x038040,
+	0x038040, 0x018040, 0x000A7D, 0x038040,
+	0x038040, 0x018040, 0x200402, 0x000882,
+	0x08001A, 0x000904, 0x015986, 0x000007,
+	0x260007, 0x000007, 0x000007, 0x018A06,
+	0x000007, 0x030C8D, 0x000810, 0x18043A,
+	0x260007, 0x00087D, 0x018042, 0x00160A,
+	0x04A206, 0x000007, 0x00218D, 0x000810,
+	0x08043A, 0x21C206, 0x000007, 0x0007FD,
+	0x018042, 0x08000A, 0x000904, 0x029386,
+	0x000195, 0x090D04, 0x000007, 0x000820,
+	0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
+	0x032206, 0x018040, 0x000A7D, 0x038042,
+	0x13804A, 0x18000A, 0x001820, 0x059060,
+	0x058860, 0x018040, 0x0000FD, 0x018042,
+	0x70000A, 0x000115, 0x071144, 0x032386,
+	0x030000, 0x007020, 0x034A06, 0x018040,
+	0x00348D, 0x000810, 0x08043A, 0x21EA06,
+	0x000007, 0x02D38D, 0x000810, 0x18043A,
+	0x018206, 0x000007, 0x240007, 0x000F8D,
+	0x000810, 0x00163A, 0x002402, 0x005C02,
+	0x0028FD, 0x000020, 0x018040, 0x08000D,
+	0x000815, 0x510984, 0x000007, 0x00004D,
+	0x000E5D, 0x000E02, 0x00418D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00008D,
+	0x000924, 0x000F02, 0x00458D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x018386, 0x000007, 0x01AA06, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x218086, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x055A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x034986, 0x000007, 0x002104, 0x034986,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x06C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00688D, 0x000810, 0x08043A, 0x288A06,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x060206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x215886, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x212086, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x07DA86, 0x00057D, 0x002820,
+	0x03B060, 0x07F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x07FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x008D8D, 0x000810,
+	0x08043A, 0x288A06, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x095186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x007FBD, 0x383DC4,
+	0x000007, 0x001A7D, 0x001375, 0x018042,
+	0x09004A, 0x10000A, 0x0B8D04, 0x139504,
+	0x000007, 0x000820, 0x019060, 0x001104,
+	0x212086, 0x010040, 0x0017FD, 0x018042,
+	0x08000A, 0x000904, 0x212286, 0x000007,
+	0x00197D, 0x038042, 0x09804A, 0x10000A,
+	0x000924, 0x001664, 0x0011FD, 0x038042,
+	0x2B804A, 0x19804A, 0x00008D, 0x218944,
+	0x000007, 0x002244, 0x0AE186, 0x000007,
+	0x001A64, 0x002A24, 0x00197D, 0x080102,
+	0x100122, 0x000820, 0x039060, 0x018040,
+	0x003DFD, 0x00008D, 0x000820, 0x018040,
+	0x001375, 0x001A7D, 0x010042, 0x09804A,
+	0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
+	0x309144, 0x000007, 0x00060D, 0x000A15,
+	0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
+	0x000464, 0x01B3E4, 0x0232E4, 0x000464,
+	0x000464, 0x000464, 0x000464, 0x00040D,
+	0x08B1C4, 0x000007, 0x000820, 0x000BF5,
+	0x030040, 0x00197D, 0x038042, 0x09804A,
+	0x000A24, 0x08000A, 0x080E64, 0x000007,
+	0x100122, 0x000820, 0x031060, 0x010040,
+	0x0064AC, 0x00027D, 0x000020, 0x018040,
+	0x00107D, 0x018042, 0x0011FD, 0x3B804A,
+	0x09804A, 0x20000A, 0x000095, 0x1A1144,
+	0x00A144, 0x0D2086, 0x00040D, 0x00B984,
+	0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
+	0x09804A, 0x28000A, 0x000095, 0x010924,
+	0x002A64, 0x0D1186, 0x000007, 0x002904,
+	0x0D2286, 0x000007, 0x0D2A06, 0x080002,
+	0x00008D, 0x00387D, 0x000820, 0x018040,
+	0x00127D, 0x018042, 0x10000A, 0x003904,
+	0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
+	0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
+	0x000015, 0x00082D, 0x02C78D, 0x000820,
+	0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
+	0x0E7186, 0x400025, 0x00008D, 0x110944,
+	0x000007, 0x00018D, 0x109504, 0x000007,
+	0x009164, 0x000424, 0x000424, 0x000424,
+	0x100102, 0x280002, 0x02C68D, 0x000820,
+	0x0EC206, 0x00018D, 0x00042D, 0x00008D,
+	0x109504, 0x000007, 0x00020D, 0x109184,
+	0x000007, 0x02C70D, 0x000820, 0x00008D,
+	0x0038FD, 0x018040, 0x003BFD, 0x001020,
+	0x03A860, 0x000815, 0x313184, 0x212184,
+	0x000007, 0x03B060, 0x03A060, 0x018040,
+	0x0022FD, 0x000095, 0x010924, 0x000424,
+	0x000424, 0x001264, 0x100102, 0x000820,
+	0x039060, 0x018040, 0x001924, 0x00FB8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x000424,
+	0x000424, 0x00117D, 0x018042, 0x08000A,
+	0x000A24, 0x280502, 0x280C02, 0x09800D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x0022FD, 0x018042, 0x08000A, 0x000095,
+	0x280DC4, 0x011924, 0x00197D, 0x018042,
+	0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
+	0x113144, 0x0A8D04, 0x000007, 0x080A44,
+	0x129504, 0x000007, 0x0023FD, 0x001020,
+	0x038040, 0x101244, 0x000007, 0x000820,
+	0x039060, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x10FA86, 0x000007,
+	0x003BFD, 0x000100, 0x000A10, 0x0B807A,
+	0x13804A, 0x090984, 0x000007, 0x000095,
+	0x013D04, 0x118086, 0x10000A, 0x100002,
+	0x090984, 0x000007, 0x038042, 0x11804A,
+	0x090D04, 0x000007, 0x10000A, 0x090D84,
+	0x000007, 0x00257D, 0x000820, 0x018040,
+	0x00010D, 0x000810, 0x28143A, 0x00127D,
+	0x018042, 0x20000A, 0x00197D, 0x018042,
+	0x00117D, 0x31804A, 0x10000A, 0x003124,
+	0x01280D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x300102, 0x003124, 0x000424, 0x000424,
+	0x001224, 0x280502, 0x001A4C, 0x130186,
+	0x700002, 0x00002D, 0x030000, 0x00387D,
+	0x018042, 0x10000A, 0x132A06, 0x002124,
+	0x0000AD, 0x100002, 0x00010D, 0x000924,
+	0x006B24, 0x01368D, 0x00397D, 0x000820,
+	0x058040, 0x038042, 0x09844A, 0x000606,
+	0x08040A, 0x003264, 0x00008D, 0x000A24,
+	0x001020, 0x00227D, 0x018040, 0x013C0D,
+	0x000810, 0x08043A, 0x29D206, 0x000007,
+	0x002820, 0x00207D, 0x018040, 0x00117D,
+	0x038042, 0x13804A, 0x33800A, 0x00387D,
+	0x018042, 0x08000A, 0x000904, 0x163A86,
+	0x000007, 0x00008D, 0x030964, 0x01478D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x380102,
+	0x000424, 0x000424, 0x001224, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x14A286,
+	0x000007, 0x280502, 0x001A4C, 0x163986,
+	0x000007, 0x032164, 0x00632C, 0x003DFD,
+	0x018042, 0x08000A, 0x000095, 0x090904,
+	0x000007, 0x000820, 0x001A4C, 0x156186,
+	0x018040, 0x030000, 0x157A06, 0x002124,
+	0x00010D, 0x000924, 0x006B24, 0x015B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x003A64,
+	0x000095, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x01628D, 0x000810, 0x08043A, 0x29D206,
+	0x000007, 0x14D206, 0x000007, 0x007020,
+	0x08010A, 0x10012A, 0x0020FD, 0x038860,
+	0x039060, 0x018040, 0x00227D, 0x018042,
+	0x003DFD, 0x08000A, 0x31844A, 0x000904,
+	0x16D886, 0x18008B, 0x00008D, 0x189904,
+	0x00312C, 0x17AA06, 0x000007, 0x00324C,
+	0x173386, 0x000007, 0x001904, 0x173086,
+	0x000007, 0x000095, 0x199144, 0x00222C,
+	0x003124, 0x00636C, 0x000E3D, 0x001375,
+	0x000BFD, 0x010042, 0x09804A, 0x10000A,
+	0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
+	0x000007, 0x00008D, 0x189904, 0x00226C,
+	0x00322C, 0x30050A, 0x301DAB, 0x002083,
+	0x0018FD, 0x018042, 0x08000A, 0x018924,
+	0x300502, 0x001083, 0x001875, 0x010042,
+	0x10000A, 0x00008D, 0x010924, 0x001375,
+	0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
+	0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
+	0x305C8B, 0x006083, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x187A86, 0x000007,
+	0x001E2D, 0x0005FD, 0x018042, 0x08000A,
+	0x028924, 0x280502, 0x00060D, 0x000810,
+	0x280C3A, 0x00008D, 0x000810, 0x28143A,
+	0x0A808D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001275, 0x030042, 0x21004A,
+	0x00008D, 0x1A0944, 0x000007, 0x01980D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0001F5, 0x030042, 0x0D004A, 0x10000A,
+	0x089144, 0x000007, 0x000820, 0x010040,
+	0x0025F5, 0x0A3144, 0x000007, 0x000820,
+	0x032860, 0x030040, 0x00217D, 0x038042,
+	0x0B804A, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00008D, 0x000124, 0x00012C,
+	0x000E64, 0x001A64, 0x00636C, 0x08010A,
+	0x10012A, 0x000820, 0x031060, 0x030040,
+	0x0020FD, 0x018042, 0x08000A, 0x00227D,
+	0x018042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00197D, 0x018042, 0x08000A,
+	0x0022FD, 0x038042, 0x10000A, 0x000820,
+	0x031060, 0x030040, 0x090D04, 0x000007,
+	0x000820, 0x030040, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x038042, 0x13804A, 0x19804A, 0x110D04,
+	0x198D04, 0x000007, 0x08000A, 0x001020,
+	0x031860, 0x030860, 0x030040, 0x00008D,
+	0x0B0944, 0x000007, 0x000820, 0x010040,
+	0x0005F5, 0x030042, 0x08000A, 0x000820,
+	0x010040, 0x0000F5, 0x010042, 0x08000A,
+	0x000904, 0x1C6086, 0x001E75, 0x030042,
+	0x01044A, 0x000C0A, 0x1C7206, 0x000007,
+	0x000402, 0x000C02, 0x00177D, 0x001AF5,
+	0x018042, 0x03144A, 0x031C4A, 0x03244A,
+	0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
+	0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
+	0x030042, 0x0B004A, 0x1B804A, 0x13804A,
+	0x20000A, 0x089144, 0x19A144, 0x0389E4,
+	0x0399EC, 0x005502, 0x005D0A, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x006502, 0x006D0A, 0x030042, 0x0B004A,
+	0x19004A, 0x2B804A, 0x13804A, 0x21804A,
+	0x30000A, 0x089144, 0x19A144, 0x2AB144,
+	0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
+	0x03A9E4, 0x000702, 0x00107D, 0x000415,
+	0x018042, 0x08000A, 0x0109E4, 0x000F02,
+	0x002AF5, 0x0019FD, 0x010042, 0x09804A,
+	0x10000A, 0x000934, 0x001674, 0x0029F5,
+	0x010042, 0x10000A, 0x00917C, 0x002075,
+	0x010042, 0x08000A, 0x000904, 0x1ED286,
+	0x0026F5, 0x0027F5, 0x030042, 0x09004A,
+	0x10000A, 0x000A3C, 0x00167C, 0x001A75,
+	0x000BFD, 0x010042, 0x51804A, 0x48000A,
+	0x160007, 0x001075, 0x010042, 0x282C0A,
+	0x281D12, 0x282512, 0x001F32, 0x1E0007,
+	0x0E0007, 0x001975, 0x010042, 0x002DF5,
+	0x0D004A, 0x10000A, 0x009144, 0x1FB286,
+	0x010042, 0x28340A, 0x000E5D, 0x00008D,
+	0x000375, 0x000820, 0x010040, 0x05D2F4,
+	0x54D104, 0x00735C, 0x205386, 0x000007,
+	0x0C0007, 0x080007, 0x0A0007, 0x02040D,
+	0x000810, 0x08043A, 0x332206, 0x000007,
+	0x205A06, 0x000007, 0x080007, 0x002275,
+	0x010042, 0x20000A, 0x002104, 0x212086,
+	0x001E2D, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x209286, 0x000007, 0x002010,
+	0x30043A, 0x00057D, 0x0180C3, 0x08000A,
+	0x028924, 0x280502, 0x280C02, 0x0A810D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x0004FD, 0x018042, 0x70000A, 0x030000,
+	0x007020, 0x06FA06, 0x018040, 0x02180D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x218A86, 0x000007, 0x01F206, 0x000007,
+	0x000875, 0x0009FD, 0x00010D, 0x220A06,
+	0x000295, 0x000B75, 0x00097D, 0x00000D,
+	0x000515, 0x010042, 0x18000A, 0x001904,
+	0x287886, 0x0006F5, 0x001020, 0x010040,
+	0x0004F5, 0x000820, 0x010040, 0x000775,
+	0x010042, 0x09804A, 0x10000A, 0x001124,
+	0x000904, 0x22BA86, 0x000815, 0x080102,
+	0x101204, 0x22DA06, 0x000575, 0x081204,
+	0x000007, 0x100102, 0x000575, 0x000425,
+	0x021124, 0x100102, 0x000820, 0x031060,
+	0x010040, 0x001924, 0x287886, 0x00008D,
+	0x000464, 0x009D04, 0x278886, 0x180102,
+	0x000575, 0x010042, 0x28040A, 0x00018D,
+	0x000924, 0x280D02, 0x00000D, 0x000924,
+	0x281502, 0x10000D, 0x000820, 0x0002F5,
+	0x010040, 0x200007, 0x001175, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x23C286,
+	0x000007, 0x000100, 0x080B20, 0x130B60,
+	0x1B0B60, 0x030A60, 0x010040, 0x050042,
+	0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
+	0x0006F5, 0x010042, 0x28140A, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x24CA86, 0x004015, 0x000095, 0x010D04,
+	0x24B886, 0x100022, 0x10002A, 0x24E206,
+	0x000007, 0x333104, 0x2AA904, 0x000007,
+	0x032124, 0x280502, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x00292C, 0x00636C,
+	0x25F386, 0x000007, 0x02B164, 0x000464,
+	0x000464, 0x00008D, 0x000A64, 0x280D02,
+	0x10008D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x00008D, 0x38B904, 0x000007,
+	0x03296C, 0x30010A, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x25BA86, 0x000007,
+	0x02312C, 0x28050A, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x267A86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x26C086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x26CA86, 0x000007, 0x003124, 0x300502,
+	0x003924, 0x300583, 0x000883, 0x0005F5,
+	0x010042, 0x28040A, 0x00008D, 0x008124,
+	0x280D02, 0x00008D, 0x008124, 0x281502,
+	0x10018D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001025, 0x000575, 0x030042,
+	0x09004A, 0x10000A, 0x0A0904, 0x121104,
+	0x000007, 0x001020, 0x050860, 0x050040,
+	0x0006FD, 0x018042, 0x09004A, 0x10000A,
+	0x0000A5, 0x0A0904, 0x121104, 0x000007,
+	0x000820, 0x019060, 0x010040, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x284286,
+	0x000007, 0x230A06, 0x000007, 0x000606,
+	0x000007, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x289286, 0x000007, 0x000100,
+	0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
+	0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
+	0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
+	0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
+	0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
+	0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
+	0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
+	0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
+	0x000606, 0x018040, 0x00008D, 0x000A64,
+	0x280D02, 0x000A24, 0x00027D, 0x018042,
+	0x10000A, 0x001224, 0x0003FD, 0x018042,
+	0x08000A, 0x000904, 0x2A8286, 0x000007,
+	0x00018D, 0x000A24, 0x000464, 0x000464,
+	0x080102, 0x000924, 0x000424, 0x000424,
+	0x100102, 0x02000D, 0x009144, 0x2AD986,
+	0x000007, 0x0001FD, 0x018042, 0x08000A,
+	0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x00027D, 0x001020, 0x000606, 0x018040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x2B2A86, 0x000007, 0x00037D, 0x018042,
+	0x08000A, 0x000904, 0x2B5A86, 0x000007,
+	0x000075, 0x002E7D, 0x010042, 0x0B804A,
+	0x000020, 0x000904, 0x000686, 0x010040,
+	0x31844A, 0x30048B, 0x000883, 0x00008D,
+	0x000810, 0x28143A, 0x00008D, 0x000810,
+	0x280C3A, 0x000675, 0x010042, 0x08000A,
+	0x003815, 0x010924, 0x280502, 0x0B000D,
+	0x000820, 0x0002F5, 0x010040, 0x000606,
+	0x220007, 0x000464, 0x000464, 0x000606,
+	0x000007, 0x000134, 0x007F8D, 0x00093C,
+	0x281D12, 0x282512, 0x001F32, 0x0E0007,
+	0x00010D, 0x00037D, 0x000820, 0x018040,
+	0x05D2F4, 0x000007, 0x080007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2D0286,
+	0x000007, 0x000606, 0x000007, 0x000007,
+	0x000012, 0x100007, 0x320007, 0x600007,
+	0x100080, 0x48001A, 0x004904, 0x2D6186,
+	0x000007, 0x001210, 0x58003A, 0x000145,
+	0x5C5D04, 0x000007, 0x000080, 0x48001A,
+	0x004904, 0x2DB186, 0x000007, 0x001210,
+	0x50003A, 0x005904, 0x2E0886, 0x000045,
+	0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
+	0x004224, 0x500102, 0x200502, 0x000082,
+	0x40001A, 0x004104, 0x2E3986, 0x000007,
+	0x003865, 0x40001A, 0x004020, 0x00104D,
+	0x04C184, 0x301B86, 0x000040, 0x040007,
+	0x000165, 0x000145, 0x004020, 0x000040,
+	0x000765, 0x080080, 0x40001A, 0x004104,
+	0x2EC986, 0x000007, 0x001210, 0x40003A,
+	0x004104, 0x2F2286, 0x00004D, 0x0000CD,
+	0x004810, 0x20043A, 0x000882, 0x40001A,
+	0x004104, 0x2F3186, 0x000007, 0x004820,
+	0x005904, 0x300886, 0x000040, 0x0007E5,
+	0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
+	0x4216E0, 0x021260, 0x000040, 0x000032,
+	0x400075, 0x00007D, 0x07D574, 0x200512,
+	0x000082, 0x40001A, 0x004104, 0x2FE186,
+	0x000007, 0x037206, 0x640007, 0x060007,
+	0x0000E5, 0x000020, 0x000040, 0x000A65,
+	0x000020, 0x020040, 0x020040, 0x000040,
+	0x000165, 0x000042, 0x70000A, 0x007104,
+	0x30A286, 0x000007, 0x018206, 0x640007,
+	0x050000, 0x007020, 0x000040, 0x037206,
+	0x640007, 0x000007, 0x00306D, 0x028860,
+	0x029060, 0x08000A, 0x028860, 0x008040,
+	0x100012, 0x00100D, 0x009184, 0x314186,
+	0x000E0D, 0x009184, 0x325186, 0x000007,
+	0x300007, 0x001020, 0x003B6D, 0x008040,
+	0x000080, 0x08001A, 0x000904, 0x316186,
+	0x000007, 0x001220, 0x000DED, 0x008040,
+	0x008042, 0x10000A, 0x40000D, 0x109544,
+	0x000007, 0x001020, 0x000DED, 0x008040,
+	0x008042, 0x20040A, 0x000082, 0x08001A,
+	0x000904, 0x31F186, 0x000007, 0x003B6D,
+	0x008042, 0x08000A, 0x000E15, 0x010984,
+	0x329B86, 0x600007, 0x08001A, 0x000C15,
+	0x010984, 0x328386, 0x000020, 0x1A0007,
+	0x0002ED, 0x008040, 0x620007, 0x00306D,
+	0x028042, 0x0A804A, 0x000820, 0x0A804A,
+	0x000606, 0x10804A, 0x000007, 0x282512,
+	0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
+	0x000786, 0x000007, 0x0C0007, 0x0A0007,
+	0x1C0007, 0x003465, 0x020040, 0x004820,
+	0x025060, 0x40000A, 0x024060, 0x000040,
+	0x454944, 0x000007, 0x004020, 0x003AE5,
+	0x000040, 0x0028E5, 0x000042, 0x48000A,
+	0x004904, 0x386886, 0x002C65, 0x000042,
+	0x40000A, 0x0000D5, 0x454104, 0x000007,
+	0x000655, 0x054504, 0x34F286, 0x0001D5,
+	0x054504, 0x34F086, 0x002B65, 0x000042,
+	0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
+	0x000007, 0x454504, 0x000007, 0x0000CD,
+	0x444944, 0x000007, 0x454504, 0x000007,
+	0x00014D, 0x554944, 0x000007, 0x045144,
+	0x34E986, 0x002C65, 0x000042, 0x48000A,
+	0x4CD104, 0x000007, 0x04C144, 0x34F386,
+	0x000007, 0x160007, 0x002CE5, 0x040042,
+	0x40000A, 0x004020, 0x000040, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x356086,
+	0x000007, 0x002402, 0x36A206, 0x005C02,
+	0x0025E5, 0x000042, 0x40000A, 0x004274,
+	0x002AE5, 0x000042, 0x40000A, 0x004274,
+	0x500112, 0x0029E5, 0x000042, 0x40000A,
+	0x004234, 0x454104, 0x000007, 0x004020,
+	0x000040, 0x003EE5, 0x000020, 0x000040,
+	0x002DE5, 0x400152, 0x50000A, 0x045144,
+	0x364A86, 0x0000C5, 0x003EE5, 0x004020,
+	0x000040, 0x002BE5, 0x000042, 0x40000A,
+	0x404254, 0x000007, 0x002AE5, 0x004020,
+	0x000040, 0x500132, 0x040134, 0x005674,
+	0x0029E5, 0x020042, 0x42000A, 0x000042,
+	0x50000A, 0x05417C, 0x0028E5, 0x000042,
+	0x48000A, 0x0000C5, 0x4CC144, 0x371086,
+	0x0026E5, 0x0027E5, 0x020042, 0x40004A,
+	0x50000A, 0x00423C, 0x00567C, 0x0028E5,
+	0x004820, 0x000040, 0x281D12, 0x282512,
+	0x001F72, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x37AA86, 0x0E0007, 0x160007,
+	0x1E0007, 0x003EE5, 0x000042, 0x40000A,
+	0x004104, 0x37E886, 0x002D65, 0x000042,
+	0x28340A, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
+	0x54D104, 0x00735C, 0x385186, 0x000007,
+	0x000606, 0x080007, 0x0C0007, 0x080007,
+	0x0A0007, 0x0001E5, 0x020045, 0x004020,
+	0x000060, 0x000365, 0x000040, 0x002E65,
+	0x001A20, 0x0A1A60, 0x000040, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x000606, 0x50004A, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+// --------------------------------------------
+//  DS-1E Controller InstructionRAM Code
+//	1999/06/21
+//	Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09  creat
+// 04/12  stop nise fix
+// 06/21  WorkingOff timming
+static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x00800D, 0x000810, 0x20043A, 0x001A82,
+	0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+	0x000810, 0x18043A, 0x00010D, 0x020015,
+	0x0000FD, 0x000020, 0x038860, 0x039060,
+	0x038060, 0x038040, 0x038040, 0x038040,
+	0x018040, 0x000A7D, 0x038040, 0x038040,
+	0x018040, 0x200402, 0x000882, 0x08001A,
+	0x000904, 0x017186, 0x000007, 0x260007,
+	0x400007, 0x000007, 0x03258D, 0x000810,
+	0x18043A, 0x260007, 0x284402, 0x00087D,
+	0x018042, 0x00160A, 0x05A206, 0x000007,
+	0x440007, 0x00230D, 0x000810, 0x08043A,
+	0x22FA06, 0x000007, 0x0007FD, 0x018042,
+	0x08000A, 0x000904, 0x02AB86, 0x000195,
+	0x090D04, 0x000007, 0x000820, 0x0000F5,
+	0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+	0x018040, 0x000A7D, 0x038042, 0x13804A,
+	0x18000A, 0x001820, 0x059060, 0x058860,
+	0x018040, 0x0000FD, 0x018042, 0x70000A,
+	0x000115, 0x071144, 0x033B86, 0x030000,
+	0x007020, 0x036206, 0x018040, 0x00360D,
+	0x000810, 0x08043A, 0x232206, 0x000007,
+	0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+	0x000007, 0x240007, 0x000F8D, 0x000810,
+	0x00163A, 0x002402, 0x005C02, 0x0028FD,
+	0x000020, 0x018040, 0x08000D, 0x000815,
+	0x510984, 0x000007, 0x00004D, 0x000E5D,
+	0x000E02, 0x00430D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x00008D, 0x000924,
+	0x000F02, 0x00470D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x480480, 0x001210,
+	0x28043A, 0x00778D, 0x000810, 0x280C3A,
+	0x00068D, 0x000810, 0x28143A, 0x284402,
+	0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+	0x000820, 0x0002FD, 0x018040, 0x260007,
+	0x200007, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x051286, 0x000007, 0x240007,
+	0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x019B86, 0x000007, 0x01B206, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x22B886, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x065A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x036186, 0x000007, 0x002104, 0x036186,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x07C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00788D, 0x000810, 0x08043A, 0x2A1206,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x070206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x229086, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x225886, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x08DA86, 0x00057D, 0x002820,
+	0x03B060, 0x08F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x08FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x009D8D, 0x000810,
+	0x08043A, 0x2A1206, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x0A5186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x00107D, 0x018042,
+	0x08000A, 0x000215, 0x010984, 0x3A8186,
+	0x000007, 0x007FBD, 0x383DC4, 0x000007,
+	0x001A7D, 0x001375, 0x018042, 0x09004A,
+	0x10000A, 0x0B8D04, 0x139504, 0x000007,
+	0x000820, 0x019060, 0x001104, 0x225886,
+	0x010040, 0x0017FD, 0x018042, 0x08000A,
+	0x000904, 0x225A86, 0x000007, 0x00197D,
+	0x038042, 0x09804A, 0x10000A, 0x000924,
+	0x001664, 0x0011FD, 0x038042, 0x2B804A,
+	0x19804A, 0x00008D, 0x218944, 0x000007,
+	0x002244, 0x0C1986, 0x000007, 0x001A64,
+	0x002A24, 0x00197D, 0x080102, 0x100122,
+	0x000820, 0x039060, 0x018040, 0x003DFD,
+	0x00008D, 0x000820, 0x018040, 0x001375,
+	0x001A7D, 0x010042, 0x09804A, 0x10000A,
+	0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+	0x000007, 0x00060D, 0x000A15, 0x000C1D,
+	0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+	0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+	0x000464, 0x000464, 0x00040D, 0x08B1C4,
+	0x000007, 0x000820, 0x000BF5, 0x030040,
+	0x00197D, 0x038042, 0x09804A, 0x000A24,
+	0x08000A, 0x080E64, 0x000007, 0x100122,
+	0x000820, 0x031060, 0x010040, 0x0064AC,
+	0x00027D, 0x000020, 0x018040, 0x00107D,
+	0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+	0x20000A, 0x000095, 0x1A1144, 0x00A144,
+	0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+	0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+	0x28000A, 0x000095, 0x010924, 0x002A64,
+	0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+	0x000007, 0x0E6206, 0x080002, 0x00008D,
+	0x00387D, 0x000820, 0x018040, 0x00127D,
+	0x018042, 0x10000A, 0x003904, 0x0F0986,
+	0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+	0x000025, 0x0FB206, 0x00002D, 0x000015,
+	0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+	0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+	0x400025, 0x00008D, 0x110944, 0x000007,
+	0x00018D, 0x109504, 0x000007, 0x009164,
+	0x000424, 0x000424, 0x000424, 0x100102,
+	0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+	0x00018D, 0x00042D, 0x00008D, 0x109504,
+	0x000007, 0x00020D, 0x109184, 0x000007,
+	0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+	0x018040, 0x003BFD, 0x001020, 0x03A860,
+	0x000815, 0x313184, 0x212184, 0x000007,
+	0x03B060, 0x03A060, 0x018040, 0x0022FD,
+	0x000095, 0x010924, 0x000424, 0x000424,
+	0x001264, 0x100102, 0x000820, 0x039060,
+	0x018040, 0x001924, 0x010F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x000424, 0x000424,
+	0x00117D, 0x018042, 0x08000A, 0x000A24,
+	0x280502, 0x280C02, 0x09800D, 0x000820,
+	0x0002FD, 0x018040, 0x200007, 0x0022FD,
+	0x018042, 0x08000A, 0x000095, 0x280DC4,
+	0x011924, 0x00197D, 0x018042, 0x0011FD,
+	0x09804A, 0x10000A, 0x0000B5, 0x113144,
+	0x0A8D04, 0x000007, 0x080A44, 0x129504,
+	0x000007, 0x0023FD, 0x001020, 0x038040,
+	0x101244, 0x000007, 0x000820, 0x039060,
+	0x018040, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x123286, 0x000007, 0x003BFD,
+	0x000100, 0x000A10, 0x0B807A, 0x13804A,
+	0x090984, 0x000007, 0x000095, 0x013D04,
+	0x12B886, 0x10000A, 0x100002, 0x090984,
+	0x000007, 0x038042, 0x11804A, 0x090D04,
+	0x000007, 0x10000A, 0x090D84, 0x000007,
+	0x00257D, 0x000820, 0x018040, 0x00010D,
+	0x000810, 0x28143A, 0x00127D, 0x018042,
+	0x20000A, 0x00197D, 0x018042, 0x00117D,
+	0x31804A, 0x10000A, 0x003124, 0x013B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x300102,
+	0x003124, 0x000424, 0x000424, 0x001224,
+	0x280502, 0x001A4C, 0x143986, 0x700002,
+	0x00002D, 0x030000, 0x00387D, 0x018042,
+	0x10000A, 0x146206, 0x002124, 0x0000AD,
+	0x100002, 0x00010D, 0x000924, 0x006B24,
+	0x014A0D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x003264, 0x00008D, 0x000A24, 0x001020,
+	0x00227D, 0x018040, 0x014F8D, 0x000810,
+	0x08043A, 0x2B5A06, 0x000007, 0x002820,
+	0x00207D, 0x018040, 0x00117D, 0x038042,
+	0x13804A, 0x33800A, 0x00387D, 0x018042,
+	0x08000A, 0x000904, 0x177286, 0x000007,
+	0x00008D, 0x030964, 0x015B0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x380102, 0x000424,
+	0x000424, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x280502, 0x001A4C, 0x177186, 0x000007,
+	0x032164, 0x00632C, 0x003DFD, 0x018042,
+	0x08000A, 0x000095, 0x090904, 0x000007,
+	0x000820, 0x001A4C, 0x169986, 0x018040,
+	0x030000, 0x16B206, 0x002124, 0x00010D,
+	0x000924, 0x006B24, 0x016F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x003A64, 0x000095,
+	0x001224, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x171286, 0x000007, 0x01760D,
+	0x000810, 0x08043A, 0x2B5A06, 0x000007,
+	0x160A06, 0x000007, 0x007020, 0x08010A,
+	0x10012A, 0x0020FD, 0x038860, 0x039060,
+	0x018040, 0x00227D, 0x018042, 0x003DFD,
+	0x08000A, 0x31844A, 0x000904, 0x181086,
+	0x18008B, 0x00008D, 0x189904, 0x00312C,
+	0x18E206, 0x000007, 0x00324C, 0x186B86,
+	0x000007, 0x001904, 0x186886, 0x000007,
+	0x000095, 0x199144, 0x00222C, 0x003124,
+	0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+	0x010042, 0x09804A, 0x10000A, 0x038AEC,
+	0x0393EC, 0x00224C, 0x18E186, 0x000007,
+	0x00008D, 0x189904, 0x00226C, 0x00322C,
+	0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+	0x018042, 0x08000A, 0x018924, 0x300502,
+	0x001083, 0x001875, 0x010042, 0x10000A,
+	0x00008D, 0x010924, 0x001375, 0x330542,
+	0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+	0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+	0x006083, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x19B286, 0x000007, 0x001E2D,
+	0x0005FD, 0x018042, 0x08000A, 0x028924,
+	0x280502, 0x00060D, 0x000810, 0x280C3A,
+	0x00008D, 0x000810, 0x28143A, 0x0A808D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x001275, 0x030042, 0x21004A, 0x00008D,
+	0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+	0x030042, 0x0D004A, 0x10000A, 0x089144,
+	0x000007, 0x000820, 0x010040, 0x0025F5,
+	0x0A3144, 0x000007, 0x000820, 0x032860,
+	0x030040, 0x00217D, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00008D, 0x000124, 0x00012C, 0x000E64,
+	0x001A64, 0x00636C, 0x08010A, 0x10012A,
+	0x000820, 0x031060, 0x030040, 0x0020FD,
+	0x018042, 0x08000A, 0x00227D, 0x018042,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00197D, 0x018042, 0x08000A, 0x0022FD,
+	0x038042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x090D04, 0x000007, 0x000820,
+	0x030040, 0x038042, 0x0B804A, 0x10000A,
+	0x000820, 0x031060, 0x030040, 0x038042,
+	0x13804A, 0x19804A, 0x110D04, 0x198D04,
+	0x000007, 0x08000A, 0x001020, 0x031860,
+	0x030860, 0x030040, 0x00008D, 0x0B0944,
+	0x000007, 0x000820, 0x010040, 0x0005F5,
+	0x030042, 0x08000A, 0x000820, 0x010040,
+	0x0000F5, 0x010042, 0x08000A, 0x000904,
+	0x1D9886, 0x001E75, 0x030042, 0x01044A,
+	0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+	0x000C02, 0x00177D, 0x001AF5, 0x018042,
+	0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+	0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+	0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x005502, 0x005D0A, 0x030042, 0x0B004A,
+	0x1B804A, 0x13804A, 0x20000A, 0x089144,
+	0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+	0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+	0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+	0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+	0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+	0x000702, 0x00107D, 0x000415, 0x018042,
+	0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+	0x0019FD, 0x010042, 0x09804A, 0x10000A,
+	0x000934, 0x001674, 0x0029F5, 0x010042,
+	0x10000A, 0x00917C, 0x002075, 0x010042,
+	0x08000A, 0x000904, 0x200A86, 0x0026F5,
+	0x0027F5, 0x030042, 0x09004A, 0x10000A,
+	0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+	0x010042, 0x51804A, 0x48000A, 0x160007,
+	0x001075, 0x010042, 0x282C0A, 0x281D12,
+	0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+	0x001975, 0x010042, 0x002DF5, 0x0D004A,
+	0x10000A, 0x009144, 0x20EA86, 0x010042,
+	0x28340A, 0x000E5D, 0x00008D, 0x000375,
+	0x000820, 0x010040, 0x05D2F4, 0x54D104,
+	0x00735C, 0x218B86, 0x000007, 0x0C0007,
+	0x080007, 0x0A0007, 0x02178D, 0x000810,
+	0x08043A, 0x34B206, 0x000007, 0x219206,
+	0x000007, 0x080007, 0x002275, 0x010042,
+	0x20000A, 0x002104, 0x225886, 0x001E2D,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x21CA86, 0x000007, 0x002010, 0x30043A,
+	0x00057D, 0x0180C3, 0x08000A, 0x028924,
+	0x280502, 0x280C02, 0x0A810D, 0x000820,
+	0x0002F5, 0x010040, 0x220007, 0x0004FD,
+	0x018042, 0x70000A, 0x030000, 0x007020,
+	0x07FA06, 0x018040, 0x022B8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x22C286,
+	0x000007, 0x020206, 0x000007, 0x000875,
+	0x0009FD, 0x00010D, 0x234206, 0x000295,
+	0x000B75, 0x00097D, 0x00000D, 0x000515,
+	0x010042, 0x18000A, 0x001904, 0x2A0086,
+	0x0006F5, 0x001020, 0x010040, 0x0004F5,
+	0x000820, 0x010040, 0x000775, 0x010042,
+	0x09804A, 0x10000A, 0x001124, 0x000904,
+	0x23F286, 0x000815, 0x080102, 0x101204,
+	0x241206, 0x000575, 0x081204, 0x000007,
+	0x100102, 0x000575, 0x000425, 0x021124,
+	0x100102, 0x000820, 0x031060, 0x010040,
+	0x001924, 0x2A0086, 0x00008D, 0x000464,
+	0x009D04, 0x291086, 0x180102, 0x000575,
+	0x010042, 0x28040A, 0x00018D, 0x000924,
+	0x280D02, 0x00000D, 0x000924, 0x281502,
+	0x10000D, 0x000820, 0x0002F5, 0x010040,
+	0x200007, 0x001175, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x24FA86, 0x000007,
+	0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+	0x030A60, 0x010040, 0x050042, 0x3D004A,
+	0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+	0x010042, 0x28140A, 0x0004F5, 0x010042,
+	0x08000A, 0x000315, 0x010D04, 0x260286,
+	0x004015, 0x000095, 0x010D04, 0x25F086,
+	0x100022, 0x10002A, 0x261A06, 0x000007,
+	0x333104, 0x2AA904, 0x000007, 0x032124,
+	0x280502, 0x284402, 0x001124, 0x400102,
+	0x000424, 0x000424, 0x003224, 0x00292C,
+	0x00636C, 0x277386, 0x000007, 0x02B164,
+	0x000464, 0x000464, 0x00008D, 0x000A64,
+	0x280D02, 0x10008D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x00008D, 0x38B904,
+	0x000007, 0x03296C, 0x30010A, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x270286,
+	0x000007, 0x00212C, 0x28050A, 0x00316C,
+	0x00046C, 0x00046C, 0x28450A, 0x001124,
+	0x006B64, 0x100102, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x004124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x27FA86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x284086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x284A86, 0x000007, 0x284402, 0x003124,
+	0x300502, 0x003924, 0x300583, 0x000883,
+	0x0005F5, 0x010042, 0x28040A, 0x00008D,
+	0x008124, 0x280D02, 0x00008D, 0x008124,
+	0x281502, 0x10018D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001025, 0x000575,
+	0x030042, 0x09004A, 0x10000A, 0x0A0904,
+	0x121104, 0x000007, 0x001020, 0x050860,
+	0x050040, 0x0006FD, 0x018042, 0x09004A,
+	0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+	0x000007, 0x000820, 0x019060, 0x010040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x29CA86, 0x000007, 0x244206, 0x000007,
+	0x000606, 0x000007, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x2A1A86, 0x000007,
+	0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+	0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+	0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+	0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+	0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+	0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+	0x038A60, 0x000606, 0x018040, 0x00008D,
+	0x000A64, 0x280D02, 0x000A24, 0x00027D,
+	0x018042, 0x10000A, 0x001224, 0x0003FD,
+	0x018042, 0x08000A, 0x000904, 0x2C0A86,
+	0x000007, 0x00018D, 0x000A24, 0x000464,
+	0x000464, 0x080102, 0x000924, 0x000424,
+	0x000424, 0x100102, 0x02000D, 0x009144,
+	0x2C6186, 0x000007, 0x0001FD, 0x018042,
+	0x08000A, 0x000A44, 0x2C4386, 0x018042,
+	0x0A000D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00027D, 0x001020, 0x000606,
+	0x018040, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x2CB286, 0x000007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2CE286,
+	0x000007, 0x000075, 0x002E7D, 0x010042,
+	0x0B804A, 0x000020, 0x000904, 0x000686,
+	0x010040, 0x31844A, 0x30048B, 0x000883,
+	0x00008D, 0x000810, 0x28143A, 0x00008D,
+	0x000810, 0x280C3A, 0x000675, 0x010042,
+	0x08000A, 0x003815, 0x010924, 0x280502,
+	0x0B000D, 0x000820, 0x0002F5, 0x010040,
+	0x000606, 0x220007, 0x000464, 0x000464,
+	0x000606, 0x000007, 0x000134, 0x007F8D,
+	0x00093C, 0x281D12, 0x282512, 0x001F32,
+	0x0E0007, 0x00010D, 0x00037D, 0x000820,
+	0x018040, 0x05D2F4, 0x000007, 0x080007,
+	0x00037D, 0x018042, 0x08000A, 0x000904,
+	0x2E8A86, 0x000007, 0x000606, 0x000007,
+	0x000007, 0x000012, 0x100007, 0x320007,
+	0x600007, 0x460007, 0x100080, 0x48001A,
+	0x004904, 0x2EF186, 0x000007, 0x001210,
+	0x58003A, 0x000145, 0x5C5D04, 0x000007,
+	0x000080, 0x48001A, 0x004904, 0x2F4186,
+	0x000007, 0x001210, 0x50003A, 0x005904,
+	0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+	0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+	0x200502, 0x000082, 0x40001A, 0x004104,
+	0x2FC986, 0x000007, 0x003865, 0x40001A,
+	0x004020, 0x00104D, 0x04C184, 0x31AB86,
+	0x000040, 0x040007, 0x000165, 0x000145,
+	0x004020, 0x000040, 0x000765, 0x080080,
+	0x40001A, 0x004104, 0x305986, 0x000007,
+	0x001210, 0x40003A, 0x004104, 0x30B286,
+	0x00004D, 0x0000CD, 0x004810, 0x20043A,
+	0x000882, 0x40001A, 0x004104, 0x30C186,
+	0x000007, 0x004820, 0x005904, 0x319886,
+	0x000040, 0x0007E5, 0x200480, 0x2816A0,
+	0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+	0x000040, 0x000032, 0x400075, 0x00007D,
+	0x07D574, 0x200512, 0x000082, 0x40001A,
+	0x004104, 0x317186, 0x000007, 0x038A06,
+	0x640007, 0x0000E5, 0x000020, 0x000040,
+	0x000A65, 0x000020, 0x020040, 0x020040,
+	0x000040, 0x000165, 0x000042, 0x70000A,
+	0x007104, 0x323286, 0x000007, 0x060007,
+	0x019A06, 0x640007, 0x050000, 0x007020,
+	0x000040, 0x038A06, 0x640007, 0x000007,
+	0x00306D, 0x028860, 0x029060, 0x08000A,
+	0x028860, 0x008040, 0x100012, 0x00100D,
+	0x009184, 0x32D186, 0x000E0D, 0x009184,
+	0x33E186, 0x000007, 0x300007, 0x001020,
+	0x003B6D, 0x008040, 0x000080, 0x08001A,
+	0x000904, 0x32F186, 0x000007, 0x001220,
+	0x000DED, 0x008040, 0x008042, 0x10000A,
+	0x40000D, 0x109544, 0x000007, 0x001020,
+	0x000DED, 0x008040, 0x008042, 0x20040A,
+	0x000082, 0x08001A, 0x000904, 0x338186,
+	0x000007, 0x003B6D, 0x008042, 0x08000A,
+	0x000E15, 0x010984, 0x342B86, 0x600007,
+	0x08001A, 0x000C15, 0x010984, 0x341386,
+	0x000020, 0x1A0007, 0x0002ED, 0x008040,
+	0x620007, 0x00306D, 0x028042, 0x0A804A,
+	0x000820, 0x0A804A, 0x000606, 0x10804A,
+	0x000007, 0x282512, 0x001F32, 0x05D2F4,
+	0x54D104, 0x00735C, 0x000786, 0x000007,
+	0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+	0x020040, 0x004820, 0x025060, 0x40000A,
+	0x024060, 0x000040, 0x454944, 0x000007,
+	0x004020, 0x003AE5, 0x000040, 0x0028E5,
+	0x000042, 0x48000A, 0x004904, 0x39F886,
+	0x002C65, 0x000042, 0x40000A, 0x0000D5,
+	0x454104, 0x000007, 0x000655, 0x054504,
+	0x368286, 0x0001D5, 0x054504, 0x368086,
+	0x002B65, 0x000042, 0x003AE5, 0x50004A,
+	0x40000A, 0x45C3D4, 0x000007, 0x454504,
+	0x000007, 0x0000CD, 0x444944, 0x000007,
+	0x454504, 0x000007, 0x00014D, 0x554944,
+	0x000007, 0x045144, 0x367986, 0x002C65,
+	0x000042, 0x48000A, 0x4CD104, 0x000007,
+	0x04C144, 0x368386, 0x000007, 0x160007,
+	0x002CE5, 0x040042, 0x40000A, 0x004020,
+	0x000040, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x36F086, 0x000007, 0x002402,
+	0x383206, 0x005C02, 0x0025E5, 0x000042,
+	0x40000A, 0x004274, 0x002AE5, 0x000042,
+	0x40000A, 0x004274, 0x500112, 0x0029E5,
+	0x000042, 0x40000A, 0x004234, 0x454104,
+	0x000007, 0x004020, 0x000040, 0x003EE5,
+	0x000020, 0x000040, 0x002DE5, 0x400152,
+	0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+	0x003EE5, 0x004020, 0x000040, 0x002BE5,
+	0x000042, 0x40000A, 0x404254, 0x000007,
+	0x002AE5, 0x004020, 0x000040, 0x500132,
+	0x040134, 0x005674, 0x0029E5, 0x020042,
+	0x42000A, 0x000042, 0x50000A, 0x05417C,
+	0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+	0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+	0x020042, 0x40004A, 0x50000A, 0x00423C,
+	0x00567C, 0x0028E5, 0x004820, 0x000040,
+	0x281D12, 0x282512, 0x001F72, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x393A86,
+	0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+	0x000042, 0x40000A, 0x004104, 0x397886,
+	0x002D65, 0x000042, 0x28340A, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+	0x39E186, 0x000007, 0x000606, 0x080007,
+	0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+	0x020045, 0x004020, 0x000060, 0x000365,
+	0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+	0x000040, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x000606, 0x50004A,
+	0x0017FD, 0x018042, 0x08000A, 0x000904,
+	0x225A86, 0x000007, 0x00107D, 0x018042,
+	0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+	0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+	0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+	0x018042, 0x0010FD, 0x09804A, 0x38000A,
+	0x000095, 0x010924, 0x003A64, 0x3B8186,
+	0x000007, 0x003904, 0x3B9286, 0x000007,
+	0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+	0x00387D, 0x018040, 0x700002, 0x00117D,
+	0x018042, 0x00197D, 0x29804A, 0x30000A,
+	0x380002, 0x003124, 0x000424, 0x000424,
+	0x002A24, 0x280502, 0x00068D, 0x000810,
+	0x28143A, 0x00750D, 0x00B124, 0x002264,
+	0x3D0386, 0x284402, 0x000810, 0x280C3A,
+	0x0B800D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00758D, 0x00B124, 0x100102,
+	0x012144, 0x3E4986, 0x001810, 0x10003A,
+	0x00387D, 0x018042, 0x08000A, 0x000904,
+	0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+	0x00008D, 0x023164, 0x000A64, 0x280D02,
+	0x0B808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00387D, 0x018042, 0x08000A,
+	0x000904, 0x3E3286, 0x030000, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x3D8286,
+	0x000007, 0x002810, 0x28043A, 0x00750D,
+	0x030924, 0x002264, 0x280D02, 0x02316C,
+	0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x00008D, 0x000A24,
+	0x3E4A06, 0x100102, 0x001810, 0x10003A,
+	0x0000BD, 0x003810, 0x30043A, 0x00187D,
+	0x018042, 0x0018FD, 0x09804A, 0x20000A,
+	0x0000AD, 0x028924, 0x07212C, 0x001010,
+	0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+	0x002083, 0x00137D, 0x038042, 0x33844A,
+	0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+	0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+	0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+	0x020924, 0x00068D, 0x00A96C, 0x00009D,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+	0x284402, 0x001810, 0x28143A, 0x0C008D,
+	0x000820, 0x0002FD, 0x018040, 0x220007,
+	0x003904, 0x225886, 0x001E0D, 0x00057D,
+	0x018042, 0x20000A, 0x020924, 0x0000A5,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x402A86, 0x000007, 0x280502, 0x280C02,
+	0x002010, 0x28143A, 0x0C010D, 0x000820,
+	0x0002FD, 0x018040, 0x225A06, 0x220007,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+#endif	//_HWMCODE_
diff -Nru linux/sound/oss/yss225.c linux-2.4.19-pre5-mjc/sound/oss/yss225.c
--- linux/sound/oss/yss225.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/yss225.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,319 @@
+#include <linux/init.h>
+
+unsigned char page_zero[] __initdata = {
+0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
+0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
+0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19,
+0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01,
+0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00,
+0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02,
+0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
+0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00,
+0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02,
+0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40,
+0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02,
+0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00,
+0x1d, 0x02, 0xdf
+};    
+
+unsigned char page_one[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00,
+0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01,
+0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00,
+0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7,
+0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0,
+0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00,
+0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0,
+0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03,
+0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00,
+0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02,
+0x60, 0x00, 0x1b
+};
+
+unsigned char page_two[] __initdata = {
+0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4,
+0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07,
+0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46,
+0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46,
+0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07,
+0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05,
+0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05,
+0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44
+};
+
+unsigned char page_three[] __initdata = {
+0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
+0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
+0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40,
+0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00,
+0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40
+};
+
+unsigned char page_four[] __initdata = {
+0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
+0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00,
+0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60,
+0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00,
+0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22,
+0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01
+};
+
+unsigned char page_six[] __initdata = {
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e,
+0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00,
+0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00,
+0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24,
+0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00,
+0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00,
+0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a,
+0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00,
+0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d,
+0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50,
+0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00,
+0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17,
+0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66,
+0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c,
+0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00,
+0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c,
+0x80, 0x00, 0x7e, 0x80, 0x80
+};
+
+unsigned char page_seven[] __initdata = {
+0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f,
+0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff,
+0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f,
+0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38,
+0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06,
+0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b,
+0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06,
+0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55,
+0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14,
+0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93,
+0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x00
+};
+
+unsigned char page_zero_v2[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char page_one_v2[] __initdata = {
+0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char page_two_v2[] __initdata = {
+0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+unsigned char page_three_v2[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+unsigned char page_four_v2[] __initdata = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char page_seven_v2[] __initdata = {
+0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned char mod_v2[] __initdata = {
+0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02,
+0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05,
+0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0,
+0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20,
+0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3,
+0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff,
+0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16,
+0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff,
+0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31,
+0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
+0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44,
+0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00,
+0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57,
+0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00,
+0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72,
+0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0,
+0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85,
+0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00,
+0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0,
+0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00,
+0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00,
+0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6,
+0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00,
+0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02,
+0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03,
+0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01,
+0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01
+};
+unsigned char coefficients[] __initdata = {
+0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03,
+0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49,
+0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01,
+0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00,
+0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00,
+0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47,
+0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07,
+0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00,
+0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01,
+0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43,
+0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07,
+0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00,
+0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02,
+0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44,
+0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07,
+0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40,
+0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a,
+0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56,
+0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07,
+0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda,
+0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05,
+0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79,
+0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07,
+0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52,
+0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03,
+0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a,
+0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06,
+0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3,
+0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20,
+0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c,
+0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06,
+0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48,
+0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02,
+0xba
+};
+unsigned char coefficients2[] __initdata = {
+0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f,
+0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d,
+0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07,
+0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00,
+0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00
+};
+unsigned char coefficients3[] __initdata = { 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00,
+0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc,
+0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01,
+0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99,
+0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02,
+0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f,
+0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03,
+0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c,
+0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03,
+0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51,
+0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04,
+0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e,
+0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05,
+0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14,
+0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06,
+0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1,
+0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07,
+0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7,
+0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08,
+0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3,
+0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09,
+0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99,
+0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a,
+0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66,
+0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a,
+0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c,
+0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b,
+0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28,
+0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c,
+0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e,
+0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d,
+0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb,
+0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e,
+0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1,
+0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f,
+0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae,
+0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff
+};
+
diff -Nru linux/sound/oss/yss225.h linux-2.4.19-pre5-mjc/sound/oss/yss225.h
--- linux/sound/oss/yss225.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/oss/yss225.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,24 @@
+#ifndef __yss255_h__
+#define __yss255_h__
+
+extern unsigned char page_zero[256];
+extern unsigned char page_one[256];
+extern unsigned char page_two[128];
+extern unsigned char page_three[128];
+extern unsigned char page_four[128];
+extern unsigned char page_six[192];
+extern unsigned char page_seven[256];
+extern unsigned char page_zero_v2[96];
+extern unsigned char page_one_v2[96];
+extern unsigned char page_two_v2[48];
+extern unsigned char page_three_v2[48];
+extern unsigned char page_four_v2[48];
+extern unsigned char page_seven_v2[96];
+extern unsigned char mod_v2[304];
+extern unsigned char coefficients[364];
+extern unsigned char coefficients2[56];
+extern unsigned char coefficients3[404];
+
+
+#endif /* __ys225_h__ */
+
diff -Nru linux/sound/pci/Config.help linux-2.4.19-pre5-mjc/sound/pci/Config.help
--- linux/sound/pci/Config.help	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/Config.help	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,75 @@
+CONFIG_SND_ALI5451
+  Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core.
+
+CONFIG_SND_CS46XX
+  Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 /
+  CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips.
+
+CONFIG_SND_CS46XX_ACCEPT_VALID
+  Say 'Y' to allow sample resolution for mmap() transfers.
+
+CONFIG_SND_EMU10K1
+  Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!,
+  Audigy and E-mu APS (partially supported).
+
+CONFIG_SND_KORG1212
+  Say 'Y' or 'M' to include support for Korg 1212IO.
+
+CONFIG_SND_NM256
+  Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips.
+
+CONFIG_SND_RME96
+  Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and
+  Digi96/8 PRO/PAD/PST.
+
+CONFIG_SND_RME9652
+  Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 /
+  Digi9636) soundcards.
+
+CONFIG_SND_TRIDENT
+
+CONFIG_SND_YMFPCI
+  Say 'Y' or 'M' to include support for Yamaha PCI audio chips - 
+  YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754.
+
+CONFIG_SND_ALS4000
+  Say 'Y' or 'M' to include support for Avance Logic ALS4000.
+
+CONFIG_SND_CMIPCI
+  Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI
+  soundcards.
+
+CONFIG_SND_ENS1370
+  Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370.
+
+CONFIG_SND_ENS1371
+  Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and
+  Sound Blaster PCI 64 or 128 soundcards.
+
+CONFIG_SND_ES1938
+  Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946)
+  soundcard.
+
+CONFIG_SND_ES1968
+  Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E.
+
+CONFIG_SND_MAESTRO3
+  Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard.
+
+CONFIG_SND_FM801
+  Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards.
+
+CONFIG_SND_ICE1712
+  Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards.
+
+CONFIG_SND_INTEL8X0
+  Say 'Y' or 'M' to include support for Intel8x0 based soundcards.
+
+CONFIG_SND_SONICVIBES
+  Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards.
+
+CONFIG_SND_VIA686
+  Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge.
+
+CONFIG_SND_VIA8233
+  Say 'Y' or 'M' to include support for VIA VT8233 South Bridge.
diff -Nru linux/sound/pci/Config.in linux-2.4.19-pre5-mjc/sound/pci/Config.in
--- linux/sound/pci/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/Config.in	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,32 @@
+# ALSA PCI drivers
+
+mainmenu_option next_comment
+comment 'PCI devices'
+
+dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND
+dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND
+if [ "$CONFIG_SND_CS46XX" != "n" ]; then
+  bool       '  Cirrus Logic (Sound Fusion) MMAP support for OSS' CONFIG_SND_CS46XX_ACCEPT_VALID
+fi
+dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
+dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND
+dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND
+dep_tristate 'RME Digi96, 96/8, 96/8 PRO' CONFIG_SND_RME96 $CONFIG_SND
+dep_tristate 'RME Digi9652 (Hammerfall)' CONFIG_SND_RME9652 $CONFIG_SND
+dep_tristate 'Trident 4D-Wave DX/NX; SiS 7018' CONFIG_SND_TRIDENT $CONFIG_SND
+dep_tristate 'Yamaha YMF724/740/744/754' CONFIG_SND_YMFPCI $CONFIG_SND
+dep_tristate 'Avance Logic ALS4000' CONFIG_SND_ALS4000 $CONFIG_SND
+dep_tristate 'C-Media 8738, 8338' CONFIG_SND_CMIPCI $CONFIG_SND
+dep_tristate '(Creative) Ensoniq AudioPCI 1370' CONFIG_SND_ENS1370 $CONFIG_SND
+dep_tristate '(Creative) Ensoniq AudioPCI 1371/1373' CONFIG_SND_ENS1371 $CONFIG_SND
+dep_tristate 'ESS ES1938/1946 (Solo-1)' CONFIG_SND_ES1938 $CONFIG_SND
+dep_tristate 'ESS ES1968/1978 (Maestro-1/2/2E)' CONFIG_SND_ES1968 $CONFIG_SND
+dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND
+dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND
+dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND
+dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND
+dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND
+dep_tristate 'VIA 82C686A/B South Bridge' CONFIG_SND_VIA686 $CONFIG_SND
+dep_tristate 'VIA 8233 South Bridge' CONFIG_SND_VIA8233 $CONFIG_SND
+
+endmenu
diff -Nru linux/sound/pci/Makefile linux-2.4.19-pre5-mjc/sound/pci/Makefile
--- linux/sound/pci/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,94 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := pci.o
+
+subdir-y     := ac97 ali5451 cs46xx emu10k1 korg1212 nm256 rme9652 trident ymfpci
+subdir-m     := $(subdir-y)
+
+list-multi   := snd-als4000.o snd-cmipci.o snd-cs4281.o snd-ens1370.o \
+		snd-ens1371.o snd-es1938.o snd-es1968.o snd-fm801.o \
+		snd-ice1712.o snd-intel8x0.o snd-maestro3.o snd-rme96.o \
+		snd-sonicvibes.o snd-via686.o snd-via8233.o
+
+snd-als4000-objs := als4000.o
+snd-cmipci-objs := cmipci.o
+snd-cs4281-objs := cs4281.o
+snd-ens1370-objs := ens1370.o
+snd-ens1371-objs := ens1371.o
+snd-es1938-objs := es1938.o
+snd-es1968-objs := es1968.o
+snd-fm801-objs := fm801.o
+snd-ice1712-objs := ice1712.o
+snd-intel8x0-objs := intel8x0.o
+snd-maestro3-objs := maestro3.o
+snd-rme96-objs := rme96.o
+snd-sonicvibes-objs := sonicvibes.o
+snd-via686-objs := via686.o
+snd-via8233-objs := via8233.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS4000) += snd-als4000.o
+obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
+obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
+obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
+obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
+obj-$(CONFIG_SND_ES1938) += snd-es1938.o
+obj-$(CONFIG_SND_ES1968) += snd-es1968.o
+obj-$(CONFIG_SND_FM801) += snd-fm801.o
+obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o
+obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
+obj-$(CONFIG_SND_RME96) += snd-rme96.o
+obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
+obj-$(CONFIG_SND_VIA686) += snd-via686.o
+obj-$(CONFIG_SND_VIA8233) += snd-via8233.o
+
+include $(TOPDIR)/Rules.make
+
+snd-als4000.o: $(snd-als4000-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-als4000-objs)
+
+snd-cmipci.o: $(snd-cmipci-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cmipci-objs)
+
+snd-cs4281.o: $(snd-cs4281-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4281-objs)
+
+snd-ens1370.o: $(snd-ens1370-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1370-objs)
+
+snd-ens1371.o: $(snd-ens1371-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1371-objs)
+
+snd-es1938.o: $(snd-es1938-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-es1938-objs)
+
+snd-es1968.o: $(snd-es1968-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-es1968-objs)
+
+snd-fm801.o: $(snd-fm801-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-fm801-objs)
+
+snd-ice1712.o: $(snd-ice1712-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ice1712-objs)
+
+snd-intel8x0.o: $(snd-intel8x0-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-intel8x0-objs)
+
+snd-maestro3.o: $(snd-maestro3-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-maestro3-objs)
+
+snd-rme96.o: $(snd-rme96-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-rme96-objs)
+
+snd-sonicvibes.o: $(snd-sonicvibes-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-sonicvibes-objs)
+
+snd-via686.o: $(snd-via686-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-via686-objs)
+
+snd-via8233.o: $(snd-via8233-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-via8233-objs)
diff -Nru linux/sound/pci/ac97/Makefile linux-2.4.19-pre5-mjc/sound/pci/ac97/Makefile
--- linux/sound/pci/ac97/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ac97/Makefile	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,39 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _ac97.o
+
+list-multi   := snd-ac97-codec.o snd-ak4531-codec.o
+
+export-objs  := ac97_codec.o ak4531_codec.o
+
+snd-ac97-codec-objs := ac97_codec.o
+snd-ak4531-codec-objs := ak4531_codec.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o
+obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o
+obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o
+obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o
+obj-$(CONFIG_SND_VIA686) += snd-ac97-codec.o
+obj-$(CONFIG_SND_VIA8233) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o
+obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o
+obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o
+obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o
+obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o
+obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ac97-codec.o: $(snd-ac97-codec-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ac97-codec-objs)
+
+snd-ak4531-codec.o: $(snd-ak4531-codec-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ak4531-codec-objs)
diff -Nru linux/sound/pci/ac97/ac97_codec.c linux-2.4.19-pre5-mjc/sound/pci/ac97/ac97_codec.c
--- linux/sound/pci/ac97/ac97_codec.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ac97/ac97_codec.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2085 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
+MODULE_LICENSE("GPL");
+
+static int enable_loopback = 0;
+
+MODULE_PARM(enable_loopback, "i");
+MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
+MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC);
+
+#define chip_t ac97_t
+
+/*
+
+ */
+
+static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97);
+static void snd_ac97_proc_done(ac97_t * ac97);
+
+static int patch_wolfson(ac97_t * ac97);
+
+static int patch_tritech_tr28028(ac97_t * ac97);
+static int patch_sigmatel_stac9708(ac97_t * ac97);
+static int patch_sigmatel_stac9721(ac97_t * ac97);
+static int patch_sigmatel_stac9744(ac97_t * ac97);
+static int patch_sigmatel_stac9756(ac97_t * ac97);
+static int patch_cirrus_cs4299(ac97_t * ac97);
+static int patch_ad1819(ac97_t * ac97);
+static int patch_ad1881(ac97_t * ac97);
+
+typedef struct {
+	unsigned int id;
+	unsigned int mask;
+	char *name;
+	int (*patch)(ac97_t *ac97);
+} ac97_codec_id_t;
+
+static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = {
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL },
+{ 0x41445300, 0xffffff00, "Analog Devices",	NULL },
+{ 0x414c4300, 0xffffff00, "Realtek",		NULL },
+{ 0x414c4700, 0xffffff00, "Avance Logic",	NULL },
+{ 0x43525900, 0xffffff00, "Cirrus Logic",	NULL },
+{ 0x48525300, 0xffffff00, "Intersil",		NULL },
+{ 0x49434500, 0xffffff00, "ICEnsemble",		NULL },
+{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL },
+{ 0x53494c00, 0xffffff00, "Silicon Laboratory",	NULL },
+{ 0x54524100, 0xffffff00, "TriTech",		NULL },
+{ 0x54584e00, 0xffffff00, "Texas Instruments",	NULL },
+{ 0x57454300, 0xffffff00, "Winbond",		NULL },
+{ 0x574d4c00, 0xffffff00, "Wolfson",		patch_wolfson },
+{ 0x594d4800, 0xffffff00, "Yamaha",		NULL },
+{ 0x83847600, 0xffffff00, "SigmaTel",		NULL },
+{ 0x45838300, 0xffffff00, "ESS Technology",	NULL },
+{ 0,	      0, 	  NULL,			NULL }
+};
+
+static const ac97_codec_id_t snd_ac97_codec_ids[] = {
+{ 0x414b4d00, 0xffffffff, "AK4540",		NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542",		NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543",		NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545",		NULL },
+{ 0x41445303, 0xffffffff, "AD1819",		patch_ad1819 },
+{ 0x41445340, 0xffffffff, "AD1881",		patch_ad1881 },
+{ 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881 },
+{ 0x41445360, 0xffffffff, "AD1885",		patch_ad1881 },
+{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1881 },
+{ 0x41445362, 0xffffffff, "AD1887",		patch_ad1881 },
+{ 0x414c4300, 0xfffffff0, "RL5306",	 	NULL },
+{ 0x414c4310, 0xfffffff0, "RL5382", 		NULL },
+{ 0x414c4320, 0xfffffff0, "RL5383", 		NULL },
+{ 0x414c4710, 0xffffffff, "ALC200/200P",	NULL },
+{ 0x43525900, 0xfffffff8, "CS4297",		NULL },
+{ 0x43525910, 0xfffffff8, "CS4297A",		NULL },
+{ 0x42525920, 0xfffffff8, "CS4294/4298",	NULL },
+{ 0x42525928, 0xfffffff8, "CS4294",		NULL },
+{ 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299 },
+{ 0x43525948, 0xfffffff8, "CS4201",		NULL },
+{ 0x43525958, 0xfffffff8, "CS4205",		NULL },
+{ 0x43525960, 0xfffffff8, "CS4291",		NULL },
+{ 0x48525300, 0xffffff00, "HMP9701",		NULL },
+{ 0x49434501, 0xffffffff, "ICE1230",		NULL },
+{ 0x49434511, 0xffffffff, "ICE1232",		NULL }, // alias VIA VT1611A?
+{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48",	NULL }, // only guess --jk
+{ 0x4e534331, 0xffffffff, "LM4549",		NULL },
+{ 0x53494c22, 0xffffffff, "Si3036",		NULL },
+{ 0x53494c23, 0xffffffff, "Si3038",		NULL },
+{ 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028 }, // added by xin jin [07/09/99]
+{ 0x54524123, 0xffffffff, "TR28602",		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
+{ 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL },
+{ 0x57454301, 0xffffffff, "W83971D",		NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL },
+{ 0x574d4c03, 0xffffffff, "WM9703/9704",	NULL },
+{ 0x574d4c04, 0xffffffff, "WM9704 (quad)",	NULL },
+{ 0x594d4800, 0xffffffff, "YMF743",		NULL },
+{ 0x83847600, 0xffffffff, "STAC9700/83/84",	NULL },
+{ 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL },
+{ 0x83847605, 0xffffffff, "STAC9704",		NULL },
+{ 0x83847608, 0xffffffff, "STAC9708/11",	patch_sigmatel_stac9708 },
+{ 0x83847609, 0xffffffff, "STAC9721/23",	patch_sigmatel_stac9721 },
+{ 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744 },
+{ 0x83847656, 0xffffffff, "STAC9756/57",	patch_sigmatel_stac9756 },
+{ 0x45838308, 0xffffffff, "ESS1988",		NULL },
+{ 0, 	      0,	  NULL,			NULL }
+};
+
+#define AC97_ID_AK4540		0x414b4d00
+#define AC97_ID_AK4542		0x414b4d01
+#define AC97_ID_AD1819		0x41445303
+#define AC97_ID_AD1881		0x41445340
+#define AC97_ID_AD1881A		0x41445348
+#define AC97_ID_AD1885		0x41445360
+#define AC97_ID_AD1886		0x41445361
+#define AC97_ID_AD1887		0x41445362
+#define AC97_ID_TR28028		0x54524108
+#define AC97_ID_STAC9700	0x83847600
+#define AC97_ID_STAC9704	0x83847604
+#define AC97_ID_STAC9705	0x83847605
+#define AC97_ID_STAC9708	0x83847608
+#define AC97_ID_STAC9721	0x83847609
+#define AC97_ID_STAC9744	0x83847644
+#define AC97_ID_STAC9756	0x83847656
+
+static const char *snd_ac97_stereo_enhancements[] =
+{
+  /*   0 */ "No 3D Stereo Enhancement",
+  /*   1 */ "Analog Devices Phat Stereo",
+  /*   2 */ "Creative Stereo Enhancement",
+  /*   3 */ "National Semi 3D Stereo Enhancement",
+  /*   4 */ "YAMAHA Ymersion",
+  /*   5 */ "BBE 3D Stereo Enhancement",
+  /*   6 */ "Crystal Semi 3D Stereo Enhancement",
+  /*   7 */ "Qsound QXpander",
+  /*   8 */ "Spatializer 3D Stereo Enhancement",
+  /*   9 */ "SRS 3D Stereo Enhancement",
+  /*  10 */ "Platform Tech 3D Stereo Enhancement",
+  /*  11 */ "AKM 3D Audio",
+  /*  12 */ "Aureal Stereo Enhancement",
+  /*  13 */ "Aztech 3D Enhancement",
+  /*  14 */ "Binaura 3D Audio Enhancement",
+  /*  15 */ "ESS Technology Stereo Enhancement",
+  /*  16 */ "Harman International VMAx",
+  /*  17 */ "Nvidea 3D Stereo Enhancement",
+  /*  18 */ "Philips Incredible Sound",
+  /*  19 */ "Texas Instruments 3D Stereo Enhancement",
+  /*  20 */ "VLSI Technology 3D Stereo Enhancement",
+  /*  21 */ "TriTech 3D Stereo Enhancement",
+  /*  22 */ "Realtek 3D Stereo Enhancement",
+  /*  23 */ "Samsung 3D Stereo Enhancement",
+  /*  24 */ "Wolfson Microelectronics 3D Enhancement",
+  /*  25 */ "Delta Integration 3D Enhancement",
+  /*  26 */ "SigmaTel 3D Enhancement",
+  /*  27 */ "Reserved 27",
+  /*  28 */ "Rockwell 3D Stereo Enhancement",
+  /*  29 */ "Reserved 29",
+  /*  30 */ "Reserved 30",
+  /*  31 */ "Reserved 31"
+};
+
+/*
+ *  I/O routines
+ */
+
+static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg)
+{
+	/* filter some registers for buggy codecs */
+	switch (ac97->id) {
+	case AC97_ID_AK4540:
+	case AC97_ID_AK4542:
+		if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)
+			return 1;
+		return 0;
+	case AC97_ID_AD1819:	/* AD1819 */
+	case AC97_ID_AD1881:	/* AD1881 */
+	case AC97_ID_AD1881A:	/* AD1881A */
+		if (reg >= 0x3a && reg <= 0x6e)	/* 0x59 */
+			return 0;
+		return 1;
+	case AC97_ID_AD1885:	/* AD1885 */
+	case AC97_ID_AD1886:	/* AD1886 */
+	case AC97_ID_AD1887:	/* AD1887 - !!verify!! --jk */
+		if (reg == 0x5a)
+			return 1;
+		if (reg >= 0x3c && reg <= 0x6e)	/* 0x59 */
+			return 0;
+		return 1;
+	case AC97_ID_STAC9700:
+	case AC97_ID_STAC9704:
+	case AC97_ID_STAC9705:
+	case AC97_ID_STAC9708:
+	case AC97_ID_STAC9721:
+	case AC97_ID_STAC9744:
+	case AC97_ID_STAC9756:
+		if (reg <= 0x3a || reg >= 0x5a)
+			return 1;
+		return 0;
+	}
+	return 1;
+}
+
+void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return;
+	ac97->write(ac97, reg, value);
+}
+
+unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return 0;
+	return ac97->read(ac97, reg);
+}
+
+void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return;
+	spin_lock(&ac97->reg_lock);
+	ac97->write(ac97, reg, ac97->regs[reg] = value);
+	spin_unlock(&ac97->reg_lock);
+	set_bit(reg, ac97->reg_accessed);
+}
+
+#ifndef CONFIG_SND_DEBUG
+#define snd_ac97_write_cache_test snd_ac97_write_cache
+#else
+static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+	return snd_ac97_write_cache(ac97, reg, value);
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return;
+	spin_lock(&ac97->reg_lock);
+	ac97->write(ac97, reg, value);
+	ac97->regs[reg] = ac97->read(ac97, reg);
+	if (value != ac97->regs[reg])
+		snd_printk("AC97 reg=%02x val=%04x real=%04x\n", reg, value, ac97->regs[reg]);
+	spin_unlock(&ac97->reg_lock);
+}
+#endif
+
+int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value)
+{
+	int change;
+
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return -EINVAL;
+	spin_lock(&ac97->reg_lock);
+	change = ac97->regs[reg] != value;
+	if (change) {
+		ac97->write(ac97, reg, value);
+		ac97->regs[reg] = value;
+	}
+	spin_unlock(&ac97->reg_lock);
+	return change;
+}
+
+int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new;
+
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return -EINVAL;
+	spin_lock(&ac97->reg_lock);
+	old = ac97->regs[reg];
+	new = (old & ~mask) | value;
+	change = old != new;
+	if (change) {
+		ac97->write(ac97, reg, new);
+		ac97->regs[reg] = new;
+	}
+	spin_unlock(&ac97->reg_lock);
+	return change;
+}
+
+int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new;
+
+	down(&ac97->spec.ad18xx.mutex);
+	spin_lock(&ac97->reg_lock);
+	old = ac97->spec.ad18xx.pcmreg[codec];
+	new = (old & ~mask) | value;
+	change = old != new;
+	if (change) {
+		/* select single codec */
+		ac97->write(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+		/* update PCM bits */
+		ac97->write(ac97, AC97_PCM, new);
+		/* select all codecs */
+		ac97->write(ac97, AC97_AD_SERIAL_CFG, 0x7000);
+		ac97->spec.ad18xx.pcmreg[codec] = new;
+	}
+	spin_unlock(&ac97->reg_lock);
+	up(&ac97->spec.ad18xx.mutex);
+	return change;
+}
+
+/*
+ *
+ */
+
+static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Mic", "CD", "Video", "Aux", "Line",
+		"Mix", "Mix Mono", "Phone"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	
+	val = ac97->regs[AC97_REC_SEL];
+	ucontrol->value.enumerated.item[0] = (val >> 8) & 7;
+	ucontrol->value.enumerated.item[1] = (val >> 0) & 7;
+	return 0;
+}
+
+static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	
+	if (ucontrol->value.enumerated.item[0] > 7 ||
+	    ucontrol->value.enumerated.item[1] > 7)
+		return -EINVAL;
+	val = (ucontrol->value.enumerated.item[0] << 8) |
+	      (ucontrol->value.enumerated.item[1] << 0);
+	return snd_ac97_update(ac97, AC97_REC_SEL, val);
+}
+
+#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_enum_double, \
+  get: snd_ac97_get_enum_double, put: snd_ac97_put_enum_double, \
+  private_value: reg | (shift << 8) | (invert << 24) }
+
+static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts1[2] = { "pre 3D", "post 3D" };
+	static char *texts2[2] = { "Mix", "Mic" };
+	static char *texts3[2] = { "Mic1", "Mic2" };
+	char **texts = NULL;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+
+	switch (reg) {
+	case AC97_GENERAL_PURPOSE:
+		switch (shift) {
+		case 15: texts = texts1; break;
+		case 9: texts = texts2; break;
+		case 8: texts = texts3; break;
+		}
+	}
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	val = (ac97->regs[reg] >> shift) & 1;
+	if (invert)
+		val ^= 1;
+	ucontrol->value.enumerated.item[0] = val;
+	return 0;
+}
+
+static int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	if (ucontrol->value.enumerated.item[0] > 1)
+		return -EINVAL;
+	val = !!ucontrol->value.enumerated.item[0];
+	if (invert)
+		val = !val;
+	return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift);
+}
+
+#define AC97_SINGLE(xname, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_single, \
+  get: snd_ac97_get_single, put: snd_ac97_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_ac97_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ac97_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_ac97_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	return snd_ac97_update_bits(ac97, reg, mask << shift, val << shift);
+}
+
+#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: (xname), info: snd_ac97_info_double, \
+  get: snd_ac97_get_double, put: snd_ac97_put_double, \
+  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+
+static int snd_ac97_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ac97_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock(&ac97->reg_lock);
+	ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (ac97->regs[reg] >> shift_right) & mask;
+	spin_unlock(&ac97->reg_lock);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_ac97_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	return snd_ac97_update_bits(ac97, reg, 
+				    (mask << shift_left) | (mask << shift_right),
+				    (val1 << shift_left) | (val2 << shift_right));
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_master[2] = {
+AC97_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
+AC97_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_headphone[2] = {
+AC97_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+AC97_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = {
+AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_tone[2] = {
+AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
+AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_pc_beep[2] = {
+AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_phone[2] = {
+AC97_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1),
+AC97_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 15, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_mic[3] = {
+AC97_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1),
+AC97_SINGLE("Mic Playback Volume", AC97_MIC, 0, 15, 1),
+AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_line[2] = {
+AC97_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1),
+AC97_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_cd[2] = {
+AC97_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1),
+AC97_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_video[2] = {
+AC97_SINGLE("Video Playback Switch", AC97_VIDEO, 15, 1, 1),
+AC97_DOUBLE("Video Playback Volume", AC97_VIDEO, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_aux[2] = {
+AC97_SINGLE("Aux Playback Switch", AC97_AUX, 15, 1, 1),
+AC97_DOUBLE("Aux Playback Volume", AC97_AUX, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_pcm[2] = {
+AC97_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
+AC97_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_capture[3] = {
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_ac97_info_mux,
+	get: snd_ac97_get_mux,
+	put: snd_ac97_put_mux,
+},
+AC97_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_mic_capture[2] = {
+AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1),
+AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0)
+};
+
+typedef enum {
+	AC97_GENERAL_PCM_OUT = 0,
+	AC97_GENERAL_STEREO_ENHANCEMENT,
+	AC97_GENERAL_3D,
+	AC97_GENERAL_LOUDNESS,
+	AC97_GENERAL_MONO,
+	AC97_GENERAL_MIC,
+	AC97_GENERAL_LOOPBACK
+} ac97_general_index_t;
+
+static const snd_kcontrol_new_t snd_ac97_controls_general[7] = {
+AC97_ENUM_DOUBLE("PCM Out Path & Mute", AC97_GENERAL_PURPOSE, 15, 0),
+AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),
+AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
+AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
+AC97_ENUM_DOUBLE("Mono Output Select", AC97_GENERAL_PURPOSE, 9, 0),
+AC97_ENUM_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0),
+AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_3d[2] = {
+AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0),
+AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_center[2] = {
+AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1),
+AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_lfe[2] = {
+AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1),
+AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = {
+AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
+AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
+};
+
+static const snd_kcontrol_new_t snd_ac97_sigmatel_controls[] = {
+AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0),
+AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_control_eapd =
+AC97_SINGLE("External Amplifier Power Down", AC97_POWERDOWN, 15, 1, 0);
+
+static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+                        
+static int snd_ac97_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+					   IEC958_AES0_NONAUDIO |
+					   IEC958_AES0_CON_EMPHASIS_5015 |
+					   IEC958_AES0_CON_NOT_COPYRIGHT;
+	ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+					   IEC958_AES1_CON_ORIGINAL;
+	ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+	return 0;
+}
+                        
+static int snd_ac97_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	/* FIXME: AC'97 spec doesn't say which bits are used for what */
+	ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+					   IEC958_AES0_NONAUDIO |
+					   IEC958_AES0_PRO_FS |
+					   IEC958_AES0_PRO_EMPHASIS_5015;
+	return 0;
+}
+
+static int snd_ac97_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+	spin_lock(&ac97->reg_lock);
+	ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff;
+	ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff;
+	spin_unlock(&ac97->reg_lock);
+	return 0;
+}
+                        
+static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned int new = 0;
+	unsigned short val = 0;
+	int change = 0;
+
+	spin_lock(&ac97->reg_lock);
+	new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO);
+	if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) {
+		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015);
+		switch (new & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break;
+		case IEC958_AES0_PRO_FS_32000: val |= 2<<12; break;
+		case IEC958_AES0_PRO_FS_48000: val |= 1<<12; break;
+		default:		       val |= 1<<12; break;
+		}
+		if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+			val |= 1<<3;
+	} else {
+		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT);
+		new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8);
+		new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 8);
+		if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
+			val |= 1<<3;
+		if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT))
+			val |= 1<<2;
+		val |= ((new >> 8) & 0xff) << 4;	// category + original
+		switch ((new >> 24) & 0xff) {
+		case IEC958_AES3_CON_FS_44100: val |= 0<<12; break;
+		case IEC958_AES3_CON_FS_48000: val |= 2<<12; break;
+		case IEC958_AES3_CON_FS_32000: val |= 3<<12; break;
+		default:		       val |= 1<<12; break;
+		}
+	}
+	change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val);
+	change |= ac97->spdif_status != new;
+	ac97->spdif_status = new;
+	spin_unlock(&ac97->reg_lock);
+	return change;
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = {
+	{
+		access: SNDRV_CTL_ELEM_ACCESS_READ,
+		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+		name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+		info: snd_ac97_spdif_mask_info,
+		get: snd_ac97_spdif_cmask_get,
+	},
+	{
+		access: SNDRV_CTL_ELEM_ACCESS_READ,
+		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+		name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+		info: snd_ac97_spdif_mask_info,
+		get: snd_ac97_spdif_pmask_get,
+	},
+	{
+		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+		name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		info: snd_ac97_spdif_mask_info,
+		get: snd_ac97_spdif_default_get,
+		put: snd_ac97_spdif_default_put,
+	},
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0),
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",AC97_EXTENDED_STATUS, 4, 3, 0)
+};
+
+#define AD18XX_PCM_BITS(xname, codec, shift, mask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_bits, \
+  get: snd_ac97_ad18xx_pcm_get_bits, put: snd_ac97_ad18xx_pcm_put_bits, \
+  private_value: codec | (shift << 8) | (mask << 16) }
+
+static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_get_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	
+	ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> shift) & mask);
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_put_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	unsigned short val;
+	
+	val = mask - (ucontrol->value.integer.value[0] & mask);
+	return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, mask << shift, val << shift);
+}
+
+#define AD18XX_PCM_VOLUME(xname, codec) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_volume, \
+  get: snd_ac97_ad18xx_pcm_get_volume, put: snd_ac97_ad18xx_pcm_put_volume, \
+  private_value: codec }
+
+static int snd_ac97_ad18xx_pcm_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	
+	spin_lock(&ac97->reg_lock);
+	ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
+	ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
+	spin_unlock(&ac97->reg_lock);
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	unsigned short val1, val2;
+	
+	val1 = 31 - (ucontrol->value.integer.value[0] & 31);
+	val2 = 31 - (ucontrol->value.integer.value[1] & 31);
+	return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2);
+}
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_pcm[2] = {
+AD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 1),
+AD18XX_PCM_VOLUME("PCM Playback Volume", 0)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_surround[2] = {
+AD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 1),
+AD18XX_PCM_VOLUME("Surround Playback Volume", 1)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_center[2] = {
+AD18XX_PCM_BITS("Center Playback Switch", 2, 15, 1),
+AD18XX_PCM_BITS("Center Playback Volume", 2, 8, 31)
+};
+
+static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_lfe[1] = {
+AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 31)
+};
+
+/*
+ *
+ */
+
+static int snd_ac97_free(ac97_t *ac97)
+{
+	if (ac97) {
+		snd_ac97_proc_done(ac97);
+		if (ac97->private_free)
+			ac97->private_free(ac97);
+		snd_magic_kfree(ac97);
+	}
+	return 0;
+}
+
+static int snd_ac97_dev_free(snd_device_t *device)
+{
+	ac97_t *ac97 = snd_magic_cast(ac97_t, device->device_data, return -ENXIO);
+	return snd_ac97_free(ac97);
+}
+
+static int snd_ac97_try_volume_mix(ac97_t * ac97, int reg)
+{
+	unsigned short val, mask = 0x8000;
+
+	switch (reg) {
+	case AC97_MASTER_TONE:
+		return ac97->caps & 0x04 ? 1 : 0;
+	case AC97_HEADPHONE:
+		return ac97->caps & 0x10 ? 1 : 0;
+	case AC97_REC_GAIN_MIC:
+		return ac97->caps & 0x01 ? 1 : 0;
+	case AC97_3D_CONTROL:
+		if (ac97->caps & 0x7c00) {
+			val = snd_ac97_read(ac97, reg);
+			/* if nonzero - fixed and we can't set it */
+			return val == 0;
+		}
+		return 0;
+	case AC97_CENTER_LFE_MASTER:	/* center */
+		if ((ac97->ext_id & 0x40) == 0)
+			return 0;
+		break;
+	case AC97_CENTER_LFE_MASTER+1:	/* lfe */
+		if ((ac97->ext_id & 0x100) == 0)
+			return 0;
+		reg = AC97_CENTER_LFE_MASTER;
+		mask = 0x0080;
+		break;
+	case AC97_SURROUND_MASTER:
+		if ((ac97->ext_id & 0x80) == 0)
+			return 0;
+		break;
+	}
+	val = snd_ac97_read(ac97, reg);
+	if (!(val & mask)) {
+		/* nothing seems to be here - mute flag is not set */
+		/* try another test */
+		snd_ac97_write_cache_test(ac97, reg, val | mask);
+		val = snd_ac97_read(ac97, reg);
+		if (!(val & mask))
+			return 0;	/* nothing here */
+	}
+	return 1;		/* success, useable */
+}
+
+static int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit)
+{
+	unsigned short mask, val, orig, res;
+
+	mask = 1 << bit;
+	orig = snd_ac97_read(ac97, reg);
+	val = orig ^ mask;
+	snd_ac97_write(ac97, reg, val);
+	res = snd_ac97_read(ac97, reg);
+	snd_ac97_write_cache(ac97, reg, orig);
+	return res == val;
+}
+
+static void snd_ac97_change_volume_params1(ac97_t * ac97, int reg, unsigned char *max)
+{
+	unsigned short val, val1;
+
+	*max = 63;
+	val = 0x8000 | 0x0020;
+	snd_ac97_write(ac97, reg, val);
+	val1 = snd_ac97_read(ac97, reg);
+	if (val != val1) {
+		*max = 31;
+	}
+	/* reset volume to zero */
+	snd_ac97_write_cache(ac97, reg, 0x8000);
+}
+
+static void snd_ac97_change_volume_params2(ac97_t * ac97, int reg, int shift, unsigned char *max)
+{
+	unsigned short val, val1;
+
+	*max = 63;
+	val = 0x8080 | (0x20 << shift);
+	snd_ac97_write(ac97, reg, val);
+	val1 = snd_ac97_read(ac97, reg);
+	if (val != val1) {
+		*max = 31;
+	}
+	/* reset volume to zero */
+	snd_ac97_write_cache(ac97, reg, 0x8080);
+}
+
+static inline int printable(unsigned int x)
+{
+	x &= 0xff;
+	if (x < ' ' || x >= 0x71) {
+		if (x <= 0x89)
+			return x - 0x71 + 'A';
+		return '?';
+	}
+	return x;
+}
+
+static snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t * ac97)
+{
+	snd_kcontrol_new_t template;
+	memcpy(&template, _template, sizeof(template));
+	snd_runtime_check(!template.index, return NULL);
+	template.index = ac97->num;
+	return snd_ctl_new1(&template, ac97);
+}
+
+static int snd_ac97_mixer_build(snd_card_t * card, ac97_t * ac97)
+{
+	snd_kcontrol_t *kctl;
+	int err, idx;
+	unsigned char max;
+
+	/* build master controls */
+	/* AD claims to remove this control from AD1887, although spec v2.2 don't allow this */
+	if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params1(ac97, AC97_MASTER, &max);
+		kctl->private_value &= ~(0xff << 16);
+		kctl->private_value |= (int)max << 16;
+		snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000 | max | (max << 8));
+	}
+
+	ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
+
+	/* build center controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
+		kctl->private_value &= ~(0xff << 16);
+		kctl->private_value |= (int)max << 16;
+		snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
+	}
+
+	/* build LFE controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
+		kctl->private_value &= ~(0xff << 16);
+		kctl->private_value |= (int)max << 16;
+		snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
+	}
+
+	/* build surround controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_surround[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_surround[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params2(ac97, AC97_SURROUND_MASTER, 0, &max);
+		kctl->private_value &= ~(0xff << 16);
+		kctl->private_value |= (int)max << 16;
+		snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x8080 | max | (max << 8));
+	}
+
+	/* build headphone controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_headphone[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_headphone[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params1(ac97, AC97_HEADPHONE, &max);
+		kctl->private_value &= ~(0xff << 16);
+		kctl->private_value |= (int)max << 16;
+		snd_ac97_write_cache(ac97, AC97_HEADPHONE, 0x8000 | max | (max << 8));
+	}
+	
+	/* build master mono controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master_mono[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master_mono[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params1(ac97, AC97_MASTER_MONO, &max);
+		kctl->private_value &= ~(0xff << 16);
+		kctl->private_value |= (int)max << 16;
+		snd_ac97_write_cache(ac97, AC97_MASTER_MONO, 0x8000 | max);
+	}
+	
+	/* build master tone controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0)
+				return err;
+		snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f);
+	}
+	
+	/* build PC Speaker controls */
+	if ((ac97->flags & AC97_HAS_PC_BEEP) ||
+	    snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP)) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+				return err;
+		snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e);
+	}
+	
+	/* build Phone controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_phone[idx], ac97))) < 0)
+				return err;
+		snd_ac97_write_cache(ac97, AC97_PHONE, 0x801f);
+	}
+	
+	/* build MIC controls */
+	for (idx = 0; idx < 3; idx++)
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic[idx], ac97))) < 0)
+			return err;
+	snd_ac97_write_cache(ac97, AC97_MIC, 0x801f);
+
+	/* build Line controls */
+	for (idx = 0; idx < 2; idx++)
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_line[idx], ac97))) < 0)
+			return err;
+	snd_ac97_write_cache(ac97, AC97_LINE, 0x9f1f);
+	
+	/* build CD controls */
+	for (idx = 0; idx < 2; idx++)
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_cd[idx], ac97))) < 0)
+			return err;
+	snd_ac97_write_cache(ac97, AC97_CD, 0x9f1f);
+	
+	/* build Video controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_video[idx], ac97))) < 0)
+				return err;
+		snd_ac97_write_cache(ac97, AC97_VIDEO, 0x9f1f);
+	}
+
+	/* build Aux controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_aux[idx], ac97))) < 0)
+				return err;
+		snd_ac97_write_cache(ac97, AC97_AUX, 0x9f1f);
+	}
+
+	/* build PCM controls */
+	if (ac97->flags & AC97_AD_MULTI) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+				return err;
+		ac97->spec.ad18xx.pcmreg[0] = 0x9f1f;
+		if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
+			for (idx = 0; idx < 2; idx++)
+				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+					return err;
+			ac97->spec.ad18xx.pcmreg[1] = 0x9f1f;
+		}
+		if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
+			for (idx = 0; idx < 2; idx++)
+				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+					return err;
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[0], ac97))) < 0)
+				return err;
+			ac97->spec.ad18xx.pcmreg[2] = 0x9f1f;
+		}
+	} else {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pcm[idx], ac97))) < 0)
+				return err;
+	}
+	snd_ac97_write_cache(ac97, AC97_PCM, 0x9f1f);
+
+	/* build Capture controls */
+	for (idx = 0; idx < 3; idx++)
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_capture[idx], ac97))) < 0)
+			return err;
+	snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
+	snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
+
+	/* build MIC Capture controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+				return err;
+		snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
+	}
+
+	/* build PCM out path & mute control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0)
+			return err;
+	}
+
+	/* build Simulated Stereo Enhancement control */
+	if (ac97->caps & 0x0008) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
+			return err;
+	}
+
+	/* build 3D Stereo Enhancement control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0)
+			return err;
+	}
+
+	/* build Loudness control */
+	if (ac97->caps & 0x0020) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
+			return err;
+	}
+
+	/* build Mono output select control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0)
+			return err;
+	}
+
+	/* build Mic select control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0)
+			return err;
+	}
+
+	/* build ADC/DAC loopback control */
+	if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0)
+			return err;
+	}
+
+	snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x0000);
+
+	/* build 3D controls */
+	switch (ac97->id) {
+	case AC97_ID_STAC9708:
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+			return err;
+		strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+		kctl->private_value = AC97_3D_CONTROL | (3 << 16);
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+			return err;
+		strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
+		kctl->private_value = AC97_3D_CONTROL | (2 << 8) | (3 << 16);
+		snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+		break;
+	case AC97_ID_STAC9700:
+	case AC97_ID_STAC9721:
+	case AC97_ID_STAC9744:
+	case AC97_ID_STAC9756:
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+			return err;
+		strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+		kctl->private_value = AC97_3D_CONTROL | (3 << 16);
+		snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+		break;
+	default:
+		if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) {
+			unsigned short val;
+			val = 0x0707;
+			snd_ac97_write(ac97, AC97_3D_CONTROL, val);
+			val = snd_ac97_read(ac97, AC97_3D_CONTROL);
+			val = val == 0x0606;
+			if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+				return err;
+			if (val)
+				kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
+			if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0)
+				return err;
+			if (val)
+				kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16);
+			snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+		}
+	}
+	
+	/* build S/PDIF controls */
+	if (ac97->ext_id & 0x0004) {
+		for (idx = 0; idx < 5; idx++)
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+				return err;
+		/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original */
+		snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+		ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+	}
+	
+	/* build Sigmatel specific controls */
+	switch (ac97->id) {
+	case AC97_ID_STAC9700:
+	case AC97_ID_STAC9708:
+	case AC97_ID_STAC9721:
+	case AC97_ID_STAC9744:
+	case AC97_ID_STAC9756:
+		snd_ac97_write_cache_test(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003);
+		if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1))
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[0], ac97))) < 0)
+				return err;
+		if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0))
+			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[1], ac97))) < 0)
+				return err;
+	default:
+		/* nothing */
+		break;
+	}
+
+	if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate)
+{
+	unsigned short val;
+	unsigned int tmp;
+
+	tmp = ((unsigned int)rate * ac97->clock) / 48000;
+	snd_ac97_write_cache_test(ac97, reg, tmp & 0xffff);
+	val = snd_ac97_read(ac97, reg);
+	return val == (tmp & 0xffff);
+}
+
+static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result)
+{
+	unsigned int result = 0;
+
+	/* test a non-standard rate */
+	if (snd_ac97_test_rate(ac97, reg, 11000))
+		result |= SNDRV_PCM_RATE_CONTINUOUS;
+	/* let's try to obtain standard rates */
+	if (snd_ac97_test_rate(ac97, reg, 8000))
+		result |= SNDRV_PCM_RATE_8000;
+	if (snd_ac97_test_rate(ac97, reg, 11025))
+		result |= SNDRV_PCM_RATE_11025;
+	if (snd_ac97_test_rate(ac97, reg, 16000))
+		result |= SNDRV_PCM_RATE_16000;
+	if (snd_ac97_test_rate(ac97, reg, 22050))
+		result |= SNDRV_PCM_RATE_22050;
+	if (snd_ac97_test_rate(ac97, reg, 32000))
+		result |= SNDRV_PCM_RATE_32000;
+	if (snd_ac97_test_rate(ac97, reg, 44100))
+		result |= SNDRV_PCM_RATE_44100;
+	if (snd_ac97_test_rate(ac97, reg, 48000))
+		result |= SNDRV_PCM_RATE_48000;
+	*r_result = result;
+}
+
+static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name)
+{
+	const ac97_codec_id_t *pid;
+
+	sprintf(name, "0x%x %c%c%c", id,
+		printable(id >> 24),
+		printable(id >> 16),
+		printable(id >> 8));
+	for (pid = snd_ac97_codec_id_vendors; pid->id; pid++)
+		if (pid->id == (id & pid->mask)) {
+			strcpy(name, pid->name);
+			if (ac97 && pid->patch)
+				pid->patch(ac97);
+			goto __vendor_ok;
+		}
+	return;
+
+      __vendor_ok:
+	for (pid = snd_ac97_codec_ids; pid->id; pid++)
+		if (pid->id == (id & pid->mask)) {
+			strcat(name, " ");
+			strcat(name, pid->name);
+			if (pid->mask != 0xffffffff)
+				sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+			if (ac97 && pid->patch)
+				pid->patch(ac97);
+			return;
+		}
+	sprintf(name + strlen(name), " (%x)", id & 0xff);
+}
+
+int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97)
+{
+	int err;
+	ac97_t *ac97;
+	char name[64];
+	signed long end_time;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_ac97_dev_free,
+	};
+
+	snd_assert(rac97 != NULL, return -EINVAL);
+	*rac97 = NULL;
+	snd_assert(card != NULL && _ac97 != NULL, return -EINVAL);
+	ac97 = snd_magic_kcalloc(ac97_t, 0, GFP_KERNEL);
+	if (ac97 == NULL)
+		return -ENOMEM;
+	*ac97 = *_ac97;
+	ac97->card = card;
+	spin_lock_init(&ac97->reg_lock);
+	snd_ac97_write(ac97, AC97_RESET, 0);	/* reset to defaults */
+	if (ac97->wait)
+		ac97->wait(ac97);
+	else {
+		udelay(50);
+
+		/* it's necessary to wait awhile until registers are accessible after RESET */
+		/* because the PCM or MASTER volume registers can be modified, */
+		/* the REC_GAIN register is used for tests */
+		end_time = jiffies + HZ;
+		do {
+			/* use preliminary reads to settle the communication */
+			snd_ac97_read(ac97, AC97_RESET);
+			snd_ac97_read(ac97, AC97_VENDOR_ID1);
+			snd_ac97_read(ac97, AC97_VENDOR_ID2);
+			/* test if we can write to the PCM volume register */
+			snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
+			if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
+				goto __access_ok;
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/100);
+		} while (end_time - (signed long)jiffies >= 0);
+		snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err);
+		snd_ac97_free(ac97);
+		return -ENXIO;
+	}
+      __access_ok:
+	ac97->caps = snd_ac97_read(ac97, AC97_RESET);
+	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+	if (ac97->ext_id == 0xffff)	/* invalid combination */
+		ac97->ext_id = 0;
+	if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) {
+		snd_printk("AC'97 %d:%d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->addr, ac97->id);
+		snd_ac97_free(ac97);
+		return -EIO;
+	}
+	/* FIXME: add powerdown control */
+	/* nothing should be in powerdown mode */
+	snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0);
+	snd_ac97_write_cache_test(ac97, AC97_RESET, 0);		/* reset to defaults */
+	udelay(100);
+	/* nothing should be in powerdown mode */
+	snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0);
+	snd_ac97_write_cache_test(ac97, AC97_GENERAL_PURPOSE, 0);
+	end_time = jiffies + (HZ / 10);
+	do {
+		if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
+			goto __ready_ok;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/10);
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr);
+      __ready_ok:
+	if (ac97->clock == 0)
+		ac97->clock = 48000;	/* standard value */
+	if (ac97->ext_id & 0x0189)	/* L/R, MIC, SDAC, LDAC VRA support */
+		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);
+	if (ac97->ext_id & 0x0001) {	/* VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac);
+		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc);
+	} else {
+		ac97->rates_front_dac = SNDRV_PCM_RATE_48000;
+		ac97->rates_adc = SNDRV_PCM_RATE_48000;
+	}
+	if (ac97->ext_id & 0x0008) {	/* MIC VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc);
+	} else {
+		ac97->rates_mic_adc = SNDRV_PCM_RATE_48000;
+	}
+	if (ac97->ext_id & 0x0080) {	/* SDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac);
+		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
+	}
+	if (ac97->ext_id & 0x0100) {	/* LDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac);
+		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
+	}
+	if (ac97->init)
+		ac97->init(ac97);
+	snd_ac97_get_name(ac97, ac97->id, name);
+	snd_ac97_get_name(NULL, ac97->id, name);  // ac97->id might be changed in the special setup code
+	if (card->mixername[0] == '\0') {
+		strcpy(card->mixername, name);
+	} else {
+		if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
+			strcat(card->mixername, ",");
+			strcat(card->mixername, name);
+		}
+	}
+	if ((err = snd_component_add(card, "AC97")) < 0) {
+		snd_ac97_free(ac97);
+		return err;
+	}
+	if (snd_ac97_mixer_build(card, ac97) < 0) {
+		snd_ac97_free(ac97);
+		return -ENOMEM;
+	}
+	snd_ac97_proc_init(card, ac97);
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) {
+		snd_ac97_free(ac97);
+		return err;
+	}
+	*rac97 = ac97;
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx)
+{
+	char name[64];
+	unsigned int id;
+	unsigned short val, tmp, ext;
+	static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" };
+	static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" };
+
+	id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+	id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	snd_ac97_get_name(NULL, id, name);
+	snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
+	val = snd_ac97_read(ac97, AC97_RESET);
+	snd_iprintf(buffer, "Capabilities     :%s%s%s%s%s%s\n",
+	    	    val & 0x0001 ? " -dedicated MIC PCM IN channel-" : "",
+		    val & 0x0002 ? " -reserved1-" : "",
+		    val & 0x0004 ? " -bass & treble-" : "",
+		    val & 0x0008 ? " -simulated stereo-" : "",
+		    val & 0x0010 ? " -headphone out-" : "",
+		    val & 0x0020 ? " -loudness-" : "");
+	tmp = ac97->caps & 0x00c0;
+	snd_iprintf(buffer, "DAC resolution   : %s%s%s%s\n",
+		    tmp == 0x0000 ? "16-bit" : "",
+		    tmp == 0x0040 ? "18-bit" : "",
+		    tmp == 0x0080 ? "20-bit" : "",
+		    tmp == 0x00c0 ? "???" : "");
+	tmp = ac97->caps & 0x0300;
+	snd_iprintf(buffer, "ADC resolution   : %s%s%s%s\n",
+		    tmp == 0x0000 ? "16-bit" : "",
+		    tmp == 0x0100 ? "18-bit" : "",
+		    tmp == 0x0200 ? "20-bit" : "",
+		    tmp == 0x0300 ? "???" : "");
+	snd_iprintf(buffer, "3D enhancement   : %s\n",
+		snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]);
+	snd_iprintf(buffer, "\nCurrent setup\n");
+	val = snd_ac97_read(ac97, AC97_MIC);
+	snd_iprintf(buffer, "Mic gain         : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB");
+	val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE);
+	snd_iprintf(buffer, "POP path         : %s 3D\n"
+		    "Sim. stereo      : %s\n"
+		    "3D enhancement   : %s\n"
+		    "Loudness         : %s\n"
+		    "Mono output      : %s\n"
+		    "Mic select       : %s\n"
+		    "ADC/DAC loopback : %s\n",
+		    val & 0x8000 ? "post" : "pre",
+		    val & 0x4000 ? "on" : "off",
+		    val & 0x2000 ? "on" : "off",
+		    val & 0x1000 ? "on" : "off",
+		    val & 0x0200 ? "Mic" : "MIX",
+		    val & 0x0100 ? "Mic2" : "Mic1",
+		    val & 0x0080 ? "on" : "off");
+	ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+	if (ext == 0)
+		return;
+	snd_iprintf(buffer, "Extended ID      : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n",
+			(ext >> 14) & 3,
+			(ext >> 10) & 3,
+			ext & 0x0200 ? " AMAP" : "",
+			ext & 0x0100 ? " LDAC" : "",
+			ext & 0x0080 ? " SDAC" : "",
+			ext & 0x0040 ? " CDAC" : "",
+			(ext >> 4) & 3,
+			ext & 0x0008 ? " VRM" : "",
+			ext & 0x0004 ? " SPDIF" : "",
+			ext & 0x0002 ? " DRA" : "",
+			ext & 0x0001 ? " VRA" : "");
+	val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+	snd_iprintf(buffer, "Extended status  :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+			val & 0x4000 ? " PRL" : "",
+			val & 0x2000 ? " PRK" : "",
+			val & 0x1000 ? " PRJ" : "",
+			val & 0x0800 ? " PRI" : "",
+			val & 0x0400 ? " SPCV" : "",
+			val & 0x0200 ? " MADC" : "",
+			val & 0x0100 ? " LDAC" : "",
+			val & 0x0080 ? " SDAC" : "",
+			val & 0x0040 ? " CDAC" : "",
+			ext & 0x0004 ? spdif_slots[(val & 0x0030) >> 4] : "",
+			val & 0x0008 ? " VRM" : "",
+			val & 0x0004 ? " SPDIF" : "",
+			val & 0x0002 ? " DRA" : "",
+			val & 0x0001 ? " VRA" : "");
+	if (ext & 1) {	/* VRA */
+		val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE);
+		snd_iprintf(buffer, "PCM front DAC    : %iHz\n", val);
+		if (ext & 0x0080) {
+			val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE);
+			snd_iprintf(buffer, "PCM Surr DAC     : %iHz\n", val);
+		}
+		if (ext & 0x0100) {
+			val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE);
+			snd_iprintf(buffer, "PCM LFE DAC      : %iHz\n", val);
+		}
+		val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE);
+		snd_iprintf(buffer, "PCM ADC          : %iHz\n", val);
+	}
+	if (ext & 0x0008) {
+		val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE);
+		snd_iprintf(buffer, "PCM MIC ADC      : %iHz\n", val);
+	}
+	if (ext & 0x0004) {
+		val = snd_ac97_read(ac97, AC97_SPDIF);
+		snd_iprintf(buffer, "SPDIF Control    :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n",
+			val & 0x0001 ? " PRO" : " Consumer",
+			val & 0x0002 ? " Non-audio" : " PCM",
+			val & 0x0004 ? " Copyright" : "",
+			val & 0x0008 ? " Preemph50/15" : "",
+			(val & 0x07f0) >> 4,
+			(val & 0x0800) >> 11,
+			spdif_rates[(val & 0x3000) >> 12],
+			val & 0x4000 ? " DRS" : "",
+			val & 0x8000 ? " Validity" : "");
+	}
+}
+
+static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return);
+	
+	if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {	// Analog Devices AD1881/85/86
+		int idx;
+		down(&ac97->spec.ad18xx.mutex);
+		for (idx = 0; idx < 3; idx++)
+			if (ac97->spec.ad18xx.id[idx]) {
+				/* select single codec */
+				snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]);
+				snd_ac97_proc_read_main(ac97, buffer, idx);
+				snd_iprintf(buffer, "\n\n");
+			}
+		/* select all codecs */
+		snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000);
+		up(&ac97->spec.ad18xx.mutex);
+		
+		snd_iprintf(buffer, "\nAD18XX configuration\n");
+		snd_iprintf(buffer, "Unchained        : 0x%04x,0x%04x,0x%04x\n",
+			ac97->spec.ad18xx.unchained[0],
+			ac97->spec.ad18xx.unchained[1],
+			ac97->spec.ad18xx.unchained[2]);
+		snd_iprintf(buffer, "Chained          : 0x%04x,0x%04x,0x%04x\n",
+			ac97->spec.ad18xx.chained[0],
+			ac97->spec.ad18xx.chained[1],
+			ac97->spec.ad18xx.chained[2]);
+	} else {
+		snd_ac97_proc_read_main(ac97, buffer, 0);
+	}
+}
+
+static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx)
+{
+	int reg, val;
+
+	for (reg = 0; reg < 0x80; reg += 2) {
+		val = snd_ac97_read(ac97, reg);
+		snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val);
+	}
+}
+
+static void snd_ac97_proc_regs_read(snd_info_entry_t *entry, 
+				    snd_info_buffer_t * buffer)
+{
+	ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return);
+
+	if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {	// Analog Devices AD1881/85/86
+		int idx;
+		down(&ac97->spec.ad18xx.mutex);
+		for (idx = 0; idx < 3; idx++)
+			if (ac97->spec.ad18xx.id[idx]) {
+				/* select single codec */
+				snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]);
+				snd_ac97_proc_regs_read_main(ac97, buffer, idx);
+			}
+		/* select all codecs */
+		snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000);
+		up(&ac97->spec.ad18xx.mutex);
+	} else {
+		snd_ac97_proc_regs_read_main(ac97, buffer, 0);
+	}	
+}
+
+static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97)
+{
+	snd_info_entry_t *entry;
+	char name[12];
+
+	sprintf(name, "ac97#%d", ac97->addr);
+	if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ac97;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = snd_ac97_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ac97->proc_entry = entry;
+	sprintf(name, "ac97#%dregs", ac97->addr);
+	if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ac97;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 1024;
+		entry->c.text.read = snd_ac97_proc_regs_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ac97->proc_regs_entry = entry;
+}
+
+static void snd_ac97_proc_done(ac97_t * ac97)
+{
+	if (ac97->proc_regs_entry) {
+		snd_info_unregister(ac97->proc_regs_entry);
+		ac97->proc_regs_entry = NULL;
+	}
+	if (ac97->proc_entry) {
+		snd_info_unregister(ac97->proc_entry);
+		ac97->proc_entry = NULL;
+	}
+}
+
+/*
+ *  Chip specific initialization
+ */
+
+static int patch_wolfson(ac97_t * ac97)
+{
+	// for all wolfson codecs (is this correct? --jk)
+	snd_ac97_write_cache(ac97, 0x72, 0x0808);
+	snd_ac97_write_cache(ac97, 0x74, 0x0808);
+
+	// patch for DVD noise
+	snd_ac97_write_cache(ac97, 0x5a, 0x0200);
+
+	// init vol
+	snd_ac97_write_cache(ac97, 0x70, 0x0808);
+
+	snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
+	return 0;
+}
+
+static int patch_tritech_tr28028(ac97_t * ac97)
+{
+	snd_ac97_write_cache(ac97, 0x26, 0x0300);
+	snd_ac97_write_cache(ac97, 0x26, 0x0000);
+	snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
+	snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000);
+	return 0;
+}
+
+static int patch_sigmatel_stac9708(ac97_t * ac97)
+{
+	unsigned int codec72, codec6c;
+
+	codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000;
+	codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG);
+
+	if ((codec72==0) && (codec6c==0)) {
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007);
+	} else if ((codec72==0x8000) && (codec6c==0)) {
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008);
+	} else if ((codec72==0x8000) && (codec6c==0x0080)) {
+		/* nothing */
+	}
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+static int patch_sigmatel_stac9721(ac97_t * ac97)
+{
+	if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) {
+		// patch for SigmaTel
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+	}
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+static int patch_sigmatel_stac9744(ac97_t * ac97)
+{
+	// patch for SigmaTel
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000);	/* is this correct? --jk */
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+static int patch_sigmatel_stac9756(ac97_t * ac97)
+{
+	// patch for SigmaTel
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000);	/* is this correct? --jk */
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+static int patch_cirrus_cs4299(ac97_t * ac97)
+{
+	ac97->flags |= AC97_HAS_PC_BEEP;	/* force the detection of PC Beep */
+	return 0;
+}
+
+static int patch_ad1819(ac97_t * ac97)
+{
+	// patch for Analog Devices
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); /* select all codecs */
+	return 0;
+}
+
+static unsigned short patch_ad1881_unchained(ac97_t * ac97, int idx, unsigned short mask)
+{
+	unsigned short val;
+
+	// test for unchained codec
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, mask);
+	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);	/* ID0C, ID1C, SDIE = off */
+	val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if ((val & 0xff40) != 0x5340)
+		return 0;
+	ac97->spec.ad18xx.unchained[idx] = mask;
+	ac97->spec.ad18xx.id[idx] = val;
+	return mask;
+}
+
+static int patch_ad1881_chained1(ac97_t * ac97, int idx, unsigned short codec_bits)
+{
+	static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
+	unsigned short val;
+	
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, cfg_bits[idx]);
+	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004);	// SDIE
+	val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if ((val & 0xff40) != 0x5340)
+		return 0;
+	if (codec_bits)
+		snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits);
+	ac97->spec.ad18xx.chained[idx] = cfg_bits[idx];
+	ac97->spec.ad18xx.id[idx] = val;
+	return 1;
+}
+
+static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, int cidx2)
+{
+	// already detected?
+	if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1])
+		cidx1 = -1;
+	if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2])
+		cidx2 = -1;
+	if (cidx1 < 0 && cidx2 < 0)
+		return;
+	// test for chained codecs
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[unchained_idx]);
+	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002);		// ID1C
+	if (cidx1 >= 0) {
+		if (patch_ad1881_chained1(ac97, cidx1, 0x0006))		// SDIE | ID1C
+			patch_ad1881_chained1(ac97, cidx2, 0);
+		else if (patch_ad1881_chained1(ac97, cidx2, 0x0006))	// SDIE | ID1C
+			patch_ad1881_chained1(ac97, cidx1, 0);
+	} else if (cidx2 >= 0) {
+		patch_ad1881_chained1(ac97, cidx2, 0);
+	}
+}
+
+static int patch_ad1881(ac97_t * ac97)
+{
+	static const char cfg_idxs[3][2] = {
+		{2, 1},
+		{0, 2},
+		{0, 1}
+	};
+	
+	// patch for Analog Devices
+	unsigned short codecs[3];
+	int idx, num;
+
+	init_MUTEX(&ac97->spec.ad18xx.mutex);
+
+	codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12));
+	codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14));
+	codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13));
+
+	snd_runtime_check(codecs[0] | codecs[1] | codecs[2], goto __end);
+
+	for (idx = 0; idx < 3; idx++)
+		if (ac97->spec.ad18xx.unchained[idx])
+			patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]);
+
+	if (ac97->spec.ad18xx.id[1]) {
+		ac97->flags |= AC97_AD_MULTI;
+		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
+	}
+	if (ac97->spec.ad18xx.id[2]) {
+		ac97->flags |= AC97_AD_MULTI;
+		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
+	}
+
+      __end:
+	/* select all codecs */
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000);
+	/* check if only one codec is present */
+	for (idx = num = 0; idx < 3; idx++)
+		if (ac97->spec.ad18xx.id[idx])
+			num++;
+	if (num == 1) {
+		/* ok, deselect all ID bits */
+		snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);
+	}
+	/* required for AD1886/AD1885 combination */
+	ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+	if (ac97->spec.ad18xx.id[0]) {
+		ac97->id &= 0xffff0000;
+		ac97->id |= ac97->spec.ad18xx.id[0];
+	}
+	return 0;
+}
+
+/*
+ *  PCM support
+ */
+
+int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate)
+{
+	unsigned short mask;
+	unsigned int tmp;
+	signed long end_time;
+	
+	switch (reg) {
+	case AC97_PCM_MIC_ADC_RATE:
+		mask = 0x0000;
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0008) == 0)	/* MIC VRA */
+			if (rate != 48000)
+				return -EINVAL;
+		break;
+	case AC97_PCM_FRONT_DAC_RATE:
+		mask = 0x0200;
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0)	/* VRA */
+			if (rate != 48000)
+				return -EINVAL;
+		break;
+	case AC97_PCM_LR_ADC_RATE:
+		mask = 0x0100;
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0)	/* VRA */
+			if (rate != 48000)
+				return -EINVAL;
+		break;
+	default: return -EINVAL;
+	}
+	tmp = ((unsigned int)rate * ac97->clock) / 48000;
+	if (tmp > 65535)
+		return -EINVAL;
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, mask);
+	end_time = jiffies + (HZ / 50);
+	do {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_ac97_update(ac97, reg, tmp & 0xffff);
+	udelay(10);
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, 0);
+	end_time = jiffies + (HZ / 50);
+	do {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (end_time - (signed long)jiffies >= 0);
+	end_time = jiffies + (HZ / 10);
+	do {
+		if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0003) == 0x0003)
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (end_time - (signed long)jiffies >= 0);
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * general suspend procedure
+ */
+void snd_ac97_suspend(ac97_t *ac97)
+{
+	unsigned short power = (ac97->regs[AC97_POWERDOWN] ^ 0x8000) & ~0x8000;	/* invert EAPD */
+
+	power |= 0x4000;	/* Headphone amplifier powerdown */
+	power |= 0x0300;	/* ADC & DAC powerdown */
+	snd_ac97_write(ac97, AC97_POWERDOWN, power);
+	udelay(100);
+	power |= 0x0400;	/* Analog Mixer powerdown (Vref on) */
+	snd_ac97_write(ac97, AC97_POWERDOWN, power);
+	udelay(100);
+	power |= 0x3800;	/* AC-link powerdown, internal Clk disable */
+	snd_ac97_write(ac97, AC97_POWERDOWN, power);
+}
+
+/*
+ * general resume procedure
+ */
+void snd_ac97_resume(ac97_t *ac97)
+{
+	int i;
+
+	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+	snd_ac97_write(ac97, AC97_RESET, 0);
+	udelay(100);
+	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+	snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
+
+	snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
+	snd_ac97_write(ac97, AC97_MASTER, 0x8000);
+	for (i = 0; i < 10; i++) {
+		if (snd_ac97_read(ac97, AC97_MASTER) == 0x8000)
+			break;
+		mdelay(1);
+	}
+
+	if (ac97->init)
+		ac97->init(ac97);
+
+	/* restore ac97 status */
+	for (i = 2; i < 0x7c ; i += 2) {
+		if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
+			continue;
+		/* restore only accessible registers
+		 * some chip (e.g. nm256) may hang up when unsupported registers
+		 * are accessed..!
+		 */
+		if (test_bit(i, ac97->reg_accessed))
+			snd_ac97_write(ac97, i, ac97->regs[i]);
+	}
+}
+#endif
+
+
+/*
+ *  Exported symbols
+ */
+
+EXPORT_SYMBOL(snd_ac97_write);
+EXPORT_SYMBOL(snd_ac97_read);
+EXPORT_SYMBOL(snd_ac97_write_cache);
+EXPORT_SYMBOL(snd_ac97_update);
+EXPORT_SYMBOL(snd_ac97_update_bits);
+EXPORT_SYMBOL(snd_ac97_mixer);
+EXPORT_SYMBOL(snd_ac97_set_rate);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_ac97_resume);
+#endif
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_ac97_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ac97_exit(void)
+{
+}
+
+module_init(alsa_ac97_init)
+module_exit(alsa_ac97_exit)
diff -Nru linux/sound/pci/ac97/ak4531_codec.c linux-2.4.19-pre5-mjc/sound/pci/ac97/ak4531_codec.c
--- linux/sound/pci/ac97/ak4531_codec.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ac97/ak4531_codec.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,466 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal routines for AK4531 codec
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ak4531_codec.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Universal routines for AK4531 codec");
+MODULE_LICENSE("GPL");
+
+#define chip_t ak4531_t
+
+static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531);
+static void snd_ak4531_proc_done(ak4531_t * ak4531);
+
+/*
+ *
+ */
+ 
+#if 0
+
+static void snd_ak4531_dump(ak4531_t *ak4531)
+{
+	int idx;
+	
+	for (idx = 0; idx < 0x19; idx++)
+		printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]);
+}
+
+#endif
+
+/*
+ *
+ */
+
+#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ak4531_info_single, \
+  get: snd_ak4531_get_single, put: snd_ak4531_put_single, \
+  private_value: reg | (shift << 16) | (mask << 24) | (invert << 22) }
+
+static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+ 
+static int snd_ak4531_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int val;
+
+	spin_lock_irqsave(&ak4531->reg_lock, flags);
+	val = (ak4531->regs[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
+	if (invert) {
+		val = mask - val;
+	}
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}                                                                                                                                                                                                                                                                                                            
+
+static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	int val;
+
+	val = ucontrol->value.integer.value[0] & mask;
+	if (invert) {
+		val = mask - val;
+	}
+	val <<= shift;
+	spin_lock_irqsave(&ak4531->reg_lock, flags);
+	val = (ak4531->regs[reg] & ~(mask << shift)) | val;
+	change = val != ak4531->regs[reg];
+	ak4531->write(ak4531, reg, ak4531->regs[reg] = val);
+	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
+	return change;
+}                                                                                                                                                                                                                                                                                                            
+
+#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ak4531_info_double, \
+  get: snd_ak4531_get_double, put: snd_ak4531_put_double, \
+  private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+ 
+static int snd_ak4531_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int left, right;
+
+	spin_lock_irqsave(&ak4531->reg_lock, flags);
+	left = (ak4531->regs[left_reg] >> left_shift) & mask;
+	right = (ak4531->regs[right_reg] >> right_shift) & mask;
+	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
+	if (invert) {
+		left = mask - left;
+		right = mask - right;
+	}
+	ucontrol->value.integer.value[0] = left;
+	ucontrol->value.integer.value[1] = right;
+	return 0;
+}                                                                                                                                                                                                                                                                                                            
+
+static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	int left, right;
+
+	left = ucontrol->value.integer.value[0] & mask;
+	right = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		left = mask - left;
+		right = mask - right;
+	}
+	left <<= left_shift;
+	right <<= right_shift;
+	spin_lock_irqsave(&ak4531->reg_lock, flags);
+	if (left_reg == right_reg) {
+		left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right;
+		change = left != ak4531->regs[left_reg];
+		ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
+	} else {
+		left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left;
+		right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right;
+		change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg];
+		ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
+		ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right);
+	}
+	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
+	return change;
+}                                                                                                                                                                                                                                                                                                            
+
+#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ak4531_info_input_sw, \
+  get: snd_ak4531_get_input_sw, put: snd_ak4531_put_input_sw, \
+  private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
+
+static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+ 
+static int snd_ak4531_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+
+	spin_lock_irqsave(&ak4531->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1;
+	ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1;
+	ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1;
+	ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1;
+	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
+	return 0;
+}                                                                                                                                                                                                                                                                                                            
+
+static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+	int change;
+	int val1, val2;
+
+	spin_lock_irqsave(&ak4531->reg_lock, flags);
+	val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift));
+	val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift));
+	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
+	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
+	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
+	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
+	change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2];
+	ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1);
+	ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2);
+	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
+	return change;
+}                                                                                                                                                                                                                                                                                                            
+
+#define AK4531_CONTROLS (sizeof(snd_ak4531_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ak4531_controls[] = {
+
+AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1),
+AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
+
+AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1),
+AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
+
+AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
+AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
+AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
+
+AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
+AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
+AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
+
+AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
+AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
+AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
+
+AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
+AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
+AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
+
+AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
+AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
+AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
+
+AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
+AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1),
+AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
+AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
+
+AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
+AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1),
+AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
+AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
+
+AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1),
+AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
+AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
+AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),
+
+AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0),
+AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0),
+AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0),
+
+AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0),
+AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0)
+};
+
+static int snd_ak4531_free(ak4531_t *ak4531)
+{
+	if (ak4531) {
+		snd_ak4531_proc_done(ak4531);
+		if (ak4531->private_free)
+			ak4531->private_free(ak4531);
+		snd_magic_kfree(ak4531);
+	}
+	return 0;
+}
+
+static int snd_ak4531_dev_free(snd_device_t *device)
+{
+	ak4531_t *ak4531 = snd_magic_cast(ak4531_t, device->device_data, return -ENXIO);
+	return snd_ak4531_free(ak4531);
+}
+
+static u8 snd_ak4531_initial_map[0x19 + 1] = {
+	0x9f,		/* 00: Master Volume Lch */
+	0x9f,		/* 01: Master Volume Rch */
+	0x9f,		/* 02: Voice Volume Lch */
+	0x9f,		/* 03: Voice Volume Rch */
+	0x9f,		/* 04: FM Volume Lch */
+	0x9f,		/* 05: FM Volume Rch */
+	0x9f,		/* 06: CD Audio Volume Lch */
+	0x9f,		/* 07: CD Audio Volume Rch */
+	0x9f,		/* 08: Line Volume Lch */
+	0x9f,		/* 09: Line Volume Rch */
+	0x9f,		/* 0a: Aux Volume Lch */
+	0x9f,		/* 0b: Aux Volume Rch */
+	0x9f,		/* 0c: Mono1 Volume */
+	0x9f,		/* 0d: Mono2 Volume */
+	0x9f,		/* 0e: Mic Volume */
+	0x87,		/* 0f: Mono-out Volume */
+	0x00,		/* 10: Output Mixer SW1 */
+	0x00,		/* 11: Output Mixer SW2 */
+	0x00,		/* 12: Lch Input Mixer SW1 */
+	0x00,		/* 13: Rch Input Mixer SW1 */
+	0x00,		/* 14: Lch Input Mixer SW2 */
+	0x00,		/* 15: Rch Input Mixer SW2 */
+	0x00,		/* 16: Reset & Power Down */
+	0x00,		/* 17: Clock Select */
+	0x00,		/* 18: AD Input Select */
+	0x01		/* 19: Mic Amp Setup */
+};
+
+int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531)
+{
+	int idx, err;
+	ak4531_t * ak4531;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_ak4531_dev_free,
+	};
+
+	snd_assert(rak4531 != NULL, return -EINVAL);
+	*rak4531 = NULL;
+	snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL);
+	ak4531 = snd_magic_kcalloc(ak4531_t, 0, GFP_KERNEL);
+	if (ak4531 == NULL)
+		return -ENOMEM;
+	*ak4531 = *_ak4531;
+	spin_lock_init(&ak4531->reg_lock);
+	if ((err = snd_component_add(card, "AK4531")) < 0) {
+		snd_ak4531_free(ak4531);
+		return err;
+	}
+	strcpy(card->mixername, "Asahi Kasei AK4531");
+	ak4531->write(ak4531, AK4531_RESET, 0x03);	/* no RST, PD */
+	udelay(100);
+	ak4531->write(ak4531, AK4531_CLOCK, 0x00);	/* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
+	for (idx = 0; idx < 0x19; idx++) {
+		if (idx == AK4531_RESET || idx == AK4531_CLOCK)
+			continue;
+		ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]);	/* recording source is mixer */
+	}
+	for (idx = 0; idx < AK4531_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) {
+			snd_ak4531_free(ak4531);
+			return err;
+		}
+	}
+	snd_ak4531_proc_init(card, ak4531);
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ak4531, &ops)) < 0) {
+		snd_ak4531_free(ak4531);
+		return err;
+	}
+
+#if 0
+	snd_ak4531_dump(ak4531);
+#endif
+	*rak4531 = ak4531;
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_ak4531_proc_read(snd_info_entry_t *entry, 
+				 snd_info_buffer_t * buffer)
+{
+	ak4531_t *ak4531 = snd_magic_cast(ak4531_t, entry->private_data, return);
+
+	snd_iprintf(buffer, "Asahi Kasei AK4531\n\n");
+	snd_iprintf(buffer, "Recording source   : %s\n"
+		    "MIC gain           : %s\n",
+		    ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer",
+		    ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
+}
+
+static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(card, "ak4531", card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ak4531;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_ak4531_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ak4531->proc_entry = entry;
+}
+
+static void snd_ak4531_proc_done(ak4531_t * ak4531)
+{
+	if (ak4531->proc_entry) {
+		snd_info_unregister(ak4531->proc_entry);
+		ak4531->proc_entry = NULL;
+	}
+}
+
+EXPORT_SYMBOL(snd_ak4531_mixer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_ak4531_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ak4531_exit(void)
+{
+}
+
+module_init(alsa_ak4531_init)
+module_exit(alsa_ak4531_exit)
diff -Nru linux/sound/pci/ali5451/Makefile linux-2.4.19-pre5-mjc/sound/pci/ali5451/Makefile
--- linux/sound/pci/ali5451/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ali5451/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _ali5451.o
+
+list-multi   := snd-ali5451.o
+
+snd-ali5451-objs := ali5451.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ali5451.o: $(snd-ali5451-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ali5451-objs)
diff -Nru linux/sound/pci/ali5451/ali5451.c linux-2.4.19-pre5-mjc/sound/pci/ali5451/ali5451.c
--- linux/sound/pci/ali5451/ali5451.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ali5451/ali5451.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2291 @@
+/*
+ *  Matt Wu <Matt_Wu@acersoftech.com.cn>
+ *  Apr 26, 2001
+ *  Routines for control of ALi pci audio M5451
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public Lcodecnse as published by
+ *   the Free Software Foundation; either version 2 of the Lcodecnse, 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 Lcodecnse for more details.
+ *
+ *   You should have received a copy of the GNU General Public Lcodecnse
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __SNDRV_OSS_COMPAT__
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
+MODULE_DESCRIPTION("ALI M5451");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALI,M5451,pci},{ALI,M5451}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ALI M5451 PCI Audio.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ALI M5451 PCI Audio.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ALI 5451 PCI Audio.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_pcm_channels, "PCM Channels");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}");
+
+/*
+ *  Debug part definations
+ */
+
+//#define ALI_DEBUG
+
+#ifdef ALI_DEBUG
+#define snd_ali_printk(format, args...) printk(format, ##args);
+#else
+#define snd_ali_printk(format, args...)
+#endif
+
+/*
+ *  Constants defination
+ */
+
+#ifndef PCI_VENDOR_ID_ALI
+#define PCI_VENDOR_ID_ALI	0x10b9
+#endif
+
+#ifndef PCI_DEVICE_ID_ALI_5451
+#define PCI_DEVICE_ID_ALI_5451	0x5451
+#endif
+
+#define DEVICE_ID_ALI5451	((PCI_VENDOR_ID_ALI<<16)|PCI_DEVICE_ID_ALI_5451)
+
+
+#define ALI_CHANNELS		32
+
+#define ALI_PCM_IN_CHANNEL	31
+#define ALI_SPDIF_IN_CHANNEL	19
+#define ALI_SPDIF_OUT_CHANNEL	15
+#define ALI_CENTER_CHANNEL	24
+#define ALI_LEF_CHANNEL		23
+#define ALI_SURR_LEFT_CHANNEL	26
+#define ALI_SURR_RIGHT_CHANNEL	25
+
+#define	SNDRV_ALI_VOICE_TYPE_PCM	01
+#define SNDRV_ALI_VOICE_TYPE_OTH	02
+
+#define	ALI_5451_V02		0x02
+
+/*
+ *  Direct Registers
+ */
+
+#define ALI_LEGACY_DMAR0        0x00  // ADR0
+#define ALI_LEGACY_DMAR4        0x04  // CNT0
+#define ALI_LEGACY_DMAR11       0x0b  // MOD 
+#define ALI_LEGACY_DMAR15       0x0f  // MMR 
+#define ALI_MPUR0		0x20
+#define ALI_MPUR1		0x21
+#define ALI_MPUR2		0x22
+#define ALI_MPUR3		0x23
+
+#define	ALI_AC97_WRITE		0x40
+#define ALI_AC97_READ		0x44
+
+#define ALI_SCTRL		0x48
+#define ALI_AC97_GPIO		0x4c
+#define ALI_SPDIF_CS		0x70
+#define ALI_SPDIF_CTRL		0x74
+#define ALI_START		0x80
+#define ALI_STOP		0x84
+#define ALI_CSPF		0x90
+#define ALI_AINT		0x98
+#define ALI_GC_CIR		0xa0
+	#define ENDLP_IE		0x00001000
+	#define MIDLP_IE		0x00002000
+#define ALI_AINTEN		0xa4
+#define ALI_VOLUME		0xa8
+#define ALI_SBDELTA_DELTA_R     0xac
+#define ALI_MISCINT		0xb0
+	#define ADDRESS_IRQ		0x00000020
+	#define TARGET_REACHED		0x00008000
+	#define MIXER_OVERFLOW		0x00000800
+	#define MIXER_UNDERFLOW		0x00000400
+#define ALI_SBBL_SBCL           0xc0
+#define ALI_SBCTRL_SBE2R_SBDD   0xc4
+#define ALI_STIMER		0xc8
+#define ALI_GLOBAL_CONTROL	0xd4
+
+#define ALI_CSO_ALPHA_FMS	0xe0
+#define ALI_LBA			0xe4
+#define ALI_ESO_DELTA		0xe8
+#define ALI_GVSEL_PAN_VOC_CTRL_EC	0xf0
+#define ALI_EBUF1		0xf4
+#define ALI_EBUF2		0xf8
+
+#define ALI_REG(codec, x) ((codec)->port + x)
+
+typedef struct snd_stru_ali ali_t;
+typedef struct snd_ali_stru_voice snd_ali_voice_t;
+#define chip_t ali_t
+
+typedef struct snd_ali_channel_control {
+	// register data
+	struct REGDATA {
+		unsigned int start;
+		unsigned int stop;
+		unsigned int aint;
+		unsigned int ainten;
+	} data;
+		
+	// register addresses
+	struct REGS {
+		unsigned int start;
+		unsigned int stop;
+		unsigned int aint;
+		unsigned int ainten;
+		unsigned int ac97read;
+		unsigned int ac97write;
+	} regs;
+
+} snd_ali_channel_control_t;
+
+struct snd_ali_stru_voice {
+	unsigned int number;
+	int use: 1,
+	    pcm: 1,
+	    midi: 1,
+	    mode: 1,
+	    synth: 1;
+
+	/* PCM data */
+	ali_t *codec;
+	snd_pcm_substream_t *substream;
+	snd_ali_voice_t *extra;
+	
+	int running: 1;
+
+	int eso;                /* final ESO value for channel */
+	int count;              /* runtime->period_size */
+
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(void *private_data);
+};
+
+
+typedef struct snd_stru_alidev {
+
+	snd_ali_voice_t voices[ALI_CHANNELS];	
+
+	unsigned int	chcnt;			/* num of opened channels */
+	unsigned int	chmap;			/* bitmap for opened channels */
+	unsigned int synthcount;
+
+} alidev_t;
+
+
+#ifdef CONFIG_PM
+#define ALI_GLOBAL_REGS		56
+#define ALI_CHANNEL_REGS	8
+typedef struct snd_ali_image {
+	unsigned long regs[ALI_GLOBAL_REGS];
+	unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+} ali_image_t;
+#endif
+
+
+struct snd_stru_ali {
+	unsigned long	irq;
+	unsigned long	port;
+	unsigned char	revision;
+
+	struct resource *res_port;
+
+	struct pci_dev	*pci;
+	struct pci_dev	*pci_m1533;
+	struct pci_dev	*pci_m7101;
+
+	snd_card_t	*card;
+	snd_pcm_t	*pcm;
+	alidev_t	synth;
+	snd_ali_channel_control_t chregs;
+
+	/* S/PDIF Mask */
+	unsigned int	spdif_mask;
+
+	unsigned int spurious_irq_count;
+	unsigned int spurious_irq_max_delta;
+
+	ac97_t *ac97;
+	unsigned short	ac97_ext_id;
+	unsigned short	ac97_ext_status;
+
+	spinlock_t	reg_lock;
+	spinlock_t	voice_alloc;
+
+#ifdef CONFIG_PM
+	ali_image_t *image;
+#endif
+};
+
+static struct pci_device_id snd_ali_ids[] __devinitdata = {
+	{0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ali_ids);
+
+static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int);
+static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short);
+static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short);
+
+/*
+ *  Debug Part
+ */
+
+#ifdef ALI_DEBUG
+
+static void ali_read_regs(ali_t *codec, int channel)
+{
+	int i,j;
+	unsigned int dwVal;
+
+	printk("channel %d registers map:\n", channel);
+	outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR));
+
+	printk("    ");
+	for(j=0;j<8;j++)
+		printk("%2.2x       ", j*4);
+	printk("\n");
+
+	for (i=0; i<=0xf8/4;i++) {
+		if(i%8 == 0)
+			printk("%2.2x  ", (i*4/0x10)*0x10);
+		dwVal = inl(ALI_REG(codec,i*4));
+		printk("%8.8x ", dwVal);
+		if ((i+1)%8 == 0)
+			printk("\n");
+	}
+	printk("\n");
+}
+static void ali_read_cfg(unsigned int vendor, unsigned deviceid)
+{
+	unsigned int dwVal;
+	struct pci_dev *pci_dev = NULL;
+	int i,j;
+
+
+        pci_dev = pci_find_device(vendor, deviceid, pci_dev);
+        if (pci_dev == NULL)
+                return ;
+
+	printk("\nM%x PCI CFG\n", deviceid);
+	printk("    ");
+	for(j=0;j<8;j++)
+		printk("%d        ",j);
+	printk("\n");
+
+	for(i=0;i<8;i++) {
+		printk("%d   ",i);
+		for(j=0;j<8;j++)
+		{
+			pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal);
+			printk("%8.8x ", dwVal);
+		}
+		printk("\n");
+	}
+ }
+static void ali_read_ac97regs(ali_t *codec, int secondary)
+{
+	unsigned short i,j;
+	unsigned short wVal;
+
+	printk("\ncodec %d registers map:\n", secondary);
+
+	printk("    ");
+	for(j=0;j<8;j++)
+		printk("%2.2x   ",j*2);
+	printk("\n");
+
+	for (i=0; i<64;i++) {
+		if(i%8 == 0)
+			printk("%2.2x  ", (i/8)*0x10);
+		wVal = snd_ali_codec_peek(codec, secondary, i*2);
+		printk("%4.4x ", wVal);
+		if ((i+1)%8 == 0)
+			printk("\n");
+	}
+	printk("\n");
+}
+
+#endif
+
+/*
+ *  AC97 ACCESS
+ */
+
+static inline unsigned int snd_ali_5451_peek(ali_t *codec,
+						unsigned int port )
+{
+	return (unsigned int)inl(ALI_REG(codec, port)); 
+}
+
+static inline void snd_ali_5451_poke(	ali_t *codec,
+					unsigned int port,
+					unsigned int val )
+{
+	outl((unsigned int)val, ALI_REG(codec, port));
+}
+
+static int snd_ali_codec_ready(	ali_t *codec,
+				unsigned int port,
+				int sched )
+{
+	signed long end_time;
+	
+	end_time = jiffies + 10 * (HZ >> 2);
+	do {
+		if (!(snd_ali_5451_peek(codec,port) & 0x8000))
+			return 0;
+		if (sched) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		}
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_printk("ali_codec_ready: codec is not ready.\n ");
+	return -EIO;
+}
+
+static int snd_ali_stimer_ready(ali_t *codec, int sched)
+{
+	signed long end_time;
+	unsigned long dwChk1,dwChk2;
+	
+	dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
+	dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+
+	end_time = jiffies + 10 * (HZ >> 2);
+	do {
+		dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+		if (dwChk2 != dwChk1)
+			return 0;
+		if (sched) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		}
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_printk("ali_stimer_read: stimer is not ready.\n");
+	return -EIO;
+}
+
+static void snd_ali_codec_poke(ali_t *codec,int secondary,
+				     unsigned short reg,
+				     unsigned short val)
+{
+	unsigned int dwVal = 0;
+	unsigned int port = 0;
+
+	if (reg >= 0x80) {
+		snd_printk("ali_codec_poke: reg(%xh) invalid.\n", reg);
+		return;
+	}
+
+	port = codec->chregs.regs.ac97write;
+
+	if (snd_ali_codec_ready(codec, port, 0) < 0)
+		return;
+	if (snd_ali_stimer_ready(codec, 0) < 0)
+		return;
+
+	dwVal  = (unsigned int) (reg & 0xff);
+	dwVal |= 0x8000 | (val << 16);
+	if (secondary) dwVal |= 0x0080;
+	if (codec->revision == ALI_5451_V02) dwVal |= 0x0100;
+
+	snd_ali_5451_poke(codec,port,dwVal);
+
+	return ;
+}
+
+static unsigned short snd_ali_codec_peek( ali_t *codec,
+					  int secondary,
+					  unsigned short reg)
+{
+	unsigned int dwVal = 0;
+	unsigned int port = 0;
+
+	if (reg >= 0x80) {
+		snd_printk("ali_codec_peek: reg(%xh) invalid.\n", reg);
+		return ~0;
+	}
+
+	port = codec->chregs.regs.ac97read;
+
+	if (snd_ali_codec_ready(codec, port, 0) < 0)
+		return ~0;
+	if (snd_ali_stimer_ready(codec, 0) < 0)
+		return ~0;
+
+	dwVal  = (unsigned int) (reg & 0xff);
+	dwVal |= 0x8000;				/* bit 15*/
+	if (secondary) dwVal |= 0x0080;
+
+	snd_ali_5451_poke(codec, port, dwVal);
+
+	if (snd_ali_stimer_ready(codec, 0) < 0)
+		return ~0;
+	if (snd_ali_codec_ready(codec, port, 0) < 0)
+		return ~0;
+	
+	return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16;
+}
+
+static void snd_ali_codec_write(ac97_t *ac97,
+				unsigned short reg,
+				unsigned short val )
+{
+	ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return);
+
+	snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
+	snd_ali_codec_poke(codec, 0, reg, val);
+	return ;
+}
+
+
+static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return -ENXIO);
+
+	snd_ali_printk("codec_read reg=%xh.\n", reg);
+	return (snd_ali_codec_peek(codec, 0, reg));
+}
+
+/*
+ *	AC97 Reset
+ */
+
+static int snd_ali_reset_5451(ali_t *codec)
+{
+	struct pci_dev *pci_dev = NULL;
+	unsigned short wCount, wReg;
+	unsigned int   dwVal;
+	
+	if ((pci_dev = codec->pci_m1533) != NULL) {
+		pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+		pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+		udelay(5000);
+		pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+		pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+		udelay(5000);
+	}
+	
+	pci_dev = codec->pci;
+	pci_read_config_dword(pci_dev, 0x44, &dwVal);
+	pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
+	udelay(500);
+	pci_read_config_dword(pci_dev, 0x44, &dwVal);
+	pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
+	udelay(5000);
+	
+	wCount = 200;
+	while(wCount--) {
+		wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
+		if((wReg & 0x000f) == 0x000f)
+			return 0;
+		udelay(5000);
+	}
+
+	return -1;
+}
+
+#ifdef CODEC_RESET
+
+static int snd_ali_reset_codec(ali_t *codec)
+{
+	struct pci_dev *pci_dev = NULL;
+	unsigned char bVal = 0;
+	unsigned int   dwVal;
+	unsigned short wCount, wReg;
+
+	pci_dev = codec->pci_m1533;
+	
+	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+	pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+	udelay(5000);
+	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+	pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+	udelay(5000);
+
+	bVal = inb(ALI_REG(codec,ALI_SCTRL));
+	bVal |= 0x02;
+	outb(ALI_REG(codec,ALI_SCTRL),bVal);
+	udelay(5000);
+	bVal = inb(ALI_REG(codec,ALI_SCTRL));
+	bVal &= 0xfd;
+	outb(ALI_REG(codec,ALI_SCTRL),bVal);
+	udelay(15000);
+
+	wCount = 200;
+	while(wCount--) {
+		wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
+		if((wReg & 0x000f) == 0x000f)
+			return 0;
+		udelay(5000);
+	}
+	return -1;
+}
+
+#endif
+
+/*
+ *  ALI 5451 Controller
+ */
+
+static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel)
+{
+	unsigned long dwVal = 0;
+
+	dwVal  = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL));
+	dwVal |= 1 << (channel & 0x0000001f);
+	outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_disable_special_channel(ali_t *codec, unsigned int channel)
+{
+	unsigned long dwVal = 0;
+
+	dwVal  = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL));
+	dwVal &= ~(1 << (channel & 0x0000001f));
+	outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_enable_address_interrupt(ali_t * codec)
+{
+	unsigned int gc;
+
+	gc  = inl(ALI_REG(codec, ALI_GC_CIR));
+	gc |= ENDLP_IE;
+	gc |= MIDLP_IE;
+	outl( gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+static void snd_ali_disable_address_interrupt(ali_t * codec)
+{
+	unsigned int gc;
+
+	gc  = inl(ALI_REG(codec, ALI_GC_CIR));
+	gc &= ~ENDLP_IE;
+	gc &= ~MIDLP_IE;
+	outl(gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+#if 0 // not used
+static void snd_ali_enable_voice_irq(ali_t *codec, unsigned int channel)
+{
+	unsigned int mask;
+	snd_ali_channel_control_t *pchregs = &(codec->chregs);
+
+	snd_ali_printk("enable_voice_irq channel=%d\n",channel);
+	
+	mask = 1 << (channel & 0x1f);
+	pchregs->data.ainten  = inl(ALI_REG(codec,pchregs->regs.ainten));
+	pchregs->data.ainten |= mask;
+	outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten));
+}
+#endif
+
+static void snd_ali_disable_voice_irq(ali_t *codec, unsigned int channel)
+{
+	unsigned int mask;
+	snd_ali_channel_control_t *pchregs = &(codec->chregs);
+
+	snd_ali_printk("disable_voice_irq channel=%d\n",channel);
+
+	mask = 1 << (channel & 0x1f);
+	pchregs->data.ainten  = inl(ALI_REG(codec,pchregs->regs.ainten));
+	pchregs->data.ainten &= ~mask;
+	outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten));
+}
+
+static int snd_ali_alloc_pcm_channel(ali_t *codec, int channel)
+{
+	unsigned int idx =  channel & 0x1f;
+
+	if (codec->synth.chcnt >= ALI_CHANNELS){
+		snd_printk("ali_alloc_pcm_channel: no free channels.\n");
+		return -1;
+	}
+
+	if (!(codec->synth.chmap & (1 << idx))) {
+		codec->synth.chmap |= 1 << idx;
+		codec->synth.chcnt++;
+		snd_ali_printk("alloc_pcm_channel no. %d.\n",idx);
+		return idx;
+	}
+	return -1;
+}
+
+static int snd_ali_find_free_channel(ali_t * codec, int rec)
+{
+	int idx;
+	int result = -1;
+
+	snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm");
+
+	// recording
+	if (rec) {
+		if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<11) &&
+			( codec->revision == ALI_5451_V02 ))
+			idx = ALI_SPDIF_IN_CHANNEL;
+		else
+			idx = ALI_PCM_IN_CHANNEL;
+
+		if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
+			return result;
+		} else {
+			snd_printk("ali_find_free_channel: record channel is busy now.\n");
+			return -1;
+		}
+	}
+
+	//playback...
+	if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) {
+		idx = ALI_SPDIF_OUT_CHANNEL;
+		if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
+			return result;
+		} else {
+			snd_printk("ali_find_free_channel: S/PDIF out channel is in busy now.\n");
+		}
+	}
+
+	for (idx = 0; idx < ALI_CHANNELS; idx++) {
+		if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0)
+			return result;
+	}
+	snd_printk("ali_find_free_channel: no free channels.\n");
+	return -1;
+}
+
+static void snd_ali_free_channel_pcm(ali_t *codec, int channel)
+{
+	unsigned int idx = channel & 0x0000001f;
+
+	snd_ali_printk("free_channel_pcm channel=%d\n",channel);
+
+	if (channel < 0 || channel >= ALI_CHANNELS)
+		return;
+
+	if (!(codec->synth.chmap & (1 << idx))) {
+		snd_printk("ali_free_channel_pcm: channel %d is not in use.\n",channel);
+		return;
+	} else {
+		codec->synth.chmap &= ~(1 << idx);
+		codec->synth.chcnt--;
+	}
+}
+
+#if 0 // not used
+static void snd_ali_start_voice(ali_t * codec, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	
+	snd_ali_printk("start_voice: channel=%d\n",channel);
+	outl(mask, ALI_REG(codec,codec->chregs.regs.start));
+}
+#endif
+
+static void snd_ali_stop_voice(ali_t * codec, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+
+	snd_ali_printk("stop_voice: channel=%d\n",channel);
+	outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
+}
+
+/*
+ *    S/PDIF Part
+ */
+
+static void snd_ali_delay(ali_t *codec,int interval)
+{
+	unsigned long  begintimer,currenttimer;
+
+	begintimer   = inl(ALI_REG(codec, ALI_STIMER));
+	currenttimer = inl(ALI_REG(codec, ALI_STIMER));
+
+	while (currenttimer < begintimer + interval) {
+		if(snd_ali_stimer_ready(codec, 1) < 0)
+			break;
+		currenttimer = inl(ALI_REG(codec,  ALI_STIMER));
+	}
+}
+
+static void snd_ali_detect_spdif_rate(ali_t *codec)
+{
+	u16 wval  = 0;
+	u16 count = 0;
+	u8  bval = 0, R1 = 0, R2 = 0;
+
+	bval  = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+	bval |= 0x1F;
+	outb(bval,ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+
+	while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) {
+		count ++;
+		snd_ali_delay(codec, 6);
+		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+		R1 = bval & 0x1F;
+	}
+
+	if (count > 50000) {
+		snd_printk("ali_detect_spdif_rate: timeout!\n");
+		return;
+	}
+
+	count = 0;
+	while (count++ <= 50000) {
+		snd_ali_delay(codec, 6);
+		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+		R2 = bval & 0x1F;
+		if (R2 != R1) R1 = R2; else break;
+	}
+
+	if (count > 50000) {
+		snd_printk("ali_detect_spdif_rate: timeout!\n");
+		return;
+	}
+
+	if (R2 >= 0x0b && R2 <= 0x0e) {
+		wval  = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+		wval &= 0xE0F0;
+		wval |= (u16)0x09 << 8 | (u16)0x05;
+		outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0;
+		outb(bval|0x02,ALI_REG(codec,ALI_SPDIF_CS + 3));
+	} else if (R2 == 0x12) {
+		wval  = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+		wval &= 0xE0F0;
+		wval |= (u16)0x0E << 8 | (u16)0x08;
+		outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0;
+		outb(bval|0x03,ALI_REG(codec,ALI_SPDIF_CS + 3));
+	}
+}
+
+static unsigned int snd_ali_get_spdif_in_rate(ali_t *codec)
+{
+	u32	dwRate = 0;
+	u8	bval = 0;
+
+	bval  = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+	bval &= 0x7F;
+	bval |= 0x40;
+	outb(bval, ALI_REG(codec,ALI_SPDIF_CTRL));
+
+	snd_ali_detect_spdif_rate(codec);
+
+	bval  = inb(ALI_REG(codec,ALI_SPDIF_CS + 3));
+	bval &= 0x0F;
+
+	if (bval == 0) dwRate = 44100;
+	if (bval == 1) dwRate = 48000;
+	if (bval == 2) dwRate = 32000;
+
+	return dwRate;
+}
+
+static void snd_ali_enable_spdif_in(ali_t *codec)
+{	
+	unsigned int dwVal;
+
+	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	dwVal |= 1<<11;
+	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+	dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	dwVal |= 0x02;
+	outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+	snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_in(ali_t *codec)
+{
+	unsigned int dwVal;
+	
+	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	dwVal &= ~(1<<11);
+	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	
+	snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);	
+}
+
+
+static void snd_ali_set_spdif_out_rate(ali_t *codec, unsigned int rate)
+{
+	unsigned char  bVal;
+	unsigned int  dwRate = 0;
+	
+	if (rate == 32000) dwRate = 0x300;
+	if (rate == 44100) dwRate = 0;
+	if (rate == 48000) dwRate = 0x200;
+	
+	bVal  = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	bVal &= (unsigned char)(~(1<<6));
+	
+	bVal |= 0x80;		//select right
+	outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+	outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2));
+	
+	bVal &= (~0x80);	//select left
+	outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+	outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2));
+}
+
+static void snd_ali_enable_spdif_out(ali_t *codec)
+{
+	unsigned short wVal;
+	unsigned char bVal;
+
+        struct pci_dev *pci_dev = NULL;
+
+        pci_dev = codec->pci_m1533;
+        if (pci_dev == NULL)
+                return;
+        pci_read_config_byte(pci_dev, 0x61, &bVal);
+        bVal |= 0x40;
+        pci_write_config_byte(pci_dev, 0x61, bVal);
+        pci_read_config_byte(pci_dev, 0x7d, &bVal);
+        bVal |= 0x01;
+        pci_write_config_byte(pci_dev, 0x7d, bVal);
+
+        pci_read_config_byte(pci_dev, 0x7e, &bVal);
+        bVal &= (~0x20);
+        bVal |= 0x10;
+        pci_write_config_byte(pci_dev, 0x7e, bVal);
+
+	bVal = inb(ALI_REG(codec, ALI_SCTRL));
+	outb(bVal | 0x20, ALI_REG(codec, ALI_SCTRL));
+
+	bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	outb(bVal & ~(1<<6), ALI_REG(codec, ALI_SPDIF_CTRL));
+   
+	{
+   		wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+   		wVal |= (1<<10);
+   		outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+		snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
+   	}
+}
+
+static void snd_ali_enable_spdif_chnout(ali_t *codec)
+{
+	unsigned short wVal = 0;
+
+	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+   	wVal &= ~(1<<10);
+   	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+/*
+	wVal = inw(ALI_REG(codec, ALI_SPDIF_CS));
+	if (flag & ALI_SPDIF_OUT_NON_PCM)
+   		wVal |= 0x0002;
+	else	
+		wVal &= (~0x0002);
+   	outw(wVal, ALI_REG(codec, ALI_SPDIF_CS));
+*/
+	snd_ali_enable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_chnout(ali_t *codec)
+{
+	unsigned short wVal = 0;
+  	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+   	wVal |= (1<<10);
+   	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+	snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_out(ali_t *codec)
+{
+	unsigned char  bVal;
+
+	bVal = inb(ALI_REG(codec, ALI_SCTRL));
+	outb(bVal & (~0x20), ALI_REG(codec, ALI_SCTRL));
+
+	snd_ali_disable_spdif_chnout(codec);
+}
+
+static void snd_ali_update_ptr(ali_t *codec,int channel)
+{
+	snd_ali_voice_t *pvoice = NULL;
+	snd_pcm_runtime_t *runtime;
+	snd_ali_channel_control_t *pchregs = NULL;
+	unsigned int old, mask;
+#ifdef ALI_DEBUG
+	unsigned int temp, cspf;
+#endif
+
+	pchregs = &(codec->chregs);
+
+	// check if interrupt occured for channel
+	old  = pchregs->data.aint;
+	mask = ((unsigned int) 1L) << (channel & 0x1f);
+
+	if (!(old & mask))
+		return;
+
+	pvoice = &codec->synth.voices[channel];
+	runtime = pvoice->substream->runtime;
+
+	udelay(100);
+	spin_lock(&codec->reg_lock);
+
+	if (pvoice->pcm && pvoice->substream) {
+		/* pcm interrupt */
+#ifdef ALI_DEBUG
+		outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR));
+		temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+		cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask;
+#endif
+		if (pvoice->running) {
+			snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",(u16)temp,cspf);
+			spin_unlock(&codec->reg_lock);
+			snd_pcm_period_elapsed(pvoice->substream);
+			spin_lock(&codec->reg_lock);
+		} else {
+			snd_ali_stop_voice(codec, channel);
+			snd_ali_disable_voice_irq(codec, channel);
+		}	
+	} else if (codec->synth.voices[channel].synth) {
+		/* synth interrupt */
+	} else if (codec->synth.voices[channel].midi) {
+		/* midi interrupt */
+	} else {
+		/* unknown interrupt */
+		snd_ali_stop_voice(codec, channel);
+		snd_ali_disable_voice_irq(codec, channel);
+	}
+	spin_unlock(&codec->reg_lock);
+	outl(mask,ALI_REG(codec,pchregs->regs.aint));
+	pchregs->data.aint = old & (~mask);
+}
+
+static void snd_ali_interrupt(ali_t * codec)
+{
+	int channel;
+	unsigned int audio_int;
+	snd_ali_channel_control_t *pchregs = NULL;
+	pchregs = &(codec->chregs);
+
+	audio_int = inl(ALI_REG(codec, ALI_MISCINT));
+	if (audio_int & ADDRESS_IRQ) {
+		// get interrupt status for all channels
+		pchregs->data.aint = inl(ALI_REG(codec,pchregs->regs.aint));
+		for (channel = 0; channel < ALI_CHANNELS; channel++) {
+			snd_ali_update_ptr(codec, channel);
+		}
+	}
+	outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
+		ALI_REG(codec,ALI_MISCINT));
+}
+
+
+static void snd_ali_card_interrupt(int irq,
+				   void *dev_id,
+				   struct pt_regs *regs)
+{
+	ali_t 	*codec = snd_magic_cast(ali_t, dev_id, return);
+
+	if (codec == NULL)
+		return;
+	snd_ali_interrupt(codec);
+}
+
+
+static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec)
+{
+	snd_ali_voice_t *pvoice = NULL;
+	unsigned long flags;
+	int idx;
+
+	snd_ali_printk("alloc_voice: type=%d rec=%d\n",type,rec);
+
+	spin_lock_irqsave(&codec->voice_alloc, flags);
+	if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
+		idx = snd_ali_find_free_channel(codec,rec);
+		if(idx < 0) {
+			snd_printk("ali_alloc_voice: err.\n");
+			spin_unlock_irqrestore(&codec->voice_alloc, flags);
+			return NULL;
+		}
+		pvoice = &(codec->synth.voices[idx]);
+		pvoice->use = 1;
+		pvoice->pcm = 1;
+		pvoice->mode = rec;
+		spin_unlock_irqrestore(&codec->voice_alloc, flags);
+		return pvoice;
+	}
+	spin_unlock_irqrestore(&codec->voice_alloc, flags);
+	return NULL;
+}
+
+
+static void snd_ali_free_voice(ali_t * codec, snd_ali_voice_t *pvoice)
+{
+	unsigned long flags;
+	void (*private_free)(void *);
+	void *private_data;
+
+	snd_ali_printk("free_voice: channel=%d\n",pvoice->number);
+	if (pvoice == NULL || !pvoice->use)
+		return;
+	snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
+	spin_lock_irqsave(&codec->voice_alloc, flags);
+	private_free = pvoice->private_free;
+	private_data = pvoice->private_data;
+	pvoice->private_free = NULL;
+	pvoice->private_data = NULL;
+	if (pvoice->pcm) {
+		snd_ali_free_channel_pcm(codec, pvoice->number);
+	}
+	pvoice->use = pvoice->pcm = pvoice->synth = 0;
+	pvoice->substream = NULL;
+	spin_unlock_irqrestore(&codec->voice_alloc, flags);
+	if (private_free)
+		private_free(private_data);
+}
+
+
+static void snd_ali_clear_voices(ali_t * codec,
+			  unsigned int v_min,
+			  unsigned int v_max)
+{
+	unsigned int i;
+
+	for (i = v_min; i <= v_max; i++) {
+		snd_ali_stop_voice(codec, i);
+		snd_ali_disable_voice_irq(codec, i);
+	}
+}
+
+static void snd_ali_write_voice_regs(ali_t * codec,
+			 unsigned int Channel,
+			 unsigned int LBA,
+			 unsigned int CSO,
+			 unsigned int ESO,
+			 unsigned int DELTA,
+			 unsigned int ALPHA_FMS,
+			 unsigned int GVSEL,
+			 unsigned int PAN,
+			 unsigned int VOL,
+			 unsigned int CTRL,
+			 unsigned int EC)
+{
+	unsigned int ctlcmds[4];
+	
+	outb((unsigned char)(Channel & 0x001f),ALI_REG(codec,ALI_GC_CIR));
+
+	ctlcmds[0] =  (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
+	ctlcmds[1] =  LBA;
+	ctlcmds[2] =  (ESO << 16) | (DELTA & 0x0ffff);
+	ctlcmds[3] =  (GVSEL << 31) |
+		      ((PAN & 0x0000007f) << 24) |
+		      ((VOL & 0x000000ff) << 16) |
+		      ((CTRL & 0x0000000f) << 12) |
+		      (EC & 0x00000fff);
+
+	outb(Channel, ALI_REG(codec, ALI_GC_CIR));
+
+	outl(ctlcmds[0], ALI_REG(codec,ALI_CSO_ALPHA_FMS));
+	outl(ctlcmds[1], ALI_REG(codec,ALI_LBA));
+	outl(ctlcmds[2], ALI_REG(codec,ALI_ESO_DELTA));
+	outl(ctlcmds[3], ALI_REG(codec,ALI_GVSEL_PAN_VOC_CTRL_EC));
+
+	outl(0x30000000, ALI_REG(codec, ALI_EBUF1));	/* Still Mode */
+	outl(0x30000000, ALI_REG(codec, ALI_EBUF2));	/* Still Mode */
+}
+
+static unsigned int snd_ali_convert_rate(unsigned int rate, int rec)
+{
+	unsigned int delta;
+
+	if (rate < 4000)  rate = 4000;
+	if (rate > 48000) rate = 48000;
+
+	if (rec) {
+		if (rate == 44100)
+			delta = 0x116a;
+		else if (rate == 8000)
+			delta = 0x6000;
+		else if (rate == 48000)
+			delta = 0x1000;
+		else
+			delta = ((48000 << 12) / rate) & 0x0000ffff;
+	} else {
+		if (rate == 44100)
+			delta = 0xeb3;
+		else if (rate == 8000)
+			delta = 0x2ab;
+		else if (rate == 48000)
+			delta = 0x1000;
+		else 
+			delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
+	}
+
+	return delta;
+}
+
+static unsigned int snd_ali_control_mode(snd_pcm_substream_t *substream)
+{
+	unsigned int CTRL;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	/* set ctrl mode
+	   CTRL default: 8-bit (unsigned) mono, loop mode enabled
+	 */
+	CTRL = 0x00000001;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		CTRL |= 0x00000008;	// 16-bit data
+	if (!snd_pcm_format_unsigned(runtime->format))
+		CTRL |= 0x00000002;	// signed data
+	if (runtime->channels > 1)
+		CTRL |= 0x00000004;	// stereo data
+	return CTRL;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_ali_ioctl(snd_pcm_substream_t * substream,
+				  unsigned int cmd, void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_ali_trigger(snd_pcm_substream_t *substream,
+			       int cmd)
+				    
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_substream_t *s;
+	unsigned int what, whati, capture_flag;
+	snd_ali_voice_t *pvoice = NULL, *evoice = NULL;
+	unsigned int val;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		what = whati = capture_flag = 0;
+		s = substream;
+		do {
+			if ((ali_t *) _snd_pcm_chip(s->pcm) == codec) {
+				pvoice = (snd_ali_voice_t *) s->runtime->private_data;
+				evoice = pvoice->extra;
+				what |= 1 << (pvoice->number & 0x1f);
+				if (evoice == NULL) {
+					whati |= 1 << (pvoice->number & 0x1f);
+				} else {
+					whati |= 1 << (evoice->number & 0x1f);
+					what |= 1 << (evoice->number & 0x1f);
+				}
+				if (cmd == SNDRV_PCM_TRIGGER_START) {
+					pvoice->running = 1;
+					if (evoice != NULL)
+						evoice->running = 1;
+				}
+				snd_pcm_trigger_done(s, substream);
+				if (pvoice->mode)
+					capture_flag = 1;
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&codec->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+			outl(what, ALI_REG(codec, ALI_STOP));
+			pvoice->running = 0;
+			if (evoice != NULL)
+				evoice->running = 0;
+		}
+		val = inl(ALI_REG(codec, ALI_AINTEN));
+		if (cmd == SNDRV_PCM_TRIGGER_START) {
+			val |= whati;
+		} else {
+			val &= ~whati;
+		}
+		outl(val, ALI_REG(codec, ALI_AINTEN));
+		if (cmd == SNDRV_PCM_TRIGGER_START) {
+			outl(what, ALI_REG(codec, ALI_START));
+		}
+		snd_ali_printk("trigger: what=%xh whati=%xh\n",what,whati);
+		spin_unlock(&codec->reg_lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	snd_ali_voice_t *evoice = pvoice->extra;
+	int err;
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+	if (err < 0) return err;
+	
+	/* voice management */
+
+	if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) {
+		if (evoice == NULL) {
+			evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+			if (evoice == NULL)
+				return -ENOMEM;
+			pvoice->extra = evoice;
+			evoice->substream = substream;
+		}
+	} else {
+		if (evoice != NULL) {
+			snd_ali_free_voice(codec, evoice);
+			pvoice->extra = evoice = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	snd_ali_voice_t *evoice = pvoice ? pvoice->extra : NULL;
+
+	snd_pcm_lib_free_pages(substream);
+	if (evoice != NULL) {
+		snd_ali_free_voice(codec, evoice);
+		pvoice->extra = NULL;
+	}
+	return 0;
+}
+
+static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ali_playback_prepare(snd_pcm_substream_t * substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	snd_ali_voice_t *evoice = pvoice->extra;
+	unsigned long flags;
+
+	unsigned int LBA;
+	unsigned int Delta;
+	unsigned int ESO;
+	unsigned int CTRL;
+	unsigned int GVSEL;
+	unsigned int PAN;
+	unsigned int VOL;
+	unsigned int EC;
+	
+	snd_ali_printk("playback_prepare ...\n");
+
+	spin_lock_irqsave(&codec->reg_lock, flags);	
+	
+	/* set Delta (rate) value */
+	Delta = snd_ali_convert_rate(runtime->rate, 0);
+
+	if ((pvoice->number == ALI_SPDIF_IN_CHANNEL) || 
+	    (pvoice->number == ALI_PCM_IN_CHANNEL))
+		snd_ali_disable_special_channel(codec, pvoice->number);
+	else if ((inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) 
+		&& (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) {
+		if (codec->revision == ALI_5451_V02) {
+			snd_ali_set_spdif_out_rate(codec, runtime->rate);
+			Delta = 0x1000;
+		}
+	}
+	
+	/* set Loop Back Address */
+	LBA = runtime->dma_addr;
+
+	/* set interrupt count size */
+	pvoice->count = runtime->period_size;
+
+	/* set target ESO for channel */
+	pvoice->eso = runtime->buffer_size; 
+
+	snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",pvoice->eso,pvoice->count);
+
+	/* set ESO to capture first MIDLP interrupt */
+	ESO = pvoice->eso -1;
+	/* set ctrl mode */
+	CTRL = snd_ali_control_mode(substream);
+
+	GVSEL = 1;
+	PAN = 0;
+	VOL = 0;
+	EC = 0;
+	snd_ali_printk("playback_prepare:\n    ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
+	snd_ali_write_voice_regs(    codec,
+				     pvoice->number,
+				     LBA,
+				     0,	/* cso */
+				     ESO,
+				     Delta,
+				     0,	/* alpha */
+				     GVSEL,
+				     PAN,
+				     VOL,
+				     CTRL,
+				     EC);
+	if (evoice != NULL) {
+		evoice->count = pvoice->count;
+		evoice->eso = pvoice->count << 1;
+		ESO = evoice->eso - 1;
+		snd_ali_write_voice_regs(codec,
+				     evoice->number,
+				     LBA,
+				     0,	/* cso */
+				     ESO,
+				     Delta,
+				     0,	/* alpha */
+				     GVSEL,
+				     (unsigned int)0x7f,
+				     (unsigned int)0x3ff,
+				     CTRL,
+				     EC);
+	}
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	return 0;
+}
+
+
+static int snd_ali_capture_prepare(snd_pcm_substream_t * substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	unsigned long flags;
+	unsigned int LBA;
+	unsigned int Delta;
+	unsigned int ESO;
+	unsigned int CTRL;
+	unsigned int GVSEL;
+	unsigned int PAN;
+	unsigned int VOL;
+	unsigned int EC;
+	u8	 bValue;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+
+	snd_ali_printk("capture_prepare...\n");
+
+	snd_ali_enable_special_channel(codec,pvoice->number);
+
+	Delta = snd_ali_convert_rate(runtime->rate, 1);
+
+	// Prepare capture intr channel
+	if (pvoice->number == ALI_SPDIF_IN_CHANNEL) {
+
+		unsigned int rate;
+		
+		if (codec->revision != ALI_5451_V02)
+			return -1;
+		rate = snd_ali_get_spdif_in_rate(codec);
+		if (rate == 0) {
+			snd_printk("ali_capture_preapre: spdif rate detect err!\n");
+			rate = 48000;
+		}
+		bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+		if (bValue & 0x10) {
+			outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
+			printk("clear SPDIF parity error flag.\n");
+		}
+
+		if (rate != 48000)
+			Delta = ((rate << 12)/runtime->rate)&0x00ffff;
+	}
+
+	// set target ESO for channel 
+	pvoice->eso = runtime->buffer_size; 
+
+	// set interrupt count size 
+	pvoice->count = runtime->period_size;
+
+	// set Loop Back Address 
+	LBA = runtime->dma_addr;
+
+	// set ESO to capture first MIDLP interrupt 
+	ESO = pvoice->eso - 1;
+	CTRL = snd_ali_control_mode(substream);
+	GVSEL = 0;
+	PAN = 0x00;
+	VOL = 0x00;
+	EC = 0;
+
+	snd_ali_write_voice_regs(    codec,
+				     pvoice->number,
+				     LBA,
+				     0,	/* cso */
+				     ESO,
+				     Delta,
+				     0,	/* alpha */
+				     GVSEL,
+				     PAN,
+				     VOL,
+				     CTRL,
+				     EC);
+
+
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+	return 0;
+}
+
+
+static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	unsigned int cso;
+	unsigned long flags;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (!pvoice->running) {
+		spin_unlock_irqrestore(&codec->reg_lock, flags);
+		return 0;
+	}
+	outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+	cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	snd_ali_printk("playback pointer returned cso=%xh.\n", cso);
+
+	return cso;
+}
+
+
+static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	unsigned int cso;
+	unsigned long flags;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	if (!pvoice->running) {
+		spin_unlock_irqrestore(&codec->reg_lock, flags);
+		return 0;
+	}
+	outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+	cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+	return cso;
+}
+
+static snd_pcm_hardware_t snd_ali_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(256*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(256*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_ali_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+	unsigned long flags;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+	ali_t *codec;
+
+	if (pvoice) {
+		codec = pvoice->codec;
+		spin_lock_irqsave(&codec->reg_lock, flags);
+		snd_ali_free_voice(pvoice->codec, pvoice);
+		spin_unlock_irqrestore(&codec->reg_lock, flags);
+	}
+}
+
+static int snd_ali_playback_open(snd_pcm_substream_t * substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+	if (pvoice == NULL) {
+		spin_unlock_irqrestore(&codec->reg_lock, flags);
+		return -EAGAIN;
+	}
+	pvoice->codec = codec;
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+	pvoice->substream = substream;
+	runtime->private_data = pvoice;
+	runtime->private_free = snd_ali_pcm_free_substream;
+
+	runtime->hw = snd_ali_playback;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+	return 0;
+}
+
+
+static int snd_ali_capture_open(snd_pcm_substream_t * substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice;
+	unsigned long flags;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1);
+	if (pvoice == NULL) {
+		spin_unlock_irqrestore(&codec->reg_lock, flags);
+		return -EAGAIN;
+	}
+	pvoice->codec = codec;
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+	pvoice->substream = substream;
+	runtime->private_data = pvoice;
+	runtime->private_free = snd_ali_pcm_free_substream;
+	runtime->hw = snd_ali_capture;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+	return 0;
+}
+
+
+static int snd_ali_playback_close(snd_pcm_substream_t * substream)
+{
+	return 0;
+}
+
+static int snd_ali_capture_close(snd_pcm_substream_t * substream)
+{
+	ali_t *codec = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+
+	snd_ali_disable_special_channel(codec,pvoice->number);
+
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ali_playback_ops = {
+	open:		snd_ali_playback_open,
+	close:		snd_ali_playback_close,
+	ioctl:		snd_ali_ioctl,
+	hw_params:	snd_ali_playback_hw_params,
+	hw_free:	snd_ali_playback_hw_free,
+	prepare:	snd_ali_playback_prepare,
+	trigger:	snd_ali_trigger,
+	pointer:	snd_ali_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ali_capture_ops = {
+	open:		snd_ali_capture_open,
+	close:		snd_ali_capture_close,
+	ioctl:		snd_ali_ioctl,
+	hw_params:	snd_ali_capture_hw_params,
+	hw_free:	snd_ali_capture_hw_free,
+	prepare:	snd_ali_capture_prepare,
+	trigger:	snd_ali_trigger,
+	pointer:	snd_ali_capture_pointer,
+};
+
+
+static void snd_ali_pcm_free(snd_pcm_t *pcm)
+{
+	ali_t *codec = snd_magic_cast(ali_t, pcm->private_data, return);
+	codec->pcm = NULL;
+}
+
+static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm) *rpcm = NULL;
+	err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm);
+	if (err < 0) {
+		snd_printk("snd_ali_pcm: err called snd_pcm_new.\n");
+		return err;
+	}
+	pcm->private_data = codec;
+	pcm->private_free = snd_ali_pcm_free;
+	pcm->info_flags = 0;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops);
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(codec->pci, pcm, 64*1024, 128*1024);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "ALI 5451");
+	codec->pcm = pcm;
+	if (rpcm) *rpcm = pcm;
+	return 0;
+}
+
+#define ALI5451_SPDIF(xname, xindex, value) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+info: snd_ali5451_spdif_info, get: snd_ali5451_spdif_get, \
+put: snd_ali5451_spdif_put, private_value: value}
+
+static int snd_ali5451_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+        uinfo->count = 1;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 1;
+        return 0;
+}
+
+static int snd_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO);
+	unsigned int enable;
+
+	enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	switch(kcontrol->private_value) {
+	case 0:
+		enable = (codec->spdif_mask & 0x02) ? 1 : 0;
+		break;
+	case 1:
+		enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0;
+		break;
+	case 2:
+		enable = (codec->spdif_mask & 0x01) ? 1 : 0;
+		break;
+	default:
+		break;
+	}
+	ucontrol->value.integer.value[0] = enable;
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO);
+	unsigned int change = 0, enable = 0;
+
+	enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+	spin_lock_irqsave(&codec->reg_lock, flags);
+	switch (kcontrol->private_value) {
+	case 0:
+		change = (codec->spdif_mask & 0x02) ? 1 : 0;
+		change = change ^ enable;
+		if (change) {
+			if (enable) {
+				codec->spdif_mask |= 0x02;
+				snd_ali_enable_spdif_out(codec);
+			} else {
+				codec->spdif_mask &= ~(0x02);
+				codec->spdif_mask &= ~(0x04);
+				snd_ali_disable_spdif_out(codec);
+			}
+		}
+		break;
+	case 1: 
+		change = (codec->spdif_mask & 0x04) ? 1 : 0;
+		change = change ^ enable;
+		if (change && (codec->spdif_mask & 0x02)) {
+			if (enable) {
+				codec->spdif_mask |= 0x04;
+				snd_ali_enable_spdif_chnout(codec);
+			} else {
+				codec->spdif_mask &= ~(0x04);
+				snd_ali_disable_spdif_chnout(codec);
+			}
+		}
+		break;
+	case 2:
+		change = (codec->spdif_mask & 0x01) ? 1 : 0;
+		change = change ^ enable;
+		if (change) {
+			if (enable) {
+				codec->spdif_mask |= 0x01;
+				snd_ali_enable_spdif_in(codec);
+			} else {
+				codec->spdif_mask &= ~(0x01);
+				snd_ali_disable_spdif_in(codec);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&codec->reg_lock, flags);
+	
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinit = {
+	/* spdif aplayback switch */
+	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, 0),
+	/* spdif out to spdif channel */
+	ALI5451_SPDIF("IEC958 Channel Output Switch", 0, 1),
+	/* spdif in from spdif channel */
+	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2)
+};
+
+static void snd_ali_mixer_free_ac97(ac97_t *ac97)
+{
+	ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return);
+	codec->ac97 = NULL;
+}
+
+static int __devinit snd_ali_mixer(ali_t * codec)
+{
+	ac97_t ac97;
+	int err, idx;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_ali_codec_write;
+	ac97.read = snd_ali_codec_read;
+	ac97.private_data = codec;
+	ac97.private_free = snd_ali_mixer_free_ac97;
+	if ((err = snd_ac97_mixer(codec->card, &ac97, &codec->ac97)) < 0) {
+		snd_printk("ali mixer creating error.\n");
+		return err;
+	}
+	if (codec->revision == ALI_5451_V02) {
+		for(idx = 0; idx < 3; idx++) {
+			err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
+			if (err < 0) return err;
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifndef PCI_OLD_SUSPEND
+static int snd_ali_suspend(struct pci_dev *dev, u32 state)
+#else
+static void snd_ali_suspend(struct pci_dev *dev)
+#endif
+{
+#ifndef PCI_OLD_SUSPEND
+	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO);
+#else
+	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return);
+#endif
+	ali_image_t *im;
+	unsigned long flags;
+	int i, j;
+
+	im = chip->image;
+	if (! im)
+#ifndef PCI_OLD_SUSPEND
+		return -ENXIO;
+#else
+		return;
+#endif
+
+	save_flags(flags); 
+	cli();
+	
+	im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
+	// im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START));
+	im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP));
+	
+	// disable all IRQ bits
+	outl(0, ALI_REG(chip, ALI_MISCINT));
+	
+	for (i = 0; i < ALI_GLOBAL_REGS; i++) {	
+		if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP))
+			continue;
+		im->regs[i] = inl(ALI_REG(chip, i*4));
+	}
+	
+	for (i = 0; i < ALI_CHANNELS; i++) {
+		outb(i, ALI_REG(chip, ALI_GC_CIR));
+		for (j = 0; j < ALI_CHANNEL_REGS; j++) 
+			im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0));
+	}
+
+	// stop all HW channel
+	outl(0xffffffff, ALI_REG(chip, ALI_STOP));
+
+	restore_flags(flags);
+#ifndef PCI_OLD_SUSPEND
+	return 0;
+#endif
+}
+
+#ifndef PCI_OLD_SUSPEND
+static int snd_ali_resume(struct pci_dev *dev)
+#else
+static void snd_ali_resume(struct pci_dev *dev)
+#endif
+{
+#ifndef PCI_OLD_SUSPEND
+	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO);
+#else
+	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return);
+#endif
+	ali_image_t *im;
+	unsigned long flags;
+	int i, j;
+
+	im = chip->image;
+	if (! im)
+#ifndef PCI_OLD_SUSPEND
+		return -ENXIO;
+#else
+		return;
+#endif
+
+	pci_enable_device(chip->pci);
+
+	save_flags(flags); 
+	cli();
+	
+	for (i = 0; i < ALI_CHANNELS; i++) {
+		outb(i, ALI_REG(chip, ALI_GC_CIR));
+		for (j = 0; j < ALI_CHANNEL_REGS; j++) 
+			outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
+	}
+	
+	for (i = 0; i < ALI_GLOBAL_REGS; i++) {	
+		if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || (i*4 == ALI_START))
+			continue;
+		outl(im->regs[i], ALI_REG(chip, i*4));
+	}
+	
+	snd_ac97_resume(chip->ac97);
+	
+	// start HW channel
+	outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
+	// restore IRQ enable bits
+	outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
+	
+	restore_flags(flags);
+#ifndef PCI_OLD_SUSPEND
+	return 0;
+#endif
+}
+#endif
+
+static int snd_ali_free(ali_t * codec)
+{
+	snd_ali_disable_address_interrupt(codec);
+	synchronize_irq();
+	if (codec->irq >=0)
+		free_irq(codec->irq, (void *)codec);
+	if (codec->res_port) {
+		release_resource(codec->res_port);
+		kfree_nocheck(codec->res_port);
+	}
+#ifdef CONFIG_PM
+	if (codec->image)
+		kfree(codec->image);
+#endif
+	snd_magic_kfree(codec);
+	return 0;
+}
+
+static int snd_ali_chip_init(ali_t *codec)
+{
+	unsigned int legacy;
+	unsigned char temp;
+	struct pci_dev *pci_dev = NULL;
+
+	snd_ali_printk("chip initializing ... \n");
+
+	if (snd_ali_reset_5451(codec)) {
+		snd_printk("ali_chip_init: reset 5451 error.\n");
+		return -1;
+	}
+
+	if (codec->revision == ALI_5451_V02) {
+        	pci_dev = codec->pci_m1533;
+        	if (pci_dev == NULL)
+                	return -1;
+		pci_read_config_byte(pci_dev, 0x59, &temp);
+	
+		pci_dev = pci_find_device(0x10b9,0x7101, pci_dev);
+		if (pci_dev == NULL)
+                	return -1;
+		pci_read_config_byte(pci_dev,0xb8,&temp);
+		temp |= 1 << 6;
+		pci_write_config_byte(pci_dev, 0xB8, temp);
+	}
+
+	pci_read_config_dword(codec->pci, 0x44, &legacy);
+	legacy &= 0xff00ff00;
+	legacy |= 0x000800aa;
+	pci_write_config_dword(codec->pci, 0x44, legacy);
+
+	outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	outl(0x00000000, ALI_REG(codec, ALI_AINTEN));
+	outl(0xffffffff, ALI_REG(codec, ALI_AINT));
+	outl(0x00000000, ALI_REG(codec, ALI_VOLUME));
+	outb(0x10, 	 ALI_REG(codec, ALI_MPUR2));
+
+	codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID);
+	codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_STATUS);
+	if (codec->revision == ALI_5451_V02) {
+		snd_ali_enable_spdif_out(codec);
+		codec->spdif_mask = 0x00000002;
+	}
+
+	snd_ali_printk("chip initialize succeed.\n");
+	return 0;
+
+}
+
+static int __devinit snd_ali_resources(ali_t *codec)
+{
+	snd_ali_printk("resouces allocation ...\n");
+	if ((codec->res_port = request_region(codec->port, 0x100, "ALI 5451")) == NULL) {
+		snd_ali_free(codec);
+		snd_printk("Unalbe to request io ports.\n");
+		return -EBUSY;
+	}
+
+	if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) {
+		snd_ali_free(codec);
+		snd_printk("Unable to request irq.\n");
+		return -EBUSY;
+	}
+	codec->irq = codec->pci->irq;
+	snd_ali_printk("resouces allocated.\n");
+	return 0;
+}
+static int snd_ali_dev_free(snd_device_t *device) 
+{
+	ali_t *codec=snd_magic_cast(ali_t, device->device_data, return -ENXIO);
+	snd_ali_free(codec);
+	return 0;
+}
+
+static int __devinit snd_ali_create(snd_card_t * card,
+				 struct pci_dev *pci,
+				 int pcm_streams,
+				 ali_t ** r_ali)
+{
+	ali_t *codec;
+	int i, err;
+	unsigned short cmdw = 0;
+	struct pci_dev *pci_dev = NULL;
+        static snd_device_ops_t ops = {
+		(snd_dev_free_t *)snd_ali_dev_free,
+		NULL,
+		NULL
+        };
+
+	*r_ali = NULL;
+
+	snd_ali_printk("creating ...\n");
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 30 bits */
+	if (!pci_dma_supported(pci, 0x3fffffff)) {
+		snd_printk("architecture does not support 30bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x3fffffff);
+
+	if ((codec = snd_magic_kcalloc(ali_t, 0, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&codec->reg_lock);
+	spin_lock_init(&codec->voice_alloc);
+
+	codec->card = card;
+	codec->pci = pci;
+	codec->irq = -1;
+	codec->port = pci_resource_start(pci, 0);
+	pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision);
+
+	if (pcm_streams < 1)
+		pcm_streams = 1;
+	if (pcm_streams > 32)
+		pcm_streams = 32;
+	
+	pci_set_master(pci);
+	pci_read_config_word(pci, PCI_COMMAND, &cmdw);
+	if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) {
+		cmdw |= PCI_COMMAND_IO;
+		pci_write_config_word(pci, PCI_COMMAND, cmdw);
+	}
+	pci_set_master(pci);
+	
+	if (snd_ali_resources(codec)) {
+		return -EBUSY;
+	}
+
+	synchronize_irq();
+
+	codec->synth.chmap = 0;
+	codec->synth.chcnt = 0;
+	codec->spdif_mask = 0;
+	codec->synth.synthcount = 0;
+
+	if (codec->revision == ALI_5451_V02)
+		codec->chregs.regs.ac97read = ALI_AC97_WRITE;
+	else
+		codec->chregs.regs.ac97read = ALI_AC97_READ;
+	codec->chregs.regs.ac97write = ALI_AC97_WRITE;
+
+	codec->chregs.regs.start  = ALI_START;
+	codec->chregs.regs.stop   = ALI_STOP;
+	codec->chregs.regs.aint   = ALI_AINT;
+	codec->chregs.regs.ainten = ALI_AINTEN;
+
+	codec->chregs.data.start  = 0x00;
+	codec->chregs.data.stop   = 0x00;
+	codec->chregs.data.aint   = 0x00;
+	codec->chregs.data.ainten = 0x00;
+
+       	pci_dev = pci_find_device(0x10b9,0x1533, pci_dev);
+	codec->pci_m1533 = pci_dev;
+       	pci_dev = pci_find_device(0x10b9,0x7101, pci_dev);
+	codec->pci_m7101 = pci_dev;
+
+	snd_ali_printk("snd_device_new is called.\n");
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
+		snd_ali_free(codec);
+		return err;
+	}
+
+	/* initialise synth voices*/
+	for (i = 0; i < ALI_CHANNELS; i++ ) {
+		codec->synth.voices[i].number = i;
+	}
+
+	if ((err = snd_ali_chip_init(codec)) < 0) {
+		snd_printk("ali create: chip init error.\n");
+		snd_ali_free(codec);
+		return err;
+	}
+
+#ifdef CONFIG_PM
+	codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
+	if (! codec->image)
+		snd_printk("can't allocate apm buffer\n");
+#endif
+
+	snd_ali_enable_address_interrupt(codec);
+
+	*r_ali = codec;
+	snd_ali_printk("created.\n");
+	return 0;
+}
+
+static int __devinit snd_ali_probe(struct pci_dev *pci,
+				   const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	ali_t *codec;
+	int err;
+
+	snd_ali_printk("probe ...\n");
+
+        if (dev >= SNDRV_CARDS)
+                return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ali_create(card, pci, snd_pcm_channels[dev], &codec)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_ali_printk("mixer building ...\n");
+	if ((err = snd_ali_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	snd_ali_printk("pcm building ...\n");
+	if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "ALI5451");
+	strcpy(card->shortname, "ALI 5451");
+	
+	sprintf(card->longname, "%s at 0x%lx, irq %li",
+		card->shortname, codec->port, codec->irq);
+
+	snd_ali_printk("register card.\n");
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, codec);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_ali_remove(struct pci_dev *pci)
+{
+	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "ALI 5451",
+	id_table: snd_ali_ids,
+	probe: snd_ali_probe,
+	remove: __devexit_p(snd_ali_remove),
+#ifdef CONFIG_PM
+	suspend: snd_ali_suspend,
+	resume: snd_ali_resume,
+#endif
+};                                
+
+static int __init alsa_card_ali_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "ALi pci audio not found or device busy.\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ali_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ali_init)
+module_exit(alsa_card_ali_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ali5451=snd_enable,snd_index,snd_id,snd_pcm_channels */
+
+static int __init alsa_card_ali_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-ali5451=", alsa_card_ali_setup);
+
+#endif /* ifndef */
diff -Nru linux/sound/pci/als4000.c linux-2.4.19-pre5-mjc/sound/pci/als4000.c
--- linux/sound/pci/als4000.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/als4000.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,729 @@
+/*
+ *  card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
+ *  Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
+ *			  Jaroslav Kysela <perex@suse.cz>
+ *
+ *  Framework borrowed from Massimo Piccioni's card-als100.c.
+ *
+ * NOTES
+ *
+ *  Since Avance does not provide any meaningful documentation, and I
+ *  bought an ALS4000 based soundcard, I was forced to base this driver
+ *  on reverse engineering.
+ *
+ *  The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an
+ *  ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport 
+ *  interface. These subsystems can be mapped into ISA io-port space, 
+ *  using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ 
+ *  services to the subsystems.
+ * 
+ * While ALS4000 is very similar to a SoundBlaster, the differences in
+ * DMA and capturing require more changes to the SoundBlaster than
+ * desirable, so I made this separate driver.
+ * 
+ * The ALS4000 can do real full duplex playback/capture.
+ *
+ * BUGS
+ *   The box suggests there is some support for 3D sound, but I did not
+ *   investigate this yet.
+ * 
+ *
+ *
+ *  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
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>");
+MODULE_DESCRIPTION("Avance Logic ALS4000");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Avance Logic,ALS4000}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_joystick_port[SNDRV_CARDS] =
+#ifdef CONFIG_ISA
+	{0x200};	/* enable as default */
+#else
+	{0};	/* disabled */
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ALS4000 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ALS4000 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ALS4000 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
+MODULE_PARM_SYNTAX(snd_joystick_port, SNDRV_ENABLED);
+
+#define chip_t sb_t
+
+typedef struct {
+	unsigned long gcr;
+} snd_card_als4000_t;
+
+static struct pci_device_id snd_als4000_ids[] __devinitdata = {
+	{ 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ALS4000 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_als4000_ids);
+
+static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val)
+{
+	outb(reg, port+0x0c);
+	outl(val, port+0x08);
+}
+
+static inline void snd_als4000_gcr_write(sb_t *sb, u32 reg, u32 val)
+{
+	snd_als4000_gcr_write_addr(sb->alt_port, reg, val);
+}	
+
+static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg)
+{
+	outb(reg, port+0x0c);
+	return inl(port+0x08);
+}
+
+static inline u32 snd_als4000_gcr_read(sb_t *sb, u32 reg)
+{
+	return snd_als4000_gcr_read_addr(sb->alt_port, reg);
+}
+
+static void snd_als4000_set_rate(sb_t *chip, unsigned int rate)
+{
+	if (!(chip->mode & SB_RATE_LOCK)) {
+		snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
+		snd_sbdsp_command(chip, rate>>8);
+		snd_sbdsp_command(chip, rate);
+	}
+}
+
+static void snd_als4000_set_capture_dma(sb_t *chip, dma_addr_t addr, unsigned size)
+{
+	snd_als4000_gcr_write(chip, 0xa2, addr);
+	snd_als4000_gcr_write(chip, 0xa3, (size-1));
+}
+
+static void snd_als4000_set_playback_dma(sb_t *chip, dma_addr_t addr, unsigned size)
+{
+	snd_als4000_gcr_write(chip, 0x91, addr);
+	snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000);
+}
+
+#define ALS4000_FORMAT_SIGNED	(1<<0)
+#define ALS4000_FORMAT_16BIT	(1<<1)
+#define ALS4000_FORMAT_STEREO	(1<<2)
+
+static int snd_als4000_get_format(snd_pcm_runtime_t *runtime)
+{
+	int result;
+
+	result = 0;
+	if (snd_pcm_format_signed(runtime->format))
+		result |= ALS4000_FORMAT_SIGNED;
+	if (snd_pcm_format_physical_width(runtime->format) == 16)
+		result |= ALS4000_FORMAT_16BIT;
+	if (runtime->channels > 1)
+		result |= ALS4000_FORMAT_STEREO;
+	return result;
+}
+
+/* structure for setting up playback */
+static struct {
+	unsigned char dsp_cmd, dma_on, dma_off, format;
+} playback_cmd_vals[]={
+/* ALS4000_FORMAT_U8_MONO */
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO },
+/* ALS4000_FORMAT_S8_MONO */	
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO },
+/* ALS4000_FORMAT_U16L_MONO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO },
+/* ALS4000_FORMAT_S16L_MONO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO },
+/* ALS4000_FORMAT_U8_STEREO */
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO },
+/* ALS4000_FORMAT_S8_STEREO */	
+{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO },
+/* ALS4000_FORMAT_U16L_STEREO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO },
+/* ALS4000_FORMAT_S16L_STEREO */
+{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO },
+};
+#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format])
+
+/* structure for setting up capture */
+enum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 };
+static unsigned char capture_cmd_vals[]=
+{
+CMD_WIDTH8|CMD_MONO,			/* ALS4000_FORMAT_U8_MONO */
+CMD_WIDTH8|CMD_SIGNED|CMD_MONO,		/* ALS4000_FORMAT_S8_MONO */	
+CMD_MONO,				/* ALS4000_FORMAT_U16L_MONO */
+CMD_SIGNED|CMD_MONO,			/* ALS4000_FORMAT_S16L_MONO */
+CMD_WIDTH8|CMD_STEREO,			/* ALS4000_FORMAT_U8_STEREO */
+CMD_WIDTH8|CMD_SIGNED|CMD_STEREO,	/* ALS4000_FORMAT_S8_STEREO */	
+CMD_STEREO,				/* ALS4000_FORMAT_U16L_STEREO */
+CMD_SIGNED|CMD_STEREO,			/* ALS4000_FORMAT_S16L_STEREO */
+};	
+#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format])
+
+static int snd_als4000_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_als4000_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_als4000_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long size;
+	unsigned count;
+
+	chip->capture_format = snd_als4000_get_format(runtime);
+		
+	size = snd_pcm_lib_buffer_bytes(substream);
+	count = snd_pcm_lib_period_bytes(substream);
+	
+	if (chip->capture_format & ALS4000_FORMAT_16BIT)
+		count >>=1;
+	count--;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_als4000_set_rate(chip, runtime->rate);
+	snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	spin_lock_irqsave(&chip->mixer_lock, flags );
+	snd_sbmixer_write(chip, 0xdc, count);
+	snd_sbmixer_write(chip, 0xdd, count>>8);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags );
+	return 0;
+}
+
+static int snd_als4000_playback_prepare(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long size;
+	unsigned count;
+
+	chip->playback_format = snd_als4000_get_format(runtime);
+	
+	size = snd_pcm_lib_buffer_bytes(substream);
+	count = snd_pcm_lib_period_bytes(substream);
+	
+	if (chip->playback_format & ALS4000_FORMAT_16BIT)
+		count >>=1;
+	count--;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_als4000_set_rate(chip, runtime->rate);
+	snd_als4000_set_playback_dma(chip, runtime->dma_addr, size);
+	
+	snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
+	snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);
+	snd_sbdsp_command(chip, playback_cmd(chip).format);
+	snd_sbdsp_command(chip, count);
+	snd_sbdsp_command(chip, count>>8);
+	snd_sbdsp_command(chip, playback_cmd(chip).dma_off);	
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	
+	return 0;
+}
+
+static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+	
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		chip->mode |= SB_RATE_LOCK_CAPTURE;
+		snd_sbmixer_write(chip, 0xde, capture_cmd(chip));
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		chip->mode &= ~SB_RATE_LOCK_CAPTURE;
+		snd_sbmixer_write(chip, 0xde, 0);
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	return result;
+}
+
+static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		chip->mode |= SB_RATE_LOCK_PLAYBACK;
+		snd_sbdsp_command(chip, playback_cmd(chip).dma_on);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
+		chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int result;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);	
+	result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return bytes_to_frames( substream->runtime, result );
+}
+
+static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	unsigned result;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);	
+	result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return bytes_to_frames( substream->runtime, result );
+}
+
+static void snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	sb_t *chip = snd_magic_cast(sb_t, dev_id, return);
+	unsigned long flags;
+	unsigned gcr_status;
+	unsigned sb_status;
+
+	/* find out which bit of the ALS4000 produced the interrupt */
+	gcr_status = inb(chip->alt_port + 0xe);
+
+	if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */
+		snd_pcm_period_elapsed(chip->playback_substream);
+	if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
+		snd_pcm_period_elapsed(chip->capture_substream);
+	if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interupt */
+		snd_mpu401_uart_interrupt(irq, chip->rmidi, regs);
+	/* release the gcr */
+	outb(gcr_status, chip->alt_port + 0xe);
+	
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	
+	if (sb_status & SB_IRQTYPE_8BIT) 
+		inb(SBP(chip, DATA_AVAIL));
+	if (sb_status & SB_IRQTYPE_16BIT) 
+		inb(SBP(chip, DATA_AVAIL_16));
+	if (sb_status & SB_IRQTYPE_MPUIN)
+		inb(chip->mpu_port);
+	if (sb_status & 0x20)
+		inb(SBP(chip, RESET));
+}
+
+/*****************************************************************/
+
+static snd_pcm_hardware_t snd_als4000_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0
+};
+
+static snd_pcm_hardware_t snd_als4000_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0
+};
+
+/*****************************************************************/
+
+static int snd_als4000_playback_open(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->playback_substream = substream;
+	runtime->hw = snd_als4000_playback;
+	return 0;
+}
+
+static int snd_als4000_playback_close(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_als4000_capture_open(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->capture_substream = substream;
+	runtime->hw = snd_als4000_capture;
+	return 0;
+}
+
+static int snd_als4000_capture_close(snd_pcm_substream_t * substream)
+{
+	sb_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+/******************************************************************/
+
+static snd_pcm_ops_t snd_als4000_playback_ops = {
+	open:		snd_als4000_playback_open,
+	close:		snd_als4000_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_als4000_hw_params,
+	hw_free:	snd_als4000_hw_free,
+	prepare:	snd_als4000_playback_prepare,
+	trigger:	snd_als4000_playback_trigger,
+	pointer:	snd_als4000_playback_pointer
+};
+
+static snd_pcm_ops_t snd_als4000_capture_ops = {
+	open:		snd_als4000_capture_open,
+	close:		snd_als4000_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_als4000_hw_params,
+	hw_free:	snd_als4000_hw_free,
+	prepare:	snd_als4000_capture_prepare,
+	trigger:	snd_als4000_capture_trigger,
+	pointer:	snd_als4000_capture_pointer
+};
+
+static void snd_als4000_pcm_free(snd_pcm_t *pcm)
+{
+	sb_t *chip = snd_magic_cast(sb_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_als4000_pcm(sb_t *chip, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0)
+		return err;
+	pcm->private_free = snd_als4000_pcm_free;
+	pcm->private_data = chip;
+	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
+
+	chip->pcm = pcm;
+
+	return 0;
+}
+
+/******************************************************************/
+
+static void __devinit snd_als4000_set_addr(unsigned long gcr,
+					unsigned int sb,
+					unsigned int mpu,
+					unsigned int opl,
+					unsigned int game)
+{
+	u32 confA = 0;
+	u32 confB = 0;
+
+	if (mpu > 0)
+		confB |= (mpu | 1) << 16;
+	if (sb > 0)
+		confB |= (sb | 1);
+	if (game > 0)
+		confA |= (game | 1) << 16;
+	if (opl > 0)	
+		confA |= (opl | 1);
+	snd_als4000_gcr_write_addr(gcr, 0xa8, confA);
+	snd_als4000_gcr_write_addr(gcr, 0xa9, confB);
+}
+
+static void __devinit snd_als4000_configure(sb_t *chip)
+{
+	unsigned long flags;
+	unsigned tmp;
+	int i;
+
+	/* do some more configuration */
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	tmp = snd_sbmixer_read(chip, 0xc0);
+	snd_sbmixer_write(chip, 0xc0, tmp|0x80);
+	/* always select DMA channel 0, since we do not actually use DMA */
+	snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
+	snd_sbmixer_write(chip, 0xc0, tmp&0x7f);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	
+	spin_lock_irqsave(&chip->reg_lock,flags);
+	/* magic number. Enables interrupts(?) */
+	snd_als4000_gcr_write(chip, 0x8c, 0x28000);
+	for(i = 0x91; i <= 0x96; ++i)
+		snd_als4000_gcr_write(chip, i, 0);
+	
+	snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99));
+	spin_unlock_irqrestore(&chip->reg_lock,flags);
+}
+
+static void snd_card_als4k_free( snd_card_t *card )
+{
+	snd_card_als4000_t * acard = (snd_card_als4000_t *)card->private_data;
+	/* make sure that interrupts are disabled */
+	snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0);
+}
+
+static int __devinit snd_card_als4k_probe(struct pci_dev *pci,
+					  const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	snd_card_als4000_t *acard;
+	unsigned long gcr;
+	struct resource *res_gcr_port;
+	sb_t *chip;
+	opl3_t *opl3;
+	unsigned short word;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0) {
+		return err;
+	}
+	/* check, if we can restrict PCI DMA transfers to 24 bits */
+	if (!pci_dma_supported(pci, 0x00ffffff)) {
+		snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x00ffffff);
+
+	gcr = pci_resource_start(pci, 0);
+	if ((res_gcr_port = request_region(gcr, 0x40, "ALS4000")) == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n", gcr, gcr + 0x40 - 1);
+		return -EBUSY;
+	}
+
+	pci_read_config_word(pci, PCI_COMMAND, &word);
+	pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
+	pci_set_master(pci);
+	
+	/* disable all legacy ISA stuff except for joystick */
+	snd_als4000_set_addr(gcr, 0, 0, 0, snd_joystick_port[dev]);
+	
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 
+			    sizeof( snd_card_als4000_t ) );
+	if (card == NULL) {
+		release_resource(res_gcr_port);
+		kfree_nocheck(res_gcr_port);
+		return -ENOMEM;
+	}
+
+	acard = (snd_card_als4000_t *)card->private_data;
+	acard->gcr = gcr;
+	card->private_free = snd_card_als4k_free;
+
+	if ((err = snd_sbdsp_create(card,
+				    gcr + 0x10,
+				    pci->irq,
+				    snd_als4000_interrupt,
+				    -1,
+				    -1,
+				    SB_HW_ALS4000,
+				    &chip)) < 0) {
+		release_resource(res_gcr_port);
+		kfree_nocheck(res_gcr_port);
+		snd_card_free(card);
+		return err;
+	}
+
+	chip->pci = pci;
+	chip->alt_port = gcr;
+	chip->res_alt_port = res_gcr_port;
+
+	snd_als4000_configure(chip);
+	
+	if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
+				        gcr+0x30, 1, pci->irq, 0,
+				        &chip->rmidi)) < 0) {
+		snd_card_free(card);
+		printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30);
+		return err;
+	}
+
+	if ((err = snd_als4000_pcm(chip, 0)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}	    
+
+	if (snd_opl3_create(card, gcr+0x10, gcr+0x12,
+			    OPL3_HW_AUTO, 1, &opl3) < 0) {
+		printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n",
+			   gcr+0x10, gcr+0x12 );
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	strcpy(card->driver, "ALS4000");
+	strcpy(card->shortname, "Avance Logic ALS4000");
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, chip->alt_port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_card_als4k_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "ALS4000",
+	id_table: snd_als4000_ids,
+	probe: snd_card_als4k_probe,
+	remove: __devexit_p(snd_card_als4k_remove),
+};
+
+static int __init alsa_card_als4k_init(void)
+{
+	int err;
+	
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "no ALS4000 based soundcards found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_als4k_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_als4k_init)
+module_exit(alsa_card_als4k_exit)
+
+#ifndef MODULE
+
+/* format is: snd-als4000=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_als4000_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-als4000=", alsa_card_als4000_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/cmipci.c linux-2.4.19-pre5-mjc/sound/pci/cmipci.c
--- linux/sound/pci/cmipci.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/cmipci.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2775 @@
+/*
+ * Driver for C-Media CMI8338 and 8738 PCI soundcards.
+ * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+ 
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#include <sound/asoundef.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("C-Media CMI8x38 PCI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{C-Media,CMI8738},"
+		"{C-Media,CMI8738B},"
+		"{C-Media,CMI8338A},"
+		"{C-Media,CMI8338B}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+static long snd_mpu_port[SNDRV_CARDS] = {0x330, [1 ... (SNDRV_CARDS-1)]=-1};
+static long snd_fm_port[SNDRV_CARDS] = {0x388, [1 ... (SNDRV_CARDS-1)]=-1};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for C-Media PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for C-Media PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable C-Media PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{-1},{0x330},{0x320},{0x310},{0x300}},dialog:list");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list");
+
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738	0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B	0x0112
+#endif
+
+/*
+ * CM8x38 registers definition
+ */
+
+#define CM_REG_FUNCTRL0		0x00
+#define CM_RST_CH1		0x00080000
+#define CM_RST_CH0		0x00040000
+#define CM_CHEN1		0x00020000	/* ch1: enable */
+#define CM_CHEN0		0x00010000	/* ch0: enable */
+#define CM_PAUSE1		0x00000008	/* ch1: pause */
+#define CM_PAUSE0		0x00000004	/* ch0: pause */
+#define CM_CHADC1		0x00000002	/* ch1, 0:playback, 1:record */
+#define CM_CHADC0		0x00000001	/* ch0, 0:playback, 1:record */
+
+#define CM_REG_FUNCTRL1		0x04
+#define CM_ASFC_MASK		0x0000E000	/* ADC sampling frequency */
+#define CM_ASFC_SHIFT		13
+#define CM_DSFC_MASK		0x00001C00	/* DAC sampling frequency */
+#define CM_DSFC_SHIFT		10
+#define CM_SPDF_1		0x00000200	/* SPDIF IN/OUT at channel B */
+#define CM_SPDF_0		0x00000100	/* SPDIF OUT only channel A */
+#define CM_SPDFLOOP		0x00000080	/* ext. SPDIIF/OUT -> IN loopback */
+#define CM_SPDO2DAC		0x00000040	/* SPDIF/OUT can be heard from internal DAC */
+#define CM_INTRM		0x00000020	/* master control block (MCB) interrupt enabled */
+#define CM_BREQ			0x00000010	/* bus master enabled */
+#define CM_VOICE_EN		0x00000008	/* legacy voice (SB16,FM) */
+#define CM_UART_EN		0x00000004	/* UART */
+#define CM_JYSTK_EN		0x00000002	/* joy stick */
+
+#define CM_REG_CHFORMAT		0x08
+
+#define CM_CHB3D5C		0x80000000	/* 5 channels */
+#define CM_CHB3D		0x20000000	/* 4,5,6 channels */
+
+#define CM_CHIP_MASK1		0x1f000000
+#define CM_CHIP_037		0x01000000
+
+#define CM_AC3EN1		0x00100000	/* enable AC3: model 037 */
+#define CM_SPD24SEL		0x00020000	/* 24bit spdif: model 037 */
+#define CM_SPDIF_INVERSE	0x00010000
+
+#define CM_ADCBITLEN_MASK	0x0000C000	
+#define CM_ADCBITLEN_16		0x00000000
+#define CM_ADCBITLEN_15		0x00004000
+#define CM_ADCBITLEN_14		0x00008000
+#define CM_ADCBITLEN_13		0x0000C000
+
+#define CM_ADCDACLEN_MASK	0x00003000
+#define CM_ADCDACLEN_060	0x00000000
+#define CM_ADCDACLEN_066	0x00001000
+#define CM_ADCDACLEN_130	0x00002000
+#define CM_ADCDACLEN_280	0x00003000
+
+#define CM_CH1_SRATE_176K	0x00000800
+#define CM_CH1_SRATE_88K	0x00000400
+#define CM_CH0_SRATE_176K	0x00000200
+#define CM_CH0_SRATE_88K	0x00000100
+
+#define CM_SPDIF_INVERSE2	0x00000080	/* model 055? */
+
+#define CM_CH1FMT_MASK		0x0000000C
+#define CM_CH1FMT_SHIFT		2
+#define CM_CH0FMT_MASK		0x00000003
+#define CM_CH0FMT_SHIFT		0
+
+#define CM_REG_INT_HLDCLR	0x0C
+#define CM_CHIP_MASK2		0xff000000
+#define CM_CHIP_039		0x04000000
+#define CM_CHIP_039_6CH		0x01000000
+#define CM_TDMA_INT_EN		0x00040000
+#define CM_CH1_INT_EN		0x00020000
+#define CM_CH0_INT_EN		0x00010000
+#define CM_INT_HOLD		0x00000002
+#define CM_INT_CLEAR		0x00000001
+
+#define CM_REG_INT_STATUS	0x10
+#define CM_INTR			0x80000000
+#define CM_UARTINT		0x00010000
+#define CM_LTDMAINT		0x00008000
+#define CM_HTDMAINT		0x00004000
+#define CM_CH1BUSY		0x00000008
+#define CM_CH0BUSY		0x00000004
+#define CM_CHINT1		0x00000002
+#define CM_CHINT0		0x00000001
+
+#define CM_REG_LEGACY_CTRL	0x14
+#define CM_NXCHG		0x80000000	/* h/w multi channels? */
+#define CM_VMPU_MASK		0x60000000	/* MPU401 i/o port address */
+#define CM_VMPU_330		0x00000000
+#define CM_VMPU_320		0x20000000
+#define CM_VMPU_310		0x40000000
+#define CM_VMPU_300		0x60000000
+#define CM_VSBSEL_MASK		0x0C000000	/* SB16 base address */
+#define CM_VSBSEL_220		0x00000000
+#define CM_VSBSEL_240		0x04000000
+#define CM_VSBSEL_260		0x08000000
+#define CM_VSBSEL_280		0x0C000000
+#define CM_FMSEL_MASK		0x03000000	/* FM OPL3 base address */
+#define CM_FMSEL_388		0x00000000
+#define CM_FMSEL_3C8		0x01000000
+#define CM_FMSEL_3E0		0x02000000
+#define CM_FMSEL_3E8		0x03000000
+#define CM_ENSPDOUT		0x00800000	/* enable XPDIF/OUT to I/O interface */
+#define CM_SPDCOPYRHT		0x00400000	/* set copyright spdif in/out */
+#define CM_DAC2SPDO		0x00200000	/* enable wave+fm_midi -> SPDIF/OUT */
+#define CM_SETRETRY		0x00010000	/* 0: legacy i/o wait (default), 1: legacy i/o bus retry */
+#define CM_CHB3D6C		0x00008000	/* 5.1 channels support */
+#define CM_LINE_AS_BASS		0x00006000	/* use line-in as bass */
+
+#define CM_REG_MISC_CTRL	0x18
+#define CM_PWD			0x80000000
+#define CM_RESET		0x40000000
+#define CM_SFIL_MASK		0x30000000
+#define CM_TXVX			0x08000000
+#define CM_N4SPK3D		0x04000000	/* 4ch output */
+#define CM_SPDO5V		0x02000000	/* 5V spdif output */
+#define CM_SPDIF48K		0x01000000	/* write */
+#define CM_SPATUS48K		0x01000000	/* read */
+#define CM_ENDBDAC		0x00800000	/* enable dual dac */
+#define CM_XCHGDAC		0x00400000	/* 0: front=ch0, 1: front=ch1 */
+#define CM_SPD32SEL		0x00200000	/* 0: 16bit SPDIF, 1: 32bit */
+#define CM_SPDFLOOPI		0x00100000	/* int. SPDIF-IN -> int. OUT */
+#define CM_FM_EN		0x00080000	/* enalbe FM */
+#define CM_AC3EN2		0x00040000	/* enable AC3: model 039 */
+#define CM_VIDWPDSB		0x00010000 
+#define CM_SPDF_AC97		0x00008000	/* 0: SPDIF/OUT 44.1K, 1: 48K */
+#define CM_MASK_EN		0x00004000
+#define CM_VIDWPPRT		0x00002000
+#define CM_SFILENB		0x00001000
+#define CM_MMODE_MASK		0x00000E00
+#define CM_SPDIF_SELECT		0x00000100	/* for model > 039 ? */
+#define CM_ENCENTER		0x00000080	/* shared with FLINKON? */
+#define CM_FLINKON		0x00000080
+#define CM_FLINKOFF		0x00000040
+#define CM_MIDSMP		0x00000010
+#define CM_UPDDMA_MASK		0x0000000C
+#define CM_TWAIT_MASK		0x00000003
+
+	/* byte */
+#define CM_REG_MIXER0		0x20
+
+#define CM_REG_SB16_DATA	0x22
+#define CM_REG_SB16_ADDR	0x23
+
+#define CM_REG_MIXER1		0x24
+#define CM_FMMUTE		0x80	/* mute FM */
+#define CM_FMMUTE_SHIFT		7
+#define CM_WSMUTE		0x40	/* mute PCM */
+#define CM_WSMUTE_SHIFT		6
+#define CM_SPK4			0x20	/* lin-in -> rear line out */
+#define CM_SPK4_SHIFT		5
+#define CM_REAR2FRONT		0x10	/* exchange rear/front */
+#define CM_REAR2FRONT_SHIFT	4
+#define CM_WAVEINL		0x08	/* digital wave rec. left chan */
+#define CM_WAVEINL_SHIFT	3
+#define CM_WAVEINR		0x04	/* digical wave rec. right */
+#define CM_WAVEINR_SHIFT	2
+#define CM_X3DEN		0x02	/* 3D surround enable */
+#define CM_X3DEN_SHIFT		1
+#define CM_CDPLAY		0x01	/* enable SPDIF/IN PCM -> DAC */
+#define CM_CDPLAY_SHIFT		0
+
+#define CM_REG_MIXER2		0x25
+#define CM_RAUXREN		0x80	/* AUX right capture */
+#define CM_RAUXREN_SHIFT	7
+#define CM_RAUXLEN		0x40	/* AUX left capture */
+#define CM_RAUXLEN_SHIFT	6
+#define CM_VAUXRM		0x20	/* AUX right mute */
+#define CM_VAUXRM_SHIFT		5
+#define CM_VAUXLM		0x10	/* AUX left mute */
+#define CM_VAUXLM_SHIFT		4
+#define CM_VADMIC_MASK		0x0e	/* mic gain level (0-3) << 1 */
+#define CM_VADMIC_SHIFT		1
+#define CM_MICGAINZ		0x01	/* mic boost */
+#define CM_MICGAINZ_SHIFT	0
+
+#define CM_REG_AUX_VOL		0x26
+#define CM_VAUXL_MASK		0xf0
+#define CM_VAUXR_MASK		0x0f
+
+#define CM_REG_MISC		0x27
+#define CM_XGPO1		0x20
+#define CM_XGPBIO		0x04
+#define CM_SPDVALID		0x02	/* spdif input valid check */
+#define CM_DMAUTO		0x01
+
+#define CM_REG_AC97		0x28	/* hmmm.. do we have ac97 link? */
+
+/*
+ * extended registers
+ */
+#define CM_REG_CH0_FRAME1	0x80	/* base address */
+#define CM_REG_CH0_FRAME2	0x84
+#define CM_REG_CH1_FRAME1	0x88	/* 0-15: count of samples at bus master; buffer size */
+#define CM_REG_CH1_FRAME2	0x8C	/* 16-31: count of samples at codec; fragment size */
+
+/*
+ * size of i/o region
+ */
+#define CM_EXTENT_CODEC	  0x100
+#define CM_EXTENT_MIDI	  0x2
+#define CM_EXTENT_SYNTH	  0x4
+
+/*
+ * pci ids
+ */
+#ifndef PCI_VENDOR_ID_CMEDIA
+#define PCI_VENDOR_ID_CMEDIA         0x13F6
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738  0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
+#endif
+
+/*
+ * channels for playback / capture
+ */
+#define CM_CH_PLAY	0
+#define CM_CH_CAPT	1
+
+/*
+ * flags to check device open/close
+ */
+#define CM_OPEN_NONE	0
+#define CM_OPEN_CH_MASK	0x01
+#define CM_OPEN_DAC	0x10
+#define CM_OPEN_ADC	0x20
+#define CM_OPEN_SPDIF	0x40
+#define CM_OPEN_PLAYBACK	(CM_CH_PLAY | CM_OPEN_DAC)
+#define CM_OPEN_PLAYBACK2	(CM_CH_CAPT | CM_OPEN_DAC)
+#define CM_OPEN_CAPTURE		(CM_CH_CAPT | CM_OPEN_ADC)
+#define CM_OPEN_SPDIF_PLAYBACK	(CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF)
+#define CM_OPEN_SPDIF_CAPTURE	(CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF)
+
+
+#if CM_CH_PLAY == 1
+#define CM_PLAYBACK_SRATE_176K	CM_CH1_SRATE_176K
+#define CM_PLAYBACK_SPDF	CM_SPDF_1
+#define CM_CAPTURE_SPDF		CM_SPDF_0
+#else
+#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K
+#define CM_PLAYBACK_SPDF	CM_SPDF_0
+#define CM_CAPTURE_SPDF		CM_SPDF_1
+#endif
+
+
+/*
+ * define this if you want soft ac3 encoding - it's still buggy..
+ */
+/* #define DO_SOFT_AC3 */
+
+
+/*
+ * driver data
+ */
+
+typedef struct snd_stru_cmipci cmipci_t;
+typedef struct snd_stru_cmipci_pcm cmipci_pcm_t;
+
+#define chip_t cmipci_t
+
+struct snd_stru_cmipci_pcm {
+	snd_pcm_substream_t *substream;
+	int running;		/* dac/adc running? */
+	unsigned int dma_size;	/* in frames */
+	unsigned int period_size;	/* in frames */
+	unsigned int offset;	/* physical address of the buffer */
+	unsigned int fmt;	/* format bits */
+	int ch;			/* channel (0/1) */
+	unsigned int is_dac;		/* is dac? */
+	int bytes_per_frame;
+	int shift;
+	int ac3_shift;	/* extra shift: 1 on soft ac3 mode */
+};
+
+/* mixer elements toggled/resumed during ac3 playback */
+struct cmipci_mixer_auto_switches {
+	const char *name;	/* switch to toggle */
+	int toggle_on;		/* value to change when ac3 mode */
+};
+static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = {
+	{"PCM Playback Switch", 0},
+	{"IEC958 Output Switch", 1},
+	{"IEC958 Mix Analog", 0},
+	// {"IEC958 Out To DAC", 1}, // no longer used
+	{"IEC958 Loop", 0},
+};
+#define CM_SAVED_MIXERS		(sizeof(cm_saved_mixer)/sizeof(cm_saved_mixer[0]))
+
+struct snd_stru_cmipci {
+	snd_card_t *card;
+
+	struct pci_dev *pci;
+	unsigned int device;	/* device ID */
+	int irq;
+
+	unsigned long iobase;
+	struct resource *res_iobase;
+	unsigned int ctrl;	/* FUNCTRL0 current value */
+
+	snd_pcm_t *pcm;		/* DAC/ADC PCM */
+	snd_pcm_t *pcm2;	/* 2nd DAC */
+	snd_pcm_t *pcm_spdif;	/* SPDIF */
+
+	int chip_version;
+	int max_channels;
+	unsigned int has_dual_dac: 1;
+	unsigned int can_ac3_sw: 1;
+	unsigned int can_ac3_hw: 1;
+	unsigned int can_multi_ch: 1;
+
+	unsigned int spdif_playback_avail: 1;	/* spdif ready? */
+	unsigned int spdif_playback_enabled: 1;	/* spdif switch enabled? */
+	int spdif_counter;	/* for software AC3 */
+
+	unsigned int dig_status;
+	unsigned int dig_pcm_status;
+	snd_kcontrol_t *spdif_pcm_ctl;
+
+	snd_pcm_hardware_t *hw_info[3]; /* for playbacks */
+
+	int opened[2];	/* open mode */
+	struct semaphore open_mutex;
+
+	int mixer_insensitive: 1;
+	snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS];
+	int mixer_res_status[CM_SAVED_MIXERS];
+
+	opl3_t *opl3;
+	snd_hwdep_t *opl3hwdep;
+
+	cmipci_pcm_t channel[2];	/* ch0 - DAC, ch1 - ADC or 2nd DAC */
+
+	/* external MIDI */
+	snd_rawmidi_t *rmidi;
+
+	spinlock_t reg_lock;
+	snd_info_entry_t *proc_entry;
+};
+
+
+/* read/write operations for dword register */
+inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data)
+{
+	outl(data, cm->iobase + cmd);
+}
+inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd)
+{
+	return inl(cm->iobase + cmd);
+}
+
+/* read/write operations for word register */
+inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data)
+{
+	outw(data, cm->iobase + cmd);
+}
+inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd)
+{
+	return inw(cm->iobase + cmd);
+}
+
+/* read/write operations for byte register */
+inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data)
+{
+	outb(data, cm->iobase + cmd);
+}
+
+inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd)
+{
+	return inb(cm->iobase + cmd);
+}
+
+/* bit operations for dword register */
+static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+{
+	unsigned int val;
+	val = inl(cm->iobase + cmd);
+	val |= flag;
+	outl(val, cm->iobase + cmd);
+}
+
+static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+{
+	unsigned int val;
+	val = inl(cm->iobase + cmd);
+	val &= ~flag;
+	outl(val, cm->iobase + cmd);
+}
+
+#if 0 // not used
+/* bit operations for byte register */
+static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+{
+	unsigned char val;
+	val = inb(cm->iobase + cmd);
+	val |= flag;
+	outb(val, cm->iobase + cmd);
+}
+
+static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+{
+	unsigned char val;
+	val = inb(cm->iobase + cmd);
+	val &= ~flag;
+	outb(val, cm->iobase + cmd);
+}
+#endif
+
+
+/*
+ * PCM interface
+ */
+
+/*
+ * calculate frequency
+ */
+
+static int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
+#define RATES (sizeof(rates) / sizeof(rates[0]))
+
+static unsigned int snd_cmipci_rate_freq(unsigned int rate)
+{
+	int i;
+	for (i = 0; i < RATES; i++) {
+		if (rates[i] == rate)
+			return i;
+	}
+	snd_BUG();
+	return 0;
+}
+
+static int snd_cmipci_hw_params(snd_pcm_substream_t * substream,
+				snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_cmipci_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+
+/*
+ */
+
+static unsigned int hw_channels[] = {1, 2, 4, 5, 6};
+static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = {
+	count: 3,
+	list: hw_channels,
+	mask: 0,
+};
+static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = {
+	count: 5,
+	list: hw_channels,
+	mask: 0,
+};
+
+static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels)
+{
+	unsigned long flags;
+
+	if (channels > 2) {
+		if (! cm->can_multi_ch)
+			return -EINVAL;
+		if (rec->fmt != 0x03) /* stereo 16bit only */
+			return -EINVAL;
+
+		spin_lock_irqsave(&cm->reg_lock, flags);
+		snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
+		if (channels > 4) {
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+		} else {
+			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+		}
+		if (channels == 6) {
+			snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+		} else {
+			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+		}
+		spin_unlock_irqrestore(&cm->reg_lock, flags);
+
+	} else {
+		if (cm->can_multi_ch) {
+			spin_lock_irqsave(&cm->reg_lock, flags);
+			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+			spin_unlock_irqrestore(&cm->reg_lock, flags);
+		}
+	}
+	return 0;
+}
+
+
+/*
+ * prepare playback/capture channel
+ * channel to be used must have been set in rec->ch.
+ */
+static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec,
+				 snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	unsigned int reg, freq, val;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	rec->fmt = 0;
+	rec->shift = 0;
+	if (snd_pcm_format_width(runtime->format) >= 16) {
+		rec->fmt |= 0x02;
+		if (snd_pcm_format_width(runtime->format) > 16)
+			rec->shift++; /* 24/32bit */
+	}
+	if (runtime->channels > 1)
+		rec->fmt |= 0x01;
+	if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) {
+		snd_printd("cannot set dac channels\n");
+		return -EINVAL;
+	}
+
+	rec->offset = runtime->dma_addr;
+	/* buffer and period sizes in frame */
+	rec->dma_size = runtime->buffer_size << rec->shift;
+	rec->period_size = runtime->period_size << rec->shift;
+	rec->dma_size <<= rec->ac3_shift;
+	rec->period_size <<= rec->ac3_shift;
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+
+	/* set buffer address */
+	reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
+	snd_cmipci_write(cm, reg, rec->offset);
+	/* program sample counts */
+	reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
+	snd_cmipci_write_w(cm, reg, rec->dma_size - 1);
+	snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1);
+
+	/* set adc/dac flag */
+	val = rec->ch ? CM_CHADC1 : CM_CHADC0;
+	if (rec->is_dac)
+		cm->ctrl &= ~val;
+	else
+		cm->ctrl |= val;
+	snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+	//snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+
+	/* set sample rate */
+	freq = snd_cmipci_rate_freq(runtime->rate);
+	val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
+	if (rec->ch) {
+		val &= ~CM_ASFC_MASK;
+		val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;
+	} else {
+		val &= ~CM_DSFC_MASK;
+		val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK;
+	}
+	snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
+	//snd_printd("cmipci: functrl1 = %08x\n", val);
+
+	/* set format */
+	val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
+	if (rec->ch) {
+		val &= ~CM_CH1FMT_MASK;
+		val |= rec->fmt << CM_CH1FMT_SHIFT;
+	} else {
+		val &= ~CM_CH0FMT_MASK;
+		val |= rec->fmt << CM_CH0FMT_SHIFT;
+	}
+	snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+	//snd_printd("cmipci: chformat = %08x\n", val);
+
+	rec->running = 0;
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+
+	return 0;
+}
+
+/*
+ * PCM trigger/stop
+ */
+static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec,
+				 snd_pcm_substream_t *substream, int cmd)
+{
+	unsigned int inthld, chen, reset, pause;
+	int result = 0;
+
+	inthld = CM_CH0_INT_EN << rec->ch;
+	chen = CM_CHEN0 << rec->ch;
+	reset = CM_RST_CH0 << rec->ch;
+	pause = CM_PAUSE0 << rec->ch;
+
+	spin_lock(&cm->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		rec->running = 1;
+		/* set interrupt */
+		snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld);
+		cm->ctrl |= chen;
+		/* enable channel */
+		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+		//snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		rec->running = 0;
+		/* disable interrupt */
+		snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld);
+		/* reset */
+		cm->ctrl &= ~chen;
+		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);
+		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		cm->ctrl |= pause;
+		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		cm->ctrl &= ~pause;
+		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&cm->reg_lock);
+	return result;
+}
+
+/*
+ * return the current pointer
+ */
+static snd_pcm_uframes_t snd_cmipci_pcm_pointer(cmipci_t *cm, cmipci_pcm_t *rec,
+					  snd_pcm_substream_t *substream)
+{
+	size_t ptr;
+	unsigned int reg;
+	if (!rec->running)
+		return 0;
+#if 1 // this seems better..
+	reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
+	ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1);
+	ptr >>= rec->shift;
+#else
+	reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
+	ptr = snd_cmipci_read(cm, reg) - rec->offset;
+	ptr = bytes_to_frames(substream->runtime, ptr);
+#endif
+	ptr >>= rec->ac3_shift;
+	return ptr;
+}
+
+/*
+ * playback
+ */
+
+static int snd_cmipci_playback_trigger(snd_pcm_substream_t *substream,
+				       int cmd)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_cmipci_playback_pointer(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+
+
+/*
+ * capture
+ */
+
+static int snd_cmipci_capture_trigger(snd_pcm_substream_t *substream,
+				     int cmd)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_cmipci_capture_pointer(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+#ifdef DO_SOFT_AC3
+/*
+ * special tricks for soft ac3 transfer:
+ *
+ * we compose an iec958 subframe from 16bit ac3 sample and
+ * write the raw subframe via 32bit data mode.
+ */
+
+/* find parity for bit 4~30 */
+static unsigned parity(unsigned int data)
+{
+	unsigned int parity = 0;
+	int counter = 4;
+
+	data >>= 4;	/* start from bit 4 */
+	while (counter <= 30) {
+		if (data & 1)
+			parity++;
+		data >>= 1;
+		counter++;
+	}
+	return parity & 1;
+}
+
+/*
+ * compose 32bit iec958 subframe with non-audio data.
+ * bit 0-3  = preamble
+ *     4-7  = aux (=0)
+ *     8-27 = data (12-27 for 16bit)
+ *     28   = validity (=0)
+ *     29   = user data (=0)
+ *     30   = channel status
+ *     31   = parity
+ *
+ * channel status is assumed as consumer, non-audio
+ * thus all 0 except bit 1
+ */
+inline static u32 convert_ac3_32bit(cmipci_t *cm, u32 val)
+{
+	u32 data = (u32)val << 12;
+
+	if (cm->spdif_counter == 2 || cm->spdif_counter == 3) /* bit 1 */
+		data |= 0x40000000;	/* indicate AC-3 raw data */
+	if (parity(data))		/* parity bit 4-30 */
+		data |= 0x80000000;
+	if (cm->spdif_counter == 0)
+		data |= 3;		/* preamble 'M' */
+	else if (cm->spdif_counter & 1)
+		data |= 5;		/* odd, 'W' */
+	else
+		data |= 9;		/* even, 'M' */
+
+	cm->spdif_counter++;
+	if (cm->spdif_counter == 384)
+		cm->spdif_counter = 0;
+
+	return data;
+}
+
+
+static int snd_cmipci_ac3_copy(snd_pcm_substream_t *subs, int channel,
+			       snd_pcm_uframes_t pos, void *src,
+			       snd_pcm_uframes_t count)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(subs);
+	u32 *dst;
+	u16 *srcp = src, val;
+	snd_pcm_uframes_t offset;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+
+	if (! cm->channel[CM_CH_PLAY].ac3_shift)
+		return copy_from_user(runtime->dma_area + frames_to_bytes(runtime, pos),
+				      src, frames_to_bytes(runtime, count));
+
+	if (! access_ok(VERIFY_READ, src, count))
+		return -EFAULT;
+
+	/* frame = 16bit stereo */
+	offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2);
+	dst = (u32*)(runtime->dma_area + offset);
+
+	count /= 2;
+	while (count-- > 0) {
+		get_user(val, srcp);
+		srcp++;
+		*dst++ = convert_ac3_32bit(cm, val);
+	}
+
+	return 0;
+}
+
+static int snd_cmipci_ac3_silence(snd_pcm_substream_t *subs, int channel,
+				  snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(subs);
+	u32 *dst;
+	snd_pcm_uframes_t offset;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+
+	if (! cm->channel[CM_CH_PLAY].ac3_shift)
+		return snd_pcm_format_set_silence(runtime->format,
+						  runtime->dma_area + frames_to_bytes(runtime, pos), count);
+	
+	/* frame = 16bit stereo */
+	offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2);
+	dst = (u32*)(subs->runtime->dma_area + offset);
+
+	count /= 2;
+	while (count-- > 0) {
+		*dst++ = convert_ac3_32bit(cm, 0);
+	}
+
+	return 0;
+}
+#endif /* DO_SOFT_AC3 */
+
+
+/*
+ * hw preparation for spdif
+ */
+
+static int snd_cmipci_spdif_default_info(snd_kcontrol_t *kcontrol,
+					 snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_cmipci_spdif_default_get(snd_kcontrol_t *kcontrol,
+					snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 4; i++)
+		ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cmipci_spdif_default_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int i, change;
+	unsigned int val;
+
+	val = 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 4; i++)
+		val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
+	change = val != chip->dig_status;
+	chip->dig_status = val;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_cmipci_spdif_default_info,
+	get:		snd_cmipci_spdif_default_get,
+	put:		snd_cmipci_spdif_default_put
+};
+
+static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol,
+				      snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol,
+				     snd_ctl_elem_value_t *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_cmipci_spdif_mask_info,
+	get:		snd_cmipci_spdif_mask_get,
+};
+
+static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol,
+					snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_cmipci_spdif_stream_get(snd_kcontrol_t *kcontrol,
+				       snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 4; i++)
+		ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cmipci_spdif_stream_put(snd_kcontrol_t *kcontrol,
+				       snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int i, change;
+	unsigned int val;
+
+	val = 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 4; i++)
+		val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
+	change = val != chip->dig_pcm_status;
+	chip->dig_pcm_status = val;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_cmipci_spdif_stream_info,
+	get:		snd_cmipci_spdif_stream_get,
+	put:		snd_cmipci_spdif_stream_put
+};
+
+/*
+ */
+
+/* save mixer setting and mute for AC3 playback */
+static void save_mixer_state(cmipci_t *cm)
+{
+	if (! cm->mixer_insensitive) {
+		int i;
+		for (i = 0; i < CM_SAVED_MIXERS; i++) {
+			snd_kcontrol_t *ctl = cm->mixer_res_ctl[i];
+			if (ctl) {
+				snd_ctl_elem_value_t val;
+				int event;
+				memset(&val, 0, sizeof(val));
+				ctl->get(ctl, &val);
+				cm->mixer_res_status[i] = val.value.integer.value[0];
+				val.value.integer.value[0] = cm_saved_mixer[i].toggle_on;
+				event = SNDRV_CTL_EVENT_MASK_INFO;
+				if (cm->mixer_res_status[i] != val.value.integer.value[0]) {
+					ctl->put(ctl, &val); /* toggle */
+					event |= SNDRV_CTL_EVENT_MASK_VALUE;
+				}
+				ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+				snd_ctl_notify(cm->card, event, &ctl->id);
+			}
+		}
+		cm->mixer_insensitive = 1;
+	}
+}
+
+
+/* restore the previously saved mixer status */
+static void restore_mixer_state(cmipci_t *cm)
+{
+	if (cm->mixer_insensitive) {
+		int i;
+		cm->mixer_insensitive = 0; /* at first clear this;
+					      otherwise the changes will be ignored */
+		for (i = 0; i < CM_SAVED_MIXERS; i++) {
+			snd_kcontrol_t *ctl = cm->mixer_res_ctl[i];
+			if (ctl) {
+				snd_ctl_elem_value_t val;
+				int event;
+
+				memset(&val, 0, sizeof(val));
+				ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+				ctl->get(ctl, &val);
+				event = SNDRV_CTL_EVENT_MASK_INFO;
+				if (val.value.integer.value[0] != cm->mixer_res_status[i]) {
+					val.value.integer.value[0] = cm->mixer_res_status[i];
+					ctl->put(ctl, &val);
+					event |= SNDRV_CTL_EVENT_MASK_VALUE;
+				}
+				snd_ctl_notify(cm->card, event, &ctl->id);
+			}
+		}
+	}
+}
+
+/* spinlock held! */
+static void setup_ac3(cmipci_t *cm, int do_ac3, int rate)
+{
+	cm->channel[CM_CH_PLAY].ac3_shift = 0;
+	cm->spdif_counter = 0;
+
+	if (do_ac3) {
+		/* AC3EN for 037 */
+		snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1);
+		/* AC3EN for 039 */
+		snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2);
+	
+		if (cm->can_ac3_hw) {
+			/* SPD24SEL for 037, 0x02 */
+			/* SPD24SEL for 039, 0x20, but cannot be set */
+			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+		} else { /* can_ac3_sw */
+#ifdef DO_SOFT_AC3
+			/* FIXME: ugly hack! */
+			subs->runtime->buffer_size /= 2;
+			/* SPD32SEL for 037 & 039, 0x20 */
+			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+			/* set 176K sample rate to fix 033 HW bug */
+			if (cm->chip_version == 33) {
+				if (rate >= 48000) {
+					snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_176K);
+				} else {
+					snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+				}
+			}
+			cm->channel[CM_CH_PLAY].ac3_shift = 1; /* use 32bit */
+#endif /* DO_SOFT_AC3 */
+		}
+
+	} else {
+		snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1);
+		snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2);
+
+		if (cm->can_ac3_hw) {
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+		} else {
+			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+		}
+	}
+}
+
+static void setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3)
+{
+	int rate;
+	unsigned long flags;
+
+	rate = subs->runtime->rate;
+
+	if (up && do_ac3)
+		save_mixer_state(cm);
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	cm->spdif_playback_avail = up;
+	if (up) {
+		/* they are controlled via "IEC958 Output Switch" */
+		/* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
+		/* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+		if (cm->spdif_playback_enabled)
+			snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+		setup_ac3(cm, do_ac3, rate);
+
+		if (rate == 48000)
+			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
+		else
+			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
+
+	} else {
+		/* they are controlled via "IEC958 Output Switch" */
+		/* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
+		/* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+		snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+		setup_ac3(cm, 0, 0);
+	}
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+}
+
+
+/*
+ * preparation
+ */
+
+/* playback - enable spdif only on the certain condition */
+static int snd_cmipci_playback_prepare(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	int rate = substream->runtime->rate;
+	int do_spdif, do_ac3;
+	do_spdif = ((rate == 44100 || rate == 48000) &&
+		    substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
+		    substream->runtime->channels == 2);
+	do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
+#ifdef DO_SOFT_AC3
+	if (do_ac3 && cm->can_ac3_sw)
+		do_spdif = 0;
+#endif
+	setup_spdif_playback(cm, substream, do_spdif, do_ac3);
+	return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+/* playback  (via device #2) - enable spdif always */
+static int snd_cmipci_playback_spdif_prepare(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	setup_spdif_playback(cm, substream, 1, cm->dig_pcm_status & IEC958_AES0_NONAUDIO);
+	return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+static int snd_cmipci_playback_hw_free(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	setup_spdif_playback(cm, substream, 0, 0);
+	restore_mixer_state(cm);
+	return snd_cmipci_hw_free(substream);
+}
+
+/* capture */
+static int snd_cmipci_capture_prepare(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+/* capture with spdif (via device #2) */
+static int snd_cmipci_capture_spdif_prepare(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+
+	return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+static int snd_cmipci_capture_spdif_hw_free(snd_pcm_substream_t *subs)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(subs);
+	unsigned long flags;
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+
+	return snd_cmipci_hw_free(subs);
+}
+
+
+/*
+ * interrupt handler
+ */
+static void snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	cmipci_t *cm = snd_magic_cast(cmipci_t, dev_id, return);
+	unsigned int status;
+	
+	/* fastpath out, to ease interrupt sharing */
+	status = snd_cmipci_read(cm, CM_REG_INT_STATUS);
+	if (!(status & CM_INTR))
+		return;
+
+	/* acknowledge interrupt */
+	spin_lock(&cm->reg_lock);
+	if (status & CM_CHINT0) {
+		snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, CM_CH0_INT_EN);
+		snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, CM_CH0_INT_EN);
+	}
+	if (status & CM_CHINT1) {
+		snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, CM_CH1_INT_EN);
+		snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, CM_CH1_INT_EN);
+	}
+	spin_unlock(&cm->reg_lock);
+
+	if (cm->rmidi && (status & CM_UARTINT))
+		snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs);
+
+	if (cm->pcm) {
+		if ((status & CM_CHINT0) && cm->channel[0].running)
+			snd_pcm_period_elapsed(cm->channel[0].substream);
+		if ((status & CM_CHINT1) && cm->channel[1].running)
+			snd_pcm_period_elapsed(cm->channel[1].substream);
+	}
+}
+
+/*
+ * h/w infos
+ */
+
+/* playback on channel A */
+static snd_pcm_hardware_t snd_cmipci_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/* capture on channel B */
+static snd_pcm_hardware_t snd_cmipci_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/* playback on channel B - stereo 16bit only? */
+static snd_pcm_hardware_t snd_cmipci_playback2 =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/* spdif playback on channel A */
+static snd_pcm_hardware_t snd_cmipci_playback_spdif =
+{
+	info:			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/,
+	rates:			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	rate_min:		44100,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/* spdif capture on channel B */
+static snd_pcm_hardware_t snd_cmipci_capture_spdif =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:	        SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	rate_min:		44100,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ * check device open/close
+ */
+static int open_device_check(cmipci_t *cm, int mode, snd_pcm_substream_t *subs)
+{
+	unsigned long flags;
+	int ch = mode & CM_OPEN_CH_MASK;
+
+	/* FIXME: a file should wait until the device becomes free
+	 * when it's opened on blocking mode.  however, since the current
+	 * pcm framework doesn't pass file pointer before actually opened,
+	 * we can't know whether blocking mode or not in open callback..
+	 */
+	down(&cm->open_mutex);
+	if (cm->opened[ch]) {
+		up(&cm->open_mutex);
+		return -EBUSY;
+	}
+	cm->opened[ch] = mode;
+	cm->channel[ch].substream = subs;
+	if (! (mode & CM_OPEN_DAC)) {
+		/* disable dual DAC mode */
+		cm->channel[ch].is_dac = 0;
+		spin_lock_irqsave(&cm->reg_lock, flags);
+		snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
+		spin_unlock_irqrestore(&cm->reg_lock, flags);
+	}
+	up(&cm->open_mutex);
+	return 0;
+}
+
+static void close_device_check(cmipci_t *cm, int mode)
+{
+	unsigned long flags;
+	int ch = mode & CM_OPEN_CH_MASK;
+
+	down(&cm->open_mutex);
+	if (cm->opened[ch] == mode) {
+		cm->channel[ch].running = 0;
+		cm->channel[ch].substream = NULL;
+		cm->opened[ch] = 0;
+		if (! cm->channel[ch].is_dac) {
+			/* enable dual DAC mode again */
+			cm->channel[ch].is_dac = 1;
+			spin_lock_irqsave(&cm->reg_lock, flags);
+			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
+			spin_unlock_irqrestore(&cm->reg_lock, flags);
+		}
+	}
+	up(&cm->open_mutex);
+}
+
+/*
+ */
+
+static int snd_cmipci_playback_open(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0)
+		return err;
+	runtime->hw = snd_cmipci_playback;
+	if (cm->can_multi_ch) {
+		runtime->hw.channels_max = cm->max_channels;
+		if (cm->max_channels == 4)
+			snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4);
+		else
+			snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6);
+	}
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+	return 0;
+}
+
+static int snd_cmipci_capture_open(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0)
+		return err;
+	runtime->hw = snd_cmipci_capture;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+	return 0;
+}
+
+static int snd_cmipci_playback2_open(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */
+		return err;
+	runtime->hw = snd_cmipci_playback2;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+	return 0;
+}
+
+static int snd_cmipci_playback_spdif_open(snd_pcm_substream_t *substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */
+		return err;
+	runtime->hw = snd_cmipci_playback_spdif;
+	if (cm->can_ac3_hw) {
+		runtime->hw.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID;
+	}
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+	cm->dig_pcm_status = cm->dig_status;
+	return 0;
+}
+
+static int snd_cmipci_capture_spdif_open(snd_pcm_substream_t * substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
+		return err;
+	runtime->hw = snd_cmipci_capture_spdif;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+	return 0;
+}
+
+
+/*
+ */
+
+static int snd_cmipci_playback_close(snd_pcm_substream_t * substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	close_device_check(cm, CM_OPEN_PLAYBACK);
+	return 0;
+}
+
+static int snd_cmipci_capture_close(snd_pcm_substream_t * substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	close_device_check(cm, CM_OPEN_CAPTURE);
+	return 0;
+}
+
+static int snd_cmipci_playback2_close(snd_pcm_substream_t * substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	close_device_check(cm, CM_OPEN_PLAYBACK2);
+	return 0;
+}
+
+static int snd_cmipci_playback_spdif_close(snd_pcm_substream_t * substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK);
+	return 0;
+}
+
+static int snd_cmipci_capture_spdif_close(snd_pcm_substream_t * substream)
+{
+	cmipci_t *cm = snd_pcm_substream_chip(substream);
+	close_device_check(cm, CM_OPEN_SPDIF_CAPTURE);
+	return 0;
+}
+
+
+/*
+ */
+
+static snd_pcm_ops_t snd_cmipci_playback_ops = {
+	open:		snd_cmipci_playback_open,
+	close:		snd_cmipci_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cmipci_hw_params,
+	hw_free:	snd_cmipci_playback_hw_free,
+	prepare:	snd_cmipci_playback_prepare,
+	trigger:	snd_cmipci_playback_trigger,
+	pointer:	snd_cmipci_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_cmipci_capture_ops = {
+	open:		snd_cmipci_capture_open,
+	close:		snd_cmipci_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cmipci_hw_params,
+	hw_free:	snd_cmipci_hw_free,
+	prepare:	snd_cmipci_capture_prepare,
+	trigger:	snd_cmipci_capture_trigger,
+	pointer:	snd_cmipci_capture_pointer,
+};
+
+static snd_pcm_ops_t snd_cmipci_playback2_ops = {
+	open:		snd_cmipci_playback2_open,
+	close:		snd_cmipci_playback2_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cmipci_hw_params,
+	hw_free:	snd_cmipci_hw_free,
+	prepare:	snd_cmipci_capture_prepare,	/* channel B */
+	trigger:	snd_cmipci_capture_trigger,	/* channel B */
+	pointer:	snd_cmipci_capture_pointer,	/* channel B */
+};
+
+static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = {
+	open:		snd_cmipci_playback_spdif_open,
+	close:		snd_cmipci_playback_spdif_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cmipci_hw_params,
+	hw_free:	snd_cmipci_playback_hw_free,
+	prepare:	snd_cmipci_playback_spdif_prepare,	/* set up rate */
+	trigger:	snd_cmipci_playback_trigger,
+	pointer:	snd_cmipci_playback_pointer,
+#ifdef DO_SOFT_AC3
+	copy:		snd_cmipci_ac3_copy,
+	silence:	snd_cmipci_ac3_silence,
+#endif
+};
+
+static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = {
+	open:		snd_cmipci_capture_spdif_open,
+	close:		snd_cmipci_capture_spdif_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cmipci_hw_params,
+	hw_free:	snd_cmipci_capture_spdif_hw_free,
+	prepare:	snd_cmipci_capture_spdif_prepare,
+	trigger:	snd_cmipci_capture_trigger,
+	pointer:	snd_cmipci_capture_pointer,
+};
+
+
+/*
+ */
+
+static void snd_cmipci_pcm_free(snd_pcm_t *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_cmipci_pcm_new(cmipci_t *cm, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops);
+
+	pcm->private_data = cm;
+	pcm->private_free = snd_cmipci_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "C-Media PCI DAC/ADC");
+	cm->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
+
+	return 0;
+}
+
+static int __devinit snd_cmipci_pcm2_new(cmipci_t *cm, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops);
+
+	pcm->private_data = cm;
+	pcm->private_free = snd_cmipci_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "C-Media PCI 2nd DAC");
+	cm->pcm2 = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
+
+	return 0;
+}
+
+static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops);
+
+	pcm->private_data = cm;
+	pcm->private_free = snd_cmipci_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "C-Media PCI IEC958");
+	cm->pcm_spdif = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
+
+	return 0;
+}
+
+/*
+ * mixer interface:
+ * - CM8338/8738 has a compatible mixer interface with SB16, but
+ *   lack of some elements like tone control, i/o gain and AGC.
+ * - Access to native registers:
+ *   - A 3D switch
+ *   - Output mute switches
+ */
+
+static void snd_cmipci_mixer_write(cmipci_t *s, unsigned char idx, unsigned char data)
+{
+	outb(idx, s->iobase + CM_REG_SB16_ADDR);
+	outb(data, s->iobase + CM_REG_SB16_DATA);
+}
+
+static unsigned char snd_cmipci_mixer_read(cmipci_t *s, unsigned char idx)
+{
+	unsigned char v;
+
+	outb(idx, s->iobase + CM_REG_SB16_ADDR);
+	v = inb(s->iobase + CM_REG_SB16_DATA);
+	return v;
+}
+
+/*
+ * general mixer element
+ */
+typedef struct cmipci_sb_reg {
+	unsigned int left_reg, right_reg;
+	unsigned int left_shift, right_shift;
+	unsigned int mask;
+	unsigned int invert: 1;
+	unsigned int stereo: 1;
+} cmipci_sb_reg_t;
+
+#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \
+ ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23))
+
+#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
+  info: snd_cmipci_info_volume, \
+  get: snd_cmipci_get_volume, put: snd_cmipci_put_volume, \
+  private_value: COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \
+}
+
+#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1)
+#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0)
+#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1)
+#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0)
+
+static void cmipci_sb_reg_decode(cmipci_sb_reg_t *r, unsigned long val)
+{
+	r->left_reg = val & 0xff;
+	r->right_reg = (val >> 8) & 0xff;
+	r->left_shift = (val >> 16) & 0x07;
+	r->right_shift = (val >> 19) & 0x07;
+	r->invert = (val >> 22) & 1;
+	r->stereo = (val >> 23) & 1;
+	r->mask = (val >> 24) & 0xff;
+}
+
+static int snd_cmipci_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	cmipci_sb_reg_t reg;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = reg.stereo + 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = reg.mask;
+	return 0;
+}
+ 
+static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	cmipci_sb_reg_t reg;
+	int val;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask;
+	if (reg.invert)
+		val = reg.mask - val;
+	ucontrol->value.integer.value[0] = val;
+	if (reg.stereo) {
+		val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
+		if (reg.invert)
+			val = reg.mask - val;
+		 ucontrol->value.integer.value[1] = val;
+	}
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	cmipci_sb_reg_t reg;
+	int change;
+	int left, right, oleft, oright;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	left = ucontrol->value.integer.value[0] & reg.mask;
+	if (reg.invert)
+		left = reg.mask - left;
+	left <<= reg.left_shift;
+	if (reg.stereo) {
+		right = ucontrol->value.integer.value[1] & reg.mask;
+		if (reg.invert)
+			right = reg.mask - right;
+		right <<= reg.right_shift;
+	} else
+		right = 0;
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	oleft = snd_cmipci_mixer_read(cm, reg.left_reg);
+	left |= oleft & ~(reg.mask << reg.left_shift);
+	change = left != oleft;
+	if (reg.stereo) {
+		if (reg.left_reg != reg.right_reg) {
+			snd_cmipci_mixer_write(cm, reg.left_reg, left);
+			oright = snd_cmipci_mixer_read(cm, reg.right_reg);
+		} else
+			oright = left;
+		right |= oright & ~(reg.mask << reg.right_shift);
+		change |= right != oright;
+		snd_cmipci_mixer_write(cm, reg.right_reg, right);
+	} else
+		snd_cmipci_mixer_write(cm, reg.left_reg, left);
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return change;
+}
+
+/*
+ * input route (left,right) -> (left,right)
+ */
+#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
+  info: snd_cmipci_info_input_sw, \
+  get: snd_cmipci_get_input_sw, put: snd_cmipci_put_input_sw, \
+  private_value: COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \
+}
+
+static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+ 
+static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	cmipci_sb_reg_t reg;
+	int val1, val2;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	val1 = snd_cmipci_mixer_read(cm, reg.left_reg);
+	val2 = snd_cmipci_mixer_read(cm, reg.right_reg);
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1;
+	ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1;
+	ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1;
+	ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1;
+	return 0;
+}
+
+static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	cmipci_sb_reg_t reg;
+	int change;
+	int val1, val2, oval1, oval2;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	oval1 = snd_cmipci_mixer_read(cm, reg.left_reg);
+	oval2 = snd_cmipci_mixer_read(cm, reg.right_reg);
+	val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
+	val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
+	val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift;
+	val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift;
+	val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift;
+	val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift;
+	change = val1 != oval1 || val2 != oval2;
+	snd_cmipci_mixer_write(cm, reg.left_reg, val1);
+	snd_cmipci_mixer_write(cm, reg.right_reg, val2);
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return change;
+}
+
+/*
+ * native mixer switches/volumes
+ */
+
+#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
+  info: snd_cmipci_info_native_mixer, \
+  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
+  private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \
+}
+
+#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
+  info: snd_cmipci_info_native_mixer, \
+  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
+  private_value: COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \
+}
+
+#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
+  info: snd_cmipci_info_native_mixer, \
+  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
+  private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \
+}
+
+#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
+  info: snd_cmipci_info_native_mixer, \
+  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
+  private_value: COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \
+}
+
+static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	cmipci_sb_reg_t reg;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = reg.stereo + 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = reg.mask;
+	return 0;
+
+}
+
+static int snd_cmipci_get_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	cmipci_sb_reg_t reg;
+	unsigned long flags;
+	unsigned char oreg, val;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	oreg = inb(cm->iobase + reg.left_reg);
+	val = (oreg >> reg.left_shift) & reg.mask;
+	if (reg.invert)
+		val = reg.mask - val;
+	ucontrol->value.integer.value[0] = val;
+	if (reg.stereo) {
+		val = (oreg >> reg.right_shift) & reg.mask;
+		if (reg.invert)
+			val = reg.mask - val;
+		ucontrol->value.integer.value[1] = val;
+	}
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cmipci_put_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	cmipci_sb_reg_t reg;
+	unsigned long flags;
+	unsigned char oreg, nreg, val;
+
+	cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	oreg = inb(cm->iobase + reg.left_reg);
+	val = ucontrol->value.integer.value[0] & reg.mask;
+	if (reg.invert)
+		val = reg.mask - val;
+	nreg = oreg & ~(reg.mask << reg.left_shift);
+	nreg |= (val << reg.left_shift);
+	if (reg.stereo) {
+		val = ucontrol->value.integer.value[1] & reg.mask;
+		if (reg.invert)
+			val = reg.mask - val;
+		nreg &= ~(reg.mask << reg.right_shift);
+		nreg |= (val << reg.right_shift);
+	}
+	outb(nreg, cm->iobase + reg.left_reg);
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return (nreg != oreg);
+}
+
+/*
+ * special case - check mixer sensitivity
+ */
+static int snd_cmipci_get_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	//cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	return snd_cmipci_get_native_mixer(kcontrol, ucontrol);
+}
+
+static int snd_cmipci_put_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	if (cm->mixer_insensitive) {
+		/* ignored */
+		return 0;
+	}
+	return snd_cmipci_put_native_mixer(kcontrol, ucontrol);
+}
+
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = {
+	CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31),
+	CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0),
+	CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31),
+	//CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1),
+	{ /* switch with sensitivity */
+		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+		name: "PCM Playback Switch",
+		info: snd_cmipci_info_native_mixer,
+		get: snd_cmipci_get_native_mixer_sensitive,
+		put: snd_cmipci_put_native_mixer_sensitive,
+		private_value: COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0),
+	},
+	CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0),
+	CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31),
+	CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1),
+	CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5),
+	CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31),
+	CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1),
+	CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1),
+	CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31),
+	CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3),
+	CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3),
+	CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
+	CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
+	CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
+	CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+	CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
+	CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 1),
+	CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
+	CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
+	CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
+};
+
+/*
+ * other switches
+ */
+
+typedef struct snd_cmipci_switch_args {
+	int reg;		/* register index */
+	unsigned int mask;	/* mask bits */
+	unsigned int mask_on;	/* mask bits to turn on */
+	int is_byte: 1;		/* byte access? */
+	int ac3_sensitive: 1;	/* access forbidden during non-audio operation? */
+} snd_cmipci_switch_args_t;
+
+static int snd_cmipci_uswitch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args)
+{
+	unsigned long flags;
+	unsigned int val;
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	if (args->ac3_sensitive && cm->mixer_insensitive) {
+		ucontrol->value.integer.value[0] = 0;
+		spin_unlock_irqrestore(&cm->reg_lock, flags);
+		return 0;
+	}
+	if (args->is_byte)
+		val = inb(cm->iobase + args->reg);
+	else
+		val = snd_cmipci_read(cm, args->reg);
+	ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0;
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value;
+	snd_assert(args != NULL, return -EINVAL);
+	return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);
+}
+
+static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args)
+{
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	if (args->ac3_sensitive && cm->mixer_insensitive) {
+		/* ignored */
+		spin_unlock_irqrestore(&cm->reg_lock, flags);
+		return 0;
+	}
+	if (args->is_byte)
+		val = inb(cm->iobase + args->reg);
+	else
+		val = snd_cmipci_read(cm, args->reg);
+	change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0);
+	if (change) {
+		val &= ~args->mask;
+		if (ucontrol->value.integer.value[0])
+			val |= args->mask_on;
+		else
+			val |= (args->mask & ~args->mask_on);
+		if (args->is_byte)
+			outb((unsigned char)val, cm->iobase + args->reg);
+		else
+			snd_cmipci_write(cm, args->reg, val);
+	}
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+	return change;
+}
+
+static int snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value;
+	snd_assert(args != NULL, return -EINVAL);
+	return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args);
+}
+
+#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \
+static snd_cmipci_switch_args_t cmipci_switch_arg_##sname = { \
+  reg: xreg, \
+  mask: xmask, \
+  mask_on: xmask_on, \
+  is_byte: xis_byte, \
+  ac3_sensitive: xac3, \
+}
+	
+#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \
+	DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3)
+
+#if 0 /* these will be controlled in pcm device */
+DEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_0, CM_REG_FUNCTRL1, CM_SPDF_0, 0);
+DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0);
+#endif
+DEFINE_BIT_SWITCH_ARG(spdif_in_1_2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1);
+DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */
+DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0);
+DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0);
+#if CM_CH_PLAY == 1
+DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */
+#else
+DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0);
+#endif
+DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0);
+DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
+DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
+DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0);
+DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
+
+#define DEFINE_SWITCH(sname, stype, sarg) \
+{ name: sname, \
+  iface: stype, \
+  info: snd_cmipci_uswitch_info, \
+  get: snd_cmipci_uswitch_get, \
+  put: snd_cmipci_uswitch_put, \
+  private_value: (unsigned long)&cmipci_switch_arg_##sarg,\
+}
+
+#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg)
+#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg)
+
+
+/*
+ * callbacks for spdif output switch
+ * needs toggle two registers..
+ */
+static int snd_cmipci_spdout_enable_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	int changed;
+	changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable);
+	changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac);
+	return changed;
+}
+
+static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *chip = snd_kcontrol_chip(kcontrol);
+	int changed;
+	changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable);
+	changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac);
+	if (changed) {
+		if (ucontrol->value.integer.value[0]) {
+			if (chip->spdif_playback_avail)
+				snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+		} else {
+			if (chip->spdif_playback_avail)
+				snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+		}
+	}
+	chip->spdif_playback_enabled = ucontrol->value.integer.value[0];
+	return changed;
+}
+
+
+/* both for CM8338/8738 */
+static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = {
+	DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac),
+	DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
+	DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear),
+};
+
+/* only for CM8738 */
+static snd_kcontrol_new_t snd_cmipci_8738_mixer_switches[] __devinitdata = {
+#if 0 /* controlled in pcm device */
+	DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in),
+	DEFINE_MIXER_SWITCH("IEC958 Out", spdif_0),
+	DEFINE_MIXER_SWITCH("IEC958 Out 48KHz", spdo_48k),
+	DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac),
+#endif
+	// DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable),
+	{ name: "IEC958 Output Switch",
+	  iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  info: snd_cmipci_uswitch_info,
+	  get: snd_cmipci_spdout_enable_get,
+	  put: snd_cmipci_spdout_enable_put,
+	},
+	DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid),
+	DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright),
+	DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v),
+	DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop),
+	DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor),
+};
+
+/* only for model 033/037 */
+static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = {
+	DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out),
+	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase),
+};
+
+/* only for model 039 or later */
+static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
+	DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
+	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_1_2),
+	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
+};
+
+/* card control switches */
+static snd_kcontrol_new_t snd_cmipci_control_switches[] __devinitdata = {
+	DEFINE_CARD_SWITCH("Joystick", joystick),
+	DEFINE_CARD_SWITCH("Modem", modem),
+};
+
+
+static int __devinit snd_cmipci_mixer_new(cmipci_t *cm, int pcm_spdif_device)
+{
+	unsigned long flags;
+	snd_card_t *card;
+	snd_kcontrol_new_t *sw;
+	snd_kcontrol_t *kctl;
+	int idx, err;
+
+	snd_assert(cm != NULL && cm->card != NULL, return -EINVAL);
+
+	card = cm->card;
+
+	strcpy(card->mixername, "CMedia PCI");
+
+	spin_lock_irqsave(&cm->reg_lock, flags);
+	snd_cmipci_mixer_write(cm, 0x00, 0x00);		/* mixer reset */
+	spin_unlock_irqrestore(&cm->reg_lock, flags);
+
+	for (idx = 0; idx < num_controls(snd_cmipci_mixers); idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0)
+			return err;
+	}
+
+	/* mixer switches */
+	sw = snd_cmipci_mixer_switches;
+	for (idx = 0; idx < num_controls(snd_cmipci_mixer_switches); idx++, sw++) {
+		err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+		if (err < 0)
+			return err;
+	}
+	if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 ||
+	    cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) {
+		sw = snd_cmipci_8738_mixer_switches;
+		for (idx = 0; idx < num_controls(snd_cmipci_8738_mixer_switches); idx++, sw++) {
+			err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+			if (err < 0)
+				return err;
+		}
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0)
+			return err;
+		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0)
+			return err;
+		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0)
+			return err;
+		kctl->id.device = pcm_spdif_device;
+		cm->spdif_pcm_ctl = kctl;
+		if (cm->chip_version <= 37) {
+			sw = snd_cmipci_old_mixer_switches;
+			for (idx = 0; idx < num_controls(snd_cmipci_old_mixer_switches); idx++, sw++) {
+				err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+				if (err < 0)
+					return err;
+			}
+		}
+	}
+	if (cm->chip_version >= 39) {
+		sw = snd_cmipci_extra_mixer_switches;
+		for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) {
+			err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+			if (err < 0)
+				return err;
+		}
+	}
+
+	/* card switches */
+	sw = snd_cmipci_control_switches;
+	for (idx = 0; idx < num_controls(snd_cmipci_control_switches); idx++, sw++) {
+		err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+		if (err < 0)
+			return err;
+	}
+
+	for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
+		snd_ctl_elem_id_t id;
+		snd_kcontrol_t *ctl;
+		memset(&id, 0, sizeof(id));
+		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		strcpy(id.name, cm_saved_mixer[idx].name);
+		if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL)
+			cm->mixer_res_ctl[idx] = ctl;
+	}
+
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+
+static void snd_cmipci_proc_read(snd_info_entry_t *entry, 
+				 snd_info_buffer_t *buffer)
+{
+	cmipci_t *cm = snd_magic_cast(cmipci_t, entry->private_data, return);
+	int i;
+	
+	snd_iprintf(buffer, "%s\n\n", cm->card->longname);
+	for (i = 0; i < 0x40; i++) {
+		int v = inb(cm->iobase + i);
+		if (i % 4 == 0)
+			snd_iprintf(buffer, "%02x: ", i);
+		snd_iprintf(buffer, "%02x", v);
+		if (i % 4 == 3)
+			snd_iprintf(buffer, "\n");
+		else
+			snd_iprintf(buffer, " ");
+	}
+}
+
+static void __devinit snd_cmipci_proc_init(cmipci_t *cm)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(cm->card, "cmipci", cm->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = cm;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_cmipci_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	cm->proc_entry = entry;
+}
+
+static void snd_cmipci_proc_done(cmipci_t *cm)
+{
+	if (cm->proc_entry) {
+		snd_info_unregister(cm->proc_entry);
+		cm->proc_entry = NULL;
+	}
+}
+
+
+static struct pci_device_id snd_cmipci_ids[] __devinitdata = {
+	{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,},
+};
+
+
+/*
+ * check chip version and capabilities
+ * driver name is modified according to the chip model
+ */
+static void __devinit query_chip(cmipci_t *cm)
+{
+	unsigned int detect;
+
+	/* check reg 0Ch, bit 24-31 */
+	detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2;
+	if (! detect) {
+		/* check reg 08h, bit 24-28 */
+		detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1;
+		if (! detect) {
+			cm->chip_version = 33;
+			cm->max_channels = 2;
+			cm->can_ac3_sw = 1;
+			cm->has_dual_dac = 1;
+		} else {
+			cm->chip_version = 37;
+			cm->max_channels = 2;
+			cm->can_ac3_hw = 1;
+			cm->has_dual_dac = 1;
+		}
+	} else {
+		/* check reg 0Ch, bit 26 */
+		if (detect & CM_CHIP_039) {
+			cm->chip_version = 39;
+			if (detect & CM_CHIP_039_6CH)
+				cm->max_channels  = 6;
+			else
+				cm->max_channels = 4;
+			cm->can_ac3_hw = 1;
+			cm->has_dual_dac = 1;
+			cm->can_multi_ch = 1;
+		} else {
+			cm->chip_version = 55; /* 4 or 6 channels */
+			cm->max_channels  = 6;
+			cm->can_ac3_hw = 1;
+			cm->has_dual_dac = 1;
+			cm->can_multi_ch = 1;
+		}
+	}
+
+	/* added -MCx suffix for chip supporting multi-channels */
+	if (cm->can_multi_ch)
+		sprintf(cm->card->driver + strlen(cm->card->driver),
+			"-MC%d", cm->max_channels);
+}
+
+
+static int snd_cmipci_free(cmipci_t *cm)
+{
+	snd_cmipci_proc_done(cm);
+
+	if (cm->irq >= 0) {
+		snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);  /* disable ints */
+		snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
+
+		/* reset mixer */
+		snd_cmipci_mixer_write(cm, 0, 0);
+
+		synchronize_irq();
+
+		free_irq(cm->irq, (void *)cm);
+	}
+	if (cm->res_iobase) {
+		release_resource(cm->res_iobase);
+		kfree_nocheck(cm->res_iobase);
+	}
+	snd_magic_kfree(cm);
+	return 0;
+}
+
+static int snd_cmipci_dev_free(snd_device_t *device)
+{
+	cmipci_t *cm = snd_magic_cast(cmipci_t, device->device_data, return -ENXIO);
+	return snd_cmipci_free(cm);
+}
+
+static int __devinit snd_cmipci_create(snd_card_t *card,
+				    struct pci_dev *pci,
+				    unsigned long iomidi,
+				    unsigned long iosynth,
+				    cmipci_t **rcmipci)
+{
+	cmipci_t *cm;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_cmipci_dev_free,
+	};
+	unsigned int val = 0;
+	int pcm_index, pcm_spdif_index;
+	
+	*rcmipci = NULL;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	cm = snd_magic_kcalloc(cmipci_t, 0, GFP_KERNEL);
+	if (cm == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&cm->reg_lock);
+	init_MUTEX(&cm->open_mutex);
+	cm->device = pci->device;
+	cm->card = card;
+	cm->pci = pci;
+	cm->irq = -1;
+	cm->iobase = pci_resource_start(pci, 0);
+	cm->channel[0].ch = 0;
+	cm->channel[1].ch = 1;
+	cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */
+
+	if ((cm->res_iobase = request_region(cm->iobase, CM_EXTENT_CODEC, card->driver)) == NULL) {
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", cm->iobase, cm->iobase + CM_EXTENT_CODEC - 1);
+		err = -EBUSY;
+		goto __error;
+	}
+	if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)cm)) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		err = -EBUSY;
+		goto __error;
+	}
+	cm->irq = pci->irq;
+
+	pci_set_master(cm->pci);
+
+
+	/*
+	 * check chip version, max channels and capabilities
+	 */
+
+	cm->chip_version = 0;
+	cm->max_channels = 2;
+
+	query_chip(cm);
+
+	cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+	cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+#if CM_CH_PLAY == 1
+	cm->ctrl = CM_CHADC0;	/* default FUNCNTRL0 */
+#else
+	cm->ctrl = CM_CHADC1;	/* default FUNCNTRL0 */
+#endif
+
+	/* initialize codec registers */
+	snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);	/* disable ints */
+	snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0);	/* disable channels */
+	snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+
+	snd_cmipci_write(cm, CM_REG_CHFORMAT, 0);
+	snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D);
+#if CM_CH_PLAY == 1
+	snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+#else
+	snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+#endif
+
+	/* set MPU address */
+	switch (iomidi) {
+	case 0x320: val = CM_VMPU_320; break;
+	case 0x310: val = CM_VMPU_310; break;
+	case 0x300: val = CM_VMPU_300; break;
+	case 0x330: val = CM_VMPU_330; break;
+	default:
+		iomidi = 0; break;
+	}
+	if (iomidi > 0) {
+		snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
+		/* enable UART */
+		snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN);
+	}
+
+	/* set FM address */
+	val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK;
+	switch (iosynth) {
+	case 0x3E8: val |= CM_FMSEL_3E8; break;
+	case 0x3E0: val |= CM_FMSEL_3E0; break;
+	case 0x3C8: val |= CM_FMSEL_3C8; break;
+	case 0x388: val |= CM_FMSEL_388; break;
+	default:
+		iosynth = 0; break;
+	}
+	if (iosynth > 0) {
+		snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
+		/* enable FM */
+		snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+
+		if (snd_opl3_create(card, iosynth, iosynth + 2,
+				    OPL3_HW_OPL3, 0, &cm->opl3) < 0) {
+			printk(KERN_ERR "cmipci: no OPL device at 0x%lx\n", iosynth);
+		} else {
+			if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0)
+				printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
+		}
+	}
+
+	/* reset mixer */
+	snd_cmipci_mixer_write(cm, 0, 0);
+
+	snd_cmipci_proc_init(cm);
+
+	/* create pcm devices */
+	pcm_index = pcm_spdif_index = 0;
+	if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
+		goto __error;
+	pcm_index++;
+	if (cm->has_dual_dac) {
+		if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+			goto __error;
+		pcm_index++;
+	}
+	if (cm->can_ac3_hw || cm->can_ac3_sw) {
+		pcm_spdif_index = pcm_index;
+		if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
+			goto __error;
+	}
+
+	/* create mixer interface & switches */
+	if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0)
+		goto __error;
+
+	if (iomidi > 0) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+					       iomidi, 0,
+					       cm->irq, 0, &cm->rmidi)) < 0) {
+			printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
+		}
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
+		snd_cmipci_free(cm);
+		return err;
+	}
+
+	*rcmipci = cm;
+	return 0;
+
+ __error:
+	snd_cmipci_free(cm);
+	return err;
+}
+
+/*
+ */
+
+MODULE_DEVICE_TABLE(pci, snd_cmipci_ids);
+
+static int __devinit snd_cmipci_probe(struct pci_dev *pci,
+				      const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	cmipci_t *cm;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (! snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	
+	switch (pci->device) {
+	case PCI_DEVICE_ID_CMEDIA_CM8738:
+	case PCI_DEVICE_ID_CMEDIA_CM8738B:
+		strcpy(card->driver, "CMI8738");
+		break;
+	case PCI_DEVICE_ID_CMEDIA_CM8338A:
+	case PCI_DEVICE_ID_CMEDIA_CM8338B:
+		strcpy(card->driver, "CMI8338");
+		break;
+	default:
+		strcpy(card->driver, "CMIPCI");
+		break;
+	}
+
+	if ((err = snd_cmipci_create(card, pci,
+				     snd_mpu_port[dev],
+				     snd_fm_port[dev],
+				     &cm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->shortname, "C-Media PCI %s", card->driver);
+	sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i",
+		card->shortname,
+		cm->chip_version,
+		cm->iobase,
+		cm->irq);
+
+	//snd_printd("%s is detected\n", card->longname);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+
+}
+
+static void __devexit snd_cmipci_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+	name: "C-Media PCI",
+	id_table: snd_cmipci_ids,
+	probe: snd_cmipci_probe,
+	remove: __devexit_p(snd_cmipci_remove),
+};
+	
+static int __init alsa_card_cmipci_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "C-Media PCI soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cmipci_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cmipci_init)
+module_exit(alsa_card_cmipci_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cmipci=snd_enable,snd_index,snd_id,
+			 snd_mpu_port,snd_fm_port */
+
+static int __init alsa_card_cmipci_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-cmipci=", alsa_card_cmipci_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/cs4281.c linux-2.4.19-pre5-mjc/sound/pci/cs4281.c
--- linux/sound/pci/cs4281.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/cs4281.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1947 @@
+/*
+ *  Driver for Cirrus Logic CS4281 based PCI soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#ifndef LINUX_2_2
+#if defined(CONFIG_INPUT_GAMEPORT) || defined(CONFIG_INPUT_GAMEPORT_MODULE)
+#define HAVE_GAMEPORT_SUPPORT
+#endif
+#endif
+
+#ifdef HAVE_GAMEPORT_SUPPORT
+#include <linux/gameport.h>
+#endif
+
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Cirrus Logic CS4281");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Cirrus Logic,CS4281}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for CS4281 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for CS4281 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable CS4281 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+
+/*
+ *
+ */
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS            0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4281
+#define PCI_DEVICE_ID_CIRRUS_4281	0x6005
+#endif
+
+/*
+ *  Direct registers
+ */
+
+#define CS4281_BA0_SIZE		0x1000
+#define CS4281_BA1_SIZE		0x10000
+
+/*
+ *  BA0 registers
+ */
+#define BA0_HISR		0x0000	/* Host Interrupt Status Register */
+#define BA0_HISR_INTENA		(1<<31)	/* Internal Interrupt Enable Bit */
+#define BA0_HISR_MIDI		(1<<22)	/* MIDI port interrupt */
+#define BA0_HISR_FIFOI		(1<<20)	/* FIFO polled interrupt */
+#define BA0_HISR_DMAI		(1<<18)	/* DMA interrupt (half or end) */
+#define BA0_HISR_FIFO(c)	(1<<(12+(c))) /* FIFO channel interupt */
+#define BA0_HISR_DMA(c)		(1<<(8+(c)))  /* DMA channel interrupt */
+#define BA0_HISR_GPPI		(1<<5)	/* General Purpose Input (Primary chip) */
+#define BA0_HISR_GPSI		(1<<4)	/* General Purpose Input (Secondary chip) */
+#define BA0_HISR_GP3I		(1<<3)	/* GPIO3 pin Interrupt */
+#define BA0_HISR_GP1I		(1<<2)	/* GPIO1 pin Interrupt */
+#define BA0_HISR_VUPI		(1<<1)	/* VOLUP pin Interrupt */
+#define BA0_HISR_VDNI		(1<<0)	/* VOLDN pin Interrupt */
+
+#define BA0_HICR		0x0008	/* Host Interrupt Control Register */
+#define BA0_HICR_CHGM		(1<<1)	/* INTENA Change Mask */
+#define BA0_HICR_IEV		(1<<0)	/* INTENA Value */
+#define BA0_HICR_EOI		(3<<0)	/* End of Interrupt command */
+
+#define BA0_HIMR		0x000c	/* Host Interrupt Mask Register */
+					/* Use same contants as for BA0_HISR */
+
+#define BA0_IIER		0x0010	/* ISA Interrupt Enable Register */
+
+#define BA0_HDSR0		0x00f0	/* Host DMA Engine 0 Status Register */
+#define BA0_HDSR1		0x00f4	/* Host DMA Engine 1 Status Register */
+#define BA0_HDSR2		0x00f8	/* Host DMA Engine 2 Status Register */
+#define BA0_HDSR3		0x00fc	/* Host DMA Engine 3 Status Register */
+
+#define BA0_HDSR_CH1P		(1<<25)	/* Channel 1 Pending */
+#define BA0_HDSR_CH2P		(1<<24)	/* Channel 2 Pending */
+#define BA0_HDSR_DHTC		(1<<17)	/* DMA Half Terminal Count */
+#define BA0_HDSR_DTC		(1<<16)	/* DMA Terminal Count */
+#define BA0_HDSR_DRUN		(1<<15)	/* DMA Running */
+#define BA0_HDSR_RQ		(1<<7)	/* Pending Request */
+
+#define BA0_DCA0		0x0110	/* Host DMA Engine 0 Current Address */
+#define BA0_DCC0		0x0114	/* Host DMA Engine 0 Current Count */
+#define BA0_DBA0		0x0118	/* Host DMA Engine 0 Base Address */
+#define BA0_DBC0		0x011c	/* Host DMA Engine 0 Base Count */
+#define BA0_DCA1		0x0120	/* Host DMA Engine 1 Current Address */
+#define BA0_DCC1		0x0124	/* Host DMA Engine 1 Current Count */
+#define BA0_DBA1		0x0128	/* Host DMA Engine 1 Base Address */
+#define BA0_DBC1		0x012c	/* Host DMA Engine 1 Base Count */
+#define BA0_DCA2		0x0130	/* Host DMA Engine 2 Current Address */
+#define BA0_DCC2		0x0134	/* Host DMA Engine 2 Current Count */
+#define BA0_DBA2		0x0138	/* Host DMA Engine 2 Base Address */
+#define BA0_DBC2		0x013c	/* Host DMA Engine 2 Base Count */
+#define BA0_DCA3		0x0140	/* Host DMA Engine 3 Current Address */
+#define BA0_DCC3		0x0144	/* Host DMA Engine 3 Current Count */
+#define BA0_DBA3		0x0148	/* Host DMA Engine 3 Base Address */
+#define BA0_DBC3		0x014c	/* Host DMA Engine 3 Base Count */
+#define BA0_DMR0		0x0150	/* Host DMA Engine 0 Mode */
+#define BA0_DCR0		0x0154	/* Host DMA Engine 0 Command */
+#define BA0_DMR1		0x0158	/* Host DMA Engine 1 Mode */
+#define BA0_DCR1		0x015c	/* Host DMA Engine 1 Command */
+#define BA0_DMR2		0x0160	/* Host DMA Engine 2 Mode */
+#define BA0_DCR2		0x0164	/* Host DMA Engine 2 Command */
+#define BA0_DMR3		0x0168	/* Host DMA Engine 3 Mode */
+#define BA0_DCR3		0x016c	/* Host DMA Engine 3 Command */
+
+#define BA0_DMR_DMA		(1<<29)	/* Enable DMA mode */
+#define BA0_DMR_POLL		(1<<28)	/* Enable poll mode */
+#define BA0_DMR_TBC		(1<<25)	/* Transfer By Channel */
+#define BA0_DMR_CBC		(1<<24)	/* Count By Channel (0 = frame resolution) */
+#define BA0_DMR_SWAPC		(1<<22)	/* Swap Left/Right Channels */
+#define BA0_DMR_SIZE20		(1<<20)	/* Sample is 20-bit */
+#define BA0_DMR_USIGN		(1<<19)	/* Unsigned */
+#define BA0_DMR_BEND		(1<<18)	/* Big Endian */
+#define BA0_DMR_MONO		(1<<17)	/* Mono */
+#define BA0_DMR_SIZE8		(1<<16)	/* Sample is 8-bit */
+#define BA0_DMR_TYPE_DEMAND	(0<<6)
+#define BA0_DMR_TYPE_SINGLE	(1<<6)
+#define BA0_DMR_TYPE_BLOCK	(2<<6)
+#define BA0_DMR_TYPE_CASCADE	(3<<6)	/* Not supported */
+#define BA0_DMR_DEC		(1<<5)	/* Access Increment (0) or Decrement (1) */
+#define BA0_DMR_AUTO		(1<<4)	/* Auto-Initialize */
+#define BA0_DMR_TR_VERIFY	(0<<2)	/* Verify Transfer */
+#define BA0_DMR_TR_WRITE	(1<<2)	/* Write Transfer */
+#define BA0_DMR_TR_READ		(2<<2)	/* Read Transfer */
+
+#define BA0_DCR_HTCIE		(1<<17)	/* Half Terminal Count Interrupt */
+#define BA0_DCR_TCIE		(1<<16)	/* Terminal Count Interrupt */
+#define BA0_DCR_MSK		(1<<0)	/* DMA Mask bit */
+
+#define BA0_FCR0		0x0180	/* FIFO Control 0 */
+#define BA0_FCR1		0x0184	/* FIFO Control 1 */
+#define BA0_FCR2		0x0188	/* FIFO Control 2 */
+#define BA0_FCR3		0x018c	/* FIFO Control 3 */
+
+#define BA0_FCR_FEN		(1<<31)	/* FIFO Enable bit */
+#define BA0_FCR_DACZ		(1<<30)	/* DAC Zero */
+#define BA0_FCR_PSH		(1<<29)	/* Previous Sample Hold */
+#define BA0_FCR_RS(x)		(((x)&0x1f)<<24) /* Right Slot Mapping */
+#define BA0_FCR_LS(x)		(((x)&0x1f)<<16) /* Left Slot Mapping */
+#define BA0_FCR_SZ(x)		(((x)&0x7f)<<8)	/* FIFO buffer size (in samples) */
+#define BA0_FCR_OF(x)		(((x)&0x7f)<<0)	/* FIFO starting offset (in samples) */
+
+#define BA0_FPDR0		0x0190	/* FIFO Polled Data 0 */
+#define BA0_FPDR1		0x0194	/* FIFO Polled Data 1 */
+#define BA0_FPDR2		0x0198	/* FIFO Polled Data 2 */
+#define BA0_FPDR3		0x019c	/* FIFO Polled Data 3 */
+
+#define BA0_FCHS		0x020c	/* FIFO Channel Status */
+#define BA0_FCHS_RCO(x)		(1<<(7+(((x)&3)<<3))) /* Right Channel Out */
+#define BA0_FCHS_LCO(x)		(1<<(6+(((x)&3)<<3))) /* Left Channel Out */
+#define BA0_FCHS_MRP(x)		(1<<(5+(((x)&3)<<3))) /* Move Read Pointer */
+#define BA0_FCHS_FE(x)		(1<<(4+(((x)&3)<<3))) /* FIFO Empty */
+#define BA0_FCHS_FF(x)		(1<<(3+(((x)&3)<<3))) /* FIFO Full */
+#define BA0_FCHS_IOR(x)		(1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */
+#define BA0_FCHS_RCI(x)		(1<<(1+(((x)&3)<<3))) /* Right Channel In */
+#define BA0_FCHS_LCI(x)		(1<<(0+(((x)&3)<<3))) /* Left Channel In */
+
+#define BA0_FSIC0		0x0210	/* FIFO Status and Interrupt Control 0 */
+#define BA0_FSIC1		0x0214	/* FIFO Status and Interrupt Control 1 */
+#define BA0_FSIC2		0x0218	/* FIFO Status and Interrupt Control 2 */
+#define BA0_FSIC3		0x021c	/* FIFO Status and Interrupt Control 3 */
+
+#define BA0_FSIC_FIC(x)		(((x)&0x7f)<<24) /* FIFO Interrupt Count */
+#define BA0_FSIC_FORIE		(1<<23) /* FIFO OverRun Interrupt Enable */
+#define BA0_FSIC_FURIE		(1<<22) /* FIFO UnderRun Interrupt Enable */
+#define BA0_FSIC_FSCIE		(1<<16)	/* FIFO Sample Count Interrupt Enable */
+#define BA0_FSIC_FSC(x)		(((x)&0x7f)<<8) /* FIFO Sample Count */
+#define BA0_FSIC_FOR		(1<<7)	/* FIFO OverRun */
+#define BA0_FSIC_FUR		(1<<6)	/* FIFO UnderRun */
+#define BA0_FSIC_FSCR		(1<<0)	/* FIFO Sample Count Reached */
+
+#define BA0_PMCS		0x0344	/* Power Management Control/Status */
+#define BA0_CWPR		0x03e0	/* Configuration Write Protect */
+#define BA0_EPPMC		0x03e4	/* Extended PCI Power Management Control */
+#define BA0_GPIOR		0x03e8	/* GPIO Pin Interface Register */
+
+#define BA0_SPMC		0x03ec	/* Serial Port Power Management Control (& ASDIN2 enable) */
+#define BA0_SPMC_GIPPEN		(1<<15)	/* GP INT Primary PME# Enable */
+#define BA0_SPMC_GISPEN		(1<<14)	/* GP INT Secondary PME# Enable */
+#define BA0_SPMC_EESPD		(1<<9)	/* EEPROM Serial Port Disable */
+#define BA0_SPMC_ASDI2E		(1<<8)	/* ASDIN2 Enable */
+#define BA0_SPMC_ASDO		(1<<7)	/* Asynchronous ASDOUT Assertion */
+#define BA0_SPMC_WUP2		(1<<3)	/* Wakeup for Secondary Input */
+#define BA0_SPMC_WUP1		(1<<2)	/* Wakeup for Primary Input */
+#define BA0_SPMC_ASYNC		(1<<1)	/* Asynchronous ASYNC Assertion */
+#define BA0_SPMC_RSTN		(1<<0)	/* Reset Not! */
+
+#define BA0_CFLR		0x03f0	/* Configuration Load Register (EEPROM or BIOS) */
+#define BA0_CFLR_DEFAULT	0x00000001 /* CFLR must be in AC97 link mode */
+#define BA0_IISR		0x03f4	/* ISA Interrupt Select */
+#define BA0_TMS			0x03f8	/* Test Register */
+#define BA0_SSVID		0x03fc	/* Subsystem ID register */
+
+#define BA0_CLKCR1		0x0400	/* Clock Control Register 1 */
+#define BA0_CLKCR1_CLKON	(1<<25)	/* Read Only */
+#define BA0_CLKCR1_DLLRDY	(1<<24)	/* DLL Ready */
+#define BA0_CLKCR1_DLLOS	(1<<6)	/* DLL Output Select */
+#define BA0_CLKCR1_SWCE		(1<<5)	/* Clock Enable */
+#define BA0_CLKCR1_DLLP		(1<<4)	/* DLL PowerUp */
+#define BA0_CLKCR1_DLLSS	(((x)&3)<<3) /* DLL Source Select */
+
+#define BA0_FRR			0x0410	/* Feature Reporting Register */
+#define BA0_SLT12O		0x041c	/* Slot 12 GPIO Output Register for AC-Link */
+
+#define BA0_SERMC		0x0420	/* Serial Port Master Control */
+#define BA0_SERMC_FCRN		(1<<27)	/* Force Codec Ready Not */
+#define BA0_SERMC_ODSEN2	(1<<25)	/* On-Demand Support Enable ASDIN2 */
+#define BA0_SERMC_ODSEN1	(1<<24)	/* On-Demand Support Enable ASDIN1 */
+#define BA0_SERMC_SXLB		(1<<21)	/* ASDIN2 to ASDOUT Loopback */
+#define BA0_SERMC_SLB		(1<<20)	/* ASDOUT to ASDIN2 Loopback */
+#define BA0_SERMC_LOVF		(1<<19)	/* Loopback Output Valid Frame bit */
+#define BA0_SERMC_TCID(x)	(((x)&3)<<16) /* Target Secondary Codec ID */
+#define BA0_SERMC_PXLB		(5<<1)	/* Primary Port External Loopback */
+#define BA0_SERMC_PLB		(4<<1)	/* Primary Port Internal Loopback */
+#define BA0_SERMC_PTC		(7<<1)	/* Port Timing Configuration */
+#define BA0_SERMC_PTC_AC97	(1<<1)	/* AC97 mode */
+#define BA0_SERMC_MSPE		(1<<0)	/* Master Serial Port Enable */
+
+#define BA0_SERC1		0x0428	/* Serial Port Configuration 1 */
+#define BA0_SERC1_SO1F(x)	(((x)&7)>>1) /* Primary Output Port Format */
+#define BA0_SERC1_AC97		(1<<1)
+#define BA0_SERC1_SO1EN		(1<<0)	/* Primary Output Port Enable */
+
+#define BA0_SERC2		0x042c	/* Serial Port Configuration 2 */
+#define BA0_SERC2_SI1F(x)	(((x)&7)>>1) /* Primary Input Port Format */
+#define BA0_SERC2_AC97		(1<<1)
+#define BA0_SERC2_SI1EN		(1<<0)	/* Primary Input Port Enable */
+
+#define BA0_SLT12M		0x045c	/* Slot 12 Monitor Register for Primary AC-Link */
+
+#define BA0_ACCTL		0x0460	/* AC'97 Control */
+#define BA0_ACCTL_TC		(1<<6)	/* Target Codec */
+#define BA0_ACCTL_CRW		(1<<4)	/* 0=Write, 1=Read Command */
+#define BA0_ACCTL_DCV		(1<<3)	/* Dynamic Command Valid */
+#define BA0_ACCTL_VFRM		(1<<2)	/* Valid Frame */
+#define BA0_ACCTL_ESYN		(1<<1)	/* Enable Sync */
+
+#define BA0_ACSTS		0x0464	/* AC'97 Status */
+#define BA0_ACSTS_VSTS		(1<<1)	/* Valid Status */
+#define BA0_ACSTS_CRDY		(1<<0)	/* Codec Ready */
+
+#define BA0_ACOSV		0x0468	/* AC'97 Output Slot Valid */
+#define BA0_ACOSV_SLV(x)	(1<<((x)-3))
+
+#define BA0_ACCAD		0x046c	/* AC'97 Command Address */
+#define BA0_ACCDA		0x0470	/* AC'97 Command Data */
+
+#define BA0_ACISV		0x0474	/* AC'97 Input Slot Valid */
+#define BA0_ACISV_SLV(x)	(1<<((x)-3))
+
+#define BA0_ACSAD		0x0478	/* AC'97 Status Address */
+#define BA0_ACSDA		0x047c	/* AC'97 Status Data */
+#define BA0_JSPT		0x0480	/* Joystick poll/trigger */
+#define BA0_JSCTL		0x0484	/* Joystick control */
+#define BA0_JSC1		0x0488	/* Joystick control */
+#define BA0_JSC2		0x048c	/* Joystick control */
+#define BA0_JSIO		0x04a0
+
+#define BA0_MIDCR		0x0490	/* MIDI Control */
+#define BA0_MIDCR_MRST		(1<<5)	/* Reset MIDI Interface */
+#define BA0_MIDCR_MLB		(1<<4)	/* MIDI Loop Back Enable */
+#define BA0_MIDCR_TIE		(1<<3)	/* MIDI Transmuit Interrupt Enable */
+#define BA0_MIDCR_RIE		(1<<2)	/* MIDI Receive Interrupt Enable */
+#define BA0_MIDCR_RXE		(1<<1)	/* MIDI Receive Enable */
+#define BA0_MIDCR_TXE		(1<<0)	/* MIDI Transmit Enable */
+
+#define BA0_MIDCMD		0x0494	/* MIDI Command (wo) */
+
+#define BA0_MIDSR		0x0494	/* MIDI Status (ro) */
+#define BA0_MIDSR_RDA		(1<<15)	/* Sticky bit (RBE 1->0) */
+#define BA0_MIDSR_TBE		(1<<14) /* Sticky bit (TBF 0->1) */
+#define BA0_MIDSR_RBE		(1<<7)	/* Receive Buffer Empty */
+#define BA0_MIDSR_TBF		(1<<6)	/* Transmit Buffer Full */
+
+#define BA0_MIDWP		0x0498	/* MIDI Write */
+#define BA0_MIDRP		0x049c	/* MIDI Read (ro) */
+
+#define BA0_AODSD1		0x04a8	/* AC'97 On-Demand Slot Disable for primary link (ro) */
+#define BA0_AODSD1_NDS(x)	(1<<((x)-3))
+
+#define BA0_AODSD2		0x04ac	/* AC'97 On-Demand Slot Disable for secondary link (ro) */
+#define BA0_AODSD2_NDS(x)	(1<<((x)-3))
+
+#define BA0_CFGI		0x04b0	/* Configure Interface (EEPROM interface) */
+#define BA0_SLT12M2		0x04dc	/* Slot 12 Monitor Register 2 for secondary AC-link */
+#define BA0_ACSTS2		0x04e4	/* AC'97 Status Register 2 */
+#define BA0_ACISV2		0x04f4	/* AC'97 Input Slot Valid Register 2 */
+#define BA0_ACSAD2		0x04f8	/* AC'97 Status Address Register 2 */
+#define BA0_ACSDA2		0x04fc	/* AC'97 Status Data Register 2 */
+#define BA0_FMSR		0x0730	/* FM Synthesis Status (ro) */
+#define BA0_B0AP		0x0730	/* FM Bank 0 Address Port (wo) */
+#define BA0_FMDP		0x0734	/* FM Data Port */
+#define BA0_B1AP		0x0738	/* FM Bank 1 Address Port */
+#define BA0_B1DP		0x073c	/* FM Bank 1 Data Port */
+
+#define BA0_SSPM		0x0740	/* Sound System Power Management */
+#define BA0_SSPM_MIXEN		(1<<6)	/* Playback SRC + FM/Wavetable MIX */
+#define BA0_SSPM_CSRCEN		(1<<5)	/* Capture Sample Rate Converter Enable */
+#define BA0_SSPM_PSRCEN		(1<<4)	/* Playback Sample Rate Converter Enable */
+#define BA0_SSPM_JSEN		(1<<3)	/* Joystick Enable */
+#define BA0_SSPM_ACLEN		(1<<2)	/* Serial Port Engine and AC-Link Enable */
+#define BA0_SSPM_FMEN		(1<<1)	/* FM Synthesis Block Enable */
+
+#define BA0_DACSR		0x0744	/* DAC Sample Rate - Playback SRC */
+#define BA0_ADCSR		0x0748	/* ADC Sample Rate - Capture SRC */
+
+#define BA0_SSCR		0x074c	/* Sound System Control Register */
+#define BA0_SSCR_HVS1		(1<<23)	/* Hardwave Volume Step (0=1,1=2) */
+#define BA0_SSCR_MVCS		(1<<19)	/* Master Volume Codec Select */
+#define BA0_SSCR_MVLD		(1<<18)	/* Master Volume Line Out Disable */
+#define BA0_SSCR_MVAD		(1<<17)	/* Master Volume Alternate Out Disable */
+#define BA0_SSCR_MVMD		(1<<16)	/* Master Volume Mono Out Disable */
+#define BA0_SSCR_XLPSRC		(1<<8)	/* External SRC Loopback Mode */
+#define BA0_SSCR_LPSRC		(1<<7)	/* SRC Loopback Mode */
+#define BA0_SSCR_CDTX		(1<<5)	/* CD Transfer Data */
+#define BA0_SSCR_HVC		(1<<3)	/* Harware Volume Control Enable */
+
+#define BA0_FMLVC		0x0754	/* FM Synthesis Left Volume Control */
+#define BA0_FMRVC		0x0758	/* FM Synthesis Right Volume Control */
+#define BA0_SRCSA		0x075c	/* SRC Slot Assignments */
+#define BA0_PPLVC		0x0760	/* PCM Playback Left Volume Control */
+#define BA0_PPRVC		0x0764	/* PCM Playback Right Volume Control */
+
+/* Source Slot Numbers - Playback */
+#define SRCSLOT_LEFT_PCM_PLAYBACK		0
+#define SRCSLOT_RIGHT_PCM_PLAYBACK		1
+#define SRCSLOT_PHONE_LINE_1_DAC		2
+#define SRCSLOT_CENTER_PCM_PLAYBACK		3
+#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK	4
+#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK	5
+#define SRCSLOT_LFE_PCM_PLAYBACK		6
+#define SRCSLOT_PHONE_LINE_2_DAC		7
+#define SRCSLOT_HEADSET_DAC			8
+#define SRCSLOT_LEFT_WT				29  /* invalid for BA0_SRCSA */
+#define SRCSLOT_RIGHT_WT			30  /* invalid for BA0_SRCSA */
+
+/* Source Slot Numbers - Capture */
+#define SRCSLOT_LEFT_PCM_RECORD			10
+#define SRCSLOT_RIGHT_PCM_RECORD		11
+#define SRCSLOT_PHONE_LINE_1_ADC		12
+#define SRCSLOT_MIC_ADC				13
+#define SRCSLOT_PHONE_LINE_2_ADC		17
+#define SRCSLOT_HEADSET_ADC			18
+#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD	20
+#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD	21
+#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC	22
+#define SRCSLOT_SECONDARY_MIC_ADC		23
+#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC	27
+#define SRCSLOT_SECONDARY_HEADSET_ADC		28
+
+/* Source Slot Numbers - Others */
+#define SRCSLOT_POWER_DOWN			31
+
+/* MIDI modes */
+#define CS4281_MODE_OUTPUT		(1<<0)
+#define CS4281_MODE_INPUT		(1<<1)
+
+/* joystick bits */
+/* Bits for JSPT */
+#define JSPT_CAX                                0x00000001
+#define JSPT_CAY                                0x00000002
+#define JSPT_CBX                                0x00000004
+#define JSPT_CBY                                0x00000008
+#define JSPT_BA1                                0x00000010
+#define JSPT_BA2                                0x00000020
+#define JSPT_BB1                                0x00000040
+#define JSPT_BB2                                0x00000080
+
+/* Bits for JSCTL */
+#define JSCTL_SP_MASK                           0x00000003
+#define JSCTL_SP_SLOW                           0x00000000
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002
+#define JSCTL_SP_FAST                           0x00000003
+#define JSCTL_ARE                               0x00000004
+
+/* Data register pairs masks */
+#define JSC1_Y1V_MASK                           0x0000FFFF
+#define JSC1_X1V_MASK                           0xFFFF0000
+#define JSC1_Y1V_SHIFT                          0
+#define JSC1_X1V_SHIFT                          16
+#define JSC2_Y2V_MASK                           0x0000FFFF
+#define JSC2_X2V_MASK                           0xFFFF0000
+#define JSC2_Y2V_SHIFT                          0
+#define JSC2_X2V_SHIFT                          16
+
+/* JS GPIO */
+#define JSIO_DAX                                0x00000001
+#define JSIO_DAY                                0x00000002
+#define JSIO_DBX                                0x00000004
+#define JSIO_DBY                                0x00000008
+#define JSIO_AXOE                               0x00000010
+#define JSIO_AYOE                               0x00000020
+#define JSIO_BXOE                               0x00000040
+#define JSIO_BYOE                               0x00000080
+
+/*
+ *
+ */
+
+#define chip_t cs4281_t
+
+typedef struct snd_cs4281 cs4281_t;
+typedef struct snd_cs4281_dma cs4281_dma_t;
+
+struct snd_cs4281_dma {
+	snd_pcm_substream_t *substream;
+	unsigned int regDBA;		/* offset to DBA register */
+	unsigned int regDCA;		/* offset to DCA register */
+	unsigned int regDBC;		/* offset to DBC register */
+	unsigned int regDCC;		/* offset to DCC register */
+	unsigned int regDMR;		/* offset to DMR register */
+	unsigned int regDCR;		/* offset to DCR register */
+	unsigned int regHDSR;		/* offset to HDSR register */
+	unsigned int regFCR;		/* offset to FCR register */
+	unsigned int regFSIC;		/* offset to FSIC register */
+	unsigned int valDMR;		/* DMA mode */
+	unsigned int valDCR;		/* DMA command */
+	unsigned int valFCR;		/* FIFO control */
+	unsigned int fifo_offset;	/* FIFO offset within BA1 */
+	unsigned char left_slot;	/* FIFO left slot */
+	unsigned char right_slot;	/* FIFO right slot */
+	int frag;			/* period number */
+};
+
+#ifdef HAVE_GAMEPORT_SUPPORT
+typedef struct snd_cs4281_gameport {
+	struct gameport info;
+	cs4281_t *chip;
+} cs4281_gameport_t;
+#endif
+
+struct snd_cs4281 {
+	int irq;
+
+	unsigned long ba0;		/* virtual (accessible) address */
+	unsigned long ba1;		/* virtual (accessible) address */
+	unsigned long ba0_addr;
+	unsigned long ba1_addr;
+	struct resource *ba0_res;
+	struct resource *ba1_res;
+
+	ac97_t *ac97;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *midi_input;
+	snd_rawmidi_substream_t *midi_output;
+
+	cs4281_dma_t dma[4];
+
+	unsigned char src_left_play_slot;
+	unsigned char src_right_play_slot;
+	unsigned char src_left_rec_slot;
+	unsigned char src_right_rec_slot;
+
+	unsigned int spurious_dhtc_irq;
+	unsigned int spurious_dtc_irq;
+
+	void *proc_entry_BA0;
+	void *proc_entry_BA1;
+
+	spinlock_t reg_lock;
+	unsigned int midcr;
+	unsigned int uartm;
+	snd_info_entry_t *proc_entry;
+
+#ifdef HAVE_GAMEPORT_SUPPORT
+	cs4281_gameport_t *gameport;
+#endif
+};
+
+static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_cs4281_ids[] __devinitdata = {
+	{ 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* CS4281 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs4281_ids);
+
+/*
+ *  constants
+ */
+
+#define CS4281_FIFO_SIZE	32
+
+/*
+ *  common I/O routines
+ */
+
+static void snd_cs4281_delay(unsigned int delay)
+{
+	if (delay > 999) {
+		signed long end_time;
+		delay = (delay * HZ) / 1000000;
+		if (delay < 1)
+			delay = 1;
+		end_time = jiffies + delay;
+		do {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+	        } while (end_time - (signed long)jiffies >= 0);
+	} else {
+		udelay(delay);
+	}
+}
+
+static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val)
+{
+        writel(val, chip->ba0 + offset);
+}
+
+static inline unsigned int snd_cs4281_peekBA0(cs4281_t *chip, unsigned long offset)
+{
+        return readl(chip->ba0 + offset);
+}
+
+static void snd_cs4281_ac97_write(ac97_t *ac97,
+				   unsigned short reg, unsigned short val)
+{
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+	 *  5. if DCV not cleared, break and return error
+	 */
+	cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return);
+	int count;
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  reset CRW - Write command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+         */
+	snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg);
+	snd_cs4281_pokeBA0(chip, BA0_ACCDA, val);
+	snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM |
+				            BA0_ACCTL_ESYN);
+	for (count = 0; count < 2000; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+		 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the write has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 07h
+		 */
+		if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) {
+			return;
+		}
+	}
+	snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+}
+
+static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
+					    unsigned short reg)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO);
+	int count;
+	unsigned short result;
+
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97 
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+	 *  5. if DCV not cleared, break and return error
+	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
+	 */
+
+	snd_cs4281_peekBA0(chip, BA0_ACSDA);
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  set CRW - Read command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+	 */
+
+	snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg);
+	snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0);
+	snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW |
+					    BA0_ACCTL_VFRM | BA0_ACCTL_ESYN);
+
+
+	/*
+	 *  Wait for the read to occur.
+	 */
+	for (count = 0; count < 500; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+	 	 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the read has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
+		 */
+		if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV))
+			goto __ok1;
+	}
+
+	snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+	result = 0xffff;
+	goto __end;
+	
+      __ok1:
+	/*
+	 *  Wait for the valid status bit to go active.
+	 */
+	for (count = 0; count < 100; count++) {
+		/*
+		 *  Read the AC97 status register.
+		 *  ACSTS = Status Register = 464h
+		 *  VSTS - Valid Status
+		 */
+		if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_VSTS)
+			goto __ok2;
+		udelay(10);
+	}
+	
+	snd_printk("AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
+	result = 0xffff;
+	goto __end;
+
+      __ok2:
+	/*
+	 *  Read the data returned from the AC97 register.
+	 *  ACSDA = Status Data Register = 474h
+	 */
+	result = snd_cs4281_peekBA0(chip, BA0_ACSDA);
+
+      __end:
+	return result;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_cs4281_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+	cs4281_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dma->valDCR |= BA0_DCR_MSK;
+		dma->valFCR |= BA0_FCR_FEN;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dma->valDCR &= ~BA0_DCR_MSK;
+		dma->valFCR &= ~BA0_FCR_FEN;
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+		snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA);
+		dma->valDMR |= BA0_DMR_DMA;
+		dma->valDCR &= ~BA0_DCR_MSK;
+		dma->valFCR |= BA0_FCR_FEN;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL);
+		dma->valDCR |= BA0_DCR_MSK;
+		dma->valFCR &= ~BA0_FCR_FEN;
+		break;
+	default:
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return -EINVAL;
+	}
+	snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR);
+	snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR);
+	snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
+{
+	unsigned int val = ~0;
+	
+	if (real_rate)
+		*real_rate = rate;
+	/* special "hardcoded" rates */
+	switch (rate) {
+	case 8000:	return 5;
+	case 11025:	return 4;
+	case 16000:	return 3;
+	case 22050:	return 2;
+	case 44100:	return 1;
+	case 48000:	return 0;
+	default:
+		goto __variable;
+	}
+      __variable:
+	val = 1536000 / rate;
+	if (real_rate)
+		*real_rate = 1536000 / val;
+	return val;
+}
+
+static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src)
+{
+	int rec_mono;
+
+	dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO |
+		      (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ);
+	if (runtime->channels == 1)
+		dma->valDMR |= BA0_DMR_MONO;
+	if (snd_pcm_format_unsigned(runtime->format) > 0)
+		dma->valDMR |= BA0_DMR_USIGN;
+	if (snd_pcm_format_big_endian(runtime->format) > 0)
+		dma->valDMR |= BA0_DMR_BEND;
+	switch (snd_pcm_format_width(runtime->format)) {
+	case 8: dma->valDMR |= BA0_DMR_SIZE8;
+		if (runtime->channels == 1)
+			dma->valDMR |= BA0_DMR_SWAPC;
+		break;
+	case 32: dma->valDMR |= BA0_DMR_SIZE20; break;
+	}
+	dma->frag = 0;	/* for workaround */
+	dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK;
+	if (runtime->buffer_size != runtime->period_size)
+		dma->valDCR |= BA0_DCR_HTCIE;
+	/* Initialize DMA */
+	snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr);
+	snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1);
+	rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO;
+	snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) |
+					    (chip->src_right_play_slot << 8) |
+					    (chip->src_left_rec_slot << 16) |
+					    ((rec_mono ? 31 : chip->src_right_rec_slot) << 24));
+	if (!src)
+		goto __skip_src;
+	if (!capture) {
+		if (dma->left_slot == chip->src_left_play_slot) {
+			unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
+			snd_assert(dma->right_slot == chip->src_right_play_slot, );
+			snd_cs4281_pokeBA0(chip, BA0_DACSR, val);
+		}
+	} else {
+		if (dma->left_slot == chip->src_left_rec_slot) {
+			unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
+			snd_assert(dma->right_slot == chip->src_right_rec_slot, );
+			snd_cs4281_pokeBA0(chip, BA0_ADCSR, val);
+		}
+	}
+      __skip_src:
+	/* Initialize FIFO */
+	dma->valFCR = BA0_FCR_LS(dma->left_slot) |
+		      BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) |
+		      BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+		      BA0_FCR_OF(dma->fifo_offset);
+	snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0));
+	/* Clear FIFO Status and Interrupt Control Register */
+	snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);
+}
+
+static int snd_cs4281_hw_params(snd_pcm_substream_t * substream,
+				snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_cs4281_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4281_playback_prepare(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+	cs4281_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4281_mode(chip, dma, runtime, 0, 1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4281_capture_prepare(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+	cs4281_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4281_mode(chip, dma, runtime, 1, 1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_cs4281_pointer(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+	cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+	// printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies);
+	return runtime->buffer_size -
+	       snd_cs4281_peekBA0(chip, dma->regDCC);
+}
+
+static snd_pcm_hardware_t snd_cs4281_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+				SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+				SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+				SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(512*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(512*1024),
+	periods_min:		1,
+	periods_max:		2,
+	fifo_size:		CS4281_FIFO_SIZE,
+};
+
+static snd_pcm_hardware_t snd_cs4281_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+				SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+				SNDRV_PCM_FMTBIT_S32_BE | SNDRV_PCM_FMTBIT_S32_BE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(512*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(512*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		CS4281_FIFO_SIZE,
+};
+
+static int snd_cs4281_playback_open(snd_pcm_substream_t * substream)
+{
+	cs4281_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs4281_dma_t *dma;
+
+	dma = &chip->dma[0];
+	dma->substream = substream;
+	dma->left_slot = 0;
+	dma->right_slot = 1;
+	runtime->private_data = dma;
+	runtime->hw = snd_cs4281_playback;
+	snd_pcm_set_sync(substream);
+	/* should be detected from the AC'97 layer, but it seems
+	   that although CS4297A rev B reports 18-bit ADC resolution,
+	   samples are 20-bit */
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+	return 0;
+}
+
+static int snd_cs4281_capture_open(snd_pcm_substream_t * substream)
+{
+	cs4281_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs4281_dma_t *dma;
+
+	dma = &chip->dma[1];
+	dma->substream = substream;
+	dma->left_slot = 10;
+	dma->right_slot = 11;
+	runtime->private_data = dma;
+	runtime->hw = snd_cs4281_capture;
+	snd_pcm_set_sync(substream);
+	/* should be detected from the AC'97 layer, but it seems
+	   that although CS4297A rev B reports 18-bit ADC resolution,
+	   samples are 20-bit */
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+	return 0;
+}
+
+static int snd_cs4281_playback_close(snd_pcm_substream_t * substream)
+{
+	cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+
+	dma->substream = NULL;
+	return 0;
+}
+
+static int snd_cs4281_capture_close(snd_pcm_substream_t * substream)
+{
+	cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+
+	dma->substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_cs4281_playback_ops = {
+	open:		snd_cs4281_playback_open,
+	close:		snd_cs4281_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cs4281_hw_params,
+	hw_free:	snd_cs4281_hw_free,
+	prepare:	snd_cs4281_playback_prepare,
+	trigger:	snd_cs4281_trigger,
+	pointer:	snd_cs4281_pointer,
+};
+
+static snd_pcm_ops_t snd_cs4281_capture_ops = {
+	open:		snd_cs4281_capture_open,
+	close:		snd_cs4281_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cs4281_hw_params,
+	hw_free:	snd_cs4281_hw_free,
+	prepare:	snd_cs4281_capture_prepare,
+	trigger:	snd_cs4281_trigger,
+	pointer:	snd_cs4281_pointer,
+};
+
+static void snd_cs4281_pcm_free(snd_pcm_t *pcm)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_cs4281_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "CS4281");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer section
+ */
+
+static void snd_cs4281_mixer_free_ac97(ac97_t *ac97)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+}
+
+static int __devinit snd_cs4281_mixer(cs4281_t * chip)
+{
+	snd_card_t *card = chip->card;
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_cs4281_ac97_write;
+	ac97.read = snd_cs4281_ac97_read;
+	ac97.private_data = chip;
+	ac97.private_free = snd_cs4281_mixer_free_ac97;
+	if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_cs4281_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return);
+
+	snd_iprintf(buffer, "Cirrus Logic CS4281\n\n");
+	snd_iprintf(buffer, "Spurious half IRQs   : %u\n", chip->spurious_dhtc_irq);
+	snd_iprintf(buffer, "Spurious end IRQs    : %u\n", chip->spurious_dtc_irq);
+}
+
+static long snd_cs4281_BA0_read(snd_info_entry_t *entry, void *file_private_data,
+				struct file *file, char *buf, long count)
+{
+	long size;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO);
+	
+	size = count;
+	if (file->f_pos + size > CS4281_BA0_SIZE)
+		size = (long)CS4281_BA0_SIZE - file->f_pos;
+	if (size > 0) {
+		char *tmp;
+		long res;
+		unsigned long virt;
+		if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		virt = chip->ba0 + file->f_pos;
+		memcpy_fromio(tmp, virt, size);
+		if (copy_to_user(buf, tmp, size))
+			res = -EFAULT;
+		else {
+			res = size;
+			file->f_pos += size;
+		}
+		kfree(tmp);
+		return res;
+	}
+	return 0;
+}
+
+static long snd_cs4281_BA1_read(snd_info_entry_t *entry, void *file_private_data,
+				struct file *file, char *buf, long count)
+{
+	long size;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO);
+	
+	size = count;
+	if (file->f_pos + size > CS4281_BA1_SIZE)
+		size = (long)CS4281_BA1_SIZE - file->f_pos;
+	if (size > 0) {
+		char *tmp;
+		long res;
+		unsigned long virt;
+		if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		virt = chip->ba1 + file->f_pos;
+		memcpy_fromio(tmp, virt, size);
+		if (copy_to_user(buf, tmp, size))
+			res = -EFAULT;
+		else {
+			res = size;
+			file->f_pos += size;
+		}
+		kfree(tmp);
+		return res;
+	}
+	return 0;
+}
+
+static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
+	read: snd_cs4281_BA0_read,
+};
+
+static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
+	read: snd_cs4281_BA1_read,
+};
+
+static void __devinit snd_cs4281_proc_init(cs4281_t * chip)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(chip->card, "cs4281", chip->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_cs4281_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	chip->proc_entry = entry;
+	if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA0", chip->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = chip;
+		entry->c.ops = &snd_cs4281_proc_ops_BA0;
+		entry->size = CS4281_BA0_SIZE;
+		if (snd_info_register(entry) < 0) {
+			snd_info_unregister(entry);
+			entry = NULL;
+		}
+	}
+	chip->proc_entry_BA0 = entry;
+	if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA1", chip->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = chip;
+		entry->c.ops = &snd_cs4281_proc_ops_BA1;
+		entry->size = CS4281_BA1_SIZE;
+		if (snd_info_register(entry) < 0) {
+			snd_info_unregister(entry);
+			entry = NULL;
+		}
+	}
+	chip->proc_entry_BA1 = entry;
+}
+
+static void snd_cs4281_proc_done(cs4281_t * chip)
+{
+	if (chip->proc_entry_BA1) {
+		snd_info_unregister(chip->proc_entry_BA1);
+		chip->proc_entry_BA1 = NULL;
+	}
+	if (chip->proc_entry_BA0) {
+		snd_info_unregister(chip->proc_entry_BA0);
+		chip->proc_entry_BA0 = NULL;
+	}
+	if (chip->proc_entry) {
+		snd_info_unregister(chip->proc_entry);
+		chip->proc_entry = NULL;
+	}
+}
+
+/*
+
+ */
+
+static int snd_cs4281_free(cs4281_t *chip)
+{
+#ifdef HAVE_GAMEPORT_SUPPORT
+	if (chip->gameport) {
+		gameport_unregister_port(&chip->gameport->info);
+		kfree(chip->gameport);
+	}
+#endif
+	snd_cs4281_proc_done(chip);
+	synchronize_irq();
+
+	/* Mask interrupts */
+	snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff);
+	/* Stop the DLL Clock logic. */
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+	/* Sound System Power Management - Turn Everything OFF */
+	snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
+	/* PCI interface - D3 state */
+	pci_set_power_state(chip->pci, 3);
+
+	if (chip->ba0)
+		iounmap((void *) chip->ba0);
+	if (chip->ba1)
+		iounmap((void *) chip->ba1);
+	if (chip->ba0_res) {
+		release_resource(chip->ba0_res);
+		kfree_nocheck(chip->ba0_res);
+	}
+	if (chip->ba1_res) {
+		release_resource(chip->ba1_res);
+		kfree_nocheck(chip->ba1_res);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_cs4281_dev_free(snd_device_t *device)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, device->device_data, return -ENXIO);
+	return snd_cs4281_free(chip);
+}
+
+static int __devinit snd_cs4281_create(snd_card_t * card,
+				    struct pci_dev *pci,
+				    cs4281_t ** rchip)
+{
+	cs4281_t *chip;
+	unsigned int tmp;
+	signed long end_time;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_cs4281_dev_free,
+	};
+
+	*rchip = NULL;
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	chip = snd_magic_kcalloc(cs4281_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->ba0_addr = pci_resource_start(pci, 0);
+	chip->ba1_addr = pci_resource_start(pci, 1);
+	pci_set_master(pci);
+
+	if ((chip->ba0_res = request_mem_region(chip->ba0_addr, CS4281_BA0_SIZE, "CS4281 BA0")) == NULL) {
+		snd_cs4281_free(chip);
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->ba0_addr, chip->ba0_addr + CS4281_BA0_SIZE - 1);
+		return -ENOMEM;
+	}
+	if ((chip->ba1_res = request_mem_region(chip->ba1_addr, CS4281_BA1_SIZE, "CS4281 BA1")) == NULL) {
+		snd_cs4281_free(chip);
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->ba1_addr, chip->ba1_addr + CS4281_BA1_SIZE - 1);
+		return -ENOMEM;
+	}
+	if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) {
+		snd_cs4281_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -ENOMEM;
+	}
+	chip->irq = pci->irq;
+
+	chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, CS4281_BA0_SIZE);
+	chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, CS4281_BA1_SIZE);
+	if (!chip->ba0 || !chip->ba1) {
+		snd_cs4281_free(chip);
+		return -ENOMEM;
+	}
+	
+	tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
+	if (tmp != BA0_CFLR_DEFAULT) {
+		snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT);
+		tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
+		if (tmp != BA0_CFLR_DEFAULT) {
+			snd_printk("CFLR setup failed (0x%x)\n", tmp);
+			snd_cs4281_free(chip);
+			return -EIO;
+		}
+	}
+
+	/* Set the 'Configuration Write Protect' register
+	 * to 4281h.  Allows vendor-defined configuration
+         * space between 0e4h and 0ffh to be written. */	
+	snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281);
+	
+	if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
+		snd_printk("SERC1 AC'97 check failed (0x%x)\n", tmp);
+		snd_cs4281_free(chip);
+		return -EIO;
+	}
+	if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
+		snd_printk("SERC2 AC'97 check failed (0x%x)\n", tmp);
+		snd_cs4281_free(chip);
+		return -EIO;
+	}
+
+	/* Sound System Power Management */
+	snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN |
+				           BA0_SSPM_PSRCEN | BA0_SSPM_JSEN |
+				           BA0_SSPM_ACLEN | BA0_SSPM_FMEN);
+
+	/* Serial Port Power Management */
+ 	/* Blast the clock control register to zero so that the
+         * PLL starts out in a known state, and blast the master serial
+         * port control register to zero so that the serial ports also
+         * start out in a known state. */
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+	snd_cs4281_pokeBA0(chip, BA0_SERMC, 0);
+
+        /* Make ESYN go to zero to turn off
+         * the Sync pulse on the AC97 link. */
+	snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0);
+	udelay(50);
+                
+	/*  Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+	 *  spec) and then drive it high.  This is done for non AC97 modes since
+	 *  there might be logic external to the CS4281 that uses the ARST# line
+	 *  for a reset. */
+	snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
+	udelay(50);
+	snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN);
+	snd_cs4281_delay(50000);
+
+	/*
+	 *  Set the serial port timing configuration.
+	 */
+	snd_cs4281_pokeBA0(chip, BA0_SERMC, BA0_SERMC_TCID(1) | BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE);
+
+	/*
+	 *  Start the DLL Clock logic.
+	 */
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP);
+	snd_cs4281_delay(50000);
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP);
+
+	/*
+	 * Wait for the DLL ready signal from the clock logic.
+	 */
+	end_time = (jiffies + HZ / 4) + 1;
+	do {
+		/*
+		 *  Read the AC97 status register to see if we've seen a CODEC
+		 *  signal from the AC97 codec.
+		 */
+		if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY)
+			goto __ok0;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+        } while (end_time - (signed long)jiffies >= 0);
+
+	snd_printk("DLLRDY not seen\n");
+	snd_cs4281_free(chip);
+	return -EIO;
+
+      __ok0:
+
+	/*
+	 *  The first thing we do here is to enable sync generation.  As soon
+	 *  as we start receiving bit clock, we'll start producing the SYNC
+	 *  signal.
+	 */
+	snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN);
+
+	/*
+	 * Wait for the codec ready signal from the AC97 codec.
+	 */
+	end_time = (jiffies + (3 * HZ) / 4) + 1;
+	do {
+		/*
+		 *  Read the AC97 status register to see if we've seen a CODEC
+		 *  signal from the AC97 codec.
+		 */
+		if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY)
+			goto __ok1;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+        } while (end_time - (signed long)jiffies >= 0);
+
+	snd_printk("never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
+	snd_cs4281_free(chip);
+	return -EIO;
+
+      __ok1:
+
+	/*
+	 *  Assert the valid frame signal so that we can start sending commands
+	 *  to the AC97 codec.
+	 */
+
+	snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN);
+
+	/*
+	 *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
+	 *  the codec is pumping ADC data across the AC-link.
+	 */
+
+	end_time = (jiffies + (5 * HZ) / 4) + 1;
+	do {
+		/*
+		 *  Read the input slot valid register and see if input slots 3
+		 *  4 are valid yet.
+		 */
+                if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4)))
+                        goto __ok2;
+                set_current_state(TASK_UNINTERRUPTIBLE);
+                schedule_timeout(1);
+        } while (end_time - (signed long)jiffies >= 0);
+
+	snd_printk("never read ISV3 and ISV4 from AC'97\n");
+	snd_cs4281_free(chip);
+	return -EIO;
+
+      __ok2:
+
+	/*
+	 *  Now, assert valid frame and the slot 3 and 4 valid bits.  This will
+	 *  commense the transfer of digital audio data to the AC97 codec.
+	 */
+	snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4));
+
+	/*
+	 *  Initialize DMA structures
+	 */
+	for (tmp = 0; tmp < 4; tmp++) {
+		cs4281_dma_t *dma = &chip->dma[tmp];
+		dma->regDBA = BA0_DBA0 + (tmp * 0x10);
+		dma->regDCA = BA0_DCA0 + (tmp * 0x10);
+		dma->regDBC = BA0_DBC0 + (tmp * 0x10);
+		dma->regDCC = BA0_DCC0 + (tmp * 0x10);
+		dma->regDMR = BA0_DMR0 + (tmp * 8);
+		dma->regDCR = BA0_DCR0 + (tmp * 8);
+		dma->regHDSR = BA0_HDSR0 + (tmp * 4);
+		dma->regFCR = BA0_FCR0 + (tmp * 4);
+		dma->regFSIC = BA0_FSIC0 + (tmp * 4);
+		dma->fifo_offset = tmp * CS4281_FIFO_SIZE;
+		snd_cs4281_pokeBA0(chip, dma->regFCR,
+				   BA0_FCR_LS(31) |
+				   BA0_FCR_RS(31) |
+				   BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+				   BA0_FCR_OF(dma->fifo_offset));
+	}
+
+	chip->src_left_play_slot = 0;	/* AC'97 left PCM playback (3) */
+	chip->src_right_play_slot = 1;	/* AC'97 right PCM playback (4) */
+	chip->src_left_rec_slot = 10;	/* AC'97 left PCM record (3) */
+	chip->src_right_rec_slot = 11;	/* AC'97 right PCM record (4) */
+
+	/* Initialize digital volume */
+	snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0);
+	snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0);
+
+	snd_cs4281_proc_init(chip);
+
+	/* Enable IRQs */
+	snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+	/* Unmask interrupts */
+	snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~(
+					BA0_HISR_MIDI |
+					BA0_HISR_DMAI |
+					BA0_HISR_DMA(0) |
+					BA0_HISR_DMA(1) |
+					BA0_HISR_DMA(2) |
+					BA0_HISR_DMA(3)));
+	synchronize_irq();
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_cs4281_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+/*
+ *  MIDI section
+ */
+
+static void snd_cs4281_midi_reset(cs4281_t *chip)
+{
+	snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST);
+	udelay(100);
+	snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+}
+
+static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+ 	chip->midcr |= BA0_MIDCR_RXE;
+	chip->midi_input = substream;
+	if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
+		snd_cs4281_midi_reset(chip);
+	} else {
+		snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE);
+	chip->midi_input = NULL;
+	if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
+		snd_cs4281_midi_reset(chip);
+	} else {
+		snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	chip->uartm &= ~CS4281_MODE_INPUT;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->uartm |= CS4281_MODE_OUTPUT;
+	chip->midcr |= BA0_MIDCR_TXE;
+	chip->midi_input = substream;
+	if (!(chip->uartm & CS4281_MODE_INPUT)) {
+		snd_cs4281_midi_reset(chip);
+	} else {
+		snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE);
+	chip->midi_output = NULL;
+	if (!(chip->uartm & CS4281_MODE_INPUT)) {
+		snd_cs4281_midi_reset(chip);
+	} else {
+		snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	chip->uartm &= ~CS4281_MODE_OUTPUT;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (up) {
+		if ((chip->midcr & BA0_MIDCR_RIE) == 0) {
+			chip->midcr |= BA0_MIDCR_RIE;
+			snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	} else {
+		if (chip->midcr & BA0_MIDCR_RIE) {
+			chip->midcr &= ~BA0_MIDCR_RIE;
+			snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return);
+	unsigned char byte;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (up) {
+		if ((chip->midcr & BA0_MIDCR_TIE) == 0) {
+			chip->midcr |= BA0_MIDCR_TIE;
+			/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+			while ((chip->midcr & BA0_MIDCR_TIE) &&
+			       (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					chip->midcr &= ~BA0_MIDCR_TIE;
+				} else {
+					snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte);
+				}
+			}
+			snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	} else {
+		if (chip->midcr & BA0_MIDCR_TIE) {
+			chip->midcr &= ~BA0_MIDCR_TIE;
+			snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_cs4281_midi_output =
+{
+	open:           snd_cs4281_midi_output_open,
+	close:          snd_cs4281_midi_output_close,
+	trigger:        snd_cs4281_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_cs4281_midi_input =
+{
+	open:           snd_cs4281_midi_input_open,
+	close:          snd_cs4281_midi_input_close,
+	trigger:        snd_cs4281_midi_input_trigger,
+};
+
+static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, "CS4281");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = chip;
+	chip->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return 0;
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, dev_id, return);
+	unsigned int status, dma, val;
+	cs4281_dma_t *cdma;
+
+	if (chip == NULL)
+		return;
+	status = snd_cs4281_peekBA0(chip, BA0_HISR);
+	if ((status & 0x7fffffff) == 0) {
+		snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+		return;
+	}
+
+	if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) {
+		for (dma = 0; dma < 4; dma++)
+			if (status & BA0_HISR_DMA(dma)) {
+				cdma = &chip->dma[dma];
+				spin_lock(&chip->reg_lock);
+				/* ack DMA IRQ */
+				val = snd_cs4281_peekBA0(chip, cdma->regHDSR);
+				/* workaround, sometimes CS4281 acknowledges */
+				/* end or middle transfer position twice */
+				cdma->frag++;
+				if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) {
+					cdma->frag--;
+					chip->spurious_dhtc_irq++;
+					spin_unlock(&chip->reg_lock);
+					continue;
+				}
+				if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) {
+					cdma->frag--;
+					chip->spurious_dtc_irq++;
+					spin_unlock(&chip->reg_lock);
+					continue;
+				}
+				spin_unlock(&chip->reg_lock);
+				snd_pcm_period_elapsed(cdma->substream);
+			}
+	}
+
+	if ((status & BA0_HISR_MIDI) && chip->rmidi) {
+		unsigned char c;
+		
+		spin_lock(&chip->reg_lock);
+		while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) {
+			c = snd_cs4281_peekBA0(chip, BA0_MIDRP);
+			if ((chip->midcr & BA0_MIDCR_RIE) == 0)
+				continue;
+			snd_rawmidi_receive(chip->midi_input, &c, 1);
+		}
+		while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) {
+			if ((chip->midcr & BA0_MIDCR_TIE) == 0)
+				break;
+			if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
+				chip->midcr &= ~BA0_MIDCR_TIE;
+				snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+				break;
+			}
+			snd_cs4281_pokeBA0(chip, BA0_MIDWP, c);
+		}
+		spin_unlock(&chip->reg_lock);
+	}
+
+	/* EOI to the PCI part... reenables interrupts */
+	snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+}
+
+#ifdef HAVE_GAMEPORT_SUPPORT
+/*
+ * joystick support
+ */
+static void snd_cs4281_gameport_trigger(struct gameport *gameport)
+{
+	cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport;
+	cs4281_t *chip;
+	snd_assert(gp, return);
+	chip = snd_magic_cast(cs4281_t, gp->chip, return);
+	snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff);
+}
+
+static unsigned char snd_cs4281_gameport_read(struct gameport *gameport)
+{
+	cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport;
+	cs4281_t *chip;
+	snd_assert(gp, return 0);
+	chip = snd_magic_cast(cs4281_t, gp->chip, return 0);
+	return snd_cs4281_peekBA0(chip, BA0_JSPT);
+}
+
+#ifdef COOKED_MODE
+static int snd_cs4281_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport;
+	cs4281_t *chip;
+	unsigned js1, js2, jst;
+	
+	snd_assert(gp, return);
+	chip = snd_magic_cast(cs4281_t, gp->chip, return);
+
+	js1 = snd_cs4281_peekBA0(chip, BA0_JSC1);
+	js2 = snd_cs4281_peekBA0(chip, BA0_JSC2);
+	jst = snd_cs4281_peekBA0(chip, BA0_JSPT);
+	
+	*buttons = (~jst >> 4) & 0x0F; 
+	
+	axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+	axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+	axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+	axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+	for(jst=0;jst<4;++jst)
+		if(axes[jst]==0xFFFF) axes[jst] = -1;
+	return 0;
+}
+#endif
+
+static int snd_cs4281_gameport_open(struct gameport *gameport, int mode)
+{
+	switch (mode) {
+#ifdef COOKED_MODE
+	case GAMEPORT_MODE_COOKED:
+		return 0;
+#endif
+	case GAMEPORT_MODE_RAW:
+		return 0;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+static void __devinit snd_cs4281_gameport(cs4281_t *chip)
+{
+	cs4281_gameport_t *gp;
+	gp = kmalloc(sizeof(*gp), GFP_KERNEL);
+	if (! gp) {
+		snd_printk("cannot allocate gameport area\n");
+		return;
+	}
+	memset(gp, 0, sizeof(*gp));
+	gp->info.open = snd_cs4281_gameport_open;
+	gp->info.read = snd_cs4281_gameport_read;
+	gp->info.trigger = snd_cs4281_gameport_trigger;
+#ifdef COOKED_MODE
+	gp->info.cooked_read = snd_cs4281_gameport_cooked_read;
+#endif
+	gp->chip = chip;
+	chip->gameport = gp;
+
+	snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ?
+	snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+	gameport_register_port(&gp->info);
+}
+
+#endif /* HAVE_GAMEPORT_SUPPORT */
+
+
+static int __devinit snd_cs4281_probe(struct pci_dev *pci,
+				      const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	cs4281_t *chip;
+	opl3_t *opl3;
+	int err;
+
+        if (dev >= SNDRV_CARDS)
+                return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_cs4281_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_cs4281_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_create(card,
+				   (chip->ba0 + BA0_B0AP) >> 2,
+				   (chip->ba0 + BA0_B1AP) >> 2,
+				   OPL3_HW_OPL3_CS4281, 1, &opl3)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef HAVE_GAMEPORT_SUPPORT
+	snd_cs4281_gameport(chip);
+#endif
+	strcpy(card->driver, "CS4281");
+	strcpy(card->shortname, "Cirrus Logic CS4281");
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname,
+		chip->ba0_addr,
+		chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_cs4281_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "CS4281",
+	id_table: snd_cs4281_ids,
+	probe: snd_cs4281_probe,
+	remove: __devexit_p(snd_cs4281_remove),
+};
+	
+static int __init alsa_card_cs4281_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "CS4281 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cs4281_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs4281_init)
+module_exit(alsa_card_cs4281_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cs4281=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_cs4281_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-cs4281=", alsa_card_cs4281_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/cs46xx/Makefile linux-2.4.19-pre5-mjc/sound/pci/cs46xx/Makefile
--- linux/sound/pci/cs46xx/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _cs46xx.o
+
+list-multi   := snd-cs46xx.o
+
+snd-cs46xx-objs := cs46xx.o cs46xx_lib.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o
+
+include $(TOPDIR)/Rules.make
+
+snd-cs46xx.o: $(snd-cs46xx-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-cs46xx-objs)
diff -Nru linux/sound/pci/cs46xx/cs46xx.c linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx.c
--- linux/sound/pci/cs46xx/cs46xx.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,220 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+/*
+  NOTES:
+  - sometimes the sound is metallic and sibilant, unloading and 
+    reloading the module may solve this.
+*/
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/cs46xx.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Cirrus Logic,Sound Fusion (CS4280)},"
+		"{Cirrus Logic,Sound Fusion (CS4610)},"
+		"{Cirrus Logic,Sound Fusion (CS4612)},"
+		"{Cirrus Logic,Sound Fusion (CS4615)},"
+		"{Cirrus Logic,Sound Fusion (CS4622)},"
+		"{Cirrus Logic,Sound Fusion (CS4624)},"
+		"{Cirrus Logic,Sound Fusion (CS4630)}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for the CS46xx soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for the CS46xx soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable CS46xx soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_external_amp, "Force to enable external amplifer.");
+MODULE_PARM_SYNTAX(snd_external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+MODULE_PARM(snd_thinkpad, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_thinkpad, "Force to enable Thinkpad's CLKRUN control.");
+MODULE_PARM_SYNTAX(snd_thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+
+static struct pci_device_id snd_cs46xx_ids[] __devinitdata = {
+        { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* CS4280 */
+        { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* CS4612 */
+        { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* CS4615 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids);
+
+static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
+					   const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	cs46xx_t *chip;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if ((err = snd_cs46xx_create(card, pci,
+				     snd_external_amp[dev], snd_thinkpad[dev],
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs46xx_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	strcpy(card->driver, "CS46xx");
+	strcpy(card->shortname, "Sound Fusion CS46xx");
+	sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i",
+		card->shortname,
+		chip->ba0_addr,
+		chip->ba1_addr,
+		chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifndef PCI_OLD_SUSPEND
+static int snd_card_cs46xx_suspend(struct pci_dev *pci, u32 state)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO);
+	snd_cs46xx_suspend(chip);
+	return 0;
+}
+static int snd_card_cs46xx_resume(struct pci_dev *pci)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO);
+	snd_cs46xx_resume(chip);
+	return 0;
+}
+#else
+static void snd_card_cs46xx_suspend(struct pci_dev *pci)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return);
+	snd_cs46xx_suspend(chip);
+}
+static void snd_card_cs46xx_resume(struct pci_dev *pci)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return);
+	snd_cs46xx_resume(chip);
+}
+#endif
+#endif
+
+static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "Sound Fusion CS46xx",
+	id_table: snd_cs46xx_ids,
+	probe: snd_card_cs46xx_probe,
+	remove: __devexit_p(snd_card_cs46xx_remove),
+#ifdef CONFIG_PM
+	suspend: snd_card_cs46xx_suspend,
+	resume: snd_card_cs46xx_resume,
+#endif
+};
+
+static int __init alsa_card_cs46xx_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Sound Fusion CS46xx soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cs46xx_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs46xx_init)
+module_exit(alsa_card_cs46xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cs46xx=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_cs46xx_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-cs46xx=", alsa_card_cs46xx_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/cs46xx/cs46xx_image.h linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_image.h
--- linux/sound/pci/cs46xx/cs46xx_image.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_image.h	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,3459 @@
+struct BA1struct BA1Struct = {
+{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }},
+{0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00200040,0x00008010,0x00000000,
+0x00000000,0x80000001,0x00000001,0x00060000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00900080,0x00000173,0x00000000,
+0x00000000,0x00000010,0x00800000,0x00900000,
+0xf2c0000f,0x00000200,0x00000000,0x00010600,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x330300c2,
+0x06000000,0x00000000,0x80008000,0x80008000,
+0x3fc0000f,0x00000301,0x00010400,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00b00000,0x00d0806d,0x330480c3,
+0x04800000,0x00000001,0x00800001,0x0000ffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x066a0600,0x06350070,0x0000929d,0x929d929d,
+0x00000000,0x0000735a,0x00000600,0x00000000,
+0x929d735a,0x8734abfe,0x00010000,0x735a735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000804f,0x000000c3,
+0x05000000,0x00a00010,0x00000000,0x80008000,
+0x00000000,0x00000000,0x00000700,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000080,0x00a00000,0x0000809a,0x000000c2,
+0x07400000,0x00000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x000107a0,
+0x00c80028,0x000000c2,0x06800000,0x00000000,
+0x06e00080,0x00300000,0x000080bb,0x000000c9,
+0x07a00000,0x04000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x00000780,
+0x00c80028,0x000000c5,0xff800000,0x00000000,
+0x00640080,0x00c00000,0x00008197,0x000000c9,
+0x07800000,0x04000000,0x80008000,0xffffffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000805e,0x000000c1,
+0x00000000,0x00800000,0x80008000,0x80008000,
+0x00020000,0x0000ffff,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x929d0600,0x929d929d,0x929d929d,0x929d0000,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x00100635,0x060b013f,0x00000004,
+0x00000001,0x007a0002,0x00000000,0x066e0610,
+0x0105929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
+0x00000000,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x00000000,0x06400136,
+0x0000270f,0x00010000,0x007a0000,0x00000000,
+0x068e0645,0x0105929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x735a0100,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00010004,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00001705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00009705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00011705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00019705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00021705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00029705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00031705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00039705,0x00001400,0x000a411e,0x00001003,
+0x000fe19e,0x00001003,0x0009c730,0x00001003,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00009705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00011705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00019705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00021705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00029705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00031705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00039705,0x00001400,0x000a211e,0x00001003,
+0x0000a730,0x00001008,0x000e2730,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x00000000,0x00000000,0x000f619c,0x00001003,
+0x0007f801,0x000c0000,0x00000037,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0000373c,0x00001000,0x00000000,0x00000000,
+0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000273c,0x00001000,
+0x00000033,0x00001000,0x000e679e,0x00001003,
+0x00007705,0x00001400,0x000ac71e,0x00001003,
+0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000a730,0x00001003,
+0x00000033,0x00001000,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x000c0000,
+0x00000032,0x00001000,0x0000273d,0x00001000,
+0x0004a730,0x00001003,0x00000f41,0x00097140,
+0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+0x00002725,0x000aa400,0x00013705,0x00093a00,
+0x0000002e,0x0009d6c0,0x00038630,0x00001004,
+0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
+0x00000000,0x000c70e0,0x0007d182,0x0002c640,
+0x00000630,0x00001004,0x000799b8,0x0002c6c0,
+0x00031705,0x00092240,0x00039f05,0x000932c0,
+0x0003520a,0x00000000,0x00040731,0x0000100b,
+0x00010705,0x000b20c0,0x00000000,0x000eba44,
+0x00032108,0x000c60c4,0x00065208,0x000c2917,
+0x000406b0,0x00001007,0x00012f05,0x00036880,
+0x0002818e,0x000c0000,0x0004410a,0x00000000,
+0x00040630,0x00001007,0x00029705,0x000c0000,
+0x00000000,0x00000000,0x00003fc1,0x0003fc40,
+0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
+0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
+0x000037c1,0x00000000,0x00003fc1,0x000991c0,
+0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
+0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
+0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
+0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
+0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
+0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
+0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
+0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
+0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
+0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
+0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
+0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
+0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
+0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
+0x000683ad,0x00095241,0x00020f05,0x000991c1,
+0x00000000,0x00000000,0x00086f88,0x00001000,
+0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
+0x0009de81,0x000bd300,0x0009d601,0x000b1700,
+0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
+0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
+0x000a1681,0x000b97c0,0x00021601,0x00002500,
+0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
+0x00021681,0x00002d00,0x00020f81,0x000bd800,
+0x000a0701,0x000b5bc0,0x00021601,0x00003500,
+0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
+0x00021681,0x00003d00,0x00020f81,0x000b1d00,
+0x000a0701,0x000b1fc0,0x00021601,0x00020500,
+0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
+0x00021681,0x00020d00,0x00020f81,0x000bde80,
+0x000a0701,0x000bdfc0,0x00021601,0x00021500,
+0x00020f81,0x000b9341,0x00020701,0x000b53c1,
+0x00021681,0x00021d00,0x000a0f81,0x000d0380,
+0x0000b601,0x000b15c0,0x00007b01,0x00000000,
+0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
+0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
+0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
+0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
+0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
+0x0007e48a,0x00000000,0x00011f05,0x00084080,
+0x00000000,0x00000000,0x00001705,0x000b3540,
+0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
+0x00055488,0x00000000,0x0000d482,0x0003fc40,
+0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
+0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
+0x000c86b0,0x00001007,0x00008281,0x000bb240,
+0x0000b801,0x000b7140,0x00007888,0x00000000,
+0x0000073c,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x00055288,0x000c555c,
+0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+0x0000fa88,0x00000000,0x00000032,0x00001000,
+0x0000073d,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x0008c01c,0x00001003,
+0x00002705,0x00001008,0x0008b201,0x000c1392,
+0x0000ba01,0x00000000,0x00008731,0x00001400,
+0x0004c108,0x000fe0c4,0x00057488,0x00000000,
+0x000a6388,0x00001001,0x0008b334,0x000bc141,
+0x0003020e,0x00000000,0x000886b0,0x00001008,
+0x00003625,0x000c5dfa,0x000a638a,0x00001001,
+0x0008020e,0x00001002,0x0008a6b0,0x00001008,
+0x0007f301,0x00000000,0x00000000,0x00000000,
+0x00002725,0x000a8c40,0x000000ae,0x00000000,
+0x000d8630,0x00001008,0x00000000,0x000c74e0,
+0x0007d182,0x0002d640,0x000a8630,0x00001008,
+0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
+0x0007420a,0x000c0000,0x00062208,0x000c4117,
+0x00070630,0x00001009,0x00000000,0x000c0000,
+0x0001022e,0x00000000,0x0003a630,0x00001009,
+0x00000000,0x000c0000,0x00000036,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x0002a730,0x00001008,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0002a730,0x00001008,
+0x00000033,0x00001000,0x0002a705,0x00001008,
+0x00007a01,0x000c0000,0x000e6288,0x000d550a,
+0x0006428a,0x00000000,0x00060730,0x0000100a,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
+0x00057488,0x00000000,0x00033b94,0x00081140,
+0x000183ae,0x00000000,0x000786b0,0x0000100b,
+0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+0x00042731,0x00001003,0x0007aab0,0x00034880,
+0x00048fb0,0x0000100a,0x00057488,0x00000000,
+0x00033b94,0x00081140,0x000183ae,0x00000000,
+0x000806b0,0x0000100b,0x00022f05,0x00000000,
+0x00007401,0x00091140,0x00048f05,0x000951c0,
+0x00042731,0x00001003,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x000fe19e,0x00001003,0x00000000,0x00000000,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00000f41,0x00097140,0x0000a841,0x0009b240,
+0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
+0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
+0x00055208,0x00000000,0x00010705,0x000a2880,
+0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
+0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
+0x00065308,0x000c2997,0x000d86b0,0x0000100a,
+0x0004410a,0x000d40c7,0x00000000,0x00000000,
+0x00080730,0x00001004,0x00056f0a,0x000ea105,
+0x00000000,0x00000000,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x0000273d,0x00001000,0x00000000,0x000eba44,
+0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x00000000,0x000e5084,
+0x00000000,0x000eba44,0x00087401,0x000e4782,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
+0x0007e721,0x000bed40,0x00005f25,0x000badc0,
+0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
+0x00033217,0x00003ec0,0x00065590,0x000b8e40,
+0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
+0x000283a0,0x0000100c,0x000ee388,0x00042970,
+0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
+0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
+0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
+0x00078898,0x00001000,0x00038894,0x00000032,
+0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
+0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
+0x00041705,0x0009ed40,0x00058730,0x00001400,
+0x000d7488,0x000c3a00,0x00048f05,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000}
+ };
diff -Nru linux/sound/pci/cs46xx/cs46xx_lib.c linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_lib.c
--- linux/sound/pci/cs46xx/cs46xx_lib.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_lib.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2305 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Abramo Bagnara <abramo@alsa-project.org>
+ *                   Cirrus Logic, Inc.
+ *  Routines for control of Cirrus Logic CS461x chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    We need a DSP code to support multichannel outputs and S/PDIF.
+ *    Unfortunately, it seems that Cirrus Logic, Inc. is not willing
+ *    to provide us sufficient information about the DSP processor,
+ *    so we can't update the driver.
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/cs46xx.h>
+
+#define chip_t cs46xx_t
+
+/*
+ *  constants
+ */
+
+#if 0
+#define SND_CONFIG_CS46XX_ACCEPT_VALID		/* REQUIRED ONLY FOR OSS EMULATION */
+#endif
+
+#define CS46XX_BA0_SIZE		0x1000
+#define CS46XX_BA1_DATA0_SIZE	0x3000
+#define CS46XX_BA1_DATA1_SIZE	0x3800
+#define CS46XX_BA1_PRG_SIZE	0x7000
+#define CS46XX_BA1_REG_SIZE	0x0100
+
+
+#define CS46XX_PERIOD_SIZE 2048
+#define CS46XX_FRAGS 2
+#define CS46XX_BUFFER_SIZE CS46XX_PERIOD_SIZE * CS46XX_FRAGS
+
+extern snd_pcm_ops_t snd_cs46xx_playback_ops;
+extern snd_pcm_ops_t snd_cs46xx_playback_indirect_ops;
+extern snd_pcm_ops_t snd_cs46xx_capture_ops;
+extern snd_pcm_ops_t snd_cs46xx_capture_indirect_ops;
+
+
+/*
+ *  common I/O routines
+ */
+
+static inline void snd_cs46xx_poke(cs46xx_t *chip, unsigned long reg, unsigned int val)
+{
+	unsigned int bank = reg >> 16;
+	unsigned int offset = reg & 0xffff;
+	writel(val, chip->region.idx[bank+1].remap_addr + offset);
+}
+
+static inline unsigned int snd_cs46xx_peek(cs46xx_t *chip, unsigned long reg)
+{
+	unsigned int bank = reg >> 16;
+	unsigned int offset = reg & 0xffff;
+	return readl(chip->region.idx[bank+1].remap_addr + offset);
+}
+
+static inline void snd_cs46xx_pokeBA0(cs46xx_t *chip, unsigned long offset, unsigned int val)
+{
+	writel(val, chip->region.name.ba0.remap_addr + offset);
+}
+
+static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offset)
+{
+	return readl(chip->region.name.ba0.remap_addr + offset);
+}
+
+
+static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,
+					    unsigned short reg)
+{
+	int count;
+	unsigned short result;
+
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97 
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+	 *  5. if DCV not cleared, break and return error
+	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
+	 */
+
+	snd_cs46xx_peekBA0(chip, BA0_ACSDA);
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  set CRW - Read command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+	 */
+
+	snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
+					     ACCTL_VFRM | ACCTL_ESYN |
+					     ACCTL_RSTN);
+
+
+	/*
+	 *  Wait for the read to occur.
+	 */
+	for (count = 0; count < 1000; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+	 	 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the read has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
+		 */
+		if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV))
+			goto ok1;
+	}
+
+	snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+	result = 0xffff;
+	goto end;
+	
+ ok1:
+	/*
+	 *  Wait for the valid status bit to go active.
+	 */
+	for (count = 0; count < 100; count++) {
+		/*
+		 *  Read the AC97 status register.
+		 *  ACSTS = Status Register = 464h
+		 *  VSTS - Valid Status
+		 */
+		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_VSTS)
+			goto ok2;
+		udelay(10);
+	}
+	
+	snd_printk("AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
+	result = 0xffff;
+	goto end;
+
+ ok2:
+	/*
+	 *  Read the data returned from the AC97 register.
+	 *  ACSDA = Status Data Register = 474h
+	 */
+#if 0
+	printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
+			snd_cs46xx_peekBA0(chip, BA0_ACSDA),
+			snd_cs46xx_peekBA0(chip, BA0_ACCAD));
+#endif
+	result = snd_cs46xx_peekBA0(chip, BA0_ACSDA);
+ end:
+	return result;
+}
+
+static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97,
+					    unsigned short reg)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return -ENXIO);
+	unsigned short val;
+	chip->active_ctrl(chip, 1);
+	val = snd_cs46xx_codec_read(chip, reg);
+	chip->active_ctrl(chip, -1);
+	return val;
+}
+
+
+static void snd_cs46xx_codec_write(cs46xx_t *chip,
+				   unsigned short reg,
+				   unsigned short val)
+{
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+	 *  5. if DCV not cleared, break and return error
+	 */
+	int count;
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  reset CRW - Write command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+         */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, val);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |
+				             ACCTL_ESYN | ACCTL_RSTN);
+	for (count = 0; count < 4000; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+		 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the write has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 07h
+		 */
+		if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) {
+			return;
+		}
+	}
+	snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+}
+
+static void snd_cs46xx_ac97_write(ac97_t *ac97,
+				   unsigned short reg,
+				   unsigned short val)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return);
+	int val2 = 0;
+
+	chip->active_ctrl(chip, 1);
+	if (reg == AC97_CD)
+		val2 = snd_cs46xx_codec_read(chip, AC97_CD);
+
+	snd_cs46xx_codec_write(chip, reg, val);
+
+	
+	/*
+	 *	Adjust power if the mixer is selected/deselected according
+	 *	to the CD.
+	 *
+	 *	IF the CD is a valid input source (mixer or direct) AND
+	 *		the CD is not muted THEN power is needed
+	 *
+	 *	We do two things. When record select changes the input to
+	 *	add/remove the CD we adjust the power count if the CD is
+	 *	unmuted.
+	 *
+	 *	When the CD mute changes we adjust the power level if the
+	 *	CD was a valid input.
+	 *
+	 *      We also check for CD volume != 0, as the CD mute isn't
+	 *      normally tweaked from userspace.
+	 */
+	 
+	/* CD mute change ? */
+	
+	if (reg == AC97_CD) {
+		/* Mute bit change ? */
+		if ((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) {
+			/* Mute on */
+			if(val&0x8000 || val == 0x1f1f)
+				chip->amplifier_ctrl(chip, -1);
+			else /* Mute off power on */
+				chip->amplifier_ctrl(chip, 1);
+		}
+	}
+
+	chip->active_ctrl(chip, -1);
+}
+
+
+/*
+ *  Chip initialization
+ */
+
+int snd_cs46xx_download(cs46xx_t *chip,
+			u32 *src,
+                        unsigned long offset,
+                        unsigned long len)
+{
+	unsigned long dst;
+	unsigned int bank = offset >> 16;
+	offset = offset & 0xffff;
+
+	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+	dst = chip->region.idx[bank+1].remap_addr + offset;
+	len /= sizeof(u32);
+
+	/* writel already converts 32-bit value to right endianess */
+	while (len-- > 0) {
+		writel(*src++, dst);
+		dst += sizeof(u32);
+	}
+	return 0;
+}
+
+/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */
+#define BA1_DWORD_SIZE		(13 * 1024 + 512)
+#define BA1_MEMORY_COUNT	3
+
+struct BA1struct {
+	struct {
+		unsigned long offset;
+		unsigned long size;
+	} memory[BA1_MEMORY_COUNT];
+	u32 map[BA1_DWORD_SIZE];
+};
+
+static
+#include "cs46xx_image.h"
+
+int snd_cs46xx_download_image(cs46xx_t *chip)
+{
+	int idx, err;
+	unsigned long offset = 0;
+
+	for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
+		if ((err = snd_cs46xx_download(chip,
+					       &BA1Struct.map[offset],
+					       BA1Struct.memory[idx].offset,
+					       BA1Struct.memory[idx].size)) < 0)
+			return err;
+		offset += BA1Struct.memory[idx].size >> 2;
+	}	
+	return 0;
+}
+
+/*
+ *  Chip reset
+ */
+
+static void snd_cs46xx_reset(cs46xx_t *chip)
+{
+	int idx;
+
+	/*
+	 *  Write the reset bit of the SP control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP);
+
+	/*
+	 *  Write the control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN);
+
+	/*
+	 *  Clear the trap registers.
+	 */
+	for (idx = 0; idx < 8; idx++) {
+		snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx);
+		snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF);
+	}
+	snd_cs46xx_poke(chip, BA1_DREG, 0);
+
+	/*
+	 *  Set the frame timer to reflect the number of cycles per frame.
+	 */
+	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
+}
+
+static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip)
+{
+	int idx, loop, powerdown = 0;
+	unsigned int tmp;
+
+	/*
+	 *  See if the devices are powered down.  If so, we must power them up first
+	 *  or they will not respond.
+	 */
+	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
+	if (!(tmp & CLKCR1_SWCE)) {
+		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
+		powerdown = 1;
+	}
+
+	/*
+	 *  We want to clear out the serial port FIFOs so we don't end up playing
+	 *  whatever random garbage happens to be in them.  We fill the sample FIFOS
+	 *  with zero (silence).
+         */
+	snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0);
+
+	/*
+	 *  Fill all 256 sample FIFO locations.
+	 */
+	for (idx = 0; idx < 256; idx++) {
+		/*
+		 *  Make sure the previous FIFO write operation has completed.
+		 */
+		for (loop = 0; loop < 5; loop++) {
+			udelay(50);
+			if (!(snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY))
+				break;
+		}
+		if (snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY) {
+			if (powerdown)
+				snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+		}
+		/*
+		 *  Write the serial port FIFO index.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
+		/*
+		 *  Tell the serial port to load the new value into the FIFO location.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
+	}
+	/*
+	 *  Now, if we powered up the devices, then power them back down again.
+	 *  This is kinda ugly, but should never happen.
+	 */
+	if (powerdown)
+		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+}
+
+static void snd_cs46xx_proc_start(cs46xx_t *chip)
+{
+	int cnt;
+
+	/*
+	 *  Set the frame timer to reflect the number of cycles per frame.
+	 */
+	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
+	/*
+	 *  Turn on the run, run at frame, and DMA enable bits in the local copy of
+	 *  the SP control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
+	/*
+	 *  Wait until the run at frame bit resets itself in the SP control
+	 *  register.
+	 */
+	for (cnt = 0; cnt < 25; cnt++) {
+		udelay(50);
+		if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR))
+			break;
+	}
+
+	if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)
+		snd_printk("SPCR_RUNFR never reset\n");
+}
+
+static void snd_cs46xx_proc_stop(cs46xx_t *chip)
+{
+	/*
+	 *  Turn off the run, run at frame, and DMA enable bits in the local copy of
+	 *  the SP control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, 0);
+}
+
+/*
+ *  Sample rate routines
+ */
+
+#define GOF_PER_SEC 200
+
+static void snd_cs46xx_set_play_sample_rate(cs46xx_t *chip, unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int tmp1, tmp2;
+	unsigned int phiIncr;
+	unsigned int correctionPerGOF, correctionPerSec;
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *  phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+         *                                   GOF_PER_SEC)
+         *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
+         *                       GOF_PER_SEC * correctionPerGOF
+	 *
+	 *  i.e.
+	 *
+	 *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF:correctionPerSec =
+	 *      dividend:remainder(ulOther / GOF_PER_SEC)
+	 */
+	tmp1 = rate << 16;
+	phiIncr = tmp1 / 48000;
+	tmp1 -= phiIncr * 48000;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / 48000;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * 48000;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+
+	/*
+	 *  Fill in the SampleRateConverter control block.
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs46xx_poke(chip, BA1_PSRC,
+	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+	snd_cs46xx_poke(chip, BA1_PPI, phiIncr);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs46xx_set_capture_sample_rate(cs46xx_t *chip, unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int phiIncr, coeffIncr, tmp1, tmp2;
+	unsigned int correctionPerGOF, correctionPerSec, initialDelay;
+	unsigned int frameGroupLength, cnt;
+
+	/*
+	 *  We can only decimate by up to a factor of 1/9th the hardware rate.
+	 *  Correct the value if an attempt is made to stray outside that limit.
+	 */
+	if ((rate * 9) < 48000)
+		rate = 48000 / 9;
+
+	/*
+	 *  We can not capture at at rate greater than the Input Rate (48000).
+	 *  Return an error if an attempt is made to stray outside that limit.
+	 */
+	if (rate > 48000)
+		rate = 48000;
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *     coeffIncr = -floor((Fs,out * 2^23) / Fs,in)
+	 *     phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *     correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+	 *                                GOF_PER_SEC)
+	 *     correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
+	 *                          GOF_PER_SEC * correctionPerGOF
+	 *     initialDelay = ceil((24 * Fs,in) / Fs,out)
+	 *
+	 * i.e.
+	 *
+	 *     coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
+	 *     phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *     correctionPerGOF:correctionPerSec =
+	 * 	    dividend:remainder(ulOther / GOF_PER_SEC)
+	 *     initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
+	 */
+
+	tmp1 = rate << 16;
+	coeffIncr = tmp1 / 48000;
+	tmp1 -= coeffIncr * 48000;
+	tmp1 <<= 7;
+	coeffIncr <<= 7;
+	coeffIncr += tmp1 / 48000;
+	coeffIncr ^= 0xFFFFFFFF;
+	coeffIncr++;
+	tmp1 = 48000 << 16;
+	phiIncr = tmp1 / rate;
+	tmp1 -= phiIncr * rate;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / rate;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * rate;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+	initialDelay = ((48000 * 24) + rate - 1) / rate;
+
+	/*
+	 *  Fill in the VariDecimate control block.
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs46xx_poke(chip, BA1_CSRC,
+		((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+	snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
+	snd_cs46xx_poke(chip, BA1_CD,
+		(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
+	snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/*
+	 *  Figure out the frame group length for the write back task.  Basically,
+	 *  this is just the factors of 24000 (2^6*3*5^3) that are not present in
+	 *  the output sample rate.
+	 */
+	frameGroupLength = 1;
+	for (cnt = 2; cnt <= 64; cnt *= 2) {
+		if (((rate / cnt) * cnt) != rate)
+			frameGroupLength *= 2;
+	}
+	if (((rate / 3) * 3) != rate) {
+		frameGroupLength *= 3;
+	}
+	for (cnt = 5; cnt <= 125; cnt *= 5) {
+		if (((rate / cnt) * cnt) != rate) 
+			frameGroupLength *= 5;
+        }
+
+	/*
+	 * Fill in the WriteBack control block.
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);
+	snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));
+	snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);
+	snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));
+	snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream, 
+					snd_pcm_uframes_t frames)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->play.appl_ptr;
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		chip->play.sw_ready += diff << chip->play.shift;
+	}
+	chip->play.sw_ready += frames << chip->play.shift;
+	chip->play.appl_ptr = runtime->control->appl_ptr + frames;
+	while (chip->play.hw_ready < CS46XX_BUFFER_SIZE && 
+	       chip->play.sw_ready > 0) {
+		size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->play.hw_data;
+		size_t sw_to_end = chip->play.sw_bufsize - chip->play.sw_data;
+		size_t bytes = CS46XX_BUFFER_SIZE - chip->play.hw_ready;
+		if (chip->play.sw_ready < bytes)
+			bytes = chip->play.sw_ready;
+		if (hw_to_end < bytes)
+			bytes = hw_to_end;
+		if (sw_to_end < bytes)
+			bytes = sw_to_end;
+		memcpy(chip->play.hw_area + chip->play.hw_data,
+		       runtime->dma_area + chip->play.sw_data,
+		       bytes);
+		chip->play.hw_data += bytes;
+		if (chip->play.hw_data == CS46XX_BUFFER_SIZE)
+			chip->play.hw_data = 0;
+		chip->play.sw_data += bytes;
+		if (chip->play.sw_data == chip->play.sw_bufsize)
+			chip->play.sw_data = 0;
+		chip->play.hw_ready += bytes;
+		chip->play.sw_ready -= bytes;
+	}
+	return 0;
+}
+
+static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream, 
+				       snd_pcm_uframes_t frames)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr;
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		chip->capt.sw_ready -= diff << chip->capt.shift;
+	}
+	chip->capt.sw_ready -= frames << chip->capt.shift;
+	chip->capt.appl_ptr = runtime->control->appl_ptr + frames;
+	while (chip->capt.hw_ready > 0 && 
+	       chip->capt.sw_ready < chip->capt.sw_bufsize) {
+		size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->capt.hw_data;
+		size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data;
+		size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready;
+		if (chip->capt.hw_ready < bytes)
+			bytes = chip->capt.hw_ready;
+		if (hw_to_end < bytes)
+			bytes = hw_to_end;
+		if (sw_to_end < bytes)
+			bytes = sw_to_end;
+		memcpy(runtime->dma_area + chip->capt.sw_data,
+		       chip->capt.hw_area + chip->capt.hw_data,
+		       bytes);
+		chip->capt.hw_data += bytes;
+		if (chip->capt.hw_data == CS46XX_BUFFER_SIZE)
+			chip->capt.hw_data = 0;
+		chip->capt.sw_data += bytes;
+		if (chip->capt.sw_data == chip->capt.sw_bufsize)
+			chip->capt.sw_data = 0;
+		chip->capt.hw_ready -= bytes;
+		chip->capt.sw_ready += bytes;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr;
+	return ptr >> chip->play.shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr;
+	ssize_t bytes = ptr - chip->play.hw_io;
+	if (bytes < 0)
+		bytes += CS46XX_BUFFER_SIZE;
+	chip->play.hw_io = ptr;
+	chip->play.hw_ready -= bytes;
+	chip->play.sw_io += bytes;
+	if (chip->play.sw_io > chip->play.sw_bufsize)
+		chip->play.sw_io -= chip->play.sw_bufsize;
+	snd_cs46xx_playback_transfer(substream, 0);
+	return chip->play.sw_io >> chip->play.shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr;
+	return ptr >> chip->capt.shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr;
+	ssize_t bytes = ptr - chip->capt.hw_io;
+	if (bytes < 0)
+		bytes += CS46XX_BUFFER_SIZE;
+	chip->capt.hw_io = ptr;
+	chip->capt.hw_ready += bytes;
+	chip->capt.sw_io += bytes;
+	if (chip->capt.sw_io > chip->capt.sw_bufsize)
+		chip->capt.sw_io -= chip->capt.sw_bufsize;
+	snd_cs46xx_capture_transfer(substream, 0);
+	return chip->capt.sw_io >> chip->capt.shift;
+}
+
+static int snd_cs46xx_playback_copy(snd_pcm_substream_t *substream,
+				    int channel,
+				    snd_pcm_uframes_t hwoff,
+				    void *src,
+				    snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	size_t hwoffb = hwoff << chip->play.shift;
+	size_t bytes = frames << chip->play.shift;
+	char *hwbuf = runtime->dma_area + hwoffb;
+	if (copy_from_user(hwbuf, src, bytes))
+		return -EFAULT;
+	spin_lock_irq(&runtime->lock);
+	snd_cs46xx_playback_transfer(substream, frames);
+	spin_unlock_irq(&runtime->lock);
+	return 0;
+}
+	
+static int snd_cs46xx_capture_copy(snd_pcm_substream_t *substream,
+				   int channel,
+				   snd_pcm_uframes_t hwoff,
+				   void *dst,
+				   snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	size_t hwoffb = hwoff << chip->capt.shift;
+	size_t bytes = frames << chip->capt.shift;
+	char *hwbuf = runtime->dma_area + hwoffb;
+	if (copy_to_user(dst, hwbuf, bytes))
+		return -EFAULT;
+	spin_lock_irq(&runtime->lock);
+	snd_cs46xx_capture_transfer(substream, frames);
+	spin_unlock_irq(&runtime->lock);
+	return 0;
+}
+	
+static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int tmp;
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (substream->runtime->periods != CS46XX_FRAGS)
+			snd_cs46xx_playback_transfer(substream, 0);
+		tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+		tmp &= 0x0000ffff;
+		snd_cs46xx_poke(chip, BA1_PCTL, chip->play.ctl | tmp);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+		tmp &= 0x0000ffff;
+		snd_cs46xx_poke(chip, BA1_PCTL, tmp);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	unsigned int tmp;
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+		tmp &= 0xffff0000;
+		snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+		tmp &= 0xffff0000;
+		snd_cs46xx_poke(chip, BA1_CCTL, tmp);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if (params_periods(hw_params) == CS46XX_FRAGS) {
+		if (runtime->dma_area != chip->play.hw_area)
+			snd_pcm_lib_free_pages(substream);
+		runtime->dma_area = chip->play.hw_area;
+		runtime->dma_addr = chip->play.hw_addr;
+		runtime->dma_bytes = chip->play.hw_size;
+		substream->ops = &snd_cs46xx_playback_ops;
+	} else {
+		if (runtime->dma_area == chip->play.hw_area) {
+			runtime->dma_area = NULL;
+			runtime->dma_addr = 0;
+			runtime->dma_bytes = 0;
+		}
+		if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+			return err;
+		substream->ops = &snd_cs46xx_playback_indirect_ops;
+	}
+	return 0;
+}
+
+static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (runtime->dma_area != chip->play.hw_area)
+		snd_pcm_lib_free_pages(substream);
+	runtime->dma_area = NULL;
+	runtime->dma_addr = 0;
+	runtime->dma_bytes = 0;
+	return 0;
+}
+
+static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned int tmp;
+	unsigned int pfie;
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	pfie = snd_cs46xx_peek(chip, BA1_PFIE);
+	pfie &= ~0x0000f03f;
+
+	chip->play.shift = 2;
+	if (runtime->channels == 1) {
+		chip->play.shift--;
+		pfie |= 0x00002000;
+	}
+	if (snd_pcm_format_width(runtime->format) == 8) {
+		chip->play.shift--;
+		pfie |= 0x00001000;
+	}
+	if (snd_pcm_format_unsigned(runtime->format))
+		pfie |= 0x00008000;
+	if (snd_pcm_format_big_endian(runtime->format))
+		pfie |= 0x00004000;
+
+	
+	chip->play.sw_bufsize = snd_pcm_lib_buffer_bytes(substream);
+	chip->play.sw_data = chip->play.sw_io = chip->play.sw_ready = 0;
+	chip->play.hw_data = chip->play.hw_io = chip->play.hw_ready = 0;
+	chip->play.appl_ptr = 0;
+	snd_cs46xx_poke(chip, BA1_PBA, chip->play.hw_addr);
+
+	tmp = snd_cs46xx_peek(chip, BA1_PDTC);
+	tmp &= ~0x000003ff;
+	tmp |= (4 << chip->play.shift) - 1;
+	snd_cs46xx_poke(chip, BA1_PDTC, tmp);
+
+	snd_cs46xx_poke(chip, BA1_PFIE, pfie);
+
+	snd_cs46xx_set_play_sample_rate(chip, runtime->rate);
+	return 0;
+}
+
+static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if (runtime->periods == CS46XX_FRAGS) {
+		if (runtime->dma_area != chip->capt.hw_area)
+			snd_pcm_lib_free_pages(substream);
+		runtime->dma_area = chip->capt.hw_area;
+		runtime->dma_addr = chip->capt.hw_addr;
+		runtime->dma_bytes = chip->capt.hw_size;
+		substream->ops = &snd_cs46xx_capture_ops;
+	} else {
+		if (runtime->dma_area == chip->capt.hw_area) {
+			runtime->dma_area = NULL;
+			runtime->dma_addr = 0;
+			runtime->dma_bytes = 0;
+		}
+		if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+			return err;
+		substream->ops = &snd_cs46xx_capture_indirect_ops;
+	}
+	return 0;
+}
+
+static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (runtime->dma_area != chip->capt.hw_area)
+		snd_pcm_lib_free_pages(substream);
+	runtime->dma_area = NULL;
+	runtime->dma_addr = 0;
+	runtime->dma_bytes = 0;
+	return 0;
+}
+
+static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_addr);
+	chip->capt.shift = 2;
+	chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream);
+	chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0;
+	chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0;
+	chip->capt.appl_ptr = 0;
+	snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
+	return 0;
+}
+
+static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, dev_id, return);
+	unsigned int status;
+
+	/*
+	 *  Read the Interrupt Status Register to clear the interrupt
+	 */
+	status = snd_cs46xx_peekBA0(chip, BA0_HISR);
+	if ((status & 0x7fffffff) == 0) {
+		snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
+		return;
+	}
+
+	if ((status & HISR_VC0) && chip->pcm) {
+		if (chip->play.substream)
+			snd_pcm_period_elapsed(chip->play.substream);
+	}
+	if ((status & HISR_VC1) && chip->pcm) {
+		if (chip->capt.substream)
+			snd_pcm_period_elapsed(chip->capt.substream);
+	}
+	if ((status & HISR_MIDI) && chip->rmidi) {
+		unsigned char c;
+		
+		spin_lock(&chip->reg_lock);
+		while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) {
+			c = snd_cs46xx_peekBA0(chip, BA0_MIDRP);
+			if ((chip->midcr & MIDCR_RIE) == 0)
+				continue;
+			snd_rawmidi_receive(chip->midi_input, &c, 1);
+		}
+		while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
+			if ((chip->midcr & MIDCR_TIE) == 0)
+				break;
+			if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
+				chip->midcr &= ~MIDCR_TIE;
+				snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+				break;
+			}
+			snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c);
+		}
+		spin_unlock(&chip->reg_lock);
+	}
+	/*
+	 *  EOI to the PCI part....reenables interrupts
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
+}
+
+static snd_pcm_hardware_t snd_cs46xx_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+#ifdef SND_CONFIG_CS46XX_ACCEPT_VALID
+				/* NOT TRUE!!! OSS REQUIRES IT */
+				 SNDRV_PCM_INFO_MMAP_VALID | 
+#endif
+				 SNDRV_PCM_INFO_INTERLEAVED | 
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+				 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5500,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(256 * 1024),
+	period_bytes_min:	CS46XX_PERIOD_SIZE,
+	period_bytes_max:	CS46XX_PERIOD_SIZE,
+	periods_min:		CS46XX_FRAGS,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_cs46xx_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+#ifdef SND_CONFIG_CS46XX_ACCEPT_VALID
+				 /* NOT TRUE!!! OSS REQUIRES IT */
+				 SNDRV_PCM_INFO_MMAP_VALID |
+#endif
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5500,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	(256 * 1024),
+	period_bytes_min:	CS46XX_PERIOD_SIZE,
+	period_bytes_max:	CS46XX_PERIOD_SIZE,
+	periods_min:		CS46XX_FRAGS,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+	if ((chip->play.hw_area = snd_malloc_pci_pages(chip->pci, chip->play.hw_size, &chip->play.hw_addr)) == NULL)
+		return -ENOMEM;
+	chip->play.substream = substream;
+	substream->runtime->hw = snd_cs46xx_playback;
+	chip->active_ctrl(chip, 1);
+	chip->amplifier_ctrl(chip, 1);
+	return 0;
+}
+
+static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+	if ((chip->capt.hw_area = snd_malloc_pci_pages(chip->pci, chip->capt.hw_size, &chip->capt.hw_addr)) == NULL)
+		return -ENOMEM;
+	chip->capt.substream = substream;
+	substream->runtime->hw = snd_cs46xx_capture;
+	chip->active_ctrl(chip, 1);
+	chip->amplifier_ctrl(chip, 1);
+	return 0;
+}
+
+static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->play.substream = NULL;
+	snd_free_pci_pages(chip->pci, chip->play.hw_size, chip->play.hw_area, chip->play.hw_addr);
+	chip->active_ctrl(chip, -1);
+	chip->amplifier_ctrl(chip, -1);
+	return 0;
+}
+
+static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream)
+{
+	cs46xx_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capt.substream = NULL;
+	snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr);
+	chip->active_ctrl(chip, -1);
+	chip->amplifier_ctrl(chip, -1);
+	return 0;
+}
+
+snd_pcm_ops_t snd_cs46xx_playback_ops = {
+	open:			snd_cs46xx_playback_open,
+	close:			snd_cs46xx_playback_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_cs46xx_playback_hw_params,
+	hw_free:		snd_cs46xx_playback_hw_free,
+	prepare:		snd_cs46xx_playback_prepare,
+	trigger:		snd_cs46xx_playback_trigger,
+	pointer:		snd_cs46xx_playback_direct_pointer,
+};
+
+snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = {
+	open:			snd_cs46xx_playback_open,
+	close:			snd_cs46xx_playback_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_cs46xx_playback_hw_params,
+	hw_free:		snd_cs46xx_playback_hw_free,
+	prepare:		snd_cs46xx_playback_prepare,
+	trigger:		snd_cs46xx_playback_trigger,
+	copy:			snd_cs46xx_playback_copy,
+	pointer:		snd_cs46xx_playback_indirect_pointer,
+};
+
+snd_pcm_ops_t snd_cs46xx_capture_ops = {
+	open:			snd_cs46xx_capture_open,
+	close:			snd_cs46xx_capture_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_cs46xx_capture_hw_params,
+	hw_free:		snd_cs46xx_capture_hw_free,
+	prepare:		snd_cs46xx_capture_prepare,
+	trigger:		snd_cs46xx_capture_trigger,
+	pointer:		snd_cs46xx_capture_direct_pointer,
+};
+
+snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = {
+	open:			snd_cs46xx_capture_open,
+	close:			snd_cs46xx_capture_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_cs46xx_capture_hw_params,
+	hw_free:		snd_cs46xx_capture_hw_free,
+	prepare:		snd_cs46xx_capture_prepare,
+	trigger:		snd_cs46xx_capture_trigger,
+	copy:			snd_cs46xx_capture_copy,
+	pointer:		snd_cs46xx_capture_indirect_pointer,
+};
+
+static void snd_cs46xx_pcm_free(snd_pcm_t *pcm)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "CS46xx", device, 1, 1, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_cs46xx_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "CS46xx");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer routines
+ */
+
+static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+	chip->eapd_switch = NULL;
+}
+
+static int snd_cs46xx_vol_info(snd_kcontrol_t *kcontrol, 
+			       snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 32767;
+	return 0;
+}
+
+static int snd_cs46xx_vol_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	unsigned int val = snd_cs46xx_peek(chip, reg);
+	ucontrol->value.integer.value[0] = 0xffff - (val >> 16);
+	ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff);
+	return 0;
+}
+
+static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 | 
+			    (0xffff - ucontrol->value.integer.value[1]));
+	unsigned int old = snd_cs46xx_peek(chip, reg);
+	int change = (old != val);
+	if (change)
+		snd_cs46xx_poke(chip, reg, val);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "DAC Volume",
+	info: snd_cs46xx_vol_info,
+	get: snd_cs46xx_vol_get,
+	put: snd_cs46xx_vol_put,
+	private_value: BA1_PVOL,
+},
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "ADC Volume",
+	info: snd_cs46xx_vol_info,
+	get: snd_cs46xx_vol_get,
+	put: snd_cs46xx_vol_put,
+	private_value: BA1_CVOL,
+}};
+
+int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
+{
+	snd_card_t *card = chip->card;
+	ac97_t ac97;
+	snd_ctl_elem_id_t id;
+	int err;
+	int idx;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_cs46xx_ac97_write;
+	ac97.read = snd_cs46xx_ac97_read;
+	ac97.private_data = chip;
+	ac97.private_free = snd_cs46xx_mixer_free_ac97;
+
+	snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000);
+	for (idx = 0; idx < 100; ++idx) {
+		if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000)
+			goto _ok;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ/100);
+	}
+	return -ENXIO;
+
+ _ok:
+	if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0)
+		return err;
+	for (idx = 0; idx < sizeof(snd_cs46xx_controls) / 
+		     sizeof(snd_cs46xx_controls[0]); idx++) {
+		snd_kcontrol_t *kctl;
+		kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip);
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+
+	/* get EAPD mixer switch (for voyetra hack) */
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, "External Amplifier Power Down");
+	chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
+
+	return 0;
+}
+
+/*
+ *  RawMIDI interface
+ */
+
+static void snd_cs46xx_midi_reset(cs46xx_t *chip)
+{
+	snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST);
+	udelay(100);
+	snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+}
+
+static int snd_cs46xx_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO);
+
+	chip->active_ctrl(chip, 1);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->uartm |= CS46XX_MODE_INPUT;
+	chip->midcr |= MIDCR_RXE;
+	chip->midi_input = substream;
+	if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs46xx_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
+	chip->midi_input = NULL;
+	if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	chip->uartm &= ~CS46XX_MODE_INPUT;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	chip->active_ctrl(chip, -1);
+	return 0;
+}
+
+static int snd_cs46xx_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO);
+
+	chip->active_ctrl(chip, 1);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->uartm |= CS46XX_MODE_OUTPUT;
+	chip->midcr |= MIDCR_TXE;
+	chip->midi_output = substream;
+	if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs46xx_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
+	chip->midi_output = NULL;
+	if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	chip->uartm &= ~CS46XX_MODE_OUTPUT;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	chip->active_ctrl(chip, -1);
+	return 0;
+}
+
+static void snd_cs46xx_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (up) {
+		if ((chip->midcr & MIDCR_RIE) == 0) {
+			chip->midcr |= MIDCR_RIE;
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	} else {
+		if (chip->midcr & MIDCR_RIE) {
+			chip->midcr &= ~MIDCR_RIE;
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return);
+	unsigned char byte;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (up) {
+		if ((chip->midcr & MIDCR_TIE) == 0) {
+			chip->midcr |= MIDCR_TIE;
+			/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+			while ((chip->midcr & MIDCR_TIE) &&
+			       (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					chip->midcr &= ~MIDCR_TIE;
+				} else {
+					snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte);
+				}
+			}
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	} else {
+		if (chip->midcr & MIDCR_TIE) {
+			chip->midcr &= ~MIDCR_TIE;
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_cs46xx_midi_output =
+{
+	open:           snd_cs46xx_midi_output_open,
+	close:          snd_cs46xx_midi_output_close,
+	trigger:        snd_cs46xx_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_cs46xx_midi_input =
+{
+	open:           snd_cs46xx_midi_input_open,
+	close:          snd_cs46xx_midi_input_close,
+	trigger:        snd_cs46xx_midi_input_trigger,
+};
+
+int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, "CS46XX");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = chip;
+	chip->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	return 0;
+}
+
+/*
+ *  proc interface
+ */
+
+static long snd_cs46xx_io_read(snd_info_entry_t *entry, void *file_private_data,
+			       struct file *file, char *buf, long count)
+{
+	long size;
+	snd_cs46xx_region_t *region = (snd_cs46xx_region_t *)entry->private_data;
+	
+	size = count;
+	if (file->f_pos + size > region->size)
+		size = region->size - file->f_pos;
+	if (size > 0) {
+		char *tmp;
+		long res;
+		unsigned long virt;
+		if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		virt = region->remap_addr + file->f_pos;
+		memcpy_fromio(tmp, virt, size);
+		if (copy_to_user(buf, tmp, size))
+			res = -EFAULT;
+		else {
+			res = size;
+			file->f_pos += size;
+		}
+		kfree(tmp);
+		return res;
+	}
+	return 0;
+}
+
+static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
+	read: snd_cs46xx_io_read,
+};
+
+static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip)
+{
+	snd_info_entry_t *entry;
+	int idx;
+	
+	for (idx = 0; idx < 5; idx++) {
+		snd_cs46xx_region_t *region = &chip->region.idx[idx];
+		entry = snd_info_create_card_entry(card, region->name, card->proc_root);
+		if (entry) {
+			entry->content = SNDRV_INFO_CONTENT_DATA;
+			entry->private_data = chip;
+			entry->c.ops = &snd_cs46xx_proc_io_ops;
+			entry->size = region->size;
+			entry->mode = S_IFREG | S_IRUSR;
+			if (snd_info_register(entry) < 0) {
+				snd_info_unregister(entry);
+				entry = NULL;
+			}
+		}
+		region->proc_entry = entry;
+	}
+	return 0;
+}
+
+static int snd_cs46xx_proc_done(cs46xx_t *chip)
+{
+	int idx;
+
+	for (idx = 0; idx < 5; idx++) {
+		snd_cs46xx_region_t *region = &chip->region.idx[idx];
+		if (region->proc_entry) {
+			snd_info_unregister((snd_info_entry_t *) region->proc_entry);
+			region->proc_entry = NULL;
+		}
+	}
+	return 0;
+}
+
+/*
+ * stop the h/w
+ */
+static void snd_cs46xx_hw_stop(cs46xx_t *chip)
+{
+	unsigned int tmp;
+
+	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	tmp |=  0x00000010;
+	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt disable */
+
+	tmp = snd_cs46xx_peek(chip, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000011;
+	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt disable */
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+	snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+	/*
+         *  Reset the processor.
+         */
+	snd_cs46xx_reset(chip);
+
+	snd_cs46xx_proc_stop(chip);
+
+	/*
+	 *  Power down the PLL.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE;
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+}
+
+
+static int snd_cs46xx_free(cs46xx_t *chip)
+{
+	int idx;
+
+	snd_assert(chip != NULL, return -EINVAL);
+
+	if (chip->active_ctrl)
+		chip->active_ctrl(chip, 1);
+
+#ifdef CONFIG_PM
+	if (chip->pm_dev)
+		pm_unregister(chip->pm_dev);
+#endif
+	if (chip->amplifier_ctrl)
+		chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */
+	
+	snd_cs46xx_proc_done(chip);
+
+	if (chip->region.idx[0].resource)
+		snd_cs46xx_hw_stop(chip);
+
+	for (idx = 0; idx < 5; idx++) {
+		snd_cs46xx_region_t *region = &chip->region.idx[idx];
+		if (region->remap_addr)
+			iounmap((void *) region->remap_addr);
+		if (region->resource) {
+			release_resource(region->resource);
+			kfree_nocheck(region->resource);
+		}
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+
+	if (chip->active_ctrl)
+		chip->active_ctrl(chip, -chip->amplifier);
+
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_cs46xx_dev_free(snd_device_t *device)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, device->device_data, return -ENXIO);
+	return snd_cs46xx_free(chip);
+}
+
+/*
+ *  initialize chip
+ */
+
+static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
+{
+	unsigned int tmp;
+	int timeout;
+
+	/* 
+	 *  First, blast the clock control register to zero so that the PLL starts
+         *  out in a known state, and blast the master serial port control register
+         *  to zero so that the serial ports also start out in a known state.
+         */
+        snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
+        snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0);
+
+	/*
+	 *  If we are in AC97 mode, then we must set the part to a host controlled
+         *  AC-link.  Otherwise, we won't be able to bring up the link.
+         */        
+        snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03);	/* 1.03 codec */
+        /* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */
+
+        /*
+         *  Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+         *  spec) and then drive it high.  This is done for non AC97 modes since
+         *  there might be logic external to the CS461x that uses the ARST# line
+         *  for a reset.
+         */
+        snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0);
+        udelay(50);
+        snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN);
+
+	/*
+	 *  The first thing we do here is to enable sync generation.  As soon
+	 *  as we start receiving bit clock, we'll start producing the SYNC
+	 *  signal.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
+
+	/*
+	 *  Now wait for a short while to allow the AC97 part to start
+	 *  generating bit clock (so we don't try to start the PLL without an
+	 *  input clock).
+	 */
+	mdelay(1);
+
+	/*
+	 *  Set the serial port timing configuration, so that
+	 *  the clock control circuit gets its clock from the correct place.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97);
+
+	/*
+	 *  Write the selected clock control setup to the hardware.  Do not turn on
+	 *  SWCE yet (if requested), so that the devices clocked by the output of
+	 *  PLL are not clocked until the PLL is stable.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
+	snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a);
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8);
+
+	/*
+	 *  Power up the PLL.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP);
+
+	/*
+         *  Wait until the PLL has stabilized.
+	 */
+	mdelay(1);
+
+	/*
+	 *  Turn on clocking of the core so that we can setup the serial ports.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE);
+
+	/*
+	 *  Fill the serial port FIFOs with silence.
+	 */
+	snd_cs46xx_clear_serial_FIFOs(chip);
+
+	/*
+	 *  Set the serial port FIFO pointer to the first sample in the FIFO.
+	 */
+	/* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */
+
+	/*
+	 *  Write the serial port configuration to the part.  The master
+	 *  enable bit is not set until all other values have been written.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
+	snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
+	snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
+
+	/*
+	 * Wait for the codec ready signal from the AC97 codec.
+	 */
+	timeout = 150;
+	while (timeout-- > 0) {
+		/*
+		 *  Read the AC97 status register to see if we've seen a CODEC READY
+		 *  signal from the AC97 codec.
+		 */
+		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY)
+			goto ok1;
+		if (busywait)
+			mdelay(10);
+		else {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((HZ+99)/100);
+		}
+	}
+
+
+	snd_printk("create - never read codec ready from AC'97\n");
+	snd_printk("it is not probably bug, try to use CS4236 driver\n");
+	snd_cs46xx_free(chip);
+	return -EIO;
+ ok1:
+	/*
+	 *  Assert the vaid frame signal so that we can start sending commands
+	 *  to the AC97 codec.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+
+	/*
+	 *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
+	 *  the codec is pumping ADC data across the AC-link.
+	 */
+	timeout = 150;
+	while (timeout-- > 0) {
+		/*
+		 *  Read the input slot valid register and see if input slots 3 and
+		 *  4 are valid yet.
+		 */
+		if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+			goto ok2;
+		if (busywait)
+			mdelay(10);
+		else {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((HZ+99)/100);
+		}
+	}
+
+	snd_printk("create - never read ISV3 & ISV4 from AC'97\n");
+	snd_cs46xx_free(chip);
+	return -EIO;
+ ok2:
+
+	/*
+	 *  Now, assert valid frame and the slot 3 and 4 valid bits.  This will
+	 *  commense the transfer of digital audio data to the AC97 codec.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
+
+	/*
+	 *  Power down the DAC and ADC.  We will power them up (if) when we need
+	 *  them.
+	 */
+	/* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	/* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */
+	/* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */
+
+	/*
+         *  Reset the processor.
+         */
+	snd_cs46xx_reset(chip);
+
+	/*
+         *  Download the image to the processor.
+	 */
+	if (snd_cs46xx_download_image(chip) < 0) {
+		snd_printk("image download error\n");
+		snd_cs46xx_free(chip);
+		return -EIO;
+	}
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+	chip->play.ctl = tmp & 0xffff0000;
+	snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+	chip->capt.ctl = tmp & 0x0000ffff;
+	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+	snd_cs46xx_set_play_sample_rate(chip, 8000);
+	snd_cs46xx_set_capture_sample_rate(chip, 8000);
+
+	snd_cs46xx_proc_start(chip);
+
+	/*
+	 *  Enable interrupts on the part.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+
+	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
+
+	tmp = snd_cs46xx_peek(chip, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000001;
+	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
+	
+	/* set the attenuation to 0dB */ 
+	snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000);
+	snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
+
+	return 0;
+}
+
+
+/*
+ *	AMP control - null AMP
+ */
+ 
+static void amp_none(cs46xx_t *chip, int change)
+{	
+}
+
+/*
+ *	Crystal EAPD mode
+ */
+ 
+static void amp_voyetra(cs46xx_t *chip, int change)
+{
+	/* Manage the EAPD bit on the Crystal 4297 
+	   and the Analog AD1885 */
+	   
+	int old = chip->amplifier;
+	int oval, val;
+	
+	chip->amplifier += change;
+	oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN);
+	val = oval;
+	if (chip->amplifier && !old) {
+		/* Turn the EAPD amp on */
+		val |= 0x8000;
+	} else if (old && !chip->amplifier) {
+		/* Turn the EAPD amp off */
+		val &= ~0x8000;
+	}
+	if (val != oval) {
+		snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val);
+		if (chip->eapd_switch)
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &chip->eapd_switch->id);
+	}
+}
+
+
+/*
+ *	Game Theatre XP card - EGPIO[2] is used to enable the external amp.
+ */
+ 
+static void amp_hercules(cs46xx_t *chip, int change)
+{
+	int old = chip->amplifier;
+
+	chip->amplifier += change;
+	if (chip->amplifier && !old) {
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 
+				   EGPIODR_GPOE2);     /* enable EGPIO2 output */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 
+				   EGPIOPTR_GPPT2);   /* open-drain on output */
+	} else if (old && !chip->amplifier) {
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 0); /* disable */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 0); /* disable */
+	}
+}
+
+
+#if 0
+/*
+ *	Untested
+ */
+ 
+static void amp_voyetra_4294(cs46xx_t *chip, int change)
+{
+	chip->amplifier += change;
+
+	if (chip->amplifier) {
+		/* Switch the GPIO pins 7 and 8 to open drain */
+		snd_cs46xx_codec_write(chip, 0x4C,
+				       snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F);
+		snd_cs46xx_codec_write(chip, 0x4E,
+				       snd_cs46xx_codec_read(chip, 0x4E) | 0x0180);
+		/* Now wake the AMP (this might be backwards) */
+		snd_cs46xx_codec_write(chip, 0x54,
+				       snd_cs46xx_codec_read(chip, 0x54) & ~0x0180);
+	} else {
+		snd_cs46xx_codec_write(chip, 0x54,
+				       snd_cs46xx_codec_read(chip, 0x54) | 0x0180);
+	}
+}
+#endif
+
+
+/*
+ * piix4 pci ids
+ */
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif /* PCI_VENDOR_ID_INTEL */
+
+#ifndef PCI_DEVICE_ID_INTEL_82371AB_3
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#endif /* PCI_DEVICE_ID_INTEL_82371AB_3 */
+
+/*
+ *	Handle the CLKRUN on a thinkpad. We must disable CLKRUN support
+ *	whenever we need to beat on the chip.
+ *
+ *	The original idea and code for this hack comes from David Kaiser at
+ *	Linuxcare. Perhaps one day Crystal will document their chips well
+ *	enough to make them useful.
+ */
+ 
+static void clkrun_hack(cs46xx_t *chip, int change)
+{
+	u16 control;
+	int old;
+	
+	if (chip->acpi_dev == NULL)
+		return;
+
+	old = chip->amplifier;
+	chip->amplifier += change;
+	
+	/* Read ACPI port */	
+	control = inw(chip->acpi_port + 0x10);
+
+	/* Flip CLKRUN off while running */
+	if (! chip->amplifier && old)
+		outw(control | 0x2000, chip->acpi_port + 0x10);
+	else if (chip->amplifier && ! old)
+		outw(control & ~0x2000, chip->acpi_port + 0x10);
+}
+
+	
+/*
+ * detect intel piix4
+ */
+static void clkrun_init(cs46xx_t *chip)
+{
+	u8 pp;
+
+	chip->acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+	if (chip->acpi_dev == NULL)
+		return;		/* Not a thinkpad thats for sure */
+
+	/* Find the control port */		
+	pci_read_config_byte(chip->acpi_dev, 0x41, &pp);
+	chip->acpi_port = pp << 8;
+}
+
+
+/*
+ * Card subid table
+ */
+ 
+struct cs_card_type
+{
+	u16 vendor;
+	u16 id;
+	char *name;
+	void (*init)(cs46xx_t *);
+	void (*amp)(cs46xx_t *, int);
+	void (*active)(cs46xx_t *, int);
+};
+
+static struct cs_card_type __initdata cards[] = {
+	{0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL},
+	{0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL},
+	{0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL},
+	{0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	{0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	{0x1681, 0x0051, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	{0x1681, 0x0052, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	{0x1681, 0x0053, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	{0x1681, 0x0054, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	/* Not sure if the 570 needs the clkrun hack */
+	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", clkrun_init, NULL, clkrun_hack},
+	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", clkrun_init, NULL, clkrun_hack},
+	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL},
+	{0, 0, "Card without SSID set", NULL, NULL, NULL },
+	{0, 0, NULL, NULL, NULL, NULL}
+};
+
+
+/*
+ * APM support
+ */
+#ifdef CONFIG_PM
+void snd_cs46xx_suspend(cs46xx_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+	snd_pcm_suspend_all(chip->pcm);
+	// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
+	// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
+	snd_cs46xx_hw_stop(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+void snd_cs46xx_resume(cs46xx_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int amp_saved;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	pci_enable_device(chip->pci);
+	amp_saved = chip->amplifier;
+	chip->amplifier = 0;
+	chip->active_ctrl(chip, 1); /* force to on */
+
+	snd_cs46xx_chip_init(chip, 1);
+
+#if 0
+	snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, 
+			       chip->ac97_general_purpose);
+	snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL, 
+			       chip->ac97_powerdown);
+	mdelay(10);
+	snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN,
+			       chip->ac97_powerdown);
+	mdelay(5);
+#endif
+
+	snd_ac97_resume(chip->ac97);
+
+	if (amp_saved)
+		chip->amplifier_ctrl(chip, 1); /* try to turn on */
+	if (! amp_saved) {
+		chip->amplifier = 1;
+		chip->active_ctrl(chip, -1);
+	}
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->power_state_private_data, return -ENXIO);
+
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_cs46xx_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_cs46xx_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ */
+
+int __devinit snd_cs46xx_create(snd_card_t * card,
+		      struct pci_dev * pci,
+		      int external_amp, int thinkpad,
+		      cs46xx_t ** rchip)
+{
+	cs46xx_t *chip;
+	int err, idx;
+	snd_cs46xx_region_t *region;
+	struct cs_card_type *cp;
+	u16 ss_card, ss_vendor;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_cs46xx_dev_free,
+	};
+	
+	*rchip = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = snd_magic_kcalloc(cs46xx_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->play.hw_size = PAGE_SIZE;
+	chip->capt.hw_size = PAGE_SIZE;
+	chip->irq = -1;
+	chip->ba0_addr = pci_resource_start(pci, 0);
+	chip->ba1_addr = pci_resource_start(pci, 1);
+	if (chip->ba0_addr == 0 || chip->ba0_addr == ~0 ||
+	    chip->ba1_addr == 0 || chip->ba1_addr == ~0) {
+	    	snd_cs46xx_free(chip);
+	    	snd_printk("wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n", chip->ba0_addr, chip->ba1_addr);
+	    	return -ENOMEM;
+	}
+
+	region = &chip->region.name.ba0;
+	strcpy(region->name, "CS46xx_BA0");
+	region->base = chip->ba0_addr;
+	region->size = CS46XX_BA0_SIZE;
+
+	region = &chip->region.name.data0;
+	strcpy(region->name, "CS46xx_BA1_data0");
+	region->base = chip->ba1_addr + BA1_SP_DMEM0;
+	region->size = CS46XX_BA1_DATA0_SIZE;
+
+	region = &chip->region.name.data1;
+	strcpy(region->name, "CS46xx_BA1_data1");
+	region->base = chip->ba1_addr + BA1_SP_DMEM1;
+	region->size = CS46XX_BA1_DATA1_SIZE;
+
+	region = &chip->region.name.pmem;
+	strcpy(region->name, "CS46xx_BA1_pmem");
+	region->base = chip->ba1_addr + BA1_SP_PMEM;
+	region->size = CS46XX_BA1_PRG_SIZE;
+
+	region = &chip->region.name.reg;
+	strcpy(region->name, "CS46xx_BA1_reg");
+	region->base = chip->ba1_addr + BA1_SP_REG;
+	region->size = CS46XX_BA1_REG_SIZE;
+
+	/* set up amp and clkrun hack */
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card);
+
+	for (cp = &cards[0]; cp->name; cp++) {
+		if (cp->vendor == ss_vendor && cp->id == ss_card) {
+			snd_printd("hack for %s enabled\n", cp->name);
+			if (cp->init)
+				cp->init(chip);
+			chip->amplifier_ctrl = cp->amp;
+			chip->active_ctrl = cp->active;
+			break;
+		}
+	}
+
+	if (external_amp) {
+		snd_printk("Crystal EAPD support forced on.\n");
+		chip->amplifier_ctrl = amp_voyetra;
+	}
+
+	if (thinkpad) {
+		snd_printk("Activating CLKRUN hack for Thinkpad.\n");
+		chip->active_ctrl = clkrun_hack;
+		clkrun_init(chip);
+	}
+	
+	if (chip->amplifier_ctrl == NULL)
+		chip->amplifier_ctrl = amp_none;
+	if (chip->active_ctrl == NULL)
+		chip->active_ctrl = amp_none;
+
+	chip->active_ctrl(chip, 1);
+
+	pci_set_master(pci);
+
+	for (idx = 0; idx < 5; idx++) {
+		region = &chip->region.idx[idx];
+		if ((region->resource = request_mem_region(region->base, region->size, region->name)) == NULL) {
+			snd_cs46xx_free(chip);
+			snd_printk("unable to request memory region 0x%lx-0x%lx\n", region->base, region->base + region->size - 1);
+			return -EBUSY;
+		}
+		region->remap_addr = (unsigned long) ioremap_nocache(region->base, region->size);
+		if (region->remap_addr == 0) {
+			snd_cs46xx_free(chip);
+			snd_printk("%s ioremap problem\n", region->name);
+			return -ENOMEM;
+		}
+	}
+	if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS46XX", (void *) chip)) {
+		snd_cs46xx_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+
+	err = snd_cs46xx_chip_init(chip, 0);
+	if (err < 0) {
+		snd_cs46xx_free(chip);
+		return err;
+	}
+
+	snd_cs46xx_proc_init(card, chip);
+
+#ifdef CONFIG_PM
+	card->set_power_state = snd_cs46xx_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_cs46xx_free(chip);
+		return err;
+	}
+
+	chip->active_ctrl(chip, -1);
+
+	*rchip = chip;
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/Makefile linux-2.4.19-pre5-mjc/sound/pci/emu10k1/Makefile
--- linux/sound/pci/emu10k1/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,29 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _emu10k1.o
+
+list-multi   := snd-emu10k1.o snd-emu10k1-synth.o
+
+export-objs  := emu10k1_main.o
+
+snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
+		    irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
+		    emuproc.o emumixer.o emufx.o
+snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o snd-emu10k1-synth.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-emu10k1.o: $(snd-emu10k1-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-objs)
+
+snd-emu10k1-synth.o: $(snd-emu10k1-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-synth-objs)
diff -Nru linux/sound/pci/emu10k1/emu10k1.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1.c
--- linux/sound/pci/emu10k1/emu10k1.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,250 @@
+/*
+ *  The driver for the EMU10K1 (SB Live!) based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("EMU10K1");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
+	       "{Creative Labs,SB Audigy}}");
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#define ENABLE_SYNTH
+#include <sound/emu10k1_synth.h>
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+static int snd_max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
+static int snd_max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
+static int snd_enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for the EMU10K1 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for the EMU10K1 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable the EMU10K1 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_extin, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_extin, "Available external inputs for FX8010. Zero=default.");
+MODULE_PARM_SYNTAX(snd_extin, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16");
+MODULE_PARM(snd_extout, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_extout, "Available external outputs for FX8010. Zero=default.");
+MODULE_PARM_SYNTAX(snd_extout, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16");
+MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_seq_ports, "Allocated sequencer ports for internal synthesizer.");
+MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED "allows:{{0,32}}");
+MODULE_PARM(snd_max_synth_voices, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_max_synth_voices, "Maximum number of voices for WaveTable.");
+MODULE_PARM_SYNTAX(snd_max_synth_voices, SNDRV_ENABLED);
+MODULE_PARM(snd_max_buffer_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_max_buffer_size, "Maximum sample buffer size in MB.");
+MODULE_PARM_SYNTAX(snd_max_buffer_size, SNDRV_ENABLED);
+MODULE_PARM(snd_enable_ir, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable_ir, "Enable IR.");
+MODULE_PARM_SYNTAX(snd_enable_ir, SNDRV_ENABLE_DESC);
+
+static struct pci_device_id snd_emu10k1_ids[] __devinitdata = {
+	{ 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* EMU10K1 */
+	{ 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },	/* Audigy */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
+
+static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
+					    const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	emu10k1_t *emu;
+#ifdef ENABLE_SYNTH
+	snd_seq_device_t *wave = NULL;
+#endif
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+        	return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if (snd_max_buffer_size[dev] < 32)
+		snd_max_buffer_size[dev] = 32;
+	else if (snd_max_buffer_size[dev] > 1024)
+		snd_max_buffer_size[dev] = 1024;
+	if ((err = snd_emu10k1_create(card, pci, snd_extin[dev], snd_extout[dev],
+				      (long)snd_max_buffer_size[dev] * 1024 * 1024,
+				      snd_enable_ir[dev],
+				      &emu)) < 0) {
+		snd_card_free(card);
+		return err;
+	}		
+	if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}		
+	if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}		
+	if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}		
+	if ((err = snd_emu10k1_fx8010_pcm(emu, 3, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}		
+	if (!emu->APS) {	/* APS board has not an AC97 mixer */
+		if ((err = snd_emu10k1_mixer(emu)) < 0) {
+			snd_card_free(card);
+			return err;
+		}		
+	}
+	if (emu->audigy) {
+		if ((err = snd_emu10k1_audigy_midi(emu)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	} else {
+		if ((err = snd_emu10k1_midi(emu)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef ENABLE_SYNTH
+	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+			       sizeof(snd_emu10k1_synth_arg_t), &wave) < 0 ||
+	    wave == NULL) {
+		snd_printk("can't initialize Emu10k1 wavetable synth\n");
+	} else {
+		snd_emu10k1_synth_arg_t *arg;
+		arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+		strcpy(wave->name, "Emu-10k1 Synth");
+		arg->hwptr = emu;
+		arg->index = 1;
+		arg->seq_ports = snd_seq_ports[dev];
+		arg->max_voices = snd_max_synth_voices[dev];
+	}
+#endif
+ 
+	if (emu->audigy) {
+		strcpy(card->driver, "Audigy");
+		strcpy(card->shortname, "Sound Blaster Audigy");
+	} else if (emu->APS) {
+		strcpy(card->driver, "E-mu APS");
+		strcpy(card->shortname, "E-mu APS");
+	} else {
+		strcpy(card->driver, "EMU10K1");
+		strcpy(card->shortname, "Sound Blaster Live!");
+	}
+	sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, emu->port, emu->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "EMU10K1/Audigy",
+	id_table: snd_emu10k1_ids,
+	probe: snd_card_emu10k1_probe,
+	remove: __devexit_p(snd_card_emu10k1_remove),
+};
+
+static int __init alsa_card_emu10k1_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "EMU10K1/Audigy soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_emu10k1_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_emu10k1_init)
+module_exit(alsa_card_emu10k1_exit)
+
+#ifndef MODULE
+
+/* format is: snd-emu10k1=snd_enable,snd_index,snd_id,
+			  snd_seq_ports,snd_max_synth_voices */
+
+static int __init alsa_card_emu10k1_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_seq_ports[nr_dev]) == 2 &&
+	       get_option(&str,&snd_max_synth_voices[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-emu10k1=", alsa_card_emu10k1_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/emu10k1/emu10k1_callback.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_callback.c
--- linux/sound/pci/emu10k1/emu10k1_callback.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_callback.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,541 @@
+/*
+ *  synth callback routines for Emu10k1
+ *
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include "emu10k1_synth_local.h"
+#include <sound/asoundef.h>
+
+/* voice status */
+enum {
+	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
+};
+
+/* Keeps track of what we are finding */
+typedef struct best_voice {
+	unsigned int time;
+	int voice;
+} best_voice_t;
+
+/*
+ * prototypes
+ */
+static void lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only);
+static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port);
+static int start_voice(snd_emux_voice_t *vp);
+static void trigger_voice(snd_emux_voice_t *vp);
+static void release_voice(snd_emux_voice_t *vp);
+static void update_voice(snd_emux_voice_t *vp, int update);
+static void terminate_voice(snd_emux_voice_t *vp);
+static void free_voice(snd_emux_voice_t *vp);
+
+static void set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp);
+static void set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp);
+static void set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp);
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+
+/*
+ * set up operators
+ */
+static snd_emux_operators_t emu10k1_ops = {
+	owner:		THIS_MODULE,
+	get_voice:	get_voice,
+	prepare:	start_voice,
+	trigger:	trigger_voice,
+	release:	release_voice,
+	update:		update_voice,
+	terminate:	terminate_voice,
+	free_voice:	free_voice,
+	sample_new:	snd_emu10k1_sample_new,
+	sample_free:	snd_emu10k1_sample_free,
+};
+
+void
+snd_emu10k1_ops_setup(snd_emux_t *emu)
+{
+	emu->ops = emu10k1_ops;
+}
+
+
+/*
+ * get more voice for pcm
+ *
+ * terminate most inactive voice and give it as a pcm voice.
+ */
+int
+snd_emu10k1_synth_get_voice(emu10k1_t *hw)
+{
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	best_voice_t best[V_END];
+	unsigned long flags;
+	int i;
+
+	emu = snd_magic_cast(snd_emux_t, hw->synth, return -EINVAL);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	lookup_voices(emu, hw, best, 1); /* no OFF voices */
+	for (i = 0; i < V_END; i++) {
+		if (best[i].voice >= 0) {
+			int ch;
+			vp = &emu->voices[best[i].voice];
+			if ((ch = vp->ch) < 0) {
+				//printk("synth_get_voice: ch < 0 (%d) ??", i);
+				continue;
+			}
+			vp->emu->num_voices--;
+			vp->ch = -1;
+			vp->state = SNDRV_EMUX_ST_OFF;
+			spin_unlock_irqrestore(&emu->voice_lock, flags);
+			return ch;
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+	/* not found */
+	return -ENOMEM;
+}
+
+
+/*
+ * turn off the voice (not terminated)
+ */
+static void
+release_voice(snd_emux_voice_t *vp)
+{
+	int dcysusv;
+	emu10k1_t *hw;
+	
+	hw = snd_magic_cast(emu10k1_t, vp->hw, return);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+	snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
+}
+
+
+/*
+ * terminate the voice
+ */
+static void
+terminate_voice(snd_emux_voice_t *vp)
+{
+	emu10k1_t *hw;
+	
+	snd_assert(vp, return);
+	hw = snd_magic_cast(emu10k1_t, vp->hw, return);
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+	if (vp->block) {
+		emu10k1_memblk_t *emem;
+		emem = (emu10k1_memblk_t *)vp->block;
+		if (emem->map_locked > 0)
+			emem->map_locked--;
+	}
+}
+
+/*
+ * release the voice to system
+ */
+static void
+free_voice(snd_emux_voice_t *vp)
+{
+	emu10k1_t *hw;
+	
+	hw = snd_magic_cast(emu10k1_t, vp->hw, return);
+	if (vp->ch >= 0) {
+		snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
+		snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+		// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
+		snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
+		snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
+		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
+		vp->emu->num_voices--;
+		vp->ch = -1;
+	}
+}
+
+
+/*
+ * update registers
+ */
+static void
+update_voice(snd_emux_voice_t *vp, int update)
+{
+	emu10k1_t *hw;
+	
+	hw = snd_magic_cast(emu10k1_t, vp->hw, return);
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+	if (update & SNDRV_EMUX_UPDATE_PAN) {
+		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
+		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
+	}
+	if (update & SNDRV_EMUX_UPDATE_FMMOD)
+		set_fmmod(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+		set_fm2frq2(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_Q)
+		set_filterQ(hw, vp);
+}
+
+
+/*
+ * look up voice table - get the best voice in order of preference
+ */
+/* spinlock held! */
+static void
+lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only)
+{
+	snd_emux_voice_t *vp;
+	best_voice_t *bp;
+	int  i;
+
+	for (i = 0; i < V_END; i++) {
+		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;
+		best[i].voice = -1;
+	}
+
+	/*
+	 * Go through them all and get a best one to use.
+	 * NOTE: could also look at volume and pick the quietest one.
+	 */
+	for (i = 0; i < emu->max_voices; i++) {
+		int state, val;
+
+		vp = &emu->voices[i];
+		state = vp->state;
+		if (state == SNDRV_EMUX_ST_OFF) {
+			if (vp->ch < 0) {
+				if (active_only)
+					continue;
+				bp = best + V_FREE;
+			} else
+				bp = best + V_OFF;
+		}
+		else if (state == SNDRV_EMUX_ST_RELEASED ||
+			 state == SNDRV_EMUX_ST_PENDING) {
+			bp = best + V_RELEASED;
+#if 0
+			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
+			if (! val)
+				bp = best + V_OFF;
+#endif
+		}
+		else if (state == SNDRV_EMUX_ST_STANDBY)
+			continue;
+		else if (state & SNDRV_EMUX_ST_ON)
+			bp = best + V_PLAYING;
+		else
+			continue;
+
+		/* check if sample is finished playing (non-looping only) */
+		if (bp != best + V_OFF && bp != best + V_FREE &&
+		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+			if (val >= vp->reg.loopstart)
+				bp = best + V_OFF;
+		}
+
+		if (vp->time < bp->time) {
+			bp->time = vp->time;
+			bp->voice = i;
+		}
+	}
+}
+
+/*
+ * get an empty voice
+ *
+ * emu->voice_lock is already held.
+ */
+static snd_emux_voice_t *
+get_voice(snd_emux_t *emu, snd_emux_port_t *port)
+{
+	emu10k1_t *hw;
+	snd_emux_voice_t *vp;
+	best_voice_t best[V_END];
+	int i;
+
+	hw = snd_magic_cast(emu10k1_t, emu->hw, return NULL);
+
+	lookup_voices(emu, hw, best, 0);
+	for (i = 0; i < V_END; i++) {
+		if (best[i].voice >= 0) {
+			vp = &emu->voices[best[i].voice];
+			if (vp->ch < 0) {
+				/* allocate a voice */
+				emu10k1_voice_t *hwvoice;
+				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 0, &hwvoice) < 0 || hwvoice == NULL)
+					continue;
+				vp->ch = hwvoice->number;
+				emu->num_voices++;
+			}
+			return vp;
+		}
+	}
+
+	/* not found */
+	return NULL;
+}
+
+/*
+ * prepare envelopes and LFOs
+ */
+static int
+start_voice(snd_emux_voice_t *vp)
+{
+	unsigned int temp;
+	int ch;
+	unsigned int addr, mapped_offset;
+	snd_midi_channel_t *chan;
+	emu10k1_t *hw;
+	emu10k1_memblk_t *emem;
+	
+	hw = snd_magic_cast(emu10k1_t, vp->hw, return -EINVAL);
+	ch = vp->ch;
+	snd_assert(ch >= 0, return -EINVAL);
+	chan = vp->chan;
+
+	emem = (emu10k1_memblk_t *)vp->block;
+	if (emem == NULL)
+		return -EINVAL;
+	emem->map_locked++;
+	if (snd_emu10k1_memblk_map(hw, emem) < 0) {
+		// printk("emu: cannot map!\n");
+		return -ENOMEM;
+	}
+	mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+	vp->reg.start += mapped_offset;
+	vp->reg.end += mapped_offset;
+	vp->reg.loopstart += mapped_offset;
+	vp->reg.loopend += mapped_offset;
+
+	/* set channel routing */
+	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
+	if (hw->audigy) {
+		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 
+			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
+		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
+	} else {
+		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 
+			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
+		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
+	}
+
+	/* channel to be silent and idle */
+	snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0080);
+	snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
+	snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
+	snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
+	snd_emu10k1_ptr_write(hw, CPF, ch, 0);
+
+	/* set pitch offset */
+	snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+
+	/* set envelope parameters */
+	snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
+	snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
+	snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
+	snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
+	snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
+	/* decay/sustain parameter for volume envelope is used
+	   for triggerg the voice */
+
+	/* cutoff and volume */
+	temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
+	snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
+
+	/* modulation envelope heights */
+	snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
+
+	/* lfo1/2 delay */
+	snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
+	snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
+
+	/* lfo1 pitch & cutoff shift */
+	set_fmmod(hw, vp);
+	/* lfo1 volume & freq */
+	snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+	/* lfo2 pitch & freq */
+	set_fm2frq2(hw, vp);
+
+	/* reverb and loop start (reverb 8bit, MSB) */
+	temp = vp->reg.parm.reverb;
+	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	addr = vp->reg.loopstart;
+	snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
+
+	/* chorus & loop end (chorus 8bit, MSB) */
+	addr = vp->reg.loopend;
+	temp = vp->reg.parm.chorus;
+	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp <<24) | addr;
+	snd_emu10k1_ptr_write(hw, DSL, ch, temp);
+
+	/* clear filter delay memory */
+	snd_emu10k1_ptr_write(hw, Z1, ch, 0);
+	snd_emu10k1_ptr_write(hw, Z2, ch, 0);
+
+	/* invalidate maps */
+	temp = (hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK;
+	snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+	snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+#if 0
+	/* cache */
+	{
+		unsigned int val, sample;
+		val = 32;
+		if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+			sample = 0x80808080;
+		else {
+			sample = 0;
+			val *= 2;
+		}
+
+		/* cache */
+		snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
+		snd_emu10k1_ptr_write(hw, CDE, ch, sample);
+		snd_emu10k1_ptr_write(hw, CDF, ch, sample);
+
+		/* invalidate maps */
+		temp = ((unsigned int)hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK;
+		snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+		snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+		
+		/* fill cache */
+		val -= 4;
+		val <<= 25;
+		val |= 0x1c << 16;
+		snd_emu10k1_ptr_write(hw, CCR, ch, val);
+	}
+#endif
+
+	/* Q & current address (Q 4bit value, MSB) */
+	addr = vp->reg.start;
+	temp = vp->reg.parm.filterQ;
+	temp = (temp<<28) | addr;
+	if (vp->apitch < 0xe400)
+		temp |= CCCA_INTERPROM_0;
+	else {
+		unsigned int shift = (vp->apitch - 0xe000) >> 10;
+		temp |= shift << 25;
+	}
+	if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+		temp |= CCCA_8BITSELECT;
+	snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
+
+	/* reset volume */
+	temp = (unsigned int)vp->vtarget << 16;
+	snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
+	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
+	return 0;
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(snd_emux_voice_t *vp)
+{
+	unsigned int temp, ptarget;
+	emu10k1_t *hw;
+	emu10k1_memblk_t *emem;
+	
+	hw = snd_magic_cast(emu10k1_t, vp->hw, return);
+
+	emem = (emu10k1_memblk_t *)vp->block;
+	if (! emem || emem->mapped_page < 0)
+		return; /* not mapped */
+
+#if 0
+	ptarget = (unsigned int)vp->ptarget << 16;
+#else
+	ptarget = IP_TO_CP(vp->apitch);
+#endif
+	/* set pitch target and pan (volume) */
+	temp = ptarget | (vp->apan << 8) | vp->aaux;
+	snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
+
+	/* pitch target */
+	snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
+
+	/* trigger voice */
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
+}
+
+#define MOD_SENSE 18
+
+/* set lfo1 modulation height and cutoff */
+static void
+set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned short fmmod;
+	short pitch;
+	unsigned char cutoff;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fmmod>>8);
+	cutoff = (vp->reg.parm.fmmod & 0xff);
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fmmod = ((unsigned char)pitch<<8) | cutoff;
+	snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned short fm2frq2;
+	short pitch;
+	unsigned char freq;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fm2frq2>>8);
+	freq = vp->reg.parm.fm2frq2 & 0xff;
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fm2frq2 = ((unsigned char)pitch<<8) | freq;
+	snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned int val;
+	val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
+	val |= (vp->reg.parm.filterQ << 28);
+	snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+}
diff -Nru linux/sound/pci/emu10k1/emu10k1_main.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_main.c
--- linux/sound/pci/emu10k1/emu10k1_main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_main.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,677 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#if 0
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Creative Labs, Inc.");
+MODULE_DESCRIPTION("Routines for control of EMU10K1 chips");
+MODULE_LICENSE("GPL");
+#endif
+
+/*************************************************************************
+ * EMU10K1 init / done
+ *************************************************************************/
+
+void snd_emu10k1_voice_init(emu10k1_t * emu, int ch)
+{
+	snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+	snd_emu10k1_ptr_write(emu, IP, ch, 0);
+	snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+	snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+	snd_emu10k1_ptr_write(emu, CCR, ch, 0);
+
+	snd_emu10k1_ptr_write(emu, PSST, ch, 0);
+	snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
+	snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
+	snd_emu10k1_ptr_write(emu, Z1, ch, 0);
+	snd_emu10k1_ptr_write(emu, Z2, ch, 0);
+	snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
+
+	snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
+	snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
+	snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
+	snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
+	snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24);	/* 1 Hz */
+	snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24);	/* 1 Hz */
+	snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
+
+	/*** these are last so OFF prevents writing ***/
+	snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
+	snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
+	snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
+	snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
+	snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+
+	/* Audigy extra stuffs */
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
+		snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+	}
+}
+
+static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir)
+{
+	int ch, idx, err;
+	unsigned int silent_page;
+
+	emu->fx8010.itram_size = (16 * 1024)/2;
+	emu->fx8010.etram_size = 0;
+
+	/* disable audio and lock cache */
+	outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+
+	/* reset recording buffers */
+	snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+
+	/* disable channel interrupt */
+	outl(0, emu->port + INTE);
+	snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+	if (emu->audigy){
+		snd_emu10k1_ptr_write(emu, 0x5e, 0, 0xf00); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x5f, 0, 0x3); /* ?? */
+	}
+
+	/* init envelope engine */
+	for (ch = 0; ch < NUM_G; ch++) {
+		emu->voices[ch].emu = emu;
+		emu->voices[ch].number = ch;
+		snd_emu10k1_voice_init(emu, ch);
+	}
+
+	/*
+	 *  Init to 0x02109204 :
+	 *  Clock accuracy    = 0     (1000ppm)
+	 *  Sample Rate       = 2     (48kHz)
+	 *  Audio Channel     = 1     (Left of 2)
+	 *  Source Number     = 0     (Unspecified)
+	 *  Generation Status = 1     (Original for Cat Code 12)
+	 *  Cat Code          = 12    (Digital Signal Mixer)
+	 *  Mode              = 0     (Mode 0)
+	 *  Emphasis          = 0     (None)
+	 *  CP                = 1     (Copyright unasserted)
+	 *  AN                = 0     (Audio data)
+	 *  P                 = 0     (Consumer)
+	 */
+	snd_emu10k1_ptr_write(emu, SPCS0, 0,
+			emu->spdif_bits[0] =
+			SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			SPCS_GENERATIONSTATUS | 0x00001200 |
+			0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_emu10k1_ptr_write(emu, SPCS1, 0,
+			emu->spdif_bits[1] =
+			SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			SPCS_GENERATIONSTATUS | 0x00001200 |
+			0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_emu10k1_ptr_write(emu, SPCS2, 0,
+			emu->spdif_bits[2] =
+			SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			SPCS_GENERATIONSTATUS | 0x00001200 |
+			0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	/*
+	 *  Clear page with silence & setup all pointers to this page
+	 */
+	memset(emu->silent_page, 0, PAGE_SIZE);
+	silent_page = emu->silent_page_dmaaddr << 1;
+	for (idx = 0; idx < MAXPAGES; idx++)
+		emu->ptb_pages[idx] = silent_page | idx;
+	snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);	/* taken from original driver */
+	snd_emu10k1_ptr_write(emu, TCBS, 0, 4);	/* taken from original driver */
+
+	silent_page = (emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK;
+	for (ch = 0; ch < NUM_G; ch++) {
+		snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
+		snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
+	}
+
+	/*
+	 *  Hokay, setup HCFG
+	 *   Mute Disable Audio = 0
+	 *   Lock Tank Memory = 1
+	 *   Lock Sound Memory = 0
+	 *   Auto Mute = 1
+	 */
+	if (emu->audigy)
+		outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+	else if (emu->model == 0x20 ||
+	    emu->model == 0xc400 ||
+	    (emu->model == 0x21 && emu->revision < 6))
+		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
+	else
+		// With on-chip joystick
+		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+
+	if (enable_ir) {	/* enable IR for SB Live */
+		unsigned int reg = inl(emu->port + HCFG);
+		outl(reg | HCFG_GPOUT2, emu->port + HCFG);
+		udelay(500);
+		outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
+		udelay(100);
+		outl(reg, emu->port + HCFG);
+	}
+	
+	if (!emu->APS) {	/* enable analog output */
+		unsigned int reg = inl(emu->port + HCFG);
+		outl(reg | HCFG_GPOUT0, emu->port + HCFG);
+	}
+
+	/*
+	 *  Initialize the effect engine
+	 */
+	if ((err = snd_emu10k1_init_efx(emu)) < 0)
+		return err;
+
+	/*
+	 *  Enable the audio bit
+	 */
+	outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
+
+	/* Enable analog/digital outs on audigy */
+	if (emu->audigy)
+		outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+
+#if 0
+	{
+	unsigned int tmp;
+	/* FIXME: the following routine disables LiveDrive-II !! */
+	// TOSLink detection
+	emu->tos_link = 0;
+	tmp = inl(emu->port + HCFG);
+	if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
+		outl(tmp|0x800, emu->port + HCFG);
+		udelay(50);
+		if (tmp != (inl(emu->port + HCFG) & ~0x800)) {
+			emu->tos_link = 1;
+			outl(tmp, emu->port + HCFG);
+		}
+	}
+	}
+#endif
+
+	snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+
+	emu->reserved_page = (emu10k1_memblk_t *)snd_emu10k1_synth_alloc(emu, 4096);
+	if (emu->reserved_page)
+		emu->reserved_page->map_locked = 1;
+	
+	return 0;
+}
+
+static int snd_emu10k1_done(emu10k1_t * emu)
+{
+	int ch;
+
+	outl(0, emu->port + INTE);
+
+	/*
+	 *  Shutdown the chip
+	 */
+	for (ch = 0; ch < NUM_G; ch++)
+		snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+	for (ch = 0; ch < NUM_G; ch++) {
+		snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
+		snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
+		snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+		snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+	}
+
+	/* reset recording buffers */
+	snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
+	snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+	snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, 0x8000);
+
+	/* disable channel interrupt */
+	snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+	/* remove reserved page */
+	if (emu->reserved_page != NULL) {
+		snd_emu10k1_synth_free(emu, (snd_util_memblk_t *)emu->reserved_page);
+		emu->reserved_page = NULL;
+	}
+
+	/* disable audio and lock cache */
+	outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+	snd_emu10k1_ptr_write(emu, PTB, 0, 0);
+
+	snd_emu10k1_free_efx(emu);
+
+	return 0;
+}
+
+/*************************************************************************
+ * ECARD functional implementation
+ *************************************************************************/
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT		(1L << 12)
+#define HANDN_BIT		(1L << 11)
+#define PULSEN_BIT		(1L << 10)
+
+#define EC_GDI1			(1 << 13)
+#define EC_GDI0			(1 << 14)
+
+#define EC_NUM_CONTROL_BITS	20
+
+#define EC_AC3_DATA_SELN	0x0001L
+#define EC_EE_DATA_SEL		0x0002L
+#define EC_EE_CNTRL_SELN	0x0004L
+#define EC_EECLK		0x0008L
+#define EC_EECS			0x0010L
+#define EC_EESDO		0x0020L
+#define EC_TRIM_CSN		0x0040L
+#define EC_TRIM_SCLK		0x0080L
+#define EC_TRIM_SDATA		0x0100L
+#define EC_TRIM_MUTEN		0x0200L
+#define EC_ADCCAL		0x0400L
+#define EC_ADCRSTN		0x0800L
+#define EC_DACCAL		0x1000L
+#define EC_DACMUTEN		0x2000L
+#define EC_LEDN			0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT	15
+#define EC_SPDIF1_SEL_SHIFT	17
+#define EC_SPDIF0_SEL_MASK	(0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK	(0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x)	(((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x)	(((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01	/* Self-explanatory.  This should
+					 * be incremented any time the EEPROM's
+					 * format is changed.  */
+
+#define EC_EEPROM_SIZE		0x40	/* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR	0x20	/* Address of the current prom version */
+#define EC_BOARDREV0_ADDR	0x21	/* LSW of board rev */
+#define EC_BOARDREV1_ADDR	0x22	/* MSW of board rev */
+
+#define EC_LAST_PROMFILE_ADDR	0x2f
+
+#define EC_SERIALNUM_ADDR	0x30	/* First word of serial number.  The 
+					 * can be up to 30 characters in length
+					 * and is stored as a NULL-terminated
+					 * ASCII string.  Any unused bytes must be
+					 * filled with zeros */
+#define EC_CHECKSUM_ADDR	0x3f	/* Location at which checksum is stored */
+
+
+/* Most of this stuff is pretty self-evident.  According to the hardware 
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC 
+ * offset problem.  Weird.
+ */
+#define EC_RAW_RUN_MODE		(EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
+				 EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN	0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL	0x0
+#define EC_DEFAULT_SPDIF1_SEL	0x4
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch.  The Ecard uses a
+ *  control latch will is loaded bit-serially by toggling the Modem control
+ *  lines from function 2 on the E8010.  This function hides these details
+ *  and presents the illusion that we are actually writing to a distinct
+ *  register.
+ */
+
+static void snd_emu10k1_ecard_write(emu10k1_t * emu, unsigned int value)
+{
+	unsigned short count;
+	unsigned int data;
+	unsigned long hc_port;
+	unsigned int hc_value;
+
+	hc_port = emu->port + HCFG;
+	hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT);
+	outl(hc_value, hc_port);
+
+	for (count = 0; count < EC_NUM_CONTROL_BITS; count++) {
+
+		/* Set up the value */
+		data = ((value & 0x1) ? PULSEN_BIT : 0);
+		value >>= 1;
+
+		outl(hc_value | data, hc_port);
+
+		/* Clock the shift register */
+		outl(hc_value | data | HANDN_BIT, hc_port);
+		outl(hc_value | data, hc_port);
+	}
+
+	/* Latch the bits */
+	outl(hc_value | HOOKN_BIT, hc_port);
+	outl(hc_value, hc_port);
+}
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller.  The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel.  The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void snd_emu10k1_ecard_setadcgain(emu10k1_t * emu,
+					 unsigned short gain)
+{
+	unsigned int bit;
+
+	/* Enable writing to the TRIM registers */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+	/* Do it again to insure that we meet hold time requirements */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+	for (bit = (1 << 15); bit; bit >>= 1) {
+		unsigned int value;
+		
+		value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
+
+		if (gain & bit)
+			value |= EC_TRIM_SDATA;
+
+		/* Clock the bit */
+		snd_emu10k1_ecard_write(emu, value);
+		snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK);
+		snd_emu10k1_ecard_write(emu, value);
+	}
+
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+}
+
+static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu)
+{
+	unsigned int hc_value;
+
+	/* Set up the initial settings */
+	emu->ecard_ctrl = EC_RAW_RUN_MODE |
+			  EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
+			  EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
+
+	/* Step 0: Set the codec type in the hardware control register 
+	 * and enable audio output */
+	hc_value = inl(emu->port + HCFG);
+	outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
+	inl(emu->port + HCFG);
+
+	/* Step 1: Turn off the led and deassert TRIM_CS */
+	snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 2: Calibrate the ADC and DAC */
+	snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 3: Wait for awhile;   XXX We can't get away with this
+	 * under a real operating system; we'll need to block and wait that
+	 * way. */
+	snd_emu10k1_wait(emu, 48000);
+
+	/* Step 4: Switch off the DAC and ADC calibration.  Note
+	 * That ADC_CAL is actually an inverted signal, so we assert
+	 * it here to stop calibration.  */
+	snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 4: Switch into run mode */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+
+	/* Step 5: Set the analog input gain */
+	snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN);
+
+	return 0;
+}
+
+/*
+ *  Create the EMU10K1 instance
+ */
+
+static int snd_emu10k1_free(emu10k1_t *emu)
+{
+	snd_emu10k1_proc_done(emu);
+	if (emu->res_port != NULL) {	/* avoid access to already used hardware */
+	       	snd_emu10k1_fx8010_tram_setup(emu, 0);
+		snd_emu10k1_done(emu);
+       	}
+	if (emu->memhdr)
+		snd_util_memhdr_free(emu->memhdr);
+	if (emu->silent_page)
+		snd_free_pci_pages(emu->pci, EMUPAGESIZE, emu->silent_page, emu->silent_page_dmaaddr);
+	if (emu->ptb_pages)
+		snd_free_pci_pages(emu->pci, 32 * 1024, (void *)emu->ptb_pages, emu->ptb_pages_dmaaddr);
+	if (emu->page_ptr_table)
+		vfree(emu->page_ptr_table);
+	if (emu->page_addr_table)
+		vfree(emu->page_addr_table);
+	if (emu->res_port) {
+		release_resource(emu->res_port);
+		kfree_nocheck(emu->res_port);
+	}
+	if (emu->irq >= 0)
+		free_irq(emu->irq, (void *)emu);
+	snd_magic_kfree(emu);
+	return 0;
+}
+
+static int snd_emu10k1_dev_free(snd_device_t *device)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, device->device_data, return -ENXIO);
+	return snd_emu10k1_free(emu);
+}
+
+int __devinit snd_emu10k1_create(snd_card_t * card,
+		       struct pci_dev * pci,
+		       unsigned short extin_mask,
+		       unsigned short extout_mask,
+		       long max_cache_bytes,
+		       int enable_ir,
+		       emu10k1_t ** remu)
+{
+	emu10k1_t *emu;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_emu10k1_dev_free,
+	};
+	
+	*remu = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 31 bits */
+	if (!pci_dma_supported(pci, 0x7fffffff)) {
+		snd_printk("architecture does not support 31bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	if (pci->driver_data)
+		pci_set_dma_mask(pci, 0xffffffff); /* audigy */
+	else
+		pci_set_dma_mask(pci, 0x7fffffff);
+
+	emu = snd_magic_kcalloc(emu10k1_t, 0, GFP_KERNEL);
+	if (emu == NULL)
+		return -ENOMEM;
+	emu->card = card;
+	spin_lock_init(&emu->reg_lock);
+	spin_lock_init(&emu->emu_lock);
+	spin_lock_init(&emu->voice_lock);
+	spin_lock_init(&emu->synth_lock);
+	spin_lock_init(&emu->memblk_lock);
+	init_MUTEX(&emu->ptb_lock);
+	init_MUTEX(&emu->fx8010.lock);
+	INIT_LIST_HEAD(&emu->mapped_link_head);
+	INIT_LIST_HEAD(&emu->mapped_order_link_head);
+	emu->pci = pci;
+	emu->irq = -1;
+	emu->synth = NULL;
+	emu->get_synth_voice = NULL;
+	emu->port = pci_resource_start(pci, 0);
+
+	// emu->audigy = (int)pci->driver_data;
+	if (pci->device == 0x0004)
+		emu->audigy = 1;
+
+	if (emu->audigy)
+		emu->gpr_base = A_FXGPREGBASE;
+	else
+		emu->gpr_base = FXGPREGBASE;
+
+	if ((emu->res_port = request_region(emu->port, 0x20, "EMU10K1")) == NULL) {
+		snd_emu10k1_free(emu);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) {
+		snd_emu10k1_free(emu);
+		return -EBUSY;
+	}
+	emu->irq = pci->irq;
+
+	emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
+	emu->ptb_pages = snd_malloc_pci_pages(pci, 32 * 1024, &emu->ptb_pages_dmaaddr);
+	if (emu->ptb_pages == NULL) {
+		snd_emu10k1_free(emu);
+		return -ENOMEM;
+	}
+
+	emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*));
+	emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long));
+	if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
+		snd_emu10k1_free(emu);
+		return -ENOMEM;
+	}
+
+	emu->silent_page = snd_malloc_pci_pages(pci, EMUPAGESIZE, &emu->silent_page_dmaaddr);
+	if (emu->silent_page == NULL) {
+		snd_emu10k1_free(emu);
+		return -ENOMEM;
+	}
+	emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
+	if (emu->memhdr == NULL) {
+		snd_emu10k1_free(emu);
+		return -ENOMEM;
+	}
+	emu->memhdr->block_extra_size = sizeof(emu10k1_memblk_t) - sizeof(snd_util_memblk_t);
+
+	pci_set_master(pci);
+	/* read revision & serial */
+	pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&emu->revision);
+	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
+	emu->card_type = EMU10K1_CARD_CREATIVE;
+	if (emu->serial == 0x40011102) {
+		emu->card_type = EMU10K1_CARD_EMUAPS;
+		emu->APS = 1;
+	}
+	
+	emu->fx8010.fxbus_mask = 0x303f;
+	if (extin_mask == 0)
+		extin_mask = 0x1fcf;
+	if (extout_mask == 0)
+		extout_mask = 0x3fff;
+	emu->fx8010.extin_mask = extin_mask;
+	emu->fx8010.extout_mask = extout_mask;
+
+	if (emu->APS) {
+		if ((err = snd_emu10k1_ecard_init(emu)) < 0) {
+			snd_emu10k1_free(emu);
+			return err;
+		}
+	} else {
+		/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
+			does not support this, it shouldn't do any harm */
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+	}
+
+	if ((err = snd_emu10k1_init(emu, enable_ir)) < 0) {
+		snd_emu10k1_free(emu);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0) {
+		snd_emu10k1_free(emu);
+		return err;
+	}
+
+	snd_emu10k1_proc_init(emu);
+
+	*remu = emu;
+	return 0;
+}
+
+/* memory.c */
+EXPORT_SYMBOL(snd_emu10k1_synth_alloc);
+EXPORT_SYMBOL(snd_emu10k1_synth_free);
+EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user);
+EXPORT_SYMBOL(snd_emu10k1_memblk_map);
+/* voice.c */
+EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
+EXPORT_SYMBOL(snd_emu10k1_voice_free);
+/* io.c */
+EXPORT_SYMBOL(snd_emu10k1_ptr_read);
+EXPORT_SYMBOL(snd_emu10k1_ptr_write);
diff -Nru linux/sound/pci/emu10k1/emu10k1_patch.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_patch.c
--- linux/sound/pci/emu10k1/emu10k1_patch.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_patch.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,224 @@
+/*
+ *  Patch transfer callback for Emu10k1
+ *
+ *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+/*
+ * All the code for loading in a patch.  There is very little that is
+ * chip specific here.  Just the actual writing to the board.
+ */
+
+#define __NO_VERSION__
+#include "emu10k1_synth_local.h"
+
+/*
+ */
+#define BLANK_LOOP_START	4
+#define BLANK_LOOP_END		8
+#define BLANK_LOOP_SIZE		12
+#define BLANK_HEAD_SIZE		32
+
+/*
+ * allocate a sample block and copy data from userspace
+ */
+int
+snd_emu10k1_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp,
+		       snd_util_memhdr_t *hdr, const void *data, long count)
+{
+	int offset;
+	int truesize, size, loopsize, blocksize;
+	int loopend, sampleend;
+	unsigned int start_addr;
+	emu10k1_t *emu;
+
+	emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO);
+	snd_assert(sp != NULL, return -EINVAL);
+	snd_assert(hdr != NULL, return -EINVAL);
+
+	if (sp->v.size == 0) {
+		snd_printd("emu: rom font for sample %d\n", sp->v.sample);
+		return 0;
+	}
+
+	/* recalculate address offset */
+	sp->v.end -= sp->v.start;
+	sp->v.loopstart -= sp->v.start;
+	sp->v.loopend -= sp->v.start;
+	sp->v.start = 0;
+
+	/* some samples have invalid data.  the addresses are corrected in voice info */
+	sampleend = sp->v.end;
+	if (sampleend > sp->v.size)
+		sampleend = sp->v.size;
+	loopend = sp->v.loopend;
+	if (loopend > sampleend)
+		loopend = sampleend;
+
+	/* be sure loop points start < end */
+	if (sp->v.loopstart >= sp->v.loopend) {
+		int tmp = sp->v.loopstart;
+		sp->v.loopstart = sp->v.loopend;
+		sp->v.loopend = tmp;
+	}
+
+	/* compute true data size to be loaded */
+	truesize = sp->v.size + BLANK_HEAD_SIZE;
+	loopsize = 0;
+#if 0 /* not supported */
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+		loopsize = sp->v.loopend - sp->v.loopstart;
+	truesize += loopsize;
+#endif
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+		truesize += BLANK_LOOP_SIZE;
+
+	/* try to allocate a memory block */
+	blocksize = truesize;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		blocksize *= 2;
+	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
+	if (sp->block == NULL) {
+		snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
+		/* not ENOMEM (for compatibility with OSS) */
+		return -ENOSPC;
+	}
+	/* set the total size */
+	sp->v.truesize = blocksize;
+
+	/* write blank samples at head */
+	offset = 0;
+	size = BLANK_HEAD_SIZE;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	snd_assert(offset + size <= blocksize, return -EINVAL);
+	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
+	offset += size;
+
+	/* copy start->loopend */
+	size = loopend;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	snd_assert(offset + size <= blocksize, return -EINVAL);
+	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+		return -EFAULT;
+	}
+	offset += size;
+	data += size;
+
+#if 0 /* not suppported yet */
+	/* handle reverse (or bidirectional) loop */
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+		/* copy loop in reverse */
+		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+			int woffset;
+			unsigned short *wblock = (unsigned short*)block;
+			woffset = offset / 2;
+			snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL);
+			for (i = 0; i < loopsize; i++)
+				wblock[woffset + i] = wblock[woffset - i -1];
+			offset += loopsize * 2;
+		} else {
+			snd_assert(offset + loopsize <= blocksize, return -EINVAL);
+			for (i = 0; i < loopsize; i++)
+				block[offset + i] = block[offset - i -1];
+			offset += loopsize;
+		}
+
+		/* modify loop pointers */
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+			sp->v.loopend += loopsize;
+		} else {
+			sp->v.loopstart += loopsize;
+			sp->v.loopend += loopsize;
+		}
+		/* add sample pointer */
+		sp->v.end += loopsize;
+	}
+#endif
+
+	/* loopend -> sample end */
+	size = sp->v.size - loopend;
+	snd_assert(size >= 0, return -EINVAL);
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+		return -EFAULT;
+	}
+	offset += size;
+
+	/* clear rest of samples (if any) */
+	if (offset < blocksize)
+		snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
+
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+		/* if no blank loop is attached in the sample, add it */
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+		}
+	}
+
+#if 0 /* not supported yet */
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
+		/* unsigned -> signed */
+		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+			unsigned short *wblock = (unsigned short*)block;
+			for (i = 0; i < truesize; i++)
+				wblock[i] ^= 0x8000;
+		} else {
+			for (i = 0; i < truesize; i++)
+				block[i] ^= 0x80;
+		}
+	}
+#endif
+
+	/* recalculate offset */
+	start_addr = BLANK_HEAD_SIZE * 2;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		start_addr >>= 1;
+	sp->v.start += start_addr;
+	sp->v.end += start_addr;
+	sp->v.loopstart += start_addr;
+	sp->v.loopend += start_addr;
+
+	return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu10k1_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp,
+			snd_util_memhdr_t *hdr)
+{
+	emu10k1_t *emu;
+
+	emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO);
+	snd_assert(sp != NULL, return -EINVAL);
+	snd_assert(hdr != NULL, return -EINVAL);
+
+	if (sp->block) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+	}
+	return 0;
+}
+
diff -Nru linux/sound/pci/emu10k1/emu10k1_synth.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth.c
--- linux/sound/pci/emu10k1/emu10k1_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,122 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU10K1 WaveTable synth
+ *
+ *   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
+ */
+
+#include "emu10k1_synth_local.h"
+#include <linux/init.h>
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu10k1
+ */
+int snd_emu10k1_synth_new_device(snd_seq_device_t *dev)
+{
+	snd_emux_t *emu;
+	emu10k1_t *hw;
+	snd_emu10k1_synth_arg_t *arg;
+	unsigned long flags;
+
+	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (arg == NULL)
+		return -EINVAL;
+
+	if (arg->seq_ports <= 0)
+		return 0; /* nothing */
+	if (arg->max_voices < 1)
+		arg->max_voices = 1;
+	else if (arg->max_voices > 64)
+		arg->max_voices = 64;
+
+	if (snd_emux_new(&emu) < 0)
+		return -ENOMEM;
+
+	snd_emu10k1_ops_setup(emu);
+	emu->hw = hw = arg->hwptr;
+	emu->max_voices = arg->max_voices;
+	emu->num_ports = arg->seq_ports;
+	emu->pitch_shift = -501;
+	emu->memhdr = hw->memhdr;
+	emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
+	emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
+
+	if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
+		snd_emux_free(emu);
+		emu->hw = NULL;
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&hw->voice_lock, flags);
+	hw->synth = emu;
+	hw->get_synth_voice = snd_emu10k1_synth_get_voice;
+	spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+	dev->driver_data = emu;
+
+	return 0;
+}
+
+int snd_emu10k1_synth_delete_device(snd_seq_device_t *dev)
+{
+	snd_emux_t *emu;
+	emu10k1_t *hw;
+	unsigned long flags;
+
+	if (dev->driver_data == NULL)
+		return 0; /* not registered actually */
+
+	emu = snd_magic_cast(snd_emux_t, dev->driver_data, return -EINVAL);
+
+	hw = snd_magic_cast(emu10k1_t, emu->hw, return -EINVAL);
+	spin_lock_irqsave(&hw->voice_lock, flags);
+	hw->synth = NULL;
+	hw->get_synth_voice = NULL;
+	spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+	snd_emux_free(emu);
+	return 0;
+}
+
+
+EXPORT_NO_SYMBOLS;
+
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emu10k1_synth_init(void)
+{
+	
+	static snd_seq_dev_ops_t ops = {
+		snd_emu10k1_synth_new_device,
+		snd_emu10k1_synth_delete_device,
+	};
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, sizeof(snd_emu10k1_synth_arg_t));
+}
+
+static void __exit alsa_emu10k1_synth_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
+}
+
+module_init(alsa_emu10k1_synth_init)
+module_exit(alsa_emu10k1_synth_exit)
diff -Nru linux/sound/pci/emu10k1/emu10k1_synth_local.h linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth_local.h
--- linux/sound/pci/emu10k1/emu10k1_synth_local.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth_local.h	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,38 @@
+#ifndef __EMU10K1_SYNTH_LOCAL_H
+#define __EMU10K1_SYNTH_LOCAL_H
+/*
+ *  Local defininitons for Emu10k1 wavetable
+ *
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1_synth.h>
+
+/* emu10k1_patch.c */
+int snd_emu10k1_sample_new(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *_data, long count);
+int snd_emu10k1_sample_free(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr);
+int snd_emu10k1_memhdr_init(snd_emux_t *emu);
+
+/* emu10k1_callback.c */
+void snd_emu10k1_ops_setup(snd_emux_t *emu);
+int snd_emu10k1_synth_get_voice(emu10k1_t *hw);
+
+
+#endif	/* __EMU10K1_SYNTH_LOCAL_H */
diff -Nru linux/sound/pci/emu10k1/emufx.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emufx.c
--- linux/sound/pci/emu10k1/emufx.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emufx.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2227 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for effect processor FX8010
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define chip_t emu10k1_t
+
+#if 0		/* for testing purposes - digital out -> capture */
+#define EMU10K1_CAPTURE_DIGITAL_OUT
+#endif
+#if 0		/* for testing purposes - set S/PDIF to AC3 output */
+#define EMU10K1_SET_AC3_IEC958
+#endif
+#if 0		/* for testing purposes - feed the front signal to Center/LFE outputs */
+#define EMU10K1_CENTER_LFE_FROM_FRONT
+#endif
+
+/*
+ *  Tables
+ */ 
+
+static char *fxbuses[16] = {
+	/* 0x00 */ "PCM Left",
+	/* 0x01 */ "PCM Right",
+	/* 0x02 */ "PCM Surround Left",
+	/* 0x03 */ "PCM Surround Right",
+	/* 0x04 */ "MIDI Left",
+	/* 0x05 */ "MIDI Right",
+	/* 0x06 */ "Center",
+	/* 0x07 */ "LFE",
+	/* 0x08 */ NULL,
+	/* 0x09 */ NULL,
+	/* 0x0a */ NULL,
+	/* 0x0b */ NULL,
+	/* 0x0c */ "MIDI Reverb",
+	/* 0x0d */ "MIDI Chorus",
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL
+};
+
+static char *creative_ins[16] = {
+	/* 0x00 */ "AC97 Left",
+	/* 0x01 */ "AC97 Right",
+	/* 0x02 */ "TTL IEC958 Left",
+	/* 0x03 */ "TTL IEC958 Right",
+	/* 0x04 */ "Zoom Video Left",
+	/* 0x05 */ "Zoom Video Right",
+	/* 0x06 */ "Optical IEC958 Left",
+	/* 0x07 */ "Optical IEC958 Right",
+	/* 0x08 */ "Line/Mic 1 Left",
+	/* 0x09 */ "Line/Mic 1 Right",
+	/* 0x0a */ "Coaxial IEC958 Left",
+	/* 0x0b */ "Coaxial IEC958 Right",
+	/* 0x0c */ "Line/Mic 2 Left",
+	/* 0x0d */ "Line/Mic 2 Right",
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL
+};
+
+static char *audigy_ins[16] = {
+	/* 0x00 */ "AC97 Left",
+	/* 0x01 */ "AC97 Right",
+	/* 0x02 */ "Audigy CD Left",
+	/* 0x03 */ "Audigy CD Right",
+	/* 0x04 */ NULL,
+	/* 0x05 */ NULL,
+	/* 0x06 */ NULL,
+	/* 0x07 */ NULL,
+	/* 0x08 */ "Line/Mic 2 Left",
+	/* 0x09 */ "Line/Mic 2 Right",
+	/* 0x0a */ NULL,
+	/* 0x0b */ NULL,
+	/* 0x0c */ "Aux2 Left",
+	/* 0x0d */ "Aux2 Right",
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL
+};
+
+static char *creative_outs[32] = {
+	/* 0x00 */ "AC97 Left",
+	/* 0x01 */ "AC97 Right",
+	/* 0x02 */ "Optical IEC958 Left",
+	/* 0x03 */ "Optical IEC958 Right",
+	/* 0x04 */ "Center",
+	/* 0x05 */ "LFE",
+	/* 0x06 */ "Headphone Left",
+	/* 0x07 */ "Headphone Right",
+	/* 0x08 */ "Surround Left",
+	/* 0x09 */ "Surround Right",
+	/* 0x0a */ "PCM Capture Left",
+	/* 0x0b */ "PCM Capture Right",
+	/* 0x0c */ "MIC Capture",
+	/* 0x0d */ NULL,
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL,
+	/* 0x10 */ NULL,
+	/* 0x11 */ "Analog Center",
+	/* 0x12 */ "Analog LFE",
+	/* 0x13 */ NULL,
+	/* 0x14 */ NULL,
+	/* 0x15 */ NULL,
+	/* 0x16 */ NULL,
+	/* 0x17 */ NULL,
+	/* 0x18 */ NULL,
+	/* 0x19 */ NULL,
+	/* 0x1a */ NULL,
+	/* 0x1b */ NULL,
+	/* 0x1c */ NULL,
+	/* 0x1d */ NULL,
+	/* 0x1e */ NULL,
+	/* 0x1f */ NULL,
+};
+
+static char *audigy_outs[32] = {
+	/* 0x00 */ "Digital Front Left",
+	/* 0x01 */ "Digital Front Right",
+	/* 0x02 */ "Digital Center",
+	/* 0x03 */ "Digital LEF",
+	/* 0x04 */ "Headphone Left",
+	/* 0x05 */ "Headphone Right",
+	/* 0x06 */ "Digital Rear Left",
+	/* 0x07 */ "Digital Rear Right",
+	/* 0x08 */ "Front Left",
+	/* 0x09 */ "Front Right",
+	/* 0x0a */ "Center",
+	/* 0x0b */ "LFE",
+	/* 0x0c */ NULL,
+	/* 0x0d */ NULL,
+	/* 0x0e */ "Rear Left",
+	/* 0x0f */ "Rear Right",
+	/* 0x10 */ "AC97 Front Left",
+	/* 0x11 */ "AC97 Front Right",
+	/* 0x12 */ "ADC Caputre Left",
+	/* 0x13 */ "ADC Capture Right",
+	/* 0x14 */ NULL,
+	/* 0x15 */ NULL,
+	/* 0x16 */ NULL,
+	/* 0x17 */ NULL,
+	/* 0x18 */ NULL,
+	/* 0x19 */ NULL,
+	/* 0x1a */ NULL,
+	/* 0x1b */ NULL,
+	/* 0x1c */ NULL,
+	/* 0x1d */ NULL,
+	/* 0x1e */ NULL,
+	/* 0x1f */ NULL,
+};
+
+static const u32 bass_table[41][5] = {
+	{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
+	{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
+	{ 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
+	{ 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
+	{ 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
+	{ 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
+	{ 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
+	{ 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
+	{ 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
+	{ 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
+	{ 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
+	{ 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
+	{ 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
+	{ 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
+	{ 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
+	{ 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
+	{ 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
+	{ 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
+	{ 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
+	{ 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
+	{ 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
+	{ 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
+	{ 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
+	{ 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
+	{ 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
+	{ 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
+	{ 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
+	{ 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
+	{ 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
+	{ 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
+	{ 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
+	{ 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
+	{ 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
+	{ 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
+	{ 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
+	{ 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
+	{ 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
+	{ 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
+	{ 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
+	{ 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
+};
+
+static const u32 treble_table[41][5] = {
+	{ 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
+	{ 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
+	{ 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
+	{ 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
+	{ 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
+	{ 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
+	{ 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
+	{ 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
+	{ 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
+	{ 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
+	{ 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
+	{ 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
+	{ 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
+	{ 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
+	{ 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
+	{ 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
+	{ 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
+	{ 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
+	{ 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
+	{ 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
+	{ 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
+	{ 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
+	{ 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
+	{ 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
+	{ 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
+	{ 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
+	{ 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
+	{ 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
+	{ 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
+	{ 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
+	{ 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
+	{ 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
+	{ 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
+	{ 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
+	{ 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
+	{ 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
+	{ 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
+	{ 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
+	{ 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
+	{ 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
+	{ 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
+};
+
+static const u32 db_table[101] = {
+	0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
+	0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
+	0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1,
+	0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0,
+	0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9,
+	0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb,
+	0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005,
+	0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d,
+	0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd,
+	0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8,
+	0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481,
+	0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333,
+	0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d,
+	0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6,
+	0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d,
+	0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf,
+	0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038,
+	0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a,
+	0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea,
+	0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272,
+	0x7fffffff,
+};
+
+static const u32 onoff_table[2] = {
+	0x00000000, 0x00000001
+};
+
+/*
+ */
+ 
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+
+/*
+ *   controls
+ */
+
+static int snd_emu10k1_gpr_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value;
+
+	if (ctl->min == 0 && ctl->max == 1)
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = ctl->vcount;
+	uinfo->value.integer.min = ctl->min;
+	uinfo->value.integer.max = ctl->max;
+	return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value;
+	unsigned long flags;
+	int i;
+	
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (i = 0; i < ctl->vcount; i++)
+		ucontrol->value.integer.value[i] = ctl->value[i];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value;
+	unsigned long flags;
+	unsigned int nval, val;
+	int i, j, change = 0;
+	
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (i = 0; i < ctl->vcount; i++) {
+		nval = ucontrol->value.integer.value[i];
+		if (nval < ctl->min)
+			nval = ctl->min;
+		if (nval > ctl->max)
+			nval = ctl->max;
+		if (nval != ctl->value[i])
+			change = 1;
+		val = ctl->value[i] = nval;
+		switch (ctl->translation) {
+		case EMU10K1_GPR_TRANSLATION_NONE:
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
+			break;
+		case EMU10K1_GPR_TRANSLATION_TABLE100:
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
+			break;
+		case EMU10K1_GRP_TRANSLATION_BASS:
+			snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error);
+			for (j = 0; j < 5; j++)
+				snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]);
+			break;
+		case EMU10K1_GRP_TRANSLATION_TREBLE:
+			snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error);
+			for (j = 0; j < 5; j++)
+				snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]);
+			break;
+		case EMU10K1_GPR_TRANSLATION_ONOFF:
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]);
+			break;
+		}
+	}
+      __error:
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+/*
+ *   Interrupt handler
+ */
+
+static void snd_emu10k1_fx8010_interrupt(emu10k1_t *emu)
+{
+	snd_emu10k1_fx8010_irq_t *irq, *nirq;
+
+	irq = emu->fx8010.irq_handlers;
+	while (irq) {
+		nirq = irq->next;	/* irq ptr can be removed from list */
+		if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) {
+			if (irq->handler)
+				irq->handler(emu, irq->private_data);
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1);
+		}
+		irq = nirq;
+	}
+}
+
+static int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu,
+						   snd_fx8010_irq_handler_t *handler,
+						   unsigned char gpr_running,
+						   void *private_data,
+						   snd_emu10k1_fx8010_irq_t **r_irq)
+{
+	snd_emu10k1_fx8010_irq_t *irq;
+	unsigned long flags;
+	
+	snd_runtime_check(emu, return -EINVAL);
+	snd_runtime_check(handler, return -EINVAL);
+	irq = kmalloc(sizeof(*irq), GFP_KERNEL);
+	if (irq == NULL)
+		return -ENOMEM;
+	irq->handler = handler;
+	irq->gpr_running = gpr_running;
+	irq->private_data = private_data;
+	irq->next = NULL;
+	spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+	if (emu->fx8010.irq_handlers == NULL) {
+		emu->fx8010.irq_handlers = irq;
+		emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
+		snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE);
+	} else {
+		irq->next = emu->fx8010.irq_handlers;
+		emu->fx8010.irq_handlers = irq;
+	}
+	spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+	if (r_irq)
+		*r_irq = irq;
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu,
+						     snd_emu10k1_fx8010_irq_t *irq)
+{
+	snd_emu10k1_fx8010_irq_t *tmp;
+	unsigned long flags;
+	
+	snd_runtime_check(irq, return -EINVAL);
+	spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+	if ((tmp = emu->fx8010.irq_handlers) == irq) {
+		emu->fx8010.irq_handlers = tmp->next;
+		if (emu->fx8010.irq_handlers == NULL) {
+			snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+			emu->dsp_interrupt = NULL;
+		}
+	} else {
+		while (tmp && tmp->next != irq)
+			tmp = tmp->next;
+		if (tmp)
+			tmp->next = tmp->next->next;
+	}
+	spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+	kfree(irq);
+	return 0;
+}
+
+/*
+ *   PCM streams
+ */
+
+#define INITIAL_TRAM_SHIFT     14
+#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1)
+
+static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data)
+{
+	snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, private_data, return);
+	snd_pcm_period_elapsed(substream);
+}
+
+static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
+						   unsigned short *dst_right,
+						   unsigned short *src,
+						   unsigned int count,
+						   unsigned int tram_shift)
+{
+	// printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count);
+	if ((tram_shift & 1) == 0) {
+		while (count--) {
+			*dst_left-- = *src++;
+			*dst_right-- = *src++;
+		}
+	} else {
+		while (count--) {
+			*dst_right-- = *src++;
+			*dst_left-- = *src++;
+		}
+	}
+}
+
+static void snd_emu10k1_fx8010_playback_tram_poke(emu10k1_t *emu,
+						  unsigned int *tram_pos,
+						  unsigned int *tram_shift,
+						  unsigned int tram_size,
+						  unsigned short *src,
+						  unsigned int frames)
+{
+	unsigned int count;
+
+	while (frames > *tram_pos) {
+		count = *tram_pos + 1;
+		snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos,
+						       (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2,
+						       src, count, *tram_shift);
+		src += count * 2;
+		frames -= count;
+		*tram_pos = (tram_size / 2) - 1;
+		(*tram_shift)++;
+	}
+	snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos,
+					       (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2,
+					       src, frames, *tram_shift++);
+	*tram_pos -= frames;
+}
+
+static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream,
+						snd_pcm_uframes_t frames)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+	snd_pcm_sframes_t diff = appl_ptr - pcm->appl_ptr;
+	snd_pcm_uframes_t buffer_size = pcm->buffer_size / 2;
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		pcm->sw_ready += diff;
+	}
+	pcm->sw_ready += frames;
+	pcm->appl_ptr = appl_ptr + frames;
+	while (pcm->hw_ready < buffer_size &&
+	       pcm->sw_ready > 0) {
+	       	size_t hw_to_end = buffer_size - pcm->hw_data;
+	       	size_t sw_to_end = (runtime->buffer_size << 2) - pcm->sw_data;
+	       	size_t tframes = buffer_size - pcm->hw_ready;
+	       	if (pcm->sw_ready < tframes)
+	       		tframes = pcm->sw_ready;
+	       	if (hw_to_end < tframes)
+	       		tframes = hw_to_end;
+	       	if (sw_to_end < tframes)
+	       		tframes = sw_to_end;
+	       	snd_emu10k1_fx8010_playback_tram_poke(emu, &pcm->tram_pos, &pcm->tram_shift,
+	       					      pcm->buffer_size,
+	       					      (unsigned short *)(runtime->dma_area + (pcm->sw_data << 2)),
+	       					      tframes);
+		pcm->hw_data += tframes;
+		if (pcm->hw_data == buffer_size)
+			pcm->hw_data = 0;
+		pcm->sw_data += tframes;
+		if (pcm->sw_data == runtime->buffer_size)
+			pcm->sw_data = 0;
+		pcm->hw_ready += tframes;
+		pcm->sw_ready -= tframes;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream,
+						 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+	int i;
+
+	for (i = 0; i < pcm->channels; i++)
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0);
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+	int i;
+	
+	// printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2);
+	pcm->sw_data = pcm->sw_io = pcm->sw_ready = 0;
+	pcm->hw_data = pcm->hw_io = pcm->hw_ready = 0;
+	pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+	pcm->tram_shift = 0;
+	pcm->appl_ptr = 0;
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0);	/* reset */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);	/* reset */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0);		/* reset ptr number */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size);
+	for (i = 0; i < pcm->channels; i++)
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels));
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned long flags;
+	int result = 0;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+#ifdef EMU10K1_SET_AC3_IEC958
+	{
+		int i;
+		for (i = 0; i < 3; i++) {
+			unsigned int bits;
+			bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
+			       0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA;
+			snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits);
+		}
+	}
+#endif
+		result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq);
+		if (result < 0)
+			goto __err;
+		snd_emu10k1_fx8010_playback_transfer(substream, 0);	/* roll the ball */
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
+		pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+		pcm->tram_shift = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+      __err:
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+	size_t ptr;
+	snd_pcm_sframes_t frames;
+
+	if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0))
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0);
+	frames = ptr - pcm->hw_io;
+	if (frames < 0)
+		frames += runtime->buffer_size;
+	pcm->hw_io = ptr;
+	pcm->hw_ready -= frames;
+	pcm->sw_io += frames;
+	if (pcm->sw_io > runtime->buffer_size)
+		pcm->sw_io -= runtime->buffer_size;
+	snd_emu10k1_fx8010_playback_transfer(substream, 0);
+	return pcm->sw_io;
+}
+
+static int snd_emu10k1_fx8010_playback_copy(snd_pcm_substream_t *substream,
+					    int channel,
+					    snd_pcm_uframes_t hwoff,
+					    void *src,
+					    snd_pcm_uframes_t frames)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	size_t hwoffb = hwoff << 2;
+	size_t bytes = frames << 2;
+	char *hwbuf = runtime->dma_area + hwoffb;
+	if (copy_from_user(hwbuf, src, bytes))
+		return -EFAULT;
+	spin_lock_irq(&runtime->lock);
+	snd_emu10k1_fx8010_playback_transfer(substream, frames);
+	spin_unlock_irq(&runtime->lock);
+	return 0;
+}
+
+static snd_pcm_hardware_t snd_emu10k1_fx8010_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_48000,
+	rate_min:		48000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		1,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	1024,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+
+	runtime->hw = snd_emu10k1_fx8010_playback;
+	runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels;
+	runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2;
+	spin_lock(&emu->reg_lock);
+	if (pcm->valid == 0) {
+		spin_unlock(&emu->reg_lock);
+		return -ENODEV;
+	}
+	pcm->opened = 1;
+	spin_unlock(&emu->reg_lock);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number];
+
+	spin_lock(&emu->reg_lock);
+	pcm->opened = 0;
+	spin_unlock(&emu->reg_lock);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = {
+	open:			snd_emu10k1_fx8010_playback_open,
+	close:			snd_emu10k1_fx8010_playback_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_emu10k1_fx8010_playback_hw_params,
+	hw_free:		snd_emu10k1_fx8010_playback_hw_free,
+	prepare:		snd_emu10k1_fx8010_playback_prepare,
+	trigger:		snd_emu10k1_fx8010_playback_trigger,
+	copy:			snd_emu10k1_fx8010_playback_copy,
+	pointer:		snd_emu10k1_fx8010_playback_pointer,
+};
+
+static void snd_emu10k1_fx8010_pcm_free(snd_pcm_t *pcm)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return);
+	emu->pcm_fx8010 = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1", device, 8, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+	pcm->private_free = snd_emu10k1_fx8010_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "EMU10K1 FX8010");
+	emu->pcm_fx8010 = pcm;
+	
+	snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+/*************************************************************************
+ * EMU10K1 effect manager
+ *************************************************************************/
+
+static void snd_emu10k1_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr,
+				 u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+	snd_assert(*ptr < 512, return);
+	set_bit(*ptr, &icode->code_valid);
+	icode->code[*ptr    ][0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
+	icode->code[(*ptr)++][1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
+}
+
+#define OP(icode, ptr, op, r, a, x, y) \
+	snd_emu10k1_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_audigy_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr,
+					u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+	snd_assert(*ptr < 512, return);
+	set_bit(*ptr, &icode->code_valid);
+	icode->code[*ptr    ][0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
+	icode->code[(*ptr)++][1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
+}
+
+#define A_OP(icode, ptr, op, r, a, x, y) \
+	snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y)
+
+void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data)
+{
+	pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+	snd_emu10k1_ptr_write(emu, pc, 0, data);
+}
+
+unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc)
+{
+	pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+	return snd_emu10k1_ptr_read(emu, pc, 0);
+}
+
+static void snd_emu10k1_gpr_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int gpr;
+
+	for (gpr = 0; gpr < 0x100; gpr++) {
+		if (!test_bit(gpr, &icode->gpr_valid))
+			continue;
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, icode->gpr_map[gpr]);
+	}
+}
+
+static void snd_emu10k1_gpr_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int gpr;
+
+	for (gpr = 0; gpr < 0x100; gpr++) {
+		set_bit(gpr, &icode->gpr_valid);
+		icode->gpr_map[gpr] = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
+	}
+}
+
+static void snd_emu10k1_tram_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int tram;
+
+	for (tram = 0; tram < 0xa0; tram++) {
+		if (!test_bit(tram, &icode->tram_valid))
+			continue;
+		snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, icode->tram_data_map[tram]);
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, icode->tram_addr_map[tram]);
+	}
+}
+
+static void snd_emu10k1_tram_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int tram;
+
+	for (tram = 0; tram < 0xa0; tram++) {
+		set_bit(tram, &icode->tram_valid);
+		icode->tram_data_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0);
+		icode->tram_addr_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0);
+	}
+}
+
+static void snd_emu10k1_code_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	u32 pc;
+
+	for (pc = 0; pc < 512; pc++) {
+		if (!test_bit(pc, &icode->code_valid))
+			continue;
+		snd_emu10k1_efx_write(emu, pc * 2, icode->code[pc][0]);
+		snd_emu10k1_efx_write(emu, pc * 2 + 1, icode->code[pc][1]);
+	}
+}
+
+static void snd_emu10k1_code_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	u32 pc;
+
+	for (pc = 0; pc < 512; pc++) {
+		set_bit(pc, &icode->code_valid);
+		icode->code[pc][0] = snd_emu10k1_efx_read(emu, pc * 2);
+		icode->code[pc][1] = snd_emu10k1_efx_read(emu, pc * 2 + 1);
+	}
+}
+
+static snd_emu10k1_fx8010_ctl_t *snd_emu10k1_look_for_ctl(emu10k1_t *emu, snd_ctl_elem_id_t *id)
+{
+	snd_emu10k1_fx8010_ctl_t *ctl;
+	snd_kcontrol_t *kcontrol;
+	struct list_head *list;
+	
+	list_for_each(list, &emu->fx8010.gpr_ctl) {
+		ctl = emu10k1_gpr_ctl(list);
+		kcontrol = ctl->kcontrol;
+		if (kcontrol->id.iface == id->iface &&
+		    !strcmp(kcontrol->id.name, id->name) &&
+		    kcontrol->id.index == id->index)
+			return ctl;
+	}
+	return NULL;
+}
+
+static int snd_emu10k1_verify_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int i;
+	snd_ctl_elem_id_t *_id, id;
+	emu10k1_fx8010_control_gpr_t *_gctl, gctl;
+	
+	for (i = 0, _id = icode->gpr_del_controls;
+	     i < icode->gpr_del_control_count; i++, _id++) {
+	     	if (copy_from_user(&id, _id, sizeof(id)))
+	     		return -EFAULT;
+		if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
+			return -ENOENT;
+	}
+	for (i = 0, _gctl = icode->gpr_add_controls;
+	     i < icode->gpr_add_control_count; i++) {
+		if (copy_from_user(&gctl, _gctl, sizeof(gctl)))
+			return -EFAULT;
+		if (snd_emu10k1_look_for_ctl(emu, &gctl.id))
+			continue;
+		if (snd_ctl_find_id(emu->card, &gctl.id) != NULL)
+			return -EEXIST;
+		if (gctl.id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+		    gctl.id.iface != SNDRV_CTL_ELEM_IFACE_PCM)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static void snd_emu10k1_ctl_private_free(snd_kcontrol_t *kctl)
+{
+	snd_emu10k1_fx8010_ctl_t *ctl;
+	
+	ctl = (snd_emu10k1_fx8010_ctl_t *)kctl->private_value;
+	kctl->private_value = 0;
+	list_del(&ctl->list);
+	kfree(ctl);
+}
+
+static void snd_emu10k1_add_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int i, j;
+	emu10k1_fx8010_control_gpr_t *_gctl, gctl;
+	snd_emu10k1_fx8010_ctl_t *ctl, nctl;
+	snd_kcontrol_new_t knew;
+	snd_kcontrol_t *kctl;
+	snd_ctl_elem_value_t val;
+
+	for (i = 0, _gctl = icode->gpr_add_controls;
+	     i < icode->gpr_add_control_count; i++, _gctl++) {
+		if (copy_from_user(&gctl, _gctl, sizeof(gctl)))
+			return;
+		snd_runtime_check(gctl.id.iface == SNDRV_CTL_ELEM_IFACE_MIXER ||
+		                  gctl.id.iface == SNDRV_CTL_ELEM_IFACE_PCM, continue);
+		snd_runtime_check(gctl.id.name[0] != '\0', continue);
+		ctl = snd_emu10k1_look_for_ctl(emu, &gctl.id);
+		memset(&knew, 0, sizeof(knew));
+		knew.iface = gctl.id.iface;
+		knew.name = gctl.id.name;
+		knew.index = gctl.id.index;
+		knew.info = snd_emu10k1_gpr_ctl_info;
+		knew.get = snd_emu10k1_gpr_ctl_get;
+		knew.put = snd_emu10k1_gpr_ctl_put;
+		memset(&nctl, 0, sizeof(nctl));
+		nctl.vcount = gctl.vcount;
+		nctl.count = gctl.count;
+		for (j = 0; j < 32; j++) {
+			nctl.gpr[j] = gctl.gpr[j];
+			nctl.value[j] = ~gctl.value[j];
+			val.value.integer.value[j] = gctl.value[j];
+		}
+		nctl.min = gctl.min;
+		nctl.max = gctl.max;
+		nctl.translation = gctl.translation;
+		if (ctl == NULL) {
+			ctl = (snd_emu10k1_fx8010_ctl_t *)kmalloc(sizeof(*ctl), GFP_KERNEL);
+			if (ctl == NULL)
+				continue;
+			knew.private_value = (unsigned long)ctl;
+			memcpy(ctl, &nctl, sizeof(nctl));
+			if (snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu)) < 0) {
+				kfree(ctl);
+				continue;
+			}
+			kctl->private_free = snd_emu10k1_ctl_private_free;
+			ctl->kcontrol = kctl;
+			list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl);
+		} else {
+			/* overwrite */
+			nctl.list = ctl->list;
+			nctl.kcontrol = ctl->kcontrol;
+			memcpy(ctl, &nctl, sizeof(nctl));
+			snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			                          SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id);
+		}
+		snd_emu10k1_gpr_ctl_put(ctl->kcontrol, &val);
+	}
+}
+
+static void snd_emu10k1_del_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int i;
+	snd_ctl_elem_id_t *_id, id;
+	snd_emu10k1_fx8010_ctl_t *ctl;
+	
+	for (i = 0, _id = icode->gpr_del_controls;
+	     i < icode->gpr_del_control_count; i++, _id++) {
+	     	snd_runtime_check(copy_from_user(&id, _id, sizeof(id)) == 0, continue);
+		ctl = snd_emu10k1_look_for_ctl(emu, &id);
+		snd_runtime_check(ctl == NULL, continue);
+		snd_ctl_remove(emu->card, ctl->kcontrol);
+	}
+}
+
+static int snd_emu10k1_icode_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int err = 0;
+
+	down(&emu->fx8010.lock);
+	if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0)
+		goto __error;
+	strncpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name)-1);
+	emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0';
+	/* stop FX processor - this may be dangerous, but it's better to miss
+	   some samples than generate wrong ones - [jk] */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
+	/* ok, do the main job */
+	snd_emu10k1_del_controls(emu, icode);
+	snd_emu10k1_gpr_poke(emu, icode);
+	snd_emu10k1_tram_poke(emu, icode);
+	snd_emu10k1_code_poke(emu, icode);
+	snd_emu10k1_add_controls(emu, icode);
+	/* start FX processor when the DSP code is updated */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+      __error:
+	up(&emu->fx8010.lock);
+	return err;
+}
+
+static int snd_emu10k1_icode_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	down(&emu->fx8010.lock);
+	strncpy(icode->name, emu->fx8010.name, sizeof(icode->name)-1);
+	emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0';
+	/* ok, do the main job */
+	snd_emu10k1_gpr_peek(emu, icode);
+	snd_emu10k1_tram_peek(emu, icode);
+	snd_emu10k1_code_peek(emu, icode);
+	up(&emu->fx8010.lock);
+	return 0;
+}
+
+static int snd_emu10k1_ipcm_poke(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm)
+{
+	int err = 0, i;
+	snd_emu10k1_fx8010_pcm_t *pcm;
+
+	if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+		return -EINVAL;
+	if (ipcm->channels > 32)
+		return -EINVAL;
+	pcm = &emu->fx8010.pcm[ipcm->substream];
+	down(&emu->fx8010.lock);
+	spin_lock_irq(&emu->reg_lock);
+	if (pcm->opened) {
+		err = -EBUSY;
+		goto __error;
+	}
+	if (ipcm->channels == 0) {	/* remove */
+		pcm->valid = 0;
+	} else {
+		/* FIXME: we need to add universal code to the PCM transfer routine */
+		if (ipcm->channels != 2) {
+			err = -EINVAL;
+			goto __error;
+		}
+		pcm->valid = 1;
+		pcm->opened = 0;
+		pcm->channels = ipcm->channels;
+		pcm->tram_start = ipcm->tram_start;
+		pcm->buffer_size = ipcm->buffer_size;
+		pcm->gpr_size = ipcm->gpr_size;
+		pcm->gpr_count = ipcm->gpr_count;
+		pcm->gpr_tmpcount = ipcm->gpr_tmpcount;
+		pcm->gpr_ptr = ipcm->gpr_ptr;
+		pcm->gpr_trigger = ipcm->gpr_trigger;
+		pcm->gpr_running = ipcm->gpr_running;
+		for (i = 0; i < pcm->channels; i++)
+			pcm->etram[i] = ipcm->etram[i];
+	}
+      __error:
+	spin_unlock_irq(&emu->reg_lock);
+	up(&emu->fx8010.lock);
+	return err;
+}
+
+static int snd_emu10k1_ipcm_peek(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm)
+{
+	int err = 0, i;
+	snd_emu10k1_fx8010_pcm_t *pcm;
+
+	if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+		return -EINVAL;
+	pcm = &emu->fx8010.pcm[ipcm->substream];
+	down(&emu->fx8010.lock);
+	spin_lock_irq(&emu->reg_lock);
+	ipcm->channels = pcm->channels;
+	ipcm->tram_start = pcm->tram_start;
+	ipcm->buffer_size = pcm->buffer_size;
+	ipcm->gpr_size = pcm->gpr_size;
+	ipcm->gpr_ptr = pcm->gpr_ptr;
+	ipcm->gpr_count = pcm->gpr_count;
+	ipcm->gpr_tmpcount = pcm->gpr_tmpcount;
+	ipcm->gpr_trigger = pcm->gpr_trigger;
+	ipcm->gpr_running = pcm->gpr_running;
+	for (i = 0; i < pcm->channels; i++)
+		ipcm->etram[i] = pcm->etram[i];
+	ipcm->res1 = ipcm->res2 = 0;
+	ipcm->pad = 0;
+	spin_unlock_irq(&emu->reg_lock);
+	up(&emu->fx8010.lock);
+	return err;
+}
+
+#define SND_EMU10K1_GPR_CONTROLS	41
+#define SND_EMU10K1_INPUTS		10
+#define SND_EMU10K1_PLAYBACK_CHANNELS	6
+#define SND_EMU10K1_CAPTURE_CHANNELS	4
+
+static void __devinit snd_emu10k1_init_mono_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 1;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->min = 0;
+	ctl->max = 100;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;	
+}
+
+static void __devinit snd_emu10k1_init_stereo_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 2;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+	ctl->min = 0;
+	ctl->max = 100;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;	
+}
+
+static void __devinit snd_emu10k1_init_mono_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 1;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->min = 0;
+	ctl->max = 1;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+static void __devinit snd_emu10k1_init_stereo_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 2;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+	ctl->min = 0;
+	ctl->max = 1;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+
+/*
+ * initial DSP configuration for Audigy
+ */
+
+static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu)
+{
+	int err, i, gpr, tmp, playback, capture, nctl;
+	u32 ptr;
+	emu10k1_fx8010_code_t *icode;
+	emu10k1_fx8010_control_gpr_t *controls;
+	mm_segment_t seg;
+
+	spin_lock_init(&emu->fx8010.irq_lock);
+	INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
+
+	if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) {
+		kfree(icode);
+		return -ENOMEM;
+	}
+	
+	/* clear free GPRs */
+	for (i = 0; i < 256; i++)
+		set_bit(i, &icode->gpr_valid);
+
+	strcpy(icode->name, "Audigy DSP code for ALSA");
+	ptr = 0;
+	nctl = 0;
+	playback = 10;
+	capture = playback + 10; /* we reserve 10 voices */
+	gpr = capture + 10;
+	tmp = 0x80;
+	
+	/* stop FX processor */
+	snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
+
+	/* Wave Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Wave Surround Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Surround Playback Volume", gpr, 0);
+	gpr += 2;
+
+	/* Wave Center Playback */
+	/* Center = sub = Left/2 + Right/2 */
+	A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_FXBUS(FXBUS_PCM_LEFT), 0xcd, A_FXBUS(FXBUS_PCM_RIGHT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_GPR(tmp));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Center Playback Volume", gpr, 0);
+	gpr++;
+
+	/* Wave LFE Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_GPR(tmp));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "Wave LFE Playback Volume", gpr, 0);
+	gpr++;
+
+	/* Music Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+0), A_GPR(playback+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Wave Capture */
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* Music Capture */
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* Surround Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 80);
+	gpr += 2;
+
+	/* Center Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 80);
+	gpr++;
+
+	/* LFE Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 80);
+	gpr++;
+
+	/*
+	 * inputs
+	 */
+#define A_ADD_VOLUME_IN(var,vol,input) \
+A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+
+	/* AC'97 Playback Volume */
+	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_AC97_L);
+	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_AC97_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "AC97 Playback Volume", gpr, 0);
+	gpr += 2;
+	/* AC'97 Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "AC97 Capture Volume", gpr, 100);
+	gpr += 2;
+
+	/* Audigy CD Playback Volume */
+	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_SPDIF_CD_L);
+	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Playback Volume", gpr, 0);
+	gpr += 2;
+	/* Audigy CD Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* Line2 Playback Volume */
+	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_LINE2_L);
+	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_LINE2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Playback Volume", gpr, 0);
+	gpr += 2;
+	/* Line2 Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* Aux2 Playback Volume */
+	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_AUX2_L);
+	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_AUX2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Aux2 Playback Volume", gpr, 0);
+	gpr += 2;
+	/* Aux2 Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Aux2 Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/*
+	 * outputs
+	 */
+#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src))
+#define A_PUT_STEREO_OUTPUT(out1,out2,src) \
+	{A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);}
+
+	/* digital outputs */
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback);
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2);
+	A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4);
+	A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5);
+
+	/* analog speakers */
+	//A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback);
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_AC97_L, A_EXTOUT_AC97_R, playback);
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2);
+	A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4);
+	A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5);
+
+	/* headphone */
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback);
+
+	/* ADC buffer */
+	A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
+	A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
+
+	/*
+	 * ok, set up done..
+	 */
+
+	if (gpr > tmp) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+	/* clear remaining instruction memory */
+	while (ptr < 0x200)
+		A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
+
+	seg = snd_enter_user();
+	icode->gpr_add_control_count = nctl;
+	icode->gpr_add_controls = controls;
+	err = snd_emu10k1_icode_poke(emu, icode);
+	snd_leave_user(seg);
+
+ __err:
+	kfree(controls);
+	kfree(icode);
+	return err;
+}
+
+
+/*
+ * initial DSP configuration for Emu10k1
+ */
+
+/* when volume = max, then copy only to avoid volume modification */
+/* with iMAC0 (negative values) */
+static void __devinit _volume(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+	OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+	OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+	OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
+	OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+}
+static void __devinit _volume_add(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+	OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+	OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+	OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
+	OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+	OP(icode, ptr, iMAC0, dst, dst, src, vol);
+}
+static void __devinit _volume_out(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+	OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+	OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+	OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+	OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+	OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+}
+
+#define VOLUME(icode, ptr, dst, src, vol) \
+		_volume(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_IN(icode, ptr, dst, src, vol) \
+		_volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_ADD(icode, ptr, dst, src, vol) \
+		_volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
+		_volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_OUT(icode, ptr, dst, src, vol) \
+		_volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+#define _SWITCH(icode, ptr, dst, src, sw) \
+	OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
+#define SWITCH(icode, ptr, dst, src, sw) \
+		_SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw))
+#define SWITCH_IN(icode, ptr, dst, src, sw) \
+		_SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw))
+#define _SWITCH_NEG(icode, ptr, dst, src) \
+	OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001);
+#define SWITCH_NEG(icode, ptr, dst, src) \
+		_SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))
+
+
+static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu)
+{
+	int err, i, z, gpr, tmp, playback, capture;
+	u32 ptr;
+	emu10k1_fx8010_code_t *icode;
+	emu10k1_fx8010_pcm_t *ipcm;
+	emu10k1_fx8010_control_gpr_t *controls, *ctl;
+	mm_segment_t seg;
+
+	spin_lock_init(&emu->fx8010.irq_lock);
+	INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
+
+	if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) {
+		kfree(icode);
+		return -ENOMEM;
+	}
+	if ((ipcm = snd_kcalloc(sizeof(emu10k1_fx8010_pcm_t), GFP_KERNEL)) == NULL) {
+		kfree(controls);
+		kfree(icode);
+		return -ENOMEM;
+	}
+	
+	/* clear free GPRs */
+	for (i = 0; i < 256; i++)
+		set_bit(i, &icode->gpr_valid);
+
+	/* clear TRAM data & address lines */
+	for (i = 0; i < 160; i++)
+		set_bit(i, &icode->tram_valid);
+
+	strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
+	ptr = 0; i = 0;
+	/* we have 10 inputs */
+	playback = SND_EMU10K1_INPUTS;
+	/* we have 6 playback channels and tone control doubles */
+	capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2);
+	gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
+	tmp = 0x88;	/* we need 4 temporary GPR */
+	/* from 0x8c to 0xff is the area for tone control */
+	
+	/* stop FX processor */
+	snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
+
+	/*
+	 *  Process FX Buses
+	 */
+	OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000);	/* S/PDIF left */
+	OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000);	/* S/PDIF right */
+
+	/* Raw S/PDIF PCM */
+	ipcm->substream = 0;
+	ipcm->channels = 2;
+	ipcm->tram_start = 0;
+	ipcm->buffer_size = (64 * 1024) / 2;
+	ipcm->gpr_size = gpr++;
+	ipcm->gpr_ptr = gpr++;
+	ipcm->gpr_count = gpr++;
+	ipcm->gpr_tmpcount = gpr++;
+	ipcm->gpr_trigger = gpr++;
+	ipcm->gpr_running = gpr++;
+	ipcm->etram[0] = 0;
+	ipcm->etram[1] = 1;
+
+	icode->gpr_map[gpr + 0] = 0xfffff000;
+	icode->gpr_map[gpr + 1] = 0xffff0000;
+	icode->gpr_map[gpr + 2] = 0x70000000;
+	icode->gpr_map[gpr + 3] = 0x00000007;
+	icode->gpr_map[gpr + 4] = 0x001f << 11;
+	icode->gpr_map[gpr + 5] = 0x001c << 11;
+	icode->gpr_map[gpr + 6] = (0x22  - 0x01) - 1;	/* skip at 01 to 22 */
+	icode->gpr_map[gpr + 7] = (0x22  - 0x06) - 1;	/* skip at 06 to 22 */
+	icode->gpr_map[gpr + 8] = 0x2000000 + (2<<11);
+	icode->gpr_map[gpr + 9] = 0x4000000 + (2<<11);
+	icode->gpr_map[gpr + 10] = 1<<11;
+	icode->gpr_map[gpr + 11] = (0x24 - 0x0a) - 1;	/* skip at 0a to 24 */
+	icode->gpr_map[gpr + 12] = 0;
+
+	/* if the trigger flag is not set, skip */
+	/* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000); 
+	/* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6));
+	/* if the running flag is set, we're running */
+	/* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000);
+	/* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004);
+	/* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */
+	/* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000);
+	/* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5));
+	/* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7));
+	/* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000);
+
+	/* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001);
+	/* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000);
+	/* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11));
+	/* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000);
+
+	/* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000);
+	/* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+	/* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+	/* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+	/* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2));
+
+	/* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000);
+	/* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+	/* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+	/* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+	/* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2));
+
+	/* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000);
+	/* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size));
+	/* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001);
+	/* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000);
+	/* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000);
+	
+	/* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000);
+	/* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+	/* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000);
+	/* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000);
+	/* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000);
+
+	/* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001);
+	/* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002);
+
+	/* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff);
+	/* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff);
+
+	/* 24: */
+	gpr += 13;
+
+	/* Wave Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME(icode, &ptr, playback + z, z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Wave Surround Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0);
+	gpr += 2;
+	
+	/* Wave Center/LFE Playback Volume */
+	OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
+	OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+	VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
+	VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0);
+
+	/* Wave Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z);
+		VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0);
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0);
+	gpr += 4;
+
+	/* Music Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Music Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Music Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z);
+		VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Music Capture Volume", gpr, 0);
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Music Capture Switch", gpr + 2, 0);
+	gpr += 4;
+
+	/* Surround Digital Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Surround Digital Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z);
+		VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Capture Volume", gpr, 0);
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Digital Capture Switch", gpr + 2, 0);
+	gpr += 4;
+
+	/* Center Playback Volume */
+	VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "Center Playback Volume", gpr++, 100);
+
+	/* LFE Playback Volume + Switch */
+	VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "LFE Playback Volume", gpr++, 100);
+
+	/*
+	 *  Process inputs
+	 */
+
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_AC97_L)|(1<<EXTIN_AC97_R))) {
+		/* AC'97 Playback Volume */
+		VOLUME_ADDIN(icode, &ptr, playback + 0, EXTIN_AC97_L, gpr); gpr++;
+		VOLUME_ADDIN(icode, &ptr, playback + 1, EXTIN_AC97_R, gpr); gpr++;
+		snd_emu10k1_init_stereo_control(controls + i++, "AC97 Playback Volume", gpr-2, 0);
+		/* AC'97 Capture Volume */
+		VOLUME_ADDIN(icode, &ptr, capture + 0, EXTIN_AC97_L, gpr); gpr++;
+		VOLUME_ADDIN(icode, &ptr, capture + 1, EXTIN_AC97_R, gpr); gpr++;
+		snd_emu10k1_init_stereo_control(controls + i++, "AC97 Capture Volume", gpr-2, 100);
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_SPDIF_CD_L)|(1<<EXTIN_SPDIF_CD_R))) {
+		/* IEC958 TTL Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_SPDIF_CD_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "IEC958 TTL Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* IEC958 TTL Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_SPDIF_CD_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "IEC958 TTL Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 TTL Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_ZOOM_L)|(1<<EXTIN_ZOOM_R))) {
+		/* Zoom Video Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_ZOOM_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* Zoom Video Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_ZOOM_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "Zoom Video Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_TOSLINK_L)|(1<<EXTIN_TOSLINK_R))) {
+		/* IEC958 Optical Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_TOSLINK_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Optical Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* IEC958 Optical Capture Volume */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_TOSLINK_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Optical Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Optical Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE1_L)|(1<<EXTIN_LINE1_R))) {
+		/* Line LiveDrive Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE1_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* Line LiveDrive Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE1_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line LiveDrive Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_COAX_SPDIF_L)|(1<<EXTIN_COAX_SPDIF_R))) {
+		/* IEC958 Coax Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_COAX_SPDIF_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Coaxial Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* IEC958 Coax Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_COAX_SPDIF_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Coaxial Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Coaxial Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE2_L)|(1<<EXTIN_LINE2_R))) {
+		/* Line LiveDrive Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE2_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Playback Volume", gpr, 0);
+		controls[i-1].id.index = 1;
+		gpr += 2;
+	
+		/* Line LiveDrive Capture Volume */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE2_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line LiveDrive Capture Switch", gpr + 2, 0);
+		controls[i-1].id.index = 1;
+		gpr += 4;
+	}
+
+	/*
+	 *  Process tone control
+	 */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), GPR(playback + 0), C_00000000, C_00000000); /* left */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), GPR(playback + 1), C_00000000, C_00000000); /* right */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), GPR(playback + 2), C_00000000, C_00000000); /* rear left */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), GPR(playback + 3), C_00000000, C_00000000); /* rear right */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), GPR(playback + 4), C_00000000, C_00000000); /* center */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), GPR(playback + 5), C_00000000, C_00000000); /* LFE */
+
+	ctl = &controls[i + 0];
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, "Tone Control - Bass");
+	ctl->vcount = 2;
+	ctl->count = 10;
+	ctl->min = 0;
+	ctl->max = 40;
+	ctl->value[0] = ctl->value[1] = 20;
+	ctl->translation = EMU10K1_GRP_TRANSLATION_BASS;
+	ctl = &controls[i + 1];
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, "Tone Control - Treble");
+	ctl->vcount = 2;
+	ctl->count = 10;
+	ctl->min = 0;
+	ctl->max = 40;
+	ctl->value[0] = ctl->value[1] = 20;
+	ctl->translation = EMU10K1_GRP_TRANSLATION_TREBLE;
+
+#define BASS_GPR	0x8c
+#define TREBLE_GPR	0x96
+
+	for (z = 0; z < 5; z++) {
+		int j;
+		for (j = 0; j < 2; j++) {
+			controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+			controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+		}
+	}
+	for (z = 0; z < 3; z++) {		/* front/rear/center-lfe */
+		int j, k, l, d;
+		for (j = 0; j < 2; j++) {	/* left/right */
+			k = 0xa0 + (z * 8) + (j * 4);
+			l = 0xd0 + (z * 8) + (j * 4);
+			d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+			OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j));
+			OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j));
+			OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j));
+			OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j));
+			OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j));
+			OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000);
+
+			OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j));
+			OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j));
+			OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j));
+			OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j));
+			OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j));
+			OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010);
+
+			OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000);
+
+			if (z == 2)	/* center */
+				break;
+		}
+	}
+	i += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+	for (z = 0; z < 6; z++) {
+		SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+		SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+		SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+		OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+	}
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
+	gpr += 2;
+
+	/*
+	 *  Process outputs
+	 */
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_L)|(1<<EXTOUT_AC97_R))) {
+		/* AC'97 Playback Volume */
+
+		for (z = 0; z < 2; z++)
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), C_00000000, C_00000000);
+	}
+
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_TOSLINK_L)|(1<<EXTOUT_TOSLINK_R))) {
+		/* IEC958 Optical Raw Playback Switch */
+
+		for (z = 0; z < 2; z++) {
+			SWITCH(icode, &ptr, tmp + 0, 8 + z, gpr + z);
+			SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+			SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_TOSLINK_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+	 		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#endif
+		}
+
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Optical Raw Playback Switch", gpr, 0);
+		gpr += 2;
+	}
+
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_HEADPHONE_L)|(1<<EXTOUT_HEADPHONE_R))) {
+		/* Headphone Playback Volume */
+
+		for (z = 0; z < 2; z++) {
+			SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4 + z, gpr + 2 + z);
+			SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 2 + z);
+			SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+			OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+			VOLUME_OUT(icode, &ptr, EXTOUT_HEADPHONE_L + z, tmp + 0, gpr + z);
+		}
+
+		snd_emu10k1_init_stereo_control(controls + i++, "Headphone Playback Volume", gpr + 0, 0);
+		controls[i-1].id.index = 1;	/* AC'97 can have also Headphone control */
+		snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone Center Playback Switch", gpr + 2, 0);
+		controls[i-1].id.index = 1;
+		snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone LFE Playback Switch", gpr + 3, 0);
+		controls[i-1].id.index = 1;
+
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_REAR_L)|(1<<EXTOUT_REAR_R)))
+		for (z = 0; z < 2; z++)
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+
+	if (emu->fx8010.extout_mask & (1<<EXTOUT_CENTER)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+#else
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+#endif
+	}
+
+	if (emu->fx8010.extout_mask & (1<<EXTOUT_LFE)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+#else
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+#endif
+	}
+	
+#ifndef EMU10K1_CAPTURE_DIGITAL_OUT
+	for (z = 0; z < 2; z++)
+ 		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(capture + z), C_00000000, C_00000000);
+#endif
+	
+	if (emu->fx8010.extout_mask & (1<<EXTOUT_MIC_CAP))
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000);
+
+	if (gpr > tmp) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+	if (i > SND_EMU10K1_GPR_CONTROLS) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+	
+	/* clear remaining instruction memory */
+	while (ptr < 0x200)
+		OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000);
+
+	if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0)
+		goto __err;
+	seg = snd_enter_user();
+	icode->gpr_add_control_count = i;
+	icode->gpr_add_controls = controls;
+	err = snd_emu10k1_icode_poke(emu, icode);
+	snd_leave_user(seg);
+	if (err >= 0)
+		err = snd_emu10k1_ipcm_poke(emu, ipcm);
+      __err:
+	kfree(ipcm);
+	kfree(controls);
+	kfree(icode);
+	return err;
+}
+
+int __devinit snd_emu10k1_init_efx(emu10k1_t *emu)
+{
+	if (emu->audigy)
+		return _snd_emu10k1_audigy_init_efx(emu);
+	else
+		return _snd_emu10k1_init_efx(emu);
+}
+
+void snd_emu10k1_free_efx(emu10k1_t *emu)
+{
+	/* stop processor */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP);
+}
+
+#if 0 // FIXME: who use them?
+int snd_emu10k1_fx8010_tone_control_activate(emu10k1_t *emu, int output)
+{
+	snd_runtime_check(output >= 0 && output < 6, return -EINVAL);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1);
+	return 0;
+}
+
+int snd_emu10k1_fx8010_tone_control_deactivate(emu10k1_t *emu, int output)
+{
+	snd_runtime_check(output >= 0 && output < 6, return -EINVAL);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0);
+	return 0;
+}
+#endif
+
+int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size)
+{
+	u8 size_reg = 0;
+
+	/* size is in samples */
+	if (size != 0) {
+		size = (size - 1) >> 13;
+
+		while (size) {
+			size >>= 1;
+			size_reg++;
+		}
+		size = 0x2000 << size_reg;
+	}
+	if (emu->fx8010.etram_size == size)
+		return 0;
+	spin_lock_irq(&emu->emu_lock);
+	outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+	spin_unlock_irq(&emu->emu_lock);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+	snd_emu10k1_ptr_write(emu, TCBS, 0, 0);
+	if (emu->fx8010.etram_pages != NULL) {
+		snd_free_pci_pages(emu->pci, emu->fx8010.etram_size * 2, emu->fx8010.etram_pages, emu->fx8010.etram_pages_dmaaddr);
+		emu->fx8010.etram_pages = NULL;
+		emu->fx8010.etram_size = 0;
+	}
+
+	if (size > 0) {
+		emu->fx8010.etram_pages = snd_malloc_pci_pages(emu->pci, size * 2, &emu->fx8010.etram_pages_dmaaddr);
+		if (emu->fx8010.etram_pages == NULL)
+			return -ENOMEM;
+		memset(emu->fx8010.etram_pages, 0, size * 2);
+		snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages_dmaaddr);
+		snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
+		spin_lock_irq(&emu->emu_lock);
+		outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+		spin_unlock_irq(&emu->emu_lock);	
+	}
+
+	emu->fx8010.etram_size = size;
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_open(snd_hwdep_t * hw, struct file *file)
+{
+	return 0;
+}
+
+static void copy_string(char *dst, char *src, char *null, int idx)
+{
+	if (src == NULL)
+		sprintf(dst, "%s %02X", null, idx);
+	else
+		strcpy(dst, src);
+}
+
+static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info)
+{
+	char **fxbus, **extin, **extout;
+	unsigned short fxbus_mask, extin_mask, extout_mask;
+	int res;
+
+	memset(info, 0, sizeof(info));
+	info->card = emu->card_type;
+	info->internal_tram_size = emu->fx8010.itram_size;
+	info->external_tram_size = emu->fx8010.etram_size;
+	fxbus = fxbuses;
+	extin = emu->audigy ? audigy_ins : creative_ins;
+	extout = emu->audigy ? audigy_outs : creative_outs;
+	fxbus_mask = emu->fx8010.fxbus_mask;
+	extin_mask = emu->fx8010.extin_mask;
+	extout_mask = emu->fx8010.extout_mask;
+	for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
+		copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+		copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
+		copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+	}
+	for (res = 16; res < 32; res++, extout++)
+		copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+	info->gpr_controls = emu->fx8010.gpr_count;
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, hw->private_data, return -ENXIO);
+	emu10k1_fx8010_info_t info;
+	emu10k1_fx8010_code_t *icode;
+	emu10k1_fx8010_pcm_t *ipcm;
+	unsigned int addr;
+	int res;
+	
+	switch (cmd) {
+	case SNDRV_EMU10K1_IOCTL_INFO:
+		if ((res = snd_emu10k1_fx8010_info(emu, &info)) < 0)
+			return res;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_CODE_POKE:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL);
+		if (icode == NULL)
+			return -ENOMEM;
+		if (copy_from_user(icode, (void *)arg, sizeof(*icode))) {
+			kfree(icode);
+			return -EFAULT;
+		}
+		res = snd_emu10k1_icode_poke(emu, icode);
+		kfree(icode);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_CODE_PEEK:
+		icode = (emu10k1_fx8010_code_t *)snd_kcalloc(sizeof(*icode), GFP_KERNEL);
+		if (icode == NULL)
+			return -ENOMEM;
+		res = snd_emu10k1_icode_peek(emu, icode);
+		if (res == 0 && copy_to_user((void *)arg, icode, sizeof(*icode))) {
+			kfree(icode);
+			return -EFAULT;
+		}
+		kfree(icode);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_PCM_POKE:
+		if (emu->audigy)
+			return -EINVAL;
+		ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL);
+		if (ipcm == NULL)
+			return -ENOMEM;
+		res = snd_emu10k1_ipcm_poke(emu, ipcm);
+		kfree(ipcm);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_PCM_PEEK:
+		if (emu->audigy)
+			return -EINVAL;
+		ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL);
+		if (ipcm == NULL)
+			return -ENOMEM;
+		res = snd_emu10k1_ipcm_peek(emu, ipcm);
+		if (res == 0 && copy_to_user((void *)arg, ipcm, sizeof(*ipcm))) {
+			kfree(ipcm);
+			return -EFAULT;
+		}
+		kfree(ipcm);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_TRAM_SETUP:
+		if (emu->audigy)
+			return -EINVAL;
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (get_user(addr, (unsigned int *)arg))
+			return -EFAULT;
+		down(&emu->fx8010.lock);
+		res = snd_emu10k1_fx8010_tram_setup(emu, addr);
+		up(&emu->fx8010.lock);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_STOP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_CONTINUE:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC);
+		udelay(10);
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_SINGLE_STEP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (get_user(addr, (unsigned int *)arg))
+			return -EFAULT;
+		if (addr > 0x1ff)
+			return -EINVAL;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
+		udelay(10);
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_DBG_READ:
+		if (emu->audigy)
+			addr = snd_emu10k1_ptr_read(emu, A_DBG, 0);
+		else
+			addr = snd_emu10k1_ptr_read(emu, DBG, 0);
+		if (put_user(addr, (unsigned int *)arg))
+			return -EFAULT;
+		return 0;
+	}
+	return -ENOTTY;
+}
+
+static int snd_emu10k1_fx8010_release(snd_hwdep_t * hw, struct file *file)
+{
+	return 0;
+}
+
+int __devinit snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep)
+{
+	snd_hwdep_t *hw;
+	int err;
+	
+	if (rhwdep)
+		*rhwdep = NULL;
+	if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0)
+		return err;
+	strcpy(hw->name, "EMU10K1 (FX8010)");
+	hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
+	hw->ops.open = snd_emu10k1_fx8010_open;
+	hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
+	hw->ops.release = snd_emu10k1_fx8010_release;
+	hw->private_data = emu;
+	if (rhwdep)
+		*rhwdep = hw;
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/emumixer.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumixer.c
--- linux/sound/pci/emu10k1/emumixer.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumixer.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,549 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                   Takashi Iwai <tiwai@suse.de>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / mixer routines
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define chip_t emu10k1_t
+
+static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_emu10k1_spdif_get(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->private_value;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return 0;
+}
+
+static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol,
+				      snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+        return 0;
+}
+
+static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->private_value, change;
+	unsigned int val;
+	unsigned long flags;
+
+	val = (ucontrol->value.iec958.status[0] << 0) |
+	      (ucontrol->value.iec958.status[1] << 8) |
+	      (ucontrol->value.iec958.status[2] << 16) |
+	      (ucontrol->value.iec958.status[3] << 24);
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	change = val != emu->spdif_bits[idx];
+	if (change) {
+		snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
+		emu->spdif_bits[idx] = val;
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+        name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+        info:           snd_emu10k1_spdif_info,
+        get:            snd_emu10k1_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_emu10k1_spdif_control =
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+        name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+        info:           snd_emu10k1_spdif_info,
+        get:            snd_emu10k1_spdif_get,
+        put:            snd_emu10k1_spdif_put
+};
+
+
+static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route)
+{
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+				      snd_emu10k1_compose_audigy_fxrt1(route));
+		snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+				      snd_emu10k1_compose_audigy_fxrt2(route));
+	} else {
+		snd_emu10k1_ptr_write(emu, FXRT, voice,
+				      snd_emu10k1_compose_send_routing(route));
+	}
+}
+
+static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char *volume)
+{
+	snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]);
+	snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]);
+	snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
+	snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
+	if (emu->audigy) {
+		unsigned int val = ((unsigned int)volume[4] << 24) |
+			((unsigned int)volume[5] << 16) |
+			((unsigned int)volume[6] << 8) |
+			(unsigned int)volume[7];
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
+	}
+}
+
+static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = emu->audigy ? 3*8 : 3*4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+	return 0;
+}
+
+static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int voice, idx;
+	int num_efx = emu->audigy ? 8 : 4;
+	int mask = emu->audigy ? 0x3f : 0x0f;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (voice = 0; voice < 3; voice++)
+		for (idx = 0; idx < num_efx; idx++)
+			ucontrol->value.integer.value[(voice * num_efx) + idx] = 
+				mix->send_routing[voice][idx] & mask;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return 0;
+}
+
+static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int change = 0, voice, idx, val;
+	int num_efx = emu->audigy ? 8 : 4;
+	int mask = emu->audigy ? 0x3f : 0x0f;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (voice = 0; voice < 3; voice++)
+		for (idx = 0; idx < num_efx; idx++) {
+			val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
+			if (mix->send_routing[voice][idx] != val) {
+				mix->send_routing[voice][idx] = val;
+				change = 1;
+			}
+		}	
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+					    &mix->send_routing[1][0]);
+			update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+					    &mix->send_routing[2][0]);
+		} else if (mix->epcm->voices[0]) {
+			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+					    &mix->send_routing[0][0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_send_routing_control =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+        name:           "EMU10K1 PCM Send Routing",
+        info:           snd_emu10k1_send_routing_info,
+        get:            snd_emu10k1_send_routing_get,
+        put:            snd_emu10k1_send_routing_put
+};
+
+static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = emu->audigy ? 3*8 : 3*4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int idx;
+	int num_efx = emu->audigy ? 8 : 4;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3*num_efx; idx++)
+		ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return 0;
+}
+
+static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int change = 0, idx, val;
+	int num_efx = emu->audigy ? 8 : 4;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3*num_efx; idx++) {
+		val = ucontrol->value.integer.value[idx] & 255;
+		if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
+			mix->send_volume[idx/num_efx][idx%num_efx] = val;
+			change = 1;
+		}
+	}
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+						   &mix->send_volume[1][0]);
+			update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
+						   &mix->send_volume[2][0]);
+		} else if (mix->epcm->voices[0]) {
+			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+						   &mix->send_volume[0][0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_send_volume_control =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+        name:           "EMU10K1 PCM Send Volume",
+        info:           snd_emu10k1_send_volume_info,
+        get:            snd_emu10k1_send_volume_get,
+        put:            snd_emu10k1_send_volume_put
+};
+
+static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 3;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xffff;
+	return 0;
+}
+
+static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol,
+                                snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int idx;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3; idx++)
+		ucontrol->value.integer.value[idx] = mix->attn[idx];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return 0;
+}
+
+static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	int change = 0, idx, val;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3; idx++) {
+		val = ucontrol->value.integer.value[idx] & 0xffff;
+		if (mix->attn[idx] != val) {
+			mix->attn[idx] = val;
+			change = 1;
+		}
+	}
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
+		} else if (mix->epcm->voices[0]) {
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_attn_control =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+        name:           "EMU10K1 PCM Volume",
+        info:           snd_emu10k1_attn_info,
+        get:            snd_emu10k1_attn_get,
+        put:            snd_emu10k1_attn_put
+};
+
+static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_emu10k1_shared_spdif_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 0 : 1;
+        return 0;
+}
+
+static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int reg, val;
+	int change;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	reg = inl(emu->port + HCFG);
+	val = ucontrol->value.integer.value[0] & 1 ? 0 : HCFG_GPOUT0;
+	change = (reg & HCFG_GPOUT0) != val;
+	reg &= ~HCFG_GPOUT0;
+	reg |= val;
+	outl(reg | val, emu->port + HCFG);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_shared_spdif =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		"SB Live Analog/Digital Output Jack",
+	info:		snd_emu10k1_shared_spdif_info,
+	get:		snd_emu10k1_shared_spdif_get,
+	put:		snd_emu10k1_shared_spdif_put
+};
+
+#if 0 // XXX: not working yet..
+/*
+ * Audigy analog / digital switches
+ */
+static int audigy_output_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int audigy_output_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = (unsigned int)kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & mask ? 0 : 1;
+        return 0;
+}
+
+static int audigy_output_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	unsigned long flags;
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = (unsigned int)kcontrol->private_value;
+	unsigned int reg, oreg;
+	int change;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	reg = oreg = inl(emu->port + A_IOCFG);
+	reg &= ~mask;
+	reg |= ucontrol->value.integer.value[0] & 1 ? 0 : mask;
+	change = (reg != oreg);
+	if (change)
+		outl(reg, emu->port + A_IOCFG);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+        return change;
+}
+
+static snd_kcontrol_new_t audigy_output_analog =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		"Audigy Analog Output Switch",
+	info:		audigy_output_info,
+	get:		audigy_output_get,
+	put:		audigy_output_put,
+	private_value:	0x40,
+};
+
+static snd_kcontrol_new_t audigy_output_digital =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		"Audigy Digital Output Switch",
+	info:		audigy_output_info,
+	get:		audigy_output_get,
+	put:		audigy_output_put,
+	private_value:	0x04,
+};
+#endif // XXX
+
+
+/*
+ */
+static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return);
+	emu->ac97 = NULL;
+}
+
+int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
+{
+	ac97_t ac97;
+	int err, pcm, idx;
+	snd_kcontrol_t *kctl;
+	snd_card_t *card = emu->card;
+
+	if (!emu->APS) {
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_emu10k1_ac97_write;
+		ac97.read = snd_emu10k1_ac97_read;
+		ac97.private_data = emu;
+		ac97.private_free = snd_emu10k1_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(emu->card, &ac97, &emu->ac97)) < 0)
+			return err;
+	} else {
+		strcpy(emu->card->mixername, "EMU APS");
+	}
+
+	for (pcm = 0; pcm < 32; pcm++) {
+		emu10k1_pcm_mixer_t *mix;
+		int v;
+		
+		mix = &emu->pcm_mixer[pcm];
+		mix->epcm = NULL;
+
+		if ((kctl = mix->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)mix;
+		kctl->id.index = pcm;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		for (v = 0; v < 4; v++)
+			mix->send_routing[0][v] = 
+				mix->send_routing[1][v] = 
+				mix->send_routing[2][v] = v;
+		
+		if ((kctl = mix->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)mix;
+		kctl->id.index = pcm;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+		mix->send_volume[0][0] = mix->send_volume[0][1] =
+		mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+		
+		if ((kctl = mix->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)mix;
+		kctl->id.index = pcm;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+	}
+	
+	for (idx = 0; idx < 3; idx++) {
+		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = idx;
+		kctl->id.index = idx;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = idx;
+		kctl->id.index = idx;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+	}
+
+	if (emu->audigy) {
+#if 0 // XXX
+		if ((kctl = snd_ctl_new1(&audigy_output_analog, emu)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		if ((kctl = snd_ctl_new1(&audigy_output_digital, emu)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+#endif // XXX
+	} else {
+		if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+	}
+
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/emumpu401.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumpu401.c
--- linux/sound/pci/emu10k1/emumpu401.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumpu401.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,376 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of EMU10K1 MPU-401 in UART mode
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define EMU10K1_MIDI_MODE_INPUT		(1<<0)
+#define EMU10K1_MIDI_MODE_OUTPUT	(1<<1)
+
+static inline unsigned char mpu401_read(emu10k1_t *emu, emu10k1_midi_t *mpu, int idx)
+{
+	if (emu->audigy)
+		return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
+	else
+		return inb(emu->port + mpu->port + idx);
+}
+
+static inline void mpu401_write(emu10k1_t *emu, emu10k1_midi_t *mpu, int data, int idx)
+{
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
+	else
+		outb(data, emu->port + mpu->port + idx);
+}
+
+#define mpu401_write_data(emu, mpu, data)	mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data)	mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu)		mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu)		mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+static void mpu401_clear_rx(emu10k1_t *emu, emu10k1_midi_t *mpu)
+{
+	int timeout = 100000;
+	for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+		mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+	if (timeout <= 0)
+		snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+ */
+
+static void do_emu10k1_midi_interrupt(emu10k1_t *emu, emu10k1_midi_t *midi, unsigned int status)
+{
+	unsigned char byte;
+
+	if (midi->rmidi == NULL) {
+		snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+		return;
+	}
+
+	spin_lock(&midi->input_lock);
+	if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+		if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+			mpu401_clear_rx(emu, midi);
+		} else {
+			byte = mpu401_read_data(emu, midi);
+			spin_unlock(&midi->input_lock);
+			if (midi->substream_input)
+				snd_rawmidi_receive(midi->substream_input, &byte, 1);
+			spin_lock(&midi->input_lock);
+		}
+	}
+	spin_unlock(&midi->input_lock);
+
+	spin_lock(&midi->output_lock);
+	if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+		if (midi->substream_output &&
+		    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+			mpu401_write_data(emu, midi, byte);
+		} else {
+			snd_emu10k1_intr_disable(emu, midi->tx_enable);
+		}
+	}
+	spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1_midi_interrupt(emu10k1_t *emu, unsigned int status)
+{
+	do_emu10k1_midi_interrupt(emu, &emu->midi, status);
+}
+
+static void snd_emu10k1_midi_interrupt2(emu10k1_t *emu, unsigned int status)
+{
+	do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
+}
+
+static void snd_emu10k1_midi_cmd(emu10k1_t * emu, emu10k1_midi_t *midi, unsigned char cmd, int ack)
+{
+	unsigned long flags;
+	int timeout, ok;
+
+	spin_lock_irqsave(&midi->input_lock, flags);
+	mpu401_write_data(emu, midi, 0x00);
+	/* mpu401_clear_rx(emu, midi); */
+
+	mpu401_write_cmd(emu, midi, cmd);
+	if (ack) {
+		ok = 0;
+		timeout = 10000;
+		while (!ok && timeout-- > 0) {
+			if (mpu401_input_avail(emu, midi)) {
+				if (mpu401_read_data(emu, midi) == MPU401_ACK)
+					ok = 1;
+			}
+		}
+		if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+			ok = 1;
+	} else {
+		ok = 1;
+	}
+	spin_unlock_irqrestore(&midi->input_lock, flags);
+	if (!ok)
+		snd_printk("midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+			   cmd, emu->port,
+			   mpu401_read_stat(emu, midi),
+			   mpu401_read_data(emu, midi));
+}
+
+static int snd_emu10k1_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	emu10k1_t *emu;
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	snd_assert(emu, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
+	midi->substream_input = substream;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1);
+		snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	emu10k1_t *emu;
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	snd_assert(emu, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
+	midi->substream_output = substream;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1);
+		snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	emu10k1_t *emu;
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	snd_assert(emu, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	snd_emu10k1_intr_disable(emu, midi->rx_enable);
+	midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
+	midi->substream_input = NULL;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	emu10k1_t *emu;
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	snd_assert(emu, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	snd_emu10k1_intr_disable(emu, midi->tx_enable);
+	midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
+	midi->substream_output = NULL;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static void snd_emu10k1_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	emu10k1_t *emu;
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+	emu = midi->emu;
+	snd_assert(emu, return);
+
+	if (up)
+		snd_emu10k1_intr_enable(emu, midi->rx_enable);
+	else
+		snd_emu10k1_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	emu10k1_t *emu;
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	snd_assert(emu, return);
+
+	if (up) {
+		int max = 4;
+		unsigned char byte;
+	
+		/* try to send some amount of bytes here before interrupts */
+		spin_lock_irqsave(&midi->output_lock, flags);
+		while (max > 0) {
+			if (mpu401_output_ready(emu, midi)) {
+				if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
+				    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					/* no more data */
+					spin_unlock_irqrestore(&midi->output_lock, flags);
+					return;
+				}
+				mpu401_write_data(emu, midi, byte);
+				max--;
+			} else {
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&midi->output_lock, flags);
+		snd_emu10k1_intr_enable(emu, midi->tx_enable);
+	} else {
+		snd_emu10k1_intr_disable(emu, midi->tx_enable);
+	}
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_emu10k1_midi_output =
+{
+	open:		snd_emu10k1_midi_output_open,
+	close:		snd_emu10k1_midi_output_close,
+	trigger:	snd_emu10k1_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_emu10k1_midi_input =
+{
+	open:		snd_emu10k1_midi_input_open,
+	close:		snd_emu10k1_midi_input_close,
+	trigger:	snd_emu10k1_midi_input_trigger,
+};
+
+static void snd_emu10k1_midi_free(snd_rawmidi_t *rmidi)
+{
+	emu10k1_midi_t *midi = (emu10k1_midi_t *)rmidi->private_data;
+	midi->interrupt = NULL;
+	midi->rmidi = NULL;
+}
+
+static int __devinit emu10k1_midi_init(emu10k1_t *emu, emu10k1_midi_t *midi, int device, char *name)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+		return err;
+	midi->emu = emu;
+	spin_lock_init(&midi->open_lock);
+	spin_lock_init(&midi->input_lock);
+	spin_lock_init(&midi->output_lock);
+	strcpy(rmidi->name, name);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = midi;
+	rmidi->private_free = snd_emu10k1_midi_free;
+	midi->rmidi = rmidi;
+	return 0;
+}
+
+int __devinit snd_emu10k1_midi(emu10k1_t *emu)
+{
+	emu10k1_midi_t *midi = &emu->midi;
+	int err;
+
+	if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_MIDITXENABLE;
+	midi->rx_enable = INTE_MIDIRXENABLE;
+	midi->port = MUDATA;
+	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+	midi->interrupt = snd_emu10k1_midi_interrupt;
+	return 0;
+}
+
+int __devinit snd_emu10k1_audigy_midi(emu10k1_t *emu)
+{
+	emu10k1_midi_t *midi;
+	int err;
+
+	midi = &emu->midi;
+	if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_MIDITXENABLE;
+	midi->rx_enable = INTE_MIDIRXENABLE;
+	midi->port = A_MUDATA1;
+	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+	midi->interrupt = snd_emu10k1_midi_interrupt;
+
+	midi = &emu->midi2;
+	if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_A_MIDITXENABLE2;
+	midi->rx_enable = INTE_A_MIDIRXENABLE2;
+	midi->port = A_MUDATA2;
+	midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
+	midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
+	midi->interrupt = snd_emu10k1_midi_interrupt2;
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/emupcm.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emupcm.c
--- linux/sound/pci/emu10k1/emupcm.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emupcm.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1135 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / PCM routines
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define chip_t emu10k1_t
+
+static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice)
+{
+	emu10k1_pcm_t *epcm;
+
+	if ((epcm = voice->epcm) == NULL)
+		return;
+	if (epcm->substream == NULL)
+		return;
+#if 0
+	printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+			epcm->substream->runtime->hw->pointer(emu, epcm->substream),
+			snd_pcm_lib_period_bytes(epcm->substream),
+			snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+	snd_pcm_period_elapsed(epcm->substream);
+}
+
+static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status)
+{
+#if 0
+	if (status & IPR_ADCBUFHALFFULL) {
+		if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_substream);
+}
+
+static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status)
+{
+#if 0
+	if (status & IPR_MICBUFHALFFULL) {
+		if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_mic_substream);
+}
+
+static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status)
+{
+#if 0
+	if (status & IPR_EFXBUFHALFFULL) {
+		if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
+}
+
+static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices)
+{
+	int err;
+
+	if (epcm->voices[1] != NULL && voices < 2) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+		epcm->voices[1] = NULL;
+	}
+	if (voices == 1 && epcm->voices[0] != NULL)
+		return 0;		/* already allocated */
+	if (voices == 2 && epcm->voices[0] != NULL && epcm->voices[1] != NULL)
+		return 0;
+	if (voices > 1) {
+		if (epcm->voices[0] != NULL && epcm->voices[1] == NULL) {
+			snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
+			epcm->voices[0] = NULL;
+		}
+	}
+	err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices > 1, &epcm->voices[0]);
+	if (err < 0)
+		return err;
+	epcm->voices[0]->epcm = epcm;
+	if (voices > 1) {
+		epcm->voices[1] = &epcm->emu->voices[epcm->voices[0]->number + 1];
+		epcm->voices[1]->epcm = epcm;
+	}
+	if (epcm->extra == NULL) {
+		err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, 0, &epcm->extra);
+		if (err < 0) {
+			// printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame);
+			snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
+			epcm->voices[0] = NULL;
+			if (epcm->voices[1])
+				snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+			epcm->voices[1] = NULL;
+			return err;
+		}
+		epcm->extra->epcm = epcm;
+		epcm->extra->interrupt = snd_emu10k1_pcm_interrupt;
+	}
+	return 0;
+}
+
+static unsigned int capture_period_sizes[31] = {
+	384,	448,	512,	640,
+	384*2,	448*2,	512*2,	640*2,
+	384*4,	448*4,	512*4,	640*4,
+	384*8,	448*8,	512*8,	640*8,
+	384*16,	448*16,	512*16,	640*16,
+	384*32,	448*32,	512*32,	640*32,
+	384*64,	448*64,	512*64,	640*64,
+	384*128,448*128,512*128
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = {
+	count: 31,
+	list: capture_period_sizes,
+	mask: 0
+};
+
+static unsigned int capture_rates[8] = {
+	8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = {
+	count: 8,
+	list: capture_rates,
+	mask: 0
+};
+
+static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:	return ADCCR_SAMPLERATE_8;
+	case 11025:	return ADCCR_SAMPLERATE_11;
+	case 16000:	return ADCCR_SAMPLERATE_16;
+	case 22050:	return ADCCR_SAMPLERATE_22;
+	case 24000:	return ADCCR_SAMPLERATE_24;
+	case 32000:	return ADCCR_SAMPLERATE_32;
+	case 44100:	return ADCCR_SAMPLERATE_44;
+	case 48000:	return ADCCR_SAMPLERATE_48;
+	default:
+			snd_BUG();
+			return ADCCR_SAMPLERATE_8;
+	}
+}
+
+static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:	return A_ADCCR_SAMPLERATE_8;
+	case 11025:	return A_ADCCR_SAMPLERATE_11;
+	case 12000:	return A_ADCCR_SAMPLERATE_12; /* really supported? */
+	case 16000:	return ADCCR_SAMPLERATE_16;
+	case 22050:	return ADCCR_SAMPLERATE_22;
+	case 24000:	return ADCCR_SAMPLERATE_24;
+	case 32000:	return ADCCR_SAMPLERATE_32;
+	case 44100:	return ADCCR_SAMPLERATE_44;
+	case 48000:	return ADCCR_SAMPLERATE_48;
+	default:
+			snd_BUG();
+			return A_ADCCR_SAMPLERATE_8;
+	}
+}
+
+static unsigned int emu10k1_calc_pitch_target(unsigned int rate)
+{
+	unsigned int pitch_target;
+
+	pitch_target = (rate << 8) / 375;
+	pitch_target = (pitch_target >> 1) + (pitch_target & 1);
+	return pitch_target;
+}
+
+#define PITCH_48000 0x00004000
+#define PITCH_96000 0x00008000
+#define PITCH_85000 0x00007155
+#define PITCH_80726 0x00006ba2
+#define PITCH_67882 0x00005a82
+#define PITCH_57081 0x00004c1c
+
+static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
+{
+	if (pitch_target == PITCH_48000)
+		return CCCA_INTERPROM_0;
+	else if (pitch_target < PITCH_48000)
+		return CCCA_INTERPROM_1;
+	else if (pitch_target >= PITCH_96000)
+		return CCCA_INTERPROM_0;
+	else if (pitch_target >= PITCH_85000)
+		return CCCA_INTERPROM_6;
+	else if (pitch_target >= PITCH_80726)
+		return CCCA_INTERPROM_5;
+	else if (pitch_target >= PITCH_67882)
+		return CCCA_INTERPROM_4;
+	else if (pitch_target >= PITCH_57081)
+		return CCCA_INTERPROM_3;
+	else  
+		return CCCA_INTERPROM_2;
+}
+
+
+static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
+				       int master, int extra,
+				       emu10k1_voice_t *evoice,
+				       unsigned int start_addr,
+				       unsigned int end_addr)
+{
+	snd_pcm_substream_t *substream = evoice->epcm->substream;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number];
+	unsigned int silent_page, tmp;
+	int voice, stereo, w_16;
+	unsigned char attn, send_amount[8];
+	unsigned char send_routing[8];
+	unsigned long flags;
+	unsigned int pitch_target;
+
+	voice = evoice->number;
+	stereo = runtime->channels == 2;
+	w_16 = snd_pcm_format_width(runtime->format) == 16;
+
+	if (!extra && stereo) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+	if (w_16) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+
+	/* volume parameters */
+	if (extra) {
+		attn = 0;
+		memset(send_routing, 0, sizeof(send_routing));
+		send_routing[0] = 0;
+		send_routing[1] = 1;
+		send_routing[2] = 2;
+		send_routing[3] = 3;
+		memset(send_amount, 0, sizeof(send_amount));
+	} else {
+		tmp = stereo ? (master ? 1 : 2) : 0;
+		memcpy(send_routing, &mix->send_routing[tmp][0], 8);
+		memcpy(send_amount, &mix->send_volume[tmp][0], 8);
+	}
+
+	if (master) {
+		unsigned int ccis = stereo ? 28 : 30;
+		if (w_16)
+			ccis *= 2;
+		evoice->epcm->ccca_start_addr = start_addr + ccis;
+		if (extra) {
+			start_addr += ccis;
+			end_addr += ccis;
+		}
+		if (stereo && !extra) {
+			snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
+			snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK);
+		} else {
+			snd_emu10k1_ptr_write(emu, CPF, voice, 0);
+		}
+	}
+
+	// setup routing
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+				      ((unsigned int)send_routing[3] << 24) |
+				      ((unsigned int)send_routing[2] << 16) |
+				      ((unsigned int)send_routing[1] << 8) |
+				      (unsigned int)send_routing[0]);
+		snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+				      ((unsigned int)send_routing[7] << 24) |
+				      ((unsigned int)send_routing[6] << 16) |
+				      ((unsigned int)send_routing[5] << 8) |
+				      (unsigned int)send_routing[4]);
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
+				      ((unsigned int)send_amount[4] << 24) |
+				      ((unsigned int)send_amount[5] << 16) |
+				      ((unsigned int)send_amount[6] << 8) |
+				      (unsigned int)send_amount[7]);
+	} else
+		snd_emu10k1_ptr_write(emu, FXRT, voice,
+				      snd_emu10k1_compose_send_routing(send_routing));
+	// Stop CA
+	// Assumption that PT is already 0 so no harm overwriting
+	snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
+	snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
+	snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
+	pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+	snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr |
+			      emu10k1_select_interprom(pitch_target) |
+			      (w_16 ? 0 : CCCA_8BITSELECT));
+	// Clear filter delay memory
+	snd_emu10k1_ptr_write(emu, Z1, voice, 0);
+	snd_emu10k1_ptr_write(emu, Z2, voice, 0);
+	// invalidate maps
+	silent_page = ((unsigned int)emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK;
+	snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
+	snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
+	// modulation envelope
+	snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
+	snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
+	snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
+	snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000);
+	snd_emu10k1_ptr_write(emu, FMMOD, voice, 0);
+	snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
+	snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
+	snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
+	// volume envelope
+	snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
+	snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
+	// filter envelope
+	snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
+	// pitch envelope
+	snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
+
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream,
+					  snd_pcm_hw_params_t * hw_params)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	int err;
+
+	if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+		return err;
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0) {	/* change */
+		snd_util_memblk_t *memblk;
+		if (epcm->memblk != NULL)
+			snd_emu10k1_free_pages(emu, epcm->memblk);
+		memblk = snd_emu10k1_alloc_pages(emu, runtime->dma_addr, runtime->dma_bytes);
+		if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) {
+			epcm->start_addr = 0;
+			return -ENOMEM;
+		}
+		epcm->start_addr = ((emu10k1_memblk_t *)memblk)->mapped_page << PAGE_SHIFT;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	if (epcm->extra) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+		epcm->extra = NULL;
+	}
+	if (epcm->voices[1]) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+		epcm->voices[1] = NULL;
+	}
+	if (epcm->voices[0]) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
+		epcm->voices[0] = NULL;
+	}
+	if (epcm->memblk) {
+		snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = NULL;
+		epcm->start_addr = 0;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	unsigned int start_addr, end_addr;
+
+	start_addr = epcm->start_addr;
+	end_addr = snd_pcm_lib_period_bytes(substream);
+	if (runtime->channels == 2)
+		end_addr >>= 1;
+	end_addr += start_addr;
+	snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+				   start_addr, end_addr);
+	end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+	snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+				   start_addr, end_addr);
+	if (epcm->voices[1])
+		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
+					   start_addr, end_addr);
+	return 0;
+}
+
+static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	int idx;
+
+	snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+	switch (epcm->type) {
+	case CAPTURE_AC97ADC:
+		snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+		break;
+	case CAPTURE_EFX:
+		snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+		break;
+	default:
+		break;
+	}	
+	snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr);
+	epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream);
+	epcm->capture_bs_val = 0;
+	for (idx = 0; idx < 31; idx++) {
+		if (capture_period_sizes[idx] == epcm->capture_bufsize) {
+			epcm->capture_bs_val = idx + 1;
+			break;
+		}
+	}
+	if (epcm->capture_bs_val == 0) {
+		snd_BUG();
+		epcm->capture_bs_val++;
+	}
+	if (epcm->type == CAPTURE_AC97ADC) {
+		epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
+		if (runtime->channels > 1)
+			epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
+		epcm->capture_cr_val |= emu->audigy ?
+			snd_emu10k1_audigy_capture_rate_reg(runtime->rate) :
+			snd_emu10k1_capture_rate_reg(runtime->rate);
+	}
+	return 0;
+}
+
+static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice)
+{
+	snd_pcm_runtime_t *runtime;
+	unsigned int voice, i, ccis, cra = 64, cs, sample;
+
+	if (evoice == NULL)
+		return;
+	runtime = evoice->epcm->substream->runtime;
+	voice = evoice->number;
+	sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
+	if (runtime->channels > 1) {
+		ccis = 28;
+		cs = 4;
+	} else {
+		ccis = 30;
+		cs = 2;
+	}
+	if (sample == 0)	/* 16-bit */
+		ccis *= 2;
+	for (i = 0; i < cs; i++)
+		snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
+	// reset cache
+	snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
+	snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
+	if (runtime->channels > 1) {
+		snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
+		snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
+	}
+	// fill cache
+	snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
+}
+
+static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra)
+{
+	snd_pcm_substream_t *substream;
+	snd_pcm_runtime_t *runtime;
+	emu10k1_pcm_mixer_t *mix;
+	unsigned int voice, pitch, pitch_target, tmp;
+	unsigned int attn;
+
+	if (evoice == NULL)	/* skip second voice for mono */
+		return;
+	substream = evoice->epcm->substream;
+	runtime = substream->runtime;
+	mix = &emu->pcm_mixer[substream->number];
+	voice = evoice->number;
+	pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
+	pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+	attn = extra ? 0 : 0x00ff;
+	tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
+	snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, (mix->attn[tmp] << 16) | 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, voice, (mix->attn[tmp] << 16) | 0xffff);
+	snd_emu10k1_voice_clear_loop_stop(emu, voice);		
+	if (extra)
+		snd_emu10k1_voice_intr_enable(emu, voice);
+	snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
+	snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
+	if (master)
+		snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
+	snd_emu10k1_ptr_write(emu, IP, voice, pitch);
+}
+
+static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice)
+{
+	unsigned int voice;
+
+	if (evoice == NULL)
+		return;
+	voice = evoice->number;
+	snd_emu10k1_voice_intr_disable(emu, voice);
+	snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0);
+	snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0);
+	snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, IP, voice, 0);
+}
+
+static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream,
+				        int cmd)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	unsigned long flags;
+	int result = 0;
+
+	// printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream));
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_emu10k1_playback_invalidate_cache(emu, epcm->extra);	/* do we need this? */
+		snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]);
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		epcm->running = 0;
+		snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]);
+		snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]);
+		snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return result;
+}
+
+static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	unsigned long flags;
+	int result = 0;
+
+	// printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream));
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		outl(epcm->capture_ipr, emu->port + IPR);
+		snd_emu10k1_intr_enable(emu, epcm->capture_inte);
+		// printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs);
+		switch (epcm->type) {
+		case CAPTURE_AC97ADC:
+			snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val);
+			break;
+		case CAPTURE_EFX:
+			snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
+			printk(">> FXWC = 0x%x\n", snd_emu10k1_ptr_read(emu, FXWC, 0));
+			break;
+		default:	
+			break;
+		}
+		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		epcm->running = 0;
+		snd_emu10k1_intr_disable(emu, epcm->capture_inte);
+		outl(epcm->capture_ipr, emu->port + IPR);
+		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+		switch (epcm->type) {
+		case CAPTURE_AC97ADC:
+			snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+			break;
+		case CAPTURE_EFX:
+			snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		result = -EINVAL;
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+#if 0	/* Perex's code */
+	ptr += runtime->buffer_size;
+	ptr -= epcm->ccca_start_addr;
+	ptr %= runtime->buffer_size;
+#else	/* EMU10K1 Open Source code from Creative */
+	if (ptr < epcm->ccca_start_addr)
+		ptr += runtime->buffer_size - epcm->ccca_start_addr;
+	else
+		ptr -= epcm->ccca_start_addr;
+#endif
+	// printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size);
+	return ptr;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO);
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
+	return bytes_to_frames(runtime, ptr);
+}
+
+/*
+ *  Playback support device description
+ */
+
+static snd_pcm_hardware_t snd_emu10k1_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_emu10k1_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(64*1024),
+	period_bytes_min:	384,
+	period_bytes_max:	(64*1024),
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+
+/*
+ *
+ */
+
+static void snd_emu10k1_pcm_mixer_notify1(snd_card_t *card, snd_kcontrol_t *kctl, int activate)
+{
+	snd_runtime_check(kctl != NULL, return);
+	if (activate)
+		kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	else
+		kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+}
+
+static void snd_emu10k1_pcm_mixer_notify(snd_card_t *card, emu10k1_pcm_mixer_t *mix, int activate)
+{
+	snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_routing, activate);
+	snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_volume, activate);
+	snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_attn, activate);
+}
+
+static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+	emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return);
+
+	if (epcm)
+		snd_magic_kfree(epcm);
+}
+
+static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	emu10k1_pcm_t *epcm;
+	emu10k1_pcm_mixer_t *mix;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int i, err;
+
+	epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = PLAYBACK_EMUVOICE;
+	epcm->substream = substream;
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_playback;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+		snd_magic_kfree(epcm);
+		return err;
+	}
+	if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+		snd_magic_kfree(epcm);
+		return err;
+	}
+	mix = &emu->pcm_mixer[substream->number];
+	for (i = 0; i < 4; i++)
+		mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
+	memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+	mix->send_volume[0][0] = mix->send_volume[0][1] =
+	mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+	mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+	mix->epcm = epcm;
+	snd_emu10k1_pcm_mixer_notify(emu->card, mix, 1);
+	return 0;
+}
+
+static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number];
+
+	mix->epcm = NULL;
+	snd_emu10k1_pcm_mixer_notify(emu->card, mix, 0);
+	return 0;
+}
+
+static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm;
+
+	epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_AC97ADC;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL;
+	epcm->capture_inte = INTE_ADCBUFENABLE;
+	epcm->capture_ba_reg = ADCBA;
+	epcm->capture_bs_reg = ADCBS;
+	epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX;
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt;
+	emu->pcm_capture_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates);
+	return 0;
+}
+
+static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_substream = NULL;
+	return 0;
+}
+
+static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	emu10k1_pcm_t *epcm;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_AC97MIC;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL;
+	epcm->capture_inte = INTE_MICBUFENABLE;
+	epcm->capture_ba_reg = MICBA;
+	epcm->capture_bs_reg = MICBS;
+	epcm->capture_idx_reg = MICIDX;
+	substream->runtime->private_data = epcm;
+	substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	runtime->hw.rates = SNDRV_PCM_RATE_8000;
+	runtime->hw.rate_min = runtime->hw.rate_max = 8000;
+	runtime->hw.channels_min = 1;
+	emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt;
+	emu->pcm_capture_mic_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+	return 0;
+}
+
+static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_mic_substream = NULL;
+	return 0;
+}
+
+static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	emu10k1_pcm_t *epcm;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	int idx;
+
+	epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_EFX;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL;
+	epcm->capture_inte = INTE_EFXBUFENABLE;
+	epcm->capture_ba_reg = FXBA;
+	epcm->capture_bs_reg = FXBS;
+	epcm->capture_idx_reg = FXIDX;
+	substream->runtime->private_data = epcm;
+	substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	runtime->hw.rates = SNDRV_PCM_RATE_48000;
+	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	runtime->hw.channels_min = runtime->hw.channels_max = 0;
+	for (idx = 0; idx < 32; idx++) {
+		if (emu->efx_voices_mask & (1 << idx)) {
+			runtime->hw.channels_min++;
+			runtime->hw.channels_max++;
+		}
+	}
+	epcm->capture_cr_val = emu->efx_voices_mask;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
+	emu->pcm_capture_efx_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+	return 0;
+}
+
+static int snd_emu10k1_capture_efx_close(snd_pcm_substream_t * substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_efx_substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_emu10k1_playback_ops = {
+	open:			snd_emu10k1_playback_open,
+	close:			snd_emu10k1_playback_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_emu10k1_playback_hw_params,
+	hw_free:		snd_emu10k1_playback_hw_free,
+	prepare:		snd_emu10k1_playback_prepare,
+	trigger:		snd_emu10k1_playback_trigger,
+	pointer:		snd_emu10k1_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_emu10k1_capture_ops = {
+	open:			snd_emu10k1_capture_open,
+	close:			snd_emu10k1_capture_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_emu10k1_capture_hw_params,
+	hw_free:		snd_emu10k1_capture_hw_free,
+	prepare:		snd_emu10k1_capture_prepare,
+	trigger:		snd_emu10k1_capture_trigger,
+	pointer:		snd_emu10k1_capture_pointer,
+};
+
+static void snd_emu10k1_pcm_free(snd_pcm_t *pcm)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return);
+	emu->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+	pcm->private_free = snd_emu10k1_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "EMU10K1");
+	emu->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = {
+	open:			snd_emu10k1_capture_mic_open,
+	close:			snd_emu10k1_capture_mic_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_emu10k1_capture_hw_params,
+	hw_free:		snd_emu10k1_capture_hw_free,
+	prepare:		snd_emu10k1_capture_prepare,
+	trigger:		snd_emu10k1_capture_trigger,
+	pointer:		snd_emu10k1_capture_pointer,
+};
+
+static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return);
+	emu->pcm_mic = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+	pcm->private_free = snd_emu10k1_pcm_mic_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "EMU10K1 MIC");
+	emu->pcm_mic = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 32;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int idx;
+	
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 32; idx++)
+		ucontrol->value.integer.value[idx] = (emu->efx_voices_mask & (1 << idx)) ? 1 : 0;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int nval, bits;
+	int change, idx;
+	
+	for (idx = 0, nval = bits = 0; idx < 32; idx++)
+		if (ucontrol->value.integer.value[idx]) {
+			nval |= 1 << idx;
+			bits++;
+		}
+	if (bits != 1 && bits != 2 && bits != 4 && bits != 8)
+		return -EINVAL;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	change = nval != emu->efx_voices_mask;
+	emu->efx_voices_mask = nval;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = {
+	iface: SNDRV_CTL_ELEM_IFACE_PCM,
+	name: "EFX voices mask",
+	info: snd_emu10k1_pcm_efx_voices_mask_info,
+	get: snd_emu10k1_pcm_efx_voices_mask_get,
+	put: snd_emu10k1_pcm_efx_voices_mask_put
+};
+
+static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = {
+	open:			snd_emu10k1_capture_efx_open,
+	close:			snd_emu10k1_capture_efx_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_emu10k1_capture_hw_params,
+	hw_free:		snd_emu10k1_capture_hw_free,
+	prepare:		snd_emu10k1_capture_prepare,
+	trigger:		snd_emu10k1_capture_trigger,
+	pointer:		snd_emu10k1_capture_pointer,
+};
+
+static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return);
+	emu->pcm_efx = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 0, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+	pcm->private_free = snd_emu10k1_pcm_efx_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "EMU10K1 EFX");
+	emu->pcm_efx = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+
+	emu->efx_voices_mask = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A;
+	snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu));
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024);
+
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/emuproc.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emuproc.c
--- linux/sound/pci/emu10k1/emuproc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emuproc.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,348 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / proc interface routines
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu,
+					  snd_info_buffer_t * buffer,
+					  char *title,
+					  int status_reg,
+					  int rate_reg)
+{
+	static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
+	static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+	static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
+	static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
+	unsigned int status, rate = 0;
+	
+	status = snd_emu10k1_ptr_read(emu, status_reg, 0);
+	if (rate_reg > 0)
+		rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
+
+	snd_iprintf(buffer, "\n%s\n", title);
+
+	snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
+	snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
+	snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+	snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
+	snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
+	snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
+	snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
+	snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
+	snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
+	snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
+	snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
+
+	if (rate_reg > 0) {
+		snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
+		snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+		snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE);
+	}
+}
+
+static void snd_emu10k1_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	static char *outputs[32] = {
+		/* 00 */ "PCM Left",
+		/* 01 */ "PCM Right",
+		/* 02 */ "PCM Surround Left",
+		/* 03 */ "PCM Surround Right",
+		/* 04 */ "MIDI Left",
+		/* 05 */ "MIDI Right",
+		/* 06 */ "PCM Center",
+		/* 07 */ "PCM LFE",
+		/* 08 */ "???",
+		/* 09 */ "???",
+		/* 10 */ "???",
+		/* 11 */ "???",
+		/* 12 */ "MIDI Reverb",
+		/* 13 */ "MIDI Chorus",
+		/* 14 */ "???",
+		/* 15 */ "???",
+		/* 16 */ "???",
+		/* 17 */ "???",
+		/* 18 */ "ADC Left / CDROM S/PDIF Left",
+		/* 19 */ "ADC Right / CDROM S/PDIF Right",
+		/* 20 */ "MIC / Zoom Video Left",
+		/* 21 */ "Zoom Video Right",
+		/* 22 */ "S/PDIF Left",
+		/* 23 */ "S/PDIF Right",
+		/* 24 */ "???",
+		/* 25 */ "???",
+		/* 26 */ "???",
+		/* 27 */ "???",
+		/* 28 */ "???",
+		/* 29 */ "???",
+		/* 30 */ "???",
+		/* 31 */ "???"
+	};
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return);
+	unsigned int val;
+	int idx;
+	
+	snd_iprintf(buffer, "EMU10K1\n\n");
+	val = emu->audigy ?
+		snd_emu10k1_ptr_read(emu, A_FXRT1, 0) :
+		snd_emu10k1_ptr_read(emu, FXRT, 0);
+	snd_iprintf(buffer, "Card                  : %s\n",
+		    emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative"));
+	snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
+	snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_size);
+	snd_iprintf(buffer, "\n");
+	if (emu->audigy) {
+		snd_iprintf(buffer, "Effect Send Routing   : A=%i, B=%i, C=%i, D=%i\n",
+			    val & 0x3f,
+			    (val >> 8) & 0x3f,
+			    (val >> 16) & 0x3f,
+			    (val >> 24) & 0x3f);
+	} else {
+		snd_iprintf(buffer, "Effect Send Routing   : A=%i, B=%i, C=%i, D=%i\n",
+			    (val >> 16) & 0x0f,
+			    (val >> 20) & 0x0f,
+			    (val >> 24) & 0x0f,
+			    (val >> 28) & 0x0f);
+	}
+	snd_iprintf(buffer, "\nCaptured FX Outputs   :\n");
+	for (idx = 0; idx < 32; idx++) {
+		if (emu->efx_voices_mask & (1 << idx))
+			snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
+	}
+	snd_iprintf(buffer, "\nAll FX Outputs        :\n");
+	for (idx = 0; idx < 32; idx++)
+		snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
+	snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1);
+	snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1);
+	snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1);
+	snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS);
+	snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS);
+	val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
+	snd_iprintf(buffer, "\nZoomed Video\n");
+	snd_iprintf(buffer, "Rate Locked           : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
+	snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+}
+
+static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, 
+				        snd_info_buffer_t * buffer)
+{
+	u32 pc;
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return);
+
+	snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
+	snd_iprintf(buffer, "  Code dump      :\n");
+	for (pc = 0; pc < 512; pc++) {
+		u32 low, high;
+			
+		low = snd_emu10k1_efx_read(emu, pc * 2);
+		high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
+		if (emu->audigy)
+			snd_iprintf(buffer, "    OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+				    (high >> 24) & 0x0f,
+				    (high >> 12) & 0x7ff,
+				    (high >> 0) & 0x7ff,
+				    (low >> 12) & 0x7ff,
+				    (low >> 0) & 0x7ff,
+				    pc,
+				    high, low);
+		else
+			snd_iprintf(buffer, "    OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+				    (high >> 20) & 0x0f,
+				    (high >> 10) & 0x3ff,
+				    (high >> 0) & 0x3ff,
+				    (low >> 10) & 0x3ff,
+				    (low >> 0) & 0x3ff,
+				    pc,
+				    high, low);
+	}
+}
+
+#define TOTAL_SIZE_GPR		(0x100*4)
+#define TOTAL_SIZE_TANKMEM_DATA	(0xa0*4)
+#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4)
+#define TOTAL_SIZE_CODE		(0x200*8)
+
+static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data,
+				    struct file *file, char *buf, long count)
+{
+	long size;
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return -ENXIO);
+	unsigned int offset;
+	
+	if (!strcmp(entry->name, "fx8010_tram_addr")) {
+		if (emu->audigy) return -EINVAL;
+		offset = TANKMEMADDRREGBASE;
+	} else if (!strcmp(entry->name, "fx8010_tram_data")) {
+		if (emu->audigy) return -EINVAL;
+		offset = TANKMEMDATAREGBASE;
+	} else if (!strcmp(entry->name, "fx8010_code")) {
+		offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+	} else {
+		offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
+	}
+	size = count;
+	if (file->f_pos + size > entry->size)
+		size = (long)entry->size - file->f_pos;
+	if (size > 0) {
+		unsigned int *tmp;
+		long res;
+		unsigned int idx;
+		if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		for (idx = 0; idx < ((file->f_pos & 3) + size + 3) >> 2; idx++)
+			tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (file->f_pos >> 2), 0);
+		if (copy_to_user(buf, ((char *)tmp) + (file->f_pos & 3), size))
+			res = -EFAULT;
+		else {
+			res = size;
+			file->f_pos += size;
+		}
+		kfree(tmp);
+		return res;
+	}
+	return 0;
+}
+
+static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+	read: snd_emu10k1_fx8010_read,
+};
+
+int __devinit snd_emu10k1_proc_init(emu10k1_t * emu)
+{
+	snd_info_entry_t *entry;
+	
+	if ((entry = snd_info_create_card_entry(emu->card, "emu10k1", emu->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 4096;
+		entry->c.text.read = snd_emu10k1_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	emu->proc_entry = entry;
+	entry = NULL;
+	if ((entry = snd_info_create_card_entry(emu->card, "fx8010_gpr", emu->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->size = TOTAL_SIZE_GPR;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	emu->proc_entry_fx8010_gpr = entry;
+	entry = NULL;
+	if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_data", emu->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->size = TOTAL_SIZE_TANKMEM_DATA;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	emu->proc_entry_fx8010_tram_data = entry;
+	entry = NULL;
+	if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_addr", emu->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->size = TOTAL_SIZE_TANKMEM_ADDR;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	emu->proc_entry_fx8010_tram_addr = entry;
+	entry = NULL;
+	if ((entry = snd_info_create_card_entry(emu->card, "fx8010_code", emu->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->size = TOTAL_SIZE_CODE;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	emu->proc_entry_fx8010_code = entry;
+	entry = NULL;
+	if ((entry = snd_info_create_card_entry(emu->card, "fx8010_acode", emu->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 64*1024;
+		entry->c.text.read = snd_emu10k1_proc_acode_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	emu->proc_entry_fx8010_iblocks = entry;
+	return 0;
+}
+
+int snd_emu10k1_proc_done(emu10k1_t * emu)
+{
+	if (emu->proc_entry) {
+		snd_info_unregister(emu->proc_entry);
+		emu->proc_entry = NULL;
+	}
+	if (emu->proc_entry_fx8010_gpr) {
+		snd_info_unregister(emu->proc_entry_fx8010_gpr);
+		emu->proc_entry_fx8010_gpr = NULL;
+	}
+	if (emu->proc_entry_fx8010_tram_data) {
+		snd_info_unregister(emu->proc_entry_fx8010_tram_data);
+		emu->proc_entry_fx8010_tram_data = NULL;
+	}
+	if (emu->proc_entry_fx8010_tram_addr) {
+		snd_info_unregister(emu->proc_entry_fx8010_tram_addr);
+		emu->proc_entry_fx8010_tram_addr = NULL;
+	}
+	if (emu->proc_entry_fx8010_code) {
+		snd_info_unregister(emu->proc_entry_fx8010_code);
+		emu->proc_entry_fx8010_code = NULL;
+	}
+	if (emu->proc_entry_fx8010_iblocks) {
+		snd_info_unregister(emu->proc_entry_fx8010_iblocks);
+		emu->proc_entry_fx8010_iblocks = NULL;
+	}
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/io.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/io.c
--- linux/sound/pci/emu10k1/io.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/io.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,341 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+	unsigned int mask;
+
+	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		unsigned char size, offset;
+		
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		
+		return (val & mask) >> offset;
+	} else {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		return val;
+	}
+}
+
+void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+	unsigned int mask;
+
+	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		unsigned char size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		data = (data << offset) & mask;
+
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		data |= inl(emu->port + DATA) & ~mask;
+		outl(data, emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);		
+	} else {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		outl(data, emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+	}
+}
+
+void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) | intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) & ~intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << (voicenum - 32);
+	} else {
+		outl(CLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << voicenum;
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << (voicenum - 32));
+	} else {
+		outl(CLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << voicenum);
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIPH << 16, emu->port + PTR);
+		voicenum = 1 << (voicenum - 32);
+	} else {
+		outl(CLIPL << 16, emu->port + PTR);
+		voicenum = 1 << voicenum;
+	}
+	outl(voicenum, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int sol;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(SOLEH << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol |= 1 << (voicenum - 32);
+	} else {
+		outl(SOLEL << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol |= 1 << voicenum;
+	}
+	outl(sol, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int sol;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(SOLEH << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol &= ~(1 << (voicenum - 32));
+	} else {
+		outl(SOLEL << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol &= ~(1 << voicenum);
+	}
+	outl(sol, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait)
+{
+	volatile unsigned count;
+	unsigned int newtime = 0, curtime;
+
+	curtime = inl(emu->port + WC) >> 6;
+	while (wait-- > 0) {
+		count = 0;
+		while (count++ < 16384) {
+			newtime = inl(emu->port + WC) >> 6;
+			if (newtime != curtime)
+				break;
+		}
+		if (count >= 16384)
+			break;
+		curtime = newtime;
+	}
+}
+
+unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return -ENXIO);
+	unsigned long flags;
+	unsigned short val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	val = inw(emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return);
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	outw(data, emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/*
+ *  convert rate to pitch
+ */
+
+unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
+{
+	static u32 logMagTable[128] = {
+		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+		0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+		0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+		0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+		0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
+		0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+		0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
+		0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+		0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+		0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+	};
+	static char logSlopeTable[128] = {
+		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+	};
+	int i;
+
+	if (rate == 0)
+		return 0;	/* Bail out if no leading "1" */
+	rate *= 11185;		/* Scale 48000 to 0x20002380 */
+	for (i = 31; i > 0; i--) {
+		if (rate & 0x80000000) {	/* Detect leading "1" */
+			return (((unsigned int) (i - 15) << 20) +
+			       logMagTable[0x7f & (rate >> 24)] +
+					(0x7f & (rate >> 17)) *
+					logSlopeTable[0x7f & (rate >> 24)]);
+		}
+		rate <<= 1;
+	}
+
+	return 0;		/* Should never reach this point */
+}
+
+/*
+ *  Returns an attenuation based upon a cumulative volume value
+ *  Algorithm calculates 0x200 - 0x10 log2 (input)
+ */
+ 
+unsigned char snd_emu10k1_sum_vol_attn(unsigned int value)
+{
+	unsigned short count = 16, ans;
+
+	if (value == 0)
+		return 0xFF;
+
+	/* Find first SET bit. This is the integer part of the value */
+	while ((value & 0x10000) == 0) {
+		value <<= 1;
+		count--;
+	}
+
+	/* The REST of the data is the fractional part. */
+	ans = (unsigned short) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12)));
+	if (ans > 0xFF)
+		ans = 0xFF;
+
+	return (unsigned char) ans;
+}
diff -Nru linux/sound/pci/emu10k1/irq.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/irq.c
--- linux/sound/pci/emu10k1/irq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/irq.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,149 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for IRQ control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	emu10k1_t *emu = snd_magic_cast(emu10k1_t, dev_id, return);
+	unsigned int status;
+
+	while ((status = inl(emu->port + IPR)) != 0) {
+		// printk("irq - status = 0x%x\n", status);
+		if (status & IPR_PCIERROR) {
+			snd_printk("interrupt: PCI error\n");
+			snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
+		}
+		if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
+			if (emu->hwvol_interrupt)
+				emu->hwvol_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
+			outl(status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE), emu->port + IPR);
+		}
+		if (status & IPR_CHANNELLOOP) {
+			int voice;
+			int voice_max = status & IPR_CHANNELNUMBERMASK;
+			int voice_max_l;
+			u32 val;
+			emu10k1_voice_t *pvoice = emu->voices;
+
+			val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
+			voice_max_l = voice_max;
+			if (voice_max_l >= 0x20)
+				voice_max_l = 0x1f;
+			for (voice = 0; voice <= voice_max_l; voice++) {
+				if (val & 1) {
+					if (pvoice->use && pvoice->interrupt != NULL) {
+						pvoice->interrupt(emu, pvoice);
+						snd_emu10k1_voice_intr_ack(emu, voice);
+					} else {
+						snd_emu10k1_voice_intr_disable(emu, voice);
+					}
+				}
+				val >>= 1;
+				pvoice++;
+			}
+			if (voice_max > 0x1f) {
+				val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
+				for (; voice <= voice_max; voice++) {
+					if(val & 1) {
+						if (pvoice->use && pvoice->interrupt != NULL) {
+							pvoice->interrupt(emu, pvoice);
+							snd_emu10k1_voice_intr_ack(emu, voice);
+						} else {
+							snd_emu10k1_voice_intr_disable(emu, voice);
+						}
+					}
+					val >>= 1;
+					pvoice++;
+				}
+			}
+			outl(IPR_CHANNELLOOP | (status & IPR_CHANNELNUMBERMASK), emu->port + IPR);
+		}
+		if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
+			if (emu->capture_interrupt)
+				emu->capture_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
+			outl(status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL), emu->port + IPR);
+		}
+		if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
+			if (emu->capture_mic_interrupt)
+				emu->capture_mic_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
+			outl(status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL), emu->port + IPR);
+		}
+		if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
+			if (emu->capture_efx_interrupt)
+				emu->capture_efx_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
+			outl(status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL), emu->port + IPR);
+		}
+		if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+			if (emu->midi.interrupt)
+				emu->midi.interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+			outl(status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY), emu->port + IPR);
+		}
+		if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
+			if (emu->midi2.interrupt)
+				emu->midi2.interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
+			outl(status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2), emu->port + IPR);
+		}
+		if (status & IPR_INTERVALTIMER) {
+			if (emu->timer_interrupt)
+				emu->timer_interrupt(emu);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+			outl(IPR_INTERVALTIMER, emu->port + IPR);
+		}
+		if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
+			if (emu->spdif_interrupt)
+				emu->spdif_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
+			outl(status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE), emu->port + IPR);
+		}
+		if (status & IPR_FXDSP) {
+			if (emu->dsp_interrupt)
+				emu->dsp_interrupt(emu);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+			outl(IPR_FXDSP, emu->port + IPR);
+		}
+	}
+}
diff -Nru linux/sound/pci/emu10k1/memory.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/memory.c
--- linux/sound/pci/emu10k1/memory.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/memory.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,541 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *  EMU10K1 memory page allocation (PTB area)
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* page arguments of these two macros are Emu page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_ptb_entry(emu,page,addr) \
+	((emu)->ptb_pages[page] = ((addr) << 1) | (page))
+
+#define UNIT_PAGES		(PAGE_SIZE / EMUPAGESIZE)
+#define MAX_ALIGN_PAGES		(MAXPAGES / UNIT_PAGES)
+/* get aligned page from offset address */
+#define get_aligned_page(offset)	((offset) >> PAGE_SHIFT)
+/* get offset address from aligned page */
+#define aligned_page_offset(page)	((page) << PAGE_SHIFT)
+
+#if PAGE_SIZE == 4096
+/* page size == EMUPAGESIZE */
+/* fill PTB entrie(s) corresponding to page with addr */
+#define set_ptb_entry(emu,page,addr)	__set_ptb_entry(emu,page,addr)
+/* fill PTB entrie(s) corresponding to page with silence pointer */
+#define set_silent_ptb(emu,page)	__set_ptb_entry(emu,page,emu->silent_page_dmaaddr)
+#else
+/* fill PTB entries -- we need to fill UNIT_PAGES entries */
+static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++) {
+		__set_ptb_entry(emu, page, addr);
+		addr += EMUPAGESIZE;
+	}
+}
+static inline void set_silent_ptb(emu10k1_t *emu, int page)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++)
+		/* do not increment ptr */
+		__set_ptb_entry(emu, page, emu->silent_page_dmaaddr);
+}
+#endif /* PAGE_SIZE */
+
+
+/*
+ */
+static int synth_alloc_pages(emu10k1_t *hw, emu10k1_memblk_t *blk);
+static int synth_free_pages(emu10k1_t *hw, emu10k1_memblk_t *blk);
+
+#define get_emu10k1_memblk(l,member)	list_entry(l, emu10k1_memblk_t, member)
+
+
+/* initialize emu10k1 part */
+static void emu10k1_memblk_init(emu10k1_memblk_t *blk)
+{
+	blk->mapped_page = -1;
+	INIT_LIST_HEAD(&blk->mapped_link);
+	INIT_LIST_HEAD(&blk->mapped_order_link);
+	blk->map_locked = 0;
+
+	blk->first_page = get_aligned_page(blk->mem.offset);
+	blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1);
+	blk->pages = blk->last_page - blk->first_page + 1;
+}
+
+/*
+ * search empty region on PTB with the given size
+ *
+ * if an empty region is found, return the page and store the next mapped block
+ * in nextp
+ * if not found, return a negative error code.
+ */
+static int search_empty_map_area(emu10k1_t *emu, int npages, struct list_head **nextp)
+{
+	int page = 0, found_page = -ENOMEM;
+	int max_size = npages;
+	int size;
+	struct list_head *candidate = &emu->mapped_link_head;
+	struct list_head *pos;
+
+	list_for_each (pos, &emu->mapped_link_head) {
+		emu10k1_memblk_t *blk = get_emu10k1_memblk(pos, mapped_link);
+		snd_assert(blk->mapped_page >= 0, continue);
+		size = blk->mapped_page - page;
+		if (size == npages) {
+			*nextp = pos;
+			return page;
+		}
+		else if (size > max_size) {
+			/* we look for the maximum empty hole */
+			max_size = size;
+			candidate = pos;
+			found_page = page;
+		}
+		page = blk->mapped_page + blk->pages;
+	}
+	size = MAX_ALIGN_PAGES - page;
+	if (size >= max_size) {
+		*nextp = pos;
+		return page;
+	}
+	*nextp = candidate;
+	return found_page;
+}
+
+/*
+ * map a memory block onto emu10k1's PTB
+ *
+ * call with memblk_lock held
+ */
+static int map_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+	int page, pg;
+	struct list_head *next;
+
+	page = search_empty_map_area(emu, blk->pages, &next);
+	if (page < 0) /* not found */
+		return page;
+	/* insert this block in the proper position of mapped list */
+	list_add_tail(&blk->mapped_link, next);
+	/* append this as a newest block in order list */
+	list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+	blk->mapped_page = page;
+	/* fill PTB */
+	for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+		set_ptb_entry(emu, page, emu->page_addr_table[pg]);
+		page++;
+	}
+	return 0;
+}
+
+/*
+ * unmap the block
+ * return the size of resultant empty pages
+ *
+ * call with memblk_lock held
+ */
+static int unmap_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+	int start_page, end_page, mpage, pg;
+	struct list_head *p;
+	emu10k1_memblk_t *q;
+
+	/* calculate the expected size of empty region */
+	if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
+		q = get_emu10k1_memblk(p, mapped_link);
+		start_page = q->mapped_page + q->pages;
+	} else
+		start_page = 0;
+	if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
+		q = get_emu10k1_memblk(p, mapped_link);
+		end_page = q->mapped_page;
+	} else
+		end_page = MAX_ALIGN_PAGES;
+
+	/* remove links */
+	list_del(&blk->mapped_link);
+	list_del(&blk->mapped_order_link);
+	/* clear PTB */
+	mpage = blk->mapped_page;
+	for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+		set_silent_ptb(emu, mpage);
+		mpage++;
+	}
+	blk->mapped_page = -1;
+	return end_page - start_page; /* return the new empty size */
+}
+
+/*
+ * search empty pages with the given size, and create a memory block
+ *
+ * unlike synth_alloc the memory block is aligned to the page start
+ */
+static emu10k1_memblk_t *
+search_empty(emu10k1_t *emu, int size)
+{
+	struct list_head *p;
+	emu10k1_memblk_t *blk;
+	int page, psize;
+
+	psize = get_aligned_page(size + PAGE_SIZE -1);
+	page = 0;
+	list_for_each(p, &emu->memhdr->block) {
+		blk = get_emu10k1_memblk(p, mem.list);
+		if (page + psize <= blk->first_page)
+			goto __found_pages;
+		page = blk->last_page + 1;
+	}
+	if (page + psize > emu->max_cache_pages)
+		return NULL;
+
+__found_pages:
+	/* create a new memory block */
+	blk = (emu10k1_memblk_t *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev);
+	if (blk == NULL)
+		return NULL;
+	blk->mem.offset = aligned_page_offset(page); /* set aligned offset */
+	emu10k1_memblk_init(blk);
+	return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(dma_addr_t addr)
+{
+	if (addr & ~0x7fffffffUL) {
+		snd_printk("max memory size is 2GB!!\n");
+		return 0;
+	}
+	if (addr & (EMUPAGESIZE-1)) {
+		snd_printk("page is not aligned\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * map the given memory block on PTB.
+ * if the block is already mapped, update the link order.
+ * if no empty pages are found, tries to release unsed memory blocks
+ * and retry the mapping.
+ */
+int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+	int err;
+	int size;
+	struct list_head *p, *nextp;
+	emu10k1_memblk_t *deleted;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->memblk_lock, flags);
+	if (blk->mapped_page >= 0) {
+		/* update order link */
+		list_del(&blk->mapped_order_link);
+		list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+		spin_unlock_irqrestore(&emu->memblk_lock, flags);
+		return 0;
+	}
+	if ((err = map_memblk(emu, blk)) < 0) {
+		/* no enough page - try to unmap some blocks */
+		/* starting from the oldest block */
+		p = emu->mapped_order_link_head.next;
+		for (; p != &emu->mapped_order_link_head; p = nextp) {
+			nextp = p->next;
+			deleted = get_emu10k1_memblk(p, mapped_order_link);
+			if (deleted->map_locked)
+				continue;
+			size = unmap_memblk(emu, deleted);
+			if (size >= blk->pages) {
+				/* ok the empty region is enough large */
+				err = map_memblk(emu, blk);
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&emu->memblk_lock, flags);
+	return err;
+}
+
+/*
+ * page allocation for DMA
+ */
+snd_util_memblk_t *
+snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size)
+{
+	snd_util_memhdr_t *hdr;
+	emu10k1_memblk_t *blk;
+	int page, err;
+
+	snd_assert(emu, return NULL);
+	snd_assert(size > 0 && size < MAXPAGES * EMUPAGESIZE, return NULL);
+	hdr = emu->memhdr;
+	snd_assert(hdr, return NULL);
+
+	if (!is_valid_page(addr))
+		return NULL;
+
+	down(&hdr->block_mutex);
+	blk = search_empty(emu, size);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	/* fill buffer addresses but pointers are not stored so that
+	 * snd_free_pci_pages() is not called in in synth_free()
+	 */
+	for (page = blk->first_page; page <= blk->last_page; page++) {
+		emu->page_addr_table[page] = addr;
+		emu->page_ptr_table[page] = NULL;
+		addr += PAGE_SIZE;
+	}
+
+	/* set PTB entries */
+	blk->map_locked = 1; /* do not unmap this block! */
+	err = snd_emu10k1_memblk_map(emu, blk);
+	if (err < 0) {
+		__snd_util_mem_free(hdr, (snd_util_memblk_t *)blk);
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	up(&hdr->block_mutex);
+	return (snd_util_memblk_t *)blk;
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk)
+{
+	snd_assert(emu && blk, return -EINVAL);
+	return snd_emu10k1_synth_free(emu, blk);
+}
+
+
+/*
+ * memory allocation using multiple pages (for synth)
+ * Unlike the DMA allocation above, non-contiguous pages are assined.
+ */
+
+/*
+ * allocate a synth sample area
+ */
+snd_util_memblk_t *
+snd_emu10k1_synth_alloc(emu10k1_t *hw, unsigned int size)
+{
+	emu10k1_memblk_t *blk;
+	snd_util_memhdr_t *hdr = hw->memhdr; 
+
+	down(&hdr->block_mutex);
+	blk = (emu10k1_memblk_t *)__snd_util_mem_alloc(hdr, size);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	if (synth_alloc_pages(hw, blk)) {
+		__snd_util_mem_free(hdr, (snd_util_memblk_t *)blk);
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	snd_emu10k1_memblk_map(hw, blk);
+	up(&hdr->block_mutex);
+	return (snd_util_memblk_t *)blk;
+}
+
+
+/*
+ * free a synth sample area
+ */
+int
+snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *memblk)
+{
+	snd_util_memhdr_t *hdr = emu->memhdr; 
+	emu10k1_memblk_t *blk = (emu10k1_memblk_t *)memblk;
+	unsigned long flags;
+
+	down(&hdr->block_mutex);
+	spin_lock_irqsave(&emu->memblk_lock, flags);
+	if (blk->mapped_page >= 0)
+		unmap_memblk(emu, blk);
+	spin_unlock_irqrestore(&emu->memblk_lock, flags);
+	synth_free_pages(emu, blk);
+	 __snd_util_mem_free(hdr, memblk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+
+/* check new allocation range */
+static void get_single_page_range(snd_util_memhdr_t *hdr, emu10k1_memblk_t *blk, int *first_page_ret, int *last_page_ret)
+{
+	struct list_head *p;
+	emu10k1_memblk_t *q;
+	int first_page, last_page;
+	first_page = blk->first_page;
+	if ((p = blk->mem.list.prev) != &hdr->block) {
+		q = get_emu10k1_memblk(p, mem.list);
+		if (q->last_page == first_page)
+			first_page++;  /* first page was already allocated */
+	}
+	last_page = blk->last_page;
+	if ((p = blk->mem.list.next) != &hdr->block) {
+		q = get_emu10k1_memblk(p, mem.list);
+		if (q->first_page == last_page)
+			last_page--; /* last page was already allocated */
+	}
+	*first_page_ret = first_page;
+	*last_page_ret = last_page;
+}
+
+/*
+ * allocate kernel pages
+ */
+static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+	int page, first_page, last_page;
+	void *ptr;
+	dma_addr_t addr;
+
+	emu10k1_memblk_init(blk);
+	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+	/* allocate kernel pages */
+	for (page = first_page; page <= last_page; page++) {
+		ptr = snd_malloc_pci_pages(emu->pci, PAGE_SIZE, &addr);
+		if (ptr == NULL)
+			goto __fail;
+		if (! is_valid_page(addr)) {
+			snd_free_pci_pages(emu->pci, PAGE_SIZE, ptr, addr);
+			goto __fail;
+		}
+		emu->page_addr_table[page] = addr;
+		emu->page_ptr_table[page] = ptr;
+	}
+	return 0;
+
+__fail:
+	/* release allocated pages */
+	last_page = page - 1;
+	for (page = first_page; page <= last_page; page++) {
+		snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]);
+		emu->page_addr_table[page] = 0;
+		emu->page_ptr_table[page] = NULL;
+	}
+
+	return -ENOMEM;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk)
+{
+	int page, first_page, last_page;
+
+	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+	for (page = first_page; page <= last_page; page++) {
+		if (emu->page_ptr_table[page])
+			snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]);
+		emu->page_addr_table[page] = 0;
+		emu->page_ptr_table[page] = NULL;
+	}
+
+	return 0;
+}
+
+/* calculate buffer pointer from offset address */
+inline static void *offset_ptr(emu10k1_t *emu, int page, int offset)
+{
+	char *ptr;
+	snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL);
+	ptr = emu->page_ptr_table[page];
+	if (! ptr) {
+		printk("emu10k1: access to NULL ptr: page = %d\n", page);
+		return NULL;
+	}
+	ptr += offset & (PAGE_SIZE - 1);
+	return (void*)ptr;
+}
+
+/*
+ * bzero(blk + offset, size)
+ */
+int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+	void *ptr;
+	emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk;
+
+	offset += blk->offset & (PAGE_SIZE - 1);
+	end_offset = offset + size;
+	page = get_aligned_page(offset);
+	do {
+		nextofs = aligned_page_offset(page + 1);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		ptr = offset_ptr(emu, page + p->first_page, offset);
+		if (ptr)
+			memset(ptr, 0, temp);
+		offset = nextofs;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+	void *ptr;
+	emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk;
+
+	offset += blk->offset & (PAGE_SIZE - 1);
+	end_offset = offset + size;
+	page = get_aligned_page(offset);
+	do {
+		nextofs = aligned_page_offset(page + 1);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		ptr = offset_ptr(emu, page + p->first_page, offset);
+		if (ptr && copy_from_user(ptr, data, temp))
+			return -EFAULT;
+		offset = nextofs;
+		data += temp;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
diff -Nru linux/sound/pci/emu10k1/voice.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/voice.c
--- linux/sound/pci/emu10k1/voice.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/voice.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips - voice manager
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice)
+{
+	emu10k1_voice_t *voice, *voice2;
+	int idx;
+
+	*rvoice = NULL;
+	for (idx = 0; idx < 64; idx += pair ? 2 : 1) {
+		voice = &emu->voices[idx];
+		voice2 = pair ? &emu->voices[idx+1] : NULL;
+		if (voice->use || (voice2 && voice2->use))
+			continue;
+		voice->use = 1;
+		if (voice2)
+			voice2->use = 1;
+		switch (type) {
+		case EMU10K1_PCM:
+			voice->pcm = 1;
+			if (voice2)
+				voice2->pcm = 1;
+			break;
+		case EMU10K1_SYNTH:
+			voice->synth = 1;
+			break;
+		case EMU10K1_MIDI:
+			voice->midi = 1;
+			break;
+		}
+		// printk("voice alloc - %i, pair = %i\n", voice->number, pair);
+		*rvoice = voice;
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice)
+{
+	unsigned long flags;
+	int result;
+
+	snd_assert(rvoice != NULL, return -EINVAL);
+	snd_assert(!pair || type == EMU10K1_PCM, return -EINVAL);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (;;) {
+		result = voice_alloc(emu, type, pair, rvoice);
+		if (result == 0 || type != EMU10K1_PCM)
+			break;
+
+		/* free a voice from synth */
+		if (emu->get_synth_voice) {
+			result = emu->get_synth_voice(emu);
+			if (result >= 0) {
+				emu10k1_voice_t *pvoice = &emu->voices[result];
+				pvoice->interrupt = NULL;
+				pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+				pvoice->epcm = NULL;
+			}
+		}
+		if (result < 0)
+			break;
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+	return result;
+}
+
+int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice)
+{
+	unsigned long flags;
+
+	snd_assert(pvoice != NULL, return -EINVAL);
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	pvoice->interrupt = NULL;
+	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+	pvoice->epcm = NULL;
+	snd_emu10k1_voice_init(emu, pvoice->number);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+	return 0;
+}
diff -Nru linux/sound/pci/ens1370.c linux-2.4.19-pre5-mjc/sound/pci/ens1370.c
--- linux/sound/pci/ens1370.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ens1370.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2090 @@
+/*
+ *  Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include <sound/ak4531_codec.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t ensoniq_t
+
+#ifndef CHIP1371
+#undef CHIP1370
+#define CHIP1370
+#endif
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Thomas Sailer <sailer@ife.ee.ethz.ch>");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+#ifdef CHIP1370
+MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370");
+MODULE_DEVICES("{{Ensoniq,AudioPCI-97 ES1370},"
+	        "{Creative Labs,SB PCI64/128 (ES1370)}}");
+#endif
+#ifdef CHIP1371
+MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+");
+MODULE_DEVICES("{{Ensoniq,AudioPCI ES1371/73},"
+		"{Ensoniq,AudioPCI ES1373},"
+		"{Creative Labs,Ectiva EV1938},"
+		"{Creative Labs,SB PCI64/128 (ES1371/73)},"
+		"{Creative Labs,Vibra PCI128},"
+		"{Ectiva,EV1938}}");
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Ensoniq AudioPCI soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Ensoniq AudioPCI soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Ensoniq AudioPCI soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+
+#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880
+#define PCI_DEVICE_ID_ENSONIQ_CT5880    0x5880
+#endif
+#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371
+#define PCI_DEVICE_ID_ENSONIQ_ES1371	0x1371
+#endif
+
+/* ES1371 chip ID */
+/* This is a little confusing because all ES1371 compatible chips have the
+   same DEVICE_ID, the only thing differentiating them is the REV_ID field.
+   This is only significant if you want to enable features on the later parts.
+   Yes, I know it's stupid and why didn't we use the sub IDs?
+*/
+#define ES1371REV_ES1373_A  0x04
+#define ES1371REV_ES1373_B  0x06
+#define ES1371REV_CT5880_A  0x07
+#define CT5880REV_CT5880_C  0x02
+#define CT5880REV_CT5880_D  0x03	/* ??? -jk */
+#define CT5880REV_CT5880_E  0x04	/* mw */
+#define ES1371REV_ES1371_B  0x09
+#define EV1938REV_EV1938_A  0x00
+#define ES1371REV_ES1373_8  0x08
+
+/*
+ * Direct registers
+ */
+
+#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x)
+
+#define ES_REG_CONTROL	0x00	/* R/W: Interrupt/Chip select control register */
+#define   ES_1370_ADC_STOP	(1<<31)		/* disable capture buffer transfers */
+#define   ES_1370_XCTL1 	(1<<30)		/* general purpose output bit */
+#define   ES_1371_SPDIF_EN	(1<<26)		/* SPDIF enable */
+#define   ES_1371_JOY_ASEL(o)	(((o)&0x03)<<24)	/* joystick port mapping */
+#define   ES_1371_JOY_ASELM	(0x03<<24)	/* mask for above */
+#define   ES_1371_JOY_ASELI(i)  (((i)>>24)&0x03)
+#define   ES_1371_GPIO_IN(i)	(((i)>>20)&0x0f)	/* GPIO in [3:0] pins - R/O */
+#define   ES_1370_PCLKDIVO(o)	(((o)&0x1fff)<<16)	/* clock divide ratio for DAC2 */
+#define   ES_1370_PCLKDIVM	((0x1fff)<<16)	/* mask for above */
+#define   ES_1370_PCLKDIVI(i)	(((i)>>16)&0x1fff)	/* clock divide ratio for DAC2 */
+#define   ES_1371_GPIO_OUT(o)	(((o)&0x0f)<<16)	/* GPIO out [3:0] pins - W/R */
+#define   ES_1371_GPIO_OUTM     (0x0f<<16)	/* mask for above */
+#define   ES_MSFMTSEL		(1<<15)		/* MPEG serial data format; 0 = SONY, 1 = I2S */
+#define   ES_1370_M_SBB		(1<<14)		/* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */
+#define   ES_1371_SYNC_RES	(1<<14)		/* Warm AC97 reset */
+#define   ES_1370_WTSRSEL(o)	(((o)&0x03)<<12)	/* fixed frequency clock for DAC1 */
+#define   ES_1370_WTSRSELM	(0x03<<12)	/* mask for above */
+#define   ES_1371_ADC_STOP	(1<<13)		/* disable CCB transfer capture information */
+#define   ES_1371_PWR_INTRM	(1<<12)		/* power level change interrupts enable */
+#define   ES_1370_DAC_SYNC	(1<<11)		/* DAC's are synchronous */
+#define   ES_1371_M_CB		(1<<11)		/* capture clock source; 0 = ADC; 1 = I2S */
+#define   ES_CCB_INTRM		(1<<10)		/* CCB voice interrupts enable */
+#define   ES_1370_M_CB		(1<<9)	/* capture clock source; 0 = ADC; 1 = MPEG */
+#define   ES_1370_XCTL0		(1<<8)	/* generap purpose output bit */
+#define   ES_1371_PDLEV(o)	(((o)&0x03)<<8)		/* current power down level */
+#define   ES_1371_PDLEVM	(0x03<<8)	/* mask for above */
+#define   ES_BREQ		(1<<7)	/* memory bus request enable */
+#define   ES_DAC1_EN		(1<<6)	/* DAC1 playback channel enable */
+#define   ES_DAC2_EN		(1<<5)	/* DAC2 playback channel enable */
+#define   ES_ADC_EN		(1<<4)	/* ADC capture channel enable */
+#define   ES_UART_EN		(1<<3)	/* UART enable */
+#define   ES_JYSTK_EN		(1<<2)	/* Joystick module enable */
+#define   ES_1370_CDC_EN	(1<<1)	/* Codec interface enable */
+#define   ES_1371_XTALCKDIS	(1<<1)	/* Xtal clock disable */
+#define   ES_1370_SERR_DISABLE	(1<<0)	/* PCI serr signal disable */
+#define   ES_1371_PCICLKDIS     (1<<0)	/* PCI clock disable */
+#define ES_REG_STATUS	0x04	/* R/O: Interrupt/Chip select status register */
+#define   ES_INTR               (1<<31)	/* Interrupt is pending */
+#define   ES_1371_ST_AC97_RST	(1<<29) /* CT5880 AC'97 Reset bit */
+#define   ES_1371_ST_SPDIF_EN	(1<<18) /* SPDIF enable */
+#define   ES_1371_ST_SPDIF_TEST (1<<17) /* SPDIF test */
+#define   ES_1371_TEST          (1<<16) /* test ASIC */
+#define   ES_1370_CSTAT		(1<<10)	/* CODEC is busy or register write in progress */
+#define   ES_1370_CBUSY         (1<<9)	/* CODEC is busy */
+#define   ES_1370_CWRIP		(1<<8)	/* CODEC register write in progress */
+#define   ES_1371_SYNC_ERR	(1<<8)	/* CODEC synchronization error occured */
+#define   ES_1371_VC(i)         (((i)>>6)&0x03)		/* voice code from CCB module */
+#define   ES_1370_VC(i)		(((i)>>5)&0x03)		/* voice code from CCB module */
+#define   ES_1371_MPWR          (1<<5)	/* power level interrupt pending */
+#define   ES_MCCB		(1<<4)	/* CCB interrupt pending */
+#define   ES_UART		(1<<3)	/* UART interrupt pending */
+#define   ES_DAC1		(1<<2)	/* DAC1 channel interrupt pending */
+#define   ES_DAC2		(1<<1)	/* DAC2 channel interrupt pending */
+#define   ES_ADC		(1<<0)	/* ADC channel interrupt pending */
+#define ES_REG_UART_DATA 0x08	/* R/W: UART data register */
+#define ES_REG_UART_STATUS 0x09	/* R/O: UART status register */
+#define   ES_RXINT		(1<<7)	/* RX interrupt occured */
+#define   ES_TXINT		(1<<2)	/* TX interrupt occured */
+#define   ES_TXRDY		(1<<1)	/* transmitter ready */
+#define   ES_RXRDY		(1<<0)	/* receiver ready */
+#define ES_REG_UART_CONTROL 0x09	/* W/O: UART control register */
+#define   ES_RXINTEN		(1<<7)	/* RX interrupt enable */
+#define   ES_TXINTENO(o)	(((o)&0x03)<<5)	/* TX interrupt enable */
+#define   ES_TXINTENM		(0x03<<5)	/* mask for above */
+#define   ES_TXINTENI(i)	(((i)>>5)&0x03)
+#define   ES_CNTRL(o)		(((o)&0x03)<<0)	/* control */
+#define   ES_CNTRLM		(0x03<<0)	/* mask for above */
+#define ES_REG_UART_RES	0x0a	/* R/W: UART reserver register */
+#define   ES_TEST_MODE		(1<<0)		/* test mode enabled */
+#define ES_REG_MEM_PAGE	0x0c	/* R/W: Memory page register */
+#define   ES_MEM_PAGEO(o)	(((o)&0x0f)<<0)	/* memory page select - out */
+#define   ES_MEM_PAGEM		(0x0f<<0)	/* mask for above */
+#define   ES_MEM_PAGEI(i)	(((i)>>0)&0x0f) /* memory page select - in */
+#define ES_REG_1370_CODEC 0x10	/* W/O: Codec write register address */
+#define   ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0))
+#define ES_REG_1371_CODEC 0x14	/* W/R: Codec Read/Write register address */
+#define   ES_1371_CODEC_RDY	   (1<<31)	/* codec ready */
+#define   ES_1371_CODEC_WIP	   (1<<30)	/* codec register access in progress */
+#define   ES_1371_CODEC_PIRD	   (1<<23)	/* codec read/write select register */
+#define   ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0))
+#define   ES_1371_CODEC_READS(a)   ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD)
+#define   ES_1371_CODEC_READ(i)    (((i)>>0)&0xffff)
+
+#define ES_REG_1371_SMPRATE 0x10	/* W/R: Codec rate converter interface register */
+#define   ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)	/* address of the sample rate converter */
+#define   ES_1371_SRC_RAM_ADDRM	   (0x7f<<25)	/* mask for above */
+#define   ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)	/* address of the sample rate converter */
+#define   ES_1371_SRC_RAM_WE	   (1<<24)	/* R/W: read/write control for sample rate converter */
+#define   ES_1371_SRC_RAM_BUSY     (1<<23)	/* R/O: sample rate memory is busy */
+#define   ES_1371_SRC_DISABLE      (1<<22)	/* sample rate converter disable */
+#define   ES_1371_DIS_P1	   (1<<21)	/* playback channel 1 accumulator update disable */
+#define   ES_1371_DIS_P2	   (1<<20)	/* playback channel 1 accumulator update disable */
+#define   ES_1371_DIS_R1	   (1<<19)	/* capture channel accumulator update disable */
+#define   ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)	/* current value of the sample rate converter */
+#define   ES_1371_SRC_RAM_DATAM	   (0xffff<<0)	/* mask for above */
+#define   ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)	/* current value of the sample rate converter */
+
+#define ES_REG_1371_LEGACY 0x18	/* W/R: Legacy control/status register */
+#define   ES_1371_JFAST		(1<<31)		/* fast joystick timing */
+#define   ES_1371_HIB		(1<<30)		/* host interrupt blocking enable */
+#define   ES_1371_VSB		(1<<29)		/* SB; 0 = addr 0x220xH, 1 = 0x22FxH */
+#define   ES_1371_VMPUO(o)	(((o)&0x03)<<27)	/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */
+#define   ES_1371_VMPUM		(0x03<<27)	/* mask for above */
+#define   ES_1371_VMPUI(i)	(((i)>>27)&0x03)	/* base register address */
+#define   ES_1371_VCDCO(o)	(((o)&0x03)<<25)	/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */
+#define   ES_1371_VCDCM		(0x03<<25)	/* mask for above */
+#define   ES_1371_VCDCI(i)	(((i)>>25)&0x03)	/* CODEC address */
+#define   ES_1371_FIRQ		(1<<24)		/* force an interrupt */
+#define   ES_1371_SDMACAP	(1<<23)		/* enable event capture for slave DMA controller */
+#define   ES_1371_SPICAP	(1<<22)		/* enable event capture for slave IRQ controller */
+#define   ES_1371_MDMACAP	(1<<21)		/* enable event capture for master DMA controller */
+#define   ES_1371_MPICAP	(1<<20)		/* enable event capture for master IRQ controller */
+#define   ES_1371_ADCAP		(1<<19)		/* enable event capture for ADLIB register; 0x388xH */
+#define   ES_1371_SVCAP		(1<<18)		/* enable event capture for SB registers */
+#define   ES_1371_CDCCAP	(1<<17)		/* enable event capture for CODEC registers */
+#define   ES_1371_BACAP		(1<<16)		/* enable event capture for SoundScape base address */
+#define   ES_1371_EXI(i)	(((i)>>8)&0x07)		/* event number */
+#define   ES_1371_AI(i)		(((i)>>3)&0x1f)		/* event significant I/O address */
+#define   ES_1371_WR		(1<<2)	/* event capture; 0 = read; 1 = write */
+#define   ES_1371_LEGINT	(1<<0)	/* interrupt for legacy events; 0 = interrupt did occur */
+
+#define ES_REG_SERIAL	0x20	/* R/W: Serial interface control register */
+#define   ES_1371_DAC_TEST	(1<<22)		/* DAC test mode enable */
+#define   ES_P2_END_INCO(o)	(((o)&0x07)<<19)	/* binary offset value to increment / loop end */
+#define   ES_P2_END_INCM	(0x07<<19)	/* mask for above */
+#define   ES_P2_END_INCI(i)	(((i)>>16)&0x07)	/* binary offset value to increment / loop end */
+#define   ES_P2_ST_INCO(o)	(((o)&0x07)<<16)	/* binary offset value to increment / start */
+#define   ES_P2_ST_INCM		(0x07<<16)	/* mask for above */
+#define   ES_P2_ST_INCI(i)	(((i)<<16)&0x07)	/* binary offset value to increment / start */
+#define   ES_R1_LOOP_SEL	(1<<15)		/* ADC; 0 - loop mode; 1 = stop mode */
+#define   ES_P2_LOOP_SEL	(1<<14)		/* DAC2; 0 - loop mode; 1 = stop mode */
+#define   ES_P1_LOOP_SEL	(1<<13)		/* DAC1; 0 - loop mode; 1 = stop mode */
+#define   ES_P2_PAUSE		(1<<12)		/* DAC2; 0 - play mode; 1 = pause mode */
+#define   ES_P1_PAUSE		(1<<11)		/* DAC1; 0 - play mode; 1 = pause mode */
+#define   ES_R1_INT_EN		(1<<10)		/* ADC interrupt enable */
+#define   ES_P2_INT_EN		(1<<9)	/* DAC2 interrupt enable */
+#define   ES_P1_INT_EN		(1<<8)	/* DAC1 interrupt enable */
+#define   ES_P1_SCT_RLD		(1<<7)	/* force sample counter reload for DAC1 */
+#define   ES_P2_DAC_SEN		(1<<6)	/* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */
+#define   ES_R1_MODEO(o)	(((o)&0x03)<<4)		/* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */
+#define   ES_R1_MODEM		(0x03<<4)	/* mask for above */
+#define   ES_R1_MODEI(i)	(((i)>>4)&0x03)
+#define   ES_P2_MODEO(o)	(((o)&0x03)<<2)		/* DAC2 mode; -- '' -- */
+#define   ES_P2_MODEM		(0x03<<2)	/* mask for above */
+#define   ES_P2_MODEI(i)	(((i)>>2)&0x03)
+#define   ES_P1_MODEO(o)	(((o)&0x03)<<0)		/* DAC1 mode; -- '' -- */
+#define   ES_P1_MODEM		(0x03<<0)	/* mask for above */
+#define   ES_P1_MODEI(i)	(((i)>>0)&0x03)
+
+#define ES_REG_DAC1_COUNT 0x24	/* R/W: DAC1 sample count register */
+#define ES_REG_DAC2_COUNT 0x28	/* R/W: DAC2 sample count register */
+#define ES_REG_ADC_COUNT  0x2c	/* R/W: ADC sample count register */
+#define   ES_REG_CURR_COUNT(i)  (((i)>>16)&0xffff)
+#define   ES_REG_COUNTO(o)	(((o)&0xffff)<<0)
+#define   ES_REG_COUNTM		(0xffff<<0)
+#define   ES_REG_COUNTI(i)	(((i)>>0)&0xffff)
+
+#define ES_REG_DAC1_FRAME 0x30	/* R/W: PAGE 0x0c; DAC1 frame address */
+#define ES_REG_DAC1_SIZE  0x34	/* R/W: PAGE 0x0c; DAC1 frame size */
+#define ES_REG_DAC2_FRAME 0x38	/* R/W: PAGE 0x0c; DAC2 frame address */
+#define ES_REG_DAC2_SIZE  0x3c	/* R/W: PAGE 0x0c; DAC2 frame size */
+#define ES_REG_ADC_FRAME  0x30	/* R/W: PAGE 0x0d; ADC frame address */
+#define ES_REG_ADC_SIZE	  0x34	/* R/W: PAGE 0x0d; ADC frame size */
+#define   ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16)
+#define   ES_REG_FCURR_COUNTM    (0xffff<<16)
+#define   ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc)
+#define   ES_REG_FSIZEO(o)	 (((o)&0xffff)<<0)
+#define   ES_REG_FSIZEM		 (0xffff<<0)
+#define   ES_REG_FSIZEI(i)	 (((i)>>0)&0xffff)
+#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */
+#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */
+
+#define ES_REG_UART_FIFO  0x30	/* R/W: PAGE 0x0e; UART FIFO register */
+#define   ES_REG_UF_VALID	 (1<<8)
+#define   ES_REG_UF_BYTEO(o)	 (((o)&0xff)<<0)
+#define   ES_REG_UF_BYTEM	 (0xff<<0)
+#define   ES_REG_UF_BYTEI(i)	 (((i)>>0)&0xff)
+
+
+/*
+ *  Pages
+ */
+
+#define ES_PAGE_DAC	0x0c
+#define ES_PAGE_ADC	0x0d
+#define ES_PAGE_UART	0x0e
+#define ES_PAGE_UART1	0x0f
+
+/*
+ *  Sample rate converter addresses
+ */
+
+#define ES_SMPREG_DAC1		0x70
+#define ES_SMPREG_DAC2		0x74
+#define ES_SMPREG_ADC		0x78
+#define ES_SMPREG_VOL_ADC	0x6c
+#define ES_SMPREG_VOL_DAC1	0x7c
+#define ES_SMPREG_VOL_DAC2	0x7e
+#define ES_SMPREG_TRUNC_N	0x00
+#define ES_SMPREG_INT_REGS	0x01
+#define ES_SMPREG_ACCUM_FRAC	0x02
+#define ES_SMPREG_VFREQ_FRAC	0x03
+
+/*
+ *  Some contants
+ */
+
+#define ES_1370_SRCLOCK	   1411200
+#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2)
+
+/*
+ *  Open modes
+ */
+
+#define ES_MODE_PLAY1	0x0001
+#define ES_MODE_PLAY2	0x0002
+#define ES_MODE_CAPTURE	0x0004
+
+#define ES_MODE_OUTPUT	0x0001	/* for MIDI */
+#define ES_MODE_INPUT	0x0002	/* for MIDI */
+
+/*
+
+ */
+
+typedef struct _snd_ensoniq ensoniq_t;
+
+struct _snd_ensoniq {
+	spinlock_t reg_lock;
+
+	int irq;
+
+	unsigned long playback1size;
+	unsigned long playback2size;
+	unsigned long capture3size;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned int mode;
+	unsigned int uartm;	/* UART mode */
+
+	unsigned int ctrl;	/* control register */
+	unsigned int sctrl;	/* serial control register */
+	unsigned int cssr;	/* control status register */
+	unsigned int uartc;	/* uart control register */
+	unsigned int rev;	/* chip revision */
+
+	union {
+		struct {
+			ac97_t *ac97;
+		} es1371;
+		struct {
+			int pclkdiv_lock;
+			ak4531_t *ak4531;
+		} es1370;
+	} u;
+
+	struct pci_dev *pci;
+	unsigned short subsystem_vendor_id;
+	unsigned short subsystem_device_id;
+	snd_card_t *card;
+	snd_pcm_t *pcm1;	/* DAC1/ADC PCM */
+	snd_pcm_t *pcm2;	/* DAC2 PCM */
+	snd_pcm_substream_t *playback1_substream;
+	snd_pcm_substream_t *playback2_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p1_dma_size;
+	unsigned int p2_dma_size;
+	unsigned int c_dma_size;
+	unsigned int p1_period_size;
+	unsigned int p2_period_size;
+	unsigned int c_period_size;
+	snd_rawmidi_t *rmidi;
+	snd_rawmidi_substream_t *midi_input;
+	snd_rawmidi_substream_t *midi_output;
+
+	snd_info_entry_t *proc_entry;
+
+#ifdef CHIP1370
+	unsigned char *bugbuf;
+	dma_addr_t bugbuf_addr;
+#endif
+};
+
+static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_audiopci_ids[] __devinitdata = {
+#ifdef CHIP1370
+	{ 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1370 */
+#endif
+#ifdef CHIP1371
+	{ 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1371 */
+	{ 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1373 - CT5880 */
+	{ 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Ectiva EV1938 */
+#endif
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_audiopci_ids);
+
+/*
+ *  constants
+ */
+
+#define POLL_COUNT	0xa000
+
+#ifdef CHIP1370
+static unsigned int snd_es1370_fixed_rates[] =
+	{5512, 11025, 22050, 44100};
+static snd_pcm_hw_constraint_list_t snd_es1370_hw_constraints_rates = {
+	count: 4, 
+	list: snd_es1370_fixed_rates,
+	mask: 0,
+};
+static ratnum_t es1370_clock = {
+	num: ES_1370_SRCLOCK,
+	den_min: 29, 
+	den_max: 353,
+	den_step: 1,
+};
+static snd_pcm_hw_constraint_ratnums_t snd_es1370_hw_constraints_clock = {
+	nrats: 1,
+	rats: &es1370_clock,
+};
+#else
+static ratden_t es1371_dac_clock = {
+	num_min: 3000 * (1 << 15),
+	num_max: 48000 * (1 << 15),
+	num_step: 3000,
+	den: 1 << 15,
+};
+static snd_pcm_hw_constraint_ratdens_t snd_es1371_hw_constraints_dac_clock = {
+	nrats: 1,
+	rats: &es1371_dac_clock,
+};
+static ratnum_t es1371_adc_clock = {
+	num: 48000 << 15,
+	den_min: 32768, 
+	den_max: 393216,
+	den_step: 1,
+};
+static snd_pcm_hw_constraint_ratnums_t snd_es1371_hw_constraints_adc_clock = {
+	nrats: 1,
+	rats: &es1371_adc_clock,
+};
+#endif
+static const unsigned int snd_ensoniq_sample_shift[] =
+	{0, 1, 1, 2};
+
+/*
+ *  common I/O routines
+ */
+
+#ifdef CHIP1371
+
+static unsigned int snd_es1371_wait_src_ready(ensoniq_t * ensoniq)
+{
+	unsigned int t, r = 0;
+
+	for (t = 0; t < POLL_COUNT; t++) {
+		r = inl(ES_REG(ensoniq, 1371_SMPRATE));
+		if ((r & ES_1371_SRC_RAM_BUSY) == 0)
+			return r;
+	}
+	snd_printk("wait source ready timeout 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_SMPRATE), r);
+	return 0;
+}
+
+static unsigned int snd_es1371_src_read(ensoniq_t * ensoniq, unsigned short reg)
+{
+	unsigned int temp, i, orig, r;
+
+	/* wait for ready */
+	temp = orig = snd_es1371_wait_src_ready(ensoniq);
+
+	/* expose the SRC state bits */
+	r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+		    ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+
+	/* now, wait for busy and the correct time to read */
+	temp = snd_es1371_wait_src_ready(ensoniq);
+	
+	if ((temp & 0x00870000) != 0x00010000) {
+		/* wait for the right state */
+		for (i = 0; i < POLL_COUNT; i++) {
+			temp = inl(ES_REG(ensoniq, 1371_SMPRATE));
+			if ((temp & 0x00870000) == 0x00010000)
+				break;
+		}
+	}
+
+	/* hide the state bits */	
+	r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+		   ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg);
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	
+	return temp;
+}
+
+static void snd_es1371_src_write(ensoniq_t * ensoniq,
+				 unsigned short reg, unsigned short data)
+{
+	unsigned int r;
+
+	r = snd_es1371_wait_src_ready(ensoniq) &
+	    (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+	     ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data);
+	outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE));
+}
+
+#endif /* CHIP1371 */
+
+#ifdef CHIP1370
+
+static void snd_es1370_codec_write(ak4531_t *ak4531,
+				   unsigned short reg, unsigned short val)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return);
+	unsigned long flags;
+	signed long end_time = jiffies + HZ / 10;
+
+#if 0
+	printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n", reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+#endif
+	do {
+		spin_lock_irqsave(&ensoniq->reg_lock, flags);
+		if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
+			outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+			spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+			return;
+		}
+		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+#if 0
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+#endif
+	} while ((signed long)(end_time - jiffies) > 0);
+	snd_printk("codec write timeout, status = 0x%x\n", inl(ES_REG(ensoniq, STATUS)));
+}
+
+#endif /* CHIP1370 */
+
+#ifdef CHIP1371
+
+static void snd_es1371_codec_write(ac97_t *ac97,
+				   unsigned short reg, unsigned short val)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return);
+	unsigned long flags;
+	unsigned int t, x;
+
+	for (t = 0; t < POLL_COUNT; t++) {
+		spin_lock_irqsave(&ensoniq->reg_lock, flags);
+		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+			/* save the current state for latter */
+			x = snd_es1371_wait_src_ready(ensoniq);
+			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+			     ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for not busy (state 0) first to avoid
+			   transition states */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000)
+					break;
+			}
+			/* wait for a SAFE time to write addr/data and then do it, dammit */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000)
+					break;
+			}
+			outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
+			/* restore SRC reg */
+			snd_es1371_wait_src_ready(ensoniq);
+			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+			spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+			return;
+		}
+		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	}
+	snd_printk("codec write timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+}
+
+static unsigned short snd_es1371_codec_read(ac97_t *ac97,
+					    unsigned short reg)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return -ENXIO);
+	unsigned long flags;
+	unsigned int t, x, fail = 0;
+
+      __again:
+	for (t = 0; t < POLL_COUNT; t++) {
+		spin_lock_irqsave(&ensoniq->reg_lock, flags);
+		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+			/* save the current state for latter */
+			x = snd_es1371_wait_src_ready(ensoniq);
+			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+			     ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for not busy (state 0) first to avoid
+			   transition states */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000)
+					break;
+			}
+			/* wait for a SAFE time to write addr/data and then do it, dammit */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000)
+					break;
+			}
+			outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));
+			/* restore SRC reg */
+			snd_es1371_wait_src_ready(ensoniq);
+			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for WIP again */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP))
+					break;		
+			}
+			/* now wait for the stinkin' data (RDY) */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
+					spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+					return ES_1371_CODEC_READ(x);
+				}
+			}
+			spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+			if (++fail > 10) {
+				snd_printk("codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC)));
+				return 0;
+			}
+			goto __again;
+		}
+		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	}
+	snd_printk("es1371: codec read timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+	return 0;
+}
+
+static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate)
+{
+	unsigned int n, truncm, freq, result;
+
+	n = rate / 3000;
+	if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+		n--;
+	truncm = (21 * n - 1) | 1;
+	freq = ((48000UL << 15) / rate) * n;
+	result = (48000UL << 15) / (freq / n);
+	if (rate >= 24000) {
+		if (truncm > 239)
+			truncm = 239;
+		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+				(((239 - truncm) >> 1) << 9) | (n << 4));
+	} else {
+		if (truncm > 119)
+			truncm = 119;
+		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+				0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+	}
+	snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) |
+				     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
+}
+
+static void snd_es1371_dac1_rate(ensoniq_t * ensoniq, unsigned int rate)
+{
+	unsigned int freq, r;
+
+	freq = ((rate << 15) + 1500) / 3000;
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)) | ES_1371_DIS_P1;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1));
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+}
+
+static void snd_es1371_dac2_rate(ensoniq_t * ensoniq, unsigned int rate)
+{
+	unsigned int freq, r;
+
+	freq = ((rate << 15) + 1500) / 3000;
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)) | ES_1371_DIS_P2;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1));
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+}
+
+#endif /* CHIP1371 */
+
+static int snd_ensoniq_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	{
+		unsigned int what = 0;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == ensoniq->playback1_substream) {
+				what |= ES_P1_PAUSE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->playback2_substream) {
+				what |= ES_P2_PAUSE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->capture_substream)
+				return -EINVAL;
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&ensoniq->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+			ensoniq->sctrl |= what;
+		else
+			ensoniq->sctrl &= ~what;
+		outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+		spin_unlock(&ensoniq->reg_lock);
+		break;
+	}
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == ensoniq->playback1_substream) {
+				what |= ES_DAC1_EN;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->playback2_substream) {
+				what |= ES_DAC2_EN;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->capture_substream) {
+				what |= ES_ADC_EN;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&ensoniq->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			ensoniq->ctrl |= what;
+		else
+			ensoniq->ctrl &= ~what;
+		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+		spin_unlock(&ensoniq->reg_lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_ensoniq_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ensoniq_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ensoniq_playback1_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int mode = 0;
+
+	ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		mode |= 0x02;
+	if (runtime->channels > 1)
+		mode |= 0x01;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ensoniq->ctrl &= ~ES_DAC1_EN;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+	outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
+	outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
+	ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
+	ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT));
+#ifdef CHIP1370
+	ensoniq->ctrl &= ~ES_1370_WTSRSELM;
+	switch (runtime->rate) {
+	case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
+	case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
+	case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
+	case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
+	default: snd_BUG();
+	}
+#else
+	snd_es1371_dac1_rate(ensoniq, runtime->rate);
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_playback2_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int mode = 0;
+
+	ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		mode |= 0x02;
+	if (runtime->channels > 1)
+		mode |= 0x01;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ensoniq->ctrl &= ~ES_DAC2_EN;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+	outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
+	outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
+	ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
+			    ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
+	ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
+			  ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT));
+#ifdef CHIP1370
+	if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
+		ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+		ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+		ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
+	}
+#else
+	snd_es1371_dac2_rate(ensoniq, runtime->rate);
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int mode = 0;
+
+	ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		mode |= 0x02;
+	if (runtime->channels > 1)
+		mode |= 0x01;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ensoniq->ctrl &= ~ES_ADC_EN;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+	outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
+	outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
+	ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
+	ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT));
+#ifdef CHIP1370
+	if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
+		ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+		ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+		ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
+	}
+#else
+	snd_es1371_adc_rate(ensoniq, runtime->rate);
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	spin_lock(&ensoniq->reg_lock);
+	if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) {
+		outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE)));
+		ptr = bytes_to_frames(substream->runtime, ptr);
+	} else {
+		ptr = 0;
+	}
+	spin_unlock(&ensoniq->reg_lock);
+	return ptr;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	spin_lock(&ensoniq->reg_lock);
+	if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) {
+		outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE)));
+		ptr = bytes_to_frames(substream->runtime, ptr);
+	} else {
+		ptr = 0;
+	}
+	spin_unlock(&ensoniq->reg_lock);
+	return ptr;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	spin_lock(&ensoniq->reg_lock);
+	if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) {
+		outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE)));
+		ptr = bytes_to_frames(substream->runtime, ptr);
+	} else {
+		ptr = 0;
+	}
+	spin_unlock(&ensoniq->reg_lock);
+	return ptr;
+}
+
+static snd_pcm_hardware_t snd_ensoniq_playback1 =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:
+#ifndef CHIP1370
+				SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+#else
+				(SNDRV_PCM_RATE_KNOT | 	/* 5512Hz rate */
+				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | 
+				 SNDRV_PCM_RATE_44100),
+#endif
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ensoniq_playback2 =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | 
+				 SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ensoniq_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	ensoniq->mode |= ES_MODE_PLAY1;
+	ensoniq->playback1_substream = substream;
+	runtime->hw = snd_ensoniq_playback1;
+	snd_pcm_set_sync(substream);
+#ifdef CHIP1370
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				   &snd_es1370_hw_constraints_rates);
+#else
+	snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1371_hw_constraints_dac_clock);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_playback2_open(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	ensoniq->mode |= ES_MODE_PLAY2;
+	ensoniq->playback2_substream = substream;
+	runtime->hw = snd_ensoniq_playback2;
+	snd_pcm_set_sync(substream);
+#ifdef CHIP1370
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1370_hw_constraints_clock);
+#else
+	snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1371_hw_constraints_dac_clock);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_capture_open(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	ensoniq->mode |= ES_MODE_CAPTURE;
+	ensoniq->capture_substream = substream;
+	runtime->hw = snd_ensoniq_capture;
+	snd_pcm_set_sync(substream);
+#ifdef CHIP1370
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1370_hw_constraints_clock);
+#else
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1371_hw_constraints_adc_clock);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_playback1_close(snd_pcm_substream_t * substream)
+{
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+
+	ensoniq->playback1_substream = NULL;
+	ensoniq->mode &= ~ES_MODE_PLAY1;
+	return 0;
+}
+
+static int snd_ensoniq_playback2_close(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+
+	ensoniq->playback2_substream = NULL;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+#ifdef CHIP1370
+	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
+#endif
+	ensoniq->mode &= ~ES_MODE_PLAY2;
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_capture_close(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_pcm_substream_chip(substream);
+
+	ensoniq->capture_substream = NULL;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+#ifdef CHIP1370
+	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE;
+#endif
+	ensoniq->mode &= ~ES_MODE_CAPTURE;
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ensoniq_playback1_ops = {
+	open:		snd_ensoniq_playback1_open,
+	close:		snd_ensoniq_playback1_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ensoniq_hw_params,
+	hw_free:	snd_ensoniq_hw_free,
+	prepare:	snd_ensoniq_playback1_prepare,
+	trigger:	snd_ensoniq_trigger,
+	pointer:	snd_ensoniq_playback1_pointer,
+};
+
+static snd_pcm_ops_t snd_ensoniq_playback2_ops = {
+	open:		snd_ensoniq_playback2_open,
+	close:		snd_ensoniq_playback2_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ensoniq_hw_params,
+	hw_free:	snd_ensoniq_hw_free,
+	prepare:	snd_ensoniq_playback2_prepare,
+	trigger:	snd_ensoniq_trigger,
+	pointer:	snd_ensoniq_playback2_pointer,
+};
+
+static snd_pcm_ops_t snd_ensoniq_capture_ops = {
+	open:		snd_ensoniq_capture_open,
+	close:		snd_ensoniq_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ensoniq_hw_params,
+	hw_free:	snd_ensoniq_hw_free,
+	prepare:	snd_ensoniq_capture_prepare,
+	trigger:	snd_ensoniq_trigger,
+	pointer:	snd_ensoniq_capture_pointer,
+};
+
+static void snd_ensoniq_pcm_free(snd_pcm_t *pcm)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return);
+	ensoniq->pcm1 = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+#ifdef CHIP1370
+	err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
+#else
+	err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
+#endif
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops);
+
+	pcm->private_data = ensoniq;
+	pcm->private_free = snd_ensoniq_pcm_free;
+	pcm->info_flags = 0;
+#ifdef CHIP1370
+	strcpy(pcm->name, "ES1370 DAC2/ADC");
+#else
+	strcpy(pcm->name, "ES1371 DAC2/ADC");
+#endif
+	ensoniq->pcm1 = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static void snd_ensoniq_pcm_free2(snd_pcm_t *pcm)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return);
+	ensoniq->pcm2 = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+#ifdef CHIP1370
+	err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
+#else
+	err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
+#endif
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops);
+
+	pcm->private_data = ensoniq;
+	pcm->private_free = snd_ensoniq_pcm_free2;
+	pcm->info_flags = 0;
+#ifdef CHIP1370
+	strcpy(pcm->name, "ES1370 DAC1");
+#else
+	strcpy(pcm->name, "ES1371 DAC1");
+#endif
+	ensoniq->pcm2 = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer section
+ */
+
+#ifdef CHIP1371
+
+#define ES1371_SPDIF(xname) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_es1371_spdif_info, \
+  get: snd_es1371_spdif_get, put: snd_es1371_spdif_put }
+
+static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+        uinfo->count = 1;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 1;
+        return 0;
+}
+
+static int snd_es1371_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1371_SPDIF_EN ? 1 : 0;
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int nval1, nval2;
+	int change;
+	
+	nval1 = ucontrol->value.integer.value[0] ? ES_1371_SPDIF_EN : 0;
+	nval2 = ucontrol->value.integer.value[0] ? ES_1371_ST_SPDIF_EN : 0;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	change = (ensoniq->ctrl & ES_1371_SPDIF_EN) != nval1;
+	ensoniq->ctrl &= ~ES_1371_SPDIF_EN;
+	ensoniq->ctrl |= nval1;
+	ensoniq->cssr &= ~ES_1371_ST_SPDIF_EN;
+	ensoniq->cssr |= nval2;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_es1371_mixer_spdif __devinitdata =
+ES1371_SPDIF("IEC958 Playback Switch");
+
+static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return);
+	ensoniq->u.es1371.ac97 = NULL;
+}
+
+static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq)
+{
+	snd_card_t *card = ensoniq->card;
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_es1371_codec_write;
+	ac97.read = snd_es1371_codec_read;
+	ac97.private_data = ensoniq;
+	ac97.private_free = snd_ensoniq_mixer_free_ac97;
+	if ((err = snd_ac97_mixer(card, &ac97, &ensoniq->u.es1371.ac97)) < 0)
+		return err;
+	if (ensoniq->rev >= ES1371REV_ES1373_A)
+		snd_ctl_add(card, snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq));
+	return 0;
+}
+
+#endif /* CHIP1371 */
+
+#define ENSONIQ_CONTROL(xname, mask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_ensoniq_control_info, \
+  get: snd_ensoniq_control_get, put: snd_ensoniq_control_put, \
+  private_value: mask }
+
+static int snd_ensoniq_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+        uinfo->count = 1;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 1;
+        return 0;
+}
+
+static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int mask = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0;
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int mask = kcontrol->private_value;
+	unsigned int nval;
+	int change;
+	
+	nval = ucontrol->value.integer.value[0] ? mask : 0;
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	change = (ensoniq->ctrl & mask) != nval;
+	ensoniq->ctrl &= ~mask;
+	ensoniq->ctrl |= nval;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return change;
+}
+
+#ifdef CHIP1370
+
+#define ES1370_CONTROLS 2
+
+static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = {
+ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0),
+ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1)
+};
+
+static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return);
+	ensoniq->u.es1370.ak4531 = NULL;
+}
+
+static int __devinit snd_ensoniq_1370_mixer(ensoniq_t * ensoniq)
+{
+	snd_card_t *card = ensoniq->card;
+	ak4531_t ak4531;
+	int err, idx;
+
+	/* try reset AK4531 */
+	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC));
+	udelay(100);
+	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC));
+	udelay(100);
+
+	memset(&ak4531, 0, sizeof(ak4531));
+	ak4531.write = snd_es1370_codec_write;
+	ak4531.private_data = ensoniq;
+	ak4531.private_free = snd_ensoniq_mixer_free_ak4531;
+	if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0)
+		return err;
+	for (idx = 0; idx < ES1370_CONTROLS; idx++)
+		snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq));
+	return 0;
+}
+
+#endif /* CHIP1370 */
+
+/*
+ *  General Switches...
+ */
+
+static snd_kcontrol_new_t snd_ensoniq_control_joystick __devinitdata =
+ENSONIQ_CONTROL("Joystick Enable", ES_JYSTK_EN);
+
+#ifdef CHIP1371
+
+#define ES1371_JOYSTICK_ADDR(xname) \
+{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_es1371_joystick_addr_info, \
+  get: snd_es1371_joystick_addr_get, put: snd_es1371_joystick_addr_put }
+
+static int snd_es1371_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+        uinfo->count = 1;
+        uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item >= 3)
+		uinfo->value.enumerated.item = 2;
+	sprintf(uinfo->value.enumerated.name, "port 0x%x", (uinfo->value.enumerated.item * 8) + 0x200);
+        return 0;
+}
+
+static int snd_es1371_joystick_addr_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = ES_1371_JOY_ASELI(ensoniq->ctrl);
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_es1371_joystick_addr_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int nval;
+	int change;
+	
+	nval = ES_1371_JOY_ASEL(ucontrol->value.integer.value[0]);
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	change = (ensoniq->ctrl & ES_1371_JOY_ASELM) != nval;
+	ensoniq->ctrl &= ~ES_1371_JOY_ASELM;
+	ensoniq->ctrl |= nval;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_es1371_joystick_addr __devinitdata =
+ES1371_JOYSTICK_ADDR("Joystick Address");
+
+#endif /* CHIP1371 */
+
+/*
+
+ */
+
+static void snd_ensoniq_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, entry->private_data, return);
+
+#ifdef CHIP1370
+	snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
+#else
+	snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
+#endif
+	snd_iprintf(buffer, "Joystick enable  : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
+#ifdef CHIP1370
+	snd_iprintf(buffer, "MIC +5V bias     : %s\n", ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
+	snd_iprintf(buffer, "Line In to AOUT  : %s\n", ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
+#else
+	snd_iprintf(buffer, "Joystick port    : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
+#endif
+}
+
+static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(ensoniq->card, "audiopci", ensoniq->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ensoniq;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_ensoniq_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ensoniq->proc_entry = entry;
+}
+
+static void snd_ensoniq_proc_done(ensoniq_t * ensoniq)
+{
+	if (ensoniq->proc_entry) {
+		snd_info_unregister(ensoniq->proc_entry);
+		ensoniq->proc_entry = NULL;
+	}
+}
+
+/*
+
+ */
+
+static int snd_ensoniq_free(ensoniq_t *ensoniq)
+{
+	snd_ensoniq_proc_done(ensoniq);
+	if (ensoniq->irq < 0)
+		goto __hw_end;
+#ifdef CHIP1370
+	outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL));	/* switch everything off */
+	outl(0, ES_REG(ensoniq, SERIAL));	/* clear serial interface */
+#else
+	outl(0, ES_REG(ensoniq, CONTROL));	/* switch everything off */
+	outl(0, ES_REG(ensoniq, SERIAL));	/* clear serial interface */
+#endif
+	synchronize_irq();
+	pci_set_power_state(ensoniq->pci, 3);
+      __hw_end:
+#ifdef CHIP1370
+	if (ensoniq->bugbuf)
+		snd_free_pci_pages(ensoniq->pci, 16, ensoniq->bugbuf, ensoniq->bugbuf_addr);
+#endif
+	if (ensoniq->res_port) {
+		release_resource(ensoniq->res_port);
+		kfree_nocheck(ensoniq->res_port);
+	}
+	if (ensoniq->irq >= 0)
+		free_irq(ensoniq->irq, (void *)ensoniq);
+	snd_magic_kfree(ensoniq);
+	return 0;
+}
+
+static int snd_ensoniq_dev_free(snd_device_t *device)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, device->device_data, return -ENXIO);
+	return snd_ensoniq_free(ensoniq);
+}
+
+#ifdef CHIP1371
+static struct {
+	unsigned short svid;		/* subsystem vendor ID */
+	unsigned short sdid;		/* subsystem device ID */
+} es1371_amplifier_hack[] = {
+	{ svid: 0x107b, sdid: 0x2150 },	/* Gateway Solo 2150 */
+	{ svid: 0x13bd, sdid: 0x100c },	/* EV1938 on Mebius PC-MJ100V */
+	{ svid: 0x1102, sdid: 0x5938 },	/* Targa Xtender300 */
+	{ svid: 0x1102, sdid: 0x8938 }, /* IPC Topnote G notebook */
+	{ svid: PCI_ANY_ID, sdid: PCI_ANY_ID }
+};
+static struct {
+	unsigned short vid;		/* vendor ID */
+	unsigned short did;		/* device ID */
+	unsigned char rev;		/* revision */
+} es1371_ac97_reset_hack[] = {
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_C },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_D },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_E },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_CT5880_A },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_ES1373_8 },
+	{ vid: PCI_ANY_ID, did: PCI_ANY_ID, rev: 0 }
+};
+#endif
+
+static int __devinit snd_ensoniq_create(snd_card_t * card,
+				     struct pci_dev *pci,
+				     ensoniq_t ** rensoniq)
+{
+	ensoniq_t *ensoniq;
+	unsigned short cmdw;
+	unsigned char cmdb;
+#ifdef CHIP1371
+	int idx;
+#endif
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_ensoniq_dev_free,
+	};
+
+	*rensoniq = NULL;
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	ensoniq = snd_magic_kcalloc(ensoniq_t, 0, GFP_KERNEL);
+	if (ensoniq == NULL)
+		return -ENOMEM;
+	spin_lock_init(&ensoniq->reg_lock);
+	ensoniq->card = card;
+	ensoniq->pci = pci;
+	ensoniq->irq = -1;
+	ensoniq->port = pci_resource_start(pci, 0);
+	if ((ensoniq->res_port = request_region(ensoniq->port, 0x40, "Ensoniq AudioPCI")) == NULL) {
+		snd_ensoniq_free(ensoniq);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ensoniq->port, ensoniq->port + 0x40 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, "Ensoniq AudioPCI", (void *)ensoniq)) {
+		snd_ensoniq_free(ensoniq);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	ensoniq->irq = pci->irq;
+#ifdef CHIP1370
+	if ((ensoniq->bugbuf = snd_malloc_pci_pages(pci, 16, &ensoniq->bugbuf_addr)) == NULL) {
+		snd_ensoniq_free(ensoniq);
+		snd_printk("unable to allocate space for phantom area - bugbuf\n");
+		return -EBUSY;
+	}
+#endif
+	pci_set_master(pci);
+	pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb);
+	ensoniq->rev = cmdb;
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw);
+	ensoniq->subsystem_vendor_id = cmdw;
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw);
+	ensoniq->subsystem_device_id = cmdw;
+	snd_ensoniq_proc_init(ensoniq);
+#ifdef CHIP1370
+#if 0
+	ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
+#else	/* get microphone working */
+	ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
+#endif
+	ensoniq->sctrl = 0;
+	/* initialize the chips */
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+	outl(ensoniq->bugbuf_addr, ES_REG(ensoniq, PHANTOM_FRAME));
+	outl(0, ES_REG(ensoniq, PHANTOM_COUNT));
+#else
+	ensoniq->ctrl = 0;
+	ensoniq->sctrl = 0;
+	ensoniq->cssr = 0;
+	for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++)
+		if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid &&
+		    ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) {
+			ensoniq->ctrl |= ES_1371_GPIO_OUT(1);	/* turn amplifier on */
+			break;
+		}
+	/* initialize the chips */
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl(0, ES_REG(ensoniq, 1371_LEGACY));
+	for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+		if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
+		    pci->device == es1371_ac97_reset_hack[idx].did &&
+		    ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
+		        unsigned long tmo;
+			signed long tmo2;
+
+			ensoniq->cssr |= ES_1371_ST_AC97_RST;
+			outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+			/* need to delay around 20ms(bleech) to give
+			some CODECs enough time to wakeup */
+			tmo = jiffies + (HZ / 50) + 1;
+			while (1) {
+				tmo2 = tmo - jiffies;
+				if (tmo2 <= 0)
+					break;
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule_timeout(tmo2);
+			}
+			break;
+		}
+	/* AC'97 warm reset to start the bitclk */
+	outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL));
+	udelay(20);
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	/* Init the sample rate converter */
+	snd_es1371_wait_src_ready(ensoniq);	
+	outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE));
+	for (idx = 0; idx < 0x80; idx++)
+		snd_es1371_src_write(ensoniq, idx, 0);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
+	snd_es1371_adc_rate(ensoniq, 22050);
+	snd_es1371_dac1_rate(ensoniq, 22050);
+	snd_es1371_dac2_rate(ensoniq, 22050);
+	/* WARNING:
+	 * enabling the sample rate converter without properly programming
+	 * its parameters causes the chip to lock up (the SRC busy bit will
+	 * be stuck high, and I've found no way to rectify this other than
+	 * power cycle) - Thomas Sailer
+	 */
+	snd_es1371_wait_src_ready(ensoniq);
+	outl(0, ES_REG(ensoniq, 1371_SMPRATE));
+	/* try reset codec directly */
+	outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC));
+#endif
+	outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL));
+	outb(0x00, ES_REG(ensoniq, UART_RES));
+	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+	snd_ctl_add(card, snd_ctl_new1(&snd_ensoniq_control_joystick, ensoniq));
+#ifdef CHIP1371
+	snd_ctl_add(card, snd_ctl_new1(&snd_es1371_joystick_addr, ensoniq));
+#endif
+	synchronize_irq();
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) {
+		snd_ensoniq_free(ensoniq);
+		return err;
+	}
+
+	*rensoniq = ensoniq;
+	return 0;
+}
+
+/*
+ *  MIDI section
+ */
+
+static void snd_ensoniq_midi_interrupt(ensoniq_t * ensoniq)
+{
+	snd_rawmidi_t * rmidi = ensoniq->rmidi;
+	unsigned char status, mask, byte;
+
+	if (rmidi == NULL)
+		return;
+	/* do Rx at first */
+	spin_lock(&ensoniq->reg_lock);
+	mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
+	while (mask) {
+		status = inb(ES_REG(ensoniq, UART_STATUS));
+		if ((status & mask) == 0)
+			break;
+		byte = inb(ES_REG(ensoniq, UART_DATA));
+		spin_unlock(&ensoniq->reg_lock);
+		snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+		spin_lock(&ensoniq->reg_lock);
+	}
+	spin_unlock(&ensoniq->reg_lock);
+
+	/* do Tx at second */
+	spin_lock(&ensoniq->reg_lock);
+	mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0;
+	while (mask) {
+		status = inb(ES_REG(ensoniq, UART_STATUS));
+		if ((status & mask) == 0)
+			break;
+		if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) {
+			ensoniq->uartc &= ~ES_TXINTENM;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+			mask &= ~ES_TXRDY;
+		} else {
+			outb(byte, ES_REG(ensoniq, UART_DATA));
+		}
+	}
+	spin_unlock(&ensoniq->reg_lock);
+}
+
+static int snd_ensoniq_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ensoniq->uartm |= ES_MODE_INPUT;
+	ensoniq->midi_input = substream;
+	if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
+		outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	}
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	} else {
+		outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL));
+	}
+	ensoniq->midi_input = NULL;
+	ensoniq->uartm &= ~ES_MODE_INPUT;
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	ensoniq->uartm |= ES_MODE_OUTPUT;
+	ensoniq->midi_output = substream;
+	if (!(ensoniq->uartm & ES_MODE_INPUT)) {
+		outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	}
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ensoniq_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	if (!(ensoniq->uartm & ES_MODE_INPUT)) {
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	} else {
+		outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL));
+	}
+	ensoniq->midi_output = NULL;
+	ensoniq->uartm &= ~ES_MODE_OUTPUT;
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+	return 0;
+}
+
+static void snd_ensoniq_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return);
+	int idx;
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	if (up) {
+		if ((ensoniq->uartc & ES_RXINTEN) == 0) {
+			/* empty input FIFO */
+			for (idx = 0; idx < 32; idx++)
+				inb(ES_REG(ensoniq, UART_DATA));
+			ensoniq->uartc |= ES_RXINTEN;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	} else {
+		if (ensoniq->uartc & ES_RXINTEN) {
+			ensoniq->uartc &= ~ES_RXINTEN;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	}
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+}
+
+static void snd_ensoniq_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return);
+	unsigned char byte;
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	if (up) {
+		if (ES_TXINTENI(ensoniq->uartc) == 0) {
+			ensoniq->uartc |= ES_TXINTENO(1);
+			/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+			while (ES_TXINTENI(ensoniq->uartc) == 1 &&
+			       (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					ensoniq->uartc &= ~ES_TXINTENM;
+				} else {
+					outb(byte, ES_REG(ensoniq, UART_DATA));
+				}
+			}
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	} else {
+		if (ES_TXINTENI(ensoniq->uartc) == 1) {
+			ensoniq->uartc &= ~ES_TXINTENM;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	}
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+}
+
+static snd_rawmidi_ops_t snd_ensoniq_midi_output =
+{
+	open:           snd_ensoniq_midi_output_open,
+	close:          snd_ensoniq_midi_output_close,
+	trigger:        snd_ensoniq_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_ensoniq_midi_input =
+{
+	open:           snd_ensoniq_midi_input_open,
+	close:          snd_ensoniq_midi_input_close,
+	trigger:        snd_ensoniq_midi_input_trigger,
+};
+
+static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
+		return err;
+#ifdef CHIP1370
+	strcpy(rmidi->name, "ES1370");
+#else
+	strcpy(rmidi->name, "ES1371");
+#endif
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = ensoniq;
+	ensoniq->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return 0;
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, dev_id, return);
+	unsigned int status, sctrl;
+
+	if (ensoniq == NULL)
+		return;
+
+	status = inl(ES_REG(ensoniq, STATUS));
+	if (!(status & ES_INTR))
+		return;
+
+	spin_lock(&ensoniq->reg_lock);
+	sctrl = ensoniq->sctrl;
+	if (status & ES_DAC1)
+		sctrl &= ~ES_P1_INT_EN;
+	if (status & ES_DAC2)
+		sctrl &= ~ES_P2_INT_EN;
+	if (status & ES_ADC)
+		sctrl &= ~ES_R1_INT_EN;
+	outl(sctrl, ES_REG(ensoniq, SERIAL));
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	spin_unlock(&ensoniq->reg_lock);
+
+	if (status & ES_UART)
+		snd_ensoniq_midi_interrupt(ensoniq);
+	if ((status & ES_DAC2) && ensoniq->playback2_substream)
+		snd_pcm_period_elapsed(ensoniq->playback2_substream);
+	if ((status & ES_ADC) && ensoniq->capture_substream)
+		snd_pcm_period_elapsed(ensoniq->capture_substream);
+	if ((status & ES_DAC1) && ensoniq->playback1_substream)
+		snd_pcm_period_elapsed(ensoniq->playback1_substream);
+}
+
+static int __devinit snd_audiopci_probe(struct pci_dev *pci,
+					const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	ensoniq_t *ensoniq;
+	int err, pcm_devs[2];
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pcm_devs[0] = 0; pcm_devs[1] = 1;
+#ifdef CHIP1370
+	if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+#ifdef CHIP1371
+	if ((err = snd_ensoniq_1371_mixer(ensoniq)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef CHIP1370
+	strcpy(card->driver, "ES1370");
+#endif
+#ifdef CHIP1371
+	strcpy(card->driver, "ES1371");
+#endif
+	strcpy(card->shortname, "Ensoniq AudioPCI");
+	sprintf(card->longname, "%s %s at 0x%lx, irq %i",
+		card->shortname,
+		card->driver,
+		ensoniq->port,
+		ensoniq->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_audiopci_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "Ensoniq AudioPCI",
+	id_table: snd_audiopci_ids,
+	probe: snd_audiopci_probe,
+	remove: __devexit_p(snd_audiopci_remove),
+};
+	
+static int __init alsa_card_ens137x_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Ensoniq AudioPCI soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ens137x_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ens137x_init)
+module_exit(alsa_card_ens137x_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ens1370=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_ens137x_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+#if defined(CHIP1370)
+__setup("snd-ens1370=", alsa_card_ens137x_setup);
+#elif defined(CHIP1371)
+__setup("snd-ens1371=", alsa_card_ens137x_setup);
+#endif
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/ens1371.c linux-2.4.19-pre5-mjc/sound/pci/ens1371.c
--- linux/sound/pci/ens1371.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ens1371.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,2 @@
+#define CHIP1371
+#include "ens1370.c"
diff -Nru linux/sound/pci/es1938.c linux-2.4.19-pre5-mjc/sound/pci/es1938.c
--- linux/sound/pci/es1938.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/es1938.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1720 @@
+/*
+ *  Driver for ESS Solo-1 (ES1938, ES1946) soundcard
+ *  Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
+ *                   Jaroslav Kysela <perex@suse.cz>,
+ *                   Thomas Sailer <sailer@ife.ee.ethz.ch>,
+ *                   Abramo Bagnara <abramo@alsa-project.org>,
+ *                   Markus Gruber <gruber@eikon.tum.de>
+ * 
+ * Rewritted from sonicvibes.c source.
+ *
+ *  TODO:
+ *    Rewrite better spinlocks
+ *
+ *
+ *   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
+ *
+ */
+
+/*
+  NOTES:
+  - Capture data is written unaligned starting from dma_base + 1 so I need to
+    disable mmap and to add a copy callback.
+  - After several cycle of the following:
+    while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done
+    a "playback write error (DMA or IRQ trouble?)" may happen.
+    This is due to playback interrupts not generated.
+    I suspect a timing issue.
+  - Sometimes the interrupt handler is invoked wrongly during playback.
+    This generate some harmless "Unexpected hw_pointer: wrong interrupt
+    acknowledge".
+    I've seen that using small period sizes.
+    Reproducible with:
+    mpg123 test.mp3 &
+    hdparm -t -T /dev/hda
+*/
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#ifndef LINUX_2_2
+#include <linux/gameport.h>
+#endif
+
+#define chip_t es1938_t
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaromir Koutek <miri@punknet.cz>");
+MODULE_DESCRIPTION("ESS Solo-1");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,ES1938},"
+		"{TerraTec,128i PCI}}");
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS		0x125d
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ES1938
+#define PCI_DEVICE_ID_ESS_ES1938	0x1969
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ESS Solo-1 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ESS Solo-1 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ESS Solo-1 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+
+#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x)
+
+#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x)
+
+#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x)
+
+#define SL_PCI_LEGACYCONTROL		0x40
+#define SL_PCI_CONFIG			0x50
+#define SL_PCI_DDMACONTROL		0x60
+
+#define ESSIO_REG_AUDIO2DMAADDR		0
+#define ESSIO_REG_AUDIO2DMACOUNT	4
+#define ESSIO_REG_AUDIO2MODE		6
+#define ESSIO_REG_IRQCONTROL		7
+
+#define ESSDM_REG_DMAADDR		0x00
+#define ESSDM_REG_DMACOUNT		0x04
+#define ESSDM_REG_DMACOMMAND		0x08
+#define ESSDM_REG_DMASTATUS		0x08
+#define ESSDM_REG_DMAMODE		0x0b
+#define ESSDM_REG_DMACLEAR		0x0d
+#define ESSDM_REG_DMAMASK		0x0f
+
+#define ESSSB_REG_FMLOWADDR		0x00
+#define ESSSB_REG_FMHIGHADDR		0x02
+#define ESSSB_REG_MIXERADDR		0x04
+#define ESSSB_REG_MIXERDATA		0x05
+
+#define ESSSB_IREG_AUDIO1		0x14
+#define ESSSB_IREG_MICMIX		0x1a
+#define ESSSB_IREG_RECSRC		0x1c
+#define ESSSB_IREG_MASTER		0x32
+#define ESSSB_IREG_FM			0x36
+#define ESSSB_IREG_AUXACD		0x38
+#define ESSSB_IREG_AUXB			0x3a
+#define ESSSB_IREG_PCSPEAKER		0x3c
+#define ESSSB_IREG_LINE			0x3e
+#define ESSSB_IREG_SPATCONTROL		0x50
+#define ESSSB_IREG_SPATLEVEL		0x52
+#define ESSSB_IREG_MASTER_LEFT		0x60
+#define ESSSB_IREG_MASTER_RIGHT		0x62
+#define ESSSB_IREG_MPU401CONTROL	0x64
+#define ESSSB_IREG_MICMIXRECORD		0x68
+#define ESSSB_IREG_AUDIO2RECORD		0x69
+#define ESSSB_IREG_AUXACDRECORD		0x6a
+#define ESSSB_IREG_FMRECORD		0x6b
+#define ESSSB_IREG_AUXBRECORD		0x6c
+#define ESSSB_IREG_MONO			0x6d
+#define ESSSB_IREG_LINERECORD		0x6e
+#define ESSSB_IREG_MONORECORD		0x6f
+#define ESSSB_IREG_AUDIO2SAMPLE		0x70
+#define ESSSB_IREG_AUDIO2MODE		0x71
+#define ESSSB_IREG_AUDIO2FILTER		0x72
+#define ESSSB_IREG_AUDIO2TCOUNTL	0x74
+#define ESSSB_IREG_AUDIO2TCOUNTH	0x76
+#define ESSSB_IREG_AUDIO2CONTROL1	0x78
+#define ESSSB_IREG_AUDIO2CONTROL2	0x7a
+#define ESSSB_IREG_AUDIO2		0x7c
+
+#define ESSSB_REG_RESET			0x06
+
+#define ESSSB_REG_READDATA		0x0a
+#define ESSSB_REG_WRITEDATA		0x0c
+#define ESSSB_REG_READSTATUS		0x0c
+
+#define ESSSB_REG_STATUS		0x0e
+
+#define ESS_CMD_EXTSAMPLERATE		0xa1
+#define ESS_CMD_FILTERDIV		0xa2
+#define ESS_CMD_DMACNTRELOADL		0xa4
+#define ESS_CMD_DMACNTRELOADH		0xa5
+#define ESS_CMD_ANALOGCONTROL		0xa8
+#define ESS_CMD_IRQCONTROL		0xb1
+#define ESS_CMD_DRQCONTROL		0xb2
+#define ESS_CMD_RECLEVEL		0xb4
+#define ESS_CMD_SETFORMAT		0xb6
+#define ESS_CMD_SETFORMAT2		0xb7
+#define ESS_CMD_DMACONTROL		0xb8
+#define ESS_CMD_DMATYPE			0xb9
+#define ESS_CMD_OFFSETLEFT		0xba	
+#define ESS_CMD_OFFSETRIGHT		0xbb
+#define ESS_CMD_READREG			0xc0
+#define ESS_CMD_ENABLEEXT		0xc6
+#define ESS_CMD_PAUSEDMA		0xd0
+#define ESS_CMD_ENABLEAUDIO1		0xd1
+#define ESS_CMD_STOPAUDIO1		0xd3
+#define ESS_CMD_AUDIO1STATUS		0xd8
+#define ESS_CMD_CONTDMA			0xd4
+#define ESS_CMD_TESTIRQ			0xf2
+
+#define ESS_RECSRC_MIC		0
+#define ESS_RECSRC_AUXACD	2
+#define ESS_RECSRC_AUXB		5
+#define ESS_RECSRC_LINE		6
+#define ESS_RECSRC_NONE		7
+
+#define DAC1 0x01
+#define ADC1 0x02
+#define DAC2 0x04
+
+/*
+
+ */
+
+typedef struct _snd_es1938 es1938_t;
+
+struct _snd_es1938 {
+	unsigned long dma1size;
+	unsigned long dma2size;
+	int irq;
+
+	unsigned long io_port;
+	struct resource *res_io_port;
+	unsigned long sb_port;
+	struct resource *res_sb_port;
+	unsigned long vc_port;
+	struct resource *res_vc_port;
+	unsigned long mpu_port;
+	struct resource *res_mpu_port;
+	unsigned long game_port;
+	struct resource *res_game_port;
+	unsigned long ddma_port;
+
+	unsigned char irqmask;
+	unsigned char revision;
+
+	snd_kcontrol_t *hw_volume;
+	snd_kcontrol_t *hw_switch;
+	snd_kcontrol_t *master_volume;
+	snd_kcontrol_t *master_switch;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_substream_t *capture_substream;
+	snd_pcm_substream_t *playback1_substream;
+	snd_pcm_substream_t *playback2_substream;
+	snd_kmixer_t *mixer;
+	snd_rawmidi_t *rmidi;
+
+	unsigned int dma1_size;
+	unsigned int dma2_size;
+	unsigned int dma1_start;
+	unsigned int dma2_start;
+	unsigned int dma1_shift;
+	unsigned int dma2_shift;
+	unsigned int active;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+        snd_info_entry_t *proc_entry;
+
+#ifndef LINUX_2_2
+	struct gameport gameport;
+#endif
+};
+
+static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_es1938_ids[] __devinitdata = {
+        { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* Solo-1 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
+
+#define RESET_LOOP_TIMEOUT	0x10000
+#define WRITE_LOOP_TIMEOUT	0x10000
+#define GET_LOOP_TIMEOUT	0x01000
+
+#undef REG_DEBUG
+/* -----------------------------------------------------------------
+ * Write to a mixer register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	outb(val, SLSB_REG(chip, MIXERDATA));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read from a mixer register
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg)
+{
+	int data;
+	unsigned long flags;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	data = inb(SLSB_REG(chip, MIXERDATA));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x now is %02x\n", reg, data);
+#endif
+	return data;
+}
+
+/* -----------------------------------------------------------------
+ * Write to some bits of a mixer register (return old value)
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+	unsigned long flags;
+	unsigned char old, new, oval;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	old = inb(SLSB_REG(chip, MIXERDATA));
+	oval = old & mask;
+	if (val != oval) {
+		new = (old & ~mask) | (val & mask);
+		outb(new, SLSB_REG(chip, MIXERDATA));
+#ifdef REG_DEBUG
+		snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+	}
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	return oval;
+}
+
+/* -----------------------------------------------------------------
+ * Write command to Controller Registers
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd)
+{
+	int i;
+	unsigned char v;
+	for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
+		if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
+			outb(cmd, SLSB_REG(chip, WRITEDATA));
+			return;
+		}
+	}
+	printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
+}
+
+/* -----------------------------------------------------------------
+ * Read the Read Data Buffer
+ * -----------------------------------------------------------------*/
+static int snd_es1938_get_byte(es1938_t *chip)
+{
+	int i;
+	unsigned char v;
+	for (i = GET_LOOP_TIMEOUT; i; i--)
+		if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
+			return inb(SLSB_REG(chip, READDATA));
+	snd_printk("get_byte timeout: status 0x02%x\n", v);
+	return -ENODEV;
+}
+
+/* -----------------------------------------------------------------
+ * Write value cmd register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, reg);
+	snd_es1938_write_cmd(chip, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read data from cmd register and return it
+ * -----------------------------------------------------------------*/
+static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg)
+{
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+	snd_es1938_write_cmd(chip, reg);
+	val = snd_es1938_get_byte(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x now is %02x\n", reg, val);
+#endif
+	return val;
+}
+
+/* -----------------------------------------------------------------
+ * Write data to cmd register and return old value
+ * -----------------------------------------------------------------*/
+static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+	unsigned long flags;
+	unsigned char old, new, oval;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+	snd_es1938_write_cmd(chip, reg);
+	old = snd_es1938_get_byte(chip);
+	oval = old & mask;
+	if (val != oval) {
+		snd_es1938_write_cmd(chip, reg);
+		new = (old & ~mask) | (val & mask);
+		snd_es1938_write_cmd(chip, new);
+#ifdef REG_DEBUG
+		snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return oval;
+}
+
+/* --------------------------------------------------------------------
+ * Reset the chip
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset(es1938_t *chip)
+{
+	int i;
+
+	outb(3, SLSB_REG(chip, RESET));
+	inb(SLSB_REG(chip, RESET));
+	outb(0, SLSB_REG(chip, RESET));
+	for (i = 0; i < RESET_LOOP_TIMEOUT; i++) {
+		if (inb(SLSB_REG(chip, STATUS)) & 0x80) {
+			if (inb(SLSB_REG(chip, READDATA)) == 0xaa)
+				goto __next;
+		}
+	}
+	snd_printk("ESS Solo-1 reset failed\n");
+
+     __next:
+	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
+
+	/* Demand transfer DMA: 4 bytes per DMA request */
+	snd_es1938_write(chip, ESS_CMD_DMATYPE, 2);
+
+	/* Change behaviour of register A1
+	   4x oversampling
+	   2nd channel DAC asynchronous */                                                      
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32);
+	/* enable/select DMA channel and IRQ channel */
+	snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50);
+	snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50);
+	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1);
+	/* Set spatializer parameters to recommended values */
+	snd_es1938_mixer_write(chip, 0x54, 0x8f);
+	snd_es1938_mixer_write(chip, 0x56, 0x95);
+	snd_es1938_mixer_write(chip, 0x58, 0x94);
+	snd_es1938_mixer_write(chip, 0x5a, 0x80);
+}
+
+/* --------------------------------------------------------------------
+ * Reset the FIFOs
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset_fifo(es1938_t *chip)
+{
+	outb(2, SLSB_REG(chip, RESET));
+	outb(0, SLSB_REG(chip, RESET));
+}
+
+static ratnum_t clocks[2] = {
+	{
+		num: 793800,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 768000,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+	nrats: 2,
+	rats: clocks,
+};
+
+
+static void snd_es1938_rate_set(es1938_t *chip, 
+				snd_pcm_substream_t *substream,
+				int mode)
+{
+	unsigned int bits, div0;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->rate_num == clocks[0].num)
+		bits = 128 - runtime->rate_den;
+	else
+		bits = 256 - runtime->rate_den;
+
+	/* set filter register */
+	div0 = 256 - 7160000*20/(8*82*runtime->rate);
+		
+	if (mode == DAC2) {
+		snd_es1938_mixer_write(chip, 0x70, bits);
+		snd_es1938_mixer_write(chip, 0x72, div0);
+	} else {
+		snd_es1938_write(chip, 0xA1, bits);
+		snd_es1938_write(chip, 0xA2, div0);
+	}
+}
+
+/* --------------------------------------------------------------------
+ * Configure Solo1 builtin DMA Controller
+ * --------------------------------------------------------------------*/
+
+static void snd_es1938_playback1_setdma(es1938_t *chip)
+{
+	outb(0x00, SLIO_REG(chip, AUDIO2MODE));
+	outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR));
+	outw(0, SLIO_REG(chip, AUDIO2DMACOUNT));
+	outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT));
+}
+
+static void snd_es1938_playback2_setdma(es1938_t *chip)
+{
+	/* Enable DMA controller */
+	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+	/* 1. Master reset */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+	/* 2. Mask DMA */
+	outb(1, SLDM_REG(chip, DMAMASK));
+	outb(0x18, SLDM_REG(chip, DMAMODE));
+	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+	/* 3. Unmask DMA */
+	outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+static void snd_es1938_capture_setdma(es1938_t *chip)
+{
+	/* Enable DMA controller */
+	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+	/* 1. Master reset */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+	/* 2. Mask DMA */
+	outb(1, SLDM_REG(chip, DMAMASK));
+	outb(0x14, SLDM_REG(chip, DMAMODE));
+	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+	/* 3. Unmask DMA */
+	outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+/* ----------------------------------------------------------------------
+ *
+ *                           *** PCM part ***
+ */
+
+static int snd_es1938_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	int val;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = 0x0f;
+		chip->active |= ADC1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = 0x00;
+		chip->active &= ~ADC1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+	return 0;
+}
+
+static int snd_es1938_playback1_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* According to the documentation this should be:
+		   0x13 but that value may random swap stereo channels */
+		snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93);
+		outb(0x0a, SLIO_REG(chip, AUDIO2MODE));
+		chip->active |= DAC2;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		outb(0, SLIO_REG(chip, AUDIO2MODE));
+		snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0);
+		chip->active &= ~DAC2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_es1938_playback2_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	int val;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = 5;
+		chip->active |= DAC1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = 0;
+		chip->active &= ~DAC1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+	return 0;
+}
+
+static int snd_es1938_playback_trigger(snd_pcm_substream_t *substream,
+				       int cmd)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_trigger(substream, cmd);
+	case 1:
+		return snd_es1938_playback2_trigger(substream, cmd);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------
+ * First channel for Extended Mode Audio 1 ADC Operation
+ * --------------------------------------------------------------------*/
+static int snd_es1938_capture_prepare(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+	chip->dma1_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma1_shift = 2 - mono - is8;
+
+	snd_es1938_reset_fifo(chip);
+	
+	/* program type */
+	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, ADC1);
+
+	count = 0x10000 - count;
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+	/* initialize and configure ADC */
+	snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71);
+	snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 | 
+		       (u ? 0x00 : 0x20) | 
+		       (is8 ? 0x00 : 0x04) | 
+		       (mono ? 0x40 : 0x08));
+
+	//	snd_es1938_reset_fifo(chip);	
+
+	/* 11. configure system interrupt controller and DMA controller */
+	snd_es1938_capture_setdma(chip);
+
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------------
+ * Second Audio channel DAC Operation
+ * ------------------------------------------------------------------------------*/
+static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma2_size = size;
+	chip->dma2_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma2_shift = 2 - mono - is8;
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, DAC2);
+
+	count >>= 1;
+	count = 0x10000 - count;
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff);
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8);
+
+	/* initialize and configure Audio 2 DAC */
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1));
+
+	/* program DMA */
+	snd_es1938_playback1_setdma(chip);
+	
+	return 0;
+}
+
+static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+	chip->dma1_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma1_shift = 2 - mono - is8;
+
+	count = 0x10000 - count;
+ 
+	/* reset */
+	snd_es1938_reset_fifo(chip);
+	
+	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, DAC1);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+	/* initialized and configure DAC */
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00);
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71);
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 
+			 0x90 | (mono ? 0x40 : 0x08) |
+			 (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20));
+
+	/* program DMA */
+	snd_es1938_playback2_setdma(chip);
+	
+	return 0;
+}
+
+static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_prepare(substream);
+	case 1:
+		return snd_es1938_playback2_prepare(substream);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+static snd_pcm_uframes_t snd_es1938_capture_pointer(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	size_t old, new;
+#if 1
+	/* This stuff is *needed*, don't ask why - AB */
+	old = inw(SLDM_REG(chip, DMACOUNT));
+	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+		old = new;
+	ptr = chip->dma1_size - 1 - new;
+#else
+	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+	return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback1_pointer(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+#if 1
+	ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT));
+#else
+	ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start;
+#endif
+	return ptr >> chip->dma2_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback2_pointer(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	size_t old, new;
+#if 1
+	/* This stuff is *needed*, don't ask why - AB */
+	old = inw(SLDM_REG(chip, DMACOUNT));
+	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+		old = new;
+	ptr = chip->dma1_size - 1 - new;
+#else
+	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+	return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback_pointer(snd_pcm_substream_t *substream)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_pointer(substream);
+	case 1:
+		return snd_es1938_playback2_pointer(substream);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+static int snd_es1938_capture_copy(snd_pcm_substream_t *substream,
+				   int channel,
+				   snd_pcm_uframes_t pos,
+				   void *dst,
+				   snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	pos <<= chip->dma1_shift;
+	count <<= chip->dma1_shift;
+	snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+	if (pos + count < chip->dma1_size)
+		memcpy(dst, runtime->dma_area + pos + 1, count);
+	else {
+		memcpy(dst, runtime->dma_area + pos + 1, count - 1);
+		((unsigned char *)dst)[count - 1] = runtime->dma_area[0];
+	}
+	return 0;
+}
+
+/* ----------------------------------------------------------------------
+ * Audio1 Capture (ADC)
+ * ----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_capture =
+{
+	info:			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		6000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		256,
+};
+
+/* -----------------------------------------------------------------------
+ * Audio2 Playback (DAC)
+ * -----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		6000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		256,
+};
+
+static int snd_es1938_capture_open(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (chip->playback2_substream)
+		return -EAGAIN;
+	if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma2size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+		return -ENOMEM;
+	chip->capture_substream = substream;
+	runtime->hw = snd_es1938_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+	return 0;
+}
+
+static int snd_es1938_playback_open(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	switch (substream->number) {
+	case 0:
+		if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+			return -ENOMEM;
+		chip->playback1_substream = substream;
+		break;
+	case 1:
+		if (chip->capture_substream)
+			return -EAGAIN;
+		if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+			return -ENOMEM;
+		chip->playback2_substream = substream;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	runtime->hw = snd_es1938_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+	return 0;
+}
+
+static int snd_es1938_capture_close(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->capture_substream = NULL;
+	snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+	return 0;
+}
+
+static int snd_es1938_playback_close(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	switch (substream->number) {
+	case 0:
+		chip->playback1_substream = NULL;
+		break;
+	case 1:
+		chip->playback2_substream = NULL;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_es1938_playback_ops = {
+	open:		snd_es1938_playback_open,
+	close:		snd_es1938_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	prepare:	snd_es1938_playback_prepare,
+	trigger:	snd_es1938_playback_trigger,
+	pointer:	snd_es1938_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es1938_capture_ops = {
+	open:		snd_es1938_capture_open,
+	close:		snd_es1938_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	prepare:	snd_es1938_capture_prepare,
+	trigger:	snd_es1938_capture_trigger,
+	pointer:	snd_es1938_capture_pointer,
+	copy:		snd_es1938_capture_copy,
+};
+
+static void snd_es1938_free_pcm(snd_pcm_t *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops);
+	
+	pcm->private_data = chip;
+	pcm->private_free = snd_es1938_free_pcm;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ESS Solo-1");
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/* -------------------------------------------------------------------
+ * 
+ *                       *** Mixer part ***
+ */
+
+static int snd_es1938_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es1938_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07;
+	return 0;
+}
+
+static int snd_es1938_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = ucontrol->value.enumerated.item[0];
+	
+	if (val > 7)
+		return -EINVAL;
+	return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val;
+}
+
+static int snd_es1938_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es1938_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = snd_es1938_mixer_read(chip, 0x50);
+	ucontrol->value.integer.value[0] = !!(val & 8);
+	return 0;
+}
+
+static int snd_es1938_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char oval, nval;
+	int change;
+	nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
+	oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c;
+	change = nval != oval;
+	if (change) {
+		snd_es1938_mixer_write(chip, 0x50, nval & ~0x04);
+		snd_es1938_mixer_write(chip, 0x50, nval);
+	}
+	return change;
+}
+
+static int snd_es1938_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 63;
+	return 0;
+}
+
+static int snd_es1938_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f;
+	ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f;
+	return 0;
+}
+
+static int snd_es1938_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es1938_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40);
+	ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40);
+	return 0;
+}
+
+static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol)
+{
+	es1938_t *chip = snd_magic_cast(es1938_t, _snd_kcontrol_chip(kcontrol), return);
+	chip->master_volume = NULL;
+	chip->master_switch = NULL;
+	chip->hw_volume = NULL;
+	chip->hw_switch = NULL;
+}
+
+static int snd_es1938_reg_bits(es1938_t *chip, unsigned char reg,
+			       unsigned char mask, unsigned char val)
+{
+	if (reg < 0xa0)
+		return snd_es1938_mixer_bits(chip, reg, mask, val);
+	else
+		return snd_es1938_bits(chip, reg, mask, val);
+}
+
+static int snd_es1938_reg_read(es1938_t *chip, unsigned char reg)
+{
+	if (reg < 0xa0)
+		return snd_es1938_mixer_read(chip, reg);
+	else
+		return snd_es1938_read(chip, reg);
+}
+
+#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1938_info_single, \
+  get: snd_es1938_get_single, put: snd_es1938_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1938_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int val;
+	
+	val = snd_es1938_reg_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es1938_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	mask <<= shift;
+	val <<= shift;
+	return snd_es1938_reg_bits(chip, reg, mask, val) != val;
+}
+
+#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1938_info_double, \
+  get: snd_es1938_get_double, put: snd_es1938_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1938_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	left = snd_es1938_reg_read(chip, left_reg);
+	if (left_reg != right_reg)
+		right = snd_es1938_reg_read(chip, right_reg);
+	else
+		right = left;
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es1938_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, mask1, mask2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	mask1 = mask << shift_left;
+	mask2 = mask << shift_right;
+	if (left_reg != right_reg) {
+		change = 0;
+		if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1)
+			change = 1;
+		if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2)
+			change = 1;
+	} else {
+		change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2, 
+					      val1 | val2) != (val1 | val2));
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_es1938_controls[] = {
+ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Volume",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es1938_info_hw_volume,
+	get: snd_es1938_get_hw_volume,
+},
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Switch",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es1938_info_hw_switch,
+	get: snd_es1938_get_hw_switch,
+},
+ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
+ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
+ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
+ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_es1938_info_mux,
+	get: snd_es1938_get_mux,
+	put: snd_es1938_put_mux,
+},
+ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
+ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "3D Control - Switch",
+	info: snd_es1938_info_spatializer_enable,
+	get: snd_es1938_get_spatializer_enable,
+	put: snd_es1938_put_spatializer_enable,
+},
+ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0)
+};
+
+
+/* ---------------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------------- */
+
+static int snd_es1938_free(es1938_t *chip)
+{
+#ifndef LINUX_2_2
+	if (chip->gameport.io)
+		gameport_unregister_port(&chip->gameport);
+#endif
+	if (chip->res_io_port) {
+		release_resource(chip->res_io_port);
+		kfree_nocheck(chip->res_io_port);
+	}
+	if (chip->res_sb_port) {
+		release_resource(chip->res_sb_port);
+		kfree_nocheck(chip->res_sb_port);
+	}
+	if (chip->res_vc_port) {
+		release_resource(chip->res_vc_port);
+		kfree_nocheck(chip->res_vc_port);
+	}
+	if (chip->res_mpu_port) {
+		release_resource(chip->res_mpu_port);
+		kfree_nocheck(chip->res_mpu_port);
+	}
+	if (chip->res_game_port) {
+		release_resource(chip->res_game_port);
+		kfree_nocheck(chip->res_game_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es1938_dev_free(snd_device_t *device)
+{
+	es1938_t *chip = snd_magic_cast(es1938_t, device->device_data, return -ENXIO);
+	return snd_es1938_free(chip);
+}
+
+static int __init snd_es1938_create(snd_card_t * card,
+				    struct pci_dev * pci,
+				    unsigned long dma1size,
+				    unsigned long dma2size,
+				    es1938_t ** rchip)
+{
+	es1938_t *chip;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_es1938_dev_free,
+	};
+
+	*rchip = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+        /* check, if we can restrict PCI DMA transfers to 24 bits */
+        if (!pci_dma_supported(pci, 0x00ffffff)) {
+                snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+                return -ENXIO;
+        }
+	pci_set_dma_mask(pci, 0x00ffffff);
+
+	chip = snd_magic_kcalloc(es1938_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->mixer_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->dma1size = dma1size;
+	chip->dma2size = dma2size;
+	chip->io_port = pci_resource_start(pci, 0);
+	if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 8 - 1);
+		return -EBUSY;
+	}
+	chip->sb_port = pci_resource_start(pci, 1);
+	if ((chip->res_sb_port = request_region(chip->sb_port, 0x10, "ESS Solo-1 SB")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab SB region 0x%lx-0x%lx\n", chip->sb_port, chip->sb_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	chip->vc_port = pci_resource_start(pci, 2);
+	if ((chip->res_vc_port = request_region(chip->vc_port, 0x10, "ESS Solo-1 VC (DMA)")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab VC (DMA) region 0x%lx-0x%lx\n", chip->vc_port, chip->vc_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	chip->mpu_port = pci_resource_start(pci, 3);
+	if ((chip->res_mpu_port = request_region(chip->mpu_port, 4, "ESS Solo-1 MIDI")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab MIDI region 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 4 - 1);
+		return -EBUSY;
+	}
+	chip->game_port = pci_resource_start(pci, 4);
+	if ((chip->res_game_port = request_region(chip->game_port, 4, "ESS Solo-1 GAME")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab GAME region 0x%lx-0x%lx\n", chip->game_port, chip->game_port + 4 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+#ifdef ES1938_DDEBUG
+	snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
+		   chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
+#endif
+	/* reset chip */
+	snd_es1938_reset(chip);
+
+	/* configure native mode */
+
+	/* enable bus master */
+	pci_set_master(pci);
+
+	/* disable legacy audio */
+	pci_write_config_word(pci, SL_PCI_LEGACYCONTROL, 0x805f);
+
+	/* set DDMA base */
+	chip->ddma_port = chip->vc_port + 0x00;		/* fix from Thomas Sailer */
+	pci_write_config_word(pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1);
+
+	/* set DMA/IRQ policy */
+	pci_write_config_dword(pci, SL_PCI_CONFIG, 0);
+
+	/* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/
+	outb(0xf0, SLIO_REG(chip, IRQCONTROL));
+
+	/* reset DMA */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+
+	/* enable bus mastering */
+	pci_set_master(pci);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es1938_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+/* --------------------------------------------------------------------
+ * Interrupt handler
+ * -------------------------------------------------------------------- */
+static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es1938_t *chip = snd_magic_cast(es1938_t, dev_id, return);
+	unsigned char status, audiostatus;
+
+	status = inb(SLIO_REG(chip, IRQCONTROL));
+#if 0
+	printk("Es1938debug - interrupt status: =0x%x\n", status);
+#endif
+	
+	/* AUDIO 1 */
+	if (status & 0x10) {
+#if 0
+                printk("Es1938debug - AUDIO channel 1 interrupt\n");
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT)));
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR)));
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS)));
+#endif
+		/* clear irq */
+		audiostatus = inb(SLSB_REG(chip, STATUS));
+		if (chip->active & ADC1)
+			snd_pcm_period_elapsed(chip->capture_substream);
+		else if (chip->active & DAC1)
+			snd_pcm_period_elapsed(chip->playback2_substream);
+	}
+	
+	/* AUDIO 2 */
+	if (status & 0x20) {
+#if 0
+                printk("Es1938debug - AUDIO channel 2 interrupt\n");
+		printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
+		printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR)));
+
+#endif
+		/* clear irq */
+		snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0);
+		if (chip->active & DAC2)
+			snd_pcm_period_elapsed(chip->playback1_substream);
+	}
+
+	/* Hardware volume */
+	if (status & 0x40) {
+		int split = snd_es1938_mixer_read(chip, 0x64) & 0x80;
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+		if (!split) {
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+		}
+		/* ack interrupt */
+		snd_es1938_mixer_write(chip, 0x66, 0x00);
+	}
+
+	/* MPU401 */
+	if (status & 0x80) {
+		/* ack */
+		snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0);
+		printk("midi interrupt..\n");
+		if (chip->rmidi)
+			snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+	}
+}
+
+#define ES1938_DMA_SIZE 64
+
+static int __init snd_es1938_mixer(snd_pcm_t *pcm)
+{
+	snd_card_t *card;
+	es1938_t *chip;
+	int err, idx;
+
+	snd_assert(pcm != NULL && pcm->card != NULL, return -EINVAL);
+
+	card = pcm->card;
+	chip = snd_pcm_chip(pcm);
+
+	strcpy(card->mixername, pcm->name);
+
+	for (idx = 0; idx < sizeof(snd_es1938_controls) / 
+		     sizeof(snd_es1938_controls[0]); idx++) {
+		snd_kcontrol_t *kctl;
+		kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
+		switch (idx) {
+			case 0:
+				chip->master_volume = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 1:
+				chip->master_switch = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 2:
+				chip->hw_volume = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 3:
+				chip->hw_switch = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			}
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+	return 0;
+}
+       
+
+static int __devinit snd_es1938_probe(struct pci_dev *pci,
+				      const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	es1938_t *chip;
+	snd_pcm_t *pcm;
+	opl3_t *opl3;
+	int idx, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	for (idx = 0; idx < 5; idx++) {
+		if (pci_resource_start(pci, idx) == 0 ||
+		    !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+		    	snd_card_free(card);
+		    	return -ENODEV;
+		}
+	}
+	if ((err = snd_es1938_create(card, pci,
+				     64 * 1024,
+				     64 * 1024,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1938_new_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1938_mixer(pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_opl3_create(card,
+			    SLSB_REG(chip, FMLOWADDR),
+			    SLSB_REG(chip, FMHIGHADDR),
+			    OPL3_HW_OPL3, 1, &opl3) < 0) {
+		printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
+			   SLSB_REG(chip, FMLOWADDR));
+	} else {
+	        if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+	                snd_card_free(card);
+	                return err;
+		}
+	        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+	                snd_card_free(card);
+	                return err;
+		}
+	}
+	if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
+		printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+	}
+#ifndef LINUX_2_2
+	chip->gameport.io = chip->game_port;
+	gameport_register_port(&chip->gameport);
+#endif
+
+	strcpy(card->driver, "ES1938");
+	strcpy(card->shortname, "ESS ES1938 (Solo-1)");
+	sprintf(card->longname, "%s rev %i, irq %i",
+		card->shortname,
+		chip->revision,
+		chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_es1938_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "ESS ES1938 (Solo-1)",
+	id_table: snd_es1938_ids,
+	probe: snd_es1938_probe,
+	remove: __devexit_p(snd_es1938_remove),
+};
+
+static int __init alsa_card_es1938_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "ESS Solo-1 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_es1938_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1938_init)
+module_exit(alsa_card_es1938_exit)
+
+#ifndef MODULE
+
+/* format is: snd-es1938=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_es1938_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-es1938=", alsa_card_es1938_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/es1968.c linux-2.4.19-pre5-mjc/sound/pci/es1968.c
--- linux/sound/pci/es1968.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/es1968.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2840 @@
+/*
+ *  Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99)
+ *  Copyright (c) by Matze Braun <MatzeBraun@gmx.de>.
+ *                   Takashi Iwai <tiwai@suse.de>
+ *                  
+ *  Most of the driver code comes from Zach Brown(zab@redhat.com)
+ *	Alan Cox OSS Driver
+ *  Rewritted from card-es1938.c source.
+ *
+ *  TODO:
+ *   Perhaps Synth
+ *
+ *   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
+ *
+ *
+ *  Notes from Zach Brown about the driver code
+ *
+ *  Hardware Description
+ *
+ *	A working Maestro setup contains the Maestro chip wired to a 
+ *	codec or 2.  In the Maestro we have the APUs, the ASSP, and the
+ *	Wavecache.  The APUs can be though of as virtual audio routing
+ *	channels.  They can take data from a number of sources and perform
+ *	basic encodings of the data.  The wavecache is a storehouse for
+ *	PCM data.  Typically it deals with PCI and interracts with the
+ *	APUs.  The ASSP is a wacky DSP like device that ESS is loth
+ *	to release docs on.  Thankfully it isn't required on the Maestro
+ *	until you start doing insane things like FM emulation and surround
+ *	encoding.  The codecs are almost always AC-97 compliant codecs, 
+ *	but it appears that early Maestros may have had PT101 (an ESS
+ *	part?) wired to them.  The only real difference in the Maestro
+ *	families is external goop like docking capability, memory for
+ *	the ASSP, and initialization differences.
+ *
+ *  Driver Operation
+ *
+ *	We only drive the APU/Wavecache as typical DACs and drive the
+ *	mixers in the codecs.  There are 64 APUs.  We assign 6 to each
+ *	/dev/dsp? device.  2 channels for output, and 4 channels for
+ *	input.
+ *
+ *	Each APU can do a number of things, but we only really use
+ *	3 basic functions.  For playback we use them to convert PCM
+ *	data fetched over PCI by the wavecahche into analog data that
+ *	is handed to the codec.  One APU for mono, and a pair for stereo.
+ *	When in stereo, the combination of smarts in the APU and Wavecache
+ *	decide which wavecache gets the left or right channel.
+ *
+ *	For record we still use the old overly mono system.  For each in
+ *	coming channel the data comes in from the codec, through a 'input'
+ *	APU, through another rate converter APU, and then into memory via
+ *	the wavecache and PCI.  If its stereo, we mash it back into LRLR in
+ *	software.  The pass between the 2 APUs is supposedly what requires us
+ *	to have a 512 byte buffer sitting around in wavecache/memory.
+ *
+ *	The wavecache makes our life even more fun.  First off, it can
+ *	only address the first 28 bits of PCI address space, making it
+ *	useless on quite a few architectures.  Secondly, its insane.
+ *	It claims to fetch from 4 regions of PCI space, each 4 meg in length.
+ *	But that doesn't really work.  You can only use 1 region.  So all our
+ *	allocations have to be in 4meg of each other.  Booo.  Hiss.
+ *	So we have a module parameter, dsps_order, that is the order of
+ *	the number of dsps to provide.  All their buffer space is allocated
+ *	on open time.  The sonicvibes OSS routines we inherited really want
+ *	power of 2 buffers, so we have all those next to each other, then
+ *	512 byte regions for the recording wavecaches.  This ends up
+ *	wasting quite a bit of memory.  The only fixes I can see would be 
+ *	getting a kernel allocator that could work in zones, or figuring out
+ *	just how to coerce the WP into doing what we want.
+ *
+ *	The indirection of the various registers means we have to spinlock
+ *	nearly all register accesses.  We have the main register indirection
+ *	like the wave cache, maestro registers, etc.  Then we have beasts
+ *	like the APU interface that is indirect registers gotten at through
+ *	the main maestro indirection.  Ouch.  We spinlock around the actual
+ *	ports on a per card basis.  This means spinlock activity at each IO
+ *	operation, but the only IO operation clusters are in non critical 
+ *	paths and it makes the code far easier to follow.  Interrupts are
+ *	blocked while holding the locks because the int handler has to
+ *	get at some of them :(.  The mixer interface doesn't, however.
+ *	We also have an OSS state lock that is thrown around in a few
+ *	places.
+ */
+
+#define __SND_OSS_COMPAT__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/ac97_codec.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t es1968_t
+
+#define CARD_NAME "ESS Maestro1/2"
+#define DRIVER_NAME "ES1968"
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ESS Maestro");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+MODULE_DEVICES("{{ESS,Maestro 2e},"
+		"{ESS,Maestro 2},"
+		"{ESS,Maestro 1},"
+		"{TerraTec,DMX}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 1-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 };
+static int snd_pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 };
+static int snd_pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 };
+static int snd_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_total_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_total_bufsize, "Total buffer size in kB.");
+MODULE_PARM_SYNTAX(snd_total_bufsize, SNDRV_ENABLED ",allows:{{1,4096}},skill:advanced");
+MODULE_PARM(snd_pcm_substreams_p, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_pcm_substreams_p, SNDRV_ENABLED ",allows:{{1,8}}");
+MODULE_PARM(snd_pcm_substreams_c, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_pcm_substreams_c, SNDRV_ENABLED ",allows:{{0,8}}");
+MODULE_PARM(snd_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_clock, "Clock on " CARD_NAME " soundcard.  (0 = auto-detect)");
+MODULE_PARM_SYNTAX(snd_clock, SNDRV_ENABLED);
+
+
+/* PCI Dev ID's */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS	0x125D
+#endif
+
+#define PCI_VENDOR_ID_ESS_OLD	0x1285	/* Platform Tech, the people the ESS
+					   was bought form */
+
+#ifndef PCI_DEVICE_ID_ESS_M2E
+#define PCI_DEVICE_ID_ESS_M2E	0x1978
+#endif
+#ifndef PCI_DEVICE_ID_ESS_M2
+#define PCI_DEVICE_ID_ESS_M2	0x1968
+#endif
+#ifndef PCI_DEVICE_ID_ESS_M1
+#define PCI_DEVICE_ID_ESS_M1	0x0100
+#endif
+
+#define NR_APUS			64
+#define NR_APU_REGS		16
+
+/* NEC Versas ? */
+#define NEC_VERSA_SUBID1	0x80581033
+#define NEC_VERSA_SUBID2	0x803c1033
+
+/* Mode Flags */
+#define ESS_FMT_STEREO     	0x01
+#define ESS_FMT_16BIT      	0x02
+
+#define DAC_RUNNING		1
+#define ADC_RUNNING		2
+
+/* Values for the ESM_LEGACY_AUDIO_CONTROL */
+
+#define ESS_ENABLE_AUDIO	0x8000
+#define ESS_ENABLE_SERIAL_IRQ	0x4000
+#define IO_ADRESS_ALIAS		0x0020
+#define MPU401_IRQ_ENABLE	0x0010
+#define MPU401_IO_ENABLE	0x0008
+#define GAME_IO_ENABLE		0x0004
+#define FM_IO_ENABLE		0x0002
+#define SB_IO_ENABLE		0x0001
+
+/* Values for the ESM_CONFIG_A */
+
+#define PIC_SNOOP1		0x4000
+#define PIC_SNOOP2		0x2000
+#define SAFEGUARD		0x0800
+#define DMA_CLEAR		0x0700
+#define DMA_DDMA		0x0000
+#define DMA_TDMA		0x0100
+#define DMA_PCPCI		0x0200
+#define POST_WRITE		0x0080
+#define ISA_TIMING		0x0040
+#define SWAP_LR			0x0020
+#define SUBTR_DECODE		0x0002
+
+/* Values for the ESM_CONFIG_B */
+
+#define SPDIF_CONFB		0x0100
+#define HWV_CONFB		0x0080
+#define DEBOUNCE		0x0040
+#define GPIO_CONFB		0x0020
+#define CHI_CONFB		0x0010
+#define IDMA_CONFB		0x0008	/*undoc */
+#define MIDI_FIX		0x0004	/*undoc */
+#define IRQ_TO_ISA		0x0001	/*undoc */
+
+/* Values for Ring Bus Control B */
+#define	RINGB_2CODEC_ID_MASK	0x0003
+#define RINGB_DIS_VALIDATION	0x0008
+#define RINGB_EN_SPDIF		0x0010
+#define	RINGB_EN_2CODEC		0x0020
+#define RINGB_SING_BIT_DUAL	0x0040
+
+/* ****Port Adresses**** */
+
+/*   Write & Read */
+#define ESM_INDEX		0x02
+#define ESM_DATA		0x00
+
+/*   AC97 + RingBus */
+#define ESM_AC97_INDEX		0x30
+#define	ESM_AC97_DATA		0x32
+#define ESM_RING_BUS_DEST	0x34
+#define ESM_RING_BUS_CONTR_A	0x36
+#define ESM_RING_BUS_CONTR_B	0x38
+#define ESM_RING_BUS_SDO	0x3A
+
+/*   WaveCache*/
+#define WC_INDEX		0x10
+#define WC_DATA			0x12
+#define WC_CONTROL		0x14
+
+/*   ASSP*/
+#define ASSP_INDEX		0x80
+#define ASSP_MEMORY		0x82
+#define ASSP_DATA		0x84
+#define ASSP_CONTROL_A		0xA2
+#define ASSP_CONTROL_B		0xA4
+#define ASSP_CONTROL_C		0xA6
+#define ASSP_HOSTW_INDEX	0xA8
+#define ASSP_HOSTW_DATA		0xAA
+#define ASSP_HOSTW_IRQ		0xAC
+/* Midi */
+#define ESM_MPU401_PORT		0x98
+/* Others */
+#define ESM_PORT_HOST_IRQ	0x18
+
+#define IDR0_DATA_PORT		0x00
+#define IDR1_CRAM_POINTER	0x01
+#define IDR2_CRAM_DATA		0x02
+#define IDR3_WAVE_DATA		0x03
+#define IDR4_WAVE_PTR_LOW	0x04
+#define IDR5_WAVE_PTR_HI	0x05
+#define IDR6_TIMER_CTRL		0x06
+#define IDR7_WAVE_ROMRAM	0x07
+
+#define WRITEABLE_MAP		0xEFFFFF
+#define READABLE_MAP		0x64003F
+
+/* PCI Register */
+
+#define ESM_LEGACY_AUDIO_CONTROL 0x40
+#define ESM_ACPI_COMMAND	0x54
+#define ESM_CONFIG_A		0x50
+#define ESM_CONFIG_B		0x52
+#define ESM_DDMA		0x60
+
+/* Bob Bits */
+#define ESM_BOB_ENABLE		0x0001
+#define ESM_BOB_START		0x0001
+
+/* Host IRQ Control Bits */
+#define ESM_RESET_MAESTRO	0x8000
+#define ESM_RESET_DIRECTSOUND   0x4000
+#define ESM_HIRQ_ClkRun		0x0100
+#define ESM_HIRQ_HW_VOLUME	0x0040
+#define ESM_HIRQ_HARPO		0x0030	/* What's that? */
+#define ESM_HIRQ_ASSP		0x0010
+#define	ESM_HIRQ_DSIE		0x0004
+#define ESM_HIRQ_MPU401		0x0002
+#define ESM_HIRQ_SB		0x0001
+
+/* Host IRQ Status Bits */
+#define ESM_MPU401_IRQ		0x02
+#define ESM_SB_IRQ		0x01
+#define ESM_SOUND_IRQ		0x04
+#define	ESM_ASSP_IRQ		0x10
+#define ESM_HWVOL_IRQ		0x40
+
+#define ESS_SYSCLK		50000000
+#define ESM_BOB_FREQ 		200
+#define ESM_BOB_FREQ_MAX	800
+
+#define ESM_FREQ_ESM1  		(49152000L / 1024L)	/* default rate 48000 */
+#define ESM_FREQ_ESM2  		(50000000L / 1024L)
+
+/* APU Modes: reg 0x00, bit 4-7 */
+#define ESM_APU_MODE_SHIFT	4
+#define ESM_APU_MODE_MASK	(0xf << 4)
+#define	ESM_APU_OFF		0x00
+#define	ESM_APU_16BITLINEAR	0x01	/* 16-Bit Linear Sample Player */
+#define	ESM_APU_16BITSTEREO	0x02	/* 16-Bit Stereo Sample Player */
+#define	ESM_APU_8BITLINEAR	0x03	/* 8-Bit Linear Sample Player */
+#define	ESM_APU_8BITSTEREO	0x04	/* 8-Bit Stereo Sample Player */
+#define	ESM_APU_8BITDIFF	0x05	/* 8-Bit Differential Sample Playrer */
+#define	ESM_APU_DIGITALDELAY	0x06	/* Digital Delay Line */
+#define	ESM_APU_DUALTAP		0x07	/* Dual Tap Reader */
+#define	ESM_APU_CORRELATOR	0x08	/* Correlator */
+#define	ESM_APU_INPUTMIXER	0x09	/* Input Mixer */
+#define	ESM_APU_WAVETABLE	0x0A	/* Wave Table Mode */
+#define	ESM_APU_SRCONVERTOR	0x0B	/* Sample Rate Convertor */
+#define	ESM_APU_16BITPINGPONG	0x0C	/* 16-Bit Ping-Pong Sample Player */
+#define	ESM_APU_RESERVED1	0x0D	/* Reserved 1 */
+#define	ESM_APU_RESERVED2	0x0E	/* Reserved 2 */
+#define	ESM_APU_RESERVED3	0x0F	/* Reserved 3 */
+
+/* reg 0x00 */
+#define ESM_APU_FILTER_Q_SHIFT		0
+#define ESM_APU_FILTER_Q_MASK		(3 << 0)
+/* APU Filtey Q Control */
+#define ESM_APU_FILTER_LESSQ	0x00
+#define ESM_APU_FILTER_MOREQ	0x03
+
+#define ESM_APU_FILTER_TYPE_SHIFT	2
+#define ESM_APU_FILTER_TYPE_MASK	(3 << 2)
+#define ESM_APU_ENV_TYPE_SHIFT		8
+#define ESM_APU_ENV_TYPE_MASK		(3 << 8)
+#define ESM_APU_ENV_STATE_SHIFT		10
+#define ESM_APU_ENV_STATE_MASK		(3 << 10)
+#define ESM_APU_END_CURVE		(1 << 12)
+#define ESM_APU_INT_ON_LOOP		(1 << 13)
+#define ESM_APU_DMA_ENABLE		(1 << 14)
+
+/* reg 0x02 */
+#define ESM_APU_SUBMIX_GROUP_SHIRT	0
+#define ESM_APU_SUBMIX_GROUP_MASK	(7 << 0)
+#define ESM_APU_SUBMIX_MODE		(1 << 3)
+#define ESM_APU_6dB			(1 << 4)
+#define ESM_APU_DUAL_EFFECT		(1 << 5)
+#define ESM_APU_EFFECT_CHANNELS_SHIFT	6
+#define ESM_APU_EFFECT_CHANNELS_MASK	(3 << 6)
+
+/* reg 0x03 */
+#define ESM_APU_STEP_SIZE_MASK		0x0fff
+
+/* reg 0x04 */
+#define ESM_APU_PHASE_SHIFT		0
+#define ESM_APU_PHASE_MASK		(0xff << 0)
+#define ESM_APU_WAVE64K_PAGE_SHIFT	8	/* most 8bit of wave start offset */
+#define ESM_APU_WAVE64K_PAGE_MASK	(0xff << 8)
+
+/* reg 0x05 - wave start offset */
+/* reg 0x06 - wave end offset */
+/* reg 0x07 - wave loop length */
+
+/* reg 0x08 */
+#define ESM_APU_EFFECT_GAIN_SHIFT	0
+#define ESM_APU_EFFECT_GAIN_MASK	(0xff << 0)
+#define ESM_APU_TREMOLO_DEPTH_SHIFT	8
+#define ESM_APU_TREMOLO_DEPTH_MASK	(0xf << 8)
+#define ESM_APU_TREMOLO_RATE_SHIFT	12
+#define ESM_APU_TREMOLO_RATE_MASK	(0xf << 12)
+
+/* reg 0x09 */
+/* bit 0-7 amplitude dest? */
+#define ESM_APU_AMPLITUDE_NOW_SHIFT	8
+#define ESM_APU_AMPLITUDE_NOW_MASK	(0xff << 8)
+
+/* reg 0x0a */
+#define ESM_APU_POLAR_PAN_SHIFT		0
+#define ESM_APU_POLAR_PAN_MASK		(0x3f << 0)
+/* Polar Pan Control */
+#define	ESM_APU_PAN_CENTER_CIRCLE		0x00
+#define	ESM_APU_PAN_MIDDLE_RADIUS		0x01
+#define	ESM_APU_PAN_OUTSIDE_RADIUS		0x02
+
+#define ESM_APU_FILTER_TUNING_SHIFT	8
+#define ESM_APU_FILTER_TUNING_MASK	(0xff << 8)
+
+/* reg 0x0b */
+#define ESM_APU_DATA_SRC_A_SHIFT	0
+#define ESM_APU_DATA_SRC_A_MASK		(0x7f << 0)
+#define ESM_APU_INV_POL_A		(1 << 7)
+#define ESM_APU_DATA_SRC_B_SHIFT	8
+#define ESM_APU_DATA_SRC_B_MASK		(0x7f << 8)
+#define ESM_APU_INV_POL_B		(1 << 15)
+
+#define ESM_APU_VIBRATO_RATE_SHIFT	0
+#define ESM_APU_VIBRATO_RATE_MASK	(0xf << 0)
+#define ESM_APU_VIBRATO_DEPTH_SHIFT	4
+#define ESM_APU_VIBRATO_DEPTH_MASK	(0xf << 4)
+#define ESM_APU_VIBRATO_PHASE_SHIFT	8
+#define ESM_APU_VIBRATO_PHASE_MASK	(0xff << 8)
+
+/* reg 0x0c */
+#define ESM_APU_RADIUS_SELECT		(1 << 6)
+
+/* APU Filter Control */
+#define	ESM_APU_FILTER_2POLE_LOPASS	0x00
+#define	ESM_APU_FILTER_2POLE_BANDPASS	0x01
+#define	ESM_APU_FILTER_2POLE_HIPASS	0x02
+#define	ESM_APU_FILTER_1POLE_LOPASS	0x03
+#define	ESM_APU_FILTER_1POLE_HIPASS	0x04
+#define	ESM_APU_FILTER_OFF		0x05
+
+/* APU ATFP Type */
+#define	ESM_APU_ATFP_AMPLITUDE			0x00
+#define	ESM_APU_ATFP_TREMELO			0x01
+#define	ESM_APU_ATFP_FILTER			0x02
+#define	ESM_APU_ATFP_PAN			0x03
+
+/* APU ATFP Flags */
+#define	ESM_APU_ATFP_FLG_OFF			0x00
+#define	ESM_APU_ATFP_FLG_WAIT			0x01
+#define	ESM_APU_ATFP_FLG_DONE			0x02
+#define	ESM_APU_ATFP_FLG_INPROCESS		0x03
+
+
+/* capture mixing buffer size */
+#define ESM_MIXBUF_SIZE		512
+
+#define ESM_MODE_PLAY		0
+#define ESM_MODE_CAPTURE	1
+
+/* acpi states */
+enum {
+	ACPI_D0=0,
+	ACPI_D1,
+	ACPI_D2,
+	ACPI_D3
+};
+
+/* bits in the acpi masks */
+#define ACPI_12MHZ	( 1 << 15)
+#define ACPI_24MHZ	( 1 << 14)
+#define ACPI_978	( 1 << 13)
+#define ACPI_SPDIF	( 1 << 12)
+#define ACPI_GLUE	( 1 << 11)
+#define ACPI__10	( 1 << 10) /* reserved */
+#define ACPI_PCIINT	( 1 << 9)
+#define ACPI_HV		( 1 << 8) /* hardware volume */
+#define ACPI_GPIO	( 1 << 7)
+#define ACPI_ASSP	( 1 << 6)
+#define ACPI_SB		( 1 << 5) /* sb emul */
+#define ACPI_FM		( 1 << 4) /* fm emul */
+#define ACPI_RB		( 1 << 3) /* ringbus / aclink */
+#define ACPI_MIDI	( 1 << 2) 
+#define ACPI_GP		( 1 << 1) /* game port */
+#define ACPI_WP		( 1 << 0) /* wave processor */
+
+#define ACPI_ALL	(0xffff)
+#define ACPI_SLEEP	(~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \
+			ACPI_MIDI|ACPI_GP|ACPI_WP))
+#define ACPI_NONE	(ACPI__10)
+
+/* these masks indicate which units we care about at
+	which states */
+static u16 acpi_state_mask[] = {
+	[ACPI_D0] = ACPI_ALL,
+	[ACPI_D1] = ACPI_SLEEP,
+	[ACPI_D2] = ACPI_SLEEP,
+	[ACPI_D3] = ACPI_NONE
+};
+
+
+typedef struct snd_es1968 es1968_t;
+typedef struct snd_esschan esschan_t;
+typedef struct snd_esm_memory esm_memory_t;
+
+/* APU use in the driver */
+enum snd_enum_apu_type {
+	ESM_APU_PCM_PLAY,
+	ESM_APU_PCM_CAPTURE,
+	ESM_APU_PCM_RATECONV,
+	ESM_APU_FREE
+};
+
+/* DMA Hack! */
+struct snd_esm_memory {
+	char *buf;
+	unsigned long addr;
+	int size;
+	int empty;	/* status */
+	struct list_head list;
+};
+
+/* Playback Channel */
+struct snd_esschan {
+	int running;
+
+	u8 apu[4];
+	u8 apu_mode[4];
+
+	/* playback/capture pcm buffer */
+	esm_memory_t *memory;
+	/* capture mixer buffer */
+	esm_memory_t *mixbuf;
+
+	unsigned int hwptr;	/* current hw pointer in bytes */
+	unsigned int count;	/* sample counter in bytes */
+	unsigned int dma_size;	/* total buffer size in bytes */
+	unsigned int frag_size;	/* period size in bytes */
+	unsigned int wav_shift;
+	u16 base[4];		/* offset for ptr */
+
+	/* stereo/16bit flag */
+	unsigned char fmt;
+	int mode;	/* playback / capture */
+
+	int bob_freq;	/* required timer frequency */
+
+	snd_pcm_substream_t *substream;
+
+	/* linked list */
+	struct list_head list;
+
+#ifdef CONFIG_PM
+	u16 wc_map[4];
+#endif
+};
+
+struct snd_es1968 {
+	/* Module Config */
+	int total_bufsize;			/* in bytes */
+
+	int playback_streams, capture_streams;
+
+	unsigned int clock;		/* clock */
+
+	/* buffer */
+	void *dma_buf;
+	dma_addr_t dma_buf_addr;
+	unsigned long dma_buf_size;
+
+	/* Resources... */
+	int irq;
+	unsigned long io_port;
+	struct resource *res_io_port;
+	int type;
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+
+	/* DMA memory block */
+	struct list_head buf_list;
+
+	/* ALSA Stuff */
+	ac97_t *ac97;
+	snd_kcontrol_t *master_switch; /* for h/w volume control */
+	snd_kcontrol_t *master_volume;
+
+	snd_rawmidi_t *rmidi;
+
+	spinlock_t reg_lock;
+	struct tasklet_struct hwvol_tq;
+
+	/* Maestro Stuff */
+	u16 maestro_map[32];
+	atomic_t bobclient;	/* active timer instancs */
+	int bob_freq;		/* timer frequency */
+	spinlock_t bob_lock;
+	struct semaphore memory_mutex;	/* memory lock */
+
+	/* APU states */
+	unsigned char apu[NR_APUS];
+
+	/* active substreams */
+	struct list_head substream_list;
+	spinlock_t substream_lock;
+
+#ifdef CONFIG_PM
+	u16 apu_map[NR_APUS][NR_APU_REGS];
+#endif
+};
+
+static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+#define CARD_TYPE_ESS_ESOLDM1		0x12850100
+#define CARD_TYPE_ESS_ES1968		0x125d1968
+#define CARD_TYPE_ESS_ES1978		0x125d1978
+
+static struct pci_device_id snd_es1968_ids[] __devinitdata = {
+	/* Maestro 1 */
+        { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },
+	/* Maestro 2 */
+	{ 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },
+	/* Maestro 2E */
+        { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1968_ids);
+
+/* *********************
+   * Low Level Funcs!  *
+   *********************/
+
+/* no spinlock */
+static void __maestro_write(es1968_t *chip, u16 reg, u16 data)
+{
+	outw(reg, chip->io_port + ESM_INDEX);
+	outw(data, chip->io_port + ESM_DATA);
+	chip->maestro_map[reg] = data;
+}
+
+inline static void maestro_write(es1968_t *chip, u16 reg, u16 data)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	__maestro_write(chip, reg, data);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/* no spinlock */
+static u16 __maestro_read(es1968_t *chip, u16 reg)
+{
+	if (READABLE_MAP & (1 << reg)) {
+		outw(reg, chip->io_port + ESM_INDEX);
+		chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA);
+	}
+	return chip->maestro_map[reg];
+}
+
+inline static u16 maestro_read(es1968_t *chip, u16 reg)
+{
+	unsigned long flags;
+	u16 result;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	result = __maestro_read(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return result;
+}
+
+/* Wait for the codec bus to be free */
+static int snd_es1968_ac97_wait(es1968_t *chip)
+{
+	int timeout = 100000;
+
+	while (timeout-- > 0) {
+		if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1))
+			return 0;
+	}
+	snd_printd("es1968: ac97 timeout\n");
+	return 1; /* timeout */
+}
+
+static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	snd_es1968_ac97_wait(chip);
+
+	/* Write the bus */
+	outw(val, chip->io_port + ESM_AC97_DATA);
+	mdelay(1);
+	outb(reg, chip->io_port + ESM_AC97_INDEX);
+	mdelay(1);
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	u16 data = 0;
+	es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return 0);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	snd_es1968_ac97_wait(chip);
+
+	outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
+	mdelay(1);
+
+	if (! snd_es1968_ac97_wait(chip)) {
+		data = inw(chip->io_port + ESM_AC97_DATA);
+		mdelay(1);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return data;
+}
+
+/* no spinlock */
+static void apu_index_set(es1968_t *chip, u16 index)
+{
+	int i;
+	__maestro_write(chip, IDR1_CRAM_POINTER, index);
+	for (i = 0; i < 1000; i++)
+		if (__maestro_read(chip, IDR1_CRAM_POINTER) == index)
+			return;
+	snd_printd("es1968: APU register select failed. (Timeout)\n");
+}
+
+/* no spinlock */
+static void apu_data_set(es1968_t *chip, u16 data)
+{
+	int i;
+	for (i = 0; i < 1000; i++) {
+		if (__maestro_read(chip, IDR0_DATA_PORT) == data)
+			return;
+		__maestro_write(chip, IDR0_DATA_PORT, data);
+	}
+	snd_printd("es1968: APU register set probably failed (Timeout)!\n");
+}
+
+/* no spinlock */
+static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data)
+{
+	snd_assert(channel < NR_APUS, return);
+#ifdef CONFIG_PM
+	chip->apu_map[channel][reg] = data;
+#endif
+	reg |= (channel << 4);
+	apu_index_set(chip, reg);
+	apu_data_set(chip, data);
+}
+
+inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	__apu_set_register(chip, channel, reg, data);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg)
+{
+	snd_assert(channel < NR_APUS, return 0);
+	reg |= (channel << 4);
+	apu_index_set(chip, reg);
+	return __maestro_read(chip, IDR0_DATA_PORT);
+}
+
+inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg)
+{
+	unsigned long flags;
+	u16 v;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	v = __apu_get_register(chip, channel, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return v;
+}
+
+#if 0 /* ASSP is not supported */
+
+static void assp_set_register(es1968_t *chip, u32 reg, u32 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outl(reg, chip->io_port + ASSP_INDEX);
+	outl(value, chip->io_port + ASSP_DATA);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u32 assp_get_register(es1968_t *chip, u32 reg)
+{
+	unsigned long flags;
+	u32 value;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outl(reg, chip->io_port + ASSP_INDEX);
+	value = inl(chip->io_port + ASSP_DATA);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return value;
+}
+
+#endif
+
+static void wave_set_register(es1968_t *chip, u16 reg, u16 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outw(reg, chip->io_port + WC_INDEX);
+	outw(value, chip->io_port + WC_DATA);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u16 wave_get_register(es1968_t *chip, u16 reg)
+{
+	unsigned long flags;
+	u16 value;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outw(reg, chip->io_port + WC_INDEX);
+	value = inw(chip->io_port + WC_DATA);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return value;
+}
+
+/* *******************
+   * Bob the Timer!  *
+   *******************/
+
+static void snd_es1968_bob_stop(es1968_t *chip)
+{
+	u16 reg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	reg = __maestro_read(chip, 0x11);
+	reg &= ~ESM_BOB_ENABLE;
+	__maestro_write(chip, 0x11, reg);
+	reg = __maestro_read(chip, 0x17);
+	reg &= ~ESM_BOB_START;
+	__maestro_write(chip, 0x17, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_es1968_bob_start(es1968_t *chip)
+{
+	int prescale;
+	int divide;
+	unsigned long flags;
+
+	/* compute ideal interrupt frequency for buffer size & play rate */
+	/* first, find best prescaler value to match freq */
+	for (prescale = 5; prescale < 12; prescale++)
+		if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9)))
+			break;
+
+	/* next, back off prescaler whilst getting divider into optimum range */
+	divide = 1;
+	while ((prescale > 5) && (divide < 32)) {
+		prescale--;
+		divide <<= 1;
+	}
+	divide >>= 1;
+
+	/* now fine-tune the divider for best match */
+	for (; divide < 31; divide++)
+		if (chip->bob_freq >
+		    ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break;
+
+	/* divide = 0 is illegal, but don't let prescale = 4! */
+	if (divide == 0) {
+		divide++;
+		if (prescale > 5)
+			prescale--;
+	} else if (divide > 1)
+		divide--;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	__maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide);	/* set reg */
+
+	/* Now set IDR 11/17 */
+	__maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1);
+	__maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_es1968_bob_inc(es1968_t *chip, int freq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->bob_lock, flags);
+	atomic_inc(&chip->bobclient);
+	if (atomic_read(&chip->bobclient) == 1) {
+		chip->bob_freq = freq;
+		snd_es1968_bob_start(chip);
+	} else if (chip->bob_freq < freq) {
+		snd_es1968_bob_stop(chip);
+		chip->bob_freq = freq;
+		snd_es1968_bob_start(chip);
+	}
+	spin_unlock_irqrestore(&chip->bob_lock, flags);
+}
+
+static void snd_es1968_bob_dec(es1968_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->bob_lock, flags);
+	atomic_dec(&chip->bobclient);
+	if (atomic_read(&chip->bobclient) <= 0)
+		snd_es1968_bob_stop(chip);
+	else if (chip->bob_freq > ESM_BOB_FREQ) {
+		/* check reduction of timer frequency */
+		struct list_head *p;
+		int max_freq = ESM_BOB_FREQ;
+		spin_lock(&chip->substream_lock);
+		list_for_each(p, &chip->substream_list) {
+			esschan_t *es = list_entry(p, esschan_t, list);
+			if (max_freq < es->bob_freq)
+				max_freq = es->bob_freq;
+		}
+		spin_unlock(&chip->substream_lock);
+		if (max_freq != chip->bob_freq) {
+			snd_es1968_bob_stop(chip);
+			chip->bob_freq = max_freq;
+			snd_es1968_bob_start(chip);
+		}
+	}
+	spin_unlock_irqrestore(&chip->bob_lock, flags);
+}
+
+static int
+snd_es1968_calc_bob_rate(es1968_t *chip, esschan_t *es,
+			 snd_pcm_runtime_t *runtime)
+{
+	/* we acquire 4 interrupts per period for precise control.. */
+	int freq = runtime->rate * 4;
+	if (es->fmt & ESS_FMT_STEREO)
+		freq <<= 1;
+	if (es->fmt & ESS_FMT_16BIT)
+		freq <<= 1;
+	freq /= es->frag_size;
+	if (freq < ESM_BOB_FREQ)
+		freq = ESM_BOB_FREQ;
+	else if (freq > ESM_BOB_FREQ_MAX)
+		freq = ESM_BOB_FREQ_MAX;
+	return freq;
+}
+
+
+/*************
+ *  PCM Part *
+ *************/
+
+static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq)
+{
+	u32 rate = (freq << 16) / chip->clock;
+#if 0 /* XXX: do we need this? */ 
+	if (rate > 0x10000)
+		rate = 0x10000;
+#endif
+	return rate;
+}
+
+/* get current pointer */
+inline static unsigned int
+snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es)
+{
+	unsigned int offset;
+
+	offset = apu_get_register(chip, es->apu[0], 5);
+
+	offset -= es->base[0];
+
+	return (offset & 0xFFFE);	/* hardware is in words */
+}
+
+static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq)
+{
+	apu_set_register(chip, apu, 2,
+			   (apu_get_register(chip, apu, 2) & 0x00FF) |
+			   ((freq & 0xff) << 8) | 0x10);
+	apu_set_register(chip, apu, 3, freq >> 8);
+}
+
+/* spin lock held */
+inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode)
+{
+	/* dma on, no envelopes, filter to all 1s) */
+	__apu_set_register(esm, apu, 0, 0x400f | mode);
+}
+
+static void snd_es1968_pcm_start(es1968_t *chip, esschan_t *es)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (es->running) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	__apu_set_register(chip, es->apu[0], 5, es->base[0]);
+	snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]);
+	if (es->mode == ESM_MODE_CAPTURE) {
+		__apu_set_register(chip, es->apu[2], 5, es->base[2]);
+		snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]);
+	}
+	if (es->fmt & ESS_FMT_STEREO) {
+		__apu_set_register(chip, es->apu[1], 5, es->base[1]);
+		snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]);
+		if (es->mode == ESM_MODE_CAPTURE) {
+			__apu_set_register(chip, es->apu[3], 5, es->base[3]);
+			snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]);
+		}
+	}
+	es->running = 1;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_es1968_pcm_stop(es1968_t *chip, esschan_t *es)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (! es->running) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	snd_es1968_trigger_apu(chip, es->apu[0], 0);
+	snd_es1968_trigger_apu(chip, es->apu[1], 0);
+	if (es->mode == ESM_MODE_CAPTURE) {
+		snd_es1968_trigger_apu(chip, es->apu[2], 0);
+		snd_es1968_trigger_apu(chip, es->apu[3], 0);
+	}
+	es->running = 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/* set the wavecache control reg */
+static void snd_es1968_program_wavecache(es1968_t *chip, esschan_t *es,
+					 int channel, u32 addr, int capture)
+{
+	u32 tmpval = (addr - 0x10) & 0xFFF8;
+
+	if (! capture) {
+		if (!(es->fmt & ESS_FMT_16BIT))
+			tmpval |= 4;	/* 8bit */
+		if (es->fmt & ESS_FMT_STEREO)
+			tmpval |= 2;	/* stereo */
+	}
+
+	/* set the wavecache control reg */
+	wave_set_register(chip, es->apu[channel] << 3, tmpval);
+
+#ifdef CONFIG_PM
+	es->wc_map[channel] = tmpval;
+#endif
+}
+
+
+static void snd_es1968_playback_setup(es1968_t *chip, esschan_t *es,
+				      snd_pcm_runtime_t *runtime)
+{
+	u32 pa;
+	int high_apu = 0;
+	int channel, apu;
+	int i, size;
+	unsigned long flags;
+	u32 freq;
+
+	size = es->dma_size >> es->wav_shift;
+
+	if (es->fmt & ESS_FMT_STEREO)
+		high_apu++;
+
+	for (channel = 0; channel <= high_apu; channel++) {
+		apu = es->apu[channel];
+
+		snd_es1968_program_wavecache(chip, es, channel, es->memory->addr, 0);
+
+		/* Offset to PCMBAR */
+		pa = es->memory->addr;
+		pa -= chip->dma_buf_addr;
+		pa >>= 1;	/* words */
+
+		pa |= 0x00400000;	/* System RAM (Bit 22) */
+
+		if (es->fmt & ESS_FMT_STEREO) {
+			/* Enable stereo */
+			if (channel)
+				pa |= 0x00800000;	/* (Bit 23) */
+			if (es->fmt & ESS_FMT_16BIT)
+				pa >>= 1;
+		}
+
+		/* base offset of dma calcs when reading the pointer
+		   on this left one */
+		es->base[channel] = pa & 0xFFFF;
+
+		for (i = 0; i < 16; i++)
+			apu_set_register(chip, apu, i, 0x0000);
+
+		/* Load the buffer into the wave engine */
+		apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8);
+		apu_set_register(chip, apu, 5, pa & 0xFFFF);
+		apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF);
+		/* setting loop == sample len */
+		apu_set_register(chip, apu, 7, size);
+
+		/* clear effects/env.. */
+		apu_set_register(chip, apu, 8, 0x0000);
+		/* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */
+		apu_set_register(chip, apu, 9, 0xD000);
+
+		/* clear routing stuff */
+		apu_set_register(chip, apu, 11, 0x0000);
+		/* dma on, no envelopes, filter to all 1s) */
+		// apu_set_register(chip, apu, 0, 0x400F);
+
+		if (es->fmt & ESS_FMT_16BIT)
+			es->apu_mode[channel] = 0x10;	/* 16bit mono */
+		else
+			es->apu_mode[channel] = 0x30;	/* 8bit mono */
+
+		if (es->fmt & ESS_FMT_STEREO) {
+			/* set panning: left or right */
+			/* Check: different panning. On my Canyon 3D Chipset the
+			   Channels are swapped. I don't know, about the output
+			   to the SPDif Link. Perhaps you have to change this
+			   and not the APU Regs 4-5. */
+			apu_set_register(chip, apu, 10,
+					 0x8F00 | (channel ? 0 : 0x10));
+			es->apu_mode[channel] += 0x10;	/* stereo */
+		} else
+			apu_set_register(chip, apu, 10, 0x8F08);
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* clear WP interupts */
+	outw(1, chip->io_port + 0x04);
+	/* enable WP ints */
+	outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	freq = runtime->rate;
+	/* set frequency */
+	if (freq > 48000)
+		freq = 48000;
+	if (freq < 4000)
+		freq = 4000;
+
+	/* hmmm.. */
+	if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO))
+		freq >>= 1;
+
+	freq = snd_es1968_compute_rate(chip, freq);
+
+	/* Load the frequency, turn on 6dB */
+	snd_es1968_apu_set_freq(chip, es->apu[0], freq);
+	snd_es1968_apu_set_freq(chip, es->apu[1], freq);
+}
+
+static void snd_es1968_capture_setup(es1968_t *chip, esschan_t *es,
+				     snd_pcm_runtime_t *runtime)
+{
+	int apu_step = 2;
+	int channel, apu;
+	int i, size;
+	u32 freq;
+	unsigned long flags;
+
+	size = es->dma_size >> es->wav_shift;
+
+	/* we're given the full size of the buffer, but
+	   in stereo each channel will only use its half */
+	if (es->fmt & ESS_FMT_STEREO)
+		apu_step = 1;
+
+	/* APU assignments:
+	   0 = mono/left SRC
+	   1 = right SRC
+	   2 = mono/left Input Mixer
+	   3 = right Input Mixer */
+	for (channel = 0; channel < 4; channel += apu_step) {
+		int bsize, route;
+		u32 pa;
+
+		apu = es->apu[channel];
+
+		/* data seems to flow from the codec, through an apu into
+		   the 'mixbuf' bit of page, then through the SRC apu
+		   and out to the real 'buffer'.  ok.  sure.  */
+
+		if (channel & 2) {
+			/* ok, we're an input mixer going from adc
+			   through the mixbuf to the other apus */
+
+			if (!(channel & 0x01)) {
+				pa = es->mixbuf->addr;
+			} else {
+				pa = es->mixbuf->addr + ESM_MIXBUF_SIZE / 2;
+			}
+
+			/* we source from a 'magic' apu */
+			bsize = ESM_MIXBUF_SIZE / 4; /* half of this channels alloc, in words */
+			/* parallel in crap, see maestro reg 0xC [8-11] */
+			route = 0x14 + channel - 2;
+			es->apu_mode[channel] = 0x90;	/* Input Mixer */
+		} else {
+			/* we're a rate converter taking
+			   input from the input apus and outputing it to
+			   system memory */
+			if (!(channel & 0x01))
+				pa = es->memory->addr;
+			else
+				pa = es->memory->addr + size * 2; /* size is in word */
+
+			es->apu_mode[channel] = 0xB0;	/* Sample Rate Converter */
+
+			bsize = size;
+			/* get input from inputing apu */
+			route = es->apu[channel + 2];
+		}
+
+		/* set the wavecache control reg */
+		snd_es1968_program_wavecache(chip, es, channel, pa, 1);
+
+		/* Offset to PCMBAR */
+		pa -= chip->dma_buf_addr;
+		pa >>= 1;	/* words */
+
+		/* base offset of dma calcs when reading the pointer
+		   on this left one */
+		es->base[channel] = pa & 0xFFFF;
+
+		pa |= 0x00400000;	/* bit 22 -> System RAM */
+                
+                /* Begin loading the APU */
+		for (i = 0; i < 16; i++)
+			apu_set_register(chip, apu, i, 0x0000);
+
+		/* need to enable subgroups.. and we should probably
+		   have different groups for different /dev/dsps..  */
+		apu_set_register(chip, apu, 2, 0x8);
+
+		/* Load the buffer into the wave engine */
+		apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8);
+		/* XXX reg is little endian.. */
+		apu_set_register(chip, apu, 5, pa & 0xFFFF);
+		apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF);
+		apu_set_register(chip, apu, 7, bsize);
+#if 0
+		if (es->fmt & ESS_FMT_STEREO) /* ??? really ??? */
+			apu_set_register(chip, apu, 7, bsize - 1);
+#endif
+
+		/* clear effects/env.. */
+		apu_set_register(chip, apu, 8, 0x00F0);
+		/* amplitude now?  sure.  why not.  */
+		apu_set_register(chip, apu, 9, 0x0000);
+		/* set filter tune, radius, polar pan */
+		apu_set_register(chip, apu, 10, 0x8F08);
+		/* route input */
+		apu_set_register(chip, apu, 11, route);
+		/* dma on, no envelopes, filter to all 1s) */
+		// apu_set_register(chip, apu, 0, 0x400F);
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* clear WP interupts */
+	outw(1, chip->io_port + 0x04);
+	/* enable WP ints */
+	outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	freq = runtime->rate;
+	/* Sample Rate conversion APUs don't like 0x10000 for their rate */
+	if (freq > 47999)
+		freq = 47999;
+	if (freq < 4000)
+		freq = 4000;
+
+	freq = snd_es1968_compute_rate(chip, freq);
+
+	/* Load the frequency, turn on 6dB */
+	snd_es1968_apu_set_freq(chip, es->apu[0], freq);
+	snd_es1968_apu_set_freq(chip, es->apu[1], freq);
+
+	/* fix mixer rate at 48khz.  and its _must_ be 0x10000. */
+	freq = 0x10000;
+	snd_es1968_apu_set_freq(chip, es->apu[2], freq);
+	snd_es1968_apu_set_freq(chip, es->apu[3], freq);
+}
+
+/*******************
+ *  ALSA Interface *
+ *******************/
+
+static int snd_es1968_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO);
+
+	es->dma_size = snd_pcm_lib_buffer_bytes(substream);
+	es->frag_size = snd_pcm_lib_period_bytes(substream);
+
+	es->wav_shift = 1; /* maestro handles always 16bit */
+	es->fmt = 0;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		es->fmt |= ESS_FMT_16BIT;
+	if (runtime->channels > 1) {
+		es->fmt |= ESS_FMT_STEREO;
+		if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */
+			es->wav_shift++;
+	}
+	es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime);
+
+	switch (es->mode) {
+	case ESM_MODE_PLAY:
+		snd_es1968_playback_setup(chip, es, runtime);
+		break;
+	case ESM_MODE_CAPTURE:
+		snd_es1968_capture_setup(chip, es, runtime);
+		break;
+	}
+
+	return 0;
+}
+
+static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO);
+	unsigned long flags;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (es->running)
+			return 0;
+		snd_es1968_bob_inc(chip, es->bob_freq);
+		es->count = 0;
+		es->hwptr = 0;
+		snd_es1968_pcm_start(chip, es);
+		spin_lock_irqsave(&chip->substream_lock, flags);
+		list_add(&es->list, &chip->substream_list);
+		spin_unlock_irqrestore(&chip->substream_lock, flags);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (! es->running)
+			return 0;
+		snd_es1968_pcm_stop(chip, es);
+		spin_lock_irqsave(&chip->substream_lock, flags);
+		list_del(&es->list);
+		spin_unlock_irqrestore(&chip->substream_lock, flags);
+		snd_es1968_bob_dec(chip);
+		break;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO);
+	unsigned int ptr;
+
+	ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift;
+	
+	return bytes_to_frames(substream->runtime, ptr % es->dma_size);
+}
+
+static snd_pcm_hardware_t snd_es1968_playback = {
+	info:			(SNDRV_PCM_INFO_MMAP |
+               		         SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 /*SNDRV_PCM_INFO_PAUSE |*/
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	256,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_es1968_capture = {
+	info:			(SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 /*SNDRV_PCM_INFO_PAUSE |*/
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		/*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	256,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/* *************************
+   * DMA memory management *
+   *************************/
+
+/* Because the Maestro can only take adresses relative to the PCM base adress
+   register :( */
+
+static int calc_available_memory_size(es1968_t *chip)
+{
+	struct list_head *p;
+	int max_size = 0;
+	
+	down(&chip->memory_mutex);
+	list_for_each(p, &chip->buf_list) {
+		esm_memory_t *buf = list_entry(p, esm_memory_t, list);
+		if (buf->empty && buf->size > max_size)
+			max_size = buf->size;
+	}
+	up(&chip->memory_mutex);
+	if (max_size >= 128*1024)
+		max_size = 127*1024;
+	return max_size;
+}
+
+/* allocate a new memory chunk with the specified size */
+static esm_memory_t *snd_es1968_new_memory(es1968_t *chip, int size)
+{
+	esm_memory_t *buf;
+	struct list_head *p;
+	
+	down(&chip->memory_mutex);
+	list_for_each(p, &chip->buf_list) {
+		buf = list_entry(p, esm_memory_t, list);
+		if (buf->empty && buf->size >= size)
+			goto __found;
+	}
+	up(&chip->memory_mutex);
+	return NULL;
+
+__found:
+	if (buf->size > size) {
+		esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
+		if (chunk == NULL)
+			return NULL;
+		chunk->size = buf->size - size;
+		chunk->buf = buf->buf + size;
+		chunk->addr = buf->addr + size;
+		chunk->empty = 1;
+		buf->size = size;
+		list_add(&chunk->list, &buf->list);
+	}
+	buf->empty = 0;
+	up(&chip->memory_mutex);
+	return buf;
+}
+
+/* free a memory chunk */
+static void snd_es1968_free_memory(es1968_t *chip, esm_memory_t *buf)
+{
+	esm_memory_t *chunk;
+
+	down(&chip->memory_mutex);
+	buf->empty = 1;
+	if (buf->list.prev != &chip->buf_list) {
+		chunk = list_entry(buf->list.prev, esm_memory_t, list);
+		if (chunk->empty) {
+			chunk->size += buf->size;
+			list_del(&buf->list);
+			kfree(buf);
+			buf = chunk;
+		}
+	}
+	if (buf->list.next != &chip->buf_list) {
+		chunk = list_entry(buf->list.next, esm_memory_t, list);
+		if (chunk->empty) {
+			buf->size += chunk->size;
+			list_del(&chunk->list);
+			kfree(chunk);
+		}
+	}
+	up(&chip->memory_mutex);
+}
+
+static void snd_es1968_free_dmabuf(es1968_t *chip)
+{
+	struct list_head *p;
+
+	if (! chip->dma_buf)
+		return;
+	snd_free_pci_pages(chip->pci, chip->dma_buf_size, chip->dma_buf, chip->dma_buf_addr);
+	while ((p = chip->buf_list.next) != &chip->buf_list) {
+		esm_memory_t *chunk = list_entry(p, esm_memory_t, list);
+		list_del(p);
+		kfree(chunk);
+	}
+}
+
+static int __devinit
+snd_es1968_init_dmabuf(es1968_t *chip)
+{
+	esm_memory_t *chunk;
+	chip->dma_buf = snd_malloc_pci_pages_fallback(chip->pci, chip->total_bufsize,
+						      &chip->dma_buf_addr, &chip->dma_buf_size);
+	//snd_printd("es1968: allocated buffer size %ld at %p\n", chip->dma_buf_size, chip->dma_buf);
+	if (chip->dma_buf == NULL) {
+		snd_printk("es1968: can't allocate dma pages for size %d\n",
+			   chip->total_bufsize);
+		return -ENOMEM;
+	}
+	if ((chip->dma_buf_addr + chip->dma_buf_size - 1) & ~((1 << 28) - 1)) {
+		snd_es1968_free_dmabuf(chip);
+		snd_printk("es1968: DMA buffer beyond 256MB.\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&chip->buf_list);
+	/* allocate an empty chunk */
+	chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
+	if (chunk == NULL) {
+		snd_es1968_free_dmabuf(chip);
+		return -ENOMEM;
+	}
+	memset(chip->dma_buf, 0, 512);
+	chunk->buf = chip->dma_buf + 512;
+	chunk->addr = chip->dma_buf_addr + 512;
+	chunk->size = chip->dma_buf_size - 512;
+	chunk->empty = 1;
+	list_add(&chunk->list, &chip->buf_list);
+
+	return 0;
+}
+
+/* setup the dma_areas */
+/* buffer is extracted from the pre-allocated memory chunk */
+static int snd_es1968_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *hw_params)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	esschan_t *chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO);
+	int size = params_buffer_bytes(hw_params);
+
+	if (chan->memory) {
+		if (chan->memory->size >= size) {
+			runtime->dma_bytes = size;
+			return 0;
+		}
+		snd_es1968_free_memory(chip, chan->memory);
+	}
+	chan->memory = snd_es1968_new_memory(chip, size);
+	if (chan->memory == NULL) {
+		// snd_printd("cannot allocate dma buffer: size = %d\n", size);
+		return -ENOMEM;
+	}
+	runtime->dma_bytes = size;
+	runtime->dma_area = chan->memory->buf;
+	runtime->dma_addr = chan->memory->addr;
+	return 1; /* area was changed */
+}
+
+/* remove dma areas if allocated */
+static int snd_es1968_hw_free(snd_pcm_substream_t * substream)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	esschan_t *chan;
+	
+	if (runtime->private_data == NULL)
+		return 0;
+	chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO);
+	if (chan->memory) {
+		snd_es1968_free_memory(chip, chan->memory);
+		chan->memory = NULL;
+	}
+	return 0;
+}
+
+
+/*
+ * allocate APU pair
+ */
+static int snd_es1968_alloc_apu_pair(es1968_t *chip, int type)
+{
+	int apu;
+
+	for (apu = 0; apu < NR_APUS; apu += 2) {
+		if (chip->apu[apu] == ESM_APU_FREE &&
+		    chip->apu[apu + 1] == ESM_APU_FREE) {
+			chip->apu[apu] = chip->apu[apu + 1] = type;
+			return apu;
+		}
+	}
+	return -EBUSY;
+}
+
+/*
+ * release APU pair
+ */
+static void snd_es1968_free_apu_pair(es1968_t *chip, int apu)
+{
+	chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE;
+}
+
+
+/******************
+ * PCM open/close *
+ ******************/
+
+static int snd_es1968_playback_open(snd_pcm_substream_t *substream)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	esschan_t *es;
+	int apu1;
+
+	/* search 2 APUs */
+	apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY);
+	if (apu1 < 0)
+		return apu1;
+
+	es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL);
+	if (!es) {
+		snd_es1968_free_apu_pair(chip, apu1);
+		return -ENOMEM;
+	}
+
+	es->apu[0] = apu1;
+	es->apu[1] = apu1 + 1;
+	es->apu_mode[0] = 0;
+	es->apu_mode[1] = 0;
+	es->running = 0;
+	es->substream = substream;
+	es->mode = ESM_MODE_PLAY;
+	INIT_LIST_HEAD(&es->list);
+
+	runtime->private_data = es;
+	runtime->hw = snd_es1968_playback;
+	runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
+		calc_available_memory_size(chip);
+
+	return 0;
+}
+
+static int snd_es1968_capture_copy(snd_pcm_substream_t *substream,
+				   int channel, snd_pcm_uframes_t pos,
+				   void *buf, snd_pcm_uframes_t count)
+{
+	//es1968_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO);
+	char *src = runtime->dma_area;
+
+	if (runtime->channels == 1)
+		return copy_to_user(buf, src + pos, count) ? -EFAULT : 0;
+	else {
+		count /= 2;
+		pos /= 2;
+		if (copy_to_user(buf, src + pos, count))
+			return -EFAULT;
+		if (copy_to_user(buf + count, src + pos + es->dma_size/2, count))
+			return -EFAULT;
+		return 0;
+	}
+}
+
+static int snd_es1968_capture_open(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	esschan_t *es;
+	int apu1, apu2;
+
+	apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE);
+	if (apu1 < 0)
+		return apu1;
+	apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV);
+	if (apu2 < 0) {
+		snd_es1968_free_apu_pair(chip, apu1);
+		return apu2;
+	}
+	
+	es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL);
+	if (!es) {
+		snd_es1968_free_apu_pair(chip, apu1);
+		snd_es1968_free_apu_pair(chip, apu2);
+		return -ENOMEM;
+	}
+
+	es->apu[0] = apu1;
+	es->apu[1] = apu1 + 1;
+	es->apu[2] = apu2;
+	es->apu[3] = apu2 + 1;
+	es->apu_mode[0] = 0;
+	es->apu_mode[1] = 0;
+	es->apu_mode[2] = 0;
+	es->apu_mode[3] = 0;
+	es->running = 0;
+	es->substream = substream;
+	es->mode = ESM_MODE_CAPTURE;
+	INIT_LIST_HEAD(&es->list);
+
+	/* get mixbuffer */
+	if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) {
+		snd_es1968_free_apu_pair(chip, apu1);
+		snd_es1968_free_apu_pair(chip, apu2);
+		snd_magic_kfree(es);
+                return -ENOMEM;
+        }
+
+	runtime->private_data = es;
+	runtime->hw = snd_es1968_capture;
+	runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
+		calc_available_memory_size(chip) - 1024;
+
+	return 0;
+}
+
+static int snd_es1968_playback_close(snd_pcm_substream_t * substream)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	esschan_t *es;
+	if (substream->runtime->private_data == NULL)
+		return 0;
+	es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO);
+	snd_es1968_free_apu_pair(chip, es->apu[0]);
+	snd_magic_kfree(es);
+
+	return 0;
+}
+
+static int snd_es1968_capture_close(snd_pcm_substream_t * substream)
+{
+	es1968_t *chip = snd_pcm_substream_chip(substream);
+	esschan_t *es;
+	if (substream->runtime->private_data == NULL)
+		return 0;
+	es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO);
+	snd_es1968_free_memory(chip, es->mixbuf);
+	snd_es1968_free_apu_pair(chip, es->apu[0]);
+	snd_es1968_free_apu_pair(chip, es->apu[2]);
+	snd_magic_kfree(es);
+
+	return 0;
+}
+
+static snd_pcm_ops_t snd_es1968_playback_ops = {
+	open:		snd_es1968_playback_open,
+	close:		snd_es1968_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_es1968_hw_params,
+	hw_free:	snd_es1968_hw_free,
+	prepare:	snd_es1968_pcm_prepare,
+	trigger:	snd_es1968_pcm_trigger,
+	pointer:	snd_es1968_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_es1968_capture_ops = {
+	open:		snd_es1968_capture_open,
+	close:		snd_es1968_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_es1968_hw_params,
+	hw_free:	snd_es1968_hw_free,
+	prepare:	snd_es1968_pcm_prepare,
+	trigger:	snd_es1968_pcm_trigger,
+	pointer:	snd_es1968_pcm_pointer,
+	copy:		snd_es1968_capture_copy,
+};
+
+
+/*
+ * measure clock
+ */
+#define CLOCK_MEASURE_BUFSIZE	16768	/* enough large for a single shot */
+
+static void __devinit es1968_measure_clock(es1968_t *chip)
+{
+	int i, apu;
+	unsigned int pa, offset, t;
+	esm_memory_t *memory;
+	unsigned long flags;
+	struct timeval start_time, stop_time;
+
+	if (chip->clock == 0)
+		chip->clock = 48000; /* default clock value */
+
+	/* search 2 APUs (although one apu is enough) */
+	if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) {
+		snd_printk("Hmm, cannot find empty APU pair!?\n");
+		return;
+	}
+	if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) {
+		snd_printk("cannot allocate dma buffer - using default clock %d\n", chip->clock);
+		snd_es1968_free_apu_pair(chip, apu);
+		return;
+	}
+
+	memset(memory->buf, 0, CLOCK_MEASURE_BUFSIZE);
+
+	wave_set_register(chip, apu << 3, (memory->addr - 0x10) & 0xfff8);
+
+	pa = (unsigned int)((memory->addr - chip->dma_buf_addr) >> 1);
+	pa |= 0x00400000;	/* System RAM (Bit 22) */
+
+	/* initialize apu */
+	for (i = 0; i < 16; i++)
+		apu_set_register(chip, apu, i, 0x0000);
+
+	apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8);
+	apu_set_register(chip, apu, 5, pa & 0xffff);
+	apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff);
+	apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2);
+	apu_set_register(chip, apu, 8, 0x0000);
+	apu_set_register(chip, apu, 9, 0xD000);
+	apu_set_register(chip, apu, 10, 0x8F08);
+	apu_set_register(chip, apu, 11, 0x0000);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outw(1, chip->io_port + 0x04); /* clear WP interupts */
+	outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); /* enable WP ints */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	__apu_set_register(chip, apu, 5, pa & 0xffff);
+	snd_es1968_trigger_apu(chip, apu, 0x10); /* 16bit mono */
+	do_gettimeofday(&start_time);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ / 20); /* 50 msec */
+#else
+	/* FIXME:
+	 * schedule() above may be too inaccurate and the pointer can
+	 * overlap the boundary..
+	 */
+	mdelay(50);
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	offset = __apu_get_register(chip, apu, 5);
+	do_gettimeofday(&stop_time);
+	snd_es1968_trigger_apu(chip, apu, 0); /* stop */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* check the current position */
+	offset -= (pa & 0xffff);
+	offset &= 0xfffe;
+
+	t = stop_time.tv_sec - start_time.tv_sec;
+	t *= 1000000;
+	if (stop_time.tv_usec < start_time.tv_usec)
+		t -= start_time.tv_usec - stop_time.tv_usec;
+	else
+		t += stop_time.tv_usec - start_time.tv_usec;
+	if (t == 0) {
+		snd_printk("?? calculation error..\n");
+	} else {
+		offset *= 1000;
+		offset = (offset / t) * 1000 + ((offset % t) * 1000) / t;
+		if (offset < 47500 || offset > 48500) {
+			if (offset >= 40000 && offset <= 50000)
+				chip->clock = (chip->clock * offset) / 48000;
+		}
+		printk(KERN_INFO "es1968: clocking to %d\n", chip->clock);
+	}
+	snd_es1968_free_memory(chip, memory);
+	snd_es1968_free_apu_pair(chip, apu);
+}
+
+
+/*
+ */
+
+static void snd_es1968_pcm_free(snd_pcm_t *pcm)
+{
+	es1968_t *esm = snd_magic_cast(es1968_t, pcm->private_data, return);
+	snd_es1968_free_dmabuf(esm);
+	esm->pcm = NULL;
+}
+
+static int __devinit
+snd_es1968_pcm(es1968_t *chip, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	/* get DMA buffer */
+	if ((err = snd_es1968_init_dmabuf(chip)) < 0)
+		return err;
+
+	/* set PCMBAR */
+	wave_set_register(chip, 0x01FC, chip->dma_buf_addr >> 12);
+	wave_set_register(chip, 0x01FD, chip->dma_buf_addr >> 12);
+	wave_set_register(chip, 0x01FE, chip->dma_buf_addr >> 12);
+	wave_set_register(chip, 0x01FF, chip->dma_buf_addr >> 12);
+
+	if ((err = snd_pcm_new(chip->card, "ESS Maestro", device,
+			       chip->playback_streams,
+			       chip->capture_streams, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_es1968_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops);
+
+	pcm->info_flags = 0;
+
+	strcpy(pcm->name, "ESS Maestro");
+
+	chip->pcm = pcm;
+
+	return 0;
+}
+
+/*
+ * update pointer
+ */
+static void snd_es1968_update_pcm(es1968_t *chip, esschan_t *es)
+{
+	unsigned int hwptr;
+	unsigned int diff;
+	snd_pcm_substream_t *subs = es->substream;
+        
+	if (subs == NULL || !es->running)
+		return;
+
+	hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift;
+	hwptr %= es->dma_size;
+
+	diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size;
+
+	es->hwptr = hwptr;
+	es->count += diff;
+
+	while (es->count > es->frag_size) {
+		spin_unlock(&chip->substream_lock);
+		snd_pcm_period_elapsed(subs);
+		spin_lock(&chip->substream_lock);
+		es->count -= es->frag_size;
+	}
+}
+
+/*
+ */
+static void es1968_update_hw_volume(unsigned long private_data)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, (void*)private_data, return);
+	int x, val;
+
+	/* Figure out which volume control button was pushed,
+	   based on differences from the default register
+	   values. */
+	x = inb(chip->io_port + 0x1c);
+	/* Reset the volume control registers. */
+	outb(0x88, chip->io_port + 0x1c);
+	outb(0x88, chip->io_port + 0x1d);
+	outb(0x88, chip->io_port + 0x1e);
+	outb(0x88, chip->io_port + 0x1f);
+
+	if (! chip->master_switch || ! chip->master_volume)
+		return;
+
+	/* FIXME: more clean up is needed.. */
+	val = chip->ac97->regs[AC97_MASTER];
+	if (x & 1) {
+		/* mute */
+		snd_ac97_write_cache(chip->ac97, 0, val ^ 0x8000);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_switch->id);
+	} else {
+		val &= 0x7fff;
+		if (((x>>1) & 7) > 4) {
+			/* volume up */
+			if ((val & 0xff) > 0)
+				val--;
+			if ((val & 0xff00) > 0x100)
+				val -= 0x0100;
+		} else {
+			/* volume down */
+			if ((val & 0xff) < 0x1f)
+				val++;
+			if ((val & 0xff00) < 0x1f00)
+				val += 0x0100;
+		}
+		snd_ac97_write_cache(chip->ac97, 0, val);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_volume->id);
+	}
+}
+
+/*
+ * interrupt handler
+ */
+static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, dev_id, return);
+	u32 event;
+
+	if (!(event = inb(chip->io_port + 0x1A)))
+		return;
+
+	outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
+
+	if (event & ESM_HWVOL_IRQ)
+		tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
+
+	/* else ack 'em all, i imagine */
+	outb(0xFF, chip->io_port + 0x1A);
+
+	if ((event & ESM_MPU401_IRQ) && chip->rmidi) {
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+	}
+
+	if (event & ESM_SOUND_IRQ) {
+		struct list_head *p, *n;
+		spin_lock(&chip->substream_lock);
+		/* we need to use list_for_each_safe here since the substream
+		 * can be deleted in period_elapsed().
+		 */
+		list_for_each_safe(p, n, &chip->substream_list) {
+			esschan_t *es = list_entry(p, esschan_t, list);
+			snd_es1968_update_pcm(chip, es);
+		}
+		spin_unlock(&chip->substream_lock);
+	}
+}
+
+/*
+ *  Mixer stuff
+ */
+
+static int __devinit
+snd_es1968_mixer(es1968_t *chip)
+{
+	ac97_t ac97;
+	snd_ctl_elem_id_t id;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_es1968_ac97_write;
+	ac97.read = snd_es1968_ac97_read;
+	ac97.private_data = chip;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+
+	/* attach master switch / volumes for h/w volume control */
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, "Master Playback Switch");
+	chip->master_switch = snd_ctl_find_id(chip->card, &id);
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, "Master Playback Volume");
+	chip->master_volume = snd_ctl_find_id(chip->card, &id);
+
+	return 0;
+}
+
+/*
+ * reset ac97 codec
+ */
+
+static void snd_es1968_ac97_reset(es1968_t *chip)
+{
+	unsigned long ioaddr = chip->io_port;
+
+	unsigned short save_ringbus_a;
+	unsigned short save_68;
+	unsigned short w;
+	unsigned int vend;
+
+	/* save configuration */
+	save_ringbus_a = inw(ioaddr + 0x36);
+
+	//outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */
+	/* set command/status address i/o to 1st codec */
+	outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+	outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+	/* disable ac link */
+	outw(0x0000, ioaddr + 0x36);
+	save_68 = inw(ioaddr + 0x68);
+	pci_read_config_word(chip->pci, 0x58, &w);	/* something magical with gpio and bus arb. */
+	pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+	if (w & 1)
+		save_68 |= 0x10;
+	outw(0xfffe, ioaddr + 0x64);	/* unmask gpio 0 */
+	outw(0x0001, ioaddr + 0x68);	/* gpio write */
+	outw(0x0000, ioaddr + 0x60);	/* write 0 to gpio 0 */
+	udelay(20);
+	outw(0x0001, ioaddr + 0x60);	/* write 1 to gpio 1 */
+	mdelay(20);
+
+	outw(save_68 | 0x1, ioaddr + 0x68);	/* now restore .. */
+	outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38);
+	outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a);
+	outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c);
+
+	/* now the second codec */
+	/* disable ac link */
+	outw(0x0000, ioaddr + 0x36);
+	outw(0xfff7, ioaddr + 0x64);	/* unmask gpio 3 */
+	save_68 = inw(ioaddr + 0x68);
+	outw(0x0009, ioaddr + 0x68);	/* gpio write 0 & 3 ?? */
+	outw(0x0001, ioaddr + 0x60);	/* write 1 to gpio */
+	udelay(20);
+	outw(0x0009, ioaddr + 0x60);	/* write 9 to gpio */
+	mdelay(500);		/* .. ouch.. */
+	//outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+	outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+	outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+#if 0				/* the loop here needs to be much better if we want it.. */
+	snd_printk("trying software reset\n");
+	/* try and do a software reset */
+	outb(0x80 | 0x7c, ioaddr + 0x30);
+	for (w = 0;; w++) {
+		if ((inw(ioaddr + 0x30) & 1) == 0) {
+			if (inb(ioaddr + 0x32) != 0)
+				break;
+
+			outb(0x80 | 0x7d, ioaddr + 0x30);
+			if (((inw(ioaddr + 0x30) & 1) == 0)
+			    && (inb(ioaddr + 0x32) != 0))
+				break;
+			outb(0x80 | 0x7f, ioaddr + 0x30);
+			if (((inw(ioaddr + 0x30) & 1) == 0)
+			    && (inb(ioaddr + 0x32) != 0))
+				break;
+		}
+
+		if (w > 10000) {
+			outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37);	/* do a software reset */
+			mdelay(500);	/* oh my.. */
+			outb(inb(ioaddr + 0x37) & ~0x08,
+				ioaddr + 0x37);
+			udelay(1);
+			outw(0x80, ioaddr + 0x30);
+			for (w = 0; w < 10000; w++) {
+				if ((inw(ioaddr + 0x30) & 1) == 0)
+					break;
+			}
+		}
+	}
+#endif
+	if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) {
+		/* turn on external amp? */
+		outw(0xf9ff, ioaddr + 0x64);
+		outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68);
+		outw(0x0209, ioaddr + 0x60);
+	}
+
+	/* restore.. */
+	outw(save_ringbus_a, ioaddr + 0x36);
+
+	/* Turn on the 978 docking chip.
+	   First frob the "master output enable" bit,
+	   then set most of the playback volume control registers to max. */
+	outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0);
+	outb(0xff, ioaddr+0xc3);
+	outb(0xff, ioaddr+0xc4);
+	outb(0xff, ioaddr+0xc6);
+	outb(0xff, ioaddr+0xc8);
+	outb(0x3f, ioaddr+0xcf);
+	outb(0x3f, ioaddr+0xd0);
+}
+
+static void snd_es1968_reset(es1968_t *chip)
+{
+	/* Reset */
+	outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND,
+	     chip->io_port + ESM_PORT_HOST_IRQ);
+	udelay(10);
+	outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ);
+	udelay(10);
+}
+
+/*
+ * power management
+ */
+static void snd_es1968_set_acpi(es1968_t *chip, int state)
+{
+	u16 active_mask = acpi_state_mask[state];
+
+	pci_set_power_state(chip->pci, state);
+	/* make sure the units we care about are on 
+		XXX we might want to do this before state flipping? */
+	pci_write_config_word(chip->pci, 0x54, ~ active_mask);
+	pci_write_config_word(chip->pci, 0x56, ~ active_mask);
+}
+
+
+/*
+ * initialize maestro chip
+ */
+static void snd_es1968_chip_init(es1968_t *chip)
+{
+	struct pci_dev *pci = chip->pci;
+	int i;
+	unsigned long iobase  = chip->io_port;
+	u16 w;
+	u32 n;
+
+	/* We used to muck around with pci config space that
+	 * we had no business messing with.  We don't know enough
+	 * about the machine to know which DMA mode is appropriate, 
+	 * etc.  We were guessing wrong on some machines and making
+	 * them unhappy.  We now trust in the BIOS to do things right,
+	 * which almost certainly means a new host of problems will
+	 * arise with broken BIOS implementations.  screw 'em. 
+	 * We're already intolerant of machines that don't assign
+	 * IRQs.
+	 */
+	
+	/* do config work at full power */
+	snd_es1968_set_acpi(chip, ACPI_D0);
+
+	/* Config Reg A */
+	pci_read_config_word(pci, ESM_CONFIG_A, &w);
+
+	/*      Use TDMA for now. TDMA works on all boards, so while its
+	 *      not the most efficient its the simplest. */
+	w &= ~DMA_CLEAR;	/* Clear DMA bits */
+	w |= DMA_TDMA;		/* TDMA on */
+	w &= ~(PIC_SNOOP1 | PIC_SNOOP2);	/* Clear Pic Snoop Mode Bits */
+	w &= ~SAFEGUARD;	/* Safeguard off */
+	w |= POST_WRITE;	/* Posted write */
+	w |= ISA_TIMING;	/* ISA timing on */
+	/* XXX huh?  claims to be reserved.. */
+	w &= ~SWAP_LR;		/* swap left/right 
+				   seems to only have effect on SB
+				   Emulation */
+	w &= ~SUBTR_DECODE;	/* Subtractive decode off */
+
+	pci_write_config_word(pci, ESM_CONFIG_A, w);
+
+	/* Config Reg B */
+
+	pci_read_config_word(pci, ESM_CONFIG_B, &w);
+
+	w &= ~(1 << 15);	/* Turn off internal clock multiplier */
+	/* XXX how do we know which to use? */
+	w &= ~(1 << 14);	/* External clock */
+
+	w &= ~SPDIF_CONFB;	/* disable S/PDIF output */
+	w |= HWV_CONFB;		/* HWV on */
+	w |= DEBOUNCE;		/* Debounce off: easier to push the HW buttons */
+	w &= ~GPIO_CONFB;	/* GPIO 4:5 */
+	w |= CHI_CONFB;		/* Disconnect from the CHI.  Enabling this made a dell 7500 work. */
+	w &= ~IDMA_CONFB;	/* IDMA off (undocumented) */
+	w &= ~MIDI_FIX;		/* MIDI fix off (undoc) */
+	w &= ~(1 << 1);		/* reserved, always write 0 */
+	w &= ~IRQ_TO_ISA;	/* IRQ to ISA off (undoc) */
+
+	pci_write_config_word(pci, ESM_CONFIG_B, w);
+
+	/* DDMA off */
+
+	pci_read_config_word(pci, ESM_DDMA, &w);
+	w &= ~(1 << 0);
+	pci_write_config_word(pci, ESM_DDMA, w);
+
+	/*
+	 *	Legacy mode
+	 */
+
+	pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w);
+
+	w &= ~ESS_ENABLE_AUDIO;	/* Disable Legacy Audio */
+	w &= ~ESS_ENABLE_SERIAL_IRQ;	/* Disable SIRQ */
+	w &= ~(0x1f);		/* disable mpu irq/io, game port, fm, SB */
+
+	pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w);
+
+	/* Set up 978 docking control chip. */
+	pci_read_config_word(pci, 0x58, &w);
+	w|=1<<2;	/* Enable 978. */
+	w|=1<<3;	/* Turn on 978 hardware volume control. */
+	w&=~(1<<11);	/* Turn on 978 mixer volume control. */
+	pci_write_config_word(pci, 0x58, w);
+	
+	/* Sound Reset */
+
+	snd_es1968_reset(chip);
+
+	/*
+	 *	Ring Bus Setup
+	 */
+
+	/* setup usual 0x34 stuff.. 0x36 may be chip specific */
+	outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */
+	udelay(20);
+	outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */
+	udelay(20);
+
+	/*
+	 *	Reset the CODEC
+	 */
+	 
+	snd_es1968_ac97_reset(chip);
+
+	/* Ring Bus Control B */
+
+	n = inl(iobase + ESM_RING_BUS_CONTR_B);
+	n &= ~RINGB_EN_SPDIF;	/* SPDIF off */
+	//w |= RINGB_EN_2CODEC;	/* enable 2nd codec */
+	outl(n, iobase + ESM_RING_BUS_CONTR_B);
+
+	/* Set hardware volume control registers to midpoints.
+	   We can tell which button was pushed based on how they change. */
+	outb(0x88, iobase+0x1c);
+	outb(0x88, iobase+0x1d);
+	outb(0x88, iobase+0x1e);
+	outb(0x88, iobase+0x1f);
+
+	/* it appears some maestros (dell 7500) only work if these are set,
+	   regardless of wether we use the assp or not. */
+
+	outb(0, iobase + ASSP_CONTROL_B);
+	outb(3, iobase + ASSP_CONTROL_A);	/* M: Reserved bits... */
+	outb(0, iobase + ASSP_CONTROL_C);	/* M: Disable ASSP, ASSP IRQ's and FM Port */
+
+	/* Enable IRQ's */
+	w = ESM_HIRQ_DSIE | ESM_HIRQ_MPU401;
+	outw(w, iobase + ESM_PORT_HOST_IRQ);
+
+	/*
+	 * set up wavecache
+	 */
+	for (i = 0; i < 16; i++) {
+		/* Write 0 into the buffer area 0x1E0->1EF */
+		outw(0x01E0 + i, iobase + WC_INDEX);
+		outw(0x0000, iobase + WC_DATA);
+
+		/* The 1.10 test program seem to write 0 into the buffer area
+		 * 0x1D0-0x1DF too.*/
+		outw(0x01D0 + i, iobase + WC_INDEX);
+		outw(0x0000, iobase + WC_DATA);
+	}
+	wave_set_register(chip, IDR7_WAVE_ROMRAM,
+			  (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00));
+	wave_set_register(chip, IDR7_WAVE_ROMRAM,
+			  wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100);
+	wave_set_register(chip, IDR7_WAVE_ROMRAM,
+			  wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200);
+	wave_set_register(chip, IDR7_WAVE_ROMRAM,
+			  wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400);
+
+
+	maestro_write(chip, IDR2_CRAM_DATA, 0x0000);
+	/* Now back to the DirectSound stuff */
+	/* audio serial configuration.. ? */
+	maestro_write(chip, 0x08, 0xB004);
+	maestro_write(chip, 0x09, 0x001B);
+	maestro_write(chip, 0x0A, 0x8000);
+	maestro_write(chip, 0x0B, 0x3F37);
+	maestro_write(chip, 0x0C, 0x0098);
+
+	/* parallel in, has something to do with recording :) */
+	maestro_write(chip, 0x0C,
+		      (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000);
+	/* parallel out */
+	maestro_write(chip, 0x0C,
+		      (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500);
+
+	maestro_write(chip, 0x0D, 0x7632);
+
+	/* Wave cache control on - test off, sg off, 
+	   enable, enable extra chans 1Mb */
+
+	w = inw(iobase + WC_CONTROL);
+
+	w &= ~0xFA00;		/* Seems to be reserved? I don't know */
+	w |= 0xA000;		/* reserved... I don't know */
+	w &= ~0x0200;		/* Channels 56,57,58,59 as Extra Play,Rec Channel enable
+				   Seems to crash the Computer if enabled... */
+	w |= 0x0100;		/* Wave Cache Operation Enabled */
+	w |= 0x0080;		/* Channels 60/61 as Placback/Record enabled */
+	w &= ~0x0060;		/* Clear Wavtable Size */
+	w |= 0x0020;		/* Wavetable Size : 1MB */
+	/* Bit 4 is reserved */
+	w &= ~0x000C;		/* DMA Stuff? I don't understand what the datasheet means */
+	/* Bit 1 is reserved */
+	w &= ~0x0001;		/* Test Mode off */
+
+	outw(w, iobase + WC_CONTROL);
+
+	/* Now clear the APU control ram */
+	for (i = 0; i < NR_APUS; i++) {
+		for (w = 0; w < NR_APU_REGS; w++)
+			apu_set_register(chip, i, w, 0);
+
+	}
+}
+
+#ifdef CONFIG_PM
+/*
+ * PM support
+ */
+static void es1968_suspend(es1968_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	snd_pcm_suspend_all(chip->pcm);
+	snd_es1968_bob_stop(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void es1968_resume(es1968_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	/* restore all our config */
+	pci_enable_device(chip->pci);
+	snd_es1968_chip_init(chip);
+
+	/* need to restore the base pointers.. */ 
+	if (chip->dma_buf_addr) {
+		/* set PCMBAR */
+		wave_set_register(chip, 0x01FC, chip->dma_buf_addr >> 12);
+	}
+
+	/* restore ac97 state */
+	snd_ac97_resume(chip->ac97);
+
+	/* start timer again */
+	if (atomic_read(&chip->bobclient))
+		snd_es1968_bob_start(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+#ifndef PCI_OLD_SUSPEND
+static int snd_es1968_suspend(struct pci_dev *dev, u32 state)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO);
+	es1968_suspend(chip);
+	return 0;
+}
+static int snd_es1968_resume(struct pci_dev *dev)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO);
+	es1968_resume(chip);
+	return 0;
+}
+#else
+static void snd_es1968_suspend(struct pci_dev *dev)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return);
+	es1968_suspend(chip);
+}
+static void snd_es1968_resume(struct pci_dev *dev)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return);
+	es1968_resume(chip);
+}
+#endif
+
+/* callback */
+static int snd_es1968_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, card->power_state_private_data, return -ENXIO);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		es1968_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		es1968_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static int snd_es1968_free(es1968_t *chip)
+{
+	snd_es1968_set_acpi(chip, ACPI_D3);
+	chip->master_switch = NULL;
+	chip->master_volume = NULL;
+	if (chip->res_io_port) {
+		release_resource(chip->res_io_port);
+		kfree_nocheck(chip->res_io_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es1968_dev_free(snd_device_t *device)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, device->device_data, return -ENXIO);
+	return snd_es1968_free(chip);
+}
+
+static int __devinit snd_es1968_create(snd_card_t * card,
+				    struct pci_dev *pci,
+				    int total_bufsize,
+				    int play_streams,
+				    int capt_streams,
+				    es1968_t **chip_ret)
+{
+	static snd_device_ops_t ops = {
+		dev_free:       snd_es1968_dev_free,
+	};
+	es1968_t *chip;
+	int i, err;
+
+	*chip_ret = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 28 bits */
+	if (!pci_dma_supported(pci, 0x0fffffff)) {
+		snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x0fffffff);
+
+	chip = (es1968_t *) snd_magic_kcalloc(es1968_t, 0, GFP_KERNEL);
+	if (! chip)
+		return -ENOMEM;
+
+	/* Set Vars */
+	chip->type = (pci->vendor << 16) | pci->device;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->substream_lock);
+	spin_lock_init(&chip->bob_lock);
+	INIT_LIST_HEAD(&chip->buf_list);
+	INIT_LIST_HEAD(&chip->substream_list);
+	init_MUTEX(&chip->memory_mutex);
+	tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->total_bufsize = total_bufsize;	/* in bytes */
+	chip->playback_streams = play_streams;
+	chip->capture_streams = capt_streams;
+
+	chip->io_port = pci_resource_start(pci, 0);
+	if ((chip->res_io_port = request_region(chip->io_port, 0x100, "ESS Maestro")) == NULL) {
+		snd_es1968_free(chip);
+		snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 0x100 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			"ESS Maestro", (void*)chip)) {
+		snd_es1968_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	        
+	/* Clear Maestro_map */
+	for (i = 0; i < 32; i++)
+		chip->maestro_map[i] = 0;
+
+	/* Clear Apu Map */
+	for (i = 0; i < NR_APUS; i++)
+		chip->apu[i] = ESM_APU_FREE;
+
+	atomic_set(&chip->bobclient, 0);
+
+	/* just to be sure */
+	pci_set_master(pci);
+
+	snd_es1968_chip_init(chip);
+
+#ifdef CONFIG_PM
+	card->set_power_state = snd_es1968_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es1968_free(chip);
+		return err;
+	}
+
+	*chip_ret = chip;
+
+	return 0;
+}
+
+
+/*
+ * joystick
+ */
+
+static int snd_es1968_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es1968_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	es1968_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val;
+
+	pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val);
+	ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0;
+	return 0;
+}
+
+static int snd_es1968_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	es1968_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val, oval;
+
+	pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &oval);
+	val = oval & ~0x04;
+	if (ucontrol->value.integer.value[0])
+		val |= 0x04;
+	if (val != oval); {
+		pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val);
+		return 1;
+	}
+	return 0;
+}
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_es1968_control_switches[] __devinitdata = {
+	{
+		name: "Joystick",
+		iface: SNDRV_CTL_ELEM_IFACE_CARD,
+		info: snd_es1968_joystick_info,
+		get: snd_es1968_joystick_get,
+		put: snd_es1968_joystick_put,
+	}
+};
+
+/*
+ */
+static int __devinit snd_es1968_probe(struct pci_dev *pci,
+				      const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	es1968_t *chip;
+	int i, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (!card)
+		return -ENOMEM;
+                
+	if (snd_total_bufsize[dev] < 128)
+		snd_total_bufsize[dev] = 128;
+	if (snd_total_bufsize[dev] > 4096)
+		snd_total_bufsize[dev] = 4096;
+	if ((err = snd_es1968_create(card, pci,
+				     snd_total_bufsize[dev] * 1024, /* in bytes */
+				     snd_pcm_substreams_p[dev], 
+				     snd_pcm_substreams_c[dev],
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	switch (chip->type) {
+	case CARD_TYPE_ESS_ES1978:
+		strcpy(card->driver, "ES1978");
+		strcpy(card->shortname, "ESS ES1978 (Maestro 2E)");
+		break;
+	case CARD_TYPE_ESS_ES1968:
+		strcpy(card->driver, "ES1968");
+		strcpy(card->shortname, "ESS ES1968 (Maestro 2)");
+		break;
+	case CARD_TYPE_ESS_ESOLDM1:
+		strcpy(card->driver, "ESM1");
+		strcpy(card->shortname, "ESS Maestro 1");
+		break;
+	}
+
+	if ((err = snd_es1968_pcm(chip, 0)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_es1968_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				       chip->io_port + ESM_MPU401_PORT, 1,
+				       chip->irq, 0, &chip->rmidi)) < 0) {
+		printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
+	}
+
+	/* card switches */
+	for (i = 0; i < num_controls(snd_es1968_control_switches); i++) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_es1968_control_switches[i], chip));
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	chip->clock = snd_clock[dev];
+	if (! chip->clock)
+		es1968_measure_clock(chip);
+
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, chip->io_port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_es1968_remove(struct pci_dev *pci)
+{
+	es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "ES1968 (ESS Maestro)",
+	id_table: snd_es1968_ids,
+	probe: snd_es1968_probe,
+	remove: __devexit_p(snd_es1968_remove),
+#ifdef CONFIG_PM
+	suspend: snd_es1968_suspend,
+	resume: snd_es1968_resume,
+#endif
+};
+
+#if 0 // do we really need this?
+static int snd_es1968_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+{
+	pci_unregister_driver(&driver);
+	return NOTIFY_OK;
+}
+
+static struct notifier_block snd_es1968_nb = {snd_es1968_notifier, NULL, 0};
+#endif
+
+static int __init alsa_card_es1968_init(void)
+{
+	int err;
+
+        if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "ESS Maestro soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+#if 0 // do we really need this?
+	/* If this driver is not shutdown cleanly at reboot, it can
+	   leave the speaking emitting an annoying noise, so we catch
+	   shutdown events. */ 
+	if (register_reboot_notifier(&snd_es1968_nb)) {
+		printk(KERN_ERR "reboot notifier registration failed; may make noise at shutdown.\n");
+	}
+#endif
+	return 0;
+}
+
+static void __exit alsa_card_es1968_exit(void)
+{
+#if 0 // do we really need this?
+	unregister_reboot_notifier(&snd_es1968_nb);
+#endif
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1968_init)
+module_exit(alsa_card_es1968_exit)
+
+#ifndef MODULE
+
+/* format is: snd-es1968=snd_enable,snd_index,snd_id,
+			 snd_total_bufsize,
+			 snd_pcm_substreams_p,
+			 snd_pcm_substreams_c,
+			 snd_clock
+*/
+
+static int __init alsa_card_es1968_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_total_bufsize[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_substreams_p[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_substreams_c[nr_dev]) == 2 &&
+	       get_option(&str,&snd_clock[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-es1968=", alsa_card_es1968_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/fm801.c linux-2.4.19-pre5-mjc/sound/pci/fm801.c
--- linux/sound/pci/fm801.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/fm801.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1141 @@
+/*
+ *  The driver for the ForteMedia FM801 based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t fm801_t
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ForteMedia FM801");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ForteMedia,FM801},"
+		"{Genius,SoundMaker Live 5.1}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for the FM801 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for the FM801 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable FM801 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+
+/*
+ *  Direct registers
+ */
+
+#define FM801_REG(chip, reg)	(chip->port + FM801_##reg)
+
+#define FM801_PCM_VOL		0x00	/* PCM Output Volume */
+#define FM801_FM_VOL		0x02	/* FM Output Volume */
+#define FM801_I2S_VOL		0x04	/* I2S Volume */
+#define FM801_REC_SRC		0x06	/* Record Source */
+#define FM801_PLY_CTRL		0x08	/* Playback Control */
+#define FM801_PLY_COUNT		0x0a	/* Playback Count */
+#define FM801_PLY_BUF1		0x0c	/* Playback Bufer I */
+#define FM801_PLY_BUF2		0x10	/* Playback Buffer II */
+#define FM801_CAP_CTRL		0x14	/* Capture Control */
+#define FM801_CAP_COUNT		0x16	/* Capture Count */
+#define FM801_CAP_BUF1		0x18	/* Capture Buffer I */
+#define FM801_CAP_BUF2		0x1c	/* Capture Buffer II */
+#define FM801_CODEC_CTRL	0x22	/* Codec Control */
+#define FM801_I2S_MODE		0x24	/* I2S Mode Control */
+#define FM801_VOLUME		0x26	/* Volume Up/Down/Mute Status */
+#define FM801_I2C_CTRL		0x29	/* I2C Control */
+#define FM801_AC97_CMD		0x2a	/* AC'97 Command */
+#define FM801_AC97_DATA		0x2c	/* AC'97 Data */
+#define FM801_MPU401_DATA	0x30	/* MPU401 Data */
+#define FM801_MPU401_CMD	0x31	/* MPU401 Command */
+#define FM801_GPIO_CTRL		0x52	/* General Purpose I/O Control */
+#define FM801_GEN_CTRL		0x54	/* General Control */
+#define FM801_IRQ_MASK		0x56	/* Interrupt Mask */
+#define FM801_IRQ_STATUS	0x5a	/* Interrupt Status */
+#define FM801_OPL3_BANK0	0x68	/* OPL3 Status Read / Bank 0 Write */
+#define FM801_OPL3_DATA0	0x69	/* OPL3 Data 0 Write */
+#define FM801_OPL3_BANK1	0x6a	/* OPL3 Bank 1 Write */
+#define FM801_OPL3_DATA1	0x6b	/* OPL3 Bank 1 Write */
+#define FM801_POWERDOWN		0x70	/* Blocks Power Down Control */
+
+#define FM801_AC97_ADDR_SHIFT	10
+
+/* playback and record control register bits */
+#define FM801_BUF1_LAST		(1<<1)
+#define FM801_BUF2_LAST		(1<<2)
+#define FM801_START		(1<<5)
+#define FM801_PAUSE		(1<<6)
+#define FM801_IMMED_STOP	(1<<7)
+#define FM801_RATE_SHIFT	8
+#define FM801_RATE_MASK		(15 << FM801_RATE_SHIFT)
+#define FM801_CHANNELS_4	(1<<12)	/* playback only */
+#define FM801_CHANNELS_6	(2<<12)	/* playback only */
+#define FM801_CHANNELS_6MS	(3<<12)	/* playback only */
+#define FM801_CHANNELS_MASK	(3<<12)
+#define FM801_16BIT		(1<<14)
+#define FM801_STEREO		(1<<15)
+
+/* IRQ status bits */
+#define FM801_IRQ_PLAYBACK	(1<<8)
+#define FM801_IRQ_CAPTURE	(1<<9)
+#define FM801_IRQ_VOLUME	(1<<14)
+#define FM801_IRQ_MPU		(1<<15)
+	
+/*
+
+ */
+
+typedef struct _snd_fm801 fm801_t;
+
+struct _snd_fm801 {
+	int irq;
+
+	unsigned long port;	/* I/O port number */
+	struct resource *res_port;
+	unsigned int multichannel: 1,	/* multichannel support */
+		     secondary: 1;	/* secondary codec */
+	unsigned char secondary_addr;	/* addres of the secondary codec */
+
+	unsigned short ply_ctrl; /* playback control */
+	unsigned short cap_ctrl; /* capture control */
+
+	unsigned long ply_buffer;
+	unsigned int ply_buf;
+	unsigned int ply_count;
+	unsigned int ply_size;
+	unsigned int ply_pos;
+
+	unsigned long cap_buffer;
+	unsigned int cap_buf;
+	unsigned int cap_count;
+	unsigned int cap_size;
+	unsigned int cap_pos;
+
+	ac97_t *ac97;
+	ac97_t *ac97_sec;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *rmidi;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	spinlock_t reg_lock;
+	snd_info_entry_t *proc_entry;
+};
+
+static struct pci_device_id snd_fm801_ids[] __devinitdata = {
+	{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* FM801 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
+
+/*
+ *  common I/O routines
+ */
+
+static int snd_fm801_update_bits(fm801_t *chip, unsigned short reg,
+				 unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new;
+
+	spin_lock(&chip->reg_lock);
+	old = inw(chip->port + reg);
+	new = (old & ~mask) | value;
+	change = old != new;
+	if (change)
+		outw(new, chip->port + reg);
+	spin_unlock(&chip->reg_lock);
+	return change;
+}
+
+static void snd_fm801_codec_write(ac97_t *ac97,
+				  unsigned short reg,
+				  unsigned short val)
+{
+	fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return);
+	int idx;
+
+	/*
+	 *  Wait until the codec interface is not ready..
+	 */
+	for (idx = 0; idx < 100; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			goto ok1;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface is busy (1)\n");
+	return;
+
+ ok1:
+	/* write data and address */
+	outw(val, FM801_REG(chip, AC97_DATA));
+	outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+	/*
+	 *  Wait until the write command is not completed..
+         */
+	for (idx = 0; idx < 1000; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			return;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num);
+}
+
+static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return -ENXIO);
+	int idx;
+
+	/*
+	 *  Wait until the codec interface is not ready..
+	 */
+	for (idx = 0; idx < 100; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			goto ok1;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface is busy (1)\n");
+	return 0;
+
+ ok1:
+	/* read command */
+	outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD));
+	for (idx = 0; idx < 100; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			goto ok2;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num);
+	return 0;
+
+ ok2:
+	for (idx = 0; idx < 1000; idx++) {
+		if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8))
+			goto ok3;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface #%d is not valid (2)\n", ac97->num);
+	return 0;
+
+ ok3:
+	return inw(FM801_REG(chip, AC97_DATA));
+}
+
+static unsigned int rates[] = {
+  5500,  8000,  9600, 11025,
+  16000, 19200, 22050, 32000,
+  38400, 44100, 48000
+};
+
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: RATES,
+	list: rates,
+	mask: 0,
+};
+
+static unsigned int channels[] = {
+  2, 4, 6
+};
+
+#define CHANNELS sizeof(channels) / sizeof(channels[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
+	count: CHANNELS,
+	list: channels,
+	mask: 0,
+};
+
+/*
+ *  Sample rate routines
+ */
+
+static unsigned short snd_fm801_rate_bits(int rate)
+{
+	unsigned int idx;
+
+	for (idx = 0; idx < 11; idx++)
+		if (rates[idx] == rate)
+			return idx;
+	snd_BUG();
+	return RATES - 1;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		chip->ply_ctrl &= ~(FM801_BUF1_LAST |
+				     FM801_BUF2_LAST |
+				     FM801_PAUSE);
+		chip->ply_ctrl |= FM801_START |
+				   FM801_IMMED_STOP;
+		outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		chip->ply_ctrl &= ~FM801_START;
+		outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream,
+				     int cmd)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		chip->cap_ctrl &= ~(FM801_BUF1_LAST |
+				     FM801_BUF2_LAST |
+				     FM801_PAUSE);
+		chip->cap_ctrl |= FM801_START |
+				   FM801_IMMED_STOP;
+		outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		chip->cap_ctrl &= ~FM801_START;
+		outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+static int snd_fm801_hw_params(snd_pcm_substream_t * substream,
+			       snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_fm801_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_fm801_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
+	chip->ply_count = snd_pcm_lib_period_bytes(substream);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
+			     FM801_STEREO | FM801_RATE_MASK |
+			     FM801_CHANNELS_MASK);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		chip->ply_ctrl |= FM801_16BIT;
+	if (runtime->channels > 1) {
+		chip->ply_ctrl |= FM801_STEREO;
+		if (runtime->channels == 4)
+			chip->ply_ctrl |= FM801_CHANNELS_4;
+		else if (runtime->channels == 6)
+			chip->ply_ctrl |= FM801_CHANNELS_6;
+	}
+	chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
+	chip->ply_buf = 0;
+	outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+	outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+	chip->ply_buffer = runtime->dma_addr;
+	chip->ply_pos = 0;
+	outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
+	outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_fm801_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
+	chip->cap_count = snd_pcm_lib_period_bytes(substream);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
+			     FM801_STEREO | FM801_RATE_MASK);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		chip->cap_ctrl |= FM801_16BIT;
+	if (runtime->channels > 1)
+		chip->cap_ctrl |= FM801_STEREO;
+	chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
+	chip->cap_buf = 0;
+	outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+	outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+	chip->cap_buffer = runtime->dma_addr;
+	chip->cap_pos = 0;
+	outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
+	outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	size_t ptr;
+
+	if (!(chip->ply_ctrl & FM801_START))
+		return 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
+	if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+		ptr += chip->ply_count;
+		ptr %= chip->ply_size;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	size_t ptr;
+
+	if (!(chip->cap_ctrl & FM801_START))
+		return 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
+	if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+		ptr += chip->cap_count;
+		ptr %= chip->cap_size;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	fm801_t *chip = snd_magic_cast(fm801_t, dev_id, return);
+	unsigned short status;
+	unsigned int tmp;
+
+	status = inw(FM801_REG(chip, IRQ_STATUS));
+	if ((status & (FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME)) == 0)
+		return;
+	if (chip->pcm && (status & FM801_IRQ_PLAYBACK)) {
+		spin_lock(&chip->reg_lock);
+		chip->ply_buf++;
+		chip->ply_pos += chip->ply_count;
+		chip->ply_pos %= chip->ply_size;
+		tmp = chip->ply_pos + chip->ply_count;
+		tmp %= chip->ply_size;
+		outl(chip->ply_buffer + tmp,
+				(chip->ply_buf & 1) ?
+					FM801_REG(chip, PLY_BUF1) :
+					FM801_REG(chip, PLY_BUF2));
+		outw(FM801_IRQ_PLAYBACK, FM801_REG(chip, IRQ_STATUS));
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(chip->playback_substream);
+	}
+	if (chip->pcm && (status & FM801_IRQ_CAPTURE)) {
+		spin_lock(&chip->reg_lock);
+		chip->cap_buf++;
+		chip->cap_pos += chip->cap_count;
+		chip->cap_pos %= chip->cap_size;
+		tmp = chip->cap_pos + chip->cap_count;
+		tmp %= chip->cap_size;
+		outl(chip->cap_buffer + tmp,
+				(chip->cap_buf & 1) ?
+					FM801_REG(chip, CAP_BUF1) :
+					FM801_REG(chip, CAP_BUF2));
+		outw(FM801_IRQ_CAPTURE, FM801_REG(chip, IRQ_STATUS));
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(chip->capture_substream);
+	}
+	if ((status & FM801_IRQ_MPU) && chip->rmidi != NULL) {
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+		outw(FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+	}
+	if (status & FM801_IRQ_VOLUME) {
+		/* TODO */
+		outw(FM801_IRQ_VOLUME, FM801_REG(chip, IRQ_STATUS));
+	}
+}
+
+static snd_pcm_hardware_t snd_fm801_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5500,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_fm801_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5500,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_fm801_playback_open(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback_substream = substream;
+	runtime->hw = snd_fm801_playback;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	if (chip->multichannel) {
+		runtime->hw.channels_max = 6;
+		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+	}
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_fm801_capture_open(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture_substream = substream;
+	runtime->hw = snd_fm801_capture;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_fm801_playback_close(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	return 0;
+}
+
+static int snd_fm801_capture_close(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_fm801_playback_ops = {
+	open:		snd_fm801_playback_open,
+	close:		snd_fm801_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_fm801_hw_params,
+	hw_free:	snd_fm801_hw_free,
+	prepare:	snd_fm801_playback_prepare,
+	trigger:	snd_fm801_playback_trigger,
+	pointer:	snd_fm801_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_fm801_capture_ops = {
+	open:		snd_fm801_capture_open,
+	close:		snd_fm801_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_fm801_hw_params,
+	hw_free:	snd_fm801_hw_free,
+	prepare:	snd_fm801_capture_prepare,
+	trigger:	snd_fm801_capture_trigger,
+	pointer:	snd_fm801_capture_pointer,
+};
+
+static void snd_fm801_pcm_free(snd_pcm_t *pcm)
+{
+	fm801_t *chip = snd_magic_cast(fm801_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_fm801_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "FM801");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer routines
+ */
+
+#define FM801_SINGLE(xname, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_single, \
+  get: snd_fm801_get_single, put: snd_fm801_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val;
+
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);
+}
+
+#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_double, \
+  get: snd_fm801_get_double, put: snd_fm801_put_double, \
+  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+
+static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+        int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	spin_lock(&chip->reg_lock);
+	ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
+	spin_unlock(&chip->reg_lock);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val1, val2;
+ 
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	return snd_fm801_update_bits(chip, reg,
+				     (mask << shift_left) | (mask << shift_right),
+				     (val1 << shift_left ) | (val2 << shift_right));
+}
+
+static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[5] = {
+		"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
+	};
+ 
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item > 4)
+		uinfo->value.enumerated.item = 4;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+        unsigned short val;
+ 
+	val = inw(FM801_REG(chip, REC_SRC)) & 7;
+	if (val > 4)
+		val = 4;
+        ucontrol->value.enumerated.item[0] = val;
+        return 0;
+}
+
+static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+        unsigned short val;
+ 
+        if ((val = ucontrol->value.enumerated.item[0]) > 4)
+                return -EINVAL;
+	return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
+}
+
+#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = {
+FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1),
+FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
+FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1),
+FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
+FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),
+FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Digital Capture Source",
+	info: snd_fm801_info_mux,
+	get: snd_fm801_get_mux,
+	put: snd_fm801_put_mux,
+}
+};
+
+#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = {
+FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
+FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
+FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0),
+FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0),
+FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0),
+};
+
+static void snd_fm801_mixer_free_ac97(ac97_t *ac97)
+{
+	fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return);
+	if (ac97->num == 0) {
+		chip->ac97 = NULL;
+	} else {
+		chip->ac97_sec = NULL;
+	}
+}
+
+static int __init snd_fm801_mixer(fm801_t *chip)
+{
+	ac97_t ac97;
+	int err, i;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_fm801_codec_write;
+	ac97.read = snd_fm801_codec_read;
+	ac97.private_data = chip;
+	ac97.private_free = snd_fm801_mixer_free_ac97;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+	if (chip->secondary) {
+		ac97.num = 1;
+		ac97.addr = chip->secondary_addr;
+		if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97_sec)) < 0)
+			return err;
+	}
+	for (i = 0; i < FM801_CONTROLS; i++)
+		snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
+	if (chip->multichannel) {
+		for (i = 0; i < FM801_CONTROLS_MULTI; i++)
+			snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+	}
+	return 0;
+}
+
+/*
+ *  initialization routines
+ */
+
+static int snd_fm801_free(fm801_t *chip)
+{
+	unsigned short cmdw;
+
+	if (chip->irq < 0)
+		goto __end_hw;
+
+	/* interrupt setup - mask everything */
+	cmdw = inw(FM801_REG(chip, IRQ_MASK));
+	cmdw |= 0x00c3;
+	outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+      __end_hw:
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_fm801_dev_free(snd_device_t *device)
+{
+	fm801_t *chip = snd_magic_cast(fm801_t, device->device_data, return -ENXIO);
+	return snd_fm801_free(chip);
+}
+
+static int __devinit snd_fm801_create(snd_card_t * card,
+				   struct pci_dev * pci,
+				   fm801_t ** rchip)
+{
+	fm801_t *chip;
+	unsigned char rev, id;
+	unsigned short cmdw;
+	signed long timeout;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_fm801_dev_free,
+	};
+	
+	*rchip = NULL;
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	chip = snd_magic_kcalloc(fm801_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 0x80, "FM801")) == NULL) {
+		snd_fm801_free(chip);
+		snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->port, chip->port + 0x80 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) {
+		snd_fm801_free(chip);
+		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	pci_set_master(pci);
+
+	pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
+	if (rev >= 0xb1)	/* FM801-AU */
+		chip->multichannel = 1;
+
+	/* codec cold reset + AC'97 warm reset */
+	outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL));
+	udelay(100);
+	outw(0, FM801_REG(chip, CODEC_CTRL));
+
+	timeout = (jiffies + (3 * HZ) / 4) + 1;		/* min 750ms */
+
+	outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+	udelay(5);
+	do {
+		if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
+			goto __ac97_secondary;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while ((timeout - (signed long)jiffies) > 0);
+	snd_printk("Primary AC'97 codec not found\n");
+	snd_fm801_free(chip);
+	return -EIO;
+
+      __ac97_secondary:
+      	if (!chip->multichannel)	/* lookup is not required */
+      		goto __ac97_ok;
+	for (id = 3; id > 0; id--) {	/* my card has the secondary codec */
+					/* at address #3, so the loop is inverted */
+
+		if ((timeout - (signed long)jiffies) < HZ / 20)
+			timeout = jiffies + HZ / 20;
+
+		outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD));
+		udelay(5);
+		do {
+			if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) {
+				cmdw = inw(FM801_REG(chip, AC97_DATA));
+				if (cmdw != 0xffff && cmdw != 0) {
+					chip->secondary = 1;
+					chip->secondary_addr = id;
+					goto __ac97_ok;
+				}
+			}
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		} while ((timeout - (signed long)jiffies) > 0);
+	}
+
+	/* the recovery phase, it seems that probing for non-existing codec might */
+	/* cause timeout problems */
+	timeout = (jiffies + (3 * HZ) / 4) + 1;		/* min 750ms */
+
+	outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+	udelay(5);
+	do {
+		if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
+			goto __ac97_ok;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while ((timeout - (signed long)jiffies) > 0);
+	snd_printk("Primary AC'97 codec not responding\n");
+	snd_fm801_free(chip);
+	return -EIO;
+
+      __ac97_ok:
+
+	/* init volume */
+	outw(0x0808, FM801_REG(chip, PCM_VOL));
+	outw(0x9f1f, FM801_REG(chip, FM_VOL));
+	outw(0x8808, FM801_REG(chip, I2S_VOL));
+
+	/* I2S control - I2S mode */
+	outw(0x0003, FM801_REG(chip, I2S_MODE));
+
+	/* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */
+	cmdw = inw(FM801_REG(chip, IRQ_MASK));
+	cmdw &= ~0x0083;
+	outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+	/* interrupt clear */
+	outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_fm801_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
+					  const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	fm801_t *chip;
+	opl3_t *opl3;
+	int err;
+
+        if (dev >= SNDRV_CARDS)
+                return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if ((err = snd_fm801_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_fm801_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
+				       FM801_REG(chip, MPU401_DATA), 1,
+				       chip->irq, 0, &chip->rmidi)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
+				   FM801_REG(chip, OPL3_BANK1),
+				   OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+ 
+	strcpy(card->driver, "FM801");
+	strcpy(card->shortname, "ForteMedia FM801-");
+	strcat(card->shortname, chip->multichannel ? "AU" : "AS");
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_card_fm801_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "FM801",
+	id_table: snd_fm801_ids,
+	probe: snd_card_fm801_probe,
+	remove: __devexit_p(snd_card_fm801_remove),
+};
+                                                                
+static int __init alsa_card_fm801_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "ForteMedia FM801 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_fm801_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_fm801_init)
+module_exit(alsa_card_fm801_exit)
+
+#ifndef MODULE
+
+/* format is: snd-fm801=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_fm801_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-fm801=", alsa_card_fm801_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/ice1712.c linux-2.4.19-pre5-mjc/sound/pci/ice1712.c
--- linux/sound/pci/ice1712.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ice1712.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,4366 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */      
+
+/*
+  NOTES:
+  - spdif nonaudio consumer mode does not work (at least with my
+    Sony STR-DB830)
+*/
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/i2c.h>
+#include <sound/cs8427.h>
+#include <sound/info.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#include <sound/asoundef.h>
+
+#define SND_CS8403
+#define SND_CS8404
+#include <sound/cs8403.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Hoontech SoundTrack DSP 24},"
+		"{MidiMan M Audio,Delta 1010},"
+		"{MidiMan M Audio,Delta DiO 2496},"
+		"{MidiMan M Audio,Delta 66},"
+		"{MidiMan M Audio,Delta 44},"
+		"{MidiMan M Audio,Audiophile 24/96},"
+		"{TerraTec,EWX 24/96},"
+		"{TerraTec,EWS 88MT},"
+		"{TerraTec,EWS 88D},"
+		"{TerraTec,DMX 6Fire},"
+		"{ICEnsemble,Generic ICE1712},"
+		"{ICEnsemble,Generic Envy24}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;		/* Enable this card */
+static int snd_omni[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};	/* Delta44 & 66 Omni I/O support */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ICE1712 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for ICE1712 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ICE1712 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_omni, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_omni, "Enable Midiman M-Audio Delta Omni I/O support.");
+MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+
+#ifndef PCI_VENDOR_ID_ICE
+#define PCI_VENDOR_ID_ICE		0x1412
+#endif
+#ifndef PCI_DEVICE_ID_ICE_1712
+#define PCI_DEVICE_ID_ICE_1712		0x1712
+#endif
+
+#define ICE1712_SUBDEVICE_STDSP24	0x12141217	/* Hoontech SoundTrack Audio DSP 24 */
+#define ICE1712_SUBDEVICE_DELTA1010	0x121430d6
+#define ICE1712_SUBDEVICE_DELTADIO2496	0x121431d6
+#define ICE1712_SUBDEVICE_DELTA66	0x121432d6
+#define ICE1712_SUBDEVICE_DELTA44	0x121433d6
+#define ICE1712_SUBDEVICE_AUDIOPHILE	0x121434d6
+#define ICE1712_SUBDEVICE_EWX2496	0x3b153011
+#define ICE1712_SUBDEVICE_EWS88MT	0x3b151511
+#define ICE1712_SUBDEVICE_EWS88D	0x3b152b11
+#define ICE1712_SUBDEVICE_DMX6FIRE	0x3b153811
+
+/*
+ *  Direct registers
+ */
+
+#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
+
+#define ICE1712_REG_CONTROL		0x00	/* byte */
+#define   ICE1712_RESET			0x80	/* reset whole chip */
+#define   ICE1712_SERR_LEVEL		0x04	/* SERR# level otherwise edge */
+#define   ICE1712_NATIVE		0x01	/* native mode otherwise SB */
+#define ICE1712_REG_IRQMASK		0x01	/* byte */
+#define   ICE1712_IRQ_MPU1		0x80
+#define   ICE1712_IRQ_TIMER		0x40
+#define   ICE1712_IRQ_MPU2		0x20
+#define   ICE1712_IRQ_PROPCM		0x10
+#define   ICE1712_IRQ_FM		0x08	/* FM/MIDI - legacy */
+#define   ICE1712_IRQ_PBKDS		0x04	/* playback DS channels */
+#define   ICE1712_IRQ_CONCAP		0x02	/* consumer capture */
+#define   ICE1712_IRQ_CONPBK		0x01	/* consumer playback */
+#define ICE1712_REG_IRQSTAT		0x02	/* byte */
+/* look to ICE1712_IRQ_* */
+#define ICE1712_REG_INDEX		0x03	/* byte - indirect CCIxx regs */
+#define ICE1712_REG_DATA		0x04	/* byte - indirect CCIxx regs */
+#define ICE1712_REG_NMI_STAT1		0x05	/* byte */
+#define ICE1712_REG_NMI_DATA		0x06	/* byte */
+#define ICE1712_REG_NMI_INDEX		0x07	/* byte */
+#define ICE1712_REG_AC97_INDEX		0x08	/* byte */
+#define ICE1712_REG_AC97_CMD		0x09	/* byte */
+#define   ICE1712_AC97_COLD		0x80	/* cold reset */
+#define   ICE1712_AC97_WARM		0x40	/* warm reset */
+#define   ICE1712_AC97_WRITE		0x20	/* W: write, R: write in progress */
+#define   ICE1712_AC97_READ		0x10	/* W: read, R: read in progress */
+#define   ICE1712_AC97_READY		0x08	/* codec ready status bit */
+#define   ICE1712_AC97_PBK_VSR		0x02	/* playback VSR */
+#define   ICE1712_AC97_CAP_VSR		0x01	/* capture VSR */
+#define ICE1712_REG_AC97_DATA		0x0a	/* word (little endian) */
+#define ICE1712_REG_MPU1_CTRL		0x0c	/* byte */
+#define ICE1712_REG_MPU1_DATA		0x0d	/* byte */
+#define ICE1712_REG_I2C_DEV_ADDR	0x10	/* byte */
+#define   ICE1712_I2C_WRITE		0x01	/* write direction */
+#define ICE1712_REG_I2C_BYTE_ADDR	0x11	/* byte */
+#define ICE1712_REG_I2C_DATA		0x12	/* byte */
+#define ICE1712_REG_I2C_CTRL		0x13	/* byte */
+#define   ICE1712_I2C_EEPROM		0x80	/* EEPROM exists */
+#define   ICE1712_I2C_BUSY		0x01	/* busy bit */
+#define ICE1712_REG_CONCAP_ADDR		0x14	/* dword - consumer capture */
+#define ICE1712_REG_CONCAP_COUNT	0x18	/* word - current/base count */
+#define ICE1712_REG_SERR_SHADOW		0x1b	/* byte */
+#define ICE1712_REG_MPU2_CTRL		0x1c	/* byte */
+#define ICE1712_REG_MPU2_DATA		0x1d	/* byte */
+#define ICE1712_REG_TIMER		0x1e	/* word */
+
+/*
+ *  Indirect registers
+ */
+
+#define ICE1712_IREG_PBK_COUNT_HI	0x00
+#define ICE1712_IREG_PBK_COUNT_LO	0x01
+#define ICE1712_IREG_PBK_CTRL		0x02
+#define ICE1712_IREG_PBK_LEFT		0x03	/* left volume */
+#define ICE1712_IREG_PBK_RIGHT		0x04	/* right volume */
+#define ICE1712_IREG_PBK_SOFT		0x05	/* soft volume */
+#define ICE1712_IREG_PBK_RATE_LO	0x06
+#define ICE1712_IREG_PBK_RATE_MID	0x07
+#define ICE1712_IREG_PBK_RATE_HI	0x08
+#define ICE1712_IREG_CAP_COUNT_HI	0x10
+#define ICE1712_IREG_CAP_COUNT_LO	0x11
+#define ICE1712_IREG_CAP_CTRL		0x12
+#define ICE1712_IREG_GPIO_DATA		0x20
+#define ICE1712_IREG_GPIO_WRITE_MASK	0x21
+#define ICE1712_IREG_GPIO_DIRECTION	0x22
+#define ICE1712_IREG_CONSUMER_POWERDOWN	0x30
+#define ICE1712_IREG_PRO_POWERDOWN	0x31
+
+/*
+ *  Consumer section direct DMA registers
+ */
+
+#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
+ 
+#define ICE1712_DS_INTMASK		0x00	/* word - interrupt mask */
+#define ICE1712_DS_INTSTAT		0x02	/* word - interrupt status */
+#define ICE1712_DS_DATA			0x04	/* dword - channel data */
+#define ICE1712_DS_INDEX		0x08	/* dword - channel index */
+
+/*
+ *  Consumer section channel registers
+ */
+ 
+#define ICE1712_DSC_ADDR0		0x00	/* dword - base address 0 */
+#define ICE1712_DSC_COUNT0		0x01	/* word - count 0 */
+#define ICE1712_DSC_ADDR1		0x02	/* dword - base address 1 */
+#define ICE1712_DSC_COUNT1		0x03	/* word - count 1 */
+#define ICE1712_DSC_CONTROL		0x04	/* byte - control & status */
+#define   ICE1712_BUFFER1		0x80	/* buffer1 is active */
+#define   ICE1712_BUFFER1_AUTO		0x40	/* buffer1 auto init */
+#define   ICE1712_BUFFER0_AUTO		0x20	/* buffer0 auto init */
+#define   ICE1712_FLUSH			0x10	/* flush FIFO */
+#define   ICE1712_STEREO		0x08	/* stereo */
+#define   ICE1712_16BIT			0x04	/* 16-bit data */
+#define   ICE1712_PAUSE			0x02	/* pause */
+#define   ICE1712_START			0x01	/* start */
+#define ICE1712_DSC_RATE		0x05	/* dword - rate */
+#define ICE1712_DSC_VOLUME		0x06	/* word - volume control */
+
+/* 
+ *  Professional multi-track direct control registers
+ */
+
+#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x)
+
+#define ICE1712_MT_IRQ			0x00	/* byte - interrupt mask */
+#define   ICE1712_MULTI_CAPTURE		0x80	/* capture IRQ */
+#define   ICE1712_MULTI_PLAYBACK	0x40	/* playback IRQ */
+#define   ICE1712_MULTI_CAPSTATUS	0x02	/* capture IRQ status */
+#define   ICE1712_MULTI_PBKSTATUS	0x01	/* playback IRQ status */
+#define ICE1712_MT_RATE			0x01	/* byte - sampling rate select */
+#define   ICE1712_SPDIF_MASTER		0x10	/* S/PDIF input is master clock */
+#define ICE1712_MT_I2S_FORMAT		0x02	/* byte - I2S data format */
+#define ICE1712_MT_AC97_INDEX		0x04	/* byte - AC'97 index */
+#define ICE1712_MT_AC97_CMD		0x05	/* byte - AC'97 command & status */
+/* look to ICE1712_AC97_* */
+#define ICE1712_MT_AC97_DATA		0x06	/* word - AC'97 data */
+#define ICE1712_MT_PLAYBACK_ADDR	0x10	/* dword - playback address */
+#define ICE1712_MT_PLAYBACK_SIZE	0x14	/* word - playback size */
+#define ICE1712_MT_PLAYBACK_COUNT	0x16	/* word - playback count */
+#define ICE1712_MT_PLAYBACK_CONTROL	0x18	/* byte - control */
+#define   ICE1712_CAPTURE_START_SHADOW	0x04	/* capture start */
+#define   ICE1712_PLAYBACK_PAUSE	0x02	/* playback pause */
+#define   ICE1712_PLAYBACK_START	0x01	/* playback start */
+#define ICE1712_MT_CAPTURE_ADDR		0x20	/* dword - capture address */
+#define ICE1712_MT_CAPTURE_SIZE		0x24	/* word - capture size */
+#define ICE1712_MT_CAPTURE_COUNT	0x26	/* word - capture count */
+#define ICE1712_MT_CAPTURE_CONTROL	0x28	/* byte - control */
+#define   ICE1712_CAPTURE_START		0x01	/* capture start */
+#define ICE1712_MT_ROUTE_PSDOUT03	0x30	/* word */
+#define ICE1712_MT_ROUTE_SPDOUT		0x32	/* word */
+#define ICE1712_MT_ROUTE_CAPTURE	0x34	/* dword */
+#define ICE1712_MT_MONITOR_VOLUME	0x38	/* word */
+#define ICE1712_MT_MONITOR_INDEX	0x3a	/* byte */
+#define ICE1712_MT_MONITOR_RATE		0x3b	/* byte */
+#define ICE1712_MT_MONITOR_ROUTECTRL	0x3c	/* byte */
+#define   ICE1712_ROUTE_AC97		0x01	/* route digital mixer output to AC'97 */
+#define ICE1712_MT_MONITOR_PEAKINDEX	0x3e	/* byte */
+#define ICE1712_MT_MONITOR_PEAKDATA	0x3f	/* byte */
+
+/*
+ *  Codec configuration bits
+ */
+
+/* PCI[60] System Configuration */
+#define ICE1712_CFG_CLOCK	0xc0
+#define   ICE1712_CFG_CLOCK512	0x00	/* 22.5692Mhz, 44.1kHz*512 */
+#define   ICE1712_CFG_CLOCK384  0x40	/* 16.9344Mhz, 44.1kHz*384 */
+#define   ICE1712_CFG_EXT	0x80	/* external clock */
+#define ICE1712_CFG_2xMPU401	0x20	/* two MPU401 UARTs */
+#define ICE1712_CFG_NO_CON_AC97 0x10	/* consumer AC'97 codec is not present */
+#define ICE1712_CFG_ADC_MASK	0x0c	/* one, two, three, four stereo ADCs */
+#define ICE1712_CFG_DAC_MASK	0x03	/* one, two, three, four stereo DACs */
+/* PCI[61] AC-Link Configuration */
+#define ICE1712_CFG_PRO_I2S	0x80	/* multitrack converter: I2S or AC'97 */
+#define ICE1712_CFG_AC97_PACKED	0x01	/* split or packed mode - AC'97 */
+/* PCI[62] I2S Features */
+#define ICE1712_CFG_I2S_VOLUME	0x80	/* volume/mute capability */
+#define ICE1712_CFG_I2S_96KHZ	0x40	/* supports 96kHz sampling */
+#define ICE1712_CFG_I2S_RESMASK	0x30	/* resolution mask, 16,18,20,24-bit */
+#define ICE1712_CFG_I2S_OTHER	0x0f	/* other I2S IDs */
+/* PCI[63] S/PDIF Configuration */
+#define ICE1712_CFG_I2S_CHIPID	0xfc	/* I2S chip ID */
+#define ICE1712_CFG_SPDIF_IN	0x02	/* S/PDIF input is present */
+#define ICE1712_CFG_SPDIF_OUT	0x01	/* S/PDIF output is present */
+
+/*
+ *  MidiMan M-Audio Delta GPIO definitions
+ */
+
+/* MidiMan M-Audio Delta1010 */
+#define ICE1712_DELTA_DFS 0x01		/* fast/slow sample rate mode */
+					/* (>48kHz must be 1) */
+#define ICE1712_DELTA_SPDIF_IN_STAT 0x02
+					/* S/PDIF input status */
+					/* 0 = valid signal is present */
+					/* all except Delta44 */
+					/* look to CS8414 datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04
+					/* S/PDIF output status clock */
+					/* (writting on rising edge - 0->1) */
+					/* all except Delta44 */
+					/* look to CS8404A datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08
+					/* S/PDIF output status data */
+					/* all except Delta44 */
+					/* look to CS8404A datasheet */
+/* MidiMan M-Audio DeltaDiO */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10
+					/* coaxial (0), optical (1) */
+					/* S/PDIF input select*/
+
+/* MidiMan M-Audio Delta1010 */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10
+					/* 1 - clock are taken from S/PDIF input */
+					/* 0 - clock are taken from Word Clock input */
+					/* affected SPMCLKIN pin of Envy24 */
+#define ICE1712_DELTA_WORD_CLOCK_STATUS	0x20
+					/* 0 = valid word clock signal is present */
+
+/* MidiMan M-Audio Delta66 */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10
+					/* AKM4524 serial data */
+#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20
+					/* AKM4524 serial clock */
+					/* (writting on rising edge - 0->1 */
+#define ICE1712_DELTA_CODEC_CHIP_A	0x40
+#define ICE1712_DELTA_CODEC_CHIP_B	0x80
+					/* 1 - select chip A or B */
+
+/* MidiMan M-Audio Delta44 */
+/* 0x01 = DFS */
+/* 0x10 = CODEC_SERIAL_DATA */
+/* 0x20 = CODEC_SERIAL_CLOCK */
+/* 0x40 = CODEC_CHIP_A */
+/* 0x80 = CODEC_CHIP_B */
+
+/* MidiMan M-Audio Audiophile definitions */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_AP_CCLK	0x02	/* SPI clock */
+					/* (clocking on rising edge - 0->1) */
+#define ICE1712_DELTA_AP_DIN	0x04	/* data input */
+#define ICE1712_DELTA_AP_DOUT	0x08	/* data output */
+#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */
+					/* low signal = select */
+#define ICE1712_DELTA_AP_CS_CODEC 0x20	/* AK4528 chip select */
+					/* low signal = select */
+
+/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
+
+#define ICE1712_STDSP24_0_BOX(r, x)	r[0] = ((r[0] & ~3) | ((x)&3))
+#define ICE1712_STDSP24_0_DAREAR(r, x)	r[0] = ((r[0] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_1_CHN1(r, x)	r[1] = ((r[1] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_1_CHN2(r, x)	r[1] = ((r[1] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_1_CHN3(r, x)	r[1] = ((r[1] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_2_CHN4(r, x)	r[2] = ((r[2] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_2_MIDIIN(r, x)	r[2] = ((r[2] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_2_MIDI1(r, x)	r[2] = ((r[2] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_3_MIDI2(r, x)	r[3] = ((r[3] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_3_MUTE(r, x)	r[3] = ((r[3] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_3_INSEL(r, x)	r[3] = ((r[3] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_SET_ADDR(r, a)	r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3))
+#define ICE1712_STDSP24_CLOCK(r, a, c)	r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5))
+#define ICE1712_STDSP24_CLOCK_BIT	(1<<5)
+
+/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */
+
+#define ICE1712_STDSP24_DAREAR		(1<<0)
+#define ICE1712_STDSP24_MUTE		(1<<1)
+#define ICE1712_STDSP24_INSEL		(1<<2)
+
+#define ICE1712_STDSP24_BOX_CHN1	(1<<0)	/* input channel 1 */
+#define ICE1712_STDSP24_BOX_CHN2	(1<<1)	/* input channel 2 */
+#define ICE1712_STDSP24_BOX_CHN3	(1<<2)	/* input channel 3 */
+#define ICE1712_STDSP24_BOX_CHN4	(1<<3)	/* input channel 4 */
+#define ICE1712_STDSP24_BOX_MIDI1	(1<<8)
+#define ICE1712_STDSP24_BOX_MIDI2	(1<<9)
+
+/* TerraTec EWX 24/96 configuration definitions */
+
+#define ICE1712_EWX2496_AK4524_CS	0x01	/* AK4524 chip select; low = active */
+#define ICE1712_EWX2496_AIN_SEL		0x02	/* input sensitivity switch; high = louder */
+#define ICE1712_EWX2496_AOUT_SEL	0x04	/* output sensitivity switch; high = louder */
+#define ICE1712_EWX2496_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_EWX2496_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_EWX2496_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_EWX2496_TX2		0x40	/* MIDI2 (not used) */
+#define ICE1712_EWX2496_RX2		0x80	/* MIDI2 (not used) */
+
+/* TerraTec EWS 88MT/D configuration definitions */
+/* RW, SDA snd SCLK are identical with EWX24/96 */
+#define ICE1712_EWS88_CS8414_RATE	0x07	/* CS8414 sample rate: gpio 0-2 */
+#define ICE1712_EWS88_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_EWS88_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_EWS88_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_EWS88_TX2		0x40	/* MIDI2 (only on 88D) */
+#define ICE1712_EWS88_RX2		0x80	/* MIDI2 (only on 88D) */
+
+/* i2c address */
+#define ICE1712_EWS88MT_CS8404_ADDR	(0x40>>1)
+#define ICE1712_EWS88MT_INPUT_ADDR	(0x46>>1)
+#define ICE1712_EWS88MT_OUTPUT_ADDR	(0x48>>1)
+#define ICE1712_EWS88MT_OUTPUT_SENSE	0x40	/* mask */
+#define ICE1712_EWS88D_PCF_ADDR		(0x40>>1)
+
+/* TerraTec DMX 6Fire configuration definitions */
+#define ICE1712_6FIRE_AK4524_CS_MASK	0x07	/* AK4524 chip select #1-#3 */
+#define ICE1712_6FIRE_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_6FIRE_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_6FIRE_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_6FIRE_TX2		0x40	/* MIDI2 */
+#define ICE1712_6FIRE_RX2		0x80	/* MIDI2 */
+
+#define ICE1712_6FIRE_CS8427_ADDR	(0x22>>1) /* ?? */
+
+/*
+ * DMA mode values
+ * identical with DMA_XXX on i386 architecture.
+ */
+#define ICE1712_DMA_MODE_WRITE		0x48
+#define ICE1712_DMA_AUTOINIT		0x10
+
+
+/*
+ *  
+ */
+
+typedef struct _snd_ice1712 ice1712_t;
+
+typedef struct {
+	unsigned int subvendor;	/* PCI[2c-2f] */
+	unsigned char size;	/* size of EEPROM image in bytes */
+	unsigned char version;	/* must be 1 */
+	unsigned char codec;	/* codec configuration PCI[60] */
+	unsigned char aclink;	/* ACLink configuration PCI[61] */
+	unsigned char i2sID;	/* PCI[62] */
+	unsigned char spdif;	/* S/PDIF configuration PCI[63] */
+	unsigned char gpiomask;	/* GPIO initial mask, 0 = write, 1 = don't */
+	unsigned char gpiostate; /* GPIO initial state */
+	unsigned char gpiodir;	/* GPIO direction state */
+	unsigned short ac97main;
+	unsigned short ac97pcm;
+	unsigned short ac97rec;
+	unsigned char ac97recsrc;
+	unsigned char dacID[4];	/* I2S IDs for DACs */
+	unsigned char adcID[4];	/* I2S IDs for ADCs */
+	unsigned char extra[4];
+} ice1712_eeprom_t;
+
+struct _snd_ice1712 {
+	unsigned long conp_dma_size;
+	unsigned long conc_dma_size;
+	unsigned long prop_dma_size;
+	unsigned long proc_dma_size;
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long ddma_port;
+	struct resource *res_ddma_port;
+	unsigned long dmapath_port;
+	struct resource *res_dmapath_port;
+	unsigned long profi_port;
+	struct resource *res_profi_port;
+
+	unsigned int config;	/* system configuration */
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm_ds;
+	snd_pcm_t *pcm_pro;
+        snd_pcm_substream_t *playback_con_substream;
+        snd_pcm_substream_t *playback_con_substream_ds[6];
+        snd_pcm_substream_t *capture_con_substream;
+        snd_pcm_substream_t *playback_pro_substream;
+        snd_pcm_substream_t *capture_pro_substream;
+	unsigned int playback_pro_size;
+	unsigned int capture_pro_size;
+	unsigned int playback_con_virt_addr[6];
+	unsigned int playback_con_active_buf[6];
+	unsigned int capture_con_virt_addr;
+	unsigned int ac97_ext_id;
+	ac97_t *ac97;
+	snd_rawmidi_t *rmidi[2];
+
+	spinlock_t reg_lock;
+	struct semaphore gpio_mutex;
+	snd_info_entry_t *proc_entry;
+
+	ice1712_eeprom_t eeprom;
+
+	unsigned int pro_volumes[20];
+	int ak4528: 1,			/* AK4524 or AK4528 */
+	    omni: 1;			/* Delta Omni I/O */
+	int num_adcs;			/* AK4524 or AK4528 ADCs */
+	int num_dacs;			/* AK4524 or AK4528 DACs */
+	int num_total_dacs;		/* total DACs */
+	unsigned char ak4524_images[4][8];
+	unsigned char ak4524_ipga_gain[4][2];
+	unsigned char hoontech_boxbits[4];
+	unsigned int hoontech_config;
+	unsigned short hoontech_boxconfig[4];
+
+	snd_i2c_bus_t *i2c;		/* I2C bus */
+	snd_i2c_device_t *cs8404;	/* CS8404A I2C device */
+	snd_i2c_device_t *cs8427;	/* CS8427 I2C device */
+	snd_i2c_device_t *pcf8574[2];	/* PCF8574 Output/Input (EWS88MT) */
+	snd_i2c_device_t *pcf8575;	/* PCF8575 (EWS88D) */
+	
+	unsigned char cs8403_spdif_bits;
+	unsigned char cs8403_spdif_stream_bits;
+	snd_kcontrol_t *spdif_stream_ctl;
+
+	unsigned char gpio_direction, gpio_write_mask;
+};
+
+#define chip_t ice1712_t
+
+static struct pci_device_id snd_ice1712_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ICE1712 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
+
+static int snd_ice1712_build_pro_mixer(ice1712_t *ice);
+static int snd_ice1712_build_controls(ice1712_t *ice);
+
+/*
+ *  Basic I/O
+ */
+ 
+static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data)
+{
+	outb(addr, ICEREG(ice, INDEX));
+	outb(data, ICEREG(ice, DATA));
+}
+
+static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr)
+{
+	outb(addr, ICEREG(ice, INDEX));
+	return inb(ICEREG(ice, DATA));
+}
+
+static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data)
+{
+	outb((channel << 4) | addr, ICEDS(ice, INDEX));
+	outl(data, ICEDS(ice, DATA));
+}
+
+static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr)
+{
+	outb((channel << 4) | addr, ICEDS(ice, INDEX));
+	return inl(ICEDS(ice, DATA));
+}
+
+static void snd_ice1712_ac97_write(ac97_t *ac97,
+				   unsigned short reg,
+				   unsigned short val)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEREG(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEREG(ice, AC97_INDEX));
+	outw(val, ICEREG(ice, AC97_DATA));
+	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+	outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+			break;
+}
+
+static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,
+					    unsigned short reg)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEREG(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEREG(ice, AC97_INDEX));
+	outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+			break;
+	if (tm >= 0x10000)		/* timeout */
+		return ~0;
+	return inw(ICEREG(ice, AC97_DATA));
+}
+
+/*
+ * pro ac97 section
+ */
+
+static void snd_ice1712_pro_ac97_write(ac97_t *ac97,
+				       unsigned short reg,
+				       unsigned short val)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEMT(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEMT(ice, AC97_INDEX));
+	outw(val, ICEMT(ice, AC97_DATA));
+	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+	outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+			break;
+}
+
+
+static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,
+						unsigned short reg)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEMT(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEMT(ice, AC97_INDEX));
+	outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+			break;
+	if (tm >= 0x10000)		/* timeout */
+		return ~0;
+	return inw(ICEMT(ice, AC97_DATA));
+}
+
+static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char val, nval;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
+	nval = val & ~ICE1712_ROUTE_AC97;
+	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Digital Mixer To AC97",
+	info: snd_ice1712_digmix_route_ac97_info,
+	get: snd_ice1712_digmix_route_ac97_get,
+	put: snd_ice1712_digmix_route_ac97_put,
+};
+
+
+/*
+ */
+
+static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+	unsigned char tmp, mask1, mask2;
+	int idx;
+	/* send byte to transmitter */
+	mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
+	mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
+	down(&ice->gpio_mutex);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~(mask1 | mask2);
+		if (bits & (1 << idx))
+			tmp |= mask2;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(100);
+		tmp |= mask1;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(100);
+	}
+	tmp &= ~mask1;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	up(&ice->gpio_mutex);
+}
+
+
+/*
+ * set gpio direction, write mask and data
+ */
+static void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits)
+{
+	ice->gpio_direction |= mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, mask & bits);
+}
+
+/*
+ */
+static void save_gpio_status(ice1712_t *ice, unsigned char *tmp)
+{
+	down(&ice->gpio_mutex);
+	tmp[0] = ice->gpio_direction;
+	tmp[1] = ice->gpio_write_mask;
+}
+
+static void restore_gpio_status(ice1712_t *ice, unsigned char *tmp)
+{
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, tmp[0]);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, tmp[1]);
+	ice->gpio_direction = tmp[0];
+	ice->gpio_write_mask = tmp[1];
+	up(&ice->gpio_mutex);
+}
+
+/*
+ * CS8427 via SPI mode (for Audiophile), emulated I2C
+ */
+
+/* send 8 bits */
+static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp)
+{
+	int idx;
+
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK);
+		if (data & (1 << idx))
+			tmp |= ICE1712_DELTA_AP_DOUT;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+		tmp |= ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+	}
+}
+
+/* read 8 bits */
+static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp)
+{
+	unsigned char data = 0;
+	int idx;
+	
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+		if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN)
+			data |= 1 << idx;
+		tmp |= ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+	}
+	return data;
+}
+
+/* assert chip select */
+static unsigned char ap_cs8427_codec_select(ice1712_t *ice)
+{
+	unsigned char tmp;
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
+	tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(5);
+	return tmp;
+}
+
+/* deassert chip select */
+static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp)
+{
+	tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+}
+
+/* sequential write */
+static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO);
+	int res = count;
+	unsigned char tmp;
+
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
+	while (count-- > 0)
+		ap_cs8427_write_byte(ice, *bytes++, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+	return res;
+}
+
+/* sequential read */
+static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO);
+	int res = count;
+	unsigned char tmp;
+	
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
+	while (count-- > 0)
+		*bytes++ = ap_cs8427_read_byte(ice, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+	return res;
+}
+
+static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
+{
+	if (addr == 0x10)
+		return 1;
+	return -ENOENT;
+}
+
+static snd_i2c_ops_t ap_cs8427_i2c_ops = {
+	sendbytes: ap_cs8427_sendbytes,
+	readbytes: ap_cs8427_readbytes,
+	probeaddr: ap_cs8427_probeaddr,
+};
+
+/*
+ * access via i2c mode (for EWX 24/96, EWS 88MT&D)
+ */
+
+/* send SDA and SCL */
+static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	unsigned char tmp = 0;
+	if (clk)
+		tmp |= ICE1712_EWX2496_SERIAL_CLOCK;
+	if (data)
+		tmp |= ICE1712_EWX2496_SERIAL_DATA;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(5);
+}
+
+static int ewx_i2c_getclock(snd_i2c_bus_t *bus)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO);
+	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0;
+}
+
+static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO);
+	int bit;
+	/* set RW pin to low */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0);
+	if (ack)
+		udelay(5);
+	bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0;
+	/* set RW pin to high */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW);
+	/* reset write mask */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK);
+	return bit;
+}
+
+static void ewx_i2c_start(snd_i2c_bus_t *bus)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	unsigned char mask;
+
+	save_gpio_status(ice, (unsigned char *)&bus->private_value);
+	/* set RW high */
+	mask = ICE1712_EWX2496_RW;
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */
+		break;
+	}
+	snd_ice1712_gpio_write_bits(ice, mask, mask);
+}
+
+static void ewx_i2c_stop(snd_i2c_bus_t *bus)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	restore_gpio_status(ice, (unsigned char *)&bus->private_value);
+}
+
+static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	unsigned char mask = 0;
+
+	if (clock)
+		mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */
+	if (data)
+		mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */
+	ice->gpio_direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA);
+	ice->gpio_direction |= mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
+}
+
+static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = {
+	start: ewx_i2c_start,
+	stop: ewx_i2c_stop,
+	direction: ewx_i2c_direction,
+	setlines: ewx_i2c_setlines,
+	getclock: ewx_i2c_getclock,
+	getdata: ewx_i2c_getdata,
+};
+
+/* AK4524 chip select; address 0x48 bit 0-3 */
+static void snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask)
+{
+	unsigned char data, ndata;
+
+	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return);
+	snd_i2c_lock(ice->i2c);
+	snd_runtime_check(snd_i2c_readbytes(ice->pcf8574[1], &data, 1) == 1, snd_i2c_unlock(ice->i2c); return);
+	ndata = (data & 0xf0) | chip_mask;
+	if (ndata != data)
+		snd_runtime_check(snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) == 1, snd_i2c_unlock(ice->i2c); return);
+	snd_i2c_unlock(ice->i2c);
+}
+
+/*
+ * write AK4524 register
+ */
+static void snd_ice1712_ak4524_write(ice1712_t *ice, int chip,
+				     unsigned char addr, unsigned char data)
+{
+	unsigned char tmp, data_mask, clk_mask, saved[2];
+	unsigned char codecs_mask;
+	int idx, cif;
+	unsigned int addrdata;
+
+	snd_assert(chip >= 0 && chip < 4, return);
+
+	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) {
+		/* assert AK4524 CS */
+		snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f);
+		//snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+	}
+
+	cif = 0; /* the default level of the CIF pin from AK4524 */
+	save_gpio_status(ice, saved);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		data_mask = ICE1712_DELTA_AP_DOUT;
+		clk_mask = ICE1712_DELTA_AP_CCLK;
+		codecs_mask = ICE1712_DELTA_AP_CS_CODEC; /* select AK4528 codec */
+		tmp |= ICE1712_DELTA_AP_CS_DIGITAL; /* assert digital high */
+		break;
+	case ICE1712_SUBDEVICE_EWX2496:
+		data_mask = ICE1712_EWX2496_SERIAL_DATA;
+		clk_mask = ICE1712_EWX2496_SERIAL_CLOCK;
+		codecs_mask = ICE1712_EWX2496_AK4524_CS;
+		tmp |= ICE1712_EWX2496_RW; /* set rw bit high */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+				  ice->gpio_direction | data_mask | clk_mask |
+				  codecs_mask | ICE1712_EWX2496_RW);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
+				  ~(data_mask | clk_mask |
+				    codecs_mask | ICE1712_EWX2496_RW));
+		cif = 1; /* CIF high */
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		data_mask = ICE1712_EWS88_SERIAL_DATA;
+		clk_mask = ICE1712_EWS88_SERIAL_CLOCK;
+		codecs_mask = 0; /* no chip select on gpio */
+		tmp |= ICE1712_EWS88_RW; /* set rw bit high */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+				  ice->gpio_direction | data_mask | clk_mask |
+				  codecs_mask | ICE1712_EWS88_RW);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
+				  ~(data_mask | clk_mask |
+				    codecs_mask | ICE1712_EWS88_RW));
+		cif = 1; /* CIF high */
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		data_mask = ICE1712_6FIRE_SERIAL_DATA;
+		clk_mask = ICE1712_6FIRE_SERIAL_CLOCK;
+		codecs_mask = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK;
+		tmp |= ICE1712_6FIRE_RW; /* set rw bit high */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+				  ice->gpio_direction | data_mask | clk_mask |
+				  codecs_mask | ICE1712_6FIRE_RW);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
+				  ~(data_mask | clk_mask |
+				    codecs_mask | ICE1712_6FIRE_RW));
+		cif = 1; /* CIF high */
+		break;
+	default:
+		data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA;
+		clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK;
+		codecs_mask = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : ICE1712_DELTA_CODEC_CHIP_B;
+		break;
+	}
+
+	if (cif) {
+		tmp |= codecs_mask; /* start without chip select */
+	} else {
+		tmp &= ~codecs_mask; /* chip select low */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	addr &= 0x07;
+	/* build I2C address + data byte */
+	addrdata = 0xa000 | (addr << 8) | data;
+	for (idx = 15; idx >= 0; idx--) {
+		tmp &= ~(data_mask|clk_mask);
+		if (addrdata & (1 << idx))
+			tmp |= data_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		//udelay(200);
+		udelay(1);
+		tmp |= clk_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) {
+		restore_gpio_status(ice, saved);
+		//snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f);
+		udelay(1);
+		snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+	} else {
+		if (cif) {
+			/* assert a cs pulse to trigger */
+			tmp &= ~codecs_mask;
+			snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+			udelay(1);
+		}
+		tmp |= codecs_mask; /* chip select high to trigger */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+		restore_gpio_status(ice, saved);
+	}
+
+	if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0)
+		ice->ak4524_images[chip][addr] = data;
+	else
+		ice->ak4524_ipga_gain[chip][addr-4] = data;
+}
+
+static void snd_ice1712_ak4524_reset(ice1712_t *ice, int state)
+{
+	int chip;
+	unsigned char reg;
+	
+	for (chip = 0; chip < ice->num_dacs/2; chip++) {
+		snd_ice1712_ak4524_write(ice, chip, 0x01, state ? 0x00 : 0x03);
+		if (state)
+			continue;
+		for (reg = 0x04; reg < (ice->ak4528 ? 0x06 : 0x08); reg++)
+			snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_images[chip][reg]);
+		if (ice->ak4528)
+			continue;
+		for (reg = 0x04; reg < 0x06; reg++)
+			snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_ipga_gain[chip][reg-4]);
+	}
+}
+
+static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+	unsigned char bytes[2];
+
+	snd_i2c_lock(ice->i2c);
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWS88MT:
+		snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, snd_i2c_unlock(ice->i2c); return);
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return);
+		if (bits != bytes[1]) {
+			bytes[1] = bits;
+			snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return);
+		}
+		break;
+	}
+	snd_i2c_unlock(ice->i2c);
+}
+
+
+/*
+ * change the input clock selection
+ * spdif_clock = 1 - IEC958 input, 0 - Envy24
+ */
+static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock)
+{
+	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */
+	unsigned char val, nval;
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	nval = val & 0xf0;
+	if (spdif_clock)
+		nval |= 0x01;
+	else
+		nval |= 0x04;
+	if (val != nval) {
+		if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
+			snd_i2c_unlock(ice->i2c);
+			return -EREMOTE;
+		}
+		return 1;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return 0;
+}
+
+/*
+ */
+static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	unsigned int rate;
+	unsigned char val, tmp;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+						 ICE1712_PLAYBACK_PAUSE|
+						 ICE1712_PLAYBACK_START))
+		goto __end;
+	if (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)
+		goto __end;
+	rate = substream->runtime->rate;
+	switch (rate) {
+	case 8000: val = 6; break;
+	case 9600: val = 3; break;
+	case 11025: val = 10; break;
+	case 12000: val = 2; break;
+	case 16000: val = 5; break;
+	case 22050: val = 9; break;
+	case 24000: val = 1; break;
+	case 32000: val = 4; break;
+	case 44100: val = 8; break;
+	case 48000: val = 0; break;
+	case 64000: val = 15; break;
+	case 88200: val = 11; break;
+	case 96000: val = 7; break;
+	default:
+		snd_BUG();
+		val = 0;
+		break;
+	}
+	outb(val, ICEMT(ice, RATE));
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		snd_ice1712_ak4524_reset(ice, 1);
+		down(&ice->gpio_mutex);
+		tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+		if (val == 15 || val == 11 || val == 7) {
+			tmp |= ICE1712_DELTA_DFS;
+		} else {
+			tmp &= ~ICE1712_DELTA_DFS;
+		}
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		up(&ice->gpio_mutex);
+		snd_ice1712_ak4524_reset(ice, 0);
+		return;
+	}
+      __end:
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return);
+	unsigned char status;
+
+	while (1) {
+		status = inb(ICEREG(ice, IRQSTAT));
+		if (status == 0)
+			break;
+		if (status & ICE1712_IRQ_MPU1) {
+			if (ice->rmidi[0])
+				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
+			outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
+			status &= ~ICE1712_IRQ_MPU1;
+		}
+		if (status & ICE1712_IRQ_TIMER)
+			outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
+		if (status & ICE1712_IRQ_MPU2) {
+			if (ice->rmidi[1])
+				snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);
+			outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
+			status &= ~ICE1712_IRQ_MPU2;
+		}
+		if (status & ICE1712_IRQ_PROPCM) {
+			unsigned char mtstat = inb(ICEMT(ice, IRQ));
+			if (mtstat & ICE1712_MULTI_PBKSTATUS) {
+				if (ice->playback_pro_substream)
+					snd_pcm_period_elapsed(ice->playback_pro_substream);
+				outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
+			}
+			if (mtstat & ICE1712_MULTI_CAPSTATUS) {
+				if (ice->capture_pro_substream)
+					snd_pcm_period_elapsed(ice->capture_pro_substream);
+				outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
+			}
+		}
+		if (status & ICE1712_IRQ_FM)
+			outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
+		if (status & ICE1712_IRQ_PBKDS) {
+			u32 idx;
+			u16 pbkstatus;
+			snd_pcm_substream_t *substream;
+			pbkstatus = inw(ICEDS(ice, INTSTAT));
+			//printk("pbkstatus = 0x%x\n", pbkstatus);
+			for (idx = 0; idx < 6; idx++) {
+				if ((pbkstatus & (3 << (idx * 2))) == 0)
+					continue;
+				if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+					snd_pcm_period_elapsed(substream);
+				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
+			}
+			outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
+		}
+		if (status & ICE1712_IRQ_CONCAP) {
+			if (ice->capture_con_substream)
+				snd_pcm_period_elapsed(ice->capture_con_substream);
+			outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
+		}
+		if (status & ICE1712_IRQ_CONPBK) {
+			if (ice->playback_con_substream)
+				snd_pcm_period_elapsed(ice->playback_con_substream);
+			outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
+		}
+	}
+}
+
+/*
+ *  PCM part - misc
+ */
+
+static int snd_ice1712_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ *  PCM part - consumer I/O
+ */
+
+static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u32 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+		tmp |= 2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+		tmp &= ~2;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u32 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+		tmp |= 2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+		tmp &= ~2;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u8 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size, rate, tmp;
+
+	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x0000;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp |= 0x10;
+	if (runtime->channels == 2)
+		tmp |= 0x08;
+	rate = (runtime->rate * 8192) / 375;
+	if (rate > 0x000fffff)
+		rate = 0x000fffff;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outb(0, ice->ddma_port + 15);
+	outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
+	outl(runtime->dma_addr, ice->ddma_port + 0);
+	outw(buf_size, ice->ddma_port + 4);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size, rate, tmp, chn;
+
+	period_size = snd_pcm_lib_period_bytes(substream) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x0064;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp &= ~0x04;
+	if (runtime->channels == 2)
+		tmp |= 0x08;
+	rate = (runtime->rate * 8192) / 375;
+	if (rate > 0x000fffff)
+		rate = 0x000fffff;
+	ice->playback_con_active_buf[substream->number] = 0;
+	ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
+	chn = substream->number * 2;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
+	if (runtime->channels == 2) {
+		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
+		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size;
+	u8 tmp;
+
+	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x06;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp &= ~0x04;
+	if (runtime->channels == 2)
+		tmp &= ~0x02;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+	outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	size_t ptr;
+
+	if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
+		return 0;
+	ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	u8 addr;
+	size_t ptr;
+
+	if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
+		return 0;
+	if (ice->playback_con_active_buf[substream->number])
+		addr = ICE1712_DSC_ADDR1;
+	else
+		addr = ICE1712_DSC_ADDR0;
+	ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
+		ice->playback_con_virt_addr[substream->number];
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
+		return 0;
+	ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(64*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(64*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_playback_ds =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(64*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(64*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_ice1712_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_con_substream = substream;
+	runtime->hw = snd_ice1712_playback;
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	u32 tmp;
+
+	ice->playback_con_substream_ds[substream->number] = substream;
+	runtime->hw = snd_ice1712_playback_ds;
+	spin_lock_irqsave(&ice->reg_lock, flags); 
+	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
+	outw(tmp, ICEDS(ice, INTMASK));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_con_substream = substream;
+	runtime->hw = snd_ice1712_capture;
+	runtime->hw.rates = ice->ac97->rates_adc;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	return 0;
+}
+
+static int snd_ice1712_playback_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_con_substream = NULL;
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&ice->reg_lock, flags); 
+	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
+	outw(tmp, ICEDS(ice, INTMASK));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	ice->playback_con_substream_ds[substream->number] = NULL;
+	return 0;
+}
+
+static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	/* disable ADC power */
+	snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	ice->capture_con_substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_ops = {
+	open:		snd_ice1712_playback_open,
+	close:		snd_ice1712_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_playback_prepare,
+	trigger:	snd_ice1712_playback_trigger,
+	pointer:	snd_ice1712_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
+	open:		snd_ice1712_playback_ds_open,
+	close:		snd_ice1712_playback_ds_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_playback_ds_prepare,
+	trigger:	snd_ice1712_playback_ds_trigger,
+	pointer:	snd_ice1712_playback_ds_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_ops = {
+	open:		snd_ice1712_capture_open,
+	close:		snd_ice1712_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_capture_prepare,
+	trigger:	snd_ice1712_capture_trigger,
+	pointer:	snd_ice1712_capture_pointer,
+};
+
+static void snd_ice1712_pcm_free(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 consumer");
+	ice->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
+
+	return 0;
+}
+
+static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm_ds = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_free_ds;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 consumer (DS)");
+	ice->pcm_ds = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+/*
+ *  PCM code - professional part (multitrack)
+ */
+
+static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+				32000, 44100, 48000, 64000, 88200, 96000 };
+
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: RATES,
+	list: rates,
+	mask: 0,
+};
+
+#if 0
+
+static int snd_ice1712_playback_pro_ioctl(snd_pcm_substream_t * substream,
+					  unsigned int cmd,
+					  void *arg)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_DIG_INFO:
+	{
+		snd_pcm_dig_info_t *info = arg;
+		switch (ice->eeprom.subvendor) {
+		case ICE1712_SUBDEVICE_DELTA1010:
+		case ICE1712_SUBDEVICE_DELTADIO2496:
+		case ICE1712_SUBDEVICE_DELTA66:
+			return snd_ice1712_dig_cs8403_info(substream, info);
+		}
+	}
+	case SNDRV_PCM_IOCTL1_DIG_PARAMS:
+	{
+		snd_pcm_dig_params_t *params = arg;
+		switch (ice->eeprom.subvendor) {
+		case ICE1712_SUBDEVICE_DELTA1010:
+		case ICE1712_SUBDEVICE_DELTADIO2496:
+		case ICE1712_SUBDEVICE_DELTA66:
+			return snd_ice1712_dig_cs8403_params(substream, params);
+		}
+	}
+	default:
+		break;
+	}
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+#endif
+
+static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream,
+				   int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	{
+		unsigned int what;
+		unsigned int old;
+		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			return -EINVAL;
+		what = ICE1712_PLAYBACK_PAUSE;
+		snd_pcm_trigger_done(substream, substream);
+		spin_lock(&ice->reg_lock);
+		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+			old |= what;
+		else
+			old &= ~what;
+		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+		spin_unlock(&ice->reg_lock);
+		break;
+	}
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		unsigned int old;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == ice->playback_pro_substream) {
+				what |= ICE1712_PLAYBACK_START;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ice->capture_pro_substream) {
+				what |= ICE1712_CAPTURE_START_SHADOW;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&ice->reg_lock);
+		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			old |= what;
+		else
+			old &= ~what;
+		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+		spin_unlock(&ice->reg_lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned int tmp;
+	int change;
+
+	ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ice1712_set_pro_rate(ice, substream);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
+	outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
+	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		/* setup S/PDIF */
+		tmp = ice->cs8403_spdif_stream_bits;
+		if (tmp & 0x01)		/* consumer */
+			tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
+		switch (substream->runtime->rate) {
+		case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
+		case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
+		case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
+		default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+		}
+		change = ice->cs8403_spdif_stream_bits != tmp;
+		ice->cs8403_spdif_stream_bits = tmp;
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		if (change)
+			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
+		snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
+		return 0;
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		snd_cs8427_iec958_pcm(ice->cs8427, substream->runtime->rate);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		/* setup S/PDIF */
+		tmp = ice->cs8403_spdif_stream_bits;
+		if (tmp & 0x10)		/* consumer */
+			tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
+		switch (substream->runtime->rate) {
+		case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
+		case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+		case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
+		default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+		}
+		change = ice->cs8403_spdif_stream_bits != tmp;
+		ice->cs8403_spdif_stream_bits = tmp;
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		if (change)
+			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
+		snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
+		return 0;
+	}
+
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ice1712_set_pro_rate(ice, substream);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
+	outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
+	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
+		return 0;
+	ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
+		return 0;
+	ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback_pro =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+	rate_min:		4000,
+	rate_max:		96000,
+	channels_min:		10,
+	channels_max:		10,
+	buffer_bytes_max:	(256*1024),
+	period_bytes_min:	10 * 4 * 2,
+	period_bytes_max:	131040,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture_pro =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+	rate_min:		4000,
+	rate_max:		96000,
+	channels_min:		12,
+	channels_max:		12,
+	buffer_bytes_max:	(256*1024),
+	period_bytes_min:	12 * 4 * 2,
+	period_bytes_max:	131040,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_substream = substream;
+	runtime->hw = snd_ice1712_playback_pro;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+
+	ice->cs8403_spdif_stream_bits = ice->cs8403_spdif_bits;
+	if (ice->cs8427)
+		snd_cs8427_iec958_active(ice->cs8427, 1);
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	ice->capture_pro_substream = substream;
+	runtime->hw = snd_ice1712_capture_pro;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_substream = NULL;
+	if (ice->cs8427)
+		snd_cs8427_iec958_active(ice->cs8427, 0);
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_pro_substream = NULL;
+	return 0;
+}
+
+static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm_pro = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_pro_ops = {
+	open:		snd_ice1712_playback_pro_open,
+	close:		snd_ice1712_playback_pro_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_playback_pro_prepare,
+	trigger:	snd_ice1712_pro_trigger,
+	pointer:	snd_ice1712_playback_pro_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_pro_ops = {
+	open:		snd_ice1712_capture_pro_open,
+	close:		snd_ice1712_capture_pro_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_capture_pro_prepare,
+	trigger:	snd_ice1712_pro_trigger,
+	pointer:	snd_ice1712_capture_pro_pointer,
+};
+
+static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_profi_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 multi");
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
+
+	ice->pcm_pro = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	
+	if (ice->cs8427) {
+		/* assign channels to iec958 */
+		err = snd_cs8427_iec958_build(ice->cs8427,
+					      pcm->streams[0].substream,
+					      pcm->streams[1].substream);
+		if (err < 0)
+			return err;
+	}
+
+	if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *  Mixer section
+ */
+
+static void snd_ice1712_update_volume(ice1712_t *ice, int index)
+{
+	unsigned int vol = ice->pro_volumes[index];
+	unsigned short val = 0;
+
+	val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f;
+	val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8;
+	outb(index, ICEMT(ice, MONITOR_INDEX));
+	outw(val, ICEMT(ice, MONITOR_VOLUME));
+}
+
+static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int index = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
+	ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	unsigned int nval, change;
+
+	nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
+	       (ucontrol->value.integer.value[1] ? 0 : 0x80000000);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= ice->pro_volumes[index] & ~0x80008000;
+	change = nval != ice->pro_volumes[index];
+	ice->pro_volumes[index] = nval;
+	snd_ice1712_update_volume(ice, index);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 96;
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int index = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
+	ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	unsigned int nval, change;
+
+	nval = (ucontrol->value.integer.value[0] & 127) |
+	       ((ucontrol->value.integer.value[1] & 127) << 16);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= ice->pro_volumes[index] & ~0x007f007f;
+	change = nval != ice->pro_volumes[index];
+	ice->pro_volumes[index] = nval;
+	snd_ice1712_update_volume(ice, index);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static int snd_ice1712_ak4524_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	ucontrol->value.integer.value[0] = ice->ak4524_images[chip][addr];
+	return 0;
+}
+
+static int snd_ice1712_ak4524_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	unsigned char nval = ucontrol->value.integer.value[0];
+	int change = ice->ak4524_images[chip][addr] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, addr, nval);
+	return change;
+}
+
+static int snd_ice1712_ak4524_ipga_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 36;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	ucontrol->value.integer.value[0] = ice->ak4524_ipga_gain[chip][addr-4] & 0x7f;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
+	int change = ice->ak4524_ipga_gain[chip][addr] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, addr, nval);
+	return change;
+}
+
+static int snd_ice1712_ak4524_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[4] = {
+		"44.1kHz", "Off", "48kHz", "32kHz",
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= 4)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_ak4524_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->id.index;
+	ucontrol->value.enumerated.item[0] = ice->ak4524_images[chip][3] & 3;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->id.index;
+	unsigned char nval = ucontrol->value.enumerated.item[0];
+	int change;
+	nval |= (nval & 3) | (ice->ak4524_images[chip][3] & ~3);
+	change = ice->ak4524_images[chip][3] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, 3, nval);
+	return change;
+}
+
+static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice)
+{
+	snd_card_t * card = ice->card;
+	snd_kcontrol_t ctl;
+	int idx, err;
+
+	/* PCM playback */
+	for (idx = 0; idx < 10; idx++) {
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Playback Switch");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_switch_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_switch_get;
+		ctl.put = snd_ice1712_pro_mixer_switch_put;
+		ctl.private_value = idx;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Playback Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_volume_get;
+		ctl.put = snd_ice1712_pro_mixer_volume_put;
+		ctl.private_value = idx;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+
+	/* PCM capture */
+	for (idx = 0; idx < 10; idx++) {
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Capture Switch");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_switch_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_switch_get;
+		ctl.put = snd_ice1712_pro_mixer_switch_put;
+		ctl.private_value = idx + 10;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Capture Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_volume_get;
+		ctl.put = snd_ice1712_pro_mixer_volume_put;
+		ctl.private_value = idx + 10;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+	
+	/* initialize volumes */
+	for (idx = 0; idx < 20; idx++) {
+		ice->pro_volumes[idx] = 0x80008000;	/* mute */
+		snd_ice1712_update_volume(ice, idx);
+	}
+	return 0;
+}
+
+static void snd_ice1712_ac97_init(ac97_t *ac97)
+{
+	// ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
+
+        /* disable center DAC/surround DAC/LFE DAC/MIC ADC */
+        snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+}
+
+static void snd_ice1712_mixer_free_ac97(ac97_t *ac97)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
+	ice->ac97 = NULL;
+}
+
+static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
+{
+	int err;
+
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
+		ac97_t ac97;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_ice1712_ac97_write;
+		ac97.read = snd_ice1712_ac97_read;
+		ac97.init = snd_ice1712_ac97_init;
+		ac97.private_data = ice;
+		ac97.private_free = snd_ice1712_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
+			printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
+			// return err;
+		} else {
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
+				return err;
+		}
+		return 0;
+	}
+	/* hmm.. can we have both consumer and pro ac97 mixers? */
+	if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) {
+		ac97_t ac97;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_ice1712_pro_ac97_write;
+		ac97.read = snd_ice1712_pro_ac97_read;
+		ac97.init = snd_ice1712_ac97_init;
+		ac97.private_data = ice;
+		ac97.private_free = snd_ice1712_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
+			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+			// return err;
+		}
+		return 0;
+	}
+	/* I2S mixer only */
+	strcat(ice->card->mixername, "ICE1712 - multitrack");
+	return 0;
+}
+
+/*
+ *
+ */
+
+static void snd_ice1712_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return);
+	unsigned int idx;
+
+	snd_iprintf(buffer, "ICE1712\n\n");
+	snd_iprintf(buffer, "EEPROM:\n");
+	snd_iprintf(buffer, "  Subvendor        : 0x%x\n", ice->eeprom.subvendor);
+	snd_iprintf(buffer, "  Size             : %i bytes\n", ice->eeprom.size);
+	snd_iprintf(buffer, "  Version          : %i\n", ice->eeprom.version);
+	snd_iprintf(buffer, "  Codec            : 0x%x\n", ice->eeprom.codec);
+	snd_iprintf(buffer, "  ACLink           : 0x%x\n", ice->eeprom.aclink);
+	snd_iprintf(buffer, "  I2S ID           : 0x%x\n", ice->eeprom.i2sID);
+	snd_iprintf(buffer, "  S/PDIF           : 0x%x\n", ice->eeprom.spdif);
+	snd_iprintf(buffer, "  GPIO mask        : 0x%x\n", ice->eeprom.gpiomask);
+	snd_iprintf(buffer, "  GPIO state       : 0x%x\n", ice->eeprom.gpiostate);
+	snd_iprintf(buffer, "  GPIO direction   : 0x%x\n", ice->eeprom.gpiodir);
+	snd_iprintf(buffer, "  AC'97 main       : 0x%x\n", ice->eeprom.ac97main);
+	snd_iprintf(buffer, "  AC'97 pcm        : 0x%x\n", ice->eeprom.ac97pcm);
+	snd_iprintf(buffer, "  AC'97 record     : 0x%x\n", ice->eeprom.ac97rec);
+	snd_iprintf(buffer, "  AC'97 record src : 0x%x\n", ice->eeprom.ac97recsrc);
+	for (idx = 0; idx < 4; idx++)
+		snd_iprintf(buffer, "  DAC ID #%i        : 0x%x\n", idx, ice->eeprom.dacID[idx]);
+	for (idx = 0; idx < 4; idx++)
+		snd_iprintf(buffer, "  ADC ID #%i        : 0x%x\n", idx, ice->eeprom.adcID[idx]);
+	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
+		snd_iprintf(buffer, "  Extra #%02i        : 0x%x\n", idx, ice->eeprom.extra[idx - 0x1c]);
+}
+
+static void __devinit snd_ice1712_proc_init(ice1712_t * ice)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ice;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 2048;
+		entry->c.text.read = snd_ice1712_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ice->proc_entry = entry;
+}
+
+static void snd_ice1712_proc_done(ice1712_t * ice)
+{
+	if (ice->proc_entry) {
+		snd_info_unregister(ice->proc_entry);
+		ice->proc_entry = NULL;
+	}
+}
+
+/*
+ *
+ */
+
+static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 32;
+	return 0;
+}
+
+static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	
+	memcpy(ucontrol->value.bytes.data, &ice->eeprom, 32);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	name: "ICE1712 EEPROM",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_ice1712_eeprom_info,
+	get: snd_ice1712_eeprom_get
+};
+
+/*
+ */
+static int snd_ice1712_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits);
+		break;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_bits != val;
+		ice->cs8403_spdif_bits = val;
+		if (change && ice->playback_pro_substream == NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_delta_cs8403_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_bits != val;
+		ice->cs8403_spdif_bits = val;
+		if (change && ice->playback_pro_substream == NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_ews_cs8404_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	default:
+		change = 0;
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_ice1712_spdif_default_info,
+	get:		snd_ice1712_spdif_default_get,
+	put:		snd_ice1712_spdif_default_put
+};
+
+static int snd_ice1712_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+						     IEC958_AES0_PROFESSIONAL |
+						     IEC958_AES0_CON_NOT_COPYRIGHT |
+						     IEC958_AES0_CON_EMPHASIS;
+		ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
+						     IEC958_AES1_CON_CATEGORY;
+		ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		ucontrol->value.iec958.status[0] = 0xff;
+		ucontrol->value.iec958.status[1] = 0xff;
+		ucontrol->value.iec958.status[2] = 0xff;
+		ucontrol->value.iec958.status[3] = 0xff;
+		ucontrol->value.iec958.status[4] = 0xff;
+		break;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+						     IEC958_AES0_PROFESSIONAL |
+						     IEC958_AES0_PRO_FS |
+						     IEC958_AES0_PRO_EMPHASIS;
+		ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE;
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		ucontrol->value.iec958.status[0] = 0xff;
+		ucontrol->value.iec958.status[1] = 0xff;
+		ucontrol->value.iec958.status[2] = 0xff;
+		ucontrol->value.iec958.status[3] = 0xff;
+		ucontrol->value.iec958.status[4] = 0xff;
+		break;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_ice1712_spdif_mask_info,
+	get:		snd_ice1712_spdif_maskc_get,
+};
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	info:		snd_ice1712_spdif_mask_info,
+	get:		snd_ice1712_spdif_maskp_get,
+};
+
+static int snd_ice1712_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits);
+		break;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_stream_bits != val;
+		ice->cs8403_spdif_stream_bits = val;
+		if (change && ice->playback_pro_substream != NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_delta_cs8403_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_stream_bits != val;
+		ice->cs8403_spdif_stream_bits = val;
+		if (change && ice->playback_pro_substream != NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_ews_cs8404_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	default:
+		change = 0;
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_ice1712_spdif_stream_info,
+	get:		snd_ice1712_spdif_stream_get,
+	put:		snd_ice1712_spdif_stream_put
+};
+
+#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \
+{ iface: xiface, name: xname, access: xaccess, info: snd_ice1712_gpio_info, \
+  get: snd_ice1712_gpio_get, put: snd_ice1712_gpio_put, \
+  private_value: mask | (invert << 24) }
+
+static int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
+	unsigned char saved[2];
+	
+	save_gpio_status(ice, saved);
+	ucontrol->value.integer.value[0] = (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0) ^ invert;
+	restore_gpio_status(ice, saved);
+	return 0;
+}
+
+static int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
+	unsigned char saved[2];
+	int val, nval;
+
+	if (kcontrol->private_value & (1 << 31))
+		return -EPERM;
+	nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert;
+	save_gpio_status(ice, saved);
+	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	nval |= val & ~mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+	restore_gpio_status(ice, saved);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
+static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+
+static int snd_ice1712_pro_spdif_master_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_pro_spdif_master_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER ? 1 : 0;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_spdif_master_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int nval, change;
+
+	nval = ucontrol->value.integer.value[0] ? ICE1712_SPDIF_MASTER : 0;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= inb(ICEMT(ice, RATE)) & ~ICE1712_SPDIF_MASTER;
+	change = inb(ICEMT(ice, RATE)) != nval;
+	outb(nval, ICEMT(ice, RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+	if (ice->cs8427) {
+		/* change CS8427 clock source too */
+		snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]);
+	}
+
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_spdif_master __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Multi Track IEC958 Master",
+	info: snd_ice1712_pro_spdif_master_info,
+	get: snd_ice1712_pro_spdif_master_get,
+	put: snd_ice1712_pro_spdif_master_put
+};
+
+/*
+ * routing
+ */
+static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[] = {
+		"PCM Out", /* 0 */
+		"H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
+		"H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
+		"IEC958 In L", "IEC958 In R", /* 9-10 */
+		"Digital Mixer", /* 11 - optional */
+	};
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->id.index;
+	unsigned int val, cval;
+	val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+	val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
+	val &= 3;
+	cval = inl(ICEMT(ice, ROUTE_CAPTURE));
+	cval >>= ((idx / 2) * 8) + ((idx % 2) * 4);
+	if (val == 1 && idx < 2)
+		ucontrol->value.enumerated.item[0] = 11;
+	else if (val == 2)
+		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+	else if (val == 3)
+		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change, shift;
+	int idx = kcontrol->id.index;
+	unsigned int val, old_val, nval;
+	
+	/* update PSDOUT */
+	if (ucontrol->value.enumerated.item[0] >= 11)
+		nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
+	else if (ucontrol->value.enumerated.item[0] >= 9)
+		nval = 3; /* spdif in */
+	else if (ucontrol->value.enumerated.item[0] >= 1)
+		nval = 2; /* analog in */
+	else
+		nval = 0; /* pcm */
+	shift = ((idx % 2) * 8) + ((idx / 2) * 2);
+	val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+	val &= ~(0x03 << shift);
+	val |= nval << shift;
+	change = val != old_val;
+	if (change)
+		outw(val, ICEMT(ice, ROUTE_PSDOUT03));
+	if (nval < 2) /* dig mixer of pcm */
+		return change;
+
+	/* update CAPTURE */
+	val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
+	shift = ((idx / 2) * 8) + ((idx % 2) * 4);
+	if (nval == 2) { /* analog in */
+		nval = ucontrol->value.enumerated.item[0] - 1;
+		val &= ~(0x07 << shift);
+		val |= nval << shift;
+	} else { /* spdif in */
+		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+		val &= ~(0x08 << shift);
+		val |= nval << shift;
+	}
+	if (val != old_val) {
+		change = 1;
+		outl(val, ICEMT(ice, ROUTE_CAPTURE));
+	}
+	return change;
+}
+
+static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->id.index;
+	unsigned int val, cval;
+	val = inw(ICEMT(ice, ROUTE_SPDOUT));
+	cval = (val >> (idx * 4 + 8)) & 0x0f;
+	val = (val >> (idx * 2)) & 0x03;
+	if (val == 1)
+		ucontrol->value.enumerated.item[0] = 11;
+	else if (val == 2)
+		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+	else if (val == 3)
+		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change, shift;
+	int idx = kcontrol->id.index;
+	unsigned int val, old_val, nval;
+	
+	/* update SPDOUT */
+	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
+	if (ucontrol->value.enumerated.item[0] >= 11)
+		nval = 1;
+	else if (ucontrol->value.enumerated.item[0] >= 9)
+		nval = 3;
+	else if (ucontrol->value.enumerated.item[0] >= 1)
+		nval = 2;
+	else
+		nval = 0;
+	shift = idx * 2;
+	val &= ~(0x03 << shift);
+	val |= nval << shift;
+	shift = idx * 4 + 8;
+	if (nval == 2) {
+		nval = ucontrol->value.enumerated.item[0] - 1;
+		val &= ~(0x07 << shift);
+		val |= nval << shift;
+	} else if (nval == 3) {
+		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+		val &= ~(0x08 << shift);
+		val |= nval << shift;
+	}
+	change = val != old_val;
+	if (change)
+		outw(val, ICEMT(ice, ROUTE_SPDOUT));
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "H/W Playback Route",
+	info: snd_ice1712_pro_route_info,
+	get: snd_ice1712_pro_route_analog_get,
+	put: snd_ice1712_pro_route_analog_put,
+};
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "IEC958 Playback Route",
+	info: snd_ice1712_pro_route_info,
+	get: snd_ice1712_pro_route_spdif_get,
+	put: snd_ice1712_pro_route_spdif_put,
+};
+
+
+static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
+	outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Multi Track Volume Rate",
+	info: snd_ice1712_pro_volume_rate_info,
+	get: snd_ice1712_pro_volume_rate_get,
+	put: snd_ice1712_pro_volume_rate_put
+};
+
+static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 22;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int idx;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	for (idx = 0; idx < 22; idx++) {
+		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
+		ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Multi Track Peak",
+	access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	info: snd_ice1712_pro_peak_info,
+	get: snd_ice1712_pro_peak_get
+};
+
+/*
+ * EWX 24/96
+ */
+
+static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){
+
+	static char *texts[4] = {
+		"+4dBu", "-10dBV",
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= 2)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	
+	save_gpio_status(ice, saved);
+	ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0;
+	restore_gpio_status(ice, saved);
+	return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	int val, nval;
+
+	if (kcontrol->private_value & (1 << 31))
+		return -EPERM;
+	nval = ucontrol->value.enumerated.item[0] ? mask : 0;
+	save_gpio_status(ice, saved);
+	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	nval |= val & ~mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+	restore_gpio_status(ice, saved);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ewx_input_sense __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Input Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ewx_io_sense_get,
+	put: snd_ice1712_ewx_io_sense_put,
+	private_value: ICE1712_EWX2496_AIN_SEL,
+};
+
+static snd_kcontrol_new_t snd_ice1712_ewx_output_sense __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Output Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ewx_io_sense_get,
+	put: snd_ice1712_ewx_io_sense_put,
+	private_value: ICE1712_EWX2496_AOUT_SEL,
+};
+
+
+/*
+ * EWS88MT
+ */
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char data;
+
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */
+	return 0;
+}
+
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char data, ndata;
+
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
+	if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return ndata != data;
+}
+
+/* analog input sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int channel = kcontrol->id.index;
+	unsigned char data;
+
+	snd_assert(channel >= 0 && channel <= 7, return 0);
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	/* reversed; high = +4dBu, low = -10dBV */
+	ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1;
+	return 0;
+}
+
+/* analog output sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int channel = kcontrol->id.index;
+	unsigned char data, ndata;
+
+	snd_assert(channel >= 0 && channel <= 7, return 0);
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
+	if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[0], &ndata, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return ndata != data;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Input Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ews88mt_input_sense_get,
+	put: snd_ice1712_ews88mt_input_sense_put,
+};
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Output Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ews88mt_output_sense_get,
+	put: snd_ice1712_ews88mt_output_sense_put,
+};
+
+
+/*
+ * EWS88D controls
+ */
+
+static int snd_ice1712_ews88d_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	unsigned char data[2];
+	
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01;
+	if (invert)
+		data[0] ^= 0x01;
+	ucontrol->value.integer.value[0] = data[0];
+	return 0;
+}
+
+static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	unsigned char data[2], ndata[2];
+	int change;
+
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7));
+	if (invert) {
+		if (! ucontrol->value.integer.value[0])
+			ndata[shift >> 3] |= (1 << (shift & 7));
+	} else {
+		if (ucontrol->value.integer.value[0])
+			ndata[shift >> 3] |= (1 << (shift & 7));
+	}
+	change = (data[shift >> 3] != ndata[shift >> 3]);
+	if (change && snd_i2c_sendbytes(ice->pcf8575, data, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return change;
+}
+
+#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
+{ iface: xiface,\
+  name: xname,\
+  access: xaccess,\
+  info: snd_ice1712_ews88d_control_info,\
+  get: snd_ice1712_ews88d_control_get,\
+  put: snd_ice1712_ews88d_control_put,\
+  private_value: xshift | (xinvert << 8),\
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = {
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0),
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0),
+};
+
+
+/*
+ * DMX 6Fire controls
+ */
+
+#if 0 // XXX not working yet
+static int snd_ice1712_6fire_read_pca(ice1712_t *ice)
+{
+	unsigned char byte;
+	snd_i2c_lock(ice->i2c);
+	byte = 0; /* read port */
+	snd_i2c_sendbytes(ice->pcf8575, &byte, 1);
+	if (snd_i2c_readbytes(ice->pcf8575, &byte, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return byte;
+}
+
+static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char data)
+{
+	unsigned char bytes[2];
+	snd_i2c_lock(ice->i2c);
+	bytes[0] = 1; /* write port */
+	bytes[1] = data;
+	if (snd_i2c_sendbytes(ice->pcf8575, bytes, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EREMOTE;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return 0;
+}
+
+static int snd_ice1712_6fire_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	int data;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice)) < 0)
+		return data;
+	data = (data >> shift) & 1;
+	if (invert)
+		data ^= 1;
+	ucontrol->value.integer.value[0] = data;
+	return 0;
+}
+
+static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	int data, ndata;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice)) < 0)
+		return data;
+	ndata = data & ~(1 << shift);
+	if (ucontrol->value.integer.value[0])
+		ndata |= (1 << shift);
+	if (invert)
+		ndata ^= (1 << shift);
+	if (data != ndata) {
+		snd_ice1712_6fire_write_pca(ice, (unsigned char)ndata);
+		return 1;
+	}
+	return 0;
+}
+
+#define DMX6FIRE_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
+{ iface: xiface,\
+  name: xname,\
+  access: xaccess,\
+  info: snd_ice1712_6fire_control_info,\
+  get: snd_ice1712_6fire_control_get,\
+  put: snd_ice1712_6fire_control_put,\
+  private_value: xshift | (xinvert << 8),\
+}
+
+static snd_kcontrol_new_t snd_ice1712_6fire_led __devinitdata =
+DMX6FIRE_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Breakbox LED", 6, 0, 0);
+
+#endif // XXX not working yet
+
+
+/*
+ *
+ */
+
+static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice,
+						 unsigned char dev,
+						 unsigned char addr)
+{
+	long t = 0x10000;
+
+	outb(addr, ICEREG(ice, I2C_BYTE_ADDR));
+	outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR));
+	while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ;
+	return inb(ICEREG(ice, I2C_DATA));
+}
+
+static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice)
+{
+	int dev = 0xa0;		/* EEPROM device address */
+	unsigned int idx;
+
+	if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) {
+		snd_printk("ICE1712 has not detected EEPROM\n");
+		return -EIO;
+	}
+	ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
+				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
+	ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04);
+	if (ice->eeprom.size < 28) {
+		snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
+		return -EIO;
+	}
+	ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
+	if (ice->eeprom.version != 1) {
+		snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
+		return -EIO;
+	}
+	ice->eeprom.codec = snd_ice1712_read_i2c(ice, dev, 0x06);
+	ice->eeprom.aclink = snd_ice1712_read_i2c(ice, dev, 0x07);
+	ice->eeprom.i2sID = snd_ice1712_read_i2c(ice, dev, 0x08);
+	ice->eeprom.spdif = snd_ice1712_read_i2c(ice, dev, 0x09);
+	ice->eeprom.gpiomask = snd_ice1712_read_i2c(ice, dev, 0x0a);
+	ice->eeprom.gpiostate = snd_ice1712_read_i2c(ice, dev, 0x0b);
+	ice->eeprom.gpiodir = snd_ice1712_read_i2c(ice, dev, 0x0c);
+	ice->eeprom.ac97main = (snd_ice1712_read_i2c(ice, dev, 0x0d) << 0) |
+			       (snd_ice1712_read_i2c(ice, dev, 0x0e) << 8);
+	ice->eeprom.ac97pcm = (snd_ice1712_read_i2c(ice, dev, 0x0f) << 0) |
+			      (snd_ice1712_read_i2c(ice, dev, 0x10) << 8);
+	ice->eeprom.ac97rec = (snd_ice1712_read_i2c(ice, dev, 0x11) << 0) |
+			      (snd_ice1712_read_i2c(ice, dev, 0x12) << 8);
+	ice->eeprom.ac97recsrc = snd_ice1712_read_i2c(ice, dev, 0x13) << 0;
+	for (idx = 0; idx < 4; idx++) {
+		ice->eeprom.dacID[idx] = snd_ice1712_read_i2c(ice, dev, 0x14 + idx);
+		ice->eeprom.adcID[idx] = snd_ice1712_read_i2c(ice, dev, 0x18 + idx);
+	}
+	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
+		ice->eeprom.extra[idx - 0x1c] = snd_ice1712_read_i2c(ice, dev, idx);
+	return 0;
+}
+
+static void __devinit snd_ice1712_ak4524_init(ice1712_t *ice)
+{
+	static unsigned char inits[] = {
+		0x00, 0x07, /* 0: all power up */
+		0x01, 0x00, /* 1: ADC/DAC reset */
+		0x02, 0x60, /* 2: 24bit I2S */
+		0x03, 0x19, /* 3: deemphasis off */
+		0x01, 0x03, /* 1: ADC/DAC enable */
+		0x04, 0x00, /* 4: ADC left muted */
+		0x05, 0x00, /* 5: ADC right muted */
+		0x04, 0x80, /* 4: ADC IPGA gain 0dB */
+		0x05, 0x80, /* 5: ADC IPGA gain 0dB */
+		0x06, 0x00, /* 6: DAC left muted */
+		0x07, 0x00, /* 7: DAC right muted */
+		0xff, 0xff
+	};
+	int chip, idx;
+	unsigned char *ptr, reg, data;
+
+	for (chip = idx = 0; chip < ice->num_dacs/2; chip++) {
+		ptr = inits;
+		while (*ptr != 0xff) {
+			reg = *ptr++;
+			data = *ptr++;
+			if (ice->ak4528) {
+				if (reg > 5)
+					continue;
+				if (reg >= 4 && (data & 0x80))
+					continue;
+			}
+			if (reg == 0x03 && ice->ak4528)
+				data = 0x0d;	/* deemphasis off, turn LR highpass filters on */
+			snd_ice1712_ak4524_write(ice, chip, reg, data);
+		}
+	}
+}
+
+static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte)
+{
+	byte |= ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+	byte &= ~ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+	byte |= ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+}
+
+static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate)
+{
+	down(&ice->gpio_mutex);
+
+	/* select box */
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+
+	/* prepare for write */
+	if (chn == 3)
+		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	udelay(100);
+	if (chn == 3) {
+		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
+		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	} else {
+		switch (chn) {
+		case 0:	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break;
+		case 1:	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break;
+		case 2:	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break;
+		}
+		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	}
+	udelay(100);
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	udelay(100);
+
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave)
+{
+	down(&ice->gpio_mutex);
+
+	/* select box */
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	udelay(100);
+	
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	
+	udelay(100);
+	
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	udelay(100);
+
+	/* MIDI2 is direct */
+	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_init(ice1712_t *ice)
+{
+	int box, chn;
+
+	ice->hoontech_boxbits[0] = 
+	ice->hoontech_boxbits[1] = 
+	ice->hoontech_boxbits[2] = 
+	ice->hoontech_boxbits[3] = 0;	/* should be already */
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1);
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0);
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1);
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1);
+	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0);
+
+	/* let's go - activate only functions in first box */
+	ice->hoontech_config = 0;
+			    /* ICE1712_STDSP24_MUTE |
+			       ICE1712_STDSP24_INSEL |
+			       ICE1712_STDSP24_DAREAR; */
+	ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+				     ICE1712_STDSP24_BOX_CHN2 |
+				     ICE1712_STDSP24_BOX_CHN3 |
+				     ICE1712_STDSP24_BOX_CHN4 |
+				     ICE1712_STDSP24_BOX_MIDI1 |
+				     ICE1712_STDSP24_BOX_MIDI2;
+	ice->hoontech_boxconfig[1] = 
+	ice->hoontech_boxconfig[2] = 
+	ice->hoontech_boxconfig[3] = 0;
+	snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+	snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+	snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+	for (box = 0; box < 4; box++) {
+		for (chn = 0; chn < 4; chn++)
+			snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0);
+		snd_ice1712_stdsp24_box_midi(ice, box,
+				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0,
+				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0);
+	}
+}
+
+static int __devinit snd_ice1712_chip_init(ice1712_t *ice)
+{
+	int err, has_i2c = 0;
+
+	outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
+	udelay(200);
+	outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
+	udelay(200);
+	pci_write_config_byte(ice->pci, 0x60, ice->eeprom.codec);
+	pci_write_config_byte(ice->pci, 0x61, ice->eeprom.aclink);
+	pci_write_config_byte(ice->pci, 0x62, ice->eeprom.i2sID);
+	pci_write_config_byte(ice->pci, 0x63, ice->eeprom.spdif);
+	if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+		ice->gpio_write_mask = ice->eeprom.gpiomask;
+		ice->gpio_direction = ice->eeprom.gpiodir;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
+	} else {
+		ice->gpio_write_mask = 0xc0;
+		ice->gpio_direction = 0xff;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT);
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0);
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
+		outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD));
+		udelay(100);
+		outb(0, ICEREG(ice, AC97_CMD));
+		udelay(200);
+		snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
+	}
+
+	/* determine I2C, DACs and ADCs */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		ice->ak4528 = 1;
+		/* follow thru */
+	case ICE1712_SUBDEVICE_EWX2496:
+		has_i2c = 1;
+		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 2;
+		break;	
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_DELTA66:
+		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 4;
+		if (ice->omni)
+			ice->num_total_dacs = 8;
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		has_i2c = 1;
+		/* follow thru */
+	case ICE1712_SUBDEVICE_DELTA1010:
+		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 8;
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		has_i2c = 1;
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		has_i2c = 1;
+		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 6;
+		break;
+	}
+
+	if (has_i2c) {
+		if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
+			snd_printk("unable to create I2C bus\n");
+			return err;
+		}
+		ice->i2c->private_data = ice;
+		switch (ice->eeprom.subvendor) {
+		case ICE1712_SUBDEVICE_AUDIOPHILE:
+			ice->i2c->ops = &ap_cs8427_i2c_ops;
+			break;
+		case ICE1712_SUBDEVICE_EWX2496:
+		case ICE1712_SUBDEVICE_EWS88MT:
+		case ICE1712_SUBDEVICE_EWS88D:
+		case ICE1712_SUBDEVICE_DMX6FIRE:
+			ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops;
+			break;
+		}
+		switch (ice->eeprom.subvendor) {
+		case ICE1712_SUBDEVICE_AUDIOPHILE:
+		case ICE1712_SUBDEVICE_EWX2496:
+			if ((err = snd_cs8427_create(ice->i2c, CS8427_BASE_ADDR, &ice->cs8427)) < 0) {
+				snd_printk("CS8427 initialization failed\n");
+				return err;
+			}
+			break;
+		case ICE1712_SUBDEVICE_DMX6FIRE:
+#if 0 // XXX not working yet
+			if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", 0x40>>1, &ice->pcf8575)) < 0)
+				return err;
+			if ((err = snd_cs8427_create(ice->i2c, 0x11, &ice->cs8427)) < 0) {
+				snd_printk("CS8427 initialization failed\n");
+				return err;
+			}
+#endif // XXX not working yet
+			break;
+		case ICE1712_SUBDEVICE_EWS88MT:
+			if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0)
+				return err;
+			if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->pcf8574[0])) < 0)
+				return err;
+			if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->pcf8574[1])) < 0)
+				return err;
+			break;
+		case ICE1712_SUBDEVICE_EWS88D:
+			if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->pcf8575)) < 0)
+				return err;
+			break;
+		}
+	}
+	/* second stage of initialization, analog parts and others */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		snd_ice1712_ak4524_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_STDSP24:
+		snd_ice1712_stdsp24_init(ice);
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		/* Set spdif defaults */
+		snd_ice1712_delta_cs8403_spdif_write(ice, ice->cs8403_spdif_bits);
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		/* Set spdif defaults */
+		snd_ice1712_ews_cs8404_spdif_write(ice, ice->cs8403_spdif_bits);
+		break;
+	}
+	return 0;
+}
+
+static int __init snd_ice1712_build_controls(ice1712_t *ice)
+{
+	unsigned int idx;
+	snd_kcontrol_t *kctl;
+	int err;
+
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_spdif_master, ice));
+	if (err < 0)
+		return err;
+	for (idx = 0; idx < ice->num_total_dacs; idx++) {
+		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.index = idx;
+		err = snd_ctl_add(ice->card, kctl);
+		if (err < 0)
+			return err;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.index = idx;
+		err = snd_ctl_add(ice->card, kctl);
+		if (err < 0)
+			return err;
+	}
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
+	if (err < 0)
+		return err;
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_assert(ice->pcm_pro != NULL, return -EIO);
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		ice->spdif_stream_ctl = kctl;
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		for (idx = 0; idx < ice->num_dacs; ++idx) {
+			snd_kcontrol_t ctl;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "DAC Volume");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_volume_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_volume_get;
+			ctl.put = snd_ice1712_ak4524_volume_put;
+			if (ice->ak4528)
+				ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
+			else
+				ctl.private_value = (idx / 2) * 8 + (idx % 2) + 6; /* register 6 & 7 */
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+		}
+		for (idx = 0; idx < ice->num_adcs && !ice->ak4528; ++idx) {
+			snd_kcontrol_t ctl;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "ADC Volume");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_volume_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_volume_get;
+			ctl.put = snd_ice1712_ak4524_volume_put;
+			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "IPGA Analog Capture Volume");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_ipga_gain_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_ipga_gain_get;
+			ctl.put = snd_ice1712_ak4524_ipga_gain_put;
+			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+		}
+		for (idx = 0; idx < ice->num_dacs/2; idx++) {
+			snd_kcontrol_t ctl;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "Deemphasis");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_deemphasis_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_deemphasis_get;
+			ctl.put = snd_ice1712_ak4524_deemphasis_put;
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+		}
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_input_sense, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_output_sense, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		for (idx = 0; idx < 8; idx++) {
+			kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice);
+			kctl->id.index = idx;
+			err = snd_ctl_add(ice->card, kctl);
+			if (err < 0)
+				return err;
+		}
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		for (idx = 0; idx < sizeof(snd_ice1712_ews88d_controls)/sizeof(snd_ice1712_ews88d_controls[0]); idx++) {
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice));
+			if (err < 0)
+				return err;
+		}
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+#if 0 // XXX not working yet
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_led, ice));
+		if (err < 0)
+			return err;
+#endif
+		break;
+	}
+
+	return 0;
+}
+
+static int snd_ice1712_free(ice1712_t *ice)
+{
+	if (ice->res_port == NULL)
+		goto __hw_end;
+	/* mask all interrupts */
+	outb(0xc0, ICEMT(ice, IRQ));
+	outb(0xff, ICEREG(ice, IRQMASK));
+	/* --- */
+      __hw_end:
+	snd_ice1712_proc_done(ice);
+	synchronize_irq();
+	if (ice->irq)
+		free_irq(ice->irq, (void *) ice);
+	if (ice->res_port) {
+		release_resource(ice->res_port);
+		kfree_nocheck(ice->res_port);
+	}
+	if (ice->res_ddma_port) {
+		release_resource(ice->res_ddma_port);
+		kfree_nocheck(ice->res_ddma_port);
+	}
+	if (ice->res_dmapath_port) {
+		release_resource(ice->res_dmapath_port);
+		kfree_nocheck(ice->res_dmapath_port);
+	}
+	if (ice->res_profi_port) {
+		release_resource(ice->res_profi_port);
+		kfree_nocheck(ice->res_profi_port);
+	}
+	snd_magic_kfree(ice);
+	return 0;
+}
+
+static int snd_ice1712_dev_free(snd_device_t *device)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO);
+	return snd_ice1712_free(ice);
+}
+
+static int __devinit snd_ice1712_create(snd_card_t * card,
+				     struct pci_dev *pci,
+				     int omni,
+				     ice1712_t ** r_ice1712)
+{
+	ice1712_t *ice;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_ice1712_dev_free,
+	};
+
+	*r_ice1712 = NULL;
+
+        /* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 28 bits */
+	if (!pci_dma_supported(pci, 0x0fffffff)) {
+		snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x0fffffff);
+
+	ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL);
+	if (ice == NULL)
+		return -ENOMEM;
+	ice->omni = omni ? 1 : 0;
+	spin_lock_init(&ice->reg_lock);
+	init_MUTEX(&ice->gpio_mutex);
+	ice->cs8403_spdif_bits =
+	ice->cs8403_spdif_stream_bits = (0x01 |	/* consumer format */
+					 0x10 |	/* no emphasis */
+					 0x20);	/* PCM encoder/decoder */
+	ice->card = card;
+	ice->pci = pci;
+	ice->irq = -1;
+	ice->port = pci_resource_start(pci, 0);
+	ice->ddma_port = pci_resource_start(pci, 1);
+	ice->dmapath_port = pci_resource_start(pci, 2);
+	ice->profi_port = pci_resource_start(pci, 3);
+	pci_set_master(pci);
+	pci_write_config_word(ice->pci, 0x40, 0x807f);
+	pci_write_config_word(ice->pci, 0x42, 0x0006);
+	snd_ice1712_proc_init(ice);
+	synchronize_irq();
+
+	if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1);
+		return -EIO;
+	}
+	if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1);
+		return -EIO;
+	}
+	if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1);
+		return -EIO;
+	}
+	if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1);
+		return -EIO;
+	}
+	if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EIO;
+	}
+	ice->irq = pci->irq;
+
+	if (snd_ice1712_read_eeprom(ice) < 0) {
+		snd_ice1712_free(ice);
+		return -EIO;
+	}
+	if (snd_ice1712_chip_init(ice) < 0) {
+		snd_ice1712_free(ice);
+		return -EIO;
+	}
+
+	/* unmask used interrupts */
+	outb((ice->eeprom.codec & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 |
+	     (ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0,
+	     ICEREG(ice, IRQMASK));
+	outb(0x00, ICEMT(ice, IRQ));
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+		snd_ice1712_free(ice);
+		return err;
+	}
+
+	*r_ice1712 = ice;
+	return 0;
+}
+
+static int __devinit snd_ice1712_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	ice1712_t *ice;
+	int pcm_dev = 0, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ice1712_create(card, pci, snd_omni[dev], &ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
+		if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ice1712_build_controls(ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
+		if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	strcpy(card->driver, "ICE1712");
+	strcpy(card->shortname, "ICEnsemble ICE1712");
+	
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_STDSP24:
+		strcpy(card->shortname, "Hoontech SoundTrack Audio DSP24");
+		break;
+	case ICE1712_SUBDEVICE_DELTA1010:
+		strcpy(card->shortname, "M Audio Delta 1010");
+		break;
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+		strcpy(card->shortname, "M Audio Delta DiO 2496");
+		goto __no_mpu401;
+	case ICE1712_SUBDEVICE_DELTA66:
+		strcpy(card->shortname, "M Audio Delta 66");
+		goto __no_mpu401;
+	case ICE1712_SUBDEVICE_DELTA44:
+		strcpy(card->shortname, "M Audio Delta 44");
+		goto __no_mpu401;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		strcpy(card->shortname, "M Audio Audiophile 24/96");
+		break;
+	case ICE1712_SUBDEVICE_EWX2496:
+		strcpy(card->shortname, "TerraTec EWX 24/96");
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		strcpy(card->shortname, "TerraTec EWS 88MT");
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		strcpy(card->shortname, "TerraTec EWS 88D");
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		strcpy(card->shortname, "TerraTec DMX 6Fire");
+		break;
+	}
+
+	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+				       ICEREG(ice, MPU1_CTRL), 1,
+				       ice->irq, 0,
+				       &ice->rmidi[0])) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (ice->eeprom.codec & ICE1712_CFG_2xMPU401)
+		if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
+					       ICEREG(ice, MPU2_CTRL), 1,
+					       ice->irq, 0,
+					       &ice->rmidi[1])) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+      __no_mpu401:
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, ice->port, ice->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_ice1712_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "ICE1712",
+	id_table: snd_ice1712_ids,
+	probe: snd_ice1712_probe,
+	remove: __devexit_p(snd_ice1712_remove),
+};
+
+static int __init alsa_card_ice1712_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "ICE1712 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ice1712_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ice1712_init)
+module_exit(alsa_card_ice1712_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ice1712=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_ice1712_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-ice1712=", alsa_card_ice1712_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/intel8x0.c linux-2.4.19-pre5-mjc/sound/pci/intel8x0.c
--- linux/sound/pci/intel8x0.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/intel8x0.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1598 @@
+/*
+ *   ALSA driver for Intel ICH (i8x0) chipsets
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This code also contains alpha support for SiS 735 chipsets provided
+ *   by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet
+ *   for SiS735, so the code is not fully functional.
+ *
+ *
+ *   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
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,MX440; SiS 7012");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Intel,82801AA},"
+		"{Intel,82901AB},"
+		"{Intel,82801BA},"
+		"{Intel,ICH3},"
+		"{Intel,MX440},"
+		"{SiS,SI7012},"
+		"{NVidia,NForce Audio}}");
+
+#define SUPPORT_JOYSTICK 1
+#define SUPPORT_MIDI 1
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#ifdef SUPPORT_JOYSTICK
+static int snd_joystick_port[SNDRV_CARDS] =
+#ifdef CONFIG_ISA
+	{0x200};	/* enable as default */
+#else
+	{0};	/* disabled */
+#endif
+#endif
+#ifdef SUPPORT_MIDI
+static int snd_mpu_port[SNDRV_CARDS]; /* disabled */
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (0 = auto-detect).");
+MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:0");
+#ifdef SUPPORT_JOYSTICK
+MODULE_PARM(snd_joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_port, "Joystick port address for Intel i8x0 soundcard. (0 = disabled)");
+MODULE_PARM_SYNTAX(snd_joystick_port, SNDRV_ENABLED ",allows:{{0},{0x200}},dialog:list");
+#endif
+#ifdef SUPPORT_MIDI
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_port, "MPU401 port # for Intel i8x0 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog:list");
+#endif
+
+/*
+ *  Direct registers
+ */
+
+#ifndef PCI_DEVICE_ID_INTEL_82801
+#define PCI_DEVICE_ID_INTEL_82801       0x2415
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82901
+#define PCI_DEVICE_ID_INTEL_82901       0x2425
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA
+#define PCI_DEVICE_ID_INTEL_82801BA     0x2445
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_440MX
+#define PCI_DEVICE_ID_INTEL_440MX       0x7195
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH3
+#define PCI_DEVICE_ID_INTEL_ICH3	0x2485
+#endif
+#ifndef PCI_DEVICE_ID_SI_7012
+#define PCI_DEVICE_ID_SI_7012		0x7012
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO	0x01b1
+#endif
+
+#define DEVICE_INTEL	0
+#define DEVICE_SIS	1
+
+#define ICHREG(ice, x) ((ice)->bmport + ICH_REG_##x)
+#define ICHREG2(ice, x) ((ice)->bmport + x)
+
+/* capture block */
+#define ICH_REG_PI_BDBAR		0x00	/* dword - buffer descriptor list base address */
+#define ICH_REG_PI_CIV			0x04	/* byte - current index value */
+#define ICH_REG_PI_LVI			0x05	/* byte - last valid index */
+#define   ICH_REG_LVI_MASK		0x1f
+#define ICH_REG_PI_SR			0x06	/* byte - status register */
+#define   ICH_FIFOE			0x10	/* FIFO error */
+#define   ICH_BCIS			0x08	/* buffer completion interrupt status */
+#define   ICH_LVBCI			0x04	/* last valid buffer completion interrupt */
+#define   ICH_CELV			0x02	/* current equals last valid */
+#define   ICH_DCH			0x01	/* DMA controller halted */
+#define ICH_REG_PI_PICB			0x08	/* word - position in current buffer */
+#define ICH_REG_PI_PIV			0x0a	/* byte - prefetched index value */
+#define   ICH_REG_PIV_MASK		0x1f	/* mask */
+#define ICH_REG_PI_CR			0x0b	/* byte - control register */
+#define   ICH_IOCE			0x10	/* interrupt on completion enable */
+#define   ICH_FEIE			0x08	/* fifo error interrupt enable */
+#define   ICH_LVBIE			0x04	/* last valid buffer interrupt enable */
+#define   ICH_RESETREGS			0x02	/* reset busmaster registers */
+#define   ICH_STARTBM			0x01	/* start busmaster operation */
+/* playback block */
+#define ICH_REG_PO_BDBAR		0x10	/* dword - buffer descriptor list base address */
+#define ICH_REG_PO_CIV			0x14	/* byte - current index value */
+#define ICH_REG_PO_LVI			0x15	/* byte - last valid command */
+#define ICH_REG_PO_SR			0x16	/* byte - status register */
+#define ICH_REG_PO_PICB			0x18	/* word - position in current buffer */
+#define ICH_REG_PO_PIV			0x1a	/* byte - prefetched index value */
+#define ICH_REG_PO_CR			0x1b	/* byte - control register */
+/* mic capture block */
+#define ICH_REG_MC_BDBAR		0x20	/* dword - buffer descriptor list base address */
+#define ICH_REG_MC_CIV			0x24	/* byte - current index value */
+#define ICH_REG_MC_LVI			0x25	/* byte - last valid command */
+#define ICH_REG_MC_SR			0x26	/* byte - status register */
+#define ICH_REG_MC_PICB			0x28	/* word - position in current buffer */
+#define ICH_REG_MC_PIV			0x2a	/* byte - prefetched index value */
+#define ICH_REG_MC_CR			0x2b	/* byte - control register */
+/* global block */
+#define ICH_REG_GLOB_CNT		0x2c	/* dword - global control */
+#define   ICH_PCM_246_MASK	0x00300000	/* 6 channels (not all chips) */
+#define   ICH_PCM_6		0x00200000	/* 6 channels (not all chips) */
+#define   ICH_PCM_4		0x00100000	/* 4 channels (not all chips) */
+#define   ICH_PCM_2		0x00000000	/* 2 channels (stereo) */
+#define   ICH_SRIE		0x00000020	/* secondary resume interrupt enable */
+#define   ICH_PRIE		0x00000010	/* primary resume interrupt enable */
+#define   ICH_ACLINK		0x00000008	/* AClink shut off */
+#define   ICH_AC97WARM		0x00000004	/* AC'97 warm reset */
+#define   ICH_AC97COLD		0x00000002	/* AC'97 cold reset */
+#define   ICH_GIE		0x00000001	/* GPI interrupt enable */
+#define ICH_REG_GLOB_STA		0x30	/* dword - global status */
+#define   ICH_MD3		0x00020000	/* modem power down semaphore */
+#define   ICH_AD3		0x00010000	/* audio power down semaphore */
+#define   ICH_RCS		0x00008000	/* read completion status */
+#define   ICH_BIT3		0x00004000	/* bit 3 slot 12 */
+#define   ICH_BIT2		0x00002000	/* bit 2 slot 12 */
+#define   ICH_BIT1		0x00001000	/* bit 1 slot 12 */
+#define   ICH_SRI		0x00000800	/* secondary resume interrupt */
+#define   ICH_PRI		0x00000400	/* primary resume interrupt */
+#define   ICH_SCR		0x00000200	/* secondary codec ready */
+#define   ICH_PCR		0x00000100	/* primary codec ready */
+#define   ICH_MCINT		0x00000080	/* MIC capture interrupt */
+#define   ICH_POINT		0x00000040	/* playback interrupt */
+#define   ICH_PIINT		0x00000020	/* capture interrupt */
+#define   ICH_MOINT		0x00000004	/* modem playback interrupt */
+#define   ICH_MIINT		0x00000002	/* modem capture interrupt */
+#define   ICH_GSCI		0x00000001	/* GPI status change interrupt */
+#define ICH_REG_ACC_SEMA		0x34	/* byte - codec write semaphore */
+#define   ICH_CAS			0x01	/* codec access semaphore */
+
+#define ICH_MAX_FRAGS		32		/* max hw frags */
+
+/*
+ *  
+ */
+
+typedef struct {
+	unsigned long reg_offset;
+	u32 *bdbar;			/* CPU address (32bit) */
+	unsigned int bdbar_addr;		/* PCI bus address (32bit) */
+	snd_pcm_substream_t *substream;
+	unsigned int physbuf;			/* physical address (32bit) */
+        unsigned int size;
+        unsigned int fragsize;
+        unsigned int fragsize1;
+        unsigned int position;
+        int frags;
+        int lvi;
+        int lvi_frag;
+	int ack;
+	int ack_reload;
+#ifdef CONFIG_PM
+	unsigned char civ_saved;
+	unsigned char piv_saved;
+	unsigned short picb_saved;
+#endif
+} ichdev_t;
+
+typedef struct _snd_intel8x0 intel8x0_t;
+#define chip_t intel8x0_t
+
+struct _snd_intel8x0 {
+	unsigned int device_type;
+
+	unsigned long dma_playback_size;
+	unsigned long dma_capture_size;
+	unsigned long dma_mic_size;
+
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long bmport;
+	struct resource *res_bmport;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm_mic;
+	ichdev_t playback;
+	ichdev_t capture;
+	ichdev_t capture_mic;
+
+	int multi4: 1,
+	    multi6: 1;
+	int in_ac97_init: 1;
+
+	ac97_t *ac97;
+	ac97_t *ac97sec;
+
+	snd_rawmidi_t *rmidi;
+
+	spinlock_t reg_lock;
+	spinlock_t ac97_lock;
+	snd_info_entry_t *proc_entry;
+	
+	u32 *bdbars;
+	dma_addr_t bdbars_addr;
+	
+	unsigned int reg_pi_sr;
+	unsigned int reg_pi_picb;
+	unsigned int reg_po_sr;
+	unsigned int reg_po_picb;
+	unsigned int reg_mc_sr;
+	unsigned int reg_mc_picb;	
+
+#ifdef CONFIG_PM
+	int in_suspend;
+#endif
+};
+
+static struct pci_device_id snd_intel8x0_ids[] __devinitdata = {
+	{ 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82801AA */
+	{ 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82901AB */
+	{ 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82801BA */
+	{ 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* ICH3 */
+	{ 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 440MX */
+	{ 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS },	/* SI7012 */
+	{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* NFORCE */
+	{ 0x764d, 0x1022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD8111 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
+
+/*
+ *  Basic I/O
+ */
+static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec)
+{
+	int time;
+
+	/* codec ready ? */
+	if ((inl(ICHREG(chip, GLOB_STA)) & (codec ? ICH_SCR : ICH_PCR)) == 0)
+		return -EIO;
+
+	/* Anyone holding a semaphore for 1 msec should be shot... */
+	time = 100;
+      	do {
+      		if (!(inb(ICHREG(chip, ACC_SEMA)) & ICH_CAS))
+      			return 0;
+		udelay(10);
+	} while (time--);
+
+	/* access to some forbidden (non existant) ac97 registers will not
+	 * reset the semaphore. So even if you don't get the semaphore, still
+	 * continue the access. We don't need the semaphore anyway. */
+	snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+			inb(ICHREG(chip, ACC_SEMA)), inl(ICHREG(chip, GLOB_STA)));
+	inw(chip->port);	/* clear semaphore flag */
+	/* I don't care about the semaphore */
+	return -EBUSY;
+}
+ 
+static void snd_intel8x0_codec_write(ac97_t *ac97,
+				     unsigned short reg,
+				     unsigned short val)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+	
+	spin_lock(&chip->ac97_lock);
+	if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
+		if (! chip->in_ac97_init)
+			snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+	}
+	outw(val, chip->port + reg + ac97->num * 0x80);
+	spin_unlock(&chip->ac97_lock);
+}
+
+static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
+					      unsigned short reg)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0);
+	unsigned short res;
+	unsigned int tmp;
+
+	spin_lock(&chip->ac97_lock);
+	if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
+		if (! chip->in_ac97_init)
+			snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+		res = 0xffff;
+	} else {
+		res = inw(chip->port + reg + ac97->num * 0x80);
+		if ((tmp = inl(ICHREG(chip, GLOB_STA))) & ICH_RCS) {
+			/* reset RCS and preserve other R/WC bits */
+			outl(tmp & ~(ICH_SRI|ICH_PRI|ICH_GSCI), ICHREG(chip, GLOB_STA));
+			if (! chip->in_ac97_init)
+				snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+			res = 0xffff;
+		}
+	}
+	spin_unlock(&chip->ac97_lock);
+	return res;
+}
+
+static int snd_intel8x0_trigger(intel8x0_t *chip, ichdev_t *ichdev, int cmd)
+{
+	unsigned char val = 0;
+	unsigned long port = chip->bmport + ichdev->reg_offset;
+	
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val = ICH_IOCE | ICH_STARTBM;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		val = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val = ICH_IOCE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		val = ICH_IOCE | ICH_STARTBM;
+		break;
+	default:
+		return -EINVAL;
+	}
+	outb(val, port + ICH_REG_PI_CR);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		/* reset whole DMA things */
+		while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) ;
+		outb(ICH_RESETREGS, port + ICH_REG_PI_CR);
+	}
+	return 0;
+}
+
+static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) 
+{
+	int idx;
+	u32 *bdbar = ichdev->bdbar;
+	unsigned long port = chip->bmport + ichdev->reg_offset;
+	int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1;
+
+	outl(ichdev->bdbar_addr, port + ICH_REG_PI_BDBAR);
+	if (ichdev->size == ichdev->fragsize) {
+		ichdev->ack_reload = ichdev->ack = 2;
+		ichdev->fragsize1 = ichdev->fragsize >> 1;
+		for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) {
+			bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf);
+			bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+						     ichdev->fragsize1 >> shiftlen);
+			bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1));
+			bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+						     ichdev->fragsize1 >> shiftlen);
+		}
+		ichdev->frags = 2;
+	} else {
+		ichdev->ack_reload = ichdev->ack = 1;
+		ichdev->fragsize1 = ichdev->fragsize;
+		for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) {
+			bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size));
+			bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+						     ichdev->fragsize >> shiftlen);
+			// printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
+		}
+		ichdev->frags = ichdev->size / ichdev->fragsize;
+	}
+	outb(ichdev->lvi = ICH_REG_LVI_MASK, port + ICH_REG_PI_LVI);
+	ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
+	ichdev->position = 0;
+#if 0
+	printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
+			ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
+#endif
+	/* clear interrupts */
+	outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr);
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
+{
+	unsigned long port = chip->bmport + ichdev->reg_offset;
+	int ack = 0;
+
+	spin_lock(&chip->reg_lock);
+	ichdev->position += ichdev->fragsize1;
+	ichdev->position %= ichdev->size;
+	ichdev->lvi++;
+	ichdev->lvi &= ICH_REG_LVI_MASK;
+	outb(ichdev->lvi, port + ICH_REG_PI_LVI);
+	ichdev->lvi_frag++;
+	ichdev->lvi_frag %= ichdev->frags;
+	ichdev->bdbar[ichdev->lvi * 2] = ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1;
+	// printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_PI_PIV + port), inl(port + 4), inb(port + ICH_REG_PI_CR));
+	if ((ack = (--ichdev->ack == 0)) != 0)
+		ichdev->ack = ichdev->ack_reload;
+	spin_unlock(&chip->reg_lock);
+	if (ack && ichdev->substream)
+		snd_pcm_period_elapsed(ichdev->substream);
+	outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr);
+}
+
+static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return);
+	unsigned int status;
+
+	status = inl(ICHREG(chip, GLOB_STA));
+	if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0)
+		return;
+	if (status & ICH_POINT)
+		snd_intel8x0_update(chip, &chip->playback);
+	if (status & ICH_PIINT)
+		snd_intel8x0_update(chip, &chip->capture);
+	if (status & ICH_MCINT)
+		snd_intel8x0_update(chip, &chip->capture_mic);
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_intel8x0_playback_ioctl(snd_pcm_substream_t * substream,
+				       unsigned int cmd,
+				       void *arg)
+{
+	int result;
+	result = snd_pcm_lib_ioctl(substream, cmd, arg);
+	if (result < 0)
+		return result;
+	return 0;
+}
+
+static int snd_intel8x0_capture_ioctl(snd_pcm_substream_t * substream,
+				      unsigned int cmd,
+				      void *arg)
+{
+	int result;
+	result = snd_pcm_lib_ioctl(substream, cmd, arg);
+	if (result < 0)
+		return result;
+	return 0;
+}
+
+static int snd_intel8x0_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_trigger(chip, &chip->playback, cmd);
+}
+
+static int snd_intel8x0_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_trigger(chip, &chip->capture, cmd);
+}
+
+static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
+				  snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels)
+{
+	unsigned int cnt = inl(ICHREG(chip, GLOB_CNT)) & ~ICH_PCM_246_MASK;
+	if (chip->multi4 && channels == 4)
+		cnt |= ICH_PCM_4;
+	else if (chip->multi6 && channels == 6)
+		cnt |= ICH_PCM_6;
+	outl(cnt, ICHREG(chip, GLOB_CNT));
+}
+
+static int snd_intel8x0_playback_prepare(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->playback.physbuf = runtime->dma_addr;
+	chip->playback.size = snd_pcm_lib_buffer_bytes(substream);
+	chip->playback.fragsize = snd_pcm_lib_period_bytes(substream);
+	spin_lock(&chip->reg_lock);
+	snd_intel8x0_setup_multi_channels(chip, runtime->channels);
+	spin_unlock(&chip->reg_lock);
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	snd_intel8x0_setup_periods(chip, &chip->playback);
+	return 0;
+}
+
+static int snd_intel8x0_capture_prepare(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->capture.physbuf = runtime->dma_addr;
+	chip->capture.size = snd_pcm_lib_buffer_bytes(substream);
+	chip->capture.fragsize = snd_pcm_lib_period_bytes(substream);
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	snd_intel8x0_setup_periods(chip, &chip->capture);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_intel8x0_playback_pointer(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	ptr = chip->playback.fragsize1;
+	if (chip->device_type == DEVICE_SIS)
+		ptr -= inw(ICHREG2(chip,chip->reg_po_picb));
+	else
+		ptr -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
+	ptr += chip->playback.position;
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_intel8x0_capture_pointer(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	ptr = chip->capture.fragsize1;
+	if (chip->device_type == DEVICE_SIS)
+		ptr -= inw(ICHREG2(chip,chip->reg_pi_picb));
+	else
+		ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)) << 1;
+	ptr += chip->capture.position;
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_intel8x0_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_intel8x0_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static unsigned int channels4[] = {
+	2, 4,
+};
+
+#define CHANNELS4 sizeof(channels4) / sizeof(channels4[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = {
+	count: CHANNELS4,
+	list: channels4,
+	mask: 0,
+};
+
+static unsigned int channels6[] = {
+	2, 4, 6,
+};
+
+#define CHANNELS6 sizeof(channels6) / sizeof(channels6[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = {
+	count: CHANNELS6,
+	list: channels6,
+	mask: 0,
+};
+
+static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback.substream = substream;
+	runtime->hw = snd_intel8x0_playback;
+	runtime->hw.rates = chip->ac97->rates_front_dac;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if (chip->device_type == DEVICE_SIS) {
+		runtime->hw.buffer_bytes_max = 64*1024;
+		runtime->hw.period_bytes_max = 64*1024;
+	}
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	if (chip->multi6) {
+		runtime->hw.channels_max = 6;
+		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6);
+	} else if (chip->multi4) {
+		runtime->hw.channels_max = 4;
+		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4);
+	}
+	return 0;
+}
+
+static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture.substream = substream;
+	runtime->hw = snd_intel8x0_capture;
+	runtime->hw.rates = chip->ac97->rates_adc;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if (chip->device_type == DEVICE_SIS) {
+		runtime->hw.buffer_bytes_max = 64*1024;
+		runtime->hw.period_bytes_max = 64*1024;
+	}
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback.substream = NULL;
+	/* disable DAC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
+	return 0;
+}
+
+static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture.substream = NULL;
+	/* disable ADC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_intel8x0_playback_ops = {
+	open:		snd_intel8x0_playback_open,
+	close:		snd_intel8x0_playback_close,
+	ioctl:		snd_intel8x0_playback_ioctl,
+	hw_params:	snd_intel8x0_hw_params,
+	hw_free:	snd_intel8x0_hw_free,
+	prepare:	snd_intel8x0_playback_prepare,
+	trigger:	snd_intel8x0_playback_trigger,
+	pointer:	snd_intel8x0_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_ops = {
+	open:		snd_intel8x0_capture_open,
+	close:		snd_intel8x0_capture_close,
+	ioctl:		snd_intel8x0_capture_ioctl,
+	hw_params:	snd_intel8x0_hw_params,
+	hw_free:	snd_intel8x0_hw_free,
+	prepare:	snd_intel8x0_capture_prepare,
+	trigger:	snd_intel8x0_capture_trigger,
+	pointer:	snd_intel8x0_capture_pointer,
+};
+
+static void snd_intel8x0_pcm_free(snd_pcm_t *pcm)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  PCM code - MIC
+ */
+
+static int snd_intel8x0_capture_mic_ioctl(snd_pcm_substream_t * substream,
+					  unsigned int cmd,
+					  void *arg)
+{
+	int result;
+	result = snd_pcm_lib_ioctl(substream, cmd, arg);
+	if (result < 0)
+		return result;
+	return 0;
+}
+
+static int snd_intel8x0_capture_mic_trigger(snd_pcm_substream_t * substream,
+					    int cmd)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_trigger(chip, &chip->capture_mic, cmd);
+}
+
+static int snd_intel8x0_capture_mic_prepare(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->capture_mic.physbuf = runtime->dma_addr;
+	chip->capture_mic.size = snd_pcm_lib_buffer_bytes(substream);
+	chip->capture_mic.fragsize = snd_pcm_lib_period_bytes(substream);
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
+	snd_intel8x0_setup_periods(chip, &chip->capture_mic);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_intel8x0_capture_mic_pointer(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	ptr = chip->capture_mic.fragsize1;
+	if (chip->device_type == DEVICE_SIS)
+		ptr -= inw(ICHREG2(chip,chip->reg_mc_picb));
+	else
+		ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)) << 1;
+	ptr += chip->capture_mic.position;
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_intel8x0_capture_mic =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		1,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_intel8x0_capture_mic_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->capture_mic.substream = substream;
+	runtime->hw = snd_intel8x0_capture_mic;
+	runtime->hw.rates = chip->ac97->rates_mic_adc;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if (chip->device_type == DEVICE_SIS) {
+		runtime->hw.buffer_bytes_max = 64*1024;
+		runtime->hw.period_bytes_max = 64*1024;
+	}
+	return 0;
+}
+
+static int snd_intel8x0_capture_mic_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_mic.substream = NULL;
+	/* disable ADC power */
+	snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x4000, 0x4000);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
+	open:		snd_intel8x0_capture_mic_open,
+	close:		snd_intel8x0_capture_mic_close,
+	ioctl:		snd_intel8x0_capture_mic_ioctl,
+	hw_params:	snd_intel8x0_hw_params,
+	hw_free:	snd_intel8x0_hw_free,
+	prepare:	snd_intel8x0_capture_mic_prepare,
+	trigger:	snd_intel8x0_capture_mic_trigger,
+	pointer:	snd_intel8x0_capture_mic_pointer,
+};
+
+static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_mic = NULL;
+}
+
+static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_mic_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname);
+
+	chip->pcm_mic = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer part
+ */
+
+static void snd_intel8x0_codec_init(ac97_t *ac97)
+{
+	// intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+
+	/* disable DAC & ADC power */
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
+	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
+	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+}
+
+static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+	if (ac97->num == 0) {
+		chip->ac97 = NULL;
+	} else {
+		chip->ac97sec = NULL;
+	}
+}
+
+static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
+{
+	ac97_t ac97;
+	int err;
+
+	chip->in_ac97_init = 1;
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_intel8x0_codec_write;
+	ac97.read = snd_intel8x0_codec_read;
+	ac97.init = snd_intel8x0_codec_init;
+	ac97.private_data = chip;
+	ac97.private_free = snd_intel8x0_mixer_free_ac97;
+	if (ac97_clock >= 8000 && ac97_clock <= 48000)
+		ac97.clock = ac97_clock;
+	else
+		ac97.clock = 48000;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+#if 0 /* it seems that SDIN signals are mixed together (at least for AD CNR boards) */
+	if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) {
+		ac97.num = 1;
+		ac97.addr = 1;
+		snd_ac97_mixer(chip->card, &ac97, &chip->ac97sec);
+	}
+#endif
+	if ((inl(ICHREG(chip, GLOB_STA)) & (ICH_PCM_4|ICH_PCM_6)) != (ICH_PCM_4|ICH_PCM_6))
+		return 0;
+	if ((chip->ac97->scaps & AC97_SCAP_SURROUND_DAC) ||
+	    (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_SURROUND_DAC))) {
+		chip->multi4 = 1;
+		if ((chip->ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) ||
+		    (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_CENTER_LFE_DAC)))
+			chip->multi6 = 1;
+	}
+	chip->in_ac97_init = 0;
+	return 0;
+}
+
+
+/*
+ *
+ */
+
+static int snd_intel8x0_chip_init(intel8x0_t *chip)
+{
+	signed long end_time;
+	unsigned int cnt;
+	
+	/* put logic to right state */
+	/* first clear status bits */
+	cnt = inl(ICHREG(chip, GLOB_STA));
+	outl(cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA));
+
+	/* ACLink on, 2 channels */
+	cnt = inl(ICHREG(chip, GLOB_CNT));
+	cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
+	/* finish cold or do warm reset */
+	cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
+	outl(cnt, ICHREG(chip, GLOB_CNT));
+	end_time = (jiffies + (HZ / 4)) + 1;
+	do {
+		if ((inl(ICHREG(chip, GLOB_CNT)) & ICH_AC97WARM) == 0)
+			goto __ok;
+#ifdef CONFIG_PM
+		if (chip->in_suspend) {
+			mdelay(10);
+			continue;
+		}
+#endif
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_printk("AC'97 warm reset still in progress? [0x%x]\n", inl(ICHREG(chip, GLOB_CNT)));
+	return -EIO;
+
+      __ok:
+	/* wait for primary codec ready status.
+	 * Once it becomes ready it should remain ready
+	 * as long as we do not disable the ac97 link.
+ 	 */
+	end_time = jiffies + HZ;
+	do {
+		if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR)
+			goto __ok1;
+#ifdef CONFIG_PM
+		if (chip->in_suspend) {
+			mdelay(10);
+			continue;
+		}
+#endif
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_printk("codec_ready: primary codec is not ready [0x%x]\n", inl(ICHREG(chip, GLOB_STA)));
+	return -EIO;
+
+      __ok1:
+	/* wait for secondary codec ready status. No secondary codec? , ok */
+	/* the end_time variable is not initialized again */
+	do {
+		if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR)
+			break;
+#ifdef CONFIG_PM
+		if (chip->in_suspend) {
+			mdelay(10);
+			continue;
+		}
+#endif
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (end_time - (signed long)jiffies >= 0);
+
+	inw(chip->port);	/* clear semaphore flag */
+
+	/* disable interrupts */
+	outb(0x00, ICHREG(chip, PI_CR));
+	outb(0x00, ICHREG(chip, PO_CR));
+	outb(0x00, ICHREG(chip, MC_CR));
+	/* reset channels */
+	outb(ICH_RESETREGS, ICHREG(chip, PI_CR));
+	outb(ICH_RESETREGS, ICHREG(chip, PO_CR));
+	outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
+	/* initialize Buffer Descriptor Lists */
+	outl(chip->playback.bdbar_addr, ICHREG(chip, PO_BDBAR));
+	outl(chip->capture.bdbar_addr, ICHREG(chip, PI_BDBAR));
+	outl(chip->capture_mic.bdbar_addr, ICHREG(chip, MC_BDBAR));
+	return 0;
+}
+
+static int snd_intel8x0_free(intel8x0_t *chip)
+{
+	if (chip->irq < 0)
+		goto __hw_end;
+	/* disable interrupts */
+	outb(0x00, ICHREG(chip, PI_CR));
+	outb(0x00, ICHREG(chip, PO_CR));
+	outb(0x00, ICHREG(chip, MC_CR));
+	/* reset channels */
+	outb(ICH_RESETREGS, ICHREG(chip, PI_CR));
+	outb(ICH_RESETREGS, ICHREG(chip, PO_CR));
+	outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
+	/* --- */
+	synchronize_irq();
+      __hw_end:
+	if (chip->bdbars)
+		snd_free_pci_pages(chip->pci, 3 * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr);
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->res_bmport) {
+		release_resource(chip->res_bmport);
+		kfree_nocheck(chip->res_bmport);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static void intel8x0_suspend(intel8x0_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	chip->in_suspend = 1;
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	snd_pcm_suspend_all(chip->pcm);
+	if (chip->pcm_mic)
+		snd_pcm_suspend_all(chip->pcm_mic);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void intel8x0_resume(intel8x0_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	pci_enable_device(chip->pci);
+	snd_intel8x0_chip_init(chip);
+	snd_ac97_resume(chip->ac97);
+
+	chip->in_suspend = 0;
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+#ifndef PCI_OLD_SUSPEND
+static int snd_intel8x0_suspend(struct pci_dev *dev, u32 state)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO);
+	intel8x0_suspend(chip);
+	return 0;
+}
+static int snd_intel8x0_resume(struct pci_dev *dev)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO);
+	intel8x0_resume(chip);
+	return 0;
+}
+#else
+static void snd_intel8x0_suspend(struct pci_dev *dev)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return);
+	intel8x0_suspend(chip);
+}
+static void snd_intel8x0_resume(struct pci_dev *dev)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return);
+	intel8x0_resume(chip);
+}
+#endif
+
+/* callback */
+static int snd_intel8x0_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->power_state_private_data, return -ENXIO);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		intel8x0_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		intel8x0_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+#define INTEL8X0_TESTBUF_SIZE	32768	/* enough large for one shot */
+
+static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
+{
+	snd_pcm_substream_t *subs;
+	unsigned long port;
+	unsigned long pos, t;
+	unsigned long flags;
+	struct timeval start_time, stop_time;
+
+	if (chip->ac97->clock != 48000)
+		return; /* specified in module option */
+
+	subs = chip->pcm->streams[0].substream;
+	if (! subs || subs->dma_bytes < INTEL8X0_TESTBUF_SIZE) {
+		snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
+		return;
+	}
+	chip->playback.physbuf = subs->dma_addr;
+	chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE;
+	chip->playback.substream = NULL; /* don't process interrupts */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* set rate */
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000);
+	snd_intel8x0_setup_periods(chip, &chip->playback);
+	port = chip->bmport + chip->playback.reg_offset;
+	outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */
+	do_gettimeofday(&start_time);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ / 20);
+#else
+	/* FIXME: schedule() can take too long time and overlap the boundary.. */
+	mdelay(50);
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* check the position */
+	pos = chip->playback.fragsize1;
+	if (chip->device_type == DEVICE_SIS)
+		pos -= inw(ICHREG2(chip,chip->reg_po_picb));
+	else
+		pos -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
+	pos += chip->playback.position;
+	do_gettimeofday(&stop_time);
+	outb(0, port + ICH_REG_PI_CR); /* stop */
+	/* reset whole DMA things */
+	while (!(inb(port + chip->reg_pi_sr) & ICH_DCH))
+		;
+	outb(ICH_RESETREGS, port + ICH_REG_PI_CR);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	t = stop_time.tv_sec - start_time.tv_sec;
+	t *= 1000000;
+	if (stop_time.tv_usec < start_time.tv_usec)
+		t -= start_time.tv_usec - stop_time.tv_usec;
+	else
+		t += stop_time.tv_usec - start_time.tv_usec;
+	if (t == 0) {
+		snd_printk("?? calculation error..\n");
+		return;
+	}
+	pos = (pos / 4) * 1000;
+	pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
+	if ((pos > 40000 && pos < 47500) ||
+	    (pos > 48500 && pos < 50000)) {
+		chip->ac97->clock = (chip->ac97->clock * 48000) / pos;
+		printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock);
+	}
+}
+
+static int snd_intel8x0_dev_free(snd_device_t *device)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, device->device_data, return -ENXIO);
+	return snd_intel8x0_free(chip);
+}
+
+static int __devinit snd_intel8x0_create(snd_card_t * card,
+				      struct pci_dev *pci,
+				      unsigned long device_type,
+				      intel8x0_t ** r_intel8x0)
+{
+	intel8x0_t *chip;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_intel8x0_dev_free,
+	};
+	char name[32];
+
+	*r_intel8x0 = NULL;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = snd_magic_kcalloc(intel8x0_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->ac97_lock);
+	chip->device_type = device_type;
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->port = pci_resource_start(pci, 0);
+	sprintf(name, "%s - AC'97", card->shortname);
+	if ((chip->res_port = request_region(chip->port, 256, name)) == NULL) {
+		snd_intel8x0_free(chip);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
+		return -EBUSY;
+	}
+	sprintf(name, "%s - Controller", card->shortname);
+	chip->bmport = pci_resource_start(pci, 1);
+	if ((chip->res_bmport = request_region(chip->bmport, 64, name)) == NULL) {
+		snd_intel8x0_free(chip);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmport, chip->bmport + 64 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+		snd_intel8x0_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	pci_set_master(pci);
+	synchronize_irq();
+
+	/* initialize offsets */
+	chip->reg_pi_sr = ICH_REG_PI_SR;
+	chip->reg_pi_picb = ICH_REG_PI_PICB;
+	chip->reg_po_sr = ICH_REG_PO_SR;
+	chip->reg_po_picb = ICH_REG_PO_PICB;
+	chip->reg_mc_sr = ICH_REG_MC_SR;
+	chip->reg_mc_picb = ICH_REG_MC_PICB;
+	if (device_type == DEVICE_SIS) {
+		chip->reg_pi_sr = ICH_REG_PI_PICB;
+		chip->reg_pi_picb = ICH_REG_PI_SR;
+		chip->reg_po_sr = ICH_REG_PO_PICB;
+		chip->reg_po_picb = ICH_REG_PO_SR;
+		chip->reg_mc_sr = ICH_REG_MC_PICB;
+		chip->reg_mc_picb = ICH_REG_MC_SR;
+	}
+	chip->playback.reg_offset = 0x10;
+	chip->capture.reg_offset = 0;
+	chip->capture_mic.reg_offset = 0x20;
+
+	/* allocate buffer descriptor lists */
+	/* the start of each lists must be aligned to 8 bytes */
+	chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
+	if (chip->bdbars == NULL) {
+		snd_intel8x0_free(chip);
+		return -ENOMEM;
+	}
+	/* tables must be aligned to 8 bytes here, but the kernel pages
+	   are much bigger, so we don't care (on i386) */
+#ifndef __i386__
+	/* .. not sure on other architectures, so we check now. */
+	if (chip->bdbars_addr & ~((dma_addr_t)0xffffffff | 0x07)) {
+		snd_printk("invalid i/o port address %lx\n", (unsigned long)chip->bdbars_addr);
+		snd_intel8x0_free(chip);
+		return -ENOMEM;
+	}
+#endif
+	chip->playback.bdbar = chip->bdbars; /* crop to 32bit */
+	chip->playback.bdbar_addr = (unsigned int)chip->bdbars_addr;
+	chip->capture.bdbar = chip->playback.bdbar + ICH_MAX_FRAGS * 2;
+	chip->capture.bdbar_addr = chip->playback.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2;
+	chip->capture_mic.bdbar = chip->capture.bdbar + ICH_MAX_FRAGS * 2;
+	chip->capture_mic.bdbar_addr = chip->capture.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2;
+
+	if ((err = snd_intel8x0_chip_init(chip)) < 0) {
+		snd_intel8x0_free(chip);
+		return err;
+	}
+
+#ifdef CONFIG_PM
+	card->set_power_state = snd_intel8x0_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_intel8x0_free(chip);
+		return err;
+	}
+
+	*r_intel8x0 = chip;
+	return 0;
+}
+
+static struct shortname_table {
+	unsigned int id;
+	const char *s;
+} shortnames[] __devinitdata = {
+	{ PCI_DEVICE_ID_INTEL_82801, "Intel ICH 82801AA" },
+	{ PCI_DEVICE_ID_INTEL_82901, "Intel ICH 82901AB" },
+	{ PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" },
+	{ PCI_DEVICE_ID_INTEL_ICH3, "Intel ICH3" },
+	{ PCI_DEVICE_ID_SI_7012, "SiS SI7012" },
+	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" },
+	{ 0x1022, "AMD-8111" },
+	{ 0, 0 },
+};
+
+static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
+					const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	intel8x0_t *chip;
+	int pcm_dev = 0, err;
+	struct shortname_table *name;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	strcpy(card->driver, "ICH");
+	strcpy(card->shortname, "Intel ICH");
+	for (name = shortnames; name->id; name++) {
+		if (pci->device == name->id) {
+			strcpy(card->shortname, name->s);
+			break;
+		}
+	}
+
+	if ((err = snd_intel8x0_create(card, pci, id->driver_data, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_intel8x0_mixer(chip, snd_ac97_clock[dev])) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_intel8x0_pcm(chip, pcm_dev++, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (chip->ac97->ext_id & 0x0008) {	/* MIC VRM */
+		if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	
+	if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+					       snd_mpu_port[dev], 0,
+					       -1, 0, &chip->rmidi)) < 0) {
+			printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]);
+			snd_mpu_port[dev] = 0;
+		}
+	} else
+		snd_mpu_port[dev] = 0;
+
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, chip->port, chip->irq);
+
+	if (! snd_ac97_clock[dev])
+		intel8x0_measure_ac97_clock(chip);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "Intel ICH",
+	id_table: snd_intel8x0_ids,
+	probe: snd_intel8x0_probe,
+	remove: __devexit_p(snd_intel8x0_remove),
+#ifdef CONFIG_PM
+	suspend: snd_intel8x0_suspend,
+	resume: snd_intel8x0_resume,
+#endif
+};
+
+
+#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI)
+/*
+ * initialize joystick/midi addresses
+ */
+
+static int __devinit snd_intel8x0_joystick_probe(struct pci_dev *pci,
+						 const struct pci_device_id *id)
+{
+	static int dev = 0;
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	if (snd_joystick_port[dev] > 0 || snd_mpu_port[dev] > 0) {
+		u16 val;
+		pci_read_config_word(pci, 0xe6, &val);
+		if (snd_joystick_port[dev] > 0)
+			val |= 0x100;
+		if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330)
+			val |= 0x20;
+		pci_write_config_word(pci, 0xe6, val | 0x100);
+
+		if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) {
+			u8 b;
+			pci_read_config_byte(pci, 0xe2, &b);
+			if (snd_mpu_port[dev] == 0x300)
+				b |= 0x08;
+			else
+				b &= ~0x08;
+			pci_write_config_byte(pci, 0xe2, b);
+		}
+	}
+	return 0;
+}
+
+static struct pci_device_id snd_intel8x0_joystick_ids[] __devinitdata = {
+	{ 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* 82801AA */
+	{ 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* 82901AB */
+	{ 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2 */
+	{ 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2M */
+	{ 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* ICH3 */
+	// { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* 440MX */
+	// { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* SI7012 */
+	{ 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* NFORCE */
+	{ 0, }
+};
+
+static struct pci_driver joystick_driver = {
+	name: "Intel ICH Joystick",
+	id_table: snd_intel8x0_joystick_ids,
+	probe: snd_intel8x0_joystick_probe,
+};
+#endif
+
+static int __init alsa_card_intel8x0_init(void)
+{
+	int err;
+
+        if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Intel ICH soundcard not found or device busy\n");
+#endif
+                return err;
+        }
+#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI)
+	pci_module_init(&joystick_driver);
+#endif
+        return 0;
+
+}
+
+static void __exit alsa_card_intel8x0_exit(void)
+{
+	pci_unregister_driver(&driver);
+#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI)
+	pci_unregister_driver(&joystick_driver);
+#endif
+}
+
+module_init(alsa_card_intel8x0_init)
+module_exit(alsa_card_intel8x0_exit)
+
+#ifndef MODULE
+
+/* format is: snd-intel8x0=snd_enable,snd_index,snd_id,snd_ac97_clock */
+
+static int __init alsa_card_intel8x0_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-intel8x0=", alsa_card_intel8x0_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/korg1212/Makefile linux-2.4.19-pre5-mjc/sound/pci/korg1212/Makefile
--- linux/sound/pci/korg1212/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/korg1212/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _korg1212.o
+
+list-multi   := snd-korg1212.o
+
+snd-korg1212-objs := korg1212.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o
+
+include $(TOPDIR)/Rules.make
+
+snd-korg1212.o: $(snd-korg1212-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-korg1212-objs)
diff -Nru linux/sound/pci/korg1212/korg1212-firmware.h linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212-firmware.h
--- linux/sound/pci/korg1212/korg1212-firmware.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212-firmware.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,987 @@
+static char dspCode [] = {
+0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
+0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff,
+0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff,
+0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
+0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff,
+0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff,
+0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
+0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff,
+0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,
+0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff,
+0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,
+0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
+0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
+0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
+0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
+0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff,
+0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff,
+0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff,
+0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,
+0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
+0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff,
+0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
+0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,
+0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff,
+0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff,
+0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
+0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
+0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,
+0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,
+0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
+0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
+0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
+0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
+0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,
+0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff,
+0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,
+0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
+0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
+0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
+0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
+0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,
+0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff,
+0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,
+0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,
+0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
+0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff,
+0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
+0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
+0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff,
+0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff,
+0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,
+0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff,
+0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
+0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
+0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
+0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
+0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,
+0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,
+0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,
+0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
+0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
+0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,
+0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,
+0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,
+0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,
+0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
+0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff,
+0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
+0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
+0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
+0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
+0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
+0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
+0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,
+0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
+0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff,
+0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
+0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
+0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,
+0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff,
+0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff,
+0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
+0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,
+0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,
+0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff,
+0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,
+0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,
+0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
+0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff,
+0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
+0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
+0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,
+0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,
+0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff,
+0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff,
+0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff };
diff -Nru linux/sound/pci/korg1212/korg1212.c linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212.c
--- linux/sound/pci/korg1212/korg1212.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2350 @@
+/*
+ *   Driver for the Korg 1212 IO PCI card
+ *
+ *	Copyright (c) 2001 Haroldo Gamal <gamal@alternex.com.br>
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define DEBUG                    1
+//#define LARGEALLOC               1
+#define PRINTK	printk
+
+// ----------------------------------------------------------------------------
+// the following enum defines the valid states of the Korg 1212 I/O card.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_STATE_NONEXISTENT,		// there is no card here
+   K1212_STATE_UNINITIALIZED,		// the card is awaiting DSP download
+   K1212_STATE_DSP_IN_PROCESS,		// the card is currently downloading its DSP code
+   K1212_STATE_DSP_COMPLETE,		// the card has finished the DSP download
+   K1212_STATE_READY,			// the card can be opened by an application.  Any application
+					//    requests prior to this state should fail.  Only an open
+					//    request can be made at this state.
+   K1212_STATE_OPEN,			// an application has opened the card
+   K1212_STATE_SETUP,			// the card has been setup for play
+   K1212_STATE_PLAYING,			// the card is playing
+   K1212_STATE_MONITOR,			// the card is in the monitor mode
+   K1212_STATE_CALIBRATING,		// the card is currently calibrating
+   K1212_STATE_ERRORSTOP,		// the card has stopped itself because of an error and we
+					//    are in the process of cleaning things up.
+   K1212_STATE_MAX_STATE		// state values of this and beyond are invalid
+} CardState;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants written to the card's
+// host-to-card doorbell to initiate a command.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_DB_RequestForData        = 0,    // sent by the card to request a buffer fill.
+   K1212_DB_TriggerPlay           = 1,    // starts playback/record on the card.
+   K1212_DB_SelectPlayMode        = 2,    // select monitor, playback setup, or stop.
+   K1212_DB_ConfigureBufferMemory = 3,    // tells card where the host audio buffers are.
+   K1212_DB_RequestAdatTimecode   = 4,    // asks the card for the latest ADAT timecode value.
+   K1212_DB_SetClockSourceRate    = 5,    // sets the clock source and rate for the card.
+   K1212_DB_ConfigureMiscMemory   = 6,    // tells card where other buffers are.
+   K1212_DB_TriggerFromAdat       = 7,    // tells card to trigger from Adat at a specific
+                                          //    timecode value.
+   K1212_DB_RebootCard            = 0xA0, // instructs the card to reboot.
+   K1212_DB_BootFromDSPPage4      = 0xA4, // instructs the card to boot from the DSP microcode
+                                          //    on page 4 (local page to card).
+   K1212_DB_DSPDownloadDone       = 0xAE, // sent by the card to indicate the download has
+                                          //    completed.
+   K1212_DB_StartDSPDownload      = 0xAF  // tells the card to download its DSP firmware.
+} korg1212_dbcnst_t;
+
+#define K1212_ISRCODE_DMAERROR      0x80
+#define K1212_ISRCODE_CARDSTOPPED   0x81
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines return codes for DeviceIoControl() calls
+// to the Korg 1212 I/O driver.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_CMDRET_Success         = 0,   // command was successfully placed
+   K1212_CMDRET_DIOCFailure,           // the DeviceIoControl call failed
+   K1212_CMDRET_PMFailure,             // the protected mode call failed
+   K1212_CMDRET_FailUnspecified,       // unspecified failure
+   K1212_CMDRET_FailBadState,          // the specified command can not be given in
+                                       //    the card's current state. (or the wave device's
+                                       //    state)
+   K1212_CMDRET_CardUninitialized,     // the card is uninitialized and cannot be used
+   K1212_CMDRET_BadIndex,              // an out of range card index was specified
+   K1212_CMDRET_BadHandle,             // an invalid card handle was specified
+   K1212_CMDRET_NoFillRoutine,         // a play request has been made before a fill routine set
+   K1212_CMDRET_FillRoutineInUse,      // can't set a new fill routine while one is in use
+   K1212_CMDRET_NoAckFromCard,         // the card never acknowledged a command
+   K1212_CMDRET_BadParams,             // bad parameters were provided by the caller
+
+   // --------------------------------------------------------------
+   // the following return errors are specific to the wave device
+   // driver interface.  These will not be encountered by users of
+   // the 32 bit DIOC interface (a.k.a. custom or native API).
+   // --------------------------------------------------------------
+   K1212_CMDRET_BadDevice,             // the specified wave device was out of range
+   K1212_CMDRET_BadFormat              // the specified wave format is unsupported
+} snd_korg1212rc;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants used to select the play
+// mode for the card in the SelectPlayMode command.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_MODE_SetupPlay  = 0x00000001,     // provides card with pre-play information
+   K1212_MODE_MonitorOn  = 0x00000002,     // tells card to turn on monitor mode
+   K1212_MODE_MonitorOff = 0x00000004,     // tells card to turn off monitor mode
+   K1212_MODE_StopPlay   = 0x00000008      // stops playback on the card
+} PlayModeSelector;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants used to select the monitor
+// mode for the card in the SetMonitorMode command.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_MONMODE_Off  = 0,     // tells card to turn off monitor mode
+   K1212_MONMODE_On            // tells card to turn on monitor mode
+} MonitorModeSelector;
+
+#define MAILBOX0_OFFSET      0x40	// location of mailbox 0 relative to base address
+#define MAILBOX1_OFFSET      0x44	// location of mailbox 1 relative to base address
+#define MAILBOX2_OFFSET      0x48	// location of mailbox 2 relative to base address
+#define MAILBOX3_OFFSET      0x4c	// location of mailbox 3 relative to base address
+#define OUT_DOORBELL_OFFSET  0x60	// location of PCI to local doorbell "
+#define IN_DOORBELL_OFFSET   0x64	// location of local to PCI doorbell "
+#define STATUS_REG_OFFSET    0x68	// location of interrupt control/status register "
+#define PCI_CONTROL_OFFSET   0x6c	// location of the EEPROM, PCI, User I/O, init control
+					//    register
+#define SENS_CONTROL_OFFSET  0x6e	// location of the input sensitivity setting register.
+					//    this is the upper word of the PCI control reg.
+#define DEV_VEND_ID_OFFSET   0x70	// location of the device and vendor ID register
+
+#define COMMAND_ACK_DELAY    13        // number of RTC ticks to wait for an acknowledgement
+                                        //    from the card after sending a command.
+#define INTERCOMMAND_DELAY   40
+#define MAX_COMMAND_RETRIES  5         // maximum number of times the driver will attempt
+                                       //    to send a command before giving up.
+#define COMMAND_ACK_MASK     0x8000    // the MSB is set in the command acknowledgment from
+                                        //    the card.
+#define DOORBELL_VAL_MASK    0x00FF    // the doorbell value is one byte
+
+#define CARD_BOOT_DELAY_IN_MS  10
+
+#define DSP_BOOT_DELAY_IN_MS   200
+
+#define kNumBuffers		8
+#define k1212MaxCards		4
+#define k1212NumWaveDevices	6
+#define k16BitChannels		10
+#define k32BitChannels		2
+#define kAudioChannels		(k16BitChannels + k32BitChannels)
+#define kPlayBufferFrames	1024
+
+#define K1212_CHANNELS     	16
+#define K1212_FRAME_SIZE        (sizeof(KorgAudioFrame))
+#define K1212_MAX_SAMPLES	(kPlayBufferFrames*kNumBuffers)
+#define K1212_PERIODS		(K1212_BUF_SIZE/K1212_BLOCK_SIZE)
+#define K1212_PERIOD_BYTES	(K1212_BLOCK_SIZE)
+#define K1212_BLOCK_SIZE        (K1212_FRAME_SIZE*kPlayBufferFrames)
+#define K1212_BUF_SIZE          (K1212_BLOCK_SIZE*kNumBuffers)
+
+#define k1212MinADCSens     0x7f
+#define k1212MaxADCSens     0x00
+#define k1212MaxVolume      0x7fff
+#define k1212MaxWaveVolume  0xffff
+#define k1212MinVolume      0x0000
+#define k1212MaxVolInverted 0x8000
+
+// -----------------------------------------------------------------
+// the following bits are used for controlling interrupts in the
+// interrupt control/status reg
+// -----------------------------------------------------------------
+#define  PCI_INT_ENABLE_BIT               0x00000100
+#define  PCI_DOORBELL_INT_ENABLE_BIT      0x00000200
+#define  LOCAL_INT_ENABLE_BIT             0x00010000
+#define  LOCAL_DOORBELL_INT_ENABLE_BIT    0x00020000
+#define  LOCAL_DMA1_INT_ENABLE_BIT        0x00080000
+
+// -----------------------------------------------------------------
+// the following bits are defined for the PCI command register
+// -----------------------------------------------------------------
+#define  PCI_CMD_MEM_SPACE_ENABLE_BIT     0x0002
+#define  PCI_CMD_IO_SPACE_ENABLE_BIT      0x0001
+#define  PCI_CMD_BUS_MASTER_ENABLE_BIT    0x0004
+
+// -----------------------------------------------------------------
+// the following bits are defined for the PCI status register
+// -----------------------------------------------------------------
+#define  PCI_STAT_PARITY_ERROR_BIT        0x8000
+#define  PCI_STAT_SYSTEM_ERROR_BIT        0x4000
+#define  PCI_STAT_MASTER_ABORT_RCVD_BIT   0x2000
+#define  PCI_STAT_TARGET_ABORT_RCVD_BIT   0x1000
+#define  PCI_STAT_TARGET_ABORT_SENT_BIT   0x0800
+
+// ------------------------------------------------------------------------
+// the following constants are used in setting the 1212 I/O card's input
+// sensitivity.
+// ------------------------------------------------------------------------
+#define  SET_SENS_LOCALINIT_BITPOS        15
+#define  SET_SENS_DATA_BITPOS             10
+#define  SET_SENS_CLOCK_BITPOS            8
+#define  SET_SENS_LOADSHIFT_BITPOS        0
+
+#define  SET_SENS_LEFTCHANID              0x00
+#define  SET_SENS_RIGHTCHANID             0x01
+
+#define  K1212SENSUPDATE_DELAY_IN_MS      50
+
+// --------------------------------------------------------------------------
+// WaitRTCTicks
+//
+//    This function waits the specified number of real time clock ticks.
+//    According to the DDK, each tick is ~0.8 microseconds.
+//    The defines following the function declaration can be used for the
+//    numTicksToWait parameter.
+// --------------------------------------------------------------------------
+#define ONE_RTC_TICK         1
+#define SENSCLKPULSE_WIDTH   4
+#define LOADSHIFT_DELAY      4
+#define INTERCOMMAND_DELAY  40
+#define STOPCARD_DELAY      300        // max # RTC ticks for the card to stop once we write
+                                       //    the command register.  (could be up to 180 us)
+#define COMMAND_ACK_DELAY   13         // number of RTC ticks to wait for an acknowledgement
+                                       //    from the card after sending a command.
+
+#include "korg1212-firmware.h"
+
+typedef struct _snd_korg1212 korg1212_t;
+
+typedef u16 K1212Sample;          // channels 0-9 use 16 bit samples
+typedef u32 K1212SpdifSample;     // channels 10-11 use 32 bits - only 20 are sent
+                                  //  across S/PDIF.
+typedef u32 K1212TimeCodeSample;  // holds the ADAT timecode value
+
+typedef enum {
+   K1212_CLKIDX_AdatAt44_1K = 0,    // selects source as ADAT at 44.1 kHz
+   K1212_CLKIDX_AdatAt48K,          // selects source as ADAT at 48 kHz
+   K1212_CLKIDX_WordAt44_1K,        // selects source as S/PDIF at 44.1 kHz
+   K1212_CLKIDX_WordAt48K,          // selects source as S/PDIF at 48 kHz
+   K1212_CLKIDX_LocalAt44_1K,       // selects source as local clock at 44.1 kHz
+   K1212_CLKIDX_LocalAt48K,         // selects source as local clock at 48 kHz
+   K1212_CLKIDX_Invalid             // used to check validity of the index
+} ClockSourceIndex;
+
+typedef enum {
+   K1212_CLKIDX_Adat = 0,    // selects source as ADAT
+   K1212_CLKIDX_Word,        // selects source as S/PDIF
+   K1212_CLKIDX_Local        // selects source as local clock
+} ClockSourceType;
+
+typedef struct KorgAudioFrame {
+   K1212Sample          frameData16[k16BitChannels];
+   K1212SpdifSample     frameData32[k32BitChannels];
+   K1212TimeCodeSample  timeCodeVal;
+} KorgAudioFrame;
+
+typedef struct KorgAudioBuffer {
+   KorgAudioFrame  bufferData[kPlayBufferFrames];     /* buffer definition */
+} KorgAudioBuffer;
+
+typedef struct KorgSharedBuffer {
+#ifdef LARGEALLOC
+   KorgAudioBuffer   playDataBufs[kNumBuffers];
+   KorgAudioBuffer   recordDataBufs[kNumBuffers];
+#endif
+   short             volumeData[kAudioChannels];
+   u32               cardCommand;
+   u16               routeData [kAudioChannels];
+   u32               AdatTimeCode;                 // ADAT timecode value
+} KorgSharedBuffer;
+
+typedef struct SensBits {
+   union {
+      struct {
+         unsigned int leftChanVal:8;
+         unsigned int leftChanId:8;
+      } v;
+      u16  leftSensBits;
+   } l;
+   union {
+      struct {
+         unsigned int rightChanVal:8;
+         unsigned int rightChanId:8;
+      } v;
+      u16  rightSensBits;
+   } r;
+} SensBits;
+
+struct _snd_korg1212 {
+        struct pci_dev *pci;
+        snd_card_t *card;
+        snd_pcm_t *pcm16;
+        int irq;
+
+        spinlock_t    lock;
+
+        wait_queue_head_t wait;
+
+        unsigned long iomem;
+        unsigned long ioport;
+	unsigned long iomem2;
+        unsigned long irqcount;
+        unsigned long inIRQ;
+        unsigned long iobase;
+
+	struct resource *res_iomem;
+	struct resource *res_ioport;
+	struct resource *res_iomem2;
+
+        u32 dspCodeSize;
+        u32 dspMemPhy;          // DSP memory block handle (Physical Address)
+        void * dspMemPtr;       //            block memory (Virtual Address)
+
+	u32 DataBufsSize;
+
+        KorgAudioBuffer  * playDataBufsPtr;
+        KorgAudioBuffer  * recordDataBufsPtr;
+
+	KorgSharedBuffer * sharedBufferPtr;
+	u32 RecDataPhy;
+	u32 PlayDataPhy;
+	unsigned long sharedBufferPhy;
+	u32 VolumeTablePhy;
+	u32 RoutingTablePhy;
+	u32 AdatTimeCodePhy;
+
+        u32 * statusRegPtr;	     // address of the interrupt status/control register
+        u32 * outDoorbellPtr;	     // address of the host->card doorbell register
+        u32 * inDoorbellPtr;	     // address of the card->host doorbell register
+        u32 * mailbox0Ptr;	     // address of mailbox 0 on the card
+        u32 * mailbox1Ptr;	     // address of mailbox 1 on the card
+        u32 * mailbox2Ptr;	     // address of mailbox 2 on the card
+        u32 * mailbox3Ptr;	     // address of mailbox 3 on the card
+        u32 * controlRegPtr;	     // address of the EEPROM, PCI, I/O, Init ctrl reg
+        u16 * sensRegPtr;	     // address of the sensitivity setting register
+        u32 * idRegPtr;		     // address of the device and vendor ID registers
+
+
+        size_t periodsize;
+        size_t currentBuffer;
+
+        snd_pcm_substream_t *playback_substream;
+        snd_pcm_substream_t *capture_substream;
+        snd_info_entry_t * proc_entry;
+
+ 	CardState cardState;
+        int running;
+        int idleMonitorOn;           // indicates whether the card is in idle monitor mode.
+        u32 cmdRetryCount;           // tracks how many times we have retried sending to the card.
+
+        ClockSourceIndex clkSrcRate; // sample rate and clock source
+
+        ClockSourceType clkSource;   // clock source
+        int clkRate;                 // clock rate
+
+        int volumePhase[kAudioChannels];
+
+        u16 leftADCInSens;           // ADC left channel input sensitivity
+        u16 rightADCInSens;          // ADC right channel input sensitivity
+};
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("korg1212");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{KORG,korg1212}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	   /* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Korg 1212 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Korg 1212 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Korg 1212 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
+
+static struct pci_device_id snd_korg1212_ids[] __devinitdata = {
+	{ 0x10b5, 0x906d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ 0, }
+};
+
+static char* stateName[] = {
+		        "Non-existent",
+                        "Uninitialized",
+                        "DSP download in process",
+                        "DSP download complete",
+                        "Ready",
+                        "Open",
+                        "Setup for play",
+                        "Playing",
+                        "Monitor mode on",
+                        "Calibrating"
+                        "Invalid"
+};
+
+static char* clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
+
+static char* clockSourceName[] = {
+                        "ADAT at 44.1 kHz",
+                        "ADAT at 48 kHz",
+                        "S/PDIF at 44.1 kHz",
+                        "S/PDIF at 48 kHz",
+                        "local clock at 44.1 kHz",
+                        "local clock at 48 kHz"
+};
+
+static char* channelName[] = {
+                        "ADAT-1",
+                        "ADAT-2",
+                        "ADAT-3",
+                        "ADAT-4",
+                        "ADAT-5",
+                        "ADAT-6",
+                        "ADAT-7",
+                        "ADAT-8",
+                        "Analog-L",
+                        "Analog-R",
+                        "SPDIF-L",
+                        "SPDIF-R",
+};
+
+u16 ClockSourceSelector[] = {0x8000,   // selects source as ADAT at 44.1 kHz
+                             0x0000,   // selects source as ADAT at 48 kHz
+                             0x8001,   // selects source as S/PDIF at 44.1 kHz
+                             0x0001,   // selects source as S/PDIF at 48 kHz
+                             0x8002,   // selects source as local clock at 44.1 kHz
+                             0x0002    // selects source as local clock at 48 kHz
+                            };
+
+snd_korg1212rc rc;
+
+
+MODULE_DEVICE_TABLE(pci, snd_korg1212_ids);
+
+typedef union swap_u32 { unsigned char c[4]; u32 i; } swap_u32;
+
+#ifdef SNDRV_BIG_ENDIAN
+static u32 LowerWordSwap(u32 swappee)
+#else
+static u32 UpperWordSwap(u32 swappee)
+#endif
+{
+   swap_u32 retVal, swapper;
+
+   swapper.i = swappee;
+   retVal.c[2] = swapper.c[3];
+   retVal.c[3] = swapper.c[2];
+   retVal.c[1] = swapper.c[1];
+   retVal.c[0] = swapper.c[0];
+
+   return retVal.i;
+}
+
+#ifdef SNDRV_BIG_ENDIAN
+static u32 UpperWordSwap(u32 swappee)
+#else
+static u32 LowerWordSwap(u32 swappee)
+#endif
+{
+   swap_u32 retVal, swapper;
+
+   swapper.i = swappee;
+   retVal.c[2] = swapper.c[2];
+   retVal.c[3] = swapper.c[3];
+   retVal.c[1] = swapper.c[0];
+   retVal.c[0] = swapper.c[1];
+
+   return retVal.i;
+}
+
+#if 0 /* not used */
+
+static u32 EndianSwap(u32 swappee)
+{
+   swap_u32 retVal, swapper;
+
+   swapper.i = swappee;
+   retVal.c[0] = swapper.c[3];
+   retVal.c[1] = swapper.c[2];
+   retVal.c[2] = swapper.c[1];
+   retVal.c[3] = swapper.c[0];
+
+   return retVal.i;
+}
+
+#endif /* not used */
+
+void TickDelay(int time)
+{
+        udelay(time);
+}
+
+#define SetBitInWord(theWord,bitPosition)       (*theWord) |= (0x0001 << bitPosition)
+#define SetBitInDWord(theWord,bitPosition)      (*theWord) |= (0x00000001 << bitPosition)
+#define ClearBitInWord(theWord,bitPosition)     (*theWord) &= ~(0x0001 << bitPosition)
+#define ClearBitInDWord(theWord,bitPosition)    (*theWord) &= ~(0x00000001 << bitPosition)
+
+static snd_korg1212rc snd_korg1212_Send1212Command(korg1212_t *korg1212, korg1212_dbcnst_t doorbellVal,
+                            u32 mailBox0Val, u32 mailBox1Val, u32 mailBox2Val, u32 mailBox3Val)
+{
+        u32 retryCount;
+        u16 mailBox3Lo;
+
+        if (korg1212->outDoorbellPtr) {
+#ifdef DEBUG
+		PRINTK("DEBUG: Card <- 0x%08x 0x%08x [%s]\n", doorbellVal, mailBox0Val, stateName[korg1212->cardState]);
+#endif
+                for (retryCount = 0; retryCount < MAX_COMMAND_RETRIES; retryCount++) {
+
+                        writel(mailBox3Val, korg1212->mailbox3Ptr);
+                        writel(mailBox2Val, korg1212->mailbox2Ptr);
+                        writel(mailBox1Val, korg1212->mailbox1Ptr);
+                        writel(mailBox0Val, korg1212->mailbox0Ptr);
+                        writel(doorbellVal, korg1212->outDoorbellPtr);  // interrupt the card
+
+                        // --------------------------------------------------------------
+                        // the reboot command will not give an acknowledgement.
+                        // --------------------------------------------------------------
+                        switch (doorbellVal) {
+                                case K1212_DB_RebootCard:
+                                case K1212_DB_BootFromDSPPage4:
+                                case K1212_DB_StartDSPDownload:
+                                        return K1212_CMDRET_Success;
+                                default:
+                                        break;
+                        }
+
+                        // --------------------------------------------------------------
+                        // See if the card acknowledged the command.  Wait a bit, then
+                        // read in the low word of mailbox3.  If the MSB is set and the
+                        // low byte is equal to the doorbell value, then it ack'd.
+                        // --------------------------------------------------------------
+                        TickDelay(COMMAND_ACK_DELAY);
+                        mailBox3Lo = readl(korg1212->mailbox3Ptr);
+                        if (mailBox3Lo & COMMAND_ACK_MASK) {
+                                if ((mailBox3Lo & DOORBELL_VAL_MASK) == (doorbellVal & DOORBELL_VAL_MASK)) {
+                                        korg1212->cmdRetryCount += retryCount;
+                                        return K1212_CMDRET_Success;
+                                }
+                        }
+                }
+                korg1212->cmdRetryCount += retryCount;
+                return K1212_CMDRET_NoAckFromCard;
+        } else {
+                return K1212_CMDRET_CardUninitialized;
+        }
+}
+
+static void snd_korg1212_WaitForCardStopAck(korg1212_t *korg1212)
+{
+        unsigned long endtime = jiffies + 20 * HZ;
+
+#ifdef DEBUG
+        PRINTK("DEBUG: WaitForCardStopAck [%s]\n", stateName[korg1212->cardState]);
+#endif // DEBUG
+
+        if (korg1212->inIRQ)
+                return;
+
+        do {
+                if (readl(&korg1212->sharedBufferPtr->cardCommand) == 0)
+                        return;
+                if (!korg1212->inIRQ)
+                        schedule();
+        } while (jiffies < endtime);
+
+        writel(0, &korg1212->sharedBufferPtr->cardCommand);
+}
+
+static void snd_korg1212_TurnOnIdleMonitor(korg1212_t *korg1212)
+{
+        TickDelay(INTERCOMMAND_DELAY);
+        korg1212->idleMonitorOn = 1;
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                        K1212_MODE_MonitorOn, 0, 0, 0);
+}
+
+static void snd_korg1212_TurnOffIdleMonitor(korg1212_t *korg1212)
+{
+        if (korg1212->idleMonitorOn) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                snd_korg1212_WaitForCardStopAck(korg1212);
+                korg1212->idleMonitorOn = 0;
+        }
+}
+
+static void snd_korg1212_setCardState(korg1212_t * korg1212, CardState csState)
+{
+        switch (csState) {
+                case K1212_STATE_READY:
+                        snd_korg1212_TurnOnIdleMonitor(korg1212);
+                        break;
+
+                case K1212_STATE_OPEN:
+                        snd_korg1212_TurnOffIdleMonitor(korg1212);
+                        break;
+
+                default:
+                        break;
+        }
+
+        korg1212->cardState = csState;
+}
+
+static int snd_korg1212_OpenCard(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: OpenCard [%s]\n", stateName[korg1212->cardState]);
+#endif
+        snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+        return 1;
+}
+
+static int snd_korg1212_CloseCard(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: CloseCard [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        if (korg1212->cardState == K1212_STATE_SETUP) {
+                rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                K1212_MODE_StopPlay, 0, 0, 0);
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: CloseCard - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+                if (rc != K1212_CMDRET_Success)
+                        return 0;
+        } else if (korg1212->cardState > K1212_STATE_SETUP) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                snd_korg1212_WaitForCardStopAck(korg1212);
+        }
+
+        if (korg1212->cardState > K1212_STATE_READY)
+                snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
+
+        return 0;
+}
+
+static int snd_korg1212_SetupForPlay(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: SetupForPlay [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        snd_korg1212_setCardState(korg1212, K1212_STATE_SETUP);
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                        K1212_MODE_SetupPlay, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: SetupForPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+        if (rc != K1212_CMDRET_Success) {
+                return 0;
+        }
+        return 1;
+}
+
+static int snd_korg1212_TriggerPlay(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: TriggerPlay [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        snd_korg1212_setCardState(korg1212, K1212_STATE_PLAYING);
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_TriggerPlay, 0, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: TriggerPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        if (rc != K1212_CMDRET_Success) {
+                return 0;
+        }
+        return 1;
+}
+
+static int snd_korg1212_StopPlay(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: StopPlay [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        if (korg1212->cardState != K1212_STATE_ERRORSTOP) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                snd_korg1212_WaitForCardStopAck(korg1212);
+        }
+        snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+        return 1;
+}
+
+static void snd_korg1212_EnableCardInterrupts(korg1212_t * korg1212)
+{
+	* korg1212->statusRegPtr = PCI_INT_ENABLE_BIT            |
+                                   PCI_DOORBELL_INT_ENABLE_BIT   |
+                                   LOCAL_INT_ENABLE_BIT          |
+                                   LOCAL_DOORBELL_INT_ENABLE_BIT |
+                                   LOCAL_DMA1_INT_ENABLE_BIT;
+}
+
+#if 0 /* not used */
+
+static int snd_korg1212_SetMonitorMode(korg1212_t *korg1212, MonitorModeSelector mode)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: SetMonitorMode [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        switch (mode) {
+                case K1212_MONMODE_Off:
+                        if (korg1212->cardState != K1212_STATE_MONITOR) {
+                                return 0;
+                        } else {
+                                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                                snd_korg1212_WaitForCardStopAck(korg1212);
+                                snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+                        }
+                        break;
+
+                case K1212_MONMODE_On:
+                        if (korg1212->cardState != K1212_STATE_OPEN) {
+                                return 0;
+                        } else {
+                                snd_korg1212_setCardState(korg1212, K1212_STATE_MONITOR);
+                                rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                                        K1212_MODE_MonitorOn, 0, 0, 0);
+                                if (rc != K1212_CMDRET_Success) {
+                                        return 0;
+                                }
+                        }
+                        break;
+
+                default:
+                        return 0;
+        }
+
+        return 1;
+}
+
+#endif /* not used */
+
+static int snd_korg1212_SetRate(korg1212_t *korg1212, int rate)
+{
+        static ClockSourceIndex s44[] = { K1212_CLKIDX_AdatAt44_1K,
+                                          K1212_CLKIDX_WordAt44_1K,
+                                          K1212_CLKIDX_LocalAt44_1K };
+        static ClockSourceIndex s48[] = {
+                                          K1212_CLKIDX_AdatAt48K,
+                                          K1212_CLKIDX_WordAt48K,
+                                          K1212_CLKIDX_LocalAt48K };
+        int parm;
+
+        switch(rate) {
+                case 44100:
+                parm = s44[korg1212->clkSource];
+                break;
+
+                case 48000:
+                parm = s48[korg1212->clkSource];
+                break;
+
+                default:
+                return -EINVAL;
+        }
+
+        korg1212->clkSrcRate = parm;
+        korg1212->clkRate = rate;
+
+	TickDelay(INTERCOMMAND_DELAY);
+	rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate,
+					  ClockSourceSelector[korg1212->clkSrcRate],
+					  0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        return 0;
+}
+
+static int snd_korg1212_SetClockSource(korg1212_t *korg1212, int source)
+{
+
+        if (source<0 || source >2)
+           return -EINVAL;
+
+        korg1212->clkSource = source;
+
+        snd_korg1212_SetRate(korg1212, korg1212->clkRate);
+
+        return 0;
+}
+
+static void snd_korg1212_DisableCardInterrupts(korg1212_t *korg1212)
+{
+	* korg1212->statusRegPtr = 0;
+}
+
+static int snd_korg1212_WriteADCSensitivity(korg1212_t *korg1212)
+{
+        SensBits  sensVals;
+        int       bitPosition;
+        int       channel;
+        int       clkIs48K;
+        int       monModeSet;
+        u16       controlValue;    // this keeps the current value to be written to
+                                   //  the card's eeprom control register.
+        u16       count;
+
+#ifdef DEBUG
+	PRINTK("DEBUG: WriteADCSensivity [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        // ----------------------------------------------------------------------------
+        // initialize things.  The local init bit is always set when writing to the
+        // card's control register.
+        // ----------------------------------------------------------------------------
+        controlValue = 0;
+        SetBitInWord(&controlValue, SET_SENS_LOCALINIT_BITPOS);    // init the control value
+
+        // ----------------------------------------------------------------------------
+        // make sure the card is not in monitor mode when we do this update.
+        // ----------------------------------------------------------------------------
+        if (korg1212->cardState == K1212_STATE_MONITOR || korg1212->idleMonitorOn) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                monModeSet = 1;
+                snd_korg1212_WaitForCardStopAck(korg1212);
+        } else
+                monModeSet = 0;
+
+        // ----------------------------------------------------------------------------
+        // we are about to send new values to the card, so clear the new values queued
+        // flag.  Also, clear out mailbox 3, so we don't lockup.
+        // ----------------------------------------------------------------------------
+        writel(0, korg1212->mailbox3Ptr);
+        TickDelay(LOADSHIFT_DELAY);
+
+        // ----------------------------------------------------------------------------
+        // determine whether we are running a 48K or 44.1K clock.  This info is used
+        // later when setting the SPDIF FF after the volume has been shifted in.
+        // ----------------------------------------------------------------------------
+        switch (korg1212->clkSrcRate) {
+                case K1212_CLKIDX_AdatAt44_1K:
+                case K1212_CLKIDX_WordAt44_1K:
+                case K1212_CLKIDX_LocalAt44_1K:
+                        clkIs48K = 0;
+                        break;
+
+                case K1212_CLKIDX_WordAt48K:
+                case K1212_CLKIDX_AdatAt48K:
+                case K1212_CLKIDX_LocalAt48K:
+                default:
+                        clkIs48K = 1;
+                        break;
+        }
+
+        // ----------------------------------------------------------------------------
+        // start the update.  Setup the bit structure and then shift the bits.
+        // ----------------------------------------------------------------------------
+        sensVals.l.v.leftChanId   = SET_SENS_LEFTCHANID;
+        sensVals.r.v.rightChanId  = SET_SENS_RIGHTCHANID;
+        sensVals.l.v.leftChanVal  = korg1212->leftADCInSens;
+        sensVals.r.v.rightChanVal = korg1212->rightADCInSens;
+
+        // ----------------------------------------------------------------------------
+        // now start shifting the bits in.  Start with the left channel then the right.
+        // ----------------------------------------------------------------------------
+        for (channel = 0; channel < 2; channel++) {
+
+                // ----------------------------------------------------------------------------
+                // Bring the load/shift line low, then wait - the spec says >150ns from load/
+                // shift low to the first rising edge of the clock.
+                // ----------------------------------------------------------------------------
+                ClearBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS);
+                ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                          // load/shift goes low
+                TickDelay(LOADSHIFT_DELAY);
+
+                for (bitPosition = 15; bitPosition >= 0; bitPosition--) {       // for all the bits
+                        if (channel == 0) {
+                                if (sensVals.l.leftSensBits & (0x0001 << bitPosition)) {
+                                        SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);     // data bit set high
+                                } else {
+                                        ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);   // data bit set low
+                                }
+                        } else {
+                                if (sensVals.r.rightSensBits & (0x0001 << bitPosition)) {
+                                SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);     // data bit set high
+                                } else {
+                                ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);   // data bit set low
+                                }
+                        }
+
+                        ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                        writew(controlValue, korg1212->sensRegPtr);                       // clock goes low
+                        TickDelay(SENSCLKPULSE_WIDTH);
+                        SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                        writew(controlValue, korg1212->sensRegPtr);                       // clock goes high
+                        TickDelay(SENSCLKPULSE_WIDTH);
+                }
+
+                // ----------------------------------------------------------------------------
+                // finish up SPDIF for left.  Bring the load/shift line high, then write a one
+                // bit if the clock rate is 48K otherwise write 0.
+                // ----------------------------------------------------------------------------
+                ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+                ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                SetBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                   // load shift goes high - clk low
+                TickDelay(SENSCLKPULSE_WIDTH);
+
+                if (clkIs48K)
+                        SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+
+                writew(controlValue, korg1212->sensRegPtr);                   // set/clear data bit
+                TickDelay(ONE_RTC_TICK);
+                SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                   // clock goes high
+                TickDelay(SENSCLKPULSE_WIDTH);
+                ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                   // clock goes low
+                TickDelay(SENSCLKPULSE_WIDTH);
+        }
+
+        // ----------------------------------------------------------------------------
+        // The update is complete.  Set a timeout.  This is the inter-update delay.
+        // Also, if the card was in monitor mode, restore it.
+        // ----------------------------------------------------------------------------
+        for (count = 0; count < 10; count++)
+                TickDelay(SENSCLKPULSE_WIDTH);
+
+        if (monModeSet) {
+                rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                K1212_MODE_MonitorOn, 0, 0, 0);
+#ifdef DEBUG
+	        if (rc) PRINTK("DEBUG: WriteADCSensivity - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        }
+
+        return 1;
+}
+
+static void snd_korg1212_OnDSPDownloadComplete(korg1212_t *korg1212)
+{
+        int channel;
+
+#ifdef DEBUG
+        PRINTK("DEBUG: DSP download is complete. [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        // ----------------------------------------------------
+        // tell the card to boot
+        // ----------------------------------------------------
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_BootFromDSPPage4, 0, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Boot from Page 4 - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+	mdelay(DSP_BOOT_DELAY_IN_MS);
+
+        // --------------------------------------------------------------------------------
+        // Let the card know where all the buffers are.
+        // --------------------------------------------------------------------------------
+        rc = snd_korg1212_Send1212Command(korg1212,
+                        K1212_DB_ConfigureBufferMemory,
+                        LowerWordSwap(korg1212->PlayDataPhy),
+                        LowerWordSwap(korg1212->RecDataPhy),
+                        ((kNumBuffers * kPlayBufferFrames) / 2),   // size given to the card
+                                                                   // is based on 2 buffers
+                        0
+        );
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Configure Buffer Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        TickDelay(INTERCOMMAND_DELAY);
+
+        rc = snd_korg1212_Send1212Command(korg1212,
+                        K1212_DB_ConfigureMiscMemory,
+                        LowerWordSwap(korg1212->VolumeTablePhy),
+                        LowerWordSwap(korg1212->RoutingTablePhy),
+                        LowerWordSwap(korg1212->AdatTimeCodePhy),
+                        0
+        );
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Configure Misc Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+
+        // --------------------------------------------------------------------------------
+        // Initialize the routing and volume tables, then update the card's state.
+        // --------------------------------------------------------------------------------
+        TickDelay(INTERCOMMAND_DELAY);
+
+        for (channel = 0; channel < kAudioChannels; channel++) {
+                korg1212->sharedBufferPtr->volumeData[channel] = k1212MaxVolume;
+                //korg1212->sharedBufferPtr->routeData[channel] = channel;
+                korg1212->sharedBufferPtr->routeData[channel] = 8 + (channel & 1);
+        }
+
+        snd_korg1212_WriteADCSensitivity(korg1212);
+
+	TickDelay(INTERCOMMAND_DELAY);
+	rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate,
+					  ClockSourceSelector[korg1212->clkSrcRate],
+					  0, 0, 0);
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+	snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Set Monitor On - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        wake_up_interruptible(&korg1212->wait);
+}
+
+
+static void snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        u32 doorbellValue;
+        korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return);
+
+	if(irq != korg1212->irq)
+		return;
+
+        doorbellValue = readl(korg1212->inDoorbellPtr);
+
+        if (!doorbellValue)
+		return;
+
+	writel(doorbellValue, korg1212->inDoorbellPtr);
+
+        korg1212->irqcount++;
+
+	korg1212->inIRQ++;
+
+
+        switch (doorbellValue) {
+                case K1212_DB_DSPDownloadDone:
+#ifdef DEBUG
+                        PRINTK("DEBUG: IRQ DNLD count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+                        if (korg1212->cardState == K1212_STATE_DSP_IN_PROCESS) {
+                                        snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_COMPLETE);
+                                        snd_korg1212_OnDSPDownloadComplete(korg1212);
+                        }
+                        break;
+
+                // ------------------------------------------------------------------------
+                // an error occurred - stop the card
+                // ------------------------------------------------------------------------
+                case K1212_ISRCODE_DMAERROR:
+#ifdef DEBUG
+                        PRINTK("DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+                        writel(0, &korg1212->sharedBufferPtr->cardCommand);
+                        break;
+
+                // ------------------------------------------------------------------------
+                // the card has stopped by our request.  Clear the command word and signal
+                // the semaphore in case someone is waiting for this.
+                // ------------------------------------------------------------------------
+                case K1212_ISRCODE_CARDSTOPPED:
+#ifdef DEBUG
+                        PRINTK("DEBUG: IRQ CSTP count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+                        writel(0, &korg1212->sharedBufferPtr->cardCommand);
+                        break;
+
+                default:
+#ifdef XDEBUG
+                        PRINTK("DEBUG: IRQ DFLT count - %ld, %x, cpos=%d [%s].\n", korg1212->irqcount, doorbellValue, 
+				korg1212->currentBuffer, stateName[korg1212->cardState]);
+#endif
+                        if ((korg1212->cardState > K1212_STATE_SETUP) || korg1212->idleMonitorOn) {
+                                korg1212->currentBuffer++;
+
+                                if (korg1212->currentBuffer >= kNumBuffers)
+                                        korg1212->currentBuffer = 0;
+
+                                if (!korg1212->running)
+                                        break;
+
+                                if (korg1212->capture_substream) {
+                                        snd_pcm_period_elapsed(korg1212->capture_substream);
+                                }
+
+                                if (korg1212->playback_substream) {
+                                        snd_pcm_period_elapsed(korg1212->playback_substream);
+                                }
+                        }
+                        break;
+        }
+
+	korg1212->inIRQ--;
+}
+
+static int snd_korg1212_downloadDSPCode(korg1212_t *korg1212)
+{
+
+#ifdef DEBUG
+        PRINTK("DEBUG: DSP download is starting... [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        // ---------------------------------------------------------------
+        // verify the state of the card before proceeding.
+        // ---------------------------------------------------------------
+        if (korg1212->cardState >= K1212_STATE_DSP_IN_PROCESS) {
+                return 1;
+        }
+
+        snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS);
+
+        memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize);
+
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload,
+                                     UpperWordSwap(korg1212->dspMemPhy),
+                                     0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Start DSP Download RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        interruptible_sleep_on_timeout(&korg1212->wait, HZ * 4);
+
+        return 0;
+}
+
+static snd_pcm_hardware_t snd_korg1212_playback_info =
+{
+        info:                (SNDRV_PCM_INFO_MMAP |
+                              SNDRV_PCM_INFO_MMAP_VALID |
+                              SNDRV_PCM_INFO_INTERLEAVED),
+        formats:             SNDRV_PCM_FMTBIT_S16_LE,
+        rates:               (SNDRV_PCM_RATE_44100 |
+                              SNDRV_PCM_RATE_48000),
+        rate_min:            44100,
+        rate_max:            48000,
+        channels_min:        K1212_CHANNELS,
+        channels_max:        K1212_CHANNELS,
+        buffer_bytes_max:    K1212_BUF_SIZE,
+        period_bytes_min:    K1212_PERIOD_BYTES,
+        period_bytes_max:    K1212_PERIOD_BYTES,
+        periods_min:         K1212_PERIODS,
+        periods_max:         K1212_PERIODS,
+        fifo_size:           0,
+};
+
+static snd_pcm_hardware_t snd_korg1212_capture_info =
+{
+        info:                (SNDRV_PCM_INFO_MMAP |
+                              SNDRV_PCM_INFO_MMAP_VALID |
+                              SNDRV_PCM_INFO_INTERLEAVED),
+        formats:             SNDRV_PCM_FMTBIT_S16_LE,
+        rates:               (SNDRV_PCM_RATE_44100 |
+                              SNDRV_PCM_RATE_48000),
+        rate_min:            44100,
+        rate_max:            48000,
+        channels_min:        K1212_CHANNELS,
+        channels_max:        K1212_CHANNELS,
+        buffer_bytes_max:    K1212_BUF_SIZE,
+        period_bytes_min:    K1212_PERIOD_BYTES,
+        period_bytes_max:    K1212_PERIOD_BYTES,
+        periods_min:         K1212_PERIODS,
+        periods_max:         K1212_PERIODS,
+        fifo_size:           0,
+};
+
+static void snd_korg1212_free_pcm(snd_pcm_t *pcm)
+{
+        korg1212_t *korg1212 = (korg1212_t *) pcm->private_data;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_free_pcm [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        korg1212->pcm16 = NULL;
+}
+
+static unsigned int period_bytes[] = { K1212_PERIOD_BYTES };
+
+#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+        count: PERIOD_BYTES,
+        list: period_bytes,
+        mask: 0
+};
+
+static int snd_korg1212_playback_open(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        snd_pcm_runtime_t *runtime = substream->runtime;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        snd_korg1212_OpenCard(korg1212);
+
+        snd_pcm_set_sync(substream);    // ???
+
+        runtime->hw = snd_korg1212_playback_info;
+	runtime->dma_area = (char *) korg1212->playDataBufsPtr;
+	runtime->dma_bytes = K1212_BUF_SIZE;
+
+        korg1212->playback_substream = substream;
+        korg1212->periodsize = K1212_PERIODS;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE);
+        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+        return 0;
+}
+
+static int snd_korg1212_capture_open(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        snd_pcm_runtime_t *runtime = substream->runtime;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        snd_korg1212_OpenCard(korg1212);
+
+        snd_pcm_set_sync(substream);    // ???
+
+        runtime->hw = snd_korg1212_capture_info;
+	runtime->dma_area = (char *) korg1212->recordDataBufsPtr;
+	runtime->dma_bytes = K1212_BUF_SIZE;
+
+        korg1212->capture_substream = substream;
+        korg1212->periodsize = K1212_PERIODS;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE);
+        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+        return 0;
+}
+
+static int snd_korg1212_playback_close(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        korg1212->playback_substream = NULL;
+        korg1212->periodsize = 0;
+
+        snd_korg1212_CloseCard(korg1212);
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+        return 0;
+}
+
+static int snd_korg1212_capture_close(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        korg1212->capture_substream = NULL;
+        korg1212->periodsize = 0;
+
+        snd_korg1212_CloseCard(korg1212);
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+        return 0;
+}
+
+static int snd_korg1212_channel_info(snd_pcm_substream_t *substream,
+				    snd_pcm_channel_info_t *info)
+{
+	int chn = info->channel;
+
+	// snd_assert(info->channel < kAudioChannels + 1, return -EINVAL);
+
+        info->offset = 0;
+        // if (chn < k16BitChannels) {
+                info->first = chn * 16;
+        // } else {
+        //         info->first = k16BitChannels * 16 + (chn - k16BitChannels - 1) * 32;
+        // }
+        info->step = sizeof(KorgAudioFrame) * 8;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_channel_info %d:, offset=%ld, first=%d, step=%d\n", chn, info->offset, info->first, info->step);
+#endif
+
+	return 0;
+}
+
+static int snd_korg1212_ioctl(snd_pcm_substream_t *substream,
+			     unsigned int cmd, void *arg)
+{
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_ioctl: cmd=%d\n", cmd);
+#endif
+	if (cmd == SNDRV_PCM_IOCTL1_CHANNEL_INFO ) {
+		snd_pcm_channel_info_t *info = arg;
+		return snd_korg1212_channel_info(substream, info);
+	}
+
+        return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_korg1212_hw_params(snd_pcm_substream_t *substream,
+                             snd_pcm_hw_params_t *params)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        int err;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_hw_params [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+        if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) {
+                spin_unlock_irqrestore(&korg1212->lock, flags);
+                return err;
+        }
+
+        if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) {
+                spin_unlock_irqrestore(&korg1212->lock, flags);
+                return -EINVAL;
+        }
+
+        korg1212->periodsize = K1212_BLOCK_SIZE;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_prepare(snd_pcm_substream_t *substream)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        unsigned long flags;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        snd_korg1212_SetupForPlay(korg1212);
+
+        korg1212->currentBuffer = -1;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+        return 0;
+}
+
+static int snd_korg1212_trigger(snd_pcm_substream_t *substream,
+                           int cmd)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_trigger [%s] cmd=%d\n", stateName[korg1212->cardState], cmd);
+#endif
+
+        switch (cmd) {
+                case SNDRV_PCM_TRIGGER_START:
+                        korg1212->running = 1;
+                        snd_korg1212_TriggerPlay(korg1212);
+                        break;
+
+                case SNDRV_PCM_TRIGGER_STOP:
+                        korg1212->running = 0;
+                        snd_korg1212_StopPlay(korg1212);
+                        break;
+
+                default:
+                        return -EINVAL;
+        }
+        return 0;
+}
+
+static snd_pcm_uframes_t snd_korg1212_pointer(snd_pcm_substream_t *substream)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        snd_pcm_uframes_t pos;
+
+	if (korg1212->currentBuffer < 0)
+		return 0;
+
+	pos = korg1212->currentBuffer * kPlayBufferFrames;
+
+#ifdef XDEBUG
+		PRINTK("DEBUG: snd_korg1212_pointer [%s] %ld\n", stateName[korg1212->cardState], pos);
+#endif
+
+        return pos;
+}
+
+static int snd_korg1212_playback_copy(snd_pcm_substream_t *substream,
+                        int channel, /* not used (interleaved data) */
+                        snd_pcm_uframes_t pos,
+                        void *src,
+                        snd_pcm_uframes_t count)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+	KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count);
+#endif
+ 
+	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+        copy_from_user(dst, src, count * K1212_FRAME_SIZE);
+
+        return 0;
+}
+
+static int snd_korg1212_capture_copy(snd_pcm_substream_t *substream,
+                        int channel, /* not used (interleaved data) */
+                        snd_pcm_uframes_t pos,
+                        void *dst,
+                        snd_pcm_uframes_t count)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+	KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count);
+#endif
+
+	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+        copy_to_user(dst, src, count * K1212_FRAME_SIZE);
+
+        return 0;
+}
+
+static int snd_korg1212_playback_silence(snd_pcm_substream_t *substream,
+                           int channel, /* not used (interleaved data) */
+                           snd_pcm_uframes_t pos,
+                           snd_pcm_uframes_t count)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+	KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+        memset(dst, 0, count * K1212_FRAME_SIZE);
+
+        return 0;
+}
+
+static snd_pcm_ops_t snd_korg1212_playback_ops = {
+        open:           snd_korg1212_playback_open,
+        close:          snd_korg1212_playback_close,
+        ioctl:          snd_korg1212_ioctl,
+        hw_params:      snd_korg1212_hw_params,
+        prepare:        snd_korg1212_prepare,
+        trigger:        snd_korg1212_trigger,
+        pointer:        snd_korg1212_pointer,
+        copy:           snd_korg1212_playback_copy,
+        silence:        snd_korg1212_playback_silence,
+};
+
+static snd_pcm_ops_t snd_korg1212_capture_ops = {
+	open:		snd_korg1212_capture_open,
+	close:		snd_korg1212_capture_close,
+	ioctl:		snd_korg1212_ioctl,
+	hw_params:	snd_korg1212_hw_params,
+	prepare:	snd_korg1212_prepare,
+	trigger:	snd_korg1212_trigger,
+	pointer:	snd_korg1212_pointer,
+	copy:		snd_korg1212_capture_copy,
+};
+
+/*
+ * Control Interface
+ */
+
+static int snd_korg1212_control_phase_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+	return 0;
+}
+
+static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int i = kcontrol->private_value;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+        u->value.integer.value[0] = korg1212->volumePhase[i];
+
+	if (i >= 8) 
+        	u->value.integer.value[1] = korg1212->volumePhase[i+1];
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+        int i, val;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+
+	korg1212->volumePhase[i] = u->value.integer.value[0];
+
+	val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value];
+
+	if ((u->value.integer.value[0] > 0) != (val < 0)) {
+		val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1);
+		korg1212->sharedBufferPtr->volumeData[i] = val;
+		change = 1;
+	}
+
+	if (i >= 8) {
+		korg1212->volumePhase[i+1] = u->value.integer.value[1];
+
+		val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1];
+
+		if ((u->value.integer.value[1] > 0) != (val < 0)) {
+			val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1);
+			korg1212->sharedBufferPtr->volumeData[i+1] = val;
+			change = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+        uinfo->value.integer.min = k1212MinVolume;
+	uinfo->value.integer.max = k1212MaxVolume;
+        return 0;
+}
+
+static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int i;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+        u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]);
+
+	if (i >= 8) 
+                u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]);
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+        int i;
+	int val;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+
+	if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) {
+		val = korg1212->volumePhase[i] > 0 ? -1 : 1;
+		val *= u->value.integer.value[0];
+		korg1212->sharedBufferPtr->volumeData[i] = val;
+		change = 1;
+	}
+
+	if (i >= 8) {
+		if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
+			val = korg1212->volumePhase[i+1] > 0 ? -1 : 1;
+			val *= u->value.integer.value[1];
+			korg1212->sharedBufferPtr->volumeData[i+1] = val;
+			change = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+	uinfo->value.enumerated.items = kAudioChannels;
+	if (uinfo->value.enumerated.item > kAudioChannels-1) {
+		uinfo->value.enumerated.item = kAudioChannels-1;
+	}
+	strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int i;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+	u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i];
+
+	if (i >= 8) 
+		u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1];
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0, i;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+
+	if (u->value.enumerated.item[0] != korg1212->sharedBufferPtr->volumeData[i]) {
+		korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0];
+		change = 1;
+	}
+
+	if (i >= 8) {
+		if (u->value.enumerated.item[1] != korg1212->sharedBufferPtr->volumeData[i+1]) {
+			korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1];
+			change = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_analog_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = k1212MaxADCSens;
+	uinfo->value.integer.max = k1212MinADCSens;
+        return 0;
+}
+
+static int snd_korg1212_control_analog_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+        u->value.integer.value[0] = korg1212->leftADCInSens;
+        u->value.integer.value[1] = korg1212->rightADCInSens;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_analog_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+        if (u->value.integer.value[0] != korg1212->leftADCInSens) {
+                korg1212->leftADCInSens = u->value.integer.value[0];
+                change = 1;
+        }
+        if (u->value.integer.value[1] != korg1212->rightADCInSens) {
+                korg1212->rightADCInSens = u->value.integer.value[1];
+                change = 1;
+        }
+
+        if (change)
+                snd_korg1212_WriteADCSensitivity(korg1212);
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_sync_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2) {
+		uinfo->value.enumerated.item = 2;
+	}
+	strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	ucontrol->value.enumerated.item[0] = korg1212->clkSource;
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+	return 0;
+}
+
+static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&korg1212->lock, flags);
+	change = val != korg1212->clkSource;
+        snd_korg1212_SetClockSource(korg1212, val);
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+	return change;
+}
+
+#define MON_MIXER(ord,c_name)									\
+        {											\
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,					\
+                name:		c_name " Monitor Volume",						\
+                info:		snd_korg1212_control_volume_info,				\
+                get:		snd_korg1212_control_volume_get,				\
+                put:		snd_korg1212_control_volume_put,				\
+		private_value:	ord,								\
+        },                                                                                      \
+        {											\
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,					\
+                name:		c_name " Monitor Route",						\
+                info:		snd_korg1212_control_route_info,				\
+                get:		snd_korg1212_control_route_get,					\
+                put:		snd_korg1212_control_route_put,					\
+		private_value:	ord,								\
+        },                                                                                      \
+        {											\
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                iface:          SNDRV_CTL_ELEM_IFACE_PCM,					\
+                name:		c_name " Monitor Phase Invert",						\
+                info:		snd_korg1212_control_phase_info,				\
+                get:		snd_korg1212_control_phase_get,					\
+                put:		snd_korg1212_control_phase_put,					\
+		private_value:	ord,								\
+        }
+
+static snd_kcontrol_new_t snd_korg1212_controls[] = {
+        MON_MIXER(8, "Analog"),
+	MON_MIXER(10, "SPDIF"), 
+        MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"),
+        MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"),
+	{
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+                iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+                name:		"Sync Source",
+                info:		snd_korg1212_control_sync_info,
+                get:		snd_korg1212_control_sync_get,
+                put:		snd_korg1212_control_sync_put,
+        },
+        {
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+                name:		"ADC Attenuation",
+                info:		snd_korg1212_control_analog_info,
+                get:		snd_korg1212_control_analog_get,
+                put:		snd_korg1212_control_analog_put,
+        }
+};
+
+#define K1212_CONTROL_ELEMENTS (sizeof(snd_korg1212_controls) / sizeof(snd_korg1212_controls[0]))
+
+/*
+ * proc interface
+ */
+
+static void snd_korg1212_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	int n;
+	korg1212_t *korg1212 = (korg1212_t *)entry->private_data;
+
+	snd_iprintf(buffer, korg1212->card->longname);
+	snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1);
+	snd_iprintf(buffer, "\nGeneral settings\n");
+	snd_iprintf(buffer, "    period size: %d bytes\n", K1212_BLOCK_SIZE);
+	snd_iprintf(buffer, "     clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] );
+	snd_iprintf(buffer, "  left ADC Sens: %d\n", korg1212->leftADCInSens );
+	snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens );
+        snd_iprintf(buffer, "    Volume Info:\n");
+        for (n=0; n<kAudioChannels; n++)
+                snd_iprintf(buffer, " Channel %d: %s -> %s [%d]\n", n,
+                                    channelName[n],
+                                    channelName[korg1212->sharedBufferPtr->routeData[n]],
+                                    korg1212->sharedBufferPtr->volumeData[n]);
+	snd_iprintf(buffer, "\nGeneral status\n");
+        snd_iprintf(buffer, " ADAT Time Code: %d\n", korg1212->sharedBufferPtr->AdatTimeCode);
+        snd_iprintf(buffer, "     Card State: %s\n", stateName[korg1212->cardState]);
+        snd_iprintf(buffer, "Idle mon. State: %d\n", korg1212->idleMonitorOn);
+        snd_iprintf(buffer, "Cmd retry count: %d\n", korg1212->cmdRetryCount);
+        snd_iprintf(buffer, "      Irq count: %ld\n", korg1212->irqcount);
+}
+
+static void __init snd_korg1212_proc_init(korg1212_t *korg1212)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(korg1212->card, "korg1212", korg1212->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = korg1212;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_korg1212_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	korg1212->proc_entry = entry;
+}
+
+static void snd_korg1212_proc_done(korg1212_t * korg1212)
+{
+	if (korg1212->proc_entry) {
+		snd_info_unregister(korg1212->proc_entry);
+		korg1212->proc_entry = NULL;
+	}
+}
+
+static int __init snd_korg1212_create(korg1212_t *korg1212)
+{
+        struct pci_dev *pci = korg1212->pci;
+        int err;
+        int i;
+	unsigned ioport_size, iomem_size, iomem2_size;
+	dma_addr_t phys_addr;
+
+        korg1212->irq = -1;
+        korg1212->clkSource = K1212_CLKIDX_Local;
+        korg1212->clkRate = 44100;
+        korg1212->inIRQ = 0;
+        korg1212->running = 0;
+        snd_korg1212_setCardState(korg1212, K1212_STATE_UNINITIALIZED);
+        korg1212->idleMonitorOn = 0;
+        korg1212->clkSrcRate = K1212_CLKIDX_LocalAt44_1K;
+        korg1212->leftADCInSens = k1212MaxADCSens;
+        korg1212->rightADCInSens = k1212MaxADCSens;
+
+        for (i=0; i<kAudioChannels; i++)
+                korg1212->volumePhase[i] = 0;
+
+        if ((err = pci_enable_device(pci)) < 0)
+                return err;
+
+        korg1212->iomem = pci_resource_start(korg1212->pci, 0);
+        korg1212->ioport = pci_resource_start(korg1212->pci, 1);
+        korg1212->iomem2 = pci_resource_start(korg1212->pci, 2);
+
+	iomem_size = pci_resource_len(korg1212->pci, 0);
+	ioport_size = pci_resource_len(korg1212->pci, 1);
+	iomem2_size = pci_resource_len(korg1212->pci, 2);
+
+#ifdef DEBUG
+        PRINTK("DEBUG: resources:\n"
+                   "    iomem = 0x%lx (%d)\n"
+		   "    ioport  = 0x%lx (%d)\n"
+                   "    iomem = 0x%lx (%d)\n"
+		   "    [%s]\n",
+		   korg1212->iomem, iomem_size,
+		   korg1212->ioport, ioport_size,
+		   korg1212->iomem2, iomem2_size,
+		   stateName[korg1212->cardState]);
+#endif
+
+        korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212");
+        if (korg1212->res_iomem == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n",
+                           korg1212->iomem, korg1212->iomem + iomem_size - 1);
+                return -EBUSY;
+        }
+
+        korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212");
+        if (korg1212->res_ioport == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n",
+                           korg1212->ioport, korg1212->ioport + ioport_size - 1);
+                return -EBUSY;
+        }
+
+        korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212");
+        if (korg1212->res_iomem2 == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n",
+                           korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1);
+                return -EBUSY;
+        }
+
+        if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) {
+		snd_printk("unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase,
+                           korg1212->iobase + iomem_size - 1);
+                return -EBUSY;
+        }
+
+        err = request_irq(pci->irq, snd_korg1212_interrupt,
+                          SA_INTERRUPT|SA_SHIRQ,
+                          "korg1212", (void *) korg1212);
+
+        if (err) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+                return -EBUSY;
+        }
+
+        korg1212->irq = pci->irq;
+
+        init_waitqueue_head(&korg1212->wait);
+        spin_lock_init(&korg1212->lock);
+	pci_set_master(korg1212->pci);
+
+        korg1212->statusRegPtr = (u32 *) (korg1212->iobase + STATUS_REG_OFFSET);
+        korg1212->outDoorbellPtr = (u32 *) (korg1212->iobase + OUT_DOORBELL_OFFSET);
+        korg1212->inDoorbellPtr = (u32 *) (korg1212->iobase + IN_DOORBELL_OFFSET);
+        korg1212->mailbox0Ptr = (u32 *) (korg1212->iobase + MAILBOX0_OFFSET);
+        korg1212->mailbox1Ptr = (u32 *) (korg1212->iobase + MAILBOX1_OFFSET);
+        korg1212->mailbox2Ptr = (u32 *) (korg1212->iobase + MAILBOX2_OFFSET);
+        korg1212->mailbox3Ptr = (u32 *) (korg1212->iobase + MAILBOX3_OFFSET);
+        korg1212->controlRegPtr = (u32 *) (korg1212->iobase + PCI_CONTROL_OFFSET);
+        korg1212->sensRegPtr = (u16 *) (korg1212->iobase + SENS_CONTROL_OFFSET);
+        korg1212->idRegPtr = (u32 *) (korg1212->iobase + DEV_VEND_ID_OFFSET);
+
+#ifdef DEBUG
+        PRINTK("DEBUG: card registers:\n"
+                   "    Status register = 0x%p\n"
+                   "    OutDoorbell     = 0x%p\n"
+                   "    InDoorbell      = 0x%p\n"
+                   "    Mailbox0        = 0x%p\n"
+                   "    Mailbox1        = 0x%p\n"
+                   "    Mailbox2        = 0x%p\n"
+                   "    Mailbox3        = 0x%p\n"
+                   "    ControlReg      = 0x%p\n"
+                   "    SensReg         = 0x%p\n"
+                   "    IDReg           = 0x%p\n"
+		   "    [%s]\n",
+                   korg1212->statusRegPtr,
+		   korg1212->outDoorbellPtr,
+		   korg1212->inDoorbellPtr,
+                   korg1212->mailbox0Ptr,
+                   korg1212->mailbox1Ptr,
+                   korg1212->mailbox2Ptr,
+                   korg1212->mailbox3Ptr,
+                   korg1212->controlRegPtr,
+                   korg1212->sensRegPtr,
+                   korg1212->idRegPtr,
+		   stateName[korg1212->cardState]);
+#endif
+
+	korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr);
+	korg1212->sharedBufferPhy = (unsigned long)phys_addr;
+
+        if (korg1212->sharedBufferPtr == NULL) {
+		snd_printk("can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer));
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer));
+#endif
+
+#ifndef LARGEALLOC
+
+        korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers;
+
+	korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr);
+	korg1212->PlayDataPhy = (u32)phys_addr;
+
+        if (korg1212->playDataBufsPtr == NULL) {
+		snd_printk("can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n",
+		korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize);
+#endif
+
+	korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr);
+	korg1212->RecDataPhy = (u32)phys_addr;
+
+        if (korg1212->recordDataBufsPtr == NULL) {
+		snd_printk("can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n",
+		korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize);
+#endif
+
+#else // LARGEALLOC
+
+        korg1212->recordDataBufsPtr = korg1212->sharedBufferPtr->recordDataBufs;
+        korg1212->playDataBufsPtr = korg1212->sharedBufferPtr->playDataBufs;
+        korg1212->PlayDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->playDataBufs;
+        korg1212->RecDataPhy  = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->recordDataBufs;
+
+#endif // LARGEALLOC
+
+        korg1212->dspCodeSize = sizeof (dspCode);
+
+        korg1212->VolumeTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->volumeData;
+        korg1212->RoutingTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->routeData;
+        korg1212->AdatTimeCodePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->AdatTimeCode;
+
+        korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr);
+	korg1212->dspMemPhy = (u32)phys_addr;
+
+        if (korg1212->dspMemPtr == NULL) {
+		snd_printk("can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize);
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n",
+		   korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize,
+		   stateName[korg1212->cardState]);
+#endif
+
+	rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+	snd_korg1212_EnableCardInterrupts(korg1212);
+
+	mdelay(CARD_BOOT_DELAY_IN_MS);
+
+        if (snd_korg1212_downloadDSPCode(korg1212))
+        	return -EBUSY;
+
+	printk(KERN_INFO "dspMemPhy       = %08x U[%08x]\n"
+               "PlayDataPhy     = %08x L[%08x]\n"
+               "RecDataPhy      = %08x L[%08x]\n"
+               "VolumeTablePhy  = %08x L[%08x]\n"
+               "RoutingTablePhy = %08x L[%08x]\n"
+               "AdatTimeCodePhy = %08x L[%08x]\n",
+	       korg1212->dspMemPhy,       UpperWordSwap(korg1212->dspMemPhy),
+               korg1212->PlayDataPhy,     LowerWordSwap(korg1212->PlayDataPhy),
+               korg1212->RecDataPhy,      LowerWordSwap(korg1212->RecDataPhy),
+               korg1212->VolumeTablePhy,  LowerWordSwap(korg1212->VolumeTablePhy),
+               korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy),
+               korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy));
+
+        if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm16)) < 0)
+                return err;
+
+        korg1212->pcm16->private_data = korg1212;
+        korg1212->pcm16->private_free = snd_korg1212_free_pcm;
+        strcpy(korg1212->pcm16->name, "korg1212");
+
+        snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
+        snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_CAPTURE, &snd_korg1212_capture_ops);
+
+	korg1212->pcm16->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+        for (i = 0; i < K1212_CONTROL_ELEMENTS; i++) {
+                err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212));
+                if (err < 0)
+                        return err;
+        }
+
+        snd_korg1212_proc_init(korg1212);
+
+	return 0;
+
+}
+
+static void
+snd_korg1212_free(void *private_data)
+{
+        korg1212_t *korg1212 = (korg1212_t *)private_data;
+
+        if (korg1212 == NULL) {
+                return;
+        }
+
+        snd_korg1212_proc_done(korg1212);
+
+        snd_korg1212_TurnOffIdleMonitor(korg1212);
+
+        snd_korg1212_DisableCardInterrupts(korg1212);
+
+        if (korg1212->irq >= 0) {
+                free_irq(korg1212->irq, (void *)korg1212);
+                korg1212->irq = -1;
+        }
+        if (korg1212->iobase != 0) {
+                iounmap((void *)korg1212->iobase);
+                korg1212->iobase = 0;
+        }
+        if (korg1212->res_iomem != NULL) {
+                release_resource(korg1212->res_iomem);
+                kfree_nocheck(korg1212->res_iomem);
+                korg1212->res_iomem = NULL;
+        }
+        if (korg1212->res_ioport != NULL) {
+                release_resource(korg1212->res_ioport);
+                kfree_nocheck(korg1212->res_ioport);
+                korg1212->res_ioport = NULL;
+        }
+        if (korg1212->res_iomem2 != NULL) {
+                release_resource(korg1212->res_iomem2);
+                kfree_nocheck(korg1212->res_iomem2);
+                korg1212->res_iomem2 = NULL;
+        }
+
+        // ----------------------------------------------------
+        // free up memory resources used for the DSP download.
+        // ----------------------------------------------------
+        if (korg1212->dspMemPtr) {
+                snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize,
+                                   korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy);
+                korg1212->dspMemPhy = 0;
+                korg1212->dspMemPtr = 0;
+                korg1212->dspCodeSize = 0;
+        }
+
+#ifndef LARGEALLOC
+
+        // ------------------------------------------------------
+        // free up memory resources used for the Play/Rec Buffers
+        // ------------------------------------------------------
+	if (korg1212->playDataBufsPtr) {
+                snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize,
+                                   korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy);
+		korg1212->PlayDataPhy = 0;
+		korg1212->playDataBufsPtr = NULL;
+        }
+
+	if (korg1212->recordDataBufsPtr) {
+                snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize,
+                                   korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy);
+		korg1212->RecDataPhy = 0;
+		korg1212->recordDataBufsPtr = NULL;
+        }
+
+#endif
+
+        // ----------------------------------------------------
+        // free up memory resources used for the Shared Buffers
+        // ----------------------------------------------------
+	if (korg1212->sharedBufferPtr) {
+                snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer),
+                                   korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy);
+		korg1212->sharedBufferPhy = 0;
+		korg1212->sharedBufferPtr = NULL;
+        }
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_korg1212_card_free(snd_card_t *card)
+{
+#ifdef DEBUG
+        PRINTK("DEBUG: Freeing card\n");
+#endif
+	snd_korg1212_free(card->private_data);
+}
+
+static int __devinit
+snd_korg1212_probe(struct pci_dev *pci,
+		const struct pci_device_id *id)
+{
+	static int dev = 0;
+	korg1212_t *korg1212;
+	snd_card_t *card;
+	int err;
+
+	if (dev >= SNDRV_CARDS) {
+		return -ENODEV;
+	}
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(korg1212_t))) == NULL)
+		return -ENOMEM;
+
+	card->private_free = snd_korg1212_card_free;
+	korg1212 = (korg1212_t *)card->private_data;
+	korg1212->card = card;
+	korg1212->pci = pci;
+
+	if ((err = snd_korg1212_create(korg1212)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "korg1212");
+	strcpy(card->shortname, "korg1212");
+	sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
+		korg1212->iomem, korg1212->irq);
+
+#ifdef DEBUG
+        PRINTK("DEBUG: %s\n", card->longname);
+#endif
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_korg1212_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "korg1212",
+	id_table: snd_korg1212_ids,
+	probe: snd_korg1212_probe,
+	remove: __devexit_p(snd_korg1212_remove),
+};
+
+static int __init alsa_card_korg1212_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "No Korg 1212IO cards found\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_korg1212_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_korg1212_init)
+module_exit(alsa_card_korg1212_exit)
+
+#ifndef MODULE
+
+/* format is: snd-korg1212=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_korg1212_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-korg1212=", alsa_card_korg1212_setup);
+
+#endif /* ifndef MODULE */
+
diff -Nru linux/sound/pci/maestro3.c linux-2.4.19-pre5-mjc/sound/pci/maestro3.c
--- linux/sound/pci/maestro3.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/maestro3.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2715 @@
+/*
+ * Driver for ESS Maestro3/Allegro soundcards.
+ * Copyright (c) 2000 by Zach Brown <zab@zabbo.net>
+ *                       Takashi Iwai <tiwai@suse.de>
+ *
+ * Most of the hardware init stuffs are based on maestro3 driver for
+ * OSS/Free by Zach Brown.  Many thanks to Zach!
+ *
+ *   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
+ *
+ *
+ * ChangeLog:
+ * Aug. 27, 2001
+ *     - Fixed deadlock on capture
+ *     - Added Canyon3D-2 support by Rob Riggs <rob@pangalactic.org>
+ *
+ */
+ 
+#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
+#define DRIVER_NAME "Maestro3"
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ESS Maestro3 PCI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,Maestro3 PCI},"
+		"{ESS,Allegro PCI},"
+		"{ESS,Allegro-1 PCI},"
+	        "{ESS,Canyon3D-2/LE PCI}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */
+static int snd_external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int snd_amp_gpio[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable this soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_external_amp, "Enable external amp for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+MODULE_PARM(snd_amp_gpio, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_amp_gpio, "GPIO pin number for external amp. (default = -1)");
+MODULE_PARM_SYNTAX(snd_amp_gpio, SNDRV_ENABLED);
+
+#define MAX_PLAYBACKS	2
+#define MAX_CAPTURES	1
+#define NR_DSPS		(MAX_PLAYBACKS + MAX_CAPTURES)
+
+
+/*
+ * maestro3 registers
+ */
+
+/* Allegro PCI configuration registers */
+#define PCI_LEGACY_AUDIO_CTRL   0x40
+#define SOUND_BLASTER_ENABLE    0x00000001
+#define FM_SYNTHESIS_ENABLE     0x00000002
+#define GAME_PORT_ENABLE        0x00000004
+#define MPU401_IO_ENABLE        0x00000008
+#define MPU401_IRQ_ENABLE       0x00000010
+#define ALIAS_10BIT_IO          0x00000020
+#define SB_DMA_MASK             0x000000C0
+#define SB_DMA_0                0x00000040
+#define SB_DMA_1                0x00000040
+#define SB_DMA_R                0x00000080
+#define SB_DMA_3                0x000000C0
+#define SB_IRQ_MASK             0x00000700
+#define SB_IRQ_5                0x00000000
+#define SB_IRQ_7                0x00000100
+#define SB_IRQ_9                0x00000200
+#define SB_IRQ_10               0x00000300
+#define MIDI_IRQ_MASK           0x00003800
+#define SERIAL_IRQ_ENABLE       0x00004000
+#define DISABLE_LEGACY          0x00008000
+
+#define PCI_ALLEGRO_CONFIG      0x50
+#define SB_ADDR_240             0x00000004
+#define MPU_ADDR_MASK           0x00000018
+#define MPU_ADDR_330            0x00000000
+#define MPU_ADDR_300            0x00000008
+#define MPU_ADDR_320            0x00000010
+#define MPU_ADDR_340            0x00000018
+#define USE_PCI_TIMING          0x00000040
+#define POSTED_WRITE_ENABLE     0x00000080
+#define DMA_POLICY_MASK         0x00000700
+#define DMA_DDMA                0x00000000
+#define DMA_TDMA                0x00000100
+#define DMA_PCPCI               0x00000200
+#define DMA_WBDMA16             0x00000400
+#define DMA_WBDMA4              0x00000500
+#define DMA_WBDMA2              0x00000600
+#define DMA_WBDMA1              0x00000700
+#define DMA_SAFE_GUARD          0x00000800
+#define HI_PERF_GP_ENABLE       0x00001000
+#define PIC_SNOOP_MODE_0        0x00002000
+#define PIC_SNOOP_MODE_1        0x00004000
+#define SOUNDBLASTER_IRQ_MASK   0x00008000
+#define RING_IN_ENABLE          0x00010000
+#define SPDIF_TEST_MODE         0x00020000
+#define CLK_MULT_MODE_SELECT_2  0x00040000
+#define EEPROM_WRITE_ENABLE     0x00080000
+#define CODEC_DIR_IN            0x00100000
+#define HV_BUTTON_FROM_GD       0x00200000
+#define REDUCED_DEBOUNCE        0x00400000
+#define HV_CTRL_ENABLE          0x00800000
+#define SPDIF_ENABLE            0x01000000
+#define CLK_DIV_SELECT          0x06000000
+#define CLK_DIV_BY_48           0x00000000
+#define CLK_DIV_BY_49           0x02000000
+#define CLK_DIV_BY_50           0x04000000
+#define CLK_DIV_RESERVED        0x06000000
+#define PM_CTRL_ENABLE          0x08000000
+#define CLK_MULT_MODE_SELECT    0x30000000
+#define CLK_MULT_MODE_SHIFT     28
+#define CLK_MULT_MODE_0         0x00000000
+#define CLK_MULT_MODE_1         0x10000000
+#define CLK_MULT_MODE_2         0x20000000
+#define CLK_MULT_MODE_3         0x30000000
+#define INT_CLK_SELECT          0x40000000
+#define INT_CLK_MULT_RESET      0x80000000
+
+/* M3 */
+#define INT_CLK_SRC_NOT_PCI     0x00100000
+#define INT_CLK_MULT_ENABLE     0x80000000
+
+#define PCI_ACPI_CONTROL        0x54
+#define PCI_ACPI_D0             0x00000000
+#define PCI_ACPI_D1             0xB4F70000
+#define PCI_ACPI_D2             0xB4F7B4F7
+
+#define PCI_USER_CONFIG         0x58
+#define EXT_PCI_MASTER_ENABLE   0x00000001
+#define SPDIF_OUT_SELECT        0x00000002
+#define TEST_PIN_DIR_CTRL       0x00000004
+#define AC97_CODEC_TEST         0x00000020
+#define TRI_STATE_BUFFER        0x00000080
+#define IN_CLK_12MHZ_SELECT     0x00000100
+#define MULTI_FUNC_DISABLE      0x00000200
+#define EXT_MASTER_PAIR_SEL     0x00000400
+#define PCI_MASTER_SUPPORT      0x00000800
+#define STOP_CLOCK_ENABLE       0x00001000
+#define EAPD_DRIVE_ENABLE       0x00002000
+#define REQ_TRI_STATE_ENABLE    0x00004000
+#define REQ_LOW_ENABLE          0x00008000
+#define MIDI_1_ENABLE           0x00010000
+#define MIDI_2_ENABLE           0x00020000
+#define SB_AUDIO_SYNC           0x00040000
+#define HV_CTRL_TEST            0x00100000
+#define SOUNDBLASTER_TEST       0x00400000
+
+#define PCI_USER_CONFIG_C       0x5C
+
+#define PCI_DDMA_CTRL           0x60
+#define DDMA_ENABLE             0x00000001
+
+
+/* Allegro registers */
+#define HOST_INT_CTRL           0x18
+#define SB_INT_ENABLE           0x0001
+#define MPU401_INT_ENABLE       0x0002
+#define ASSP_INT_ENABLE         0x0010
+#define RING_INT_ENABLE         0x0020
+#define HV_INT_ENABLE           0x0040
+#define CLKRUN_GEN_ENABLE       0x0100
+#define HV_CTRL_TO_PME          0x0400
+#define SOFTWARE_RESET_ENABLE   0x8000
+
+/*
+ * should be using the above defines, probably.
+ */
+#define REGB_ENABLE_RESET               0x01
+#define REGB_STOP_CLOCK                 0x10
+
+#define HOST_INT_STATUS         0x1A
+#define SB_INT_PENDING          0x01
+#define MPU401_INT_PENDING      0x02
+#define ASSP_INT_PENDING        0x10
+#define RING_INT_PENDING        0x20
+#define HV_INT_PENDING          0x40
+
+#define HARDWARE_VOL_CTRL       0x1B
+#define SHADOW_MIX_REG_VOICE    0x1C
+#define HW_VOL_COUNTER_VOICE    0x1D
+#define SHADOW_MIX_REG_MASTER   0x1E
+#define HW_VOL_COUNTER_MASTER   0x1F
+
+#define CODEC_COMMAND           0x30
+#define CODEC_READ_B            0x80
+
+#define CODEC_STATUS            0x30
+#define CODEC_BUSY_B            0x01
+
+#define CODEC_DATA              0x32
+
+#define RING_BUS_CTRL_A         0x36
+#define RAC_PME_ENABLE          0x0100
+#define RAC_SDFS_ENABLE         0x0200
+#define LAC_PME_ENABLE          0x0400
+#define LAC_SDFS_ENABLE         0x0800
+#define SERIAL_AC_LINK_ENABLE   0x1000
+#define IO_SRAM_ENABLE          0x2000
+#define IIS_INPUT_ENABLE        0x8000
+
+#define RING_BUS_CTRL_B         0x38
+#define SECOND_CODEC_ID_MASK    0x0003
+#define SPDIF_FUNC_ENABLE       0x0010
+#define SECOND_AC_ENABLE        0x0020
+#define SB_MODULE_INTF_ENABLE   0x0040
+#define SSPE_ENABLE             0x0040
+#define M3I_DOCK_ENABLE         0x0080
+
+#define SDO_OUT_DEST_CTRL       0x3A
+#define COMMAND_ADDR_OUT        0x0003
+#define PCM_LR_OUT_LOCAL        0x0000
+#define PCM_LR_OUT_REMOTE       0x0004
+#define PCM_LR_OUT_MUTE         0x0008
+#define PCM_LR_OUT_BOTH         0x000C
+#define LINE1_DAC_OUT_LOCAL     0x0000
+#define LINE1_DAC_OUT_REMOTE    0x0010
+#define LINE1_DAC_OUT_MUTE      0x0020
+#define LINE1_DAC_OUT_BOTH      0x0030
+#define PCM_CLS_OUT_LOCAL       0x0000
+#define PCM_CLS_OUT_REMOTE      0x0040
+#define PCM_CLS_OUT_MUTE        0x0080
+#define PCM_CLS_OUT_BOTH        0x00C0
+#define PCM_RLF_OUT_LOCAL       0x0000
+#define PCM_RLF_OUT_REMOTE      0x0100
+#define PCM_RLF_OUT_MUTE        0x0200
+#define PCM_RLF_OUT_BOTH        0x0300
+#define LINE2_DAC_OUT_LOCAL     0x0000
+#define LINE2_DAC_OUT_REMOTE    0x0400
+#define LINE2_DAC_OUT_MUTE      0x0800
+#define LINE2_DAC_OUT_BOTH      0x0C00
+#define HANDSET_OUT_LOCAL       0x0000
+#define HANDSET_OUT_REMOTE      0x1000
+#define HANDSET_OUT_MUTE        0x2000
+#define HANDSET_OUT_BOTH        0x3000
+#define IO_CTRL_OUT_LOCAL       0x0000
+#define IO_CTRL_OUT_REMOTE      0x4000
+#define IO_CTRL_OUT_MUTE        0x8000
+#define IO_CTRL_OUT_BOTH        0xC000
+
+#define SDO_IN_DEST_CTRL        0x3C
+#define STATUS_ADDR_IN          0x0003
+#define PCM_LR_IN_LOCAL         0x0000
+#define PCM_LR_IN_REMOTE        0x0004
+#define PCM_LR_RESERVED         0x0008
+#define PCM_LR_IN_BOTH          0x000C
+#define LINE1_ADC_IN_LOCAL      0x0000
+#define LINE1_ADC_IN_REMOTE     0x0010
+#define LINE1_ADC_IN_MUTE       0x0020
+#define MIC_ADC_IN_LOCAL        0x0000
+#define MIC_ADC_IN_REMOTE       0x0040
+#define MIC_ADC_IN_MUTE         0x0080
+#define LINE2_DAC_IN_LOCAL      0x0000
+#define LINE2_DAC_IN_REMOTE     0x0400
+#define LINE2_DAC_IN_MUTE       0x0800
+#define HANDSET_IN_LOCAL        0x0000
+#define HANDSET_IN_REMOTE       0x1000
+#define HANDSET_IN_MUTE         0x2000
+#define IO_STATUS_IN_LOCAL      0x0000
+#define IO_STATUS_IN_REMOTE     0x4000
+
+#define SPDIF_IN_CTRL           0x3E
+#define SPDIF_IN_ENABLE         0x0001
+
+#define GPIO_DATA               0x60
+#define GPIO_DATA_MASK          0x0FFF
+#define GPIO_HV_STATUS          0x3000
+#define GPIO_PME_STATUS         0x4000
+
+#define GPIO_MASK               0x64
+#define GPIO_DIRECTION          0x68
+#define GPO_PRIMARY_AC97        0x0001
+#define GPI_LINEOUT_SENSE       0x0004
+#define GPO_SECONDARY_AC97      0x0008
+#define GPI_VOL_DOWN            0x0010
+#define GPI_VOL_UP              0x0020
+#define GPI_IIS_CLK             0x0040
+#define GPI_IIS_LRCLK           0x0080
+#define GPI_IIS_DATA            0x0100
+#define GPI_DOCKING_STATUS      0x0100
+#define GPI_HEADPHONE_SENSE     0x0200
+#define GPO_EXT_AMP_SHUTDOWN    0x1000
+
+#define GPO_EXT_AMP_M3		1	/* default m3 amp */
+#define GPO_EXT_AMP_ALLEGRO	8	/* default allegro amp */
+
+/* M3 */
+#define GPO_M3_EXT_AMP_SHUTDN   0x0002
+
+#define ASSP_INDEX_PORT         0x80
+#define ASSP_MEMORY_PORT        0x82
+#define ASSP_DATA_PORT          0x84
+
+#define MPU401_DATA_PORT        0x98
+#define MPU401_STATUS_PORT      0x99
+
+#define CLK_MULT_DATA_PORT      0x9C
+
+#define ASSP_CONTROL_A          0xA2
+#define ASSP_0_WS_ENABLE        0x01
+#define ASSP_CTRL_A_RESERVED1   0x02
+#define ASSP_CTRL_A_RESERVED2   0x04
+#define ASSP_CLK_49MHZ_SELECT   0x08
+#define FAST_PLU_ENABLE         0x10
+#define ASSP_CTRL_A_RESERVED3   0x20
+#define DSP_CLK_36MHZ_SELECT    0x40
+
+#define ASSP_CONTROL_B          0xA4
+#define RESET_ASSP              0x00
+#define RUN_ASSP                0x01
+#define ENABLE_ASSP_CLOCK       0x00
+#define STOP_ASSP_CLOCK         0x10
+#define RESET_TOGGLE            0x40
+
+#define ASSP_CONTROL_C          0xA6
+#define ASSP_HOST_INT_ENABLE    0x01
+#define FM_ADDR_REMAP_DISABLE   0x02
+#define HOST_WRITE_PORT_ENABLE  0x08
+
+#define ASSP_HOST_INT_STATUS    0xAC
+#define DSP2HOST_REQ_PIORECORD  0x01
+#define DSP2HOST_REQ_I2SRATE    0x02
+#define DSP2HOST_REQ_TIMER      0x04
+
+/* AC97 registers */
+/* XXX fix this crap up */
+/*#define AC97_RESET              0x00*/
+
+#define AC97_VOL_MUTE_B         0x8000
+#define AC97_VOL_M              0x1F
+#define AC97_LEFT_VOL_S         8
+
+#define AC97_MASTER_VOL         0x02
+#define AC97_LINE_LEVEL_VOL     0x04
+#define AC97_MASTER_MONO_VOL    0x06
+#define AC97_PC_BEEP_VOL        0x0A
+#define AC97_PC_BEEP_VOL_M      0x0F
+#define AC97_SROUND_MASTER_VOL  0x38
+#define AC97_PC_BEEP_VOL_S      1
+
+/*#define AC97_PHONE_VOL          0x0C
+#define AC97_MIC_VOL            0x0E*/
+#define AC97_MIC_20DB_ENABLE    0x40
+
+/*#define AC97_LINEIN_VOL         0x10
+#define AC97_CD_VOL             0x12
+#define AC97_VIDEO_VOL          0x14
+#define AC97_AUX_VOL            0x16*/
+#define AC97_PCM_OUT_VOL        0x18
+/*#define AC97_RECORD_SELECT      0x1A*/
+#define AC97_RECORD_MIC         0x00
+#define AC97_RECORD_CD          0x01
+#define AC97_RECORD_VIDEO       0x02
+#define AC97_RECORD_AUX         0x03
+#define AC97_RECORD_MONO_MUX    0x02
+#define AC97_RECORD_DIGITAL     0x03
+#define AC97_RECORD_LINE        0x04
+#define AC97_RECORD_STEREO      0x05
+#define AC97_RECORD_MONO        0x06
+#define AC97_RECORD_PHONE       0x07
+
+/*#define AC97_RECORD_GAIN        0x1C*/
+#define AC97_RECORD_VOL_M       0x0F
+
+/*#define AC97_GENERAL_PURPOSE    0x20*/
+#define AC97_POWER_DOWN_CTRL    0x26
+#define AC97_ADC_READY          0x0001
+#define AC97_DAC_READY          0x0002
+#define AC97_ANALOG_READY       0x0004
+#define AC97_VREF_ON            0x0008
+#define AC97_PR0                0x0100
+#define AC97_PR1                0x0200
+#define AC97_PR2                0x0400
+#define AC97_PR3                0x0800
+#define AC97_PR4                0x1000
+
+#define AC97_RESERVED1          0x28
+
+#define AC97_VENDOR_TEST        0x5A
+
+#define AC97_CLOCK_DELAY        0x5C
+#define AC97_LINEOUT_MUX_SEL    0x0001
+#define AC97_MONO_MUX_SEL       0x0002
+#define AC97_CLOCK_DELAY_SEL    0x1F
+#define AC97_DAC_CDS_SHIFT      6
+#define AC97_ADC_CDS_SHIFT      11
+
+#define AC97_MULTI_CHANNEL_SEL  0x74
+
+/*#define AC97_VENDOR_ID1         0x7C
+#define AC97_VENDOR_ID2         0x7E*/
+
+/*
+ * ASSP control regs
+ */
+#define DSP_PORT_TIMER_COUNT    0x06
+
+#define DSP_PORT_MEMORY_INDEX   0x80
+
+#define DSP_PORT_MEMORY_TYPE    0x82
+#define MEMTYPE_INTERNAL_CODE   0x0002
+#define MEMTYPE_INTERNAL_DATA   0x0003
+#define MEMTYPE_MASK            0x0003
+
+#define DSP_PORT_MEMORY_DATA    0x84
+
+#define DSP_PORT_CONTROL_REG_A  0xA2
+#define DSP_PORT_CONTROL_REG_B  0xA4
+#define DSP_PORT_CONTROL_REG_C  0xA6
+
+#define REV_A_CODE_MEMORY_BEGIN         0x0000
+#define REV_A_CODE_MEMORY_END           0x0FFF
+#define REV_A_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_A_CODE_MEMORY_LENGTH        (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1)
+
+#define REV_B_CODE_MEMORY_BEGIN         0x0000
+#define REV_B_CODE_MEMORY_END           0x0BFF
+#define REV_B_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_B_CODE_MEMORY_LENGTH        (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1)
+
+#define REV_A_DATA_MEMORY_BEGIN         0x1000
+#define REV_A_DATA_MEMORY_END           0x2FFF
+#define REV_A_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_A_DATA_MEMORY_LENGTH        (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1)
+
+#define REV_B_DATA_MEMORY_BEGIN         0x1000
+#define REV_B_DATA_MEMORY_END           0x2BFF
+#define REV_B_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_B_DATA_MEMORY_LENGTH        (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1)
+
+
+#define NUM_UNITS_KERNEL_CODE          16
+#define NUM_UNITS_KERNEL_DATA           2
+
+#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16
+#define NUM_UNITS_KERNEL_DATA_WITH_HSP  5
+
+/*
+ * Kernel data layout
+ */
+
+#define DP_SHIFT_COUNT                  7
+
+#define KDATA_BASE_ADDR                 0x1000
+#define KDATA_BASE_ADDR2                0x1080
+
+#define KDATA_TASK0                     (KDATA_BASE_ADDR + 0x0000)
+#define KDATA_TASK1                     (KDATA_BASE_ADDR + 0x0001)
+#define KDATA_TASK2                     (KDATA_BASE_ADDR + 0x0002)
+#define KDATA_TASK3                     (KDATA_BASE_ADDR + 0x0003)
+#define KDATA_TASK4                     (KDATA_BASE_ADDR + 0x0004)
+#define KDATA_TASK5                     (KDATA_BASE_ADDR + 0x0005)
+#define KDATA_TASK6                     (KDATA_BASE_ADDR + 0x0006)
+#define KDATA_TASK7                     (KDATA_BASE_ADDR + 0x0007)
+#define KDATA_TASK_ENDMARK              (KDATA_BASE_ADDR + 0x0008)
+
+#define KDATA_CURRENT_TASK              (KDATA_BASE_ADDR + 0x0009)
+#define KDATA_TASK_SWITCH               (KDATA_BASE_ADDR + 0x000A)
+
+#define KDATA_INSTANCE0_POS3D           (KDATA_BASE_ADDR + 0x000B)
+#define KDATA_INSTANCE1_POS3D           (KDATA_BASE_ADDR + 0x000C)
+#define KDATA_INSTANCE2_POS3D           (KDATA_BASE_ADDR + 0x000D)
+#define KDATA_INSTANCE3_POS3D           (KDATA_BASE_ADDR + 0x000E)
+#define KDATA_INSTANCE4_POS3D           (KDATA_BASE_ADDR + 0x000F)
+#define KDATA_INSTANCE5_POS3D           (KDATA_BASE_ADDR + 0x0010)
+#define KDATA_INSTANCE6_POS3D           (KDATA_BASE_ADDR + 0x0011)
+#define KDATA_INSTANCE7_POS3D           (KDATA_BASE_ADDR + 0x0012)
+#define KDATA_INSTANCE8_POS3D           (KDATA_BASE_ADDR + 0x0013)
+#define KDATA_INSTANCE_POS3D_ENDMARK    (KDATA_BASE_ADDR + 0x0014)
+
+#define KDATA_INSTANCE0_SPKVIRT         (KDATA_BASE_ADDR + 0x0015)
+#define KDATA_INSTANCE_SPKVIRT_ENDMARK  (KDATA_BASE_ADDR + 0x0016)
+
+#define KDATA_INSTANCE0_SPDIF           (KDATA_BASE_ADDR + 0x0017)
+#define KDATA_INSTANCE_SPDIF_ENDMARK    (KDATA_BASE_ADDR + 0x0018)
+
+#define KDATA_INSTANCE0_MODEM           (KDATA_BASE_ADDR + 0x0019)
+#define KDATA_INSTANCE_MODEM_ENDMARK    (KDATA_BASE_ADDR + 0x001A)
+
+#define KDATA_INSTANCE0_SRC             (KDATA_BASE_ADDR + 0x001B)
+#define KDATA_INSTANCE1_SRC             (KDATA_BASE_ADDR + 0x001C)
+#define KDATA_INSTANCE_SRC_ENDMARK      (KDATA_BASE_ADDR + 0x001D)
+
+#define KDATA_INSTANCE0_MINISRC         (KDATA_BASE_ADDR + 0x001E)
+#define KDATA_INSTANCE1_MINISRC         (KDATA_BASE_ADDR + 0x001F)
+#define KDATA_INSTANCE2_MINISRC         (KDATA_BASE_ADDR + 0x0020)
+#define KDATA_INSTANCE3_MINISRC         (KDATA_BASE_ADDR + 0x0021)
+#define KDATA_INSTANCE_MINISRC_ENDMARK  (KDATA_BASE_ADDR + 0x0022)
+
+#define KDATA_INSTANCE0_CPYTHRU         (KDATA_BASE_ADDR + 0x0023)
+#define KDATA_INSTANCE1_CPYTHRU         (KDATA_BASE_ADDR + 0x0024)
+#define KDATA_INSTANCE_CPYTHRU_ENDMARK  (KDATA_BASE_ADDR + 0x0025)
+
+#define KDATA_CURRENT_DMA               (KDATA_BASE_ADDR + 0x0026)
+#define KDATA_DMA_SWITCH                (KDATA_BASE_ADDR + 0x0027)
+#define KDATA_DMA_ACTIVE                (KDATA_BASE_ADDR + 0x0028)
+
+#define KDATA_DMA_XFER0                 (KDATA_BASE_ADDR + 0x0029)
+#define KDATA_DMA_XFER1                 (KDATA_BASE_ADDR + 0x002A)
+#define KDATA_DMA_XFER2                 (KDATA_BASE_ADDR + 0x002B)
+#define KDATA_DMA_XFER3                 (KDATA_BASE_ADDR + 0x002C)
+#define KDATA_DMA_XFER4                 (KDATA_BASE_ADDR + 0x002D)
+#define KDATA_DMA_XFER5                 (KDATA_BASE_ADDR + 0x002E)
+#define KDATA_DMA_XFER6                 (KDATA_BASE_ADDR + 0x002F)
+#define KDATA_DMA_XFER7                 (KDATA_BASE_ADDR + 0x0030)
+#define KDATA_DMA_XFER8                 (KDATA_BASE_ADDR + 0x0031)
+#define KDATA_DMA_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0032)
+
+#define KDATA_I2S_SAMPLE_COUNT          (KDATA_BASE_ADDR + 0x0033)
+#define KDATA_I2S_INT_METER             (KDATA_BASE_ADDR + 0x0034)
+#define KDATA_I2S_ACTIVE                (KDATA_BASE_ADDR + 0x0035)
+
+#define KDATA_TIMER_COUNT_RELOAD        (KDATA_BASE_ADDR + 0x0036)
+#define KDATA_TIMER_COUNT_CURRENT       (KDATA_BASE_ADDR + 0x0037)
+
+#define KDATA_HALT_SYNCH_CLIENT         (KDATA_BASE_ADDR + 0x0038)
+#define KDATA_HALT_SYNCH_DMA            (KDATA_BASE_ADDR + 0x0039)
+#define KDATA_HALT_ACKNOWLEDGE          (KDATA_BASE_ADDR + 0x003A)
+
+#define KDATA_ADC1_XFER0                (KDATA_BASE_ADDR + 0x003B)
+#define KDATA_ADC1_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x003C)
+#define KDATA_ADC1_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x003D)
+#define KDATA_ADC1_RIGHT_VOLUME  		(KDATA_BASE_ADDR + 0x003E)
+#define KDATA_ADC1_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x003F)
+#define KDATA_ADC1_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0040)
+
+#define KDATA_ADC2_XFER0                (KDATA_BASE_ADDR + 0x0041)
+#define KDATA_ADC2_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x0042)
+#define KDATA_ADC2_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0043)
+#define KDATA_ADC2_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x0044)
+#define KDATA_ADC2_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x0045)
+#define KDATA_ADC2_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0046)
+
+#define KDATA_CD_XFER0					(KDATA_BASE_ADDR + 0x0047)					
+#define KDATA_CD_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x0048)
+#define KDATA_CD_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0049)
+#define KDATA_CD_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x004A)
+#define KDATA_CD_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x004B)
+#define KDATA_CD_RIGHT_SUR_VOL			(KDATA_BASE_ADDR + 0x004C)
+
+#define KDATA_MIC_XFER0					(KDATA_BASE_ADDR + 0x004D)
+#define KDATA_MIC_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x004E)
+#define KDATA_MIC_VOLUME				(KDATA_BASE_ADDR + 0x004F)
+#define KDATA_MIC_SUR_VOL				(KDATA_BASE_ADDR + 0x0050)
+
+#define KDATA_I2S_XFER0                 (KDATA_BASE_ADDR + 0x0051)
+#define KDATA_I2S_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0052)
+
+#define KDATA_CHI_XFER0                 (KDATA_BASE_ADDR + 0x0053)
+#define KDATA_CHI_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0054)
+
+#define KDATA_SPDIF_XFER                (KDATA_BASE_ADDR + 0x0055)
+#define KDATA_SPDIF_CURRENT_FRAME       (KDATA_BASE_ADDR + 0x0056)
+#define KDATA_SPDIF_FRAME0              (KDATA_BASE_ADDR + 0x0057)
+#define KDATA_SPDIF_FRAME1              (KDATA_BASE_ADDR + 0x0058)
+#define KDATA_SPDIF_FRAME2              (KDATA_BASE_ADDR + 0x0059)
+
+#define KDATA_SPDIF_REQUEST             (KDATA_BASE_ADDR + 0x005A)
+#define KDATA_SPDIF_TEMP                (KDATA_BASE_ADDR + 0x005B)
+
+#define KDATA_SPDIFIN_XFER0             (KDATA_BASE_ADDR + 0x005C)
+#define KDATA_SPDIFIN_XFER_ENDMARK      (KDATA_BASE_ADDR + 0x005D)
+#define KDATA_SPDIFIN_INT_METER         (KDATA_BASE_ADDR + 0x005E)
+
+#define KDATA_DSP_RESET_COUNT           (KDATA_BASE_ADDR + 0x005F)
+#define KDATA_DEBUG_OUTPUT              (KDATA_BASE_ADDR + 0x0060)
+
+#define KDATA_KERNEL_ISR_LIST           (KDATA_BASE_ADDR + 0x0061)
+
+#define KDATA_KERNEL_ISR_CBSR1          (KDATA_BASE_ADDR + 0x0062)
+#define KDATA_KERNEL_ISR_CBER1          (KDATA_BASE_ADDR + 0x0063)
+#define KDATA_KERNEL_ISR_CBCR           (KDATA_BASE_ADDR + 0x0064)
+#define KDATA_KERNEL_ISR_AR0            (KDATA_BASE_ADDR + 0x0065)
+#define KDATA_KERNEL_ISR_AR1            (KDATA_BASE_ADDR + 0x0066)
+#define KDATA_KERNEL_ISR_AR2            (KDATA_BASE_ADDR + 0x0067)
+#define KDATA_KERNEL_ISR_AR3            (KDATA_BASE_ADDR + 0x0068)
+#define KDATA_KERNEL_ISR_AR4            (KDATA_BASE_ADDR + 0x0069)
+#define KDATA_KERNEL_ISR_AR5            (KDATA_BASE_ADDR + 0x006A)
+#define KDATA_KERNEL_ISR_BRCR           (KDATA_BASE_ADDR + 0x006B)
+#define KDATA_KERNEL_ISR_PASR           (KDATA_BASE_ADDR + 0x006C)
+#define KDATA_KERNEL_ISR_PAER           (KDATA_BASE_ADDR + 0x006D)
+
+#define KDATA_CLIENT_SCRATCH0           (KDATA_BASE_ADDR + 0x006E)
+#define KDATA_CLIENT_SCRATCH1           (KDATA_BASE_ADDR + 0x006F)
+#define KDATA_KERNEL_SCRATCH            (KDATA_BASE_ADDR + 0x0070)
+#define KDATA_KERNEL_ISR_SCRATCH        (KDATA_BASE_ADDR + 0x0071)
+
+#define KDATA_OUEUE_LEFT                (KDATA_BASE_ADDR + 0x0072)
+#define KDATA_QUEUE_RIGHT               (KDATA_BASE_ADDR + 0x0073)
+
+#define KDATA_ADC1_REQUEST              (KDATA_BASE_ADDR + 0x0074)
+#define KDATA_ADC2_REQUEST              (KDATA_BASE_ADDR + 0x0075)
+#define KDATA_CD_REQUEST				(KDATA_BASE_ADDR + 0x0076)
+#define KDATA_MIC_REQUEST				(KDATA_BASE_ADDR + 0x0077)
+
+#define KDATA_ADC1_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0078)
+#define KDATA_ADC2_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0079)
+#define KDATA_CD_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007A)
+#define KDATA_MIC_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007B)
+#define KDATA_MIC_SYNC_COUNTER			(KDATA_BASE_ADDR + 0x007C)
+
+/*
+ * second 'segment' (?) reserved for mixer
+ * buffers..
+ */
+
+#define KDATA_MIXER_WORD0               (KDATA_BASE_ADDR2 + 0x0000)
+#define KDATA_MIXER_WORD1               (KDATA_BASE_ADDR2 + 0x0001)
+#define KDATA_MIXER_WORD2               (KDATA_BASE_ADDR2 + 0x0002)
+#define KDATA_MIXER_WORD3               (KDATA_BASE_ADDR2 + 0x0003)
+#define KDATA_MIXER_WORD4               (KDATA_BASE_ADDR2 + 0x0004)
+#define KDATA_MIXER_WORD5               (KDATA_BASE_ADDR2 + 0x0005)
+#define KDATA_MIXER_WORD6               (KDATA_BASE_ADDR2 + 0x0006)
+#define KDATA_MIXER_WORD7               (KDATA_BASE_ADDR2 + 0x0007)
+#define KDATA_MIXER_WORD8               (KDATA_BASE_ADDR2 + 0x0008)
+#define KDATA_MIXER_WORD9               (KDATA_BASE_ADDR2 + 0x0009)
+#define KDATA_MIXER_WORDA               (KDATA_BASE_ADDR2 + 0x000A)
+#define KDATA_MIXER_WORDB               (KDATA_BASE_ADDR2 + 0x000B)
+#define KDATA_MIXER_WORDC               (KDATA_BASE_ADDR2 + 0x000C)
+#define KDATA_MIXER_WORDD               (KDATA_BASE_ADDR2 + 0x000D)
+#define KDATA_MIXER_WORDE               (KDATA_BASE_ADDR2 + 0x000E)
+#define KDATA_MIXER_WORDF               (KDATA_BASE_ADDR2 + 0x000F)
+
+#define KDATA_MIXER_XFER0               (KDATA_BASE_ADDR2 + 0x0010)
+#define KDATA_MIXER_XFER1               (KDATA_BASE_ADDR2 + 0x0011)
+#define KDATA_MIXER_XFER2               (KDATA_BASE_ADDR2 + 0x0012)
+#define KDATA_MIXER_XFER3               (KDATA_BASE_ADDR2 + 0x0013)
+#define KDATA_MIXER_XFER4               (KDATA_BASE_ADDR2 + 0x0014)
+#define KDATA_MIXER_XFER5               (KDATA_BASE_ADDR2 + 0x0015)
+#define KDATA_MIXER_XFER6               (KDATA_BASE_ADDR2 + 0x0016)
+#define KDATA_MIXER_XFER7               (KDATA_BASE_ADDR2 + 0x0017)
+#define KDATA_MIXER_XFER8               (KDATA_BASE_ADDR2 + 0x0018)
+#define KDATA_MIXER_XFER9               (KDATA_BASE_ADDR2 + 0x0019)
+#define KDATA_MIXER_XFER_ENDMARK        (KDATA_BASE_ADDR2 + 0x001A)
+
+#define KDATA_MIXER_TASK_NUMBER         (KDATA_BASE_ADDR2 + 0x001B)
+#define KDATA_CURRENT_MIXER             (KDATA_BASE_ADDR2 + 0x001C)
+#define KDATA_MIXER_ACTIVE              (KDATA_BASE_ADDR2 + 0x001D)
+#define KDATA_MIXER_BANK_STATUS         (KDATA_BASE_ADDR2 + 0x001E)
+#define KDATA_DAC_LEFT_VOLUME	        (KDATA_BASE_ADDR2 + 0x001F)
+#define KDATA_DAC_RIGHT_VOLUME          (KDATA_BASE_ADDR2 + 0x0020)
+
+#define MAX_INSTANCE_MINISRC            (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC)
+#define MAX_VIRTUAL_DMA_CHANNELS        (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0)
+#define MAX_VIRTUAL_MIXER_CHANNELS      (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0)
+#define MAX_VIRTUAL_ADC1_CHANNELS       (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0)
+
+/*
+ * client data area offsets
+ */
+#define CDATA_INSTANCE_READY            0x00
+
+#define CDATA_HOST_SRC_ADDRL            0x01
+#define CDATA_HOST_SRC_ADDRH            0x02
+#define CDATA_HOST_SRC_END_PLUS_1L      0x03
+#define CDATA_HOST_SRC_END_PLUS_1H      0x04
+#define CDATA_HOST_SRC_CURRENTL         0x05
+#define CDATA_HOST_SRC_CURRENTH         0x06
+
+#define CDATA_IN_BUF_CONNECT            0x07
+#define CDATA_OUT_BUF_CONNECT           0x08
+
+#define CDATA_IN_BUF_BEGIN              0x09
+#define CDATA_IN_BUF_END_PLUS_1         0x0A
+#define CDATA_IN_BUF_HEAD               0x0B
+#define CDATA_IN_BUF_TAIL               0x0C
+#define CDATA_OUT_BUF_BEGIN             0x0D
+#define CDATA_OUT_BUF_END_PLUS_1        0x0E
+#define CDATA_OUT_BUF_HEAD              0x0F
+#define CDATA_OUT_BUF_TAIL              0x10
+
+#define CDATA_DMA_CONTROL               0x11
+#define CDATA_RESERVED                  0x12
+
+#define CDATA_FREQUENCY                 0x13
+#define CDATA_LEFT_VOLUME               0x14
+#define CDATA_RIGHT_VOLUME              0x15
+#define CDATA_LEFT_SUR_VOL              0x16
+#define CDATA_RIGHT_SUR_VOL             0x17
+
+#define CDATA_HEADER_LEN                0x18
+
+#define SRC3_DIRECTION_OFFSET           CDATA_HEADER_LEN
+#define SRC3_MODE_OFFSET                (CDATA_HEADER_LEN + 1)
+#define SRC3_WORD_LENGTH_OFFSET         (CDATA_HEADER_LEN + 2)
+#define SRC3_PARAMETER_OFFSET           (CDATA_HEADER_LEN + 3)
+#define SRC3_COEFF_ADDR_OFFSET          (CDATA_HEADER_LEN + 8)
+#define SRC3_FILTAP_ADDR_OFFSET         (CDATA_HEADER_LEN + 10)
+#define SRC3_TEMP_INBUF_ADDR_OFFSET     (CDATA_HEADER_LEN + 16)
+#define SRC3_TEMP_OUTBUF_ADDR_OFFSET    (CDATA_HEADER_LEN + 17)
+
+#define MINISRC_IN_BUFFER_SIZE   ( 0x50 * 2 )
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_TMP_BUFFER_SIZE  ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
+#define MINISRC_BIQUAD_STAGE    2
+#define MINISRC_COEF_LOC          0x175
+
+#define DMACONTROL_BLOCK_MASK           0x000F
+#define  DMAC_BLOCK0_SELECTOR           0x0000
+#define  DMAC_BLOCK1_SELECTOR           0x0001
+#define  DMAC_BLOCK2_SELECTOR           0x0002
+#define  DMAC_BLOCK3_SELECTOR           0x0003
+#define  DMAC_BLOCK4_SELECTOR           0x0004
+#define  DMAC_BLOCK5_SELECTOR           0x0005
+#define  DMAC_BLOCK6_SELECTOR           0x0006
+#define  DMAC_BLOCK7_SELECTOR           0x0007
+#define  DMAC_BLOCK8_SELECTOR           0x0008
+#define  DMAC_BLOCK9_SELECTOR           0x0009
+#define  DMAC_BLOCKA_SELECTOR           0x000A
+#define  DMAC_BLOCKB_SELECTOR           0x000B
+#define  DMAC_BLOCKC_SELECTOR           0x000C
+#define  DMAC_BLOCKD_SELECTOR           0x000D
+#define  DMAC_BLOCKE_SELECTOR           0x000E
+#define  DMAC_BLOCKF_SELECTOR           0x000F
+#define DMACONTROL_PAGE_MASK            0x00F0
+#define  DMAC_PAGE0_SELECTOR            0x0030
+#define  DMAC_PAGE1_SELECTOR            0x0020
+#define  DMAC_PAGE2_SELECTOR            0x0010
+#define  DMAC_PAGE3_SELECTOR            0x0000
+#define DMACONTROL_AUTOREPEAT           0x1000
+#define DMACONTROL_STOPPED              0x2000
+#define DMACONTROL_DIRECTION            0x0100
+
+/*
+ * an arbitrary volume we set the internal
+ * volume settings to so that the ac97 volume
+ * range is a little less insane.  0x7fff is 
+ * max.
+ */
+#define ARB_VOLUME ( 0x6800 )
+
+/*
+ */
+
+typedef struct snd_m3_dma m3_dma_t;
+typedef struct snd_m3 m3_t;
+#define chip_t m3_t
+
+
+struct m3_list {
+	int curlen;
+	int mem_addr;
+	int max;
+};
+
+struct snd_m3_dma {
+
+	int number;
+	m3_t *chip;
+	snd_pcm_substream_t *substream;
+
+	struct assp_instance {
+		unsigned short code, data;
+	} inst;
+
+	int running;
+	int opened;
+
+	unsigned long buffer_addr;
+	int dma_size;
+	int period_size;
+	unsigned int hwptr;
+	int count;
+
+	int index[3];
+	struct m3_list *index_list[3];
+
+        int in_lists;
+	
+	struct list_head list;
+
+};
+    
+struct snd_m3 {
+	
+	snd_card_t *card;
+
+	unsigned long iobase;
+	struct resource *iobase_res;
+
+	int irq;
+	int allegro_flag : 1;
+
+	ac97_t *ac97;
+
+	snd_pcm_t *pcm;
+
+	struct pci_dev *pci;
+	/* pci_dev in 2.2 kernel doesn't have them, so we keep them private */
+	u16 subsystem_vendor;
+	u16 subsystem_device;
+
+	int dacs_active;
+	int timer_users;
+
+	struct m3_list  msrc_list;
+	struct m3_list  mixer_list;
+	struct m3_list  adc1_list;
+	struct m3_list  dma_list;
+
+	/* for storing reset state..*/
+	u8 reset_state;
+
+	int external_amp;
+	int amp_gpio;
+
+	/* pcm streams */
+	int num_substreams;
+	m3_dma_t *substreams;
+
+	spinlock_t reg_lock;
+
+#ifdef CONFIG_PM
+	u16 *suspend_mem;
+#endif
+};
+
+/*
+ * pci ids
+ */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS         0x125D
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ALLEGRO_1
+#define PCI_DEVICE_ID_ESS_ALLEGRO_1	0x1988
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ALLEGRO
+#define PCI_DEVICE_ID_ESS_ALLEGRO	0x1989
+#endif
+#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2LE
+#define PCI_DEVICE_ID_ESS_CANYON3D_2LE	0x1990
+#endif
+#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2
+#define PCI_DEVICE_ID_ESS_CANYON3D_2	0x1992
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3
+#define PCI_DEVICE_ID_ESS_MAESTRO3	0x1998
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_1
+#define PCI_DEVICE_ID_ESS_MAESTRO3_1	0x1999
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_HW
+#define PCI_DEVICE_ID_ESS_MAESTRO3_HW	0x199a
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_2
+#define PCI_DEVICE_ID_ESS_MAESTRO3_2	0x199b
+#endif
+
+static struct pci_device_id snd_m3_ids[] __devinitdata = {
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2LE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_HW, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, snd_m3_ids);
+
+/*
+ * lowlevel functions
+ */
+
+inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg)
+{
+	outw(value, chip->iobase + reg);
+}
+
+inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg)
+{
+	return inw(chip->iobase + reg);
+}
+
+inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg)
+{
+	outb(value, chip->iobase + reg);
+}
+
+inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg)
+{
+	return inb(chip->iobase + reg);
+}
+
+/*
+ * access 16bit words to the code or data regions of the dsp's memory.
+ * index addresses 16bit words.
+ */
+static u16 snd_m3_assp_read(m3_t *chip, u16 region, u16 index)
+{
+	snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+	snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX);
+	return snd_m3_inw(chip, DSP_PORT_MEMORY_DATA);
+}
+
+static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data)
+{
+	snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+	snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX);
+	snd_m3_outw(chip, data, DSP_PORT_MEMORY_DATA);
+}
+
+static void snd_m3_assp_halt(m3_t *chip)
+{
+	chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
+	mdelay(10);
+	snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+static void snd_m3_assp_continue(m3_t *chip)
+{
+	snd_m3_outb(chip, chip->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+
+/*
+ * This makes me sad. the maestro3 has lists
+ * internally that must be packed.. 0 terminates,
+ * apparently, or maybe all unused entries have
+ * to be 0, the lists have static lengths set
+ * by the binary code images.
+ */
+
+static int snd_m3_add_list(m3_t *chip, struct m3_list *list, u16 val)
+{
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  list->mem_addr + list->curlen,
+			  val);
+	return list->curlen++;
+}
+
+static void snd_m3_remove_list(m3_t *chip, struct m3_list *list, int index)
+{
+	u16  val;
+	int lastindex = list->curlen - 1;
+
+	if (index != lastindex) {
+		val = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+				       list->mem_addr + lastindex);
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  list->mem_addr + index,
+				  val);
+	}
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  list->mem_addr + lastindex,
+			  0);
+
+	list->curlen--;
+}
+
+static void snd_m3_inc_timer_users(m3_t *chip)
+{
+	chip->timer_users++;
+	if (chip->timer_users != 1) 
+		return;
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_TIMER_COUNT_RELOAD,
+			  240);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_TIMER_COUNT_CURRENT,
+			  240);
+
+	snd_m3_outw(chip,
+		    snd_m3_inw(chip, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE,
+		    HOST_INT_CTRL);
+}
+
+static void snd_m3_dec_timer_users(m3_t *chip)
+{
+	chip->timer_users--;
+	if (chip->timer_users > 0)  
+		return;
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_TIMER_COUNT_RELOAD,
+			  0);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_TIMER_COUNT_CURRENT,
+			  0);
+
+	snd_m3_outw(chip,
+		    snd_m3_inw(chip, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE,
+		    HOST_INT_CTRL);
+}
+
+/*
+ * start/stop
+ */
+
+/* spinlock held! */
+static int snd_m3_pcm_start(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+	if (! s || ! subs)
+		return -EINVAL;
+
+	snd_m3_inc_timer_users(chip);
+	switch (subs->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		chip->dacs_active++;
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  s->inst.data + CDATA_INSTANCE_READY, 1);
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  KDATA_MIXER_TASK_NUMBER,
+				  chip->dacs_active);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		snd_m3_assp_write(s->chip, MEMTYPE_INTERNAL_DATA,
+				  KDATA_ADC1_REQUEST, 1);
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  s->inst.data + CDATA_INSTANCE_READY, 1);
+		break;
+	}
+	return 0;
+}
+
+/* spinlock held! */
+static int snd_m3_pcm_stop(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+	if (! s || ! subs)
+		return -EINVAL;
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_INSTANCE_READY, 0);
+	snd_m3_dec_timer_users(chip);
+	switch (subs->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		chip->dacs_active--;
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  KDATA_MIXER_TASK_NUMBER, 
+				  chip->dacs_active);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  KDATA_ADC1_REQUEST, 0);
+		break;
+	}
+	return 0;
+}
+
+static int
+snd_m3_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+	m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
+	unsigned long flags;
+	int err = -EINVAL;
+
+	snd_assert(s != NULL, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (s->running)
+			err = -EBUSY;
+		else {
+			s->running = 1;
+			err = snd_m3_pcm_start(chip, s, subs);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (! s->running)
+			err = 0; /* should return error? */
+		else {
+			s->running = 0;
+			err = snd_m3_pcm_stop(chip, s, subs);
+		}
+		break;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return err;
+}
+
+/*
+ * setup
+ */
+static void 
+snd_m3_pcm_setup1(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+	int dsp_in_size, dsp_out_size, dsp_in_buffer, dsp_out_buffer;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+
+	if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2);
+		dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2);
+	} else {
+		dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x10 * 2);
+		dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2);
+	}
+	dsp_in_buffer = s->inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+	dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+
+	s->dma_size = frames_to_bytes(runtime, runtime->buffer_size);
+	s->period_size = frames_to_bytes(runtime, runtime->period_size);
+	s->hwptr = 0;
+	s->count = 0;
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+	/* host dma buffer pointers */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_HOST_SRC_ADDRL,
+			  LO(s->buffer_addr));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_HOST_SRC_ADDRH,
+			  HI(s->buffer_addr));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+			  LO(s->buffer_addr + s->dma_size));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+			  HI(s->buffer_addr + s->dma_size));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_HOST_SRC_CURRENTL,
+			  LO(s->buffer_addr));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_HOST_SRC_CURRENTH,
+			  HI(s->buffer_addr));
+#undef LO
+#undef HI
+
+	/* dsp buffers */
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_IN_BUF_BEGIN,
+			  dsp_in_buffer);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_IN_BUF_END_PLUS_1,
+			  dsp_in_buffer + (dsp_in_size / 2));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_IN_BUF_HEAD,
+			  dsp_in_buffer);
+    
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_IN_BUF_TAIL,
+			  dsp_in_buffer);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_OUT_BUF_BEGIN,
+			  dsp_out_buffer);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_OUT_BUF_END_PLUS_1,
+			  dsp_out_buffer + (dsp_out_size / 2));
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_OUT_BUF_HEAD,
+			  dsp_out_buffer);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_OUT_BUF_TAIL,
+			  dsp_out_buffer);
+}
+
+static void snd_m3_pcm_setup2(m3_t *chip, m3_dma_t *s, snd_pcm_runtime_t *runtime)
+{
+	u32 freq;
+
+	/* 
+	 * put us in the lists if we're not already there
+	 */
+	if (! s->in_lists) {
+		s->index[0] = snd_m3_add_list(chip, s->index_list[0],
+					      s->inst.data >> DP_SHIFT_COUNT);
+		s->index[1] = snd_m3_add_list(chip, s->index_list[1],
+					      s->inst.data >> DP_SHIFT_COUNT);
+		s->index[2] = snd_m3_add_list(chip, s->index_list[2],
+					      s->inst.data >> DP_SHIFT_COUNT);
+		s->in_lists = 1;
+	}
+
+	/* write to 'mono' word */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + SRC3_DIRECTION_OFFSET + 1, 
+			  runtime->channels == 2 ? 0 : 1);
+	/* write to '8bit' word */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + SRC3_DIRECTION_OFFSET + 2, 
+			  snd_pcm_format_width(runtime->format) == 16 ? 0 : 1);
+
+	/* set up dac/adc rate */
+	freq = ((runtime->rate << 15) + 24000 ) / 48000;
+	if (freq) 
+		freq--;
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_FREQUENCY,
+			  freq);
+}
+
+
+static struct play_vals {
+	u16 addr, val;
+} pv[] = {
+	{CDATA_LEFT_VOLUME, ARB_VOLUME},
+	{CDATA_RIGHT_VOLUME, ARB_VOLUME},
+	{SRC3_DIRECTION_OFFSET, 0} ,
+	/* +1, +2 are stereo/16 bit */
+	{SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+	{SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+	{SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+	{SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+	{SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+	{SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+	{SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+	{SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+	{SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+	{SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+	{SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+	{SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+	{SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */
+	{SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */
+	{SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */
+	{SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+	{SRC3_DIRECTION_OFFSET + 21, 0} /* booster */
+};
+
+
+/* the mode passed should be already shifted and masked */
+static void
+snd_m3_playback_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+	int i;
+
+	/*
+	 * some per client initializers
+	 */
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + SRC3_DIRECTION_OFFSET + 12,
+			  s->inst.data + 40 + 8);
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + SRC3_DIRECTION_OFFSET + 19,
+			  s->inst.code + MINISRC_COEF_LOC);
+
+	/* enable or disable low pass filter? */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + SRC3_DIRECTION_OFFSET + 22,
+			  subs->runtime->rate > 45000 ? 0xff : 0);
+    
+	/* tell it which way dma is going? */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_DMA_CONTROL,
+			  DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+	/*
+	 * set an armload of static initializers
+	 */
+	for (i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) 
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  s->inst.data + pv[i].addr, pv[i].val);
+}
+
+/*
+ *    Native record driver 
+ */
+static struct rec_vals {
+	u16 addr, val;
+} rv[] = {
+	{CDATA_LEFT_VOLUME, ARB_VOLUME},
+	{CDATA_RIGHT_VOLUME, ARB_VOLUME},
+	{SRC3_DIRECTION_OFFSET, 1} ,
+	/* +1, +2 are stereo/16 bit */
+	{SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+	{SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+	{SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+	{SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+	{SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+	{SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+	{SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+	{SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+	{SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+	{SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+	{SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+	{SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+	{SRC3_DIRECTION_OFFSET + 16, 50},/* numin */
+	{SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */
+	{SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */
+	{SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */
+	{SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+	{SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */
+	{SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */
+};
+
+static void
+snd_m3_capture_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+	int i;
+
+	/*
+	 * some per client initializers
+	 */
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + SRC3_DIRECTION_OFFSET + 12,
+			  s->inst.data + 40 + 8);
+
+	/* tell it which way dma is going? */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  s->inst.data + CDATA_DMA_CONTROL,
+			  DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + 
+			  DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+	/*
+	 * set an armload of static initializers
+	 */
+	for (i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) 
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  s->inst.data + rv[i].addr, rv[i].val);
+}
+
+static int snd_m3_pcm_hw_params(snd_pcm_substream_t * substream,
+				snd_pcm_hw_params_t * hw_params)
+{
+	m3_dma_t *s = (m3_dma_t*) substream->runtime->private_data;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	/* set buffer address */
+	s->buffer_addr = substream->runtime->dma_addr;
+	if (s->buffer_addr & 0x3) {
+		snd_printk("oh my, not aligned\n");
+		s->buffer_addr = s->buffer_addr & ~0x3;
+	}
+	return 0;
+}
+
+static int snd_m3_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+	m3_dma_t *s;
+	
+	if (substream->runtime->private_data == NULL)
+		return 0;
+	s = (m3_dma_t*) substream->runtime->private_data;
+	snd_pcm_lib_free_pages(substream);
+	s->buffer_addr = 0;
+	return 0;
+}
+
+static int
+snd_m3_pcm_prepare(snd_pcm_substream_t *subs)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	m3_dma_t *s = (m3_dma_t*)runtime->private_data;
+	unsigned long flags;
+
+	snd_assert(s != NULL, return -ENXIO);
+
+	if (runtime->format != SNDRV_PCM_FORMAT_U8 &&
+	    runtime->format != SNDRV_PCM_FORMAT_S16_LE)
+		return -EINVAL;
+	if (runtime->rate > 48000 ||
+	    runtime->rate < 8000)
+		return -EINVAL;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	snd_m3_pcm_setup1(chip, s, subs);
+
+	if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_m3_playback_setup(chip, s, subs);
+	else
+		snd_m3_capture_setup(chip, s, subs);
+
+	snd_m3_pcm_setup2(chip, s, runtime);
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return 0;
+}
+
+/*
+ * get current pointer
+ */
+static unsigned int
+snd_m3_get_pointer(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+	u16 hi = 0, lo = 0;
+	int retry = 10;
+	u32 addr;
+
+	/*
+	 * try and get a valid answer
+	 */
+	while (retry--) {
+		hi =  snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+				       s->inst.data + CDATA_HOST_SRC_CURRENTH);
+
+		lo = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+				      s->inst.data + CDATA_HOST_SRC_CURRENTL);
+
+		if (hi == snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+					   s->inst.data + CDATA_HOST_SRC_CURRENTH))
+			break;
+	}
+	addr = lo | ((u32)hi<<16);
+	return (unsigned int)(addr - s->buffer_addr);
+}
+
+static snd_pcm_uframes_t
+snd_m3_pcm_pointer(snd_pcm_substream_t * subs)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+	m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
+	snd_assert(s != NULL, return 0);
+	return bytes_to_frames(subs->runtime, snd_m3_get_pointer(chip, s, subs));
+}
+
+
+/* update pointer */
+/* spinlock held! */
+static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s)
+{
+	snd_pcm_substream_t *subs = s->substream;
+	unsigned int hwptr;
+	int diff;
+
+	if (! s->running)
+		return;
+
+	hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size;
+	diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size;
+	s->hwptr = hwptr;
+	s->count += diff;
+	while (s->count >= (signed)s->period_size) {
+		s->count -= s->period_size;
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(subs);
+		spin_lock(&chip->reg_lock);
+	}
+}
+
+static void 
+snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	m3_t *chip = snd_magic_cast(m3_t, dev_id, );
+	u8 status;
+	int i;
+
+	status = inb(chip->iobase + 0x1A);
+
+	if (status == 0xff)
+		return;
+   
+	/* presumably acking the ints? */
+	outw(status, chip->iobase + 0x1A);
+
+	/*if (in_suspend)
+		return;*/
+
+	/*
+	 * ack an assp int if its running
+	 * and has an int pending
+	 */
+	if (status & ASSP_INT_PENDING) {
+		u8 ctl = inb(chip->iobase + ASSP_CONTROL_B);
+		if (!(ctl & STOP_ASSP_CLOCK)) {
+			ctl = inb(chip->iobase + ASSP_HOST_INT_STATUS);
+			if (ctl & DSP2HOST_REQ_TIMER) {
+				outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS);
+				/* update adc/dac info if it was a timer int */
+				spin_lock(&chip->reg_lock);
+				for (i = 0; i < chip->num_substreams; i++) {
+					m3_dma_t *s = &chip->substreams[i];
+					if (s->running)
+						snd_m3_update_ptr(chip, s);
+				}
+				spin_unlock(&chip->reg_lock);
+			}
+		}
+	}
+
+	/* XXX is this needed? */
+	if (status & 0x40) 
+		outb(0x40, chip->iobase+0x1A);
+}
+
+
+/*
+ */
+
+static snd_pcm_hardware_t snd_m3_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 /*SNDRV_PCM_INFO_PAUSE |*/
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(512*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(512*1024),
+	periods_min:		1,
+	periods_max:		1024,
+};
+
+static snd_pcm_hardware_t snd_m3_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 /*SNDRV_PCM_INFO_PAUSE |*/
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(512*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(512*1024),
+	periods_min:		1,
+	periods_max:		1024,
+};
+
+
+/*
+ */
+
+static int
+snd_m3_substream_open(m3_t *chip, snd_pcm_substream_t *subs)
+{
+	int i;
+	m3_dma_t *s;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < chip->num_substreams; i++) {
+		s = &chip->substreams[i];
+		if (! s->opened)
+			goto __found;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return -ENOMEM;
+__found:
+	s->opened = 1;
+	s->running = 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	subs->runtime->private_data = s;
+	s->substream = subs;
+
+	/* set list owners */
+	if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		s->index_list[0] = &chip->mixer_list;
+	} else
+		s->index_list[0] = &chip->adc1_list;
+	s->index_list[1] = &chip->msrc_list;
+	s->index_list[2] = &chip->dma_list;
+
+	return 0;
+}
+
+static void
+snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs)
+{
+	m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data;
+	unsigned long flags;
+
+	if (s == NULL)
+		return; /* not opened properly */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (s->substream && s->running)
+		snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */
+	if (s->in_lists) {
+		snd_m3_remove_list(chip, s->index_list[0], s->index[0]);
+		snd_m3_remove_list(chip, s->index_list[1], s->index[1]);
+		snd_m3_remove_list(chip, s->index_list[2], s->index[2]);
+		s->in_lists = 0;
+	}
+	s->running = 0;
+	s->opened = 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static int
+snd_m3_playback_open(snd_pcm_substream_t *subs)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	int err;
+
+	if ((err = snd_m3_substream_open(chip, subs)) < 0)
+		return err;
+
+	runtime->hw = snd_m3_playback;
+	snd_pcm_set_sync(subs);
+
+	return 0;
+}
+
+static int
+snd_m3_playback_close(snd_pcm_substream_t *subs)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+
+	snd_m3_substream_close(chip, subs);
+	return 0;
+}
+
+static int
+snd_m3_capture_open(snd_pcm_substream_t *subs)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	int err;
+
+	if ((err = snd_m3_substream_open(chip, subs)) < 0)
+		return err;
+
+	runtime->hw = snd_m3_capture;
+	snd_pcm_set_sync(subs);
+
+	return 0;
+}
+
+static int
+snd_m3_capture_close(snd_pcm_substream_t *subs)
+{
+	m3_t *chip = snd_pcm_substream_chip(subs);
+
+	snd_m3_substream_close(chip, subs);
+	return 0;
+}
+
+/*
+ * create pcm instance
+ */
+
+static snd_pcm_ops_t snd_m3_playback_ops = {
+	open:		snd_m3_playback_open,
+	close:		snd_m3_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_m3_pcm_hw_params,
+	hw_free:	snd_m3_pcm_hw_free,
+	prepare:	snd_m3_pcm_prepare,
+	trigger:	snd_m3_pcm_trigger,
+	pointer:	snd_m3_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_m3_capture_ops = {
+	open:		snd_m3_capture_open,
+	close:		snd_m3_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_m3_pcm_hw_params,
+	hw_free:	snd_m3_pcm_hw_free,
+	prepare:	snd_m3_pcm_prepare,
+	trigger:	snd_m3_pcm_trigger,
+	pointer:	snd_m3_pcm_pointer,
+};
+
+static int __devinit
+snd_m3_pcm(m3_t * chip, int device)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	err = snd_pcm_new(chip->card, chip->card->driver, device,
+			  MAX_PLAYBACKS, MAX_CAPTURES, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_m3_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_m3_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->driver);
+	chip->pcm = pcm;
+	
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
+
+	return 0;
+}
+
+
+/*
+ * ac97 interface
+ */
+
+/*
+ * Wait for the ac97 serial bus to be free.
+ * return nonzero if the bus is still busy.
+ */
+static int snd_m3_ac97_wait(m3_t *chip)
+{
+	int i = 10000;
+
+	do {
+		if (! (snd_m3_inb(chip, 0x30) & 1))
+			return 0;
+	} while (i-- > 0);
+
+	snd_printk("ac97 serial bus busy\n");
+	return 1;
+}
+
+static unsigned short
+snd_m3_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return -ENXIO);
+	unsigned short ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (snd_m3_ac97_wait(chip))
+		goto __error;
+	snd_m3_outb(chip, 0x80 | (reg & 0x7f), 0x30);
+	if (snd_m3_ac97_wait(chip))
+		goto __error;
+	ret = snd_m3_inw(chip, 0x32);
+__error:
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return ret;
+}
+
+static void
+snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (snd_m3_ac97_wait(chip))
+		goto __error;
+	snd_m3_outw(chip, val, 0x32);
+	snd_m3_outb(chip, reg & 0x7f, 0x30);
+__error:
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+
+static void snd_m3_remote_codec_config(int io, int isremote)
+{
+	isremote = isremote ? 1 : 0;
+
+	outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
+	     io + RING_BUS_CTRL_B);
+	outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
+	     io + SDO_OUT_DEST_CTRL);
+	outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
+	     io + SDO_IN_DEST_CTRL);
+}
+
+/* 
+ * hack, returns non zero on err 
+ */
+static int snd_m3_try_read_vendor(m3_t *chip)
+{
+	u16 ret;
+
+	if (snd_m3_ac97_wait(chip))
+		return 1;
+
+	snd_m3_outb(chip, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30);
+
+	if (snd_m3_ac97_wait(chip))
+		return 1;
+
+	ret = snd_m3_inw(chip, 0x32);
+
+	return (ret == 0) || (ret == 0xffff);
+}
+
+static void snd_m3_ac97_reset(m3_t *chip, int busywait)
+{
+	u16 dir;
+	int delay1 = 0, delay2 = 0, i;
+	int io = chip->iobase;
+
+	if (chip->allegro_flag) {
+		/*
+		 * the onboard codec on the allegro seems 
+		 * to want to wait a very long time before
+		 * coming back to life 
+		 */
+		delay1 = 50;
+		delay2 = 800;
+	} else {
+		/* maestro3 */
+		delay1 = 20;
+		delay2 = 500;
+	}
+
+	for (i = 0; i < 5; i++) {
+		dir = inw(io + GPIO_DIRECTION);
+		if (chip->subsystem_vendor == 0x1028 &&
+		    chip->subsystem_device == 0x00b0) /* Dell Inspiron 4000 */
+			; /* seems conflicting with IrDA */
+		else
+			dir |= 0x10; /* assuming pci bus master? */
+
+		snd_m3_remote_codec_config(io, 0);
+
+		outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
+		udelay(20);
+
+		outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION);
+		outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK);
+		outw(0, io + GPIO_DATA);
+		outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION);
+
+		if (busywait)  {
+			mdelay(delay1);
+		} else {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((delay1 * HZ) / 1000);
+		}
+
+		outw(GPO_PRIMARY_AC97, io + GPIO_DATA);
+		udelay(5);
+		/* ok, bring back the ac-link */
+		outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A);
+		outw(~0, io + GPIO_MASK);
+
+		if (busywait) {
+			mdelay(delay2);
+		} else {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((delay2 * HZ) / 1000);
+		}
+		if (! snd_m3_try_read_vendor(chip))
+			break;
+
+		delay1 += 10;
+		delay2 += 100;
+
+		snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n",
+			   delay1, delay2);
+	}
+
+#if 0
+	/* more gung-ho reset that doesn't
+	 * seem to work anywhere :)
+	 */
+	tmp = inw(io + RING_BUS_CTRL_A);
+	outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A);
+	mdelay(20);
+	outw(tmp, io + RING_BUS_CTRL_A);
+	mdelay(50);
+#endif
+}
+
+static int snd_m3_mixer(m3_t *chip)
+{
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_m3_ac97_write;
+	ac97.read = snd_m3_ac97_read;
+	ac97.private_data = chip;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+
+	/* seems ac97 PCM needs initialization.. hack hack.. */
+	snd_ac97_write(chip->ac97, AC97_PCM, 0x8000 | (15 << 8) | 15);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ / 10);
+	snd_ac97_write(chip->ac97, AC97_PCM, 0);
+
+	return 0;
+}
+
+
+/*
+ * DSP Code images
+ */
+
+static u16 assp_kernel_image[] = {
+    0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, 
+    0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, 
+    0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, 
+    0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, 
+    0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, 
+    0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, 
+    0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, 
+    0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, 
+    0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, 
+    0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, 
+    0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, 
+    0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, 
+    0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, 
+    0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, 
+    0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, 
+    0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, 
+    0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, 
+    0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, 
+    0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, 
+    0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, 
+    0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, 
+    0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, 
+    0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, 
+    0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, 
+    0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, 
+    0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, 
+    0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, 
+    0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, 
+    0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, 
+    0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, 
+    0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, 
+    0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, 
+    0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 
+    0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, 
+    0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, 
+    0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, 
+    0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, 
+    0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 
+    0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, 
+    0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, 
+    0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, 
+    0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, 
+    0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, 
+    0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 
+    0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, 
+    0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, 
+    0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, 
+    0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 
+    0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, 
+    0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, 
+    0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, 
+    0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, 
+    0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, 
+    0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, 
+    0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, 
+    0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, 
+    0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 
+    0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, 
+    0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 
+    0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, 
+    0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, 
+    0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, 
+    0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, 
+    0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, 
+    0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, 
+    0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, 
+    0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, 
+    0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, 
+    0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, 
+    0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, 
+    0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, 
+    0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, 
+    0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, 
+    0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, 
+    0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, 
+    0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, 
+    0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, 
+    0xBE3A, 
+};
+
+/*
+ * Mini sample rate converter code image
+ * that is to be loaded at 0x400 on the DSP.
+ */
+static u16 assp_minisrc_image[] = {
+
+    0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, 
+    0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, 
+    0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, 
+    0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, 
+    0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, 
+    0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, 
+    0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, 
+    0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, 
+    0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, 
+    0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, 
+    0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, 
+    0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, 
+    0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, 
+    0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, 
+    0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, 
+    0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, 
+    0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, 
+    0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, 
+    0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, 
+    0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, 
+    0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, 
+    0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, 
+    0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, 
+    0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, 
+    0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, 
+    0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, 
+    0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, 
+    0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, 
+    0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, 
+    0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, 
+    0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, 
+    0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+
+
+/*
+ * initialize ASSP
+ */
+
+#define MINISRC_LPF_LEN 10
+static u16 minisrc_lpf[MINISRC_LPF_LEN] = {
+	0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
+	0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
+};
+
+static void snd_m3_assp_init(m3_t *chip)
+{
+	int i;
+
+	/* zero kernel data */
+	for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, 
+				  KDATA_BASE_ADDR + i, 0);
+
+	/* zero mixer data? */
+	for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  KDATA_BASE_ADDR2 + i, 0);
+
+	/* init dma pointer */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_CURRENT_DMA,
+			  KDATA_DMA_XFER0);
+
+	/* write kernel into code memory.. */
+	for (i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) {
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, 
+				  REV_B_CODE_MEMORY_BEGIN + i, 
+				  assp_kernel_image[i]);
+	}
+
+	/*
+	 * We only have this one client and we know that 0x400
+	 * is free in our kernel's mem map, so lets just
+	 * drop it there.  It seems that the minisrc doesn't
+	 * need vectors, so we won't bother with them..
+	 */
+	for (i = 0; i < sizeof(assp_minisrc_image) / 2; i++) {
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, 
+				  0x400 + i, 
+				  assp_minisrc_image[i]);
+	}
+
+	/*
+	 * write the coefficients for the low pass filter?
+	 */
+	for (i = 0; i < MINISRC_LPF_LEN ; i++) {
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+				  0x400 + MINISRC_COEF_LOC + i,
+				  minisrc_lpf[i]);
+	}
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+			  0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN,
+			  0x8000);
+
+	/*
+	 * the minisrc is the only thing on
+	 * our task list..
+	 */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, 
+			  KDATA_TASK0,
+			  0x400);
+
+	/*
+	 * init the mixer number..
+	 */
+
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_MIXER_TASK_NUMBER,0);
+
+	/*
+	 * EXTREME KERNEL MASTER VOLUME
+	 */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_DAC_LEFT_VOLUME, ARB_VOLUME);
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+			  KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME);
+
+	chip->mixer_list.curlen = 0;
+	chip->mixer_list.mem_addr = KDATA_MIXER_XFER0;
+	chip->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS;
+	chip->adc1_list.curlen = 0;
+	chip->adc1_list.mem_addr = KDATA_ADC1_XFER0;
+	chip->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS;
+	chip->dma_list.curlen = 0;
+	chip->dma_list.mem_addr = KDATA_DMA_XFER0;
+	chip->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS;
+	chip->msrc_list.curlen = 0;
+	chip->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC;
+	chip->msrc_list.max = MAX_INSTANCE_MINISRC;
+}
+
+
+static int snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index)
+{
+	int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + 
+			       MINISRC_IN_BUFFER_SIZE / 2 +
+			       1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 );
+	int address, i;
+
+	/*
+	 * the revb memory map has 0x1100 through 0x1c00
+	 * free.  
+	 */
+
+	/*
+	 * align instance address to 256 bytes so that it's
+	 * shifted list address is aligned.
+	 * list address = (mem address >> 1) >> 7;
+	 */
+	data_bytes = (data_bytes + 255) & ~255;
+	address = 0x1100 + ((data_bytes/2) * index);
+
+	if ((address + (data_bytes/2)) >= 0x1c00) {
+		snd_printk("no memory for %d bytes at ind %d (addr 0x%x)\n",
+			   data_bytes, index, address);
+		return -ENOMEM;
+	}
+
+	s->number = index;
+	s->inst.code = 0x400;
+	s->inst.data = address;
+
+	for (i = data_bytes / 2; i > 0; address++, i--) {
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+				  address, 0);
+	}
+
+	return 0;
+}
+
+
+/* 
+ * this works for the reference board, have to find
+ * out about others
+ *
+ * this needs more magic for 4 speaker, but..
+ */
+static void
+snd_m3_amp_enable(m3_t *chip, int enable)
+{
+	int io = chip->iobase;
+	u16 gpo, polarity;
+
+	if (! chip->external_amp)
+		return;
+
+	polarity = enable ? 0 : 1;
+	polarity = polarity << chip->amp_gpio;
+	gpo = 1 << chip->amp_gpio;
+
+	outw(~gpo, io + GPIO_MASK);
+
+	outw(inw(io + GPIO_DIRECTION) | gpo,
+	     io + GPIO_DIRECTION);
+
+	outw((GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity),
+	     io + GPIO_DATA);
+
+	outw(0xffff, io + GPIO_MASK);
+}
+
+static int
+snd_m3_chip_init(m3_t *chip)
+{
+	struct pci_dev *pcidev = chip->pci;
+	u32 n;
+	u8 t; /* makes as much sense as 'n', no? */
+
+	pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+	n &= REDUCED_DEBOUNCE;
+	n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
+	pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+	outb(RESET_ASSP, chip->iobase + ASSP_CONTROL_B);
+	pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+	n &= ~INT_CLK_SELECT;
+	if (!chip->allegro_flag) {
+		n &= ~INT_CLK_MULT_ENABLE; 
+		n |= INT_CLK_SRC_NOT_PCI;
+	}
+	n &=  ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 );
+	pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+	if (chip->allegro_flag) {
+		pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n);
+		n |= IN_CLK_12MHZ_SELECT;
+		pci_write_config_dword(pcidev, PCI_USER_CONFIG, n);
+	}
+
+	t = inb(chip->iobase + ASSP_CONTROL_A);
+	t &= ~( DSP_CLK_36MHZ_SELECT  | ASSP_CLK_49MHZ_SELECT);
+	t |= ASSP_CLK_49MHZ_SELECT;
+	t |= ASSP_0_WS_ENABLE; 
+	outb(t, chip->iobase + ASSP_CONTROL_A);
+
+	outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); 
+
+	return 0;
+} 
+
+static void
+snd_m3_enable_ints(m3_t *chip)
+{
+	unsigned long io = chip->iobase;
+
+	outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL);
+	outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
+	     io + ASSP_CONTROL_C);
+}
+
+
+/*
+ */
+
+static int snd_m3_free(m3_t *chip)
+{
+	unsigned long flags;
+	m3_dma_t *s;
+	int i;
+
+	if (chip->substreams) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		for (i = 0; i < chip->num_substreams; i++) {
+			s = &chip->substreams[i];
+			/* check surviving pcms; this should not happen though.. */
+			if (s->substream && s->running)
+				snd_m3_pcm_stop(chip, s, s->substream);
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		kfree(chip->substreams);
+	}
+#ifdef CONFIG_PM
+	if (chip->suspend_mem)
+		vfree(chip->suspend_mem);
+#endif
+
+	synchronize_irq();
+
+	if (chip->iobase_res) {
+		release_resource(chip->iobase_res);
+		kfree_nocheck(chip->iobase_res);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+
+/*
+ * APM support
+ */
+#ifdef CONFIG_PM
+
+static void m3_suspend(m3_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int i, index;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	snd_pcm_suspend_all(chip->pcm);
+
+	mdelay(10); /* give the assp a chance to idle.. */
+
+	snd_m3_assp_halt(chip);
+
+	/* save dsp image */
+	index = 0;
+	for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
+		chip->suspend_mem[index++] = 
+			snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i);
+	for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+		chip->suspend_mem[index++] = 
+			snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i);
+
+	/* power down apci registers */
+	snd_m3_outw(chip, 0xffff, 0x54);
+	snd_m3_outw(chip, 0xffff, 0x56);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void m3_resume(m3_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int i, index;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	/* first lets just bring everything back. .*/
+	snd_m3_outw(chip, 0, 0x54);
+	snd_m3_outw(chip, 0, 0x56);
+
+	snd_m3_chip_init(chip);
+	snd_m3_assp_halt(chip);
+	snd_m3_ac97_reset(chip, 1);
+
+	/* restore dsp image */
+	index = 0;
+	for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, 
+				  chip->suspend_mem[index++]);
+	for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+		snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, 
+				  chip->suspend_mem[index++]);
+
+	/* tell the dma engine to restart itself */
+	snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, 
+			  KDATA_DMA_ACTIVE, 0);
+
+        /* restore ac97 registers */
+	snd_ac97_resume(chip->ac97);
+
+	snd_m3_assp_continue(chip);
+	snd_m3_enable_ints(chip);
+	snd_m3_amp_enable(chip, 1);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+#ifndef PCI_OLD_SUSPEND
+static int snd_m3_suspend(struct pci_dev *pci, u32 state)
+{
+	m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO);
+	m3_suspend(chip);
+	return 0;
+}
+static int snd_m3_resume(struct pci_dev *pci)
+{
+	m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO);
+	m3_resume(chip);
+	return 0;
+}
+#else
+static void snd_m3_suspend(struct pci_dev *pci)
+{
+	m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return);
+	m3_suspend(chip);
+}
+static void snd_m3_resume(struct pci_dev *pci)
+{
+	m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return);
+	m3_resume(chip);
+}
+#endif
+
+/* callback */
+static int snd_m3_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	m3_t *chip = snd_magic_cast(m3_t, card->power_state_private_data, return -ENXIO);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		m3_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		m3_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+/*
+ */
+
+static int snd_m3_dev_free(snd_device_t *device)
+{
+	m3_t *chip = snd_magic_cast(m3_t, device->device_data, return -ENXIO);
+	return snd_m3_free(chip);
+}
+
+static int __devinit
+snd_m3_create(snd_card_t *card, struct pci_dev *pci,
+	      int enable_amp,
+	      int amp_gpio,
+	      m3_t **chip_ret)
+{
+	m3_t *chip;
+	int i, err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_m3_dev_free,
+	};
+
+	*chip_ret = NULL;
+
+	if (pci_enable_device(pci))
+		return -EIO;
+
+	/* check, if we can restrict PCI DMA transfers to 28 bits */
+	if (!pci_dma_supported(pci, 0x0fffffff)) {
+		snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x0fffffff);
+
+	chip = snd_magic_kcalloc(m3_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&chip->reg_lock);
+	switch (pci->device) {
+	case PCI_DEVICE_ID_ESS_ALLEGRO:
+	case PCI_DEVICE_ID_ESS_ALLEGRO_1:
+	case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
+	case PCI_DEVICE_ID_ESS_CANYON3D_2:
+		chip->allegro_flag = 1;
+		break;
+	}
+
+#ifndef LINUX_2_2
+	chip->subsystem_vendor = pci->subsystem_vendor;
+	chip->subsystem_device = pci->subsystem_device;
+#else
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->subsystem_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->subsystem_device);
+#endif
+
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->external_amp = enable_amp;
+	if (amp_gpio >= 0 && amp_gpio <= 0x0f)
+		chip->amp_gpio = amp_gpio;
+	else if (chip->allegro_flag) {
+		/* panasonic CF-28 "toughbook" has different GPIO connection.. */
+		if (chip->subsystem_vendor == 0x10f7 &&
+		    chip->subsystem_device == 0x833e)
+			chip->amp_gpio = 0x0d;
+		else
+			chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
+	} else
+		chip->amp_gpio = GPO_EXT_AMP_M3; /* presumably this is for all 'maestro3's.. */
+	chip->num_substreams = NR_DSPS;
+	chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL);
+	if (chip->substreams == NULL) {
+		snd_magic_kfree(chip);
+		return -ENOMEM;
+	}
+	memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams);
+
+	chip->iobase = pci_resource_start(pci, 0);
+	if ((chip->iobase_res = request_region(chip->iobase, 256,
+					       card->driver)) == NULL) {
+		snd_m3_free(chip);
+		snd_printk("unable to grab i/o ports %ld\n", chip->iobase);
+		return -EBUSY;
+	}
+	
+	/* just to be sure */
+	pci_set_master(pci);
+
+	snd_m3_chip_init(chip);
+	snd_m3_assp_halt(chip);
+
+	snd_m3_ac97_reset(chip, 0);
+
+	if ((err = snd_m3_mixer(chip)) < 0) {
+		snd_m3_free(chip);
+		return err;
+	}
+
+	snd_m3_assp_init(chip);
+	snd_m3_amp_enable(chip, 1);
+    
+	for (i = 0; i < chip->num_substreams; i++) {
+		m3_dma_t *s = &chip->substreams[i];
+		s->chip = chip;
+		if ((err = snd_m3_assp_client_init(chip, s, i)) < 0) {
+			snd_m3_free(chip);
+			return err;
+		}
+	}
+    
+	if ((err = snd_m3_pcm(chip, 0)) < 0) {
+		snd_m3_free(chip);
+		return err;
+	}
+    
+	if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			card->driver, (void *)chip)) {
+		snd_m3_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -ENOMEM;
+	}
+	chip->irq = pci->irq;
+
+#ifdef CONFIG_PM
+	chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH));
+	if (chip->suspend_mem == NULL)
+		snd_printk("can't allocate apm buffer\n");
+	card->set_power_state = snd_m3_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_m3_free(chip);
+		return err;
+	}
+
+	snd_m3_enable_ints(chip);
+	snd_m3_assp_continue(chip);
+
+	*chip_ret = chip;
+
+	return 0; 
+}
+
+/*
+ */
+static int __devinit
+snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	m3_t *chip;
+	int err;
+
+	/* don't pick up modems */
+	if (((pci->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+		return 0;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	switch (pci->device) {
+	case PCI_DEVICE_ID_ESS_ALLEGRO:
+	case PCI_DEVICE_ID_ESS_ALLEGRO_1:
+		strcpy(card->driver, "Allegro");
+		break;
+	case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
+	case PCI_DEVICE_ID_ESS_CANYON3D_2:
+		strcpy(card->driver, "Canyon3D-2");
+		break;
+	default:
+		strcpy(card->driver, "Maestro3");
+		break;
+	}
+
+	if ((err = snd_m3_create(card, pci,
+				 snd_external_amp[dev],
+				 snd_amp_gpio[dev],
+				 &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->shortname, "ESS %s PCI", card->driver);
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, chip->iobase, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_m3_remove(struct pci_dev *pci)
+{
+	m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "Maestro3",
+	id_table: snd_m3_ids,
+	probe: snd_m3_probe,
+	remove: __devexit_p(snd_m3_remove),
+#ifdef CONFIG_PM
+	suspend: snd_m3_suspend,
+	resume: snd_m3_resume,
+#endif
+};
+	
+static int __init alsa_card_m3_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Maestro3/Allegro soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_m3_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_m3_init)
+module_exit(alsa_card_m3_exit)
+
+#ifndef MODULE
+
+/* format is: snd-maestro3=snd_enable,snd_index,snd_id,snd_external_amp,snd_amp_gpio */
+
+static int __init alsa_card_maestro3_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_external_amp[nr_dev]) == 2 &&
+	       get_option(&str,&snd_amp_gpio[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-maestro3=", alsa_card_maestro3_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/nm256/Makefile linux-2.4.19-pre5-mjc/sound/pci/nm256/Makefile
--- linux/sound/pci/nm256/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/nm256/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _nm256.o
+
+list-multi   := snd-nm256.o
+
+snd-nm256-objs := nm256.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_NM256) += snd-nm256.o
+
+include $(TOPDIR)/Rules.make
+
+snd-nm256.o: $(snd-nm256-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-nm256-objs)
diff -Nru linux/sound/pci/nm256/nm256.c linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256.c
--- linux/sound/pci/nm256/nm256.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1696 @@
+/* 
+ * Driver for NeoMagic 256AV and 256ZX chipsets.
+ * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ * Based on nm256_audio.c OSS driver in linux kernel.
+ * The original author of OSS nm256 driver wishes to remain anonymous,
+ * so I just put my acknoledgment to him/her here.
+ * The original author's web page is found at
+ *	http://www.uglx.org/sony.html
+ *
+ *
+ *   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
+ */
+  
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define CARD_NAME "NeoMagic 256AV/ZX"
+#define DRIVER_NAME "NM256"
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("NeoMagic NM256AV/ZX");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{NeoMagic,NM256AV},"
+		"{NeoMagic,NM256ZX}}");
+
+/*
+ * some compile conditions.
+ */
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int snd_playback_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16};
+static int snd_capture_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16};
+static int snd_force_ac97[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled as default */
+static int snd_buffer_top[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* not specified */
+static int snd_use_cache[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */
+static int snd_vaio_hack[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable this soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_playback_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_playback_bufsize, SNDRV_ENABLED);
+MODULE_PARM(snd_capture_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_capture_bufsize, SNDRV_ENABLED);
+MODULE_PARM(snd_force_ac97, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_force_ac97, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+MODULE_PARM(snd_buffer_top, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(snd_buffer_top, SNDRV_ENABLED);
+MODULE_PARM(snd_use_cache, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_use_cache, "Enable the cache for coefficient table access.");
+MODULE_PARM_SYNTAX(snd_use_cache, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+MODULE_PARM(snd_vaio_hack, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_vaio_hack, "Enable workaround for Sony VAIO notebooks.");
+MODULE_PARM_SYNTAX(snd_vaio_hack, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+
+/*
+ * hw definitions
+ */
+
+/* The BIOS signature. */
+#define NM_SIGNATURE 0x4e4d0000
+/* Signature mask. */
+#define NM_SIG_MASK 0xffff0000
+
+/* Size of the second memory area. */
+#define NM_PORT2_SIZE 4096
+
+/* The base offset of the mixer in the second memory area. */
+#define NM_MIXER_OFFSET 0x600
+
+/* The maximum size of a coefficient entry. */
+#define NM_MAX_PLAYBACK_COEF_SIZE	0x5000
+#define NM_MAX_RECORD_COEF_SIZE		0x1260
+
+/* The interrupt register. */
+#define NM_INT_REG 0xa04
+/* And its bits. */
+#define NM_PLAYBACK_INT 0x40
+#define NM_RECORD_INT 0x100
+#define NM_MISC_INT_1 0x4000
+#define NM_MISC_INT_2 0x1
+#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1)
+
+/* The AV's "mixer ready" status bit and location. */
+#define NM_MIXER_STATUS_OFFSET 0xa04
+#define NM_MIXER_READY_MASK 0x0800
+#define NM_MIXER_PRESENCE 0xa06
+#define NM_PRESENCE_MASK 0x0050
+#define NM_PRESENCE_VALUE 0x0040
+
+/*
+ * For the ZX.  It uses the same interrupt register, but it holds 32
+ * bits instead of 16.
+ */
+#define NM2_PLAYBACK_INT 0x10000
+#define NM2_RECORD_INT 0x80000
+#define NM2_MISC_INT_1 0x8
+#define NM2_MISC_INT_2 0x2
+#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X))
+
+/* The ZX's "mixer ready" status bit and location. */
+#define NM2_MIXER_STATUS_OFFSET 0xa06
+#define NM2_MIXER_READY_MASK 0x0800
+
+/* The playback registers start from here. */
+#define NM_PLAYBACK_REG_OFFSET 0x0
+/* The record registers start from here. */
+#define NM_RECORD_REG_OFFSET 0x200
+
+/* The rate register is located 2 bytes from the start of the register area. */
+#define NM_RATE_REG_OFFSET 2
+
+/* Mono/stereo flag, number of bits on playback, and rate mask. */
+#define NM_RATE_STEREO 1
+#define NM_RATE_BITS_16 2
+#define NM_RATE_MASK 0xf0
+
+/* Playback enable register. */
+#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
+#define NM_PLAYBACK_ENABLE_FLAG 1
+#define NM_PLAYBACK_ONESHOT 2
+#define NM_PLAYBACK_FREERUN 4
+
+/* Mutes the audio output. */
+#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
+#define NM_AUDIO_MUTE_LEFT 0x8000
+#define NM_AUDIO_MUTE_RIGHT 0x0080
+
+/* Recording enable register. */
+#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
+#define NM_RECORD_ENABLE_FLAG 1
+#define NM_RECORD_FREERUN 2
+
+/* coefficient buffer pointer */
+#define NM_COEFF_START_OFFSET	0x1c
+#define NM_COEFF_END_OFFSET	0x20
+
+/* DMA buffer offsets */
+#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
+#define NM_RBUFFER_END   (NM_RECORD_REG_OFFSET + 0x10)
+#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
+#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)
+
+#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
+#define NM_PBUFFER_END   (NM_PLAYBACK_REG_OFFSET + 0x14)
+#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
+#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)
+
+/*
+ * type definitions
+ */
+
+typedef struct snd_nm256 nm256_t;
+typedef struct snd_nm256_stream nm256_stream_t;
+#define chip_t nm256_t
+
+struct snd_nm256_stream {
+
+	nm256_t *chip;
+	snd_pcm_substream_t *substream;
+	int running;
+	
+	u32 buf;	/* offset from chip->buffer */
+	int bufsize;	/* buffer size in bytes */
+	unsigned long bufptr;		/* mapped pointer */
+	unsigned long bufptr_addr;	/* physical address of the mapped pointer */
+
+	int dma_size;		/* buffer size of the substream in bytes */
+	int period_size;	/* period size in bytes */
+	int periods;		/* # of periods */
+	int shift;		/* bit shifts */
+	int cur_period;		/* current period # */
+
+};
+
+struct snd_nm256 {
+	
+	snd_card_t *card;
+
+	unsigned long cport;		/* control port */
+	struct resource *res_cport;	/* its resource */
+	unsigned long cport_addr;	/* physical address */
+
+	unsigned long buffer;		/* buffer */
+	struct resource *res_buffer;	/* its resource */
+	unsigned long buffer_addr;	/* buffer phyiscal address */
+
+	u32 buffer_start;		/* start offset from pci resource 0 */
+	u32 buffer_end;			/* end offset */
+	u32 buffer_size;		/* total buffer size */
+
+	u32 all_coeff_buf;		/* coefficient buffer */
+	u32 coeff_buf[2];		/* coefficient buffer for each stream */
+
+	int coeffs_current;		/* coeff. table is loaded? */
+	int use_cache;			/* use one big coef. table */
+
+	int mixer_base;			/* register offset of ac97 mixer */
+	int mixer_status_offset;	/* offset of mixer status reg. */
+	int mixer_status_mask;		/* bit mask to test the mixer status */
+
+	int irq;
+	void (*interrupt)(int, void *, struct pt_regs *);
+	int badintrcount;		/* counter to check bogus interrupts */
+
+	nm256_stream_t streams[2];
+
+	ac97_t *ac97;
+
+	snd_pcm_t *pcm;
+
+	struct pci_dev *pci;
+
+	spinlock_t reg_lock;
+
+};
+
+
+/*
+ * include coefficient table
+ */
+#include "nm256_coef.c"
+
+
+/*
+ * PCI ids
+ */
+
+#ifndef PCI_VENDOR_ID_NEOMAGIC
+#define PCI_VENDOR_ID_NEOMEGIC 0x10c8
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+#endif
+
+
+static struct pci_device_id snd_nm256_ids[] __devinitdata = {
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, snd_nm256_ids);
+
+
+/*
+ * lowlvel stuffs
+ */
+
+inline static u8
+snd_nm256_readb(nm256_t *chip, int offset)
+{
+	return readb(chip->cport + offset);
+}
+
+inline static u16
+snd_nm256_readw(nm256_t *chip, int offset)
+{
+	return readw(chip->cport + offset);
+}
+
+inline static u32
+snd_nm256_readl(nm256_t *chip, int offset)
+{
+	return readl(chip->cport + offset);
+}
+
+inline static void
+snd_nm256_writeb(nm256_t *chip, int offset, u8 val)
+{
+	writeb(val, chip->cport + offset);
+}
+
+inline static void
+snd_nm256_writew(nm256_t *chip, int offset, u16 val)
+{
+	writew(val, chip->cport + offset);
+}
+
+inline static void
+snd_nm256_writel(nm256_t *chip, int offset, u32 val)
+{
+	writel(val, chip->cport + offset);
+}
+
+inline static void
+snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size)
+{
+	offset -= chip->buffer_start;
+#ifdef SNDRV_CONFIG_DEBUG
+	if (offset < 0 || offset >= chip->buffer_size) {
+		snd_printk("write_buffer invalid offset = %d size = %d\n", offset, size);
+		return;
+	}
+#endif
+	memcpy_toio(chip->buffer + offset, src, size);
+}
+
+/*
+ * coefficient handlers -- what a magic!
+ */
+
+static u16
+snd_nm256_get_start_offset(int which)
+{
+	u16 offset = 0;
+	while (which-- > 0)
+		offset += coefficient_sizes[which];
+	return offset;
+}
+
+static void
+snd_nm256_load_one_coefficient(nm256_t *chip, int stream, u32 port, int which)
+{
+	u32 coeff_buf = chip->coeff_buf[stream];
+	u16 offset = snd_nm256_get_start_offset(which);
+	u16 size = coefficient_sizes[which];
+
+	snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size);
+	snd_nm256_writel(chip, port, coeff_buf);
+	/* ???  Record seems to behave differently than playback.  */
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		size--;
+	snd_nm256_writel(chip, port + 4, coeff_buf + size);
+}
+
+static void
+snd_nm256_load_coefficient(nm256_t *chip, int stream, int number)
+{
+	/* The enable register for the specified engine.  */
+	u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG);
+	u32 addr = NM_COEFF_START_OFFSET;
+
+	addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET);
+
+	if (snd_nm256_readb(chip, poffset) & 1) {
+		snd_printd("NM256: Engine was enabled while loading coefficients!\n");
+		return;
+	}
+
+	/* The recording engine uses coefficient values 8-15.  */
+	number &= 7;
+	if (stream == SNDRV_PCM_STREAM_CAPTURE)
+		number += 8;
+
+	if (! chip->use_cache) {
+		snd_nm256_load_one_coefficient(chip, stream, addr, number);
+		return;
+	}
+	if (! chip->coeffs_current) {
+		snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf,
+				       NM_TOTAL_COEFF_COUNT * 4);
+		chip->coeffs_current = 1;
+	} else {
+		u32 base = chip->all_coeff_buf;
+		u32 offset = snd_nm256_get_start_offset(number);
+		u32 end_offset = offset + coefficient_sizes[number];
+		snd_nm256_writel(chip, addr, base + offset);
+		if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+			end_offset--;
+		snd_nm256_writel(chip, addr + 4, base + end_offset);
+	}
+}
+
+
+/* The actual rates supported by the card. */
+static int samplerates[8] = {
+	8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
+};
+#define NUM_SAMPLERATES (sizeof(samplerates) / sizeof(samplerates[0]))
+static snd_pcm_hw_constraint_list_t constraints_rates = {
+	count: NUM_SAMPLERATES, 
+	list: samplerates,
+	mask: 0,
+};
+
+/*
+ * return the index of the target rate
+ */
+static int
+snd_nm256_fixed_rate(int rate)
+{
+	int i;
+	for (i = 0; i < NUM_SAMPLERATES; i++) {
+		if (rate == samplerates[i])
+			return i;
+	}
+	snd_BUG();
+	return 0;
+}
+
+/*
+ * set sample rate and format
+ */
+static void
+snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int rate_index = snd_nm256_fixed_rate(runtime->rate);
+	unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK;
+
+	s->shift = 0;
+	if (snd_pcm_format_width(runtime->format) == 16) {
+		ratebits |= NM_RATE_BITS_16;
+		s->shift++;
+	}
+	if (runtime->channels > 1) {
+		ratebits |= NM_RATE_STEREO;
+		s->shift++;
+	}
+
+	runtime->rate = samplerates[rate_index];
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */
+		snd_nm256_writeb(chip,
+				 NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
+				 ratebits);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */
+		snd_nm256_writeb(chip,
+				 NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
+				 ratebits);
+		break;
+	}
+}
+
+/*
+ * start / stop
+ */
+
+/* update the watermark (current period) */
+static void snd_nm256_pcm_mark(nm256_t *chip, nm256_stream_t *s, int reg)
+{
+	s->cur_period++;
+	s->cur_period %= s->periods;
+	snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size);
+}
+
+#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK)
+#define snd_nm256_capture_mark(chip, s)  snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK)
+
+static void
+snd_nm256_playback_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream)
+{
+	/* program buffer pointers */
+	snd_nm256_writel(chip, NM_PBUFFER_START, s->buf);
+	snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift));
+	snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf);
+	snd_nm256_playback_mark(chip, s);
+
+	/* Enable playback engine and interrupts. */
+	snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG,
+			 NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
+	/* Enable both channels. */
+	snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0);
+}
+
+static void
+snd_nm256_capture_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream)
+{
+	/* program buffer pointers */
+	snd_nm256_writel(chip, NM_RBUFFER_START, s->buf);
+	snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size);
+	snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf);
+	snd_nm256_capture_mark(chip, s);
+
+	/* Enable playback engine and interrupts. */
+	snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG,
+			 NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN);
+}
+
+/* Stop the play engine. */
+static void
+snd_nm256_playback_stop(nm256_t *chip)
+{
+	/* Shut off sound from both channels. */
+	snd_nm256_writew(chip, NM_AUDIO_MUTE_REG,
+			 NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
+	/* Disable play engine. */
+	snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0);
+}
+
+static void
+snd_nm256_capture_stop(nm256_t *chip)
+{
+	/* Disable recording engine. */
+	snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0);
+}
+
+static int
+snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+	nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+	unsigned long flags;
+	int err = 0;
+
+	snd_assert(s != NULL, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (! s->running) {
+			snd_nm256_playback_start(chip, s, substream);
+			s->running = 1;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (s->running) {
+			snd_nm256_playback_stop(chip);
+			s->running = 0;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return err;
+}
+
+static int
+snd_nm256_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+	nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+	unsigned long flags;
+	int err = 0;
+
+	snd_assert(s != NULL, return -ENXIO);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (! s->running) {
+			snd_nm256_capture_start(chip, s, substream);
+			s->running = 1;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (s->running) {
+			snd_nm256_capture_stop(chip);
+			s->running = 0;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return err;
+}
+
+
+/*
+ * prepare playback/capture channel
+ */
+static int snd_nm256_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+	unsigned long flags;
+
+	snd_assert(s, return -ENXIO);
+	s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);
+	s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);
+	s->periods = substream->runtime->periods;
+	s->cur_period = 0;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	s->running = 0;
+	snd_nm256_set_format(chip, s, substream);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return 0;
+}
+
+
+/*
+ * get the current pointer
+ */
+static snd_pcm_uframes_t
+snd_nm256_playback_pointer(snd_pcm_substream_t * substream)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+	nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+	unsigned long curp;
+
+	snd_assert(s, return 0);
+	curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;
+	curp %= s->dma_size;
+	return bytes_to_frames(substream->runtime, curp);
+}
+
+static snd_pcm_uframes_t
+snd_nm256_capture_pointer(snd_pcm_substream_t * substream)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+	nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data;
+	unsigned long curp;
+
+	snd_assert(s != NULL, return 0);
+	curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;
+	curp %= s->dma_size;	
+	return bytes_to_frames(substream->runtime, curp);
+}
+
+#ifndef __i386__
+/* FIXME: I/O space is not accessible via pointers on all architectures */
+
+/*
+ * silence / copy for playback
+ */
+static int
+snd_nm256_playback_silence(snd_pcm_substream_t *substream,
+			   int channel, /* not used (interleaved data) */
+			   snd_pcm_uframes_t pos,
+			   snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+	count = frames_to_bytes(runtime, count);
+	pos = frames_to_bytes(runtime, pos);
+	memset_io(s->bufptr + pos, 0, count);
+	return 0;
+}
+
+static int
+snd_nm256_playback_copy(snd_pcm_substream_t *substream,
+			int channel, /* not used (interleaved data) */
+			snd_pcm_uframes_t pos,
+			void *src,
+			snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+	count = frames_to_bytes(runtime, count);
+	pos = frames_to_bytes(runtime, pos);
+	if (copy_from_user_toio(s->bufptr + pos, src, count))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * copy to user
+ */
+static int
+snd_nm256_capture_copy(snd_pcm_substream_t *substream,
+		       int channel, /* not used (interleaved data) */
+		       snd_pcm_uframes_t pos,
+		       void *dst,
+		       snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	nm256_stream_t *s = (nm256_stream_t*)runtime->private_data;
+	count = frames_to_bytes(runtime, count);
+	pos = frames_to_bytes(runtime, pos);
+	if (copy_to_user_fromio(dst, s->bufptr + pos, count))
+		return -EFAULT;
+	return 0;
+}
+
+#endif /* !__i386__ */
+
+
+/*
+ * update playback/capture watermarks
+ */
+
+/* spinlock held! */
+static void
+snd_nm256_playback_update(nm256_t *chip)
+{
+	nm256_stream_t *s;
+
+	s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (s->running && s->substream) {
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(s->substream);
+		spin_lock(&chip->reg_lock);
+		snd_nm256_playback_mark(chip, s);
+	}
+}
+
+/* spinlock held! */
+static void
+snd_nm256_capture_update(nm256_t *chip)
+{
+	nm256_stream_t *s;
+
+	s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE];
+	if (s->running && s->substream) {
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(s->substream);
+		spin_lock(&chip->reg_lock);
+		snd_nm256_capture_mark(chip, s);
+	}
+}
+
+/*
+ * hardware info
+ */
+static snd_pcm_hardware_t snd_nm256_playback =
+{
+	info:
+#ifdef __i386__
+				SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|
+#endif
+				SNDRV_PCM_INFO_INTERLEAVED |
+				/*SNDRV_PCM_INFO_PAUSE |*/
+				SNDRV_PCM_INFO_RESUME,
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	periods_min:		2,
+	periods_max:		1024,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	256,
+	period_bytes_max:	128 * 1024,
+};
+
+static snd_pcm_hardware_t snd_nm256_capture =
+{
+	info:
+#ifdef __i386__
+				SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|
+#endif
+				SNDRV_PCM_INFO_INTERLEAVED |
+				/*SNDRV_PCM_INFO_PAUSE |*/
+				SNDRV_PCM_INFO_RESUME,
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	periods_min:		2,
+	periods_max:		1024,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	256,
+	period_bytes_max:	128 * 1024,
+};
+
+
+/* set dma transfer size */
+static int snd_nm256_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params)
+{
+	/* area and addr are already set and unchanged */
+	substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+	return 0;
+}
+
+/*
+ * open
+ */
+static void snd_nm256_setup_stream(nm256_t *chip, nm256_stream_t *s,
+				   snd_pcm_substream_t *substream,
+				   snd_pcm_hardware_t *hw_ptr)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	s->running = 0;
+	runtime->hw = *hw_ptr;
+	runtime->hw.buffer_bytes_max = s->bufsize;
+	runtime->hw.period_bytes_max = s->bufsize / 2;
+	runtime->dma_area = (void*) s->bufptr;
+	runtime->dma_addr = s->bufptr_addr;
+	runtime->dma_bytes = s->bufsize;
+	runtime->private_data = s;
+	s->substream = substream;
+
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				   &constraints_rates);
+}
+
+static int
+snd_nm256_playback_open(snd_pcm_substream_t *substream)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+
+	snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK],
+			       substream, &snd_nm256_playback);
+	return 0;
+}
+
+static int
+snd_nm256_capture_open(snd_pcm_substream_t *substream)
+{
+	nm256_t *chip = snd_pcm_substream_chip(substream);
+
+	snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE],
+			       substream, &snd_nm256_capture);
+	return 0;
+}
+
+/*
+ * close - we don't have to do special..
+ */
+static int
+snd_nm256_playback_close(snd_pcm_substream_t *substream)
+{
+	return 0;
+}
+
+
+static int
+snd_nm256_capture_close(snd_pcm_substream_t *substream)
+{
+	return 0;
+}
+
+/*
+ * create a pcm instance
+ */
+static snd_pcm_ops_t snd_nm256_playback_ops = {
+	open:		snd_nm256_playback_open,
+	close:		snd_nm256_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_nm256_pcm_hw_params,
+	prepare:	snd_nm256_pcm_prepare,
+	trigger:	snd_nm256_playback_trigger,
+	pointer:	snd_nm256_playback_pointer,
+#ifndef __i386__
+	copy:		snd_nm256_playback_copy,
+	silence:	snd_nm256_playback_silence,
+#endif
+};
+
+static snd_pcm_ops_t snd_nm256_capture_ops = {
+	open:		snd_nm256_capture_open,
+	close:		snd_nm256_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_nm256_pcm_hw_params,
+	prepare:	snd_nm256_pcm_prepare,
+	trigger:	snd_nm256_capture_trigger,
+	pointer:	snd_nm256_capture_pointer,
+#ifndef __i386__
+	copy:		snd_nm256_capture_copy,
+#endif
+};
+
+static int __init
+snd_nm256_pcm(nm256_t *chip, int device)
+{
+	snd_pcm_t *pcm;
+	int i, err;
+
+	for (i = 0; i < 2; i++) {
+		nm256_stream_t *s = &chip->streams[i];
+		s->bufptr = chip->buffer +  s->buf - chip->buffer_start;
+		s->bufptr_addr = chip->buffer_addr + s->buf - chip->buffer_start;
+	}
+
+	err = snd_pcm_new(chip->card, chip->card->driver, device,
+			  1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	chip->pcm = pcm;
+
+	return 0;
+}
+
+
+/* 
+ * Initialize the hardware. 
+ */
+static void
+snd_nm256_init_chip(nm256_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* Reset everything. */
+	snd_nm256_writeb(chip, 0x0, 0x11);
+	snd_nm256_writew(chip, 0x214, 0);
+	/* stop sounds.. */
+	//snd_nm256_playback_stop(chip);
+	//snd_nm256_capture_stop(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+
+inline static void
+snd_nm256_intr_check(nm256_t *chip)
+{
+	if (chip->badintrcount++ > 1000) {
+		/*
+		 * I'm not sure if the best thing is to stop the card from
+		 * playing or just release the interrupt (after all, we're in
+		 * a bad situation, so doing fancy stuff may not be such a good
+		 * idea).
+		 *
+		 * I worry about the card engine continuing to play noise
+		 * over and over, however--that could become a very
+		 * obnoxious problem.  And we know that when this usually
+		 * happens things are fairly safe, it just means the user's
+		 * inserted a PCMCIA card and someone's spamming us with IRQ 9s.
+		 */
+		if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
+			snd_nm256_playback_stop(chip);
+		if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
+			snd_nm256_capture_stop(chip);
+		chip->badintrcount = 0;
+	}
+}
+
+/* 
+ * Handle a potential interrupt for the device referred to by DEV_ID. 
+ *
+ * I don't like the cut-n-paste job here either between the two routines,
+ * but there are sufficient differences between the two interrupt handlers
+ * that parameterizing it isn't all that great either.  (Could use a macro,
+ * I suppose...yucky bleah.)
+ */
+
+static void
+snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return);
+	u16 status;
+	u8 cbyte;
+
+	status = snd_nm256_readw(chip, NM_INT_REG);
+
+	/* Not ours. */
+	if (status == 0) {
+		snd_nm256_intr_check(chip);
+		return;
+	}
+
+	chip->badintrcount = 0;
+
+	/* Rather boring; check for individual interrupts and process them. */
+
+	spin_lock(&chip->reg_lock);
+	if (status & NM_PLAYBACK_INT) {
+		status &= ~NM_PLAYBACK_INT;
+		NM_ACK_INT(chip, NM_PLAYBACK_INT);
+		snd_nm256_playback_update(chip);
+	}
+
+	if (status & NM_RECORD_INT) {
+		status &= ~NM_RECORD_INT;
+		NM_ACK_INT(chip, NM_RECORD_INT);
+		snd_nm256_capture_update(chip);
+	}
+
+	if (status & NM_MISC_INT_1) {
+		status &= ~NM_MISC_INT_1;
+		NM_ACK_INT(chip, NM_MISC_INT_1);
+		snd_printd("NM256: Got misc interrupt #1\n");
+		snd_nm256_writew(chip, NM_INT_REG, 0x8000);
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte | 2);
+	}
+
+	if (status & NM_MISC_INT_2) {
+		status &= ~NM_MISC_INT_2;
+		NM_ACK_INT(chip, NM_MISC_INT_2);
+		snd_printd("NM256: Got misc interrupt #2\n");
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte & ~2);
+	}
+
+	/* Unknown interrupt. */
+	if (status) {
+		snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+			   status);
+		/* Pray. */
+		NM_ACK_INT(chip, status);
+	}
+
+	spin_unlock(&chip->reg_lock);
+}
+
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ * This handler is for the 256ZX, and is very similar to the non-ZX
+ * routine.
+ */
+
+static void
+snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return);
+	u32 status;
+	u8 cbyte;
+
+	status = snd_nm256_readl(chip, NM_INT_REG);
+
+	/* Not ours. */
+	if (status == 0) {
+		snd_nm256_intr_check(chip);
+		return;
+	}
+
+	chip->badintrcount = 0;
+
+	/* Rather boring; check for individual interrupts and process them. */
+
+	spin_lock(&chip->reg_lock);
+	if (status & NM2_PLAYBACK_INT) {
+		status &= ~NM2_PLAYBACK_INT;
+		NM2_ACK_INT(chip, NM2_PLAYBACK_INT);
+		snd_nm256_playback_update(chip);
+	}
+
+	if (status & NM2_RECORD_INT) {
+		status &= ~NM2_RECORD_INT;
+		NM2_ACK_INT(chip, NM2_RECORD_INT);
+		snd_nm256_capture_update(chip);
+	}
+
+	if (status & NM2_MISC_INT_1) {
+		status &= ~NM2_MISC_INT_1;
+		NM2_ACK_INT(chip, NM2_MISC_INT_1);
+		snd_printd("NM256: Got misc interrupt #1\n");
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte | 2);
+	}
+
+	if (status & NM2_MISC_INT_2) {
+		status &= ~NM2_MISC_INT_2;
+		NM2_ACK_INT(chip, NM2_MISC_INT_2);
+		snd_printd("NM256: Got misc interrupt #2\n");
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte & ~2);
+	}
+
+	/* Unknown interrupt. */
+	if (status) {
+		snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+			   status);
+		/* Pray. */
+		NM2_ACK_INT(chip, status);
+	}
+
+	spin_unlock(&chip->reg_lock);
+}
+
+/*
+ * AC97 interface
+ */
+
+/*
+ * Waits for the mixer to become ready to be written; returns a zero value
+ * if it timed out.
+ */
+static int
+snd_nm256_ac97_ready(nm256_t *chip)
+{
+	int timeout = 10;
+	u32 testaddr;
+	u16 testb;
+
+	testaddr = chip->mixer_status_offset;
+	testb = chip->mixer_status_mask;
+
+	/* 
+	 * Loop around waiting for the mixer to become ready. 
+	 */
+	while (timeout-- > 0) {
+		if ((snd_nm256_readw(chip, testaddr) & testb) == 0)
+			return 1;
+		udelay(100);
+	}
+	return 0;
+}
+
+/*
+ */
+static unsigned short
+snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return -ENXIO);
+	int res;
+
+	if (reg >= 128)
+		return 0;
+
+	if (! snd_nm256_ac97_ready(chip))
+		return 0;
+	res = snd_nm256_readw(chip, chip->mixer_base + reg);
+	/* Magic delay.  Bleah yucky.  */
+	udelay(1000);
+	return res;
+}
+
+/* 
+ */
+static void
+snd_nm256_ac97_write(ac97_t *ac97,
+		     unsigned short reg, unsigned short val)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return);
+	int tries = 2;
+	u32 base;
+
+	base = chip->mixer_base;
+
+	snd_nm256_ac97_ready(chip);
+
+	/* Wait for the write to take, too. */
+	while (tries-- > 0) {
+		snd_nm256_writew(chip, base + reg, val);
+		udelay(1000);  /* a little delay here seems better.. */
+		if (snd_nm256_ac97_ready(chip))
+			return;
+	}
+	snd_printd("nm256: ac97 codec not ready..\n");
+}
+
+/* initialize the ac97 into a known state */
+static void
+snd_nm256_ac97_reset(ac97_t *ac97)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* Reset the mixer.  'Tis magic!  */
+	snd_nm256_writeb(chip, 0x6c0, 1);
+#if 0 /* Dell latitude LS will lock up by this */
+	snd_nm256_writeb(chip, 0x6cc, 0x87);
+#endif
+	snd_nm256_writeb(chip, 0x6cc, 0x80);
+	snd_nm256_writeb(chip, 0x6cc, 0x0);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/* create an ac97 mixer interface */
+static int __init
+snd_nm256_mixer(nm256_t *chip)
+{
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.init = snd_nm256_ac97_reset;
+	ac97.write = snd_nm256_ac97_write;
+	ac97.read = snd_nm256_ac97_read;
+	ac97.private_data = chip;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+
+	return 0;
+}
+
+/* 
+ * See if the signature left by the NM256 BIOS is intact; if so, we use
+ * the associated address as the end of our audio buffer in the video
+ * RAM.
+ */
+
+static int __init
+snd_nm256_peek_for_sig(nm256_t *chip)
+{
+	/* The signature is located 1K below the end of video RAM.  */
+	unsigned long temp;
+	/* Default buffer end is 5120 bytes below the top of RAM.  */
+	unsigned long pointer_found = chip->buffer_end - 0x1400;
+	u32 sig;
+
+	temp = (unsigned long) ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16);
+	if (temp == 0) {
+		snd_printk("Unable to scan for card signature in video RAM\n");
+		return -EBUSY;
+	}
+
+	sig = readl(temp);
+	if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
+		u32 pointer = readl(temp + 4);
+
+		/*
+		 * If it's obviously invalid, don't use it
+		 */
+		if (pointer == 0xffffffff ||
+		    pointer < chip->buffer_size ||
+		    pointer > chip->buffer_end) {
+			snd_printk("invalid signature found: 0x%x\n", pointer);
+			iounmap((void *)temp);
+			return -ENODEV;
+		} else {
+			pointer_found = pointer;
+			printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer);
+		}
+	}
+
+	iounmap((void *)temp);
+	chip->buffer_end = pointer_found;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * APM event handler, so the card is properly reinitialized after a power
+ * event.
+ */
+static void nm256_suspend(nm256_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	snd_pcm_suspend_all(chip->pcm);
+	chip->coeffs_current = 0;
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void nm256_resume(nm256_t *chip)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	/* Perform a full reset on the hardware */
+	pci_enable_device(chip->pci);
+	snd_nm256_init_chip(chip);
+
+	/* restore ac97 */
+	snd_ac97_resume(chip->ac97);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+#ifndef PCI_OLD_SUSPEND
+static int snd_nm256_suspend(struct pci_dev *dev, u32 state)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO);
+	nm256_suspend(chip);
+	return 0;
+}
+static int snd_nm256_resume(struct pci_dev *dev)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO);
+	nm256_resume(chip);
+	return 0;
+}
+#else
+static void snd_nm256_suspend(struct pci_dev *dev)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return);
+	nm256_suspend(chip);
+}
+static void snd_nm256_resume(struct pci_dev *dev)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return);
+	nm256_resume(chip);
+}
+#endif
+
+/* callback */
+static int snd_nm256_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, card->power_state_private_data, return -ENXIO);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		nm256_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		nm256_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static int snd_nm256_free(nm256_t *chip)
+{
+	if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
+		snd_nm256_playback_stop(chip);
+	if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
+		snd_nm256_capture_stop(chip);
+
+	synchronize_irq();
+
+	if (chip->cport)
+		iounmap((void *) chip->cport);
+	if (chip->buffer)
+		iounmap((void *) chip->buffer);
+	if (chip->res_cport) {
+		release_resource(chip->res_cport);
+		kfree_nocheck(chip->res_cport);
+	}
+	if (chip->res_buffer) {
+		release_resource(chip->res_buffer);
+		kfree_nocheck(chip->res_buffer);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void*)chip);
+
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_nm256_dev_free(snd_device_t *device)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, device->device_data, return -ENXIO);
+	return snd_nm256_free(chip);
+}
+
+static int __init
+snd_nm256_create(snd_card_t *card, struct pci_dev *pci,
+		 int play_bufsize, int capt_bufsize,
+		 int force_load,
+		 u32 buffertop,
+		 int usecache,
+		 nm256_t **chip_ret)
+{
+	nm256_t *chip;
+	int err, pval;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_nm256_dev_free,
+	};
+	u32 addr;
+
+	*chip_ret = NULL;
+
+	chip = snd_magic_kcalloc(nm256_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+	chip->pci = pci;
+	chip->use_cache = usecache;
+	spin_lock_init(&chip->reg_lock);
+	chip->irq = -1;
+
+	chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize;
+	chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize;
+
+	/* 
+	 * The NM256 has two memory ports.  The first port is nothing
+	 * more than a chunk of video RAM, which is used as the I/O ring
+	 * buffer.  The second port has the actual juicy stuff (like the
+	 * mixer and the playback engine control registers).
+	 */
+
+	chip->buffer_addr = pci_resource_start(pci, 0);
+	chip->cport_addr = pci_resource_start(pci, 1);
+
+	/* Init the memory port info.  */
+	/* remap control port (#2) */
+	chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE,
+					     card->driver);
+	if (chip->res_cport == NULL) {
+		snd_printk("memory region 0x%lx (size 0x%x) busy\n",
+			   chip->cport_addr, NM_PORT2_SIZE);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->cport = (unsigned long) ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE);
+	if (chip->cport == 0) {
+		snd_printk("unable to map control port %lx\n", chip->cport_addr);
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	if (!strcmp(card->driver, "NM256AV")) {
+		/* Ok, try to see if this is a non-AC97 version of the hardware. */
+		pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE);
+		if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
+			if (! force_load) {
+				printk(KERN_ERR "nm256: no ac97 is found!\n");
+				printk(KERN_ERR "  force the driver to load by passing in the module parameter\n");
+				printk(KERN_ERR "    snd_force_ac97=1\n");
+				printk(KERN_ERR "  or try sb16 or cs423x drivers instead.\n");
+				err = -ENXIO;
+				goto __error;
+			}
+		}
+		chip->buffer_end = 2560 * 1024;
+		chip->interrupt = snd_nm256_interrupt;
+		chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET;
+		chip->mixer_status_mask = NM_MIXER_READY_MASK;
+	} else {
+		/* Not sure if there is any relevant detect for the ZX or not.  */
+		if (snd_nm256_readb(chip, 0xa0b) != 0)
+			chip->buffer_end = 6144 * 1024;
+		else
+			chip->buffer_end = 4096 * 1024;
+
+		chip->interrupt = snd_nm256_interrupt_zx;
+		chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET;
+		chip->mixer_status_mask = NM2_MIXER_READY_MASK;
+	}
+	
+	chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
+	if (chip->use_cache)
+		chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4;
+	else
+		chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE;
+
+	if (buffertop >= chip->buffer_size && buffertop < chip->buffer_end)
+		chip->buffer_end = buffertop;
+	else {
+		/* get buffer end pointer from signature */
+		if ((err = snd_nm256_peek_for_sig(chip)) < 0)
+			goto __error;
+	}
+
+	chip->buffer_start = chip->buffer_end - chip->buffer_size;
+	chip->buffer_addr += chip->buffer_start;
+
+	printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n",
+	       chip->buffer_start, chip->buffer_end);
+
+	chip->res_buffer = request_mem_region(chip->buffer_addr,
+					      chip->buffer_size,
+					      card->driver);
+	if (chip->res_buffer == NULL) {
+		snd_printk("nm256: buffer 0x%lx (size 0x%x) busy\n",
+			   chip->buffer_addr, chip->buffer_size);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->buffer = (unsigned long) ioremap_nocache(chip->buffer_addr, chip->buffer_size);
+	if (chip->buffer == 0) {
+		err = -ENOMEM;
+		snd_printk("unable to map ring buffer at %lx\n", chip->buffer_addr);
+		goto __error;
+	}
+
+	/* set offsets */
+	addr = chip->buffer_start;
+	chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr;
+	addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize;
+	chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr;
+	addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
+	if (chip->use_cache) {
+		chip->all_coeff_buf = addr;
+	} else {
+		chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr;
+		addr += NM_MAX_PLAYBACK_COEF_SIZE;
+		chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr;
+	}
+
+	/* acquire interrupt */
+	if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ,
+			card->driver, (void*)chip)) {
+		err = -EBUSY;
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		goto __error;
+	}
+	chip->irq = pci->irq;
+
+	/* Fixed setting. */
+	chip->mixer_base = NM_MIXER_OFFSET;
+
+	chip->coeffs_current = 0;
+
+	snd_nm256_init_chip(chip);
+
+	if ((err = snd_nm256_pcm(chip, 0)) < 0)
+		goto __error;
+	
+	if ((err = snd_nm256_mixer(chip) < 0))
+		goto __error;
+
+	// pci_set_master(pci); /* needed? */
+	
+#ifdef CONFIG_PM
+	card->set_power_state = snd_nm256_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+		goto __error;
+
+	*chip_ret = chip;
+	return 0;
+
+__error:
+	snd_nm256_free(chip);
+	return err;
+}
+
+
+static int __devinit snd_nm256_probe(struct pci_dev *pci,
+				     const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	nm256_t *chip;
+	int err;
+	unsigned int buffer_top;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	switch (pci->device) {
+	case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO:
+		strcpy(card->driver, "NM256AV");
+		break;
+	case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO:
+		strcpy(card->driver, "NM256ZX");
+		break;
+	default:
+		snd_printk("invalid device id 0x%x\n", pci->device);
+		snd_card_free(card);
+		return -EINVAL;
+	}
+
+	if (snd_vaio_hack[dev])
+		buffer_top = 0x25a800;	/* this avoids conflicts with XFree86 server */
+	else
+		buffer_top = snd_buffer_top[dev];
+
+	if (snd_playback_bufsize[dev] < 4)
+		snd_playback_bufsize[dev] = 4;
+	if (snd_playback_bufsize[dev] > 128)
+		snd_playback_bufsize[dev] = 128;
+	if (snd_capture_bufsize[dev] < 4)
+		snd_capture_bufsize[dev] = 4;
+	if (snd_capture_bufsize[dev] > 128)
+		snd_capture_bufsize[dev] = 128;
+	if ((err = snd_nm256_create(card, pci,
+				    snd_playback_bufsize[dev] * 1024, /* in bytes */
+				    snd_capture_bufsize[dev] * 1024,  /* in bytes */
+				    snd_force_ac97[dev],
+				    buffer_top,
+				    snd_use_cache[dev],
+				    &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->shortname, "NeoMagic %s", card->driver);
+	sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d",
+		card->shortname,
+		chip->buffer_addr, chip->cport_addr, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_nm256_remove(struct pci_dev *pci)
+{
+	nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+	name: "NeoMagic 256",
+	id_table: snd_nm256_ids,
+	probe: snd_nm256_probe,
+	remove: __devexit_p(snd_nm256_remove),
+#ifdef CONFIG_PM
+	suspend: snd_nm256_suspend,
+	resume: snd_nm256_resume,
+#endif
+};
+
+
+static int __init alsa_card_nm256_init(void)
+{
+	int err;
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "NeoMagic 256 audio soundchip not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_nm256_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_nm256_init)
+module_exit(alsa_card_nm256_exit)
+
+#ifndef MODULE
+
+/* format is: snd-nm256=snd_enable,snd_index,snd_id,
+			snd_playback_bufsize,snd_capture_bufsize,
+			snd_force_ac97,snd_buffer_top,snd_use_cache */
+
+static int __init alsa_card_nm256_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_playback_bufsize[nr_dev]) == 2 &&
+	       get_option(&str,&snd_capture_bufsize[nr_dev]) == 2 &&
+	       get_option(&str,&snd_force_ac97[nr_dev]) == 2 &&
+	       get_option(&str,&snd_buffer_top[nr_dev]) == 2 &&
+	       get_option(&str,&snd_use_cache[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-nm256=", alsa_card_nm256_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/nm256/nm256_coef.c linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256_coef.c
--- linux/sound/pci/nm256/nm256_coef.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256_coef.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,4607 @@
+#define NM_TOTAL_COEFF_COUNT 0x3158
+
+static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { 
+	0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21,
+	0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01,
+	0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06,
+	0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1,
+	0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF,
+	0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E,
+	0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC,
+	0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05,
+	0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+	0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+	0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9,
+	0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01,
+	0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84,
+	0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+	0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+	0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C,
+	0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00,
+	0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5,
+	0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33,
+	0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00,
+	0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1,
+	0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38,
+	0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA,
+	0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD,
+	0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38,
+	0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F,
+	0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05,
+	0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06,
+	0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32,
+	0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93,
+	0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC,
+	0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+	0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA,
+	0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9,
+	0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+	0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+	0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68,
+	0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32,
+	0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00,
+	0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76,
+	0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53,
+	0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04,
+	0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01,
+	0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20,
+	0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF,
+	0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43,
+	0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1,
+	0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD,
+	0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8,
+	0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5,
+	0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04,
+	0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04,
+	0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D,
+	0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE,
+	0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD,
+	0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19,
+	0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2,
+	0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD,
+	0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF,
+	0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D,
+	0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01,
+	0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70,
+	0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07,
+	0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9,
+	0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66,
+	0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF,
+	0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F,
+	0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F,
+	0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C,
+	0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00,
+	0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF,
+	0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18,
+	0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB,
+	0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC,
+	0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98,
+	0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00,
+	0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A,
+	0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06,
+	0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01,
+	0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05,
+	0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A,
+	0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+	0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76,
+	0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD,
+	0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8,
+	0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40,
+	0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+	0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+	0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85,
+	0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36,
+	0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42,
+	0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+	0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D,
+	0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF,
+	0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39,
+	0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48,
+	0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95,
+	0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01,
+	0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F,
+	0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F,
+	0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1,
+	0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD,
+	0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE,
+	0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A,
+	0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A,
+	0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03,
+	0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05,
+	0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E,
+	0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01,
+	0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC,
+	0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B,
+	0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2,
+	0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF,
+	0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48,
+	0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01,
+	0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+	0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+	0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+	0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11,
+	0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF,
+	0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B,
+	0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A,
+	0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32,
+	0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF,
+	0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C,
+	0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE,
+	0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01,
+	0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC,
+	0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1,
+	0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00,
+	0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16,
+	0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06,
+	0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04,
+	0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04,
+	0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B,
+	0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00,
+	0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3,
+	0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD,
+	0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7,
+	0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3,
+	0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+	0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+	0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9,
+	0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A,
+	0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00,
+	0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD,
+	0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF,
+	0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A,
+	0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48,
+	0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB,
+	0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01,
+	0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91,
+	0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8,
+	0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1,
+	0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF,
+	0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA,
+	0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD,
+	0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD,
+	0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE,
+	0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06,
+	0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF,
+	0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+	0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94,
+	0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC,
+	0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB,
+	0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4,
+	0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF,
+	0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A,
+	0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01,
+	0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D,
+	0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10,
+	0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68,
+	0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+	0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B,
+	0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF,
+	0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28,
+	0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25,
+	0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF,
+	0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC,
+	0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90,
+	0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE,
+	0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16,
+	0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC,
+	0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE,
+	0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00,
+	0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0,
+	0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07,
+	0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03,
+	0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91,
+	0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73,
+	0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE,
+	0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33,
+	0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5,
+	0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20,
+	0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+	0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+	0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+	0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3,
+	0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E,
+	0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00,
+	0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47,
+	0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96,
+	0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46,
+	0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1,
+	0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01,
+	0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74,
+	0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B,
+	0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2,
+	0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF,
+	0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D,
+	0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A,
+	0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE,
+	0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06,
+	0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23,
+	0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF,
+	0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81,
+	0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC,
+	0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9,
+	0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5,
+	0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2,
+	0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF,
+	0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED,
+	0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01,
+	0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+	0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+	0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF,
+	0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25,
+	0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF,
+	0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9,
+	0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20,
+	0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF,
+	0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2,
+	0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B,
+	0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB,
+	0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC,
+	0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA,
+	0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56,
+	0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07,
+	0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+	0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02,
+	0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB,
+	0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E,
+	0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01,
+	0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4,
+	0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85,
+	0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+	0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+	0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+	0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02,
+	0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41,
+	0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00,
+	0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07,
+	0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF,
+	0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA,
+	0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44,
+	0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01,
+	0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5,
+	0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00,
+	0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A,
+	0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3,
+	0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00,
+	0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A,
+	0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C,
+	0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00,
+	0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06,
+	0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D,
+	0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+	0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0,
+	0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC,
+	0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5,
+	0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8,
+	0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7,
+	0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00,
+	0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64,
+	0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE,
+	0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39,
+	0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A,
+	0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE,
+	0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF,
+	0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD,
+	0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF,
+	0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D,
+	0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B,
+	0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE,
+	0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54,
+	0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3,
+	0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8,
+	0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C,
+	0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC,
+	0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63,
+	0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8,
+	0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06,
+	0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+	0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00,
+	0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34,
+	0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C,
+	0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00,
+	0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3,
+	0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0,
+	0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+	0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+	0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+	0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36,
+	0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44,
+	0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF,
+	0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA,
+	0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00,
+	0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4,
+	0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42,
+	0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01,
+	0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E,
+	0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31,
+	0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4,
+	0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01,
+	0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86,
+	0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D,
+	0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01,
+	0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07,
+	0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11,
+	0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88,
+	0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC,
+	0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF,
+	0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA,
+	0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92,
+	0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA,
+	0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE,
+	0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+	0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+	0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF,
+	0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5,
+	0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF,
+	0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17,
+	0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16,
+	0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01,
+	0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C,
+	0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF,
+	0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C,
+	0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6,
+	0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC,
+	0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8,
+	0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF,
+	0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8,
+	0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06,
+	0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16,
+	0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF,
+	0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99,
+	0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7,
+	0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00,
+	0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2,
+	0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F,
+	0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00,
+	0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+	0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D,
+	0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46,
+	0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF,
+	0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57,
+	0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00,
+	0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3,
+	0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F,
+	0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC,
+	0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01,
+	0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A,
+	0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00,
+	0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF,
+	0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5,
+	0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02,
+	0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9,
+	0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8,
+	0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03,
+	0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07,
+	0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0,
+	0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+	0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0,
+	0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC,
+	0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9,
+	0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD,
+	0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66,
+	0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+	0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36,
+	0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF,
+	0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39,
+	0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24,
+	0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6,
+	0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF,
+	0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11,
+	0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9,
+	0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11,
+	0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01,
+	0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74,
+	0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF,
+	0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57,
+	0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4,
+	0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04,
+	0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC,
+	0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A,
+	0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF,
+	0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8,
+	0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06,
+	0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C,
+	0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD,
+	0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08,
+	0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A,
+	0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF,
+	0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32,
+	0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1,
+	0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70,
+	0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00,
+	0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+	0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5,
+	0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47,
+	0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E,
+	0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF,
+	0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26,
+	0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00,
+	0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6,
+	0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B,
+	0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01,
+	0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E,
+	0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0,
+	0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7,
+	0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD,
+	0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F,
+	0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66,
+	0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04,
+	0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD,
+	0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06,
+	0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A,
+	0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00,
+	0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48,
+	0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC,
+	0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93,
+	0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01,
+	0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34,
+	0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+	0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA,
+	0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF,
+	0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+	0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+	0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+	0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF,
+	0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10,
+	0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35,
+	0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C,
+	0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01,
+	0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30,
+	0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF,
+	0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38,
+	0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3,
+	0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC,
+	0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A,
+	0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B,
+	0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05,
+	0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03,
+	0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04,
+	0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF,
+	0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1,
+	0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE,
+	0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B,
+	0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1,
+	0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3,
+	0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF,
+	0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+	0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE,
+	0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48,
+	0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71,
+	0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF,
+	0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C,
+	0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00,
+	0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB,
+	0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37,
+	0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01,
+	0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2,
+	0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70,
+	0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8,
+	0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD,
+	0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9,
+	0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0,
+	0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05,
+	0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE,
+	0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06,
+	0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40,
+	0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00,
+	0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53,
+	0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC,
+	0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20,
+	0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC,
+	0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D,
+	0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6,
+	0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00,
+	0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53,
+	0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E,
+	0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66,
+	0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF,
+	0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06,
+	0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF,
+	0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08,
+	0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01,
+	0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57,
+	0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF,
+	0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32,
+	0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2,
+	0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD,
+	0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B,
+	0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17,
+	0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04,
+	0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03,
+	0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24,
+	0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8,
+	0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD,
+	0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23,
+	0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1,
+	0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8,
+	0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF,
+	0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+	0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+	0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83,
+	0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04,
+	0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C,
+	0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02,
+	0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00,
+	0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3,
+	0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33,
+	0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00,
+	0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB,
+	0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B,
+	0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA,
+	0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD,
+	0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58,
+	0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14,
+	0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06,
+	0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06,
+	0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31,
+	0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0,
+	0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC,
+	0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+	0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA,
+	0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4,
+	0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA,
+	0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00,
+	0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+	0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+	0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00,
+	0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06,
+	0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49,
+	0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04,
+	0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01,
+	0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE,
+	0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF,
+	0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46,
+	0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1,
+	0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD,
+	0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0,
+	0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92,
+	0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04,
+	0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04,
+	0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B,
+	0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA,
+	0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD,
+	0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18,
+	0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2,
+	0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE,
+	0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF,
+	0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+	0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+	0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E,
+	0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08,
+	0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4,
+	0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F,
+	0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF,
+	0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B,
+	0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E,
+	0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D,
+	0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00,
+	0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E,
+	0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C,
+	0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC,
+	0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC,
+	0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6,
+	0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00,
+	0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B,
+	0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06,
+	0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01,
+	0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05,
+	0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+	0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC,
+	0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD,
+	0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8,
+	0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A,
+	0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2,
+	0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01,
+	0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88,
+	0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37,
+	0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41,
+	0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00,
+	0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20,
+	0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF,
+	0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D,
+	0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48,
+	0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96,
+	0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01,
+	0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7,
+	0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75,
+	0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1,
+	0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD,
+	0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE,
+	0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F,
+	0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5,
+	0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03,
+	0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05,
+	0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D,
+	0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3,
+	0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC,
+	0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+	0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2,
+	0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+	0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+	0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B,
+	0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C,
+	0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B,
+	0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0,
+	0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF,
+	0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66,
+	0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29,
+	0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32,
+	0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF,
+	0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E,
+	0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4,
+	0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01,
+	0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC,
+	0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB,
+	0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00,
+	0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12,
+	0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06,
+	0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04,
+	0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04,
+	0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F,
+	0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+	0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12,
+	0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD,
+	0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7,
+	0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD,
+	0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0,
+	0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01,
+	0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+	0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+	0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00,
+	0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64,
+	0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF,
+	0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E,
+	0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47,
+	0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD,
+	0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73,
+	0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF,
+	0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1,
+	0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF,
+	0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC,
+	0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1,
+	0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD,
+	0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE,
+	0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06,
+	0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF,
+	0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+	0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E,
+	0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC,
+	0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9,
+	0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4,
+	0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+	0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+	0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+	0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C,
+	0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11,
+	0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63,
+	0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+	0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2,
+	0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF,
+	0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22,
+	0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25,
+	0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF,
+	0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20,
+	0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87,
+	0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE,
+	0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15,
+	0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC,
+	0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3,
+	0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00,
+	0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7,
+	0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07,
+	0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03,
+	0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97,
+	0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+	0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA,
+	0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02,
+	0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+	0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5,
+	0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A,
+	0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE,
+	0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01,
+	0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7,
+	0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E,
+	0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00,
+	0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4,
+	0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99,
+	0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46,
+	0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3,
+	0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01,
+	0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60,
+	0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25,
+	0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2,
+	0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF,
+	0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D,
+	0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD,
+	0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE,
+	0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06,
+	0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15,
+	0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+	0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84,
+	0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC,
+	0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7,
+	0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6,
+	0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0,
+	0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+	0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+	0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+	0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15,
+	0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C,
+	0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF,
+	0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3,
+	0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF,
+	0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1,
+	0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F,
+	0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF,
+	0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8,
+	0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23,
+	0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA,
+	0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC,
+	0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB,
+	0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49,
+	0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07,
+	0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+	0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01,
+	0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2,
+	0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07,
+	0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01,
+	0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4,
+	0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E,
+	0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B,
+	0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01,
+	0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+	0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+	0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00,
+	0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB,
+	0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF,
+	0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC,
+	0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44,
+	0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01,
+	0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB,
+	0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00,
+	0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6,
+	0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3,
+	0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00,
+	0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48,
+	0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E,
+	0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00,
+	0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06,
+	0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81,
+	0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+	0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC,
+	0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC,
+	0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3,
+	0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8,
+	0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4,
+	0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+	0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+	0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A,
+	0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9,
+	0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF,
+	0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44,
+	0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF,
+	0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74,
+	0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A,
+	0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE,
+	0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B,
+	0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC,
+	0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8,
+	0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC,
+	0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F,
+	0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF,
+	0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96,
+	0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06,
+	0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+	0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00,
+	0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C,
+	0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56,
+	0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00,
+	0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3,
+	0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8,
+	0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00,
+	0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6,
+	0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+	0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A,
+	0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44,
+	0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+	0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1,
+	0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00,
+	0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6,
+	0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41,
+	0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01,
+	0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D,
+	0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F,
+	0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4,
+	0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01,
+	0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73,
+	0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D,
+	0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01,
+	0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07,
+	0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08,
+	0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E,
+	0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC,
+	0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD,
+	0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA,
+	0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F,
+	0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+	0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+	0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F,
+	0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB,
+	0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF,
+	0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89,
+	0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D,
+	0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15,
+	0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01,
+	0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6,
+	0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF,
+	0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86,
+	0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6,
+	0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC,
+	0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0,
+	0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF,
+	0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2,
+	0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06,
+	0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+	0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE,
+	0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3,
+	0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3,
+	0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF,
+	0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2,
+	0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35,
+	0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00,
+	0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD,
+	0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01,
+	0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+	0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+	0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF,
+	0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41,
+	0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00,
+	0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E,
+	0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE,
+	0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01,
+	0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1,
+	0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00,
+	0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF,
+	0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5,
+	0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02,
+	0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95,
+	0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6,
+	0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03,
+	0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07,
+	0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9,
+	0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00,
+	0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF,
+	0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC,
+	0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7,
+	0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE,
+	0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62,
+	0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+	0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+	0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+	0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A,
+	0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25,
+	0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2,
+	0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF,
+	0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0,
+	0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF,
+	0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F,
+	0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11,
+	0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01,
+	0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26,
+	0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF,
+	0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53,
+	0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4,
+	0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04,
+	0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC,
+	0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D,
+	0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D,
+	0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06,
+	0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+	0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD,
+	0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12,
+	0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29,
+	0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF,
+	0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+	0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1,
+	0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75,
+	0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00,
+	0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0,
+	0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA,
+	0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47,
+	0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60,
+	0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF,
+	0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02,
+	0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00,
+	0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B,
+	0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01,
+	0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C,
+	0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2,
+	0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7,
+	0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD,
+	0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64,
+	0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70,
+	0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04,
+	0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD,
+	0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06,
+	0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65,
+	0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+	0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71,
+	0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC,
+	0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91,
+	0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01,
+	0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F,
+	0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+	0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+	0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+	0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44,
+	0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29,
+	0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80,
+	0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF,
+	0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E,
+	0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B,
+	0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C,
+	0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01,
+	0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB,
+	0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF,
+	0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37,
+	0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2,
+	0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC,
+	0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29,
+	0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D,
+	0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05,
+	0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03,
+	0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0,
+	0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3,
+	0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE,
+	0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B,
+	0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1,
+	0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7,
+	0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF,
+	0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF,
+	0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+	0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+	0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF,
+	0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E,
+	0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00,
+	0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA,
+	0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37,
+	0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01,
+	0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7,
+	0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83,
+	0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8,
+	0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD,
+	0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC,
+	0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8,
+	0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05,
+	0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE,
+	0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06,
+	0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E,
+	0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+	0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86,
+	0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC,
+	0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+	0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC,
+	0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68,
+	0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+	0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+	0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55,
+	0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E,
+	0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63,
+	0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF,
+	0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94,
+	0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4,
+	0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08,
+	0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01,
+	0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C,
+	0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF,
+	0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33,
+	0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2,
+	0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD,
+	0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06,
+	0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6,
+	0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04,
+	0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04,
+	0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10,
+	0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F,
+	0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD,
+	0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22,
+	0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1,
+	0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA,
+	0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF,
+	0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0,
+	0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01,
+	0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81,
+	0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04,
+	0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07,
+	0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+	0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7,
+	0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00,
+	0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0,
+	0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32,
+	0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00,
+	0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5,
+	0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F,
+	0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA,
+	0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC,
+	0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78,
+	0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17,
+	0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06,
+	0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06,
+	0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31,
+	0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D,
+	0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD,
+	0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+	0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA,
+	0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE,
+	0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+	0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+	0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D,
+	0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33,
+	0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E,
+	0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00,
+	0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96,
+	0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E,
+	0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04,
+	0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01,
+	0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD,
+	0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF,
+	0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49,
+	0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1,
+	0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD,
+	0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8,
+	0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E,
+	0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03,
+	0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04,
+	0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38,
+	0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97,
+	0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD,
+	0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16,
+	0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2,
+	0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF,
+	0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+	0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5,
+	0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01,
+	0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C,
+	0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+	0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+	0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7,
+	0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF,
+	0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97,
+	0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E,
+	0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D,
+	0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00,
+	0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C,
+	0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F,
+	0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC,
+	0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC,
+	0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3,
+	0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00,
+	0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A,
+	0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06,
+	0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02,
+	0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05,
+	0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00,
+	0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03,
+	0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD,
+	0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C,
+	0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8,
+	0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54,
+	0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+	0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+	0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B,
+	0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37,
+	0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40,
+	0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00,
+	0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2,
+	0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF,
+	0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42,
+	0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48,
+	0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98,
+	0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01,
+	0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0,
+	0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A,
+	0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1,
+	0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD,
+	0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE,
+	0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43,
+	0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF,
+	0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03,
+	0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05,
+	0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B,
+	0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7,
+	0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC,
+	0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08,
+	0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3,
+	0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF,
+	0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86,
+	0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01,
+	0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A,
+	0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C,
+	0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96,
+	0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E,
+	0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF,
+	0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60,
+	0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29,
+	0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF,
+	0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0,
+	0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA,
+	0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01,
+	0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC,
+	0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04,
+	0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+	0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D,
+	0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06,
+	0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05,
+	0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04,
+	0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63,
+	0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+	0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62,
+	0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD,
+	0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7,
+	0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7,
+	0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+	0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+	0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0,
+	0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B,
+	0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00,
+	0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC,
+	0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF,
+	0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72,
+	0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47,
+	0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF,
+	0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56,
+	0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7,
+	0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1,
+	0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF,
+	0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD,
+	0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4,
+	0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD,
+	0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE,
+	0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06,
+	0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF,
+	0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF,
+	0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A,
+	0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC,
+	0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8,
+	0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4,
+	0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF,
+	0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D,
+	0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01,
+	0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+	0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+	0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+	0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97,
+	0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF,
+	0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B,
+	0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24,
+	0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF,
+	0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45,
+	0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D,
+	0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD,
+	0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC,
+	0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08,
+	0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00,
+	0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE,
+	0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07,
+	0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03,
+	0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D,
+	0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94,
+	0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02,
+	0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+	0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5,
+	0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33,
+	0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+	0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+	0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB,
+	0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F,
+	0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00,
+	0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81,
+	0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF,
+	0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D,
+	0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46,
+	0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5,
+	0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01,
+	0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D,
+	0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30,
+	0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2,
+	0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00,
+	0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC,
+	0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0,
+	0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF,
+	0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06,
+	0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07,
+	0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+	0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87,
+	0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC,
+	0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5,
+	0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6,
+	0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE,
+	0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF,
+	0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4,
+	0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01,
+	0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F,
+	0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16,
+	0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28,
+	0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60,
+	0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF,
+	0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9,
+	0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F,
+	0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE,
+	0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE,
+	0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B,
+	0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA,
+	0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC,
+	0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB,
+	0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B,
+	0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07,
+	0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D,
+	0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01,
+	0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9,
+	0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1,
+	0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01,
+	0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4,
+	0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96,
+	0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+	0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+	0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B,
+	0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42,
+	0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00,
+	0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50,
+	0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF,
+	0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF,
+	0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44,
+	0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01,
+	0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1,
+	0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00,
+	0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3,
+	0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3,
+	0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00,
+	0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35,
+	0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F,
+	0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00,
+	0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06,
+	0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76,
+	0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9,
+	0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC,
+	0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1,
+	0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8,
+	0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1,
+	0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F,
+	0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE,
+	0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+	0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+	0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF,
+	0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC,
+	0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF,
+	0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C,
+	0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A,
+	0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35,
+	0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE,
+	0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1,
+	0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5,
+	0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8,
+	0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC,
+	0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B,
+	0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+	0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84,
+	0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06,
+	0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12,
+	0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00,
+	0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45,
+	0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31,
+	0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00,
+	0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3,
+	0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF,
+	0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+	0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F,
+	0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44,
+	0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF,
+	0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB,
+	0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00,
+	0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+	0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41,
+	0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01,
+	0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C,
+	0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E,
+	0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4,
+	0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01,
+	0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60,
+	0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D,
+	0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02,
+	0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07,
+	0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE,
+	0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4,
+	0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC,
+	0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB,
+	0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB,
+	0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B,
+	0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00,
+	0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73,
+	0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF,
+	0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20,
+	0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7,
+	0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF,
+	0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B,
+	0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04,
+	0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15,
+	0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01,
+	0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70,
+	0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF,
+	0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81,
+	0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5,
+	0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC,
+	0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7,
+	0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+	0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B,
+	0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06,
+	0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+	0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE,
+	0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC,
+	0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F,
+	0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF,
+	0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2,
+	0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B,
+	0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00,
+	0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+	0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+	0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76,
+	0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46,
+	0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51,
+	0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF,
+	0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C,
+	0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00,
+	0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E,
+	0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF,
+	0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01,
+	0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8,
+	0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00,
+	0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00,
+	0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5,
+	0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE,
+	0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0,
+	0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00,
+	0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3,
+	0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03,
+	0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07,
+	0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2,
+	0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00,
+	0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF,
+	0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC,
+	0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5,
+	0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE,
+	0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E,
+	0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+	0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A,
+	0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF,
+	0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+	0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+	0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+	0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF,
+	0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30,
+	0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF,
+	0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95,
+	0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10,
+	0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01,
+	0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9,
+	0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF,
+	0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50,
+	0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4,
+	0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03,
+	0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC,
+	0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20,
+	0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83,
+	0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06,
+	0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+	0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD,
+	0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C,
+	0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07,
+	0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF,
+	0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+	0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1,
+	0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A,
+	0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00,
+	0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+	0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF,
+	0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48,
+	0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62,
+	0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF,
+	0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1,
+	0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00,
+	0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A,
+	0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01,
+	0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B,
+	0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4,
+	0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7,
+	0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD,
+	0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89,
+	0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B,
+	0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04,
+	0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD,
+	0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06,
+	0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61,
+	0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+	0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B,
+	0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC,
+	0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F,
+	0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01,
+	0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B,
+	0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+	0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6,
+	0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF,
+	0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45,
+	0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A,
+	0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D,
+	0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF,
+	0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C,
+	0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20,
+	0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C,
+	0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01,
+	0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7,
+	0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF,
+	0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35,
+	0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2,
+	0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC,
+	0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28,
+	0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F,
+	0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05,
+	0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03,
+	0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC,
+	0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6,
+	0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE,
+	0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A,
+	0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1,
+	0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB,
+	0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF,
+	0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+	0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+	0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8,
+	0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48,
+	0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF,
+	0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD,
+	0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00,
+	0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8,
+	0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36,
+	0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00,
+	0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC,
+	0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96,
+	0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8,
+	0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD,
+	0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F,
+	0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF,
+	0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05,
+	0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06,
+	0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C,
+	0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+	0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB,
+	0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC,
+	0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+	0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB,
+	0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72,
+	0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7,
+	0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00,
+	0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+	0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+	0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+	0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF,
+	0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23,
+	0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA,
+	0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07,
+	0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01,
+	0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2,
+	0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF,
+	0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34,
+	0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2,
+	0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD,
+	0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01,
+	0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4,
+	0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04,
+	0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04,
+	0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD,
+	0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76,
+	0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD,
+	0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21,
+	0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1,
+	0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD,
+	0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF,
+	0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+	0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+	0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F,
+	0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04,
+	0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03,
+	0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF,
+	0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89,
+	0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00,
+	0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD,
+	0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32,
+	0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00,
+	0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10,
+	0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73,
+	0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA,
+	0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC,
+	0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98,
+	0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B,
+	0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06,
+	0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05,
+	0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31,
+	0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00,
+	0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C,
+	0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD,
+	0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+	0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA,
+	0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9,
+	0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD,
+	0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00,
+	0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F,
+	0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33,
+	0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D,
+	0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00,
+	0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25,
+	0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11,
+	0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48,
+	0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84,
+	0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A,
+	0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00,
+	0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1,
+	0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3,
+	0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE,
+	0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7,
+	0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00,
+	0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26,
+	0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06,
+	0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16,
+	0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF,
+	0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A,
+	0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84,
+	0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD,
+	0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8,
+	0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9,
+	0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2,
+	0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+	0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+	0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12,
+	0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42,
+	0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B,
+	0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF,
+	0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7,
+	0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00,
+	0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4,
+	0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20,
+	0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01,
+	0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C,
+	0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF,
+	0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04,
+	0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2,
+	0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD,
+	0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70,
+	0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+	0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+	0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+	0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B,
+	0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD,
+	0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7,
+	0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC,
+	0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC,
+	0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2,
+	0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC,
+	0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79,
+	0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+	0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+	0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47,
+	0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44,
+	0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45,
+	0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF,
+	0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32,
+	0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF,
+	0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77,
+	0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B,
+	0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01,
+	0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E,
+	0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF,
+	0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E,
+	0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2,
+	0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD,
+	0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07,
+	0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00,
+	0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96,
+	0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07,
+	0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02,
+	0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25,
+	0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF,
+	0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85,
+	0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC,
+	0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C,
+	0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF,
+	0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49,
+	0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+	0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+	0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F,
+	0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46,
+	0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53,
+	0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE,
+	0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF,
+	0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11,
+	0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16,
+	0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01,
+	0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20,
+	0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF,
+	0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10,
+	0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3,
+	0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC,
+	0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83,
+	0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00,
+	0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+	0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+	0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03,
+	0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45,
+	0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF,
+	0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C,
+	0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC,
+	0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86,
+	0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03,
+	0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14,
+	0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+	0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+	0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+	0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8,
+	0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48,
+	0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64,
+	0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF,
+	0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54,
+	0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF,
+	0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2,
+	0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11,
+	0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01,
+	0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55,
+	0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF,
+	0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9,
+	0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5,
+	0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC,
+	0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC,
+	0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00,
+	0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38,
+	0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06,
+	0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04,
+	0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A,
+	0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+	0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7,
+	0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC,
+	0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24,
+	0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB,
+	0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3,
+	0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+	0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+	0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+	0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1,
+	0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48,
+	0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78,
+	0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF,
+	0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46,
+	0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF,
+	0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E,
+	0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C,
+	0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01,
+	0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD,
+	0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00,
+	0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77,
+	0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6,
+	0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF,
+	0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC,
+	0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C,
+	0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+	0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+	0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+	0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF,
+	0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05,
+	0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A,
+	0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+	0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC,
+	0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC,
+	0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29,
+	0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9,
+	0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19,
+	0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+	0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+	0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+	0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C,
+	0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05,
+	0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA,
+	0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF,
+	0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3,
+	0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF,
+	0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8,
+	0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08,
+	0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01,
+	0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14,
+	0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00,
+	0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44,
+	0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8,
+	0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC,
+	0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F,
+	0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00,
+	0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15,
+	0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05,
+	0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06,
+	0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9,
+	0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00,
+	0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10,
+	0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC,
+	0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E,
+	0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7,
+	0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E,
+	0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00,
+	0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+	0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68,
+	0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09,
+	0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2,
+	0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF,
+	0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A,
+	0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF,
+	0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42,
+	0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04,
+	0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01,
+	0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97,
+	0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00,
+	0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D,
+	0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9,
+	0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC,
+	0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1,
+	0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+	0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+	0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06,
+	0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C,
+	0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00,
+	0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7,
+	0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC,
+	0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32,
+	0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6,
+	0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE,
+	0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00,
+	0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+	0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56,
+	0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D,
+	0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89,
+	0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF,
+	0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60,
+	0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40,
+	0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48,
+	0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98,
+	0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01,
+	0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80,
+	0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00,
+	0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD,
+	0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB,
+	0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC,
+	0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F,
+	0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+	0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D,
+	0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04,
+	0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06,
+	0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96,
+	0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00,
+	0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0,
+	0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC,
+	0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35,
+	0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4,
+	0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66,
+	0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF,
+	0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+	0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47,
+	0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12,
+	0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51,
+	0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF,
+	0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64,
+	0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71,
+	0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47,
+	0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE,
+	0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01,
+	0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6,
+	0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C,
+	0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02,
+	0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C,
+	0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC,
+	0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA,
+	0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+	0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+	0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+	0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07,
+	0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18,
+	0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00,
+	0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B,
+	0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD,
+	0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3,
+	0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4,
+	0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF,
+	0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+	0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+	0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D,
+	0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17,
+	0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B,
+	0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF,
+	0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B,
+	0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C,
+	0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46,
+	0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4,
+	0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01,
+	0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61,
+	0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D,
+	0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE,
+	0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16,
+	0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC,
+	0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21,
+	0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13,
+	0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02,
+	0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07,
+	0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5,
+	0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00,
+	0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94,
+	0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD,
+	0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2,
+	0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17,
+	0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF,
+	0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+	0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+	0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37,
+	0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C,
+	0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA,
+	0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00,
+	0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56,
+	0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF,
+	0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE,
+	0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44,
+	0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9,
+	0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00,
+	0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47,
+	0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36,
+	0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB,
+	0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11,
+	0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC,
+	0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27,
+	0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+	0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+	0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD,
+	0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07,
+	0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E,
+	0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00,
+	0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05,
+	0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD,
+	0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33,
+	0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1,
+	0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C,
+	0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+	0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+	0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37,
+	0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21,
+	0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD,
+	0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00,
+	0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68,
+	0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF,
+	0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+	0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41,
+	0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED,
+	0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00,
+	0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C,
+	0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD,
+	0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8,
+	0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C,
+	0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD,
+	0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF,
+	0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D,
+	0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF,
+	0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE,
+	0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06,
+	0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42,
+	0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47,
+	0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02,
+	0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E,
+	0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1,
+	0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94,
+	0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+	0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+	0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+	0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C,
+	0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26,
+	0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96,
+	0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00,
+	0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0,
+	0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF,
+	0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E,
+	0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF,
+	0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00,
+	0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6,
+	0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+	0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93,
+	0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6,
+	0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08,
+	0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD,
+	0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC,
+	0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+	0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+	0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF,
+	0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06,
+	0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31,
+	0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94,
+	0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01,
+	0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26,
+	0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1,
+	0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE,
+	0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+	0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+	0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+	0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49,
+	0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B,
+	0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76,
+	0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00,
+	0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E,
+	0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF,
+	0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B,
+	0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF,
+	0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48,
+	0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00,
+	0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C,
+	0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4,
+	0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04,
+	0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE,
+	0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33,
+	0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A,
+	0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02,
+	0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01,
+	0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05,
+	0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6,
+	0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00,
+	0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C,
+	0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1,
+	0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8,
+	0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+	0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+	0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C,
+	0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30,
+	0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C,
+	0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00,
+	0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1,
+	0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF,
+	0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8,
+	0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37,
+	0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF,
+	0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6,
+	0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B,
+	0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3,
+	0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01,
+	0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF,
+	0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A,
+	0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+	0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+	0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+	0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04,
+	0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04,
+	0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C,
+	0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
+	0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48,
+	0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF,
+	0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E,
+	0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2,
+	0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5,
+	0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+	0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+	0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76,
+	0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34,
+	0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49,
+	0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF,
+	0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2,
+	0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01,
+	0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE,
+	0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32,
+	0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE,
+	0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94,
+	0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31,
+	0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2,
+	0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF,
+	0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00,
+	0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6,
+	0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+	0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC,
+	0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04,
+	0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08,
+	0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03,
+	0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93,
+	0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF,
+	0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6,
+	0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE,
+	0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF,
+	0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3,
+	0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00,
+	0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+	0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+	0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96,
+	0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38,
+	0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C,
+	0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF,
+	0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C,
+	0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00,
+	0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94,
+	0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E,
+	0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E,
+	0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE,
+	0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47,
+	0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42,
+	0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1,
+	0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+	0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00,
+	0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D,
+	0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+	0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+	0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C,
+	0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02,
+	0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD,
+	0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+	0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C,
+	0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE,
+	0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED,
+	0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5,
+	0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7,
+	0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00,
+	0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+	0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+	0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD,
+	0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C,
+	0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF,
+	0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69,
+	0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00,
+	0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C,
+	0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29,
+	0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01,
+	0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05,
+	0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF,
+	0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E,
+	0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1,
+	0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD,
+	0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01,
+	0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46,
+	0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00,
+	0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27,
+	0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06,
+	0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11,
+	0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00,
+	0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36,
+	0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47,
+	0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD,
+	0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9,
+	0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7,
+	0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE,
+	0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+	0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+	0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA,
+	0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40,
+	0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8,
+	0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00,
+	0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17,
+	0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24,
+	0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01,
+	0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94,
+	0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF,
+	0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5,
+	0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1,
+	0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE,
+	0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13,
+	0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00,
+	0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+	0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+	0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16,
+	0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF,
+	0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D,
+	0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61,
+	0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD,
+	0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4,
+	0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA,
+	0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C,
+	0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+	0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+	0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B,
+	0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43,
+	0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D,
+	0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF,
+	0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77,
+	0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00,
+	0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4,
+	0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F,
+	0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01,
+	0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B,
+	0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF,
+	0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18,
+	0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2,
+	0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD,
+	0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA,
+	0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00,
+	0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA,
+	0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07,
+	0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C,
+	0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD,
+	0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C,
+	0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6,
+	0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC,
+	0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE,
+	0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD,
+	0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71,
+	0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+	0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+	0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51,
+	0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45,
+	0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47,
+	0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF,
+	0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB,
+	0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF,
+	0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66,
+	0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A,
+	0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01,
+	0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0,
+	0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF,
+	0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96,
+	0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3,
+	0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD,
+	0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D,
+	0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00,
+	0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+	0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+	0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03,
+	0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD,
+	0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF,
+	0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80,
+	0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC,
+	0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98,
+	0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00,
+	0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40,
+	0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+	0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+	0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89,
+	0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47,
+	0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56,
+	0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61,
+	0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF,
+	0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15,
+	0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01,
+	0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7,
+	0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF,
+	0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C,
+	0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4,
+	0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC,
+	0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3,
+	0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00,
+	0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1,
+	0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07,
+	0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03,
+	0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D,
+	0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF,
+	0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97,
+	0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC,
+	0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F,
+	0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC,
+	0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42,
+	0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+	0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+	0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2,
+	0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48,
+	0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68,
+	0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF,
+	0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5,
+	0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF,
+	0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E,
+	0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10,
+	0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01,
+	0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10,
+	0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF,
+	0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9,
+	0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5,
+	0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC,
+	0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15,
+	0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00,
+	0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+	0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+	0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04,
+	0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44,
+	0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+	0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14,
+	0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC,
+	0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25,
+	0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA,
+	0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8,
+	0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+	0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+	0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA,
+	0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48,
+	0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C,
+	0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF,
+	0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86,
+	0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF,
+	0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19,
+	0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B,
+	0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01,
+	0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC,
+	0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00,
+	0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A,
+	0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7,
+	0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF,
+	0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC,
+	0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E,
+	0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00,
+	0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C,
+	0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06,
+	0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF,
+	0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05,
+	0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77,
+	0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00,
+	0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD,
+	0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC,
+	0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A,
+	0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9,
+	0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E,
+	0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+	0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78,
+	0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06,
+	0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1,
+	0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF,
+	0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15,
+	0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF,
+	0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3,
+	0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07,
+	0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01,
+	0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7,
+	0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00,
+	0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A,
+	0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8,
+	0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC,
+	0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38,
+	0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00,
+	0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+	0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+	0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06,
+	0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9,
+	0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00,
+	0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55,
+	0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC,
+	0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F,
+	0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7,
+	0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2,
+	0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00,
+	0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+	0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+	0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64,
+	0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A,
+	0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8,
+	0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF,
+	0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62,
+	0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF,
+	0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14,
+	0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48,
+	0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86,
+	0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01,
+	0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C,
+	0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00,
+	0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45,
+	0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA,
+	0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC,
+	0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00,
+	0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00,
+	0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83,
+	0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05,
+	0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06,
+	0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10,
+	0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00,
+	0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20,
+	0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC,
+	0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33,
+	0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6,
+	0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11,
+	0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00,
+	0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+	0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53,
+	0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E,
+	0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F,
+	0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF,
+	0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80,
+	0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49,
+	0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48,
+	0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C,
+	0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01,
+	0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85,
+	0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00,
+	0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25,
+	0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB,
+	0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC,
+	0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96,
+	0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF,
+	0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+	0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+	0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06,
+	0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E,
+	0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00,
+	0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E,
+	0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC,
+	0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36,
+	0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4,
+	0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77,
+	0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF,
+	0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+	0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+	0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45,
+	0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13,
+	0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47,
+	0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF,
+	0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81,
+	0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79,
+	0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47,
+	0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2,
+	0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01,
+	0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC,
+	0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7,
+	0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01,
+	0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B,
+	0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC,
+	0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7,
+	0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF,
+	0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70,
+	0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03,
+	0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07,
+	0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04,
+	0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00,
+	0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C,
+	0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD,
+	0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3,
+	0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4,
+	0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF,
+	0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+	0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+	0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B,
+	0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18,
+	0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12,
+	0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF,
+	0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78,
+	0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2,
+	0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46,
+	0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8,
+	0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01,
+	0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84,
+	0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A,
+	0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE,
+	0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16,
+	0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC,
+	0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26,
+	0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+	0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+	0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD,
+	0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07,
+	0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71,
+	0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7,
+	0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02,
+	0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05,
+	0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+	0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED,
+	0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00,
+	0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84,
+	0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+	0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+	0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00,
+	0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04,
+	0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B,
+	0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11,
+	0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00,
+	0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C,
+	0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00,
+	0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8,
+	0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3,
+	0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD,
+	0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23,
+	0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF,
+	0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+	0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+	0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07,
+	0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5,
+	0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF,
+	0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC,
+	0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD,
+	0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35,
+	0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4,
+	0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71,
+	0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+	0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44,
+	0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF,
+	0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+	0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+	0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+	0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+	0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5,
+	0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00,
+	0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1,
+	0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07,
+	0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF,
+	0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A,
+	0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00,
+	0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B,
+	0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6,
+	0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF,
+	0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE,
+	0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99,
+	0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+	0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+	0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06,
+	0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E,
+	0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8,
+	0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC,
+	0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2,
+	0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20,
+	0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C,
+	0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00,
+	0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+	0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+	0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF,
+	0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F,
+	0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00,
+	0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43,
+	0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48,
+	0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99,
+	0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE,
+	0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7,
+	0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00,
+	0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6,
+	0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9,
+	0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF,
+	0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C,
+	0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF,
+	0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+	0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+	0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00,
+	0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05,
+	0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35,
+	0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF,
+	0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1,
+	0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC,
+	0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D,
+	0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1,
+	0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A,
+	0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36,
+	0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01,
+	0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+	0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+	0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+	0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB,
+	0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00,
+	0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E,
+	0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46,
+	0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6,
+	0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01,
+	0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D,
+	0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF,
+	0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74,
+	0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC,
+	0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00,
+	0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01,
+	0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8,
+	0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+	0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+	0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06,
+	0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03,
+	0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D,
+	0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+	0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56,
+	0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC,
+	0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A,
+	0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1,
+	0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB,
+	0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00,
+	0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF,
+	0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+	0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+	0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+	0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A,
+	0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF,
+	0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8,
+	0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41,
+	0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE,
+	0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01,
+	0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9,
+	0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF,
+	0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65,
+	0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD,
+	0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14,
+	0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE,
+	0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E,
+	0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00,
+	0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+	0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+	0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F,
+	0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01,
+	0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15,
+	0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00,
+	0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD,
+	0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC,
+	0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD,
+	0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3,
+	0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00,
+	0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A,
+	0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+	0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+	0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81,
+	0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF,
+	0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A,
+	0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F,
+	0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01,
+	0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85,
+	0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF,
+	0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4,
+	0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7,
+	0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A,
+	0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD,
+	0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91,
+	0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00,
+	0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+	0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+	0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A,
+	0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD,
+	0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4,
+	0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00,
+	0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB,
+	0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD,
+	0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7,
+	0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7,
+	0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA,
+	0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF,
+	0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D,
+	0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01,
+	0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+	0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+	0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF,
+	0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40,
+	0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC,
+	0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32,
+	0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01,
+	0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A,
+	0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00,
+	0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48,
+	0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3,
+	0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03,
+	0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC,
+	0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D,
+	0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00,
+	0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+	0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+	0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00,
+	0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03,
+	0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C,
+	0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00,
+	0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6,
+	0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE,
+	0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC,
+	0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD,
+	0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C,
+	0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF,
+	0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B,
+	0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01,
+	0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+	0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+	0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF,
+	0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52,
+	0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59,
+	0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28,
+	0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01,
+	0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E,
+	0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00,
+	0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37,
+	0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1,
+	0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC,
+	0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F,
+	0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00,
+	0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+	0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+	0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF,
+	0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05,
+	0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE,
+	0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+	0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8,
+	0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01,
+	0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20,
+	0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC,
+	0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50,
+	0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF,
+	0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70,
+	0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01,
+	0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+	0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+	0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00,
+	0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B,
+	0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF,
+	0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E,
+	0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01,
+	0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B,
+	0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90,
+	0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1,
+	0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD,
+	0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC,
+	0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB,
+	0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF,
+	0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+	0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+	0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06,
+	0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49,
+	0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF,
+	0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E,
+	0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF,
+	0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8,
+	0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C,
+	0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00,
+	0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83,
+	0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE,
+	0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+	0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+	0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00,
+	0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7,
+	0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF,
+	0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7,
+	0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14,
+	0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32,
+	0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00,
+	0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47,
+	0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57,
+	0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2,
+	0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC,
+	0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29,
+	0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+	0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+	0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+	0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07,
+	0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C,
+	0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB,
+	0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD,
+	0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33,
+	0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5,
+	0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D,
+	0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+	0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59,
+	0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF,
+	0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C,
+	0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+	0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+	0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00,
+	0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13,
+	0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF,
+	0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12,
+	0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B,
+	0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29,
+	0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF,
+	0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19,
+	0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00,
+	0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85,
+	0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4,
+	0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE,
+	0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD,
+	0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6,
+	0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+	0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+	0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07,
+	0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78,
+	0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E,
+	0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC,
+	0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3,
+	0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE,
+	0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C,
+	0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00,
+	0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+	0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+	0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+	0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF,
+	0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D,
+	0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00,
+	0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18,
+	0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48,
+	0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87,
+	0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE,
+	0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61,
+	0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00,
+	0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06,
+	0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7,
+	0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF,
+	0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE,
+	0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00,
+	0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF,
+	0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+	0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+	0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06,
+	0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33,
+	0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E,
+	0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC,
+	0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32,
+	0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1,
+	0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E,
+	0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18,
+	0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01,
+	0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+	0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+	0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF,
+	0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC,
+	0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00,
+	0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C,
+	0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47,
+	0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3,
+	0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01,
+	0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73,
+	0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF,
+	0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB,
+	0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB,
+	0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00,
+	0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00,
+	0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1,
+	0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF,
+	0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+	0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+	0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04,
+	0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04,
+	0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55,
+	0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+	0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74,
+	0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC,
+	0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23,
+	0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1,
+	0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7,
+	0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2,
+	0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01,
+	0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+	0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+	0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+	0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E,
+	0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF,
+	0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5,
+	0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43,
+	0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE,
+	0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01,
+	0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1,
+	0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF,
+	0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC,
+	0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF,
+	0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18,
+	0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02,
+	0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8,
+	0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00,
+	0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+	0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+	0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B,
+	0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02,
+	0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF,
+	0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00,
+	0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F,
+	0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC,
+	0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+	0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2,
+	0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00,
+	0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06,
+	0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+	0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+	0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+	0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF,
+	0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE,
+	0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF,
+	0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6,
+	0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D,
+	0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03,
+	0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01,
+	0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02,
+	0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF,
+	0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6,
+	0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9,
+	0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD,
+	0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B,
+	0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00,
+	0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+	0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+	0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00,
+	0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06,
+	0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16,
+	0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF,
+	0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88,
+	0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02,
+	0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC,
+	0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A,
+	0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18,
+	0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF,
+	0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3,
+	0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00,
+	0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75,
+	0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10,
+	0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72,
+	0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF,
+	0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6,
+	0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF,
+	0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C,
+	0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10,
+	0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD,
+	0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00,
+	0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35,
+	0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1,
+	0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E,
+	0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9,
+	0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02,
+	0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F,
+	0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF,
+	0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71,
+	0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05,
+	0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF,
+	0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06,
+	0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6,
+	0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF,
+	0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B,
+	0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02,
+	0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89,
+	0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E,
+	0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1,
+	0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF,
+	0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD,
+	0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00,
+	0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F,
+	0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10,
+	0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E,
+	0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF,
+	0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA,
+	0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF,
+	0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6,
+	0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10,
+	0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2,
+	0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00,
+	0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3,
+	0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF,
+	0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50,
+	0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E,
+	0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2,
+	0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02,
+	0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03,
+	0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF,
+	0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB,
+	0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06,
+	0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00,
+	0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05,
+	0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73,
+	0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF,
+	0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88,
+	0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02,
+	0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93,
+	0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E,
+	0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9,
+	0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF,
+	0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E,
+	0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00,
+	0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C,
+	0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10,
+	0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C,
+	0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF,
+	0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38,
+	0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF,
+	0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6,
+	0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10,
+	0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97,
+	0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00,
+	0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D,
+	0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF,
+	0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB,
+	0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A,
+	0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00,
+	0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02,
+	0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99,
+	0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF,
+	0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45,
+	0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06,
+	0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00,
+	0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04,
+	0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4,
+	0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF,
+	0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00,
+	0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03,
+	0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D,
+	0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D,
+	0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7,
+	0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF,
+	0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05,
+	0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01,
+	0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D,
+	0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10,
+	0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D,
+	0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF,
+	0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C,
+	0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF,
+	0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB,
+	0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10,
+	0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D,
+	0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00,
+	0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3,
+	0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF,
+	0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C,
+	0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B,
+	0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01,
+	0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F,
+	0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF,
+	0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF,
+	0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07,
+	0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6,
+	0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09,
+	0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D,
+	0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+	0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73,
+	0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03,
+	0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8,
+	0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D,
+	0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA,
+	0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF,
+	0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9,
+	0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF,
+	0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70,
+	0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F,
+	0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72,
+	0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF,
+	0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6,
+	0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF,
+	0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96,
+	0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10,
+	0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84,
+	0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF,
+	0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52,
+	0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF,
+	0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3,
+	0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B,
+	0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE,
+	0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01,
+	0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5,
+	0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF,
+	0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C,
+	0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07,
+	0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC,
+	0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09,
+	0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2,
+	0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+	0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3,
+	0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04,
+	0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3,
+	0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C,
+	0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24,
+	0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF,
+	0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E,
+	0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF,
+	0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75,
+	0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F,
+	0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A,
+	0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF,
+	0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15,
+	0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF,
+	0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0,
+	0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F,
+	0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7,
+	0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF,
+	0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC,
+	0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF,
+	0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F,
+	0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C,
+	0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC,
+	0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04,
+	0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E,
+	0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00,
+	0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B,
+	0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08,
+	0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2,
+	0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08,
+	0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E,
+	0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+	0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50,
+	0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04,
+	0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE,
+	0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C,
+	0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53,
+	0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF,
+	0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88,
+	0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF,
+	0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C,
+	0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F,
+	0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86,
+	0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF,
+	0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A,
+	0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF,
+	0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF,
+	0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F,
+	0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF,
+	0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E,
+	0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF,
+	0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2,
+	0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C,
+	0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9,
+	0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04,
+	0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6,
+	0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00,
+	0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD,
+	0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09,
+	0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7,
+	0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07,
+	0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91,
+	0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF,
+	0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82,
+	0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01,
+	0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8,
+	0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B,
+	0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A,
+	0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF,
+	0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65,
+	0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF,
+	0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90,
+	0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10,
+	0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81,
+	0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF,
+	0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73,
+	0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF,
+	0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74,
+	0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F,
+	0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4,
+	0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF,
+	0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8,
+	0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF,
+	0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B,
+	0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D,
+	0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5,
+	0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03,
+	0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32,
+	0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF,
+	0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72,
+	0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09,
+	0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA,
+	0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07,
+	0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA,
+	0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF,
+	0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14,
+	0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01,
+	0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1,
+	0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B,
+	0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6,
+	0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF,
+	0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37,
+	0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00,
+	0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81,
+	0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10,
+	0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79,
+	0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF,
+	0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91,
+	0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF,
+	0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41,
+	0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10,
+	0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA,
+	0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01,
+	0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88,
+	0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF,
+	0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B,
+	0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D,
+	0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0,
+	0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03,
+	0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1,
+	0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF,
+	0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22,
+	0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04,
+	0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD,
+	0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06,
+	0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A,
+	0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF,
+	0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F,
+	0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02,
+	0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA,
+	0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A,
+	0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A,
+	0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF,
+	0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF,
+	0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00,
+	0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77,
+	0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10,
+	0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73,
+	0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF,
+	0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4,
+	0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF,
+	0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14,
+	0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10,
+	0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF,
+	0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00,
+	0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A,
+	0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1,
+	0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E,
+	0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA,
+	0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02,
+	0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53,
+	0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF,
+	0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D,
+	0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05,
+	0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF,
+	0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06,
+	0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF,
+	0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF,
+	0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23,
+	0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02,
+	0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87,
+	0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E,
+	0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D,
+	0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF,
+	0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB,
+	0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00,
+	0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70,
+	0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10,
+	0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E,
+	0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF,
+	0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB,
+	0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF,
+	0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC,
+	0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10,
+	0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4,
+	0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00,
+	0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9,
+	0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF,
+	0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F,
+	0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E,
+	0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3,
+	0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02,
+	0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7,
+	0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF,
+	0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7,
+	0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06,
+	0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00,
+	0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05,
+	0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B,
+	0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF,
+	0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1,
+	0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02,
+	0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91,
+	0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E,
+	0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5,
+	0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF,
+	0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D,
+	0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00,
+	0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D,
+	0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10,
+	0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C,
+	0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF,
+	0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27,
+	0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF,
+	0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB,
+	0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10,
+	0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99,
+	0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00,
+	0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34,
+	0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF,
+	0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3,
+	0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A,
+	0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00,
+	0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02,
+	0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D,
+	0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF,
+	0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61,
+	0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06,
+	0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00,
+	0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05,
+	0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC,
+	0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF,
+	0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A,
+	0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03,
+	0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B,
+	0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D,
+	0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1,
+	0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF,
+	0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16,
+	0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00,
+	0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C,
+	0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10,
+	0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D,
+	0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF,
+	0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D,
+	0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF,
+	0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0,
+	0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10,
+	0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F,
+	0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00,
+	0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA,
+	0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF,
+	0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43,
+	0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A,
+	0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01,
+	0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13,
+	0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF,
+	0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB,
+	0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07,
+	0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4,
+	0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09,
+	0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E,
+	0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+	0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E,
+	0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03,
+	0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6,
+	0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D,
+	0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3,
+	0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF,
+	0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA,
+	0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF,
+	0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F,
+	0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F,
+	0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71,
+	0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF,
+	0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47,
+	0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE,
+	0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30,
+	0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20,
+	0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42,
+	0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF,
+	0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC,
+	0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00,
+	0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F,
+	0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09,
+	0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD,
+	0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB,
+	0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2,
+	0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73,
+	0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03,
+	0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE,
+	0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC,
+	0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49,
+	0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF,
+	0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65,
+	0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB,
+	0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36,
+	0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E,
+	0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02,
+	0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00,
+	0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0,
+	0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00,
+	0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1,
+	0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21,
+	0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00,
+	0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62,
+	0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00,
+	0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52,
+	0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20,
+	0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D,
+	0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF,
+	0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2,
+	0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00,
+	0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78,
+	0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A,
+	0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD,
+	0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB,
+	0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4,
+	0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58,
+	0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04,
+	0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF,
+	0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB,
+	0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F,
+	0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+	0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56,
+	0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB,
+	0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D,
+	0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D,
+	0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9,
+	0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00,
+	0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A,
+	0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00,
+	0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB,
+	0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21,
+	0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00,
+	0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F,
+	0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00,
+	0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71,
+	0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20,
+	0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58,
+	0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00,
+	0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9,
+	0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00,
+	0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96,
+	0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B,
+	0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC,
+	0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB,
+	0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10,
+	0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF,
+	0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37,
+	0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB,
+	0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB,
+	0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D,
+	0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+	0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C,
+	0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB,
+	0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24,
+	0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C,
+	0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD,
+	0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00,
+	0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24,
+	0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00,
+	0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3,
+	0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21,
+	0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6,
+	0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00,
+	0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A,
+	0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00,
+	0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C,
+	0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21,
+	0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64,
+	0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00,
+	0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02,
+	0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00,
+	0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB,
+	0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C,
+	0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB,
+	0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB,
+	0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56,
+	0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF,
+	0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69,
+	0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB,
+	0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB,
+	0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44,
+	0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF,
+	0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49,
+	0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB,
+	0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D,
+	0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B,
+	0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E,
+	0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00,
+	0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD,
+	0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00,
+	0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8,
+	0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20,
+	0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2,
+	0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00,
+	0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF,
+	0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00,
+	0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4,
+	0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21,
+	0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70,
+	0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00,
+	0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A,
+	0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00,
+	0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4,
+	0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D,
+	0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA,
+	0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB,
+	0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8,
+	0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF,
+	0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4,
+	0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB,
+	0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB,
+	0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53,
+	0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF,
+	0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B,
+	0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB,
+	0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16,
+	0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A,
+	0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D,
+	0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00,
+	0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26,
+	0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF,
+	0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB,
+	0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20,
+	0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD,
+	0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00,
+	0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5,
+	0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9,
+	0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21,
+	0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D,
+	0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00,
+	0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32,
+	0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00,
+	0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12,
+	0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E,
+	0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9,
+	0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB,
+	0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05,
+	0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF,
+	0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA,
+	0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC,
+	0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9,
+	0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03,
+	0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2,
+	0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+	0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54,
+	0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB,
+	0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F,
+	0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09,
+	0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A,
+	0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00,
+	0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90,
+	0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF,
+	0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC,
+	0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20,
+	0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7,
+	0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00,
+	0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4,
+	0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00,
+	0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB,
+	0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21,
+	0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A,
+	0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00,
+	0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47,
+	0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00,
+	0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43,
+	0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F,
+	0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8,
+	0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB,
+	0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E,
+	0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF,
+	0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B,
+	0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC,
+	0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8,
+	0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02,
+	0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65,
+	0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66,
+	0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB,
+	0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A,
+	0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08,
+	0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4,
+	0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00,
+	0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB,
+	0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF,
+	0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA,
+	0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F,
+	0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1,
+	0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00,
+	0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8,
+	0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00,
+	0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5,
+	0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17,
+	0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB,
+	0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00,
+	0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59,
+	0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00,
+	0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77,
+	0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10,
+	0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7,
+	0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB,
+	0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2,
+	0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF,
+	0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98,
+	0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC,
+	0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7,
+	0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01,
+	0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29,
+	0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80,
+	0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB,
+	0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05,
+	0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07,
+	0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC,
+	0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00,
+	0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38,
+	0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF,
+	0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6,
+	0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F,
+	0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9,
+	0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00,
+	0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91,
+	0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00,
+	0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30,
+	0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18,
+	0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE,
+	0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00,
+	0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66,
+	0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00,
+	0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE,
+	0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11,
+	0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7,
+	0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC,
+	0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62,
+	0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF,
+	0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF,
+	0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC,
+	0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7,
+	0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01,
+	0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF,
+	0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF,
+	0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F,
+	0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD,
+	0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02,
+	0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06,
+	0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72,
+	0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00,
+	0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78,
+	0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF,
+	0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1,
+	0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E,
+	0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0,
+	0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00,
+	0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F,
+	0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00,
+	0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B,
+	0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19,
+	0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00,
+	0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E,
+	0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00,
+	0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7,
+	0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13,
+	0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7,
+	0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC,
+	0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED,
+	0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF,
+	0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73,
+	0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD,
+	0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7,
+	0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00,
+	0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5,
+	0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF,
+	0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87,
+	0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD,
+	0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE,
+	0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05,
+	0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38,
+	0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00,
+	0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC,
+	0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE,
+	0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA,
+	0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E,
+	0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6,
+	0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00,
+	0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21,
+	0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00,
+	0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5,
+	0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A,
+	0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05,
+	0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD,
+	0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63,
+	0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF,
+	0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22,
+	0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14,
+	0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7,
+	0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC,
+	0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84,
+	0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF,
+	0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1,
+	0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD,
+	0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7,
+	0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF,
+	0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E,
+	0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF,
+	0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B,
+	0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD,
+	0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC,
+	0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04,
+	0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC,
+	0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00,
+	0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3,
+	0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE,
+	0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2,
+	0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D,
+	0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA,
+	0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00,
+	0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6,
+	0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00,
+	0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD,
+	0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B,
+	0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09,
+	0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE,
+	0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48,
+	0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E,
+	0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15,
+	0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8,
+	0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC,
+	0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26,
+	0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF,
+	0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C,
+	0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE,
+	0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8,
+	0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF,
+	0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49,
+	0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF,
+	0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69,
+	0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC,
+	0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B,
+	0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16,
+	0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD,
+	0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00,
+	0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0,
+	0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE,
+	0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9,
+	0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C,
+	0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB,
+	0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00,
+	0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C,
+	0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00,
+	0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13,
+	0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C,
+	0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F,
+	0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE,
+	0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36,
+	0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A,
+	0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16,
+	0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9,
+	0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC,
+	0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4,
+	0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF,
+	0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12,
+	0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE,
+	0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9,
+	0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE,
+	0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18,
+	0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF,
+	0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54,
+	0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC,
+	0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E,
+	0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15,
+	0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC,
+	0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04,
+	0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE,
+	0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE,
+	0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B,
+	0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB,
+	0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00,
+	0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14,
+	0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00,
+	0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47,
+	0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C,
+	0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15,
+	0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE,
+	0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C,
+	0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00,
+	0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56,
+	0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04,
+	0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00,
+	0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD,
+	0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C,
+	0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF,
+	0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6,
+	0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8,
+	0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE,
+	0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42,
+	0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00,
+	0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5,
+	0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03,
+	0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC,
+	0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3,
+	0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF,
+	0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF,
+	0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3,
+	0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE,
+	0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68,
+	0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A,
+	0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1,
+	0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00,
+	0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8,
+	0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00,
+	0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB,
+	0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27,
+	0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3,
+	0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE,
+	0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E,
+	0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF,
+	0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4,
+	0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4,
+	0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01,
+	0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03,
+	0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F,
+	0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62,
+	0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05,
+	0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05,
+	0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB,
+	0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54,
+	0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03,
+	0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1,
+	0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3,
+	0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7,
+	0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF,
+	0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB,
+	0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE,
+	0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55,
+	0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28,
+	0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4,
+	0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00,
+	0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8,
+	0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00,
+	0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8,
+	0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29,
+	0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD,
+	0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE,
+	0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02,
+	0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF,
+	0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A,
+	0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3,
+	0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01,
+	0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03,
+	0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28,
+	0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+	0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16,
+	0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05,
+	0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05,
+	0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00,
+	0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE,
+	0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04,
+	0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5,
+	0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4,
+	0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2,
+	0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF,
+	0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F,
+	0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE,
+	0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43,
+	0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26,
+	0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6,
+	0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00,
+	0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98,
+	0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00,
+	0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25,
+	0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B,
+	0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7,
+	0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE,
+	0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8,
+	0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF,
+	0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F,
+	0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3,
+	0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02,
+	0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03,
+	0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39,
+	0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00,
+	0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC,
+	0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05,
+	0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05,
+	0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40,
+	0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71,
+	0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04,
+	0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9,
+	0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4,
+	0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E,
+	0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF,
+	0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70,
+	0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE,
+	0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31,
+	0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24,
+	0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7,
+	0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00,
+	0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68,
+	0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00,
+	0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51,
+	0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D,
+	0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1,
+	0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE,
+	0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0,
+	0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF,
+	0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4,
+	0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3,
+	0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02,
+	0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02,
+	0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41,
+	0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00,
+	0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84,
+	0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06,
+	0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D,
+	0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9,
+	0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8,
+	0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D,
+	0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04,
+	0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5,
+	0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D,
+	0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF,
+	0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42,
+	0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD,
+	0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20,
+	0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22,
+	0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07,
+	0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00,
+	0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24,
+	0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00,
+	0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C,
+	0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F,
+	0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB,
+	0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE,
+	0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC,
+	0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF,
+	0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79,
+	0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3,
+	0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03,
+	0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02,
+	0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40,
+	0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00,
+	0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C,
+	0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06,
+	0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E,
+	0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9,
+	0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2,
+	0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0,
+	0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04,
+	0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5,
+	0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F,
+	0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF,
+	0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16,
+	0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD,
+	0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10,
+	0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20,
+	0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17,
+	0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+	0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD,
+	0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00,
+	0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7,
+	0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31,
+	0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5,
+	0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF,
+	0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C,
+	0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF,
+	0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F,
+	0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2,
+	0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03,
+	0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02,
+	0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35,
+	0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00,
+	0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3,
+	0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06,
+	0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F,
+	0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA,
+	0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2,
+	0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A,
+	0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05,
+	0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6,
+	0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34,
+	0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF,
+	0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED,
+	0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD,
+	0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00,
+	0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D,
+	0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26,
+	0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00,
+	0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F,
+	0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00,
+	0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1,
+	0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32,
+	0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0,
+	0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF,
+	0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41,
+	0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF,
+	0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6,
+	0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2,
+	0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04,
+	0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01,
+	0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20,
+	0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00,
+	0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8,
+	0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06,
+	0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10,
+	0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB,
+	0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5,
+	0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00,
+	0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1,
+	0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC,
+	0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+	0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6,
+	0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D,
+	0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF,
+	0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8,
+	0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD,
+	0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2,
+	0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B,
+	0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33,
+	0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00,
+	0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB,
+	0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01,
+	0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9,
+	0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34,
+	0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA,
+	0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF,
+	0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B,
+	0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF,
+	0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F,
+	0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2,
+	0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05,
+	0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01,
+	0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02,
+	0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00,
+	0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A,
+	0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06,
+	0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10,
+	0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC,
+	0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE,
+	0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00,
+	0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2,
+	0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC,
+	0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7,
+	0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09,
+	0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB,
+	0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD,
+	0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4,
+	0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19,
+	0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F,
+	0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00,
+	0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C,
+	0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01,
+	0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20,
+	0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36,
+	0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5,
+	0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF,
+	0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB,
+	0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF,
+	0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A,
+	0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3,
+	0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06,
+	0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01,
+	0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9,
+	0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00,
+	0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09,
+	0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06,
+	0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11,
+	0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC,
+	0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC,
+	0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00,
+	0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34,
+	0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC,
+	0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B,
+	0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8,
+	0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8,
+	0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00,
+	0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95,
+	0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD,
+	0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7,
+	0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17,
+	0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A,
+	0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00,
+	0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87,
+	0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01,
+	0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45,
+	0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37,
+	0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0,
+	0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF,
+	0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61,
+	0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF,
+	0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8,
+	0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3,
+	0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06,
+	0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00,
+	0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7,
+	0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00,
+	0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3,
+	0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06,
+	0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11,
+	0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD,
+	0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F,
+	0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00,
+	0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87,
+	0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC,
+	0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96,
+	0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08,
+	0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D,
+	0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+	0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89,
+	0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD,
+	0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB,
+	0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15,
+	0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53,
+	0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF,
+	0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5,
+	0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01,
+	0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69,
+	0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39,
+	0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B,
+	0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF,
+	0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF,
+	0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF,
+	0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79,
+	0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3,
+	0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07,
+	0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00,
+	0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A,
+	0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00,
+	0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58,
+	0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06,
+	0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10,
+	0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE,
+	0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27,
+	0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00,
+	0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE,
+	0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC,
+	0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92,
+	0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06,
+	0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56,
+	0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+	0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88,
+	0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD,
+	0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0,
+	0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13,
+	0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A,
+	0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF,
+	0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6,
+	0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01,
+	0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A,
+	0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A,
+	0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97,
+	0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00,
+	0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93,
+	0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF,
+	0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D,
+	0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3,
+	0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08,
+	0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00,
+	0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23,
+	0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00,
+	0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7,
+	0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05,
+	0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10,
+	0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE,
+	0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45,
+	0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00,
+	0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69,
+	0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC,
+	0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F,
+	0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04,
+	0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D,
+	0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
+	0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94,
+	0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC,
+	0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6,
+	0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11,
+	0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F,
+	0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF,
+	0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB,
+	0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01,
+	0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9,
+	0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C,
+	0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93,
+	0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00,
+	0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F,
+	0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF,
+	0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4,
+	0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4,
+	0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09,
+	0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF,
+	0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1,
+	0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00,
+	0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90,
+	0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05,
+	0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F,
+	0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF,
+	0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67,
+	0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00,
+	0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8,
+	0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD,
+	0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C,
+	0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02,
+	0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41,
+	0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF,
+	0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A,
+	0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01,
+	0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD,
+	0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F,
+	0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63,
+	0x01, 0x8D, 0xFF, 0x0F, 0x00 
+};
+
+static u16
+coefficient_sizes[8 * 2] = {
+	/* Playback */
+	0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000,
+	/* capture */
+	0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000,
+};
+
diff -Nru linux/sound/pci/rme96.c linux-2.4.19-pre5-mjc/sound/pci/rme96.c
--- linux/sound/pci/rme96.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/rme96.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2523 @@
+/*
+ *   ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio
+ *   interfaces 
+ *
+ *	Copyright (c) 2000, 2001 Anders Torger <torger@ludd.luth.se>
+ *    
+ *      Thanks to Henk Hesselink <henk@anda.nl> for the analog volume control
+ *      code.
+ *
+ *   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
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/asoundef.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+/* note, two last pcis should be equal, it is not a bug */
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Anders Torger <torger@ludd.luth.se>");
+MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, "
+		   "Digi96/8 PAD");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{RME,Digi96},"
+		"{RME,Digi96/8},"
+		"{RME,Digi96/8 PRO},"
+		"{RME,Digi96/8 PST},"
+		"{RME,Digi96/8 PAD}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for RME Digi96 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for RME Digi96 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable RME Digi96 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+
+/*
+ * Defines for RME Digi96 series, from internal RME reference documents
+ * dated 12.01.00
+ */
+
+#define RME96_SPDIF_NCHANNELS 2
+
+/* Playback and capture buffer size */
+#define RME96_BUFFER_SIZE 0x10000
+
+/* IO area size */
+#define RME96_IO_SIZE 0x60000
+
+/* IO area offsets */
+#define RME96_IO_PLAY_BUFFER      0x0
+#define RME96_IO_REC_BUFFER       0x10000
+#define RME96_IO_CONTROL_REGISTER 0x20000
+#define RME96_IO_ADDITIONAL_REG   0x20004
+#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008
+#define RME96_IO_CONFIRM_REC_IRQ  0x2000C
+#define RME96_IO_SET_PLAY_POS     0x40000
+#define RME96_IO_RESET_PLAY_POS   0x4FFFC
+#define RME96_IO_SET_REC_POS      0x50000
+#define RME96_IO_RESET_REC_POS    0x5FFFC
+#define RME96_IO_GET_PLAY_POS     0x20000
+#define RME96_IO_GET_REC_POS      0x30000
+
+/* Write control register bits */
+#define RME96_WCR_START     (1 << 0)
+#define RME96_WCR_START_2   (1 << 1)
+#define RME96_WCR_GAIN_0    (1 << 2)
+#define RME96_WCR_GAIN_1    (1 << 3)
+#define RME96_WCR_MODE24    (1 << 4)
+#define RME96_WCR_MODE24_2  (1 << 5)
+#define RME96_WCR_BM        (1 << 6)
+#define RME96_WCR_BM_2      (1 << 7)
+#define RME96_WCR_ADAT      (1 << 8)
+#define RME96_WCR_FREQ_0    (1 << 9)
+#define RME96_WCR_FREQ_1    (1 << 10)
+#define RME96_WCR_DS        (1 << 11)
+#define RME96_WCR_PRO       (1 << 12)
+#define RME96_WCR_EMP       (1 << 13)
+#define RME96_WCR_SEL       (1 << 14)
+#define RME96_WCR_MASTER    (1 << 15)
+#define RME96_WCR_PD        (1 << 16)
+#define RME96_WCR_INP_0     (1 << 17)
+#define RME96_WCR_INP_1     (1 << 18)
+#define RME96_WCR_THRU_0    (1 << 19)
+#define RME96_WCR_THRU_1    (1 << 20)
+#define RME96_WCR_THRU_2    (1 << 21)
+#define RME96_WCR_THRU_3    (1 << 22)
+#define RME96_WCR_THRU_4    (1 << 23)
+#define RME96_WCR_THRU_5    (1 << 24)
+#define RME96_WCR_THRU_6    (1 << 25)
+#define RME96_WCR_THRU_7    (1 << 26)
+#define RME96_WCR_DOLBY     (1 << 27)
+#define RME96_WCR_MONITOR_0 (1 << 28)
+#define RME96_WCR_MONITOR_1 (1 << 29)
+#define RME96_WCR_ISEL      (1 << 30)
+#define RME96_WCR_IDIS      (1 << 31)
+
+#define RME96_WCR_BITPOS_GAIN_0 2
+#define RME96_WCR_BITPOS_GAIN_1 3
+#define RME96_WCR_BITPOS_FREQ_0 9
+#define RME96_WCR_BITPOS_FREQ_1 10
+#define RME96_WCR_BITPOS_INP_0 17
+#define RME96_WCR_BITPOS_INP_1 18
+#define RME96_WCR_BITPOS_MONITOR_0 28
+#define RME96_WCR_BITPOS_MONITOR_1 29
+
+/* Read control register bits */
+#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF
+#define RME96_RCR_IRQ_2     (1 << 16)
+#define RME96_RCR_T_OUT     (1 << 17)
+#define RME96_RCR_DEV_ID_0  (1 << 21)
+#define RME96_RCR_DEV_ID_1  (1 << 22)
+#define RME96_RCR_LOCK      (1 << 23)
+#define RME96_RCR_VERF      (1 << 26)
+#define RME96_RCR_F0        (1 << 27)
+#define RME96_RCR_F1        (1 << 28)
+#define RME96_RCR_F2        (1 << 29)
+#define RME96_RCR_AUTOSYNC  (1 << 30)
+#define RME96_RCR_IRQ       (1 << 31)
+
+#define RME96_RCR_BITPOS_F0 27
+#define RME96_RCR_BITPOS_F1 28
+#define RME96_RCR_BITPOS_F2 29
+
+/* Additonal register bits */
+#define RME96_AR_WSEL       (1 << 0)
+#define RME96_AR_ANALOG     (1 << 1)
+#define RME96_AR_FREQPAD_0  (1 << 2)
+#define RME96_AR_FREQPAD_1  (1 << 3)
+#define RME96_AR_FREQPAD_2  (1 << 4)
+#define RME96_AR_PD2        (1 << 5)
+#define RME96_AR_DAC_EN     (1 << 6)
+#define RME96_AR_CLATCH     (1 << 7)
+#define RME96_AR_CCLK       (1 << 8)
+#define RME96_AR_CDATA      (1 << 9)
+
+#define RME96_AR_BITPOS_F0 2
+#define RME96_AR_BITPOS_F1 3
+#define RME96_AR_BITPOS_F2 4
+
+/* Monitor tracks */
+#define RME96_MONITOR_TRACKS_1_2 0
+#define RME96_MONITOR_TRACKS_3_4 1
+#define RME96_MONITOR_TRACKS_5_6 2
+#define RME96_MONITOR_TRACKS_7_8 3
+
+/* Attenuation */
+#define RME96_ATTENUATION_0 0
+#define RME96_ATTENUATION_6 1
+#define RME96_ATTENUATION_12 2
+#define RME96_ATTENUATION_18 3
+
+/* Input types */
+#define RME96_INPUT_OPTICAL 0
+#define RME96_INPUT_COAXIAL 1
+#define RME96_INPUT_INTERNAL 2
+#define RME96_INPUT_XLR 3
+#define RME96_INPUT_ANALOG 4
+
+/* Clock modes */
+#define RME96_CLOCKMODE_SLAVE 0
+#define RME96_CLOCKMODE_MASTER 1
+#define RME96_CLOCKMODE_WORDCLOCK 2
+
+/* Block sizes in bytes */
+#define RME96_SMALL_BLOCK_SIZE 2048
+#define RME96_LARGE_BLOCK_SIZE 8192
+
+/* Volume control */
+#define RME96_AD1852_VOL_BITS 14
+#define RME96_AD1855_VOL_BITS 10
+
+/*
+ * PCI vendor/device ids, could in the future be defined in <linux/pci.h>,
+ * therefore #ifndef is used.
+ */
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96
+#define PCI_DEVICE_ID_DIGI96 0x3fc0
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8
+#define PCI_DEVICE_ID_DIGI96_8 0x3fc1
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8_PRO
+#define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST
+#define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3
+#endif
+
+typedef struct snd_rme96 {
+	spinlock_t    lock;
+	int irq;
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long iobase;
+	
+	u32 wcreg;    /* cached write control register value */
+	u32 wcreg_spdif;		/* S/PDIF setup */
+	u32 wcreg_spdif_stream;		/* S/PDIF setup (temporary) */
+	u32 rcreg;    /* cached read control register value */
+	u32 areg;     /* cached additional register value */
+	u16 vol[2]; /* cached volume of analog output */
+
+	u8 rev; /* card revision number */
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+
+	int playback_frlog; /* log2 of framesize */
+	int capture_frlog;
+	
+        size_t playback_periodsize; /* in bytes, zero if not used */
+	size_t capture_periodsize; /* in bytes, zero if not used */
+
+        snd_pcm_uframes_t playback_last_appl_ptr;
+	size_t playback_ptr;
+	size_t capture_ptr;
+
+	snd_card_t         *card;
+	snd_pcm_t          *spdif_pcm;
+	snd_pcm_t          *adat_pcm; 
+	struct pci_dev     *pci;
+	snd_info_entry_t   *proc_entry;
+	snd_kcontrol_t	   *spdif_ctl;
+} rme96_t;
+
+static struct pci_device_id snd_rme96_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme96_ids);
+
+#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START)
+#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2)
+#define	RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST)
+#define	RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \
+				     (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST)
+#define	RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4)
+#define	RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \
+			          ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2))
+#define	RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1)
+
+static int
+snd_rme96_playback_prepare(snd_pcm_substream_t *substream);
+
+static int
+snd_rme96_capture_prepare(snd_pcm_substream_t *substream);
+
+static int
+snd_rme96_playback_trigger(snd_pcm_substream_t *substream, 
+			   int cmd);
+
+static int
+snd_rme96_capture_trigger(snd_pcm_substream_t *substream, 
+			  int cmd);
+
+static snd_pcm_uframes_t
+snd_rme96_playback_pointer(snd_pcm_substream_t *substream);
+
+static snd_pcm_uframes_t
+snd_rme96_capture_pointer(snd_pcm_substream_t *substream);
+
+static void __init 
+snd_rme96_proc_init(rme96_t *rme96);
+
+static void
+snd_rme96_proc_done(rme96_t *rme96);
+
+static int
+snd_rme96_create_switches(snd_card_t *card,
+			  rme96_t *rme96);
+
+static inline unsigned int
+snd_rme96_playback_ptr(rme96_t *rme96)
+{
+	return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
+		& RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog;
+}
+
+static inline unsigned int
+snd_rme96_capture_ptr(rme96_t *rme96)
+{
+	return (readl(rme96->iobase + RME96_IO_GET_REC_POS)
+		& RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog;
+}
+
+static int
+snd_rme96_playback_silence(snd_pcm_substream_t *substream,
+			   int channel, /* not used (interleaved data) */
+			   snd_pcm_uframes_t pos,
+			   snd_pcm_uframes_t count)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	count <<= rme96->playback_frlog;
+	pos <<= rme96->playback_frlog;
+	memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
+		  0, count);
+	return 0;
+}
+
+static int
+snd_rme96_playback_copy(snd_pcm_substream_t *substream,
+			int channel, /* not used (interleaved data) */
+			snd_pcm_uframes_t pos,
+			void *src,
+			snd_pcm_uframes_t count)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	count <<= rme96->playback_frlog;
+	pos <<= rme96->playback_frlog;
+	copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
+			    count);
+	return 0;
+}
+
+static int
+snd_rme96_capture_copy(snd_pcm_substream_t *substream,
+		       int channel, /* not used (interleaved data) */
+		       snd_pcm_uframes_t pos,
+		       void *dst,
+		       snd_pcm_uframes_t count)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	count <<= rme96->capture_frlog;
+	pos <<= rme96->capture_frlog;
+	copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
+			    count);
+        return 0;
+}
+
+/*
+ * Digital output capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme96_playback_spdif_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+			      SNDRV_PCM_FMTBIT_S32_LE),
+	rates:		     (SNDRV_PCM_RATE_32000 |
+			      SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000 | 
+			      SNDRV_PCM_RATE_64000 |
+			      SNDRV_PCM_RATE_88200 | 
+			      SNDRV_PCM_RATE_96000),
+	rate_min:	     32000,
+	rate_max:	     96000,
+	channels_min:	     2,
+	channels_max:	     2,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:	     0,
+};
+
+/*
+ * Digital input capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_spdif_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+			      SNDRV_PCM_FMTBIT_S32_LE),
+	rates:		     (SNDRV_PCM_RATE_32000 |
+			      SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000 | 
+			      SNDRV_PCM_RATE_64000 |
+			      SNDRV_PCM_RATE_88200 | 
+			      SNDRV_PCM_RATE_96000),
+	rate_min:	     32000,
+	rate_max:	     96000,
+	channels_min:	     2,
+	channels_max:	     2,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:	     0,
+};
+
+/*
+ * Digital output capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_playback_adat_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:             SNDRV_PCM_FMTBIT_S16_LE,
+	rates:	             (SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000),
+	rate_min:            44100,
+	rate_max:            48000,
+	channels_min:        8,
+	channels_max:	     8,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:	     0,
+};
+
+/*
+ * Digital input capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_adat_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:             SNDRV_PCM_FMTBIT_S16_LE,
+	rates:	             (SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000),
+	rate_min:            44100,
+	rate_max:            48000,
+	channels_min:        8,
+	channels_max:	     8,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:           0,
+};
+
+/*
+ * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface
+ * of the AD1852 or AD1852 D/A converter on the board.  CDATA must be set up
+ * on the falling edge of CCLK and be stable on the rising edge.  The rising
+ * edge of CLATCH after the last data bit clocks in the whole data word.
+ * A fast processor could probably drive the SPI interface faster than the
+ * DAC can handle (3MHz for the 1855, unknown for the 1852).  The udelay(1)
+ * limits the data rate to 500KHz and only causes a delay of 33 microsecs.
+ *
+ * NOTE: increased delay from 1 to 10, since there where problems setting
+ * the volume.
+ */
+static void
+snd_rme96_write_SPI(rme96_t *rme96, u16 val)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		if (val & 0x8000) {
+			rme96->areg |= RME96_AR_CDATA;
+		} else {
+			rme96->areg &= ~RME96_AR_CDATA;
+		}
+		rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH);
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		udelay(10);
+		rme96->areg |= RME96_AR_CCLK;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		udelay(10);
+		val <<= 1;
+	}
+	rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA);
+	rme96->areg |= RME96_AR_CLATCH;
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	udelay(10);
+	rme96->areg &= ~RME96_AR_CLATCH;
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+}
+
+static void
+snd_rme96_apply_dac_volume(rme96_t *rme96)
+{
+	if (RME96_DAC_IS_1852(rme96)) {
+		snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0);
+		snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2);
+	} else if (RME96_DAC_IS_1855(rme96)) {
+		snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000);
+		snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400);
+	}
+}
+
+static void
+snd_rme96_reset_dac(rme96_t *rme96)
+{
+	writel(rme96->wcreg | RME96_WCR_PD,
+	       rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static int
+snd_rme96_getmontracks(rme96_t *rme96)
+{
+	return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1);
+}
+
+static int
+snd_rme96_setmontracks(rme96_t *rme96,
+		       int montracks)
+{
+	if (montracks & 1) {
+		rme96->wcreg |= RME96_WCR_MONITOR_0;
+	} else {
+		rme96->wcreg &= ~RME96_WCR_MONITOR_0;
+	}
+	if (montracks & 2) {
+		rme96->wcreg |= RME96_WCR_MONITOR_1;
+	} else {
+		rme96->wcreg &= ~RME96_WCR_MONITOR_1;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_getattenuation(rme96_t *rme96)
+{
+	return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1);
+}
+
+static int
+snd_rme96_setattenuation(rme96_t *rme96,
+			 int attenuation)
+{
+	switch (attenuation) {
+	case 0:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) &
+			~RME96_WCR_GAIN_1;
+		break;
+	case 1:
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) &
+			~RME96_WCR_GAIN_1;
+		break;
+	case 2:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) |
+			RME96_WCR_GAIN_1;
+		break;
+	case 3:
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) |
+			RME96_WCR_GAIN_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_capture_getrate(rme96_t *rme96,
+			  int *is_adat)
+{	
+	int n, rate;
+
+	*is_adat = 0;
+	if (rme96->areg & RME96_AR_ANALOG) {
+		/* Analog input, overrides S/PDIF setting */
+		n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) +
+			(((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1);
+		switch (n) {
+		case 1:
+			rate = 32000;
+			break;
+		case 2:
+			rate = 44100;
+			break;
+		case 3:
+			rate = 48000;
+			break;
+		default:
+			return -1;
+		}
+		return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate;
+	}
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (rme96->rcreg & RME96_RCR_LOCK) {
+		/* ADAT rate */
+		*is_adat = 1;
+		if (rme96->rcreg & RME96_RCR_T_OUT) {
+			return 48000;
+		}
+		return 44100;
+	}
+
+	if (rme96->rcreg & RME96_RCR_VERF) {
+		return -1;
+	}
+	
+	/* S/PDIF rate */
+	n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) +
+		(((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) +
+		(((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2);
+	
+	switch (n) {
+	case 0:		
+		if (rme96->rcreg & RME96_RCR_T_OUT) {
+			return 64000;
+		}
+		return -1;
+	case 3: return 96000;
+	case 4: return 88200;
+	case 5: return 48000;
+	case 6: return 44100;
+	case 7: return 32000;
+	default:
+		break;
+	}
+	return -1;
+}
+
+static int
+snd_rme96_playback_getrate(rme96_t *rme96)
+{
+	int rate, dummy;
+
+	if (!(rme96->wcreg & RME96_WCR_MASTER) &&
+	    (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
+	{
+	    /* slave clock */
+	    return rate;
+	}
+	rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1);
+	switch (rate) {
+	case 1:
+		rate = 32000;
+		break;
+	case 2:
+		rate = 44100;
+		break;
+	case 3:
+		rate = 48000;
+		break;
+	default:
+		return -1;
+	}
+	return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate;
+}
+
+static int
+snd_rme96_playback_setrate(rme96_t *rme96,
+			   int rate)
+{
+	int ds;
+
+	ds = rme96->wcreg & RME96_WCR_DS;
+	switch (rate) {
+	case 32000:
+		rme96->wcreg &= ~RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
+			~RME96_WCR_FREQ_1;
+		break;
+	case 44100:
+		rme96->wcreg &= ~RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
+			~RME96_WCR_FREQ_0;
+		break;
+	case 48000:
+		rme96->wcreg &= ~RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
+			RME96_WCR_FREQ_1;
+		break;
+	case 64000:
+		rme96->wcreg |= RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
+			~RME96_WCR_FREQ_1;
+		break;
+	case 88200:
+		rme96->wcreg |= RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
+			~RME96_WCR_FREQ_0;
+		break;
+	case 96000:
+		rme96->wcreg |= RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
+			RME96_WCR_FREQ_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if ((!ds && rme96->wcreg & RME96_WCR_DS) ||
+	    (ds && !(rme96->wcreg & RME96_WCR_DS)))
+	{
+		/* change to/from double-speed: reset the DAC (if available) */
+		snd_rme96_reset_dac(rme96);
+	} else {
+		writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	}
+	return 0;
+}
+
+static int
+snd_rme96_capture_analog_setrate(rme96_t *rme96,
+				 int rate)
+{
+	switch (rate) {
+	case 32000:
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
+			       ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+		break;
+	case 44100:
+		rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+		break;
+	case 48000:
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+		break;
+	case 64000:
+		if (rme96->rev < 4) {
+			return -EINVAL;
+		}
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
+			       ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+		break;
+	case 88200:
+		if (rme96->rev < 4) {
+			return -EINVAL;
+		}
+		rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+		break;
+	case 96000:
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	return 0;
+}
+
+static int
+snd_rme96_setclockmode(rme96_t *rme96,
+		       int mode)
+{
+	switch (mode) {
+	case RME96_CLOCKMODE_SLAVE:
+		rme96->wcreg &= ~RME96_WCR_MASTER;
+		rme96->areg &= ~RME96_AR_WSEL;
+		break;
+	case RME96_CLOCKMODE_MASTER:
+		rme96->wcreg |= RME96_WCR_MASTER;
+		rme96->areg &= ~RME96_AR_WSEL;
+		break;
+	case RME96_CLOCKMODE_WORDCLOCK:
+		/* Word clock is a master mode */
+		rme96->wcreg |= RME96_WCR_MASTER; 
+		rme96->areg |= RME96_AR_WSEL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	return 0;
+}
+
+static int
+snd_rme96_getclockmode(rme96_t *rme96)
+{
+	if (rme96->areg & RME96_AR_WSEL) {
+		return RME96_CLOCKMODE_WORDCLOCK;
+	}
+	return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER :
+		RME96_CLOCKMODE_SLAVE;
+}
+
+static int
+snd_rme96_setinputtype(rme96_t *rme96,
+		       int type)
+{
+	int n;
+
+	switch (type) {
+	case RME96_INPUT_OPTICAL:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) &
+			~RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_COAXIAL:
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) &
+			~RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_INTERNAL:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) |
+			RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_XLR:
+		if ((rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST &&
+		     rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO) ||
+		    (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST &&
+		     rme96->rev > 4))
+		{
+			/* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */
+			return -EINVAL;
+		}
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) |
+			RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_ANALOG:
+		if (!RME96_HAS_ANALOG_IN(rme96)) {
+			return -EINVAL;
+		}
+		rme96->areg |= RME96_AR_ANALOG;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		if (rme96->rev < 4) {
+			/*
+			 * Revision less than 004 does not support 64 and
+			 * 88.2 kHz
+			 */
+			if (snd_rme96_capture_getrate(rme96, &n) == 88200) {
+				snd_rme96_capture_analog_setrate(rme96, 44100);
+			}
+			if (snd_rme96_capture_getrate(rme96, &n) == 64000) {
+				snd_rme96_capture_analog_setrate(rme96, 32000);
+			}
+		}
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) {
+		rme96->areg &= ~RME96_AR_ANALOG;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_getinputtype(rme96_t *rme96)
+{
+	if (rme96->areg & RME96_AR_ANALOG) {
+		return RME96_INPUT_ANALOG;
+	}
+	return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1);
+}
+
+static void
+snd_rme96_setframelog(rme96_t *rme96,
+		      int n_channels,
+		      int is_playback)
+{
+	int frlog;
+	
+	if (n_channels == 2) {
+		frlog = 1;
+	} else {
+		/* assume 8 channels */
+		frlog = 3;
+	}
+	if (is_playback) {
+		frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1;
+		rme96->playback_frlog = frlog;
+	} else {
+		frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1;
+		rme96->capture_frlog = frlog;
+	}
+}
+
+static int
+snd_rme96_playback_setformat(rme96_t *rme96,
+			     int format)
+{
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		rme96->wcreg &= ~RME96_WCR_MODE24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		rme96->wcreg |= RME96_WCR_MODE24;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_capture_setformat(rme96_t *rme96,
+			    int format)
+{
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		rme96->wcreg &= ~RME96_WCR_MODE24_2;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		rme96->wcreg |= RME96_WCR_MODE24_2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static void
+snd_rme96_set_period_properties(rme96_t *rme96,
+				size_t period_bytes)
+{
+	switch (period_bytes) {
+	case RME96_LARGE_BLOCK_SIZE:
+		rme96->wcreg &= ~RME96_WCR_ISEL;
+		break;
+	case RME96_SMALL_BLOCK_SIZE:
+		rme96->wcreg |= RME96_WCR_ISEL;
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	rme96->wcreg &= ~RME96_WCR_IDIS;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static int
+snd_rme96_playback_hw_params(snd_pcm_substream_t *substream,
+			     snd_pcm_hw_params_t *params)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0)
+		return err;
+	spin_lock_irqsave(&rme96->lock, flags);
+	if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return err;
+	}
+	if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return err;
+	}
+	snd_rme96_setframelog(rme96, params_channels(params), 1);
+	if (rme96->capture_periodsize != 0) {
+		if (params_period_size(params) << rme96->playback_frlog !=
+		    rme96->capture_periodsize)
+		{
+			spin_unlock_irqrestore(&rme96->lock, flags);
+			return -EBUSY;
+		}
+	}
+	rme96->playback_periodsize =
+		params_period_size(params) << rme96->playback_frlog;
+	snd_rme96_set_period_properties(rme96, rme96->playback_periodsize);
+	/* S/PDIF setup */
+	if ((rme96->wcreg & RME96_WCR_ADAT) == 0) {
+		rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+		writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	}
+	spin_unlock_irqrestore(&rme96->lock, flags);
+		
+	return 0;
+}
+
+static int
+snd_rme96_playback_hw_free(snd_pcm_substream_t *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int
+snd_rme96_capture_hw_params(snd_pcm_substream_t *substream,
+			    snd_pcm_hw_params_t *params)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	int err, isadat;
+	
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0)
+		return err;
+	spin_lock_irqsave(&rme96->lock, flags);
+	if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return err;
+	}
+	if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
+		if ((err = snd_rme96_capture_analog_setrate(rme96,
+							    params_rate(params))) < 0)
+		{
+			spin_unlock_irqrestore(&rme96->lock, flags);
+			return err;
+		}
+	} else if (params_rate(params) != snd_rme96_capture_getrate(rme96, &isadat)) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return -EBUSY;
+	}
+	snd_rme96_setframelog(rme96, params_channels(params), 0);
+	if (rme96->playback_periodsize != 0) {
+		if (params_period_size(params) << rme96->capture_frlog !=
+		    rme96->playback_periodsize)
+		{
+			spin_unlock_irqrestore(&rme96->lock, flags);
+			return -EBUSY;
+		}
+	}
+	rme96->capture_periodsize =
+		params_period_size(params) << rme96->capture_frlog;
+	snd_rme96_set_period_properties(rme96, rme96->capture_periodsize);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+	return 0;
+}
+
+static int
+snd_rme96_capture_hw_free(snd_pcm_substream_t *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static void
+snd_rme96_playback_start(rme96_t *rme96,
+			 int from_pause)
+{
+	if (!from_pause) {
+		writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+		rme96->playback_last_appl_ptr = 0;
+		rme96->playback_ptr = 0;
+	}
+
+	rme96->wcreg |= RME96_WCR_START;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_capture_start(rme96_t *rme96,
+			int from_pause)
+{
+	if (!from_pause) {
+		writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+		rme96->capture_ptr = 0;
+	}
+
+	rme96->wcreg |= RME96_WCR_START_2;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_playback_stop(rme96_t *rme96)
+{
+	/*
+	 * Check if there is an unconfirmed IRQ, if so confirm it, or else
+	 * the hardware will not stop generating interrupts
+	 */
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (rme96->rcreg & RME96_RCR_IRQ) {
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+	}	
+	rme96->wcreg &= ~RME96_WCR_START;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_capture_stop(rme96_t *rme96)
+{
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (rme96->rcreg & RME96_RCR_IRQ_2) {
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+	}	
+	rme96->wcreg &= ~RME96_WCR_START_2;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_interrupt(int irq,
+		    void *dev_id,
+		    struct pt_regs *regs)
+{
+	rme96_t *rme96 = (rme96_t *)dev_id;
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	/* fastpath out, to ease interrupt sharing */
+	if (!((rme96->rcreg & RME96_RCR_IRQ) ||
+	      (rme96->rcreg & RME96_RCR_IRQ_2)))
+	{
+		return;
+	}
+	
+	if (rme96->rcreg & RME96_RCR_IRQ) {
+		/* playback */
+		snd_pcm_period_elapsed(rme96->playback_substream);
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+	}
+	if (rme96->rcreg & RME96_RCR_IRQ_2) {
+		/* capture */
+		snd_pcm_period_elapsed(rme96->capture_substream);		
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+	}
+}
+
+static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE };
+
+#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+	count: PERIOD_BYTES,
+	list: period_bytes,
+	mask: 0
+};
+
+static int
+snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->wcreg &= ~RME96_WCR_ADAT;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	rme96->playback_substream = substream;
+	rme96->playback_last_appl_ptr = 0;
+	rme96->playback_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+	runtime->hw = snd_rme96_playback_spdif_info;	
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+
+	rme96->wcreg_spdif_stream = rme96->wcreg_spdif;
+	rme96->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
+	return 0;
+}
+
+static int
+snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	int isadat;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (snd_rme96_capture_getrate(rme96, &isadat) < 0) {
+		/* no input */
+		return -EIO;
+	}
+	if (isadat) {
+		/* ADAT input */
+		return -EBUSY;
+	}
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->capture_substream = substream;
+	rme96->capture_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	
+	runtime->hw = snd_rme96_capture_spdif_info;
+	
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+
+	return 0;
+}
+
+static int
+snd_rme96_playback_adat_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->wcreg |= RME96_WCR_ADAT;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	rme96->playback_substream = substream;
+	rme96->playback_last_appl_ptr = 0;
+	rme96->playback_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	
+	runtime->hw = snd_rme96_playback_adat_info;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+	return 0;
+}
+
+static int
+snd_rme96_capture_adat_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	int isadat;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (snd_rme96_capture_getrate(rme96, &isadat) < 0) {
+		/* no input */
+		return -EIO;
+	}
+	if (!isadat) {
+		/* S/PDIF input */
+		return -EBUSY;
+	}
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->capture_substream = substream;
+	rme96->capture_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+	runtime->hw = snd_rme96_capture_adat_info;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+	return 0;
+}
+
+static int
+snd_rme96_playback_close(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	int spdif = 0;
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->playback_substream = NULL;
+	rme96->playback_periodsize = 0;
+	spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	if (spdif) {
+		rme96->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
+	}
+	return 0;
+}
+
+static int
+snd_rme96_capture_close(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->capture_substream = NULL;
+	rme96->capture_periodsize = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme96_playback_prepare(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);	
+	if (RME96_ISPLAYING(rme96)) {
+		snd_rme96_playback_stop(rme96);
+	}
+	writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme96_capture_prepare(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);	
+	if (RME96_ISRECORDING(rme96)) {
+		snd_rme96_capture_stop(rme96);
+	}
+	writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme96_playback_trigger(snd_pcm_substream_t *substream, 
+			   int cmd)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (!RME96_ISPLAYING(rme96)) {
+			if (substream != rme96->playback_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_playback_start(rme96, 0);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (RME96_ISPLAYING(rme96)) {
+			if (substream != rme96->playback_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_playback_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (RME96_ISPLAYING(rme96)) {
+			snd_rme96_playback_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!RME96_ISPLAYING(rme96)) {
+			snd_rme96_playback_start(rme96, 1);
+		}
+		break;
+		
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+snd_rme96_capture_trigger(snd_pcm_substream_t *substream, 
+			  int cmd)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (!RME96_ISRECORDING(rme96)) {
+			if (substream != rme96->capture_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_capture_start(rme96, 0);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (RME96_ISRECORDING(rme96)) {
+			if (substream != rme96->capture_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_capture_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (RME96_ISRECORDING(rme96)) {
+			snd_rme96_capture_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!RME96_ISRECORDING(rme96)) {
+			snd_rme96_capture_start(rme96, 1);
+		}
+		break;
+		
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_rme96_playback_pointer(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t diff;
+	size_t bytes;
+	
+	if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+		diff = runtime->control->appl_ptr -
+		    rme96->playback_last_appl_ptr;
+ 	        rme96->playback_last_appl_ptr = runtime->control->appl_ptr;
+	        if (diff != 0 &&
+		    diff < -(snd_pcm_sframes_t)(runtime->boundary >> 1))
+		{
+		        diff += runtime->boundary;
+		}
+		bytes = diff << rme96->playback_frlog;
+		
+		if (bytes > RME96_BUFFER_SIZE - rme96->playback_ptr) {
+			memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER +
+				    rme96->playback_ptr,
+				    runtime->dma_area + rme96->playback_ptr,
+				    RME96_BUFFER_SIZE - rme96->playback_ptr);
+		        bytes -= RME96_BUFFER_SIZE - rme96->playback_ptr;
+			if (bytes > RME96_BUFFER_SIZE) {
+			        bytes = RME96_BUFFER_SIZE;
+			}
+			memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER,
+				    runtime->dma_area,
+				    bytes);
+			rme96->playback_ptr = bytes;
+		} else if (bytes != 0) {
+			memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER +
+				    rme96->playback_ptr,
+				    runtime->dma_area + rme96->playback_ptr,
+				    bytes);
+			rme96->playback_ptr += bytes;
+		}
+	}
+	return snd_rme96_playback_ptr(rme96);
+}
+
+static snd_pcm_uframes_t
+snd_rme96_capture_pointer(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t frameptr;
+	size_t ptr;
+
+	frameptr = snd_rme96_capture_ptr(rme96);
+	if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+		ptr = frameptr << rme96->capture_frlog;
+		if (ptr > rme96->capture_ptr) {
+			memcpy_fromio(runtime->dma_area + rme96->capture_ptr,
+				      rme96->iobase + RME96_IO_REC_BUFFER +
+				      rme96->capture_ptr,
+				      ptr - rme96->capture_ptr);
+			rme96->capture_ptr += ptr - rme96->capture_ptr;
+		} else if (ptr < rme96->capture_ptr) {
+			memcpy_fromio(runtime->dma_area + rme96->capture_ptr,
+				      rme96->iobase + RME96_IO_REC_BUFFER +
+				      rme96->capture_ptr,
+				      RME96_BUFFER_SIZE - rme96->capture_ptr);
+			memcpy_fromio(runtime->dma_area,
+				      rme96->iobase + RME96_IO_REC_BUFFER,
+				      ptr);
+			rme96->capture_ptr = ptr;
+		}
+	}
+	return frameptr;
+}
+
+static snd_pcm_ops_t snd_rme96_playback_spdif_ops = {
+	open:		snd_rme96_playback_spdif_open,
+	close:		snd_rme96_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_playback_hw_params,
+	hw_free:	snd_rme96_playback_hw_free,
+	prepare:	snd_rme96_playback_prepare,
+	trigger:	snd_rme96_playback_trigger,
+	pointer:	snd_rme96_playback_pointer,
+	copy:		snd_rme96_playback_copy,
+	silence:	snd_rme96_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_spdif_ops = {
+	open:		snd_rme96_capture_spdif_open,
+	close:		snd_rme96_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_capture_hw_params,
+	hw_free:	snd_rme96_capture_hw_free,
+	prepare:	snd_rme96_capture_prepare,
+	trigger:	snd_rme96_capture_trigger,
+	pointer:	snd_rme96_capture_pointer,
+	copy:		snd_rme96_capture_copy,
+};
+
+static snd_pcm_ops_t snd_rme96_playback_adat_ops = {
+	open:		snd_rme96_playback_adat_open,
+	close:		snd_rme96_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_playback_hw_params,
+	hw_free:	snd_rme96_playback_hw_free,
+	prepare:	snd_rme96_playback_prepare,
+	trigger:	snd_rme96_playback_trigger,
+	pointer:	snd_rme96_playback_pointer,
+	copy:		snd_rme96_playback_copy,
+	silence:	snd_rme96_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_adat_ops = {
+	open:		snd_rme96_capture_adat_open,
+	close:		snd_rme96_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_capture_hw_params,
+	hw_free:	snd_rme96_capture_hw_free,
+	prepare:	snd_rme96_capture_prepare,
+	trigger:	snd_rme96_capture_trigger,
+	pointer:	snd_rme96_capture_pointer,
+	copy:		snd_rme96_capture_copy,
+};
+
+static void
+snd_rme96_free(void *private_data)
+{
+	rme96_t *rme96 = (rme96_t *)private_data;
+
+	if (rme96 == NULL) {
+	        return;
+	}
+	if (rme96->irq >= 0) {
+		snd_rme96_playback_stop(rme96);
+		snd_rme96_capture_stop(rme96);
+		rme96->areg &= ~RME96_AR_DAC_EN;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		snd_rme96_proc_done(rme96);
+		free_irq(rme96->irq, (void *)rme96);
+		rme96->irq = -1;
+	}
+	if (rme96->iobase) {
+		iounmap((void *)rme96->iobase);
+		rme96->iobase = 0;
+	}
+	if (rme96->res_port != NULL) {
+		release_resource(rme96->res_port);
+		kfree_nocheck(rme96->res_port);
+		rme96->res_port = NULL;
+	}
+}
+
+static void
+snd_rme96_free_spdif_pcm(snd_pcm_t *pcm)
+{
+	rme96_t *rme96 = (rme96_t *) pcm->private_data;
+	rme96->spdif_pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void
+snd_rme96_free_adat_pcm(snd_pcm_t *pcm)
+{
+	rme96_t *rme96 = (rme96_t *) pcm->private_data;
+	rme96->adat_pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init
+snd_rme96_create(rme96_t *rme96)
+{
+	struct pci_dev *pci = rme96->pci;
+	int err;
+
+	rme96->irq = -1;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	rme96->port = pci_resource_start(rme96->pci, 0);
+
+	if ((rme96->res_port = request_mem_region(rme96->port, RME96_IO_SIZE, "RME96")) == NULL) {
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	rme96->irq = pci->irq;
+
+	spin_lock_init(&rme96->lock);
+	if ((rme96->iobase = (unsigned long) ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) {
+		snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
+		return -ENOMEM;
+	}
+
+	/* read the card's revision number */
+	pci_read_config_byte(pci, 8, &rme96->rev);	
+	
+	/* set up ALSA pcm device for S/PDIF */
+	if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0,
+			       1, 1, &rme96->spdif_pcm)) < 0)
+	{
+		return err;
+	}
+	rme96->spdif_pcm->private_data = rme96;
+	rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
+	strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
+	snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
+	snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
+
+	rme96->spdif_pcm->info_flags = 0;
+
+	snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL);
+
+	/* set up ALSA pcm device for ADAT */
+	if (pci->device == PCI_DEVICE_ID_DIGI96) {
+		/* ADAT is not available on the base model */
+		rme96->adat_pcm = NULL;
+	} else {
+		if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1,
+				       1, 1, &rme96->adat_pcm)) < 0)
+		{
+			return err;
+		}		
+		rme96->adat_pcm->private_data = rme96;
+		rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
+		strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
+		snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
+		snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
+		
+		rme96->adat_pcm->info_flags = 0;
+
+		snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL);
+	}
+
+	rme96->playback_periodsize = 0;
+	rme96->capture_periodsize = 0;
+	
+	/* make sure playback/capture is stopped, if by some reason active */
+	snd_rme96_playback_stop(rme96);
+	snd_rme96_capture_stop(rme96);
+	
+	/* set default values in registers */
+	rme96->wcreg =
+		RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */
+		RME96_WCR_SEL |    /* normal playback */
+		RME96_WCR_MASTER | /* set to master clock mode */
+		RME96_WCR_INP_0;   /* set coaxial input */
+
+	rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */
+
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	
+	/* reset the ADC */
+	writel(rme96->areg | RME96_AR_PD2,
+	       rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);	
+
+	/* reset and enable the DAC (order is important). */
+	snd_rme96_reset_dac(rme96);
+	rme96->areg |= RME96_AR_DAC_EN;
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+	/* reset playback and record buffer pointers */
+	writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+	writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+
+	/* reset volume */
+	rme96->vol[0] = rme96->vol[1] = 0;
+	if (RME96_HAS_ANALOG_OUT(rme96)) {
+		snd_rme96_apply_dac_volume(rme96);
+	}
+	
+	/* init switch interface */
+	if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) {
+		return err;
+	}
+
+        /* init proc interface */
+	snd_rme96_proc_init(rme96);
+	
+	return 0;
+}
+
+/*
+ * proc interface
+ */
+
+static void 
+snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	int n;
+	rme96_t *rme96 = (rme96_t *)entry->private_data;
+	
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+
+	snd_iprintf(buffer, rme96->card->longname);
+	snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1);
+
+	snd_iprintf(buffer, "\nGeneral settings\n");
+	if (rme96->wcreg & RME96_WCR_IDIS) {
+		snd_iprintf(buffer, "  period size: N/A (interrupts "
+			    "disabled)\n");
+	} else if (rme96->wcreg & RME96_WCR_ISEL) {
+		snd_iprintf(buffer, "  period size: 2048 bytes\n");
+	} else {
+		snd_iprintf(buffer, "  period size: 8192 bytes\n");
+	}	
+	snd_iprintf(buffer, "\nInput settings\n");
+	switch (snd_rme96_getinputtype(rme96)) {
+	case RME96_INPUT_OPTICAL:
+		snd_iprintf(buffer, "  input: optical");
+		break;
+	case RME96_INPUT_COAXIAL:
+		snd_iprintf(buffer, "  input: coaxial");
+		break;
+	case RME96_INPUT_INTERNAL:
+		snd_iprintf(buffer, "  input: internal");
+		break;
+	case RME96_INPUT_XLR:
+		snd_iprintf(buffer, "  input: XLR");
+		break;
+	case RME96_INPUT_ANALOG:
+		snd_iprintf(buffer, "  input: analog");
+		break;
+	}
+	if (snd_rme96_capture_getrate(rme96, &n) < 0) {
+		snd_iprintf(buffer, "\n  sample rate: no valid signal\n");
+	} else {
+		if (n) {
+			snd_iprintf(buffer, " (8 channels)\n");
+		} else {
+			snd_iprintf(buffer, " (2 channels)\n");
+		}
+		snd_iprintf(buffer, "  sample rate: %d Hz\n",
+			    snd_rme96_capture_getrate(rme96, &n));
+	}
+	if (rme96->wcreg & RME96_WCR_MODE24_2) {
+		snd_iprintf(buffer, "  sample format: 24 bit\n");
+	} else {
+		snd_iprintf(buffer, "  sample format: 16 bit\n");
+	}
+	
+	snd_iprintf(buffer, "\nOutput settings\n");
+	if (rme96->wcreg & RME96_WCR_SEL) {
+		snd_iprintf(buffer, "  output signal: normal playback\n");
+	} else {
+		snd_iprintf(buffer, "  output signal: same as input\n");
+	}
+	snd_iprintf(buffer, "  sample rate: %d Hz\n",
+		    snd_rme96_playback_getrate(rme96));
+	if (rme96->wcreg & RME96_WCR_MODE24) {
+		snd_iprintf(buffer, "  sample format: 24 bit\n");
+	} else {
+		snd_iprintf(buffer, "  sample format: 16 bit\n");
+	}
+	if (rme96->areg & RME96_AR_WSEL) {
+		snd_iprintf(buffer, "  clock mode: word clock\n");
+	} else if (rme96->wcreg & RME96_WCR_MASTER) {
+		snd_iprintf(buffer, "  clock mode: master\n");
+	} else {
+		snd_iprintf(buffer, "  clock mode: slave\n");
+	}
+	if (rme96->wcreg & RME96_WCR_PRO) {
+		snd_iprintf(buffer, "  format: AES/EBU (professional)\n");
+	} else {
+		snd_iprintf(buffer, "  format: IEC958 (consumer)\n");
+	}
+	if (rme96->wcreg & RME96_WCR_EMP) {
+		snd_iprintf(buffer, "  emphasis: on\n");
+	} else {
+		snd_iprintf(buffer, "  emphasis: off\n");
+	}
+	if (rme96->wcreg & RME96_WCR_DOLBY) {
+		snd_iprintf(buffer, "  non-audio (dolby): on\n");
+	} else {
+		snd_iprintf(buffer, "  non-audio (dolby): off\n");
+	}
+	if (RME96_HAS_ANALOG_IN(rme96)) {
+		snd_iprintf(buffer, "\nAnalog output settings\n");
+		switch (snd_rme96_getmontracks(rme96)) {
+		case RME96_MONITOR_TRACKS_1_2:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 1+2\n");
+			break;
+		case RME96_MONITOR_TRACKS_3_4:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 3+4\n");
+			break;
+		case RME96_MONITOR_TRACKS_5_6:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 5+6\n");
+			break;
+		case RME96_MONITOR_TRACKS_7_8:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 7+8\n");
+			break;
+		}
+		switch (snd_rme96_getattenuation(rme96)) {
+		case RME96_ATTENUATION_0:
+			snd_iprintf(buffer, "  attenuation: 0 dB\n");
+			break;
+		case RME96_ATTENUATION_6:
+			snd_iprintf(buffer, "  attenuation: -6 dB\n");
+			break;
+		case RME96_ATTENUATION_12:
+			snd_iprintf(buffer, "  attenuation: -12 dB\n");
+			break;
+		case RME96_ATTENUATION_18:
+			snd_iprintf(buffer, "  attenuation: -18 dB\n");
+			break;
+		}
+		snd_iprintf(buffer, "  volume left: %u\n", rme96->vol[0]);
+		snd_iprintf(buffer, "  volume right: %u\n", rme96->vol[1]);
+	}
+}
+
+static void __init 
+snd_rme96_proc_init(rme96_t *rme96)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(rme96->card, "rme96", rme96->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = rme96;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_rme96_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	rme96->proc_entry = entry;
+}
+
+static void
+snd_rme96_proc_done(rme96_t * rme96)
+{
+	if (rme96->proc_entry) {
+		snd_info_unregister(rme96->proc_entry);
+		rme96->proc_entry = NULL;
+	}
+}
+
+/*
+ * control interface
+ */
+
+static int
+snd_rme96_info_loopback_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+static int
+snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL;
+	spin_lock_irqsave(&rme96->lock, flags);
+	val = (rme96->wcreg & ~RME96_WCR_SEL) | val;
+	change = val != rme96->wcreg;
+	writel(rme96->wcreg = val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+	case PCI_DEVICE_ID_DIGI96_8:
+		uinfo->value.enumerated.items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		uinfo->value.enumerated.items = 4;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		if (rme96->rev > 4) {
+			/* PST */
+			uinfo->value.enumerated.items = 4;
+			texts[3] = _texts[4]; /* Analog instead of XLR */
+		} else {
+			/* PAD */
+			uinfo->value.enumerated.items = 5;
+		}
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) {
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int items = 3;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96);
+	
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+	case PCI_DEVICE_ID_DIGI96_8:
+		items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		items = 4;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		if (rme96->rev > 4) {
+			/* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */
+			if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) {
+				ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR;
+			}
+			items = 4;
+		} else {
+			items = 5;
+		}
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	if (ucontrol->value.enumerated.item[0] >= items) {
+		ucontrol->value.enumerated.item[0] = items - 1;
+	}
+	
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change, items = 3;
+	
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+	case PCI_DEVICE_ID_DIGI96_8:
+		items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		items = 4;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		if (rme96->rev > 4) {
+			items = 4;
+		} else {
+			items = 5;
+		}
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	val = ucontrol->value.enumerated.item[0] % items;
+	
+	/* special case for PST */
+	if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) {
+		if (val == RME96_INPUT_XLR) {
+			val = RME96_INPUT_ANALOG;
+		}
+	}
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getinputtype(rme96);
+	snd_rme96_setinputtype(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = { "Slave", "Master", "Wordclock" };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2) {
+		uinfo->value.enumerated.item = 2;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getclockmode(rme96);
+	snd_rme96_setclockmode(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3) {
+		uinfo->value.enumerated.item = 3;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.enumerated.item[0] % 4;
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getattenuation(rme96);
+	snd_rme96_setattenuation(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3) {
+		uinfo->value.enumerated.item = 3;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.enumerated.item[0] % 4;
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getmontracks(rme96);
+	snd_rme96_setmontracks(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes)
+{
+	u32 val = 0;
+	val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0;
+	val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0;
+	if (val & RME96_WCR_PRO)
+		val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
+	else
+		val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
+	return val;
+}
+
+static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+	aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
+			 ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0);
+	if (val & RME96_WCR_PRO)
+		aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+	else
+		aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif);
+	return 0;
+}
+
+static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != rme96->wcreg_spdif;
+	rme96->wcreg_spdif = val;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream);
+	return 0;
+}
+
+static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != rme96->wcreg_spdif_stream;
+	rme96->wcreg_spdif_stream = val;
+	rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+	writel(rme96->wcreg |= val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = kcontrol->private_value;
+	return 0;
+}
+
+static int
+snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96);
+        return 0;
+}
+
+static int
+snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme96->lock, flags);
+        u->value.integer.value[0] = rme96->vol[0];
+        u->value.integer.value[1] = rme96->vol[1];
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+        return 0;
+}
+
+static int
+snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+
+	if (!RME96_HAS_ANALOG_OUT(rme96)) {
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&rme96->lock, flags);
+        if (u->value.integer.value[0] != rme96->vol[0]) {
+		rme96->vol[0] = u->value.integer.value[0];
+                change = 1;
+        }
+        if (u->value.integer.value[1] != rme96->vol[1]) {
+		rme96->vol[1] = u->value.integer.value[1];
+                change = 1;
+        }
+	if (change) {
+		snd_rme96_apply_dac_volume(rme96);
+	}
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+        return change;
+}
+
+static snd_kcontrol_new_t snd_rme96_controls[] = {
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_rme96_control_spdif_info,
+	get:		snd_rme96_control_spdif_get,
+	put:		snd_rme96_control_spdif_put
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_rme96_control_spdif_stream_info,
+	get:		snd_rme96_control_spdif_stream_get,
+	put:		snd_rme96_control_spdif_stream_put
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_rme96_control_spdif_mask_info,
+	get:		snd_rme96_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_CON_EMPHASIS
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	info:		snd_rme96_control_spdif_mask_info,
+	get:		snd_rme96_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_PRO_EMPHASIS
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Input Connector",
+	info:           snd_rme96_info_inputtype_control, 
+	get:            snd_rme96_get_inputtype_control,
+	put:            snd_rme96_put_inputtype_control 
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Loopback Input",
+	info:           snd_rme96_info_loopback_control,
+	get:            snd_rme96_get_loopback_control,
+	put:            snd_rme96_put_loopback_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Clock Mode",
+	info:           snd_rme96_info_clockmode_control, 
+	get:            snd_rme96_get_clockmode_control,
+	put:            snd_rme96_put_clockmode_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Monitor Tracks",
+	info:           snd_rme96_info_montracks_control, 
+	get:            snd_rme96_get_montracks_control,
+	put:            snd_rme96_put_montracks_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Attenuation",
+	info:           snd_rme96_info_attenuation_control, 
+	get:            snd_rme96_get_attenuation_control,
+	put:            snd_rme96_put_attenuation_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "DAC Playback Volume",
+	info:           snd_rme96_dac_volume_info,
+	get:            snd_rme96_dac_volume_get,
+	put:            snd_rme96_dac_volume_put
+}
+};
+
+static int
+snd_rme96_create_switches(snd_card_t *card,
+			  rme96_t *rme96)
+{
+	int idx, err;
+	snd_kcontrol_t *kctl;
+
+	for (idx = 0; idx < 7; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+			return err;
+		if (idx == 1)	/* IEC958 (S/PDIF) Stream */
+			rme96->spdif_ctl = kctl;
+	}
+
+	if (RME96_HAS_ANALOG_OUT(rme96)) {
+		for (idx = 7; idx < 10; idx++)
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+				return err;
+	}
+	
+	return 0;
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_rme96_card_free(snd_card_t *card)
+{
+	snd_rme96_free(card->private_data);
+}
+
+static int __devinit
+snd_rme96_probe(struct pci_dev *pci,
+		const struct pci_device_id *id)
+{
+	static int dev = 0;
+	rme96_t *rme96;
+	snd_card_t *card;
+	int err;
+	u8 val;
+
+	if (dev >= SNDRV_CARDS) {
+		return -ENODEV;
+	}
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(rme96_t))) == NULL)
+		return -ENOMEM;
+	card->private_free = snd_rme96_card_free;
+	rme96 = (rme96_t *)card->private_data;	
+	rme96->card = card;
+	rme96->pci = pci;
+	if ((err = snd_rme96_create(rme96)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	strcpy(card->driver, "Digi96");
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+		strcpy(card->shortname, "RME Digi96");
+		break;
+	case PCI_DEVICE_ID_DIGI96_8:
+		strcpy(card->shortname, "RME Digi96/8");
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		strcpy(card->shortname, "RME Digi96/8 PRO");
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		pci_read_config_byte(rme96->pci, 8, &val);
+		if (val < 5) {
+			strcpy(card->shortname, "RME Digi96/8 PAD");
+		} else {
+			strcpy(card->shortname, "RME Digi96/8 PST");
+		}
+		break;
+	}
+	sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
+		rme96->port, rme96->irq);
+	
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;	
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_rme96_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "RME Digi96",
+	id_table: snd_rme96_ids,
+	probe: snd_rme96_probe,
+	remove: __devexit_p(snd_rme96_remove),
+};
+
+static int __init alsa_card_rme96_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "No RME Digi96 cards found\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_rme96_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_rme96_init)
+module_exit(alsa_card_rme96_exit)
+
+#ifndef MODULE
+
+/* format is: snd-rme96=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_rme96_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-rme96=", alsa_card_rme96_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/rme9652/Makefile linux-2.4.19-pre5-mjc/sound/pci/rme9652/Makefile
--- linux/sound/pci/rme9652/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/rme9652/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,24 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _rme9652.o
+
+list-multi   := snd-rme9652-mem.o snd-rme9652.o
+
+export-objs  := rme9652_mem.o
+
+snd-rme9652-mem-objs := rme9652_mem.o
+snd-rme9652-objs := rme9652.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_RME9652) += snd-rme9652.o snd-rme9652-mem.o
+
+include $(TOPDIR)/Rules.make
+
+snd-rme9652-mem.o: $(snd-rme9652-mem-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-mem-objs)
+
+snd-rme9652.o: $(snd-rme9652-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-objs)
diff -Nru linux/sound/pci/rme9652/rme9652.c linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652.c
--- linux/sound/pci/rme9652/rme9652.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2761 @@
+/*
+ *   ALSA driver for RME Digi9652 audio interfaces 
+ *
+ *	Copyright (c) 1999 IEM - Winfried Ritsch
+ *      Copyright (c) 1999-2001  Paul Davis
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */
+
+EXPORT_NO_SYMBOLS;
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for RME Digi9652 (Hammerfall) soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for RME Digi9652 (Hammerfall) soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable/disable specific RME96{52,36} soundcards.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_precise_ptr, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_precise_ptr, "Enable precise pointer (doesn't work reliably).");
+MODULE_PARM_SYNTAX(snd_precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+MODULE_AUTHOR("Paul Davis <pbd@op.net>, Winfried Ritsch");
+MODULE_DESCRIPTION("RME Digi9652/Digi9636");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{RME,Hammerfall},"
+		"{RME,Hammerfall-Light}}");
+
+/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for
+   capture, one for playback. Both the ADAT and S/PDIF channels appear
+   to the host CPU in the same block of memory. There is no functional
+   difference between them in terms of access.
+   
+   The Hammerfall Light is identical to the Hammerfall, except that it
+   has 2 sets 18 channels (16 ADAT + 2 S/PDIF) for capture and playback.
+*/
+
+#define RME9652_NCHANNELS       26
+#define RME9636_NCHANNELS       18
+
+/* Preferred sync source choices - used by "sync_pref" control switch */
+
+#define RME9652_SYNC_FROM_SPDIF 0
+#define RME9652_SYNC_FROM_ADAT1 1
+#define RME9652_SYNC_FROM_ADAT2 2
+#define RME9652_SYNC_FROM_ADAT3 3
+
+/* Possible sources of S/PDIF input */
+
+#define RME9652_SPDIFIN_OPTICAL 0	/* optical (ADAT1) */
+#define RME9652_SPDIFIN_COAXIAL 1	/* coaxial (RCA) */
+#define RME9652_SPDIFIN_INTERN  2	/* internal (CDROM) */
+
+/* ------------- Status-Register bits --------------------- */
+
+#define RME9652_IRQ	   (1<<0)	/* IRQ is High if not reset by irq_clear */
+#define RME9652_lock_2	   (1<<1)	/* ADAT 3-PLL: 1=locked, 0=unlocked */
+#define RME9652_lock_1	   (1<<2)	/* ADAT 2-PLL: 1=locked, 0=unlocked */
+#define RME9652_lock_0	   (1<<3)	/* ADAT 1-PLL: 1=locked, 0=unlocked */
+#define RME9652_fs48	   (1<<4)	/* sample rate is 0=44.1/88.2,1=48/96 Khz */
+#define RME9652_wsel_rd	   (1<<5)	/* if Word-Clock is used and valid then 1 */
+                                        /* bits 6-15 encode h/w buffer pointer position */
+#define RME9652_sync_2	   (1<<16)	/* if ADAT-IN 3 in sync to system clock */
+#define RME9652_sync_1	   (1<<17)	/* if ADAT-IN 2 in sync to system clock */
+#define RME9652_sync_0	   (1<<18)	/* if ADAT-IN 1 in sync to system clock */
+#define RME9652_DS_rd	   (1<<19)	/* 1=Double Speed Mode, 0=Normal Speed */
+#define RME9652_tc_busy	   (1<<20)	/* 1=time-code copy in progress (960ms) */
+#define RME9652_tc_out	   (1<<21)	/* time-code out bit */
+#define RME9652_F_0	   (1<<22)	/* 000=64kHz, 100=88.2kHz, 011=96kHz  */
+#define RME9652_F_1	   (1<<23)	/* 111=32kHz, 110=44.1kHz, 101=48kHz, */
+#define RME9652_F_2	   (1<<24)	/* external Crystal Chip if ERF=1 */
+#define RME9652_ERF	   (1<<25)	/* Error-Flag of SDPIF Receiver (1=No Lock) */
+#define RME9652_buffer_id  (1<<26)	/* toggles by each interrupt on rec/play */
+#define RME9652_tc_valid   (1<<27)	/* 1 = a signal is detected on time-code input */
+#define RME9652_SPDIF_READ (1<<28)      /* byte available from Rev 1.5+ S/PDIF interface */
+
+#define RME9652_sync	  (RME9652_sync_0|RME9652_sync_1|RME9652_sync_2)
+#define RME9652_lock	  (RME9652_lock_0|RME9652_lock_1|RME9652_lock_2)
+#define RME9652_F	  (RME9652_F_0|RME9652_F_1|RME9652_F_2)
+#define rme9652_decode_spdif_rate(x) ((x)>>22)
+
+/* Bit 6..15 : h/w buffer pointer */
+
+#define RME9652_buf_pos	  0x000FFC0
+
+/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later
+   Rev G EEPROMS and Rev 1.5 cards or later.
+*/ 
+
+#define RME9652_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME9652_buf_pos))
+
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX		0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL	0x3fc4
+#endif
+
+/* amount of io space we remap for register access. i'm not sure we
+   even need this much, but 1K is nice round number :)
+*/
+
+#define RME9652_IO_EXTENT     1024
+
+#define RME9652_init_buffer       0
+#define RME9652_play_buffer       32	/* holds ptr to 26x64kBit host RAM */
+#define RME9652_rec_buffer        36	/* holds ptr to 26x64kBit host RAM */
+#define RME9652_control_register  64
+#define RME9652_irq_clear         96
+#define RME9652_time_code         100	/* useful if used with alesis adat */
+#define RME9652_thru_base         128	/* 132...228 Thru for 26 channels */
+
+/* Read-only registers */
+
+/* Writing to any of the register locations writes to the status
+   register. We'll use the first location as our point of access.
+*/
+
+#define RME9652_status_register    0
+
+/* --------- Control-Register Bits ---------------- */
+
+
+#define RME9652_start_bit	   (1<<0)	/* start record/play */
+                                                /* bits 1-3 encode buffersize/latency */
+#define RME9652_Master		   (1<<4)	/* Clock Mode Master=1,Slave/Auto=0 */
+#define RME9652_IE		   (1<<5)	/* Interupt Enable */
+#define RME9652_freq		   (1<<6)       /* samplerate 0=44.1/88.2, 1=48/96 kHz */
+#define RME9652_freq1		   (1<<7)       /* if 0, 32kHz, else always 1 */
+#define RME9652_DS                 (1<<8)	/* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
+#define RME9652_PRO		   (1<<9)	/* S/PDIF out: 0=consumer, 1=professional */
+#define RME9652_EMP		   (1<<10)	/*  Emphasis 0=None, 1=ON */
+#define RME9652_Dolby		   (1<<11)	/*  Non-audio bit 1=set, 0=unset */
+#define RME9652_opt_out	           (1<<12)	/* Use 1st optical OUT as SPDIF: 1=yes,0=no */
+#define RME9652_wsel		   (1<<13)	/* use Wordclock as sync (overwrites master) */
+#define RME9652_inp_0		   (1<<14)	/* SPDIF-IN: 00=optical (ADAT1),     */
+#define RME9652_inp_1		   (1<<15)	/* 01=koaxial (Cinch), 10=Internal CDROM */
+#define RME9652_SyncPref_ADAT2	   (1<<16)
+#define RME9652_SyncPref_ADAT3	   (1<<17)
+#define RME9652_SPDIF_RESET        (1<<18)      /* Rev 1.5+: h/w S/PDIF receiver */
+#define RME9652_SPDIF_SELECT       (1<<19)
+#define RME9652_SPDIF_CLOCK        (1<<20)
+#define RME9652_SPDIF_WRITE        (1<<21)
+#define RME9652_ADAT1_INTERNAL     (1<<22)      /* Rev 1.5+: if set, internal CD connector carries ADAT */
+
+/* buffersize = 512Bytes * 2^n, where n is made from Bit2 ... Bit0 */
+
+#define RME9652_latency            0x0e
+#define rme9652_encode_latency(x)  (((x)&0x7)<<1)
+#define rme9652_decode_latency(x)  (((x)>>1)&0x7)
+#define rme9652_running_double_speed(s) ((s)->control_register & RME9652_DS)
+#define RME9652_inp                (RME9652_inp_0|RME9652_inp_1)
+#define rme9652_encode_spdif_in(x) (((x)&0x3)<<14)
+#define rme9652_decode_spdif_in(x) (((x)>>14)&0x3)
+
+#define RME9652_SyncPref_Mask      (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3)
+#define RME9652_SyncPref_ADAT1	   0
+#define RME9652_SyncPref_SPDIF	   (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3)
+
+/* the size of a substream (1 mono data stream) */
+
+#define RME9652_CHANNEL_BUFFER_SAMPLES  (16*1024)
+#define RME9652_CHANNEL_BUFFER_BYTES    (4*RME9652_CHANNEL_BUFFER_SAMPLES)
+
+/* the size of the area we need to allocate for DMA transfers. the
+   size is the same regardless of the number of channels - the 
+   9636 still uses the same memory area.
+
+   Note that we allocate 1 more channel than is apparently needed
+   because the h/w seems to write 1 byte beyond the end of the last
+   page. Sigh.
+*/
+
+#define RME9652_DMA_AREA_BYTES ((RME9652_NCHANNELS+1) * RME9652_CHANNEL_BUFFER_BYTES)
+#define RME9652_DMA_AREA_KILOBYTES (RME9652_DMA_AREA_BYTES/1024)
+
+typedef struct snd_rme9652 {
+	int dev;
+
+	spinlock_t lock;
+	int irq;
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long iobase;
+	
+	int precise_ptr;
+
+	u32 control_register;	/* cached value */
+	u32 thru_bits;		/* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */
+
+	u32 creg_spdif;
+	u32 creg_spdif_stream;
+
+	char *card_name;		/* hammerfall or hammerfall light names */
+
+        size_t hw_offsetmask;     	/* &-with status register to get real hw_offset */
+	size_t prev_hw_offset;		/* previous hw offset */
+	size_t max_jitter;		/* maximum jitter in frames for 
+					   hw pointer */
+	size_t period_bytes;		/* guess what this is */
+
+	unsigned char ds_channels;
+	unsigned char ss_channels;	/* different for hammerfall/hammerfall-light */
+
+	void *capture_buffer_unaligned;	/* original buffer addresses */
+	void *playback_buffer_unaligned;	/* original buffer addresses */
+	unsigned char *capture_buffer;	/* suitably aligned address */
+	unsigned char *playback_buffer;	/* suitably aligned address */
+	dma_addr_t capture_buffer_addr;
+	dma_addr_t playback_buffer_addr;
+
+	pid_t capture_pid;
+	pid_t playback_pid;
+
+	snd_pcm_substream_t *capture_substream;
+	snd_pcm_substream_t *playback_substream;
+	int running;
+
+        int passthru;                   /* non-zero if doing pass-thru */
+        int hw_rev;                     /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */
+
+	int last_spdif_sample_rate;	/* so that we can catch externally ... */
+	int last_adat_sample_rate;	/* ... induced rate changes            */
+
+        char *channel_map;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	struct pci_dev *pci;
+	snd_info_entry_t *proc_entry;
+	snd_kcontrol_t *spdif_ctl;
+
+} rme9652_t;
+
+/* These tables map the ALSA channels 1..N to the channels that we
+   need to use in order to find the relevant channel buffer. RME
+   refer to this kind of mapping as between "the ADAT channel and
+   the DMA channel." We index it using the logical audio channel,
+   and the value is the DMA channel (i.e. channel buffer number)
+   where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_9652_ss[26] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+	18, 19, 20, 21, 22, 23, 24, 25
+};
+
+static char channel_map_9636_ss[26] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
+	/* channels 16 and 17 are S/PDIF */
+	24, 25,
+	/* channels 18-25 don't exist */
+	-1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_9652_ds[26] = {
+	/* ADAT channels are remapped */
+	1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23,
+	/* channels 12 and 13 are S/PDIF */
+	24, 25,
+	/* others don't exist */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_9636_ds[26] = {
+	/* ADAT channels are remapped */
+	1, 3, 5, 7, 9, 11, 13, 15,
+	/* channels 8 and 9 are S/PDIF */
+	24, 25
+	/* others don't exist */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+#define RME9652_PREALLOCATE_MEMORY	/* via module snd-rme9652_mem */
+
+#ifdef RME9652_PREALLOCATE_MEMORY
+extern void *snd_rme9652_get_buffer(int card, dma_addr_t *dmaaddr);
+extern void snd_rme9652_free_buffer(int card, void *ptr);
+#endif
+
+static struct pci_device_id snd_rme9652_ids[] __devinitdata = {
+	{0x10ee, 0x3fc4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},	/* RME Digi9652 */
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme9652_ids);
+
+static inline void rme9652_write(rme9652_t *rme9652, int reg, int val)
+{
+	writel(val, rme9652->iobase + reg);
+}
+
+static inline unsigned int rme9652_read(rme9652_t *rme9652, int reg)
+{
+	return readl(rme9652->iobase + reg);
+}
+
+static inline int snd_rme9652_use_is_exclusive(rme9652_t *rme9652)
+{
+	unsigned long flags;
+	int ret = 1;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+	if ((rme9652->playback_pid != rme9652->capture_pid) &&
+	    (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) {
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return ret;
+}
+
+static inline int rme9652_adat_sample_rate(rme9652_t *rme9652)
+{
+	if (rme9652_running_double_speed(rme9652)) {
+		return (rme9652_read(rme9652, RME9652_status_register) &
+			RME9652_fs48) ? 96000 : 88200;
+	} else {
+		return (rme9652_read(rme9652, RME9652_status_register) &
+			RME9652_fs48) ? 48000 : 44100;
+	}
+}
+
+static inline void rme9652_compute_period_size(rme9652_t *rme9652)
+{
+	unsigned int i;
+
+	i = rme9652->control_register & RME9652_latency;
+	rme9652->period_bytes = 1 << ((rme9652_decode_latency(i) + 8));
+	rme9652->hw_offsetmask = 
+		(rme9652->period_bytes * 2 - 1) & RME9652_buf_pos;
+	rme9652->max_jitter = 80;
+}
+
+static snd_pcm_uframes_t rme9652_hw_pointer(rme9652_t *rme9652)
+{
+	int status;
+	int offset, frag;
+	snd_pcm_uframes_t period_size = rme9652->period_bytes / 4;
+	snd_pcm_sframes_t delta;
+
+	status = rme9652_read(rme9652, RME9652_status_register);
+	if (!rme9652->precise_ptr)
+		return (status & RME9652_buffer_id) ? period_size : 0;
+	offset = status & RME9652_buf_pos;
+
+	/* The hardware may give a backward movement for up to 80 frames
+           Martin Kirst <martin.kirst@freenet.de> knows the details.
+	*/
+
+	delta = rme9652->prev_hw_offset - offset;
+	delta &= 0xffff;
+	if (delta <= rme9652->max_jitter * 4)
+		offset = rme9652->prev_hw_offset;
+	else
+		rme9652->prev_hw_offset = offset;
+	offset &= rme9652->hw_offsetmask;
+	offset /= 4;
+	frag = status & RME9652_buffer_id;
+
+	if (offset < period_size) {
+		if (offset > rme9652->max_jitter) {
+			if (frag)
+				printk(KERN_ERR "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset);
+		} else if (!frag)
+			return 0;
+		offset -= rme9652->max_jitter;
+		if (offset < 0)
+			offset += period_size * 2;
+	} else {
+		if (offset > period_size + rme9652->max_jitter) {
+			if (!frag)
+				printk(KERN_ERR "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset);
+		} else if (frag)
+			return period_size;
+		offset -= rme9652->max_jitter;
+	}
+
+	return offset;
+}
+
+static inline void rme9652_reset_hw_pointer(rme9652_t *rme9652)
+{
+	int i;
+
+	/* reset the FIFO pointer to zero. We do this by writing to 8
+	   registers, each of which is a 32bit wide register, and set
+	   them all to zero. Note that s->iobase is a pointer to
+	   int32, not pointer to char.  
+	*/
+
+	for (i = 0; i < 8; i++) {
+		rme9652_write(rme9652, i * 4, 0);
+		udelay(10);
+	}
+	rme9652->prev_hw_offset = 0;
+}
+
+static inline void rme9652_start(rme9652_t *s)
+{
+	s->control_register |= (RME9652_IE | RME9652_start_bit);
+	rme9652_write(s, RME9652_control_register, s->control_register);
+}
+
+static inline void rme9652_stop(rme9652_t *s)
+{
+	s->control_register &= ~(RME9652_start_bit | RME9652_IE);
+	rme9652_write(s, RME9652_control_register, s->control_register);
+}
+
+static int rme9652_set_interrupt_interval(rme9652_t *s,
+					  unsigned int frames)
+{
+	int restart = 0;
+	int n;
+
+	spin_lock_irq(&s->lock);
+
+	if ((restart = s->running)) {
+		rme9652_stop(s);
+	}
+
+	frames >>= 7;
+	n = 0;
+	while (frames) {
+		n++;
+		frames >>= 1;
+	}
+
+	s->control_register &= ~RME9652_latency;
+	s->control_register |= rme9652_encode_latency(n);
+
+	rme9652_write(s, RME9652_control_register, s->control_register);
+
+	rme9652_compute_period_size(s);
+
+	if (restart)
+		rme9652_start(s);
+
+	spin_unlock_irq(&s->lock);
+
+	return 0;
+}
+
+static int rme9652_set_rate(rme9652_t *rme9652, int rate)
+{
+	int restart;
+	int reject_if_open = 0;
+	int xrate;
+
+	/* Changing from a "single speed" to a "double speed" rate is
+	   not allowed if any substreams are open. This is because
+	   such a change causes a shift in the location of 
+	   the DMA buffers and a reduction in the number of available
+	   buffers. 
+
+	   Note that a similar but essentially insoluble problem
+	   exists for externally-driven rate changes. All we can do
+	   is to flag rate changes in the read/write routines.
+	 */
+
+	spin_lock_irq(&rme9652->lock);
+	xrate = rme9652_adat_sample_rate(rme9652);
+
+	switch (rate) {
+	case 44100:
+		if (xrate > 48000) {
+			reject_if_open = 1;
+		}
+		rate = 0;
+		break;
+	case 48000:
+		if (xrate > 48000) {
+			reject_if_open = 1;
+		}
+		rate = RME9652_freq;
+		break;
+	case 88200:
+		if (xrate < 48000) {
+			reject_if_open = 1;
+		}
+		rate = RME9652_DS;
+		break;
+	case 96000:
+		if (xrate < 48000) {
+			reject_if_open = 1;
+		}
+		rate = RME9652_DS | RME9652_freq;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (reject_if_open &&
+	    (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) {
+		spin_unlock_irq(&rme9652->lock);
+		return -EBUSY;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652->control_register &= ~(RME9652_freq | RME9652_DS);
+	rme9652->control_register |= rate;
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	if (rate & RME9652_DS) {
+		if (rme9652->ss_channels == RME9652_NCHANNELS) {
+			rme9652->channel_map = channel_map_9652_ds;
+		} else {
+			rme9652->channel_map = channel_map_9636_ds;
+		}
+	} else {
+		if (rme9652->ss_channels == RME9652_NCHANNELS) {
+			rme9652->channel_map = channel_map_9652_ss;
+		} else {
+			rme9652->channel_map = channel_map_9636_ss;
+		}
+	}
+
+	spin_unlock_irq(&rme9652->lock);
+	return 0;
+}
+
+static void rme9652_set_thru(rme9652_t *rme9652, int channel, int enable)
+{
+	int i;
+
+	rme9652->passthru = 0;
+
+	if (channel < 0) {
+
+		/* set thru for all channels */
+
+		if (enable) {
+			for (i = 0; i < RME9652_NCHANNELS; i++) {
+				rme9652->thru_bits |= (1 << i);
+				rme9652_write(rme9652, RME9652_thru_base + i * 4, 1);
+			}
+		} else {
+			for (i = 0; i < RME9652_NCHANNELS; i++) {
+				rme9652->thru_bits &= ~(1 << i);
+				rme9652_write(rme9652, RME9652_thru_base + i * 4, 0);
+			}
+		}
+
+	} else {
+		int mapped_channel;
+
+		snd_assert(channel == RME9652_NCHANNELS, return);
+
+		mapped_channel = rme9652->channel_map[channel];
+
+		if (enable) {
+			rme9652->thru_bits |= (1 << mapped_channel);
+		} else {
+			rme9652->thru_bits &= ~(1 << mapped_channel);
+		}
+
+		rme9652_write(rme9652,
+			       RME9652_thru_base + mapped_channel * 4,
+			       enable ? 1 : 0);			       
+	}
+}
+
+static int rme9652_set_passthru(rme9652_t *rme9652, int onoff)
+{
+	if (onoff) {
+		rme9652_set_thru(rme9652, -1, 1);
+
+		/* we don't want interrupts, so do a
+		   custom version of rme9652_start().
+		*/
+
+		rme9652->control_register =
+			RME9652_inp_0 | 
+			rme9652_encode_latency(7) |
+			RME9652_start_bit;
+
+		rme9652_reset_hw_pointer(rme9652);
+
+		rme9652_write(rme9652, RME9652_control_register,
+			      rme9652->control_register);
+		rme9652->passthru = 1;
+	} else {
+		rme9652_set_thru(rme9652, -1, 0);
+		rme9652_stop(rme9652);		
+		rme9652->passthru = 0;
+	}
+
+	return 0;
+}
+
+static void rme9652_spdif_set_bit (rme9652_t *rme9652, int mask, int onoff)
+{
+	if (onoff) 
+		rme9652->control_register |= mask;
+	else 
+		rme9652->control_register &= ~mask;
+		
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+}
+
+static void rme9652_spdif_write_byte (rme9652_t *rme9652, const int val)
+{
+	long mask;
+	long i;
+
+	for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) {
+		if (val & mask)
+			rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 1);
+		else 
+			rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 0);
+
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1);
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0);
+	}
+}
+
+static int rme9652_spdif_read_byte (rme9652_t *rme9652)
+{
+	long mask;
+	long val;
+	long i;
+
+	val = 0;
+
+	for (i = 0, mask = 0x80;  i < 8; i++, mask >>= 1) {
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1);
+		if (rme9652_read (rme9652, RME9652_status_register) & RME9652_SPDIF_READ)
+			val |= mask;
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0);
+	}
+
+	return val;
+}
+
+static void rme9652_write_spdif_codec (rme9652_t *rme9652, const int address, const int data)
+{
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+	rme9652_spdif_write_byte (rme9652, 0x20);
+	rme9652_spdif_write_byte (rme9652, address);
+	rme9652_spdif_write_byte (rme9652, data);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+}
+
+
+static int rme9652_spdif_read_codec (rme9652_t *rme9652, const int address)
+{
+	int ret;
+
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+	rme9652_spdif_write_byte (rme9652, 0x20);
+	rme9652_spdif_write_byte (rme9652, address);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+
+	rme9652_spdif_write_byte (rme9652, 0x21);
+	ret = rme9652_spdif_read_byte (rme9652);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+
+	return ret;
+}
+
+static void rme9652_initialize_spdif_receiver (rme9652_t *rme9652)
+{
+	/* XXX what unsets this ? */
+
+	rme9652->control_register |= RME9652_SPDIF_RESET;
+
+	rme9652_write_spdif_codec (rme9652, 4, 0x40);
+	rme9652_write_spdif_codec (rme9652, 17, 0x13);
+	rme9652_write_spdif_codec (rme9652, 6, 0x02);
+}
+
+static inline int rme9652_spdif_sample_rate(rme9652_t *s)
+{
+	unsigned int rate_bits;
+
+	if (rme9652_read(s, RME9652_status_register) & RME9652_ERF) {
+		return -1;	/* error condition */
+	}
+	
+	if (s->hw_rev == 15) {
+
+		int x, y, ret;
+		
+		x = rme9652_spdif_read_codec (s, 30);
+
+		if (x != 0) 
+			y = 48000 * 64 / x;
+		else
+			y = 0;
+
+		if      (y > 30400 && y < 33600)  ret = 32000; 
+		else if (y > 41900 && y < 46000)  ret = 44100;
+		else if (y > 46000 && y < 50400)  ret = 48000;
+		else if (y > 60800 && y < 67200)  ret = 64000;
+		else if (y > 83700 && y < 92000)  ret = 88200;
+		else if (y > 92000 && y < 100000) ret = 96000;
+		else                              ret = 0;
+		return ret;
+	}
+
+	rate_bits = rme9652_read(s, RME9652_status_register) & RME9652_F;
+
+	switch (rme9652_decode_spdif_rate(rate_bits)) {
+	case 0x7:
+		return 32000;
+		break;
+
+	case 0x6:
+		return 44100;
+		break;
+
+	case 0x5:
+		return 48000;
+		break;
+
+	case 0x4:
+		return 88200;
+		break;
+
+	case 0x3:
+		return 96000;
+		break;
+
+	case 0x0:
+		return 64000;
+		break;
+
+	default:
+		snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n",
+			   s->card_name, rate_bits);
+		return 0;
+		break;
+	}
+}
+
+/*-----------------------------------------------------------------------------
+  Control Interface
+  ----------------------------------------------------------------------------*/
+
+static u32 snd_rme9652_convert_from_aes(snd_aes_iec958_t *aes)
+{
+	u32 val = 0;
+	val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME9652_PRO : 0;
+	val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME9652_Dolby : 0;
+	if (val & RME9652_PRO)
+		val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME9652_EMP : 0;
+	else
+		val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME9652_EMP : 0;
+	return val;
+}
+
+static void snd_rme9652_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+	aes->status[0] = ((val & RME9652_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
+			 ((val & RME9652_Dolby) ? IEC958_AES0_NONAUDIO : 0);
+	if (val & RME9652_PRO)
+		aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+	else
+		aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme9652_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif);
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652->creg_spdif;
+	rme9652->creg_spdif = val;
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+static int snd_rme9652_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream);
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652->creg_spdif_stream;
+	rme9652->creg_spdif_stream = val;
+	rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+static int snd_rme9652_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = kcontrol->private_value;
+	return 0;
+}
+
+#define RME9652_ADAT1_IN(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_adat1_in, \
+  get: snd_rme9652_get_adat1_in, \
+  put: snd_rme9652_put_adat1_in }
+
+static unsigned int rme9652_adat1_in(rme9652_t *rme9652)
+{
+	if (rme9652->control_register & RME9652_ADAT1_INTERNAL)
+		return 1; 
+	return 0;
+}
+
+static int rme9652_set_adat1_input(rme9652_t *rme9652, int internal)
+{
+	int restart = 0;
+
+	if (internal) {
+		rme9652->control_register |= RME9652_ADAT1_INTERNAL;
+	} else {
+		rme9652->control_register &= ~RME9652_ADAT1_INTERNAL;
+	}
+
+	/* XXX do we actually need to stop the card when we do this ? */
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_adat1_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[2] = {"ADAT1", "Internal"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	val = ucontrol->value.enumerated.item[0] % 2;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_adat1_in(rme9652);
+	if (change)
+		rme9652_set_adat1_input(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#define RME9652_SPDIF_IN(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_spdif_in, \
+  get: snd_rme9652_get_spdif_in, put: snd_rme9652_put_spdif_in }
+
+static unsigned int rme9652_spdif_in(rme9652_t *rme9652)
+{
+	return rme9652_decode_spdif_in(rme9652->control_register &
+				       RME9652_inp);
+}
+
+static int rme9652_set_spdif_input(rme9652_t *rme9652, int in)
+{
+	int restart = 0;
+
+	rme9652->control_register &= ~RME9652_inp;
+	rme9652->control_register |= rme9652_encode_spdif_in(in);
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = {"ADAT1", "Coaxial", "Internal"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_spdif_in(rme9652);
+	if (change)
+		rme9652_set_spdif_input(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#define RME9652_SPDIF_OUT(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_spdif_out, \
+  get: snd_rme9652_get_spdif_out, put: snd_rme9652_put_spdif_out }
+
+static int rme9652_spdif_out(rme9652_t *rme9652)
+{
+	return (rme9652->control_register & RME9652_opt_out) ? 1 : 0;
+}
+
+static int rme9652_set_spdif_output(rme9652_t *rme9652, int out)
+{
+	int restart = 0;
+
+	if (out) {
+		rme9652->control_register |= RME9652_opt_out;
+	} else {
+		rme9652->control_register &= ~RME9652_opt_out;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_spdif_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	val = ucontrol->value.integer.value[0] & 1;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_spdif_out(rme9652);
+	rme9652_set_spdif_output(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#define RME9652_SYNC_MODE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_sync_mode, \
+  get: snd_rme9652_get_sync_mode, put: snd_rme9652_put_sync_mode }
+
+static int rme9652_sync_mode(rme9652_t *rme9652)
+{
+	if (rme9652->control_register & RME9652_wsel) {
+		return 2;
+	} else if (rme9652->control_register & RME9652_Master) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static int rme9652_set_sync_mode(rme9652_t *rme9652, int mode)
+{
+	int restart = 0;
+
+	switch (mode) {
+	case 0:
+		rme9652->control_register &=
+		    ~(RME9652_Master | RME9652_wsel);
+		break;
+	case 1:
+		rme9652->control_register =
+		    (rme9652->control_register & ~RME9652_wsel) | RME9652_Master;
+		break;
+	case 2:
+		rme9652->control_register |=
+		    (RME9652_Master | RME9652_wsel);
+		break;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_sync_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = {"AutoSync", "Master", "Word Clock"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_sync_mode(rme9652);
+	rme9652_set_sync_mode(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#define RME9652_SYNC_PREF(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_sync_pref, \
+  get: snd_rme9652_get_sync_pref, put: snd_rme9652_put_sync_pref }
+
+static int rme9652_sync_pref(rme9652_t *rme9652)
+{
+	switch (rme9652->control_register & RME9652_SyncPref_Mask) {
+	case RME9652_SyncPref_ADAT1:
+		return RME9652_SYNC_FROM_ADAT1;
+	case RME9652_SyncPref_ADAT2:
+		return RME9652_SYNC_FROM_ADAT2;
+	case RME9652_SyncPref_ADAT3:
+		return RME9652_SYNC_FROM_ADAT3;
+	case RME9652_SyncPref_SPDIF:
+		return RME9652_SYNC_FROM_SPDIF;
+	}
+	/* Not reachable */
+	return 0;
+}
+
+static int rme9652_set_sync_pref(rme9652_t *rme9652, int pref)
+{
+	int restart;
+
+	rme9652->control_register &= ~RME9652_SyncPref_Mask;
+	switch (pref) {
+	case RME9652_SYNC_FROM_ADAT1:
+		rme9652->control_register |= RME9652_SyncPref_ADAT1;
+		break;
+	case RME9652_SYNC_FROM_ADAT2:
+		rme9652->control_register |= RME9652_SyncPref_ADAT2;
+		break;
+	case RME9652_SYNC_FROM_ADAT3:
+		rme9652->control_register |= RME9652_SyncPref_ADAT3;
+		break;
+	case RME9652_SYNC_FROM_SPDIF:
+		rme9652->control_register |= RME9652_SyncPref_SPDIF;
+		break;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"};
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change, max;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
+	val = ucontrol->value.enumerated.item[0] % max;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_sync_pref(rme9652);
+	rme9652_set_sync_pref(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = rme9652->ss_channels;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned int k;
+	u32 thru_bits = rme9652->thru_bits;
+
+	for (k = 0; k < rme9652->ss_channels; ++k) {
+		ucontrol->value.integer.value[k] = !!(thru_bits & (1 << k));
+	}
+	return 0;
+}
+
+static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int chn;
+	u32 thru_bits = 0;
+
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+
+	for (chn = 0; chn < rme9652->ss_channels; ++chn) {
+		if (ucontrol->value.integer.value[chn])
+			thru_bits |= 1 << chn;
+	}
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = thru_bits ^ rme9652->thru_bits;
+	if (change) {
+		for (chn = 0; chn < rme9652->ss_channels; ++chn) {
+			if (!(change & (1 << chn)))
+				continue;
+			rme9652_set_thru(rme9652,chn,thru_bits&(1<<chn));
+		}
+	}
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return !!change;
+}
+
+#define RME9652_PASSTHRU(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_passthru, \
+  put: snd_rme9652_put_passthru, \
+  get: snd_rme9652_get_passthru }
+
+static int snd_rme9652_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.integer.value[0] = rme9652->passthru;
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	int err = 0;
+
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+
+	val = ucontrol->value.integer.value[0] & 1;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = (ucontrol->value.integer.value[0] != rme9652->passthru);
+	if (change)
+		err = rme9652_set_passthru(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return err ? err : change;
+}
+
+/* Read-only switches */
+
+#define RME9652_SPDIF_RATE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  info: snd_rme9652_info_spdif_rate, \
+  get: snd_rme9652_get_spdif_rate }
+
+static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 96000;
+	return 0;
+}
+
+static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+#define RME9652_ADAT_SYNC(xname, xindex, xidx) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  info: snd_rme9652_info_adat_sync, \
+  get: snd_rme9652_get_adat_sync, private_value: xidx }
+
+static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned int mask1, mask2, val;
+	
+	switch (kcontrol->private_value) {
+	case 0: mask1 = RME9652_lock_0; mask2 = RME9652_sync_0; break;	
+	case 1: mask1 = RME9652_lock_1; mask2 = RME9652_sync_1; break;	
+	case 2: mask1 = RME9652_lock_2; mask2 = RME9652_sync_2; break;	
+	default: return -EINVAL;
+	}
+	val = rme9652_read(rme9652, RME9652_status_register);
+	ucontrol->value.enumerated.item[0] = (val & mask1) ? 1 : 0;
+	ucontrol->value.enumerated.item[0] |= (val & mask2) ? 2 : 0;
+	return 0;
+}
+
+#define RME9652_TC_VALID(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  info: snd_rme9652_info_tc_valid, \
+  get: snd_rme9652_get_tc_valid }
+
+static int snd_rme9652_info_tc_valid(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = 
+		(rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0;
+	return 0;
+}
+
+#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE
+
+/* FIXME: this routine needs a port to the new control API --jk */
+
+static int snd_rme9652_get_tc_value(void *private_data,
+				    snd_kswitch_t *kswitch,
+				    snd_switch_t *uswitch)
+{
+	rme9652_t *s = (rme9652_t *) private_data;
+	u32 value;
+	int i;
+
+	uswitch->type = SNDRV_SW_TYPE_DWORD;
+
+	if ((rme9652_read(s, RME9652_status_register) &
+	     RME9652_tc_valid) == 0) {
+		uswitch->value.data32[0] = 0;
+		return 0;
+	}
+
+	/* timecode request */
+
+	rme9652_write(s, RME9652_time_code, 0);
+
+	/* XXX bug alert: loop-based timing !!!! */
+
+	for (i = 0; i < 50; i++) {
+		if (!(rme9652_read(s, i * 4) & RME9652_tc_busy))
+			break;
+	}
+
+	if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) {
+		return -EIO;
+	}
+
+	value = 0;
+
+	for (i = 0; i < 32; i++) {
+		value >>= 1;
+
+		if (rme9652_read(s, i * 4) & RME9652_tc_out)
+			value |= 0x80000000;
+	}
+
+	if (value > 2 * 60 * 48000) {
+		value -= 2 * 60 * 48000;
+	} else {
+		value = 0;
+	}
+
+	uswitch->value.data32[0] = value;
+
+	return 0;
+}
+
+#endif				/* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */
+
+#define RME9652_CONTROLS (sizeof(snd_rme9652_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_rme9652_controls[] = {
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_rme9652_control_spdif_info,
+	get:		snd_rme9652_control_spdif_get,
+	put:		snd_rme9652_control_spdif_put,
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_rme9652_control_spdif_stream_info,
+	get:		snd_rme9652_control_spdif_stream_get,
+	put:		snd_rme9652_control_spdif_stream_put,
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_rme9652_control_spdif_mask_info,
+	get:		snd_rme9652_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_CON_EMPHASIS,	                                                                                      
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	info:		snd_rme9652_control_spdif_mask_info,
+	get:		snd_rme9652_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_PRO_EMPHASIS,
+},
+RME9652_SPDIF_IN("IEC958 Input Connector", 0),
+RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0),
+RME9652_SYNC_MODE("Sync Mode", 0),
+RME9652_SYNC_PREF("Preferred Sync Source", 0),
+{
+	iface: SNDRV_CTL_ELEM_IFACE_PCM,
+	name: "Channels Thru",
+	index: 0,
+	info: snd_rme9652_info_thru,
+	get: snd_rme9652_get_thru,
+	put: snd_rme9652_put_thru,
+},
+RME9652_SPDIF_RATE("IEC958 Sample Rate", 0),
+RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0),
+RME9652_ADAT_SYNC("ADAT2 Sync Check", 0, 1),
+RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2),
+RME9652_TC_VALID("Timecode Valid", 0),
+RME9652_PASSTHRU("Passthru", 0)
+};
+
+static snd_kcontrol_new_t snd_rme9652_adat3_check =
+RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2);
+
+static snd_kcontrol_new_t snd_rme9652_adat1_input =
+RME9652_ADAT1_IN("ADAT1 Input Source", 0);
+
+int snd_rme9652_create_controls(snd_card_t *card, rme9652_t *rme9652)
+{
+	int idx, err;
+	snd_kcontrol_t *kctl;
+
+	for (idx = 0; idx < RME9652_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0)
+			return err;
+		if (idx == 1)	/* IEC958 (S/PDIF) Stream */
+			rme9652->spdif_ctl = kctl;
+	}
+
+	if (rme9652->ss_channels == RME9652_NCHANNELS)
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0)
+			return err;
+
+	if (rme9652->hw_rev >= 15)
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0)
+			return err;
+
+	return 0;
+}
+
+/*------------------------------------------------------------
+   /proc interface 
+ ------------------------------------------------------------*/
+
+static void
+snd_rme9652_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	rme9652_t *rme9652 = (rme9652_t *) entry->private_data;
+	u32 thru_bits = rme9652->thru_bits;
+	int show_auto_sync_source = 0;
+	int i;
+	unsigned int status;
+	int x;
+
+	status = rme9652_read(rme9652, RME9652_status_register);
+
+	snd_iprintf(buffer, "%s (Card #%d)\n", rme9652->card_name, rme9652->card->number + 1);
+	snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
+		    rme9652->capture_buffer, rme9652->playback_buffer);
+	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+		    rme9652->irq, rme9652->port, rme9652->iobase);
+	snd_iprintf(buffer, "Control register: %x\n", rme9652->control_register);
+
+	snd_iprintf(buffer, "\n");
+
+	x = 1 << (6 + rme9652_decode_latency(rme9652->control_register & 
+					     RME9652_latency));
+
+	snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n", 
+		    x, (unsigned long) rme9652->period_bytes);
+	snd_iprintf(buffer, "Hardware pointer (frames): %ld\n",
+		    rme9652_hw_pointer(rme9652));
+	snd_iprintf(buffer, "Passthru: %s\n",
+		    rme9652->passthru ? "yes" : "no");
+
+	if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) {
+		snd_iprintf(buffer, "Clock mode: autosync\n");
+		show_auto_sync_source = 1;
+	} else if (rme9652->control_register & RME9652_wsel) {
+		if (status & RME9652_wsel_rd) {
+			snd_iprintf(buffer, "Clock mode: word clock\n");
+		} else {
+			snd_iprintf(buffer, "Clock mode: word clock (no signal)\n");
+		}
+	} else {
+		snd_iprintf(buffer, "Clock mode: master\n");
+	}
+
+	if (show_auto_sync_source) {
+		switch (rme9652->control_register & RME9652_SyncPref_Mask) {
+		case RME9652_SyncPref_ADAT1:
+			snd_iprintf(buffer, "Pref. sync source: ADAT1\n");
+			break;
+		case RME9652_SyncPref_ADAT2:
+			snd_iprintf(buffer, "Pref. sync source: ADAT2\n");
+			break;
+		case RME9652_SyncPref_ADAT3:
+			snd_iprintf(buffer, "Pref. sync source: ADAT3\n");
+			break;
+		case RME9652_SyncPref_SPDIF:
+			snd_iprintf(buffer, "Pref. sync source: IEC958\n");
+			break;
+		default:
+			snd_iprintf(buffer, "Pref. sync source: ???\n");
+		}
+	}
+
+	if (rme9652->hw_rev >= 15)
+		snd_iprintf(buffer, "\nADAT1 Input source: %s\n",
+			    (rme9652->control_register & RME9652_ADAT1_INTERNAL) ?
+			    "Internal" : "ADAT1 optical");
+
+	snd_iprintf(buffer, "\n");
+
+	switch (rme9652_decode_spdif_in(rme9652->control_register & 
+					RME9652_inp)) {
+	case RME9652_SPDIFIN_OPTICAL:
+		snd_iprintf(buffer, "IEC958 input: ADAT1\n");
+		break;
+	case RME9652_SPDIFIN_COAXIAL:
+		snd_iprintf(buffer, "IEC958 input: Coaxial\n");
+		break;
+	case RME9652_SPDIFIN_INTERN:
+		snd_iprintf(buffer, "IEC958 input: Internal\n");
+		break;
+	default:
+		snd_iprintf(buffer, "IEC958 input: ???\n");
+		break;
+	}
+
+	if (rme9652->control_register & RME9652_opt_out) {
+		snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+	}
+
+	if (rme9652->control_register & RME9652_PRO) {
+		snd_iprintf(buffer, "IEC958 quality: Professional\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+	}
+
+	if (rme9652->control_register & RME9652_EMP) {
+		snd_iprintf(buffer, "IEC958 emphasis: on\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 emphasis: off\n");
+	}
+
+	if (rme9652->control_register & RME9652_Dolby) {
+		snd_iprintf(buffer, "IEC958 Dolby: on\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 Dolby: off\n");
+	}
+
+	i = rme9652_spdif_sample_rate(rme9652);
+
+	if (i < 0) {
+		snd_iprintf(buffer,
+			    "IEC958 sample rate: error flag set\n");
+	} else if (i == 0) {
+		snd_iprintf(buffer, "IEC958 sample rate: undetermined\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 sample rate: %d\n", i);
+	}
+
+	snd_iprintf(buffer, "\n");
+
+	snd_iprintf(buffer, "ADAT Sample rate: %dHz\n",
+		    rme9652_adat_sample_rate(rme9652));
+
+	/* Sync Check */
+
+	x = status & RME9652_sync_0;
+	if (status & RME9652_lock_0) {
+		snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock");
+	} else {
+		snd_iprintf(buffer, "ADAT1: No Lock\n");
+	}
+
+	x = status & RME9652_sync_1;
+	if (status & RME9652_lock_1) {
+		snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock");
+	} else {
+		snd_iprintf(buffer, "ADAT2: No Lock\n");
+	}
+
+	x = status & RME9652_sync_2;
+	if (status & RME9652_lock_2) {
+		snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock");
+	} else {
+		snd_iprintf(buffer, "ADAT3: No Lock\n");
+	}
+
+	snd_iprintf(buffer, "\n");
+
+	snd_iprintf(buffer, "Timecode signal: %s\n",
+		    (status & RME9652_tc_valid) ? "yes" : "no");
+
+	/* thru modes */
+
+	snd_iprintf(buffer, "Punch Status:\n\n");
+
+	for (i = 0; i < rme9652->ss_channels; i++) {
+		if (thru_bits & (1 << i)) {
+			snd_iprintf(buffer, "%2d:  on ", i + 1);
+		} else {
+			snd_iprintf(buffer, "%2d: off ", i + 1);
+		}
+
+		if (((i + 1) % 8) == 0) {
+			snd_iprintf(buffer, "\n");
+		}
+	}
+
+	snd_iprintf(buffer, "\n");
+}
+
+static void __init snd_rme9652_proc_init(rme9652_t *rme9652)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(rme9652->card, "rme9652", rme9652->card->proc_root)) !=
+	    NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = rme9652;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_rme9652_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	rme9652->proc_entry = entry;
+}
+
+static void snd_rme9652_proc_done(rme9652_t *rme9652)
+{
+	if (rme9652->proc_entry) {
+		snd_info_unregister(rme9652->proc_entry);
+		rme9652->proc_entry = NULL;
+	}
+}
+
+static void snd_rme9652_free_buffers(rme9652_t *rme9652)
+{
+	if (rme9652->capture_buffer_unaligned) {
+#ifndef RME9652_PREALLOCATE_MEMORY
+		snd_free_pci_pages(rme9652->pci,
+				   RME9652_DMA_AREA_BYTES,
+				   rme9652->capture_buffer_unaligned,
+				   rme9652->capture_buffer_addr);
+#else
+		snd_rme9652_free_buffer(rme9652->dev, rme9652->capture_buffer_unaligned);
+#endif
+	}
+
+	if (rme9652->playback_buffer_unaligned) {
+#ifndef RME9652_PREALLOCATE_MEMORY
+		snd_free_pci_pages(rme9652->pci,
+				   RME9652_DMA_AREA_BYTES,
+				   rme9652->playback_buffer_unaligned,
+				   rme9652->playback_buffer_addr);
+#else
+		snd_rme9652_free_buffer(rme9652->dev, rme9652->playback_buffer_unaligned);
+#endif
+	}
+}
+
+static int snd_rme9652_free(rme9652_t *rme9652)
+{
+	if (rme9652->irq >= 0)
+		rme9652_stop(rme9652);
+	snd_rme9652_proc_done(rme9652);
+	snd_rme9652_free_buffers(rme9652);
+
+	if (rme9652->iobase)
+		iounmap((void *) rme9652->iobase);
+	if (rme9652->res_port) {
+		release_resource(rme9652->res_port);
+		kfree_nocheck(rme9652->res_port);
+	}
+	if (rme9652->irq >= 0)
+		free_irq(rme9652->irq, (void *)rme9652);
+	return 0;
+}
+
+static int __init snd_rme9652_initialize_memory(rme9652_t *rme9652)
+{
+	void *pb, *cb;
+	dma_addr_t pb_addr, cb_addr;
+	unsigned long pb_bus, cb_bus;
+
+#ifndef RME9652_PREALLOCATE_MEMORY
+	cb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &cb_addr);
+	pb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &pb_addr);
+#else
+	cb = snd_rme9652_get_buffer(rme9652->dev, &cb_addr);
+	pb = snd_rme9652_get_buffer(rme9652->dev, &pb_addr);
+#endif
+
+	if (cb == 0 || pb == 0) {
+		if (cb) {
+#ifdef RME9652_PREALLOCATE_MEMORY
+			snd_rme9652_free_buffer(rme9652->dev, cb);
+#else
+			snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, cb, cb_addr);
+#endif
+		}
+		if (pb) {
+#ifdef RME9652_PREALLOCATE_MEMORY
+			snd_rme9652_free_buffer(rme9652->dev, pb);
+#else
+			snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, pb, pb_addr);
+#endif
+		}
+
+		printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name);
+		return -ENOMEM;
+	}
+
+	/* save raw addresses for use when freeing memory later */
+
+	rme9652->capture_buffer_unaligned = cb;
+	rme9652->playback_buffer_unaligned = pb;
+	rme9652->capture_buffer_addr = cb_addr;
+	rme9652->playback_buffer_addr = pb_addr;
+
+	/* Align to bus-space 64K boundary */
+
+	cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl;
+	pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl;
+
+	/* Tell the card where it is */
+
+	rme9652_write(rme9652, RME9652_rec_buffer, cb_bus);
+	rme9652_write(rme9652, RME9652_play_buffer, pb_bus);
+
+	rme9652->capture_buffer = cb + (cb_bus - cb_addr);
+	rme9652->playback_buffer = pb + (pb_bus - pb_addr);
+
+	return 0;
+}
+
+static void snd_rme9652_set_defaults(rme9652_t *rme9652)
+{
+	unsigned int k;
+
+	/* ASSUMPTION: rme9652->lock is either held, or
+	   there is no need to hold it (e.g. during module
+	   initalization).
+	 */
+
+	/* set defaults:
+
+	   SPDIF Input via Coax 
+	   autosync clock mode
+	   maximum latency (7 = 8192 samples, 64Kbyte buffer,
+	   which implies 2 4096 sample, 32Kbyte periods).
+	   
+	   if rev 1.5, initialize the S/PDIF receiver.
+
+	 */
+
+	rme9652->control_register =
+	    RME9652_inp_0 | rme9652_encode_latency(7);
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	rme9652_reset_hw_pointer(rme9652);
+	rme9652_compute_period_size(rme9652);
+
+	/* default: thru off for all channels */
+
+	for (k = 0; k < RME9652_NCHANNELS; ++k)
+		rme9652_write(rme9652, RME9652_thru_base + k * 4, 0);
+
+	rme9652->thru_bits = 0;
+	rme9652->passthru = 0;
+
+	/* set a default rate so that the channel map is set up */
+
+	rme9652_set_rate(rme9652, 48000);
+}
+
+void snd_rme9652_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	rme9652_t *rme9652 = (rme9652_t *) dev_id;
+
+	if (!(rme9652_read(rme9652, RME9652_status_register) & RME9652_IRQ)) {
+		return;
+	}
+
+	rme9652_write(rme9652, RME9652_irq_clear, 0);
+
+	if (rme9652->capture_substream) {
+		snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+	}
+
+	if (rme9652->playback_substream) {
+		snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
+	}
+}
+
+static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	return rme9652_hw_pointer(rme9652);
+}
+
+static char *rme9652_channel_buffer_location(rme9652_t *rme9652,
+					     int stream,
+					     int channel)
+
+{
+	int mapped_channel;
+
+        snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL);
+        
+	if ((mapped_channel = rme9652->channel_map[channel]) < 0) {
+		return NULL;
+	}
+	
+	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+		return rme9652->capture_buffer +
+			(mapped_channel * RME9652_CHANNEL_BUFFER_BYTES);
+	} else {
+		return rme9652->playback_buffer +
+			(mapped_channel * RME9652_CHANNEL_BUFFER_BYTES);
+	}
+}
+
+static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel,
+				     snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	char *channel_buf;
+
+	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+
+	channel_buf = rme9652_channel_buffer_location (rme9652,
+						       substream->pstr->stream,
+						       channel);
+	snd_assert(channel_buf != NULL, return -EIO);
+	copy_from_user(channel_buf + pos * 4, src, count * 4);
+	return count;
+}
+
+static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel,
+				    snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	char *channel_buf;
+
+	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+
+	channel_buf = rme9652_channel_buffer_location (rme9652,
+						       substream->pstr->stream,
+						       channel);
+	snd_assert(channel_buf != NULL, return -EIO);
+	copy_to_user(dst, channel_buf + pos * 4, count * 4);
+	return count;
+}
+
+static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel,
+				  snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	char *channel_buf;
+
+	channel_buf = rme9652_channel_buffer_location (rme9652,
+						       substream->pstr->stream,
+						       channel);
+	snd_assert(channel_buf != NULL, return -EIO);
+	memset(channel_buf + pos * 4, 0, count * 4);
+	return count;
+}
+
+static int snd_rme9652_reset(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	snd_pcm_substream_t *other;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		other = rme9652->capture_substream;
+	else
+		other = rme9652->playback_substream;
+	if (rme9652->running)
+		runtime->status->hw_ptr = rme9652_hw_pointer(rme9652);
+	else
+		runtime->status->hw_ptr = 0;
+	if (other) {
+		snd_pcm_substream_t *s = substream;
+		snd_pcm_runtime_t *oruntime = other->runtime;
+		do {
+			s = s->link_next;
+			if (s == other) {
+				oruntime->status->hw_ptr = runtime->status->hw_ptr;
+				break;
+			}
+		} while (s != substream);
+	}
+	return 0;
+}
+
+static int snd_rme9652_hw_params(snd_pcm_substream_t *substream,
+				 snd_pcm_hw_params_t *params)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	int err;
+	pid_t this_pid;
+	pid_t other_pid;
+
+	spin_lock_irq(&rme9652->lock);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+		rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream);
+		this_pid = rme9652->playback_pid;
+		other_pid = rme9652->capture_pid;
+	} else {
+		this_pid = rme9652->capture_pid;
+		other_pid = rme9652->playback_pid;
+	}
+
+	if ((other_pid > 0) && (this_pid != other_pid)) {
+
+		/* The other stream is open, and not by the same
+		   task as this one. Make sure that the parameters
+		   that matter are the same.
+		 */
+
+		if (params_rate(params) !=
+		    rme9652_adat_sample_rate(rme9652)) {
+			spin_unlock_irq(&rme9652->lock);
+			_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+			return -EBUSY;
+		}
+
+		if (params_period_size(params) != rme9652->period_bytes / 4) {
+			spin_unlock_irq(&rme9652->lock);
+			_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+			return -EBUSY;
+		}
+
+		/* We're fine. */
+
+		spin_unlock_irq(&rme9652->lock);
+ 		return 0;
+
+	} else {
+		spin_unlock_irq(&rme9652->lock);
+	}
+
+	/* how to make sure that the rate matches an externally-set one ?
+	 */
+
+	if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) {
+		_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+		return err;
+	}
+
+	if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) {
+		_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+		return err;
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_channel_info(snd_pcm_substream_t *substream,
+				    snd_pcm_channel_info_t *info)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	int chn;
+
+	snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL);
+
+	if ((chn = rme9652->channel_map[info->channel]) < 0) {
+		return -EINVAL;
+	}
+
+	info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES;
+	info->first = 0;
+	info->step = 32;
+	return 0;
+}
+
+static int snd_rme9652_ioctl(snd_pcm_substream_t *substream,
+			     unsigned int cmd, void *arg)
+{
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_RESET:
+	{
+		return snd_rme9652_reset(substream);
+	}
+	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+	{
+		snd_pcm_channel_info_t *info = arg;
+		return snd_rme9652_channel_info(substream, info);
+	}
+	default:
+		break;
+	}
+
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static void rme9652_silence_playback(rme9652_t *rme9652)
+{
+	memset(rme9652->playback_buffer, 0, RME9652_DMA_AREA_BYTES);
+}
+
+static int snd_rme9652_trigger(snd_pcm_substream_t *substream,
+			       int cmd)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	snd_pcm_substream_t *other;
+	int running;
+	spin_lock(&rme9652->lock);
+	running = rme9652->running;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		running |= 1 << substream->stream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		running &= ~(1 << substream->stream);
+		break;
+	default:
+		snd_BUG();
+		spin_unlock(&rme9652->lock);
+		return -EINVAL;
+	}
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		other = rme9652->capture_substream;
+	else
+		other = rme9652->playback_substream;
+
+	if (other) {
+		snd_pcm_substream_t *s = substream;
+		do {
+			s = s->link_next;
+			if (s == other) {
+				snd_pcm_trigger_done(s, substream);
+				if (cmd == SNDRV_PCM_TRIGGER_START)
+					running |= 1 << s->stream;
+				else
+					running &= ~(1 << s->stream);
+				goto _ok;
+			}
+		} while (s != substream);
+		if (cmd == SNDRV_PCM_TRIGGER_START) {
+			if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+			    substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+				rme9652_silence_playback(rme9652);
+		} else {
+			if (running &&
+			    substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+				rme9652_silence_playback(rme9652);
+		}
+	}
+ _ok:
+	snd_pcm_trigger_done(substream, substream);
+	if (!rme9652->running && running)
+		rme9652_start(rme9652);
+	else if (rme9652->running && !running)
+		rme9652_stop(rme9652);
+	rme9652->running = running;
+	spin_unlock(&rme9652->lock);
+
+	return 0;
+}
+
+static int snd_rme9652_prepare(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock_irq(&rme9652->lock);
+	if (!rme9652->running)
+		rme9652_reset_hw_pointer(rme9652);
+	spin_unlock_irq(&rme9652->lock);
+	return result;
+}
+
+static snd_pcm_hardware_t snd_rme9652_playback_subinfo =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_DOUBLE),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			(SNDRV_PCM_RATE_44100 | 
+				 SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | 
+				 SNDRV_PCM_RATE_96000),
+	rate_min:		44100,
+	rate_max:		96000,
+	channels_min:		10,
+	channels_max:		26,
+	buffer_bytes_max:	1024*1024,
+	period_bytes_min:	1,
+	period_bytes_max:	1024*1024,
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_rme9652_capture_subinfo =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			(SNDRV_PCM_RATE_44100 | 
+				 SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | 
+				 SNDRV_PCM_RATE_96000),
+	rate_min:		44100,
+	rate_max:		96000,
+	channels_min:		10,
+	channels_max:		26,
+	buffer_bytes_max:	1024*1024,
+	period_bytes_min:	1,
+	period_bytes_max:	1024*1024,
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+
+static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+
+#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
+	count: PERIOD_SIZES,
+	list: period_sizes,
+	mask: 0
+};
+
+static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params,
+					snd_pcm_hw_rule_t *rule)
+{
+	rme9652_t *rme9652 = rule->private;
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	unsigned int list[2] = { rme9652->ds_channels, rme9652->ss_channels };
+	return snd_interval_list(c, 2, list, 0);
+}
+
+static int snd_rme9652_hw_rule_channels_rate(snd_pcm_hw_params_t *params,
+					     snd_pcm_hw_rule_t *rule)
+{
+	rme9652_t *rme9652 = rule->private;
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	if (r->min > 48000) {
+		snd_interval_t t = {
+			min: rme9652->ds_channels,
+			max: rme9652->ds_channels,
+			integer: 1,
+		};
+		return snd_interval_refine(c, &t);
+	} else if (r->max < 88200) {
+		snd_interval_t t = {
+			min: rme9652->ss_channels,
+			max: rme9652->ss_channels,
+			integer: 1,
+		};
+		return snd_interval_refine(c, &t);
+	}
+	return 0;
+}
+
+static int snd_rme9652_hw_rule_rate_channels(snd_pcm_hw_params_t *params,
+					     snd_pcm_hw_rule_t *rule)
+{
+	rme9652_t *rme9652 = rule->private;
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	if (c->min >= rme9652->ss_channels) {
+		snd_interval_t t = {
+			min: 44100,
+			max: 48000,
+			integer: 1,
+		};
+		return snd_interval_refine(r, &t);
+	} else if (c->max <= rme9652->ds_channels) {
+		snd_interval_t t = {
+			min: 88200,
+			max: 96000,
+			integer: 1,
+		};
+		return snd_interval_refine(r, &t);
+	}
+	return 0;
+}
+
+static int snd_rme9652_playback_open(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	snd_pcm_set_sync(substream);
+
+        runtime->hw = snd_rme9652_playback_subinfo;
+	runtime->dma_area = rme9652->playback_buffer;
+	runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+
+	if (rme9652->capture_substream == NULL) {
+		rme9652_stop(rme9652);
+		rme9652_set_thru(rme9652, -1, 0);
+	}
+
+	rme9652->playback_pid = current->pid;
+	rme9652->playback_substream = substream;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels_rate, rme9652,
+			     SNDRV_PCM_HW_PARAM_RATE, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+			     snd_rme9652_hw_rule_rate_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+	rme9652->creg_spdif_stream = rme9652->creg_spdif;
+	rme9652->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id);
+	return 0;
+}
+
+static int snd_rme9652_playback_release(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	rme9652->playback_pid = -1;
+	rme9652->playback_substream = NULL;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+
+	rme9652->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id);
+	return 0;
+}
+
+
+static int snd_rme9652_capture_open(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	snd_pcm_set_sync(substream);
+
+	runtime->hw = snd_rme9652_capture_subinfo;
+	runtime->dma_area = rme9652->capture_buffer;
+	runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+
+	if (rme9652->playback_substream == NULL) {
+		rme9652_stop(rme9652);
+		rme9652_set_thru(rme9652, -1, 0);
+	}
+
+	rme9652->capture_pid = current->pid;
+	rme9652->capture_substream = substream;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels_rate, rme9652,
+			     SNDRV_PCM_HW_PARAM_RATE, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+			     snd_rme9652_hw_rule_rate_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	return 0;
+}
+
+static int snd_rme9652_capture_release(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	rme9652->capture_pid = -1;
+	rme9652->capture_substream = NULL;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_rme9652_playback_ops = {
+	open:		snd_rme9652_playback_open,
+	close:		snd_rme9652_playback_release,
+	ioctl:		snd_rme9652_ioctl,
+	hw_params:	snd_rme9652_hw_params,
+	prepare:	snd_rme9652_prepare,
+	trigger:	snd_rme9652_trigger,
+	pointer:	snd_rme9652_hw_pointer,
+	copy:		snd_rme9652_playback_copy,
+	silence:	snd_rme9652_hw_silence,
+};
+
+static snd_pcm_ops_t snd_rme9652_capture_ops = {
+	open:		snd_rme9652_capture_open,
+	close:		snd_rme9652_capture_release,
+	ioctl:		snd_rme9652_ioctl,
+	hw_params:	snd_rme9652_hw_params,
+	prepare:	snd_rme9652_prepare,
+	trigger:	snd_rme9652_trigger,
+	pointer:	snd_rme9652_hw_pointer,
+	copy:		snd_rme9652_capture_copy,
+};
+
+static int __init snd_rme9652_create_pcm(snd_card_t *card,
+					 rme9652_t *rme9652)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(card,
+			       rme9652->card_name,
+			       0, 1, 1, &pcm)) < 0) {
+		return err;
+	}
+
+	rme9652->pcm = pcm;
+	pcm->private_data = rme9652;
+	strcpy(pcm->name, rme9652->card_name);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops);
+
+	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+	return 0;
+}
+
+static int __init snd_rme9652_create(snd_card_t *card,
+				     rme9652_t *rme9652,
+				     int precise_ptr)
+{
+	struct pci_dev *pci = rme9652->pci;
+	int err;
+	int status;
+	unsigned short rev;
+
+	rme9652->irq = -1;
+	rme9652->card = card;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	spin_lock_init(&rme9652->lock);
+
+	rme9652->port = pci_resource_start(pci, 0);
+	if ((rme9652->res_port = request_mem_region(rme9652->port, RME9652_IO_EXTENT, "rme9652")) == NULL) {
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	rme9652->irq = pci->irq;
+
+	rme9652->iobase = (unsigned long) ioremap_nocache(rme9652->port, RME9652_IO_EXTENT);
+	if (rme9652->iobase == 0) {
+		snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
+		return -EBUSY;
+	}
+	
+	rme9652->precise_ptr = precise_ptr;
+
+	/* Determine the h/w rev level of the card. This seems like
+	   a particularly kludgy way to encode it, but its what RME
+	   chose to do, so we follow them ...
+	*/
+
+	status = rme9652_read(rme9652, RME9652_status_register);
+	if (rme9652_decode_spdif_rate(status&RME9652_F) == 1) {
+		rme9652->hw_rev = 15;
+	} else {
+		rme9652->hw_rev = 11;
+	}
+
+	/* Differentiate between the standard Hammerfall, and the
+	   "Light", which does not have the expansion board. This
+	   method comes from information received from Mathhias
+	   Clausen at RME. Display the EEPROM and h/w revID where
+	   relevant.  
+	*/
+
+	pci_read_config_word(rme9652->pci, PCI_CLASS_REVISION, &rev);
+	strcpy(card->driver, "RME9652");
+	switch (rev & 0xff) {
+	case 8: /* original eprom */
+		strcpy(card->driver, "RME9636");
+		if (rme9652->hw_rev == 15) {
+			rme9652->card_name = "RME Digi9636 (Rev 1.5)";
+		} else {
+			rme9652->card_name = "RME Digi9636";
+		}
+		rme9652->ss_channels = RME9636_NCHANNELS;
+		break;
+	case 9: /* W36_G EPROM */
+		strcpy(card->driver, "RME9636");
+		rme9652->card_name = "RME Digi9636 (Rev G)";
+		rme9652->ss_channels = RME9636_NCHANNELS;
+		break;
+	case 4: /* W52_G EPROM */
+		rme9652->card_name = "RME Digi9652 (Rev G)";
+		rme9652->ss_channels = RME9652_NCHANNELS;
+		break;
+	default:
+	case 3: /* original eprom */
+		if (rme9652->hw_rev == 15) {
+			rme9652->card_name = "RME Digi9652 (Rev 1.5)";
+		} else {
+			rme9652->card_name = "RME Digi9652";
+		}
+		rme9652->ss_channels = RME9652_NCHANNELS;
+		break;
+	}
+
+	rme9652->ds_channels = (rme9652->ss_channels - 2) / 2 + 2;
+
+	pci_set_master(rme9652->pci);
+
+	if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) {
+		return err;
+	}
+
+	if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) {
+		return err;
+	}
+
+	if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) {
+		return err;
+	}
+
+	snd_rme9652_proc_init(rme9652);
+
+	rme9652->last_spdif_sample_rate = -1;
+	rme9652->last_adat_sample_rate = -1;
+	rme9652->playback_pid = -1;
+	rme9652->capture_pid = -1;
+	rme9652->capture_substream = NULL;
+	rme9652->playback_substream = NULL;
+
+	snd_rme9652_set_defaults(rme9652);
+
+	if (rme9652->hw_rev == 15) {
+		rme9652_initialize_spdif_receiver (rme9652);
+	}
+
+	return 0;
+}
+
+static void snd_rme9652_card_free(snd_card_t *card)
+{
+	rme9652_t *rme9652 = (rme9652_t *) card->private_data;
+
+	if (rme9652)
+		snd_rme9652_free(rme9652);
+}
+
+static int __devinit snd_rme9652_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev = 0;
+	rme9652_t *rme9652;
+	snd_card_t *card;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(rme9652_t));
+
+	if (!card)
+		return -ENOMEM;
+
+	rme9652 = (rme9652_t *) card->private_data;
+	card->private_free = snd_rme9652_card_free;
+	rme9652->dev = dev;
+	rme9652->pci = pci;
+
+	if ((err = snd_rme9652_create(card, rme9652, snd_precise_ptr[dev])) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->shortname, rme9652->card_name);
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, rme9652->port, rme9652->irq);
+
+	
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_rme9652_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name:"RME Digi9652 (Hammerfall)",
+	id_table:snd_rme9652_ids,
+	probe:snd_rme9652_probe,
+	remove:__devexit_p(snd_rme9652_remove),
+};
+
+static int __init alsa_card_hammerfall_init(void)
+{
+	if (pci_module_init(&driver) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "RME Digi9652/Digi9636: no cards found\n");
+#endif
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit alsa_card_hammerfall_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_hammerfall_init)
+module_exit(alsa_card_hammerfall_exit)
+
+#ifndef MODULE
+
+/* format is: snd-rme9652=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_rme9652_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-rme9652=", alsa_card_rme9652_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/rme9652/rme9652_mem.c linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652_mem.c
--- linux/sound/pci/rme9652/rme9652_mem.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652_mem.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,278 @@
+/* 
+    ALSA memory allocation module for the RME Digi9652
+  
+ 	Copyright(c) 1999 IEM - Winfried Ritsch
+        Copyright (C) 1999 Paul Barton-Davis 
+
+    This module is only needed if you compiled the rme9652 driver with
+    the PREALLOCATE_MEMORY option. It allocates the memory need to
+    run the board and holds it until the module is unloaded. Because
+    we need 2 contiguous 1.6MB regions for the board, it can be
+    a problem getting them once the system memory has become fairly
+    fragmented. 
+
+    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
+
+    $Id: rme9652_mem.c,v 1.6 2002/02/04 10:21:33 tiwai Exp $
+
+
+    Tue Oct 17 2000  Jaroslav Kysela <perex@suse.cz>
+    	* space is allocated only for physical devices
+        * added support for 2.4 kernels (pci_alloc_consistent)
+    
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <sound/initval.h>
+
+#define RME9652_CARDS			8
+#define RME9652_CHANNEL_BUFFER_SAMPLES  (16*1024)
+#define RME9652_CHANNEL_BUFFER_BYTES    (4*RME9652_CHANNEL_BUFFER_SAMPLES)
+
+/* export */
+
+static int snd_enable[8] = {1,1,1,1,1,1,1,1};
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(RME9652_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable cards to allocate buffers for.");
+
+MODULE_AUTHOR("Winfried Ritsch, Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION("Memory allocator for RME Hammerfall");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+
+/* Since we don't know at this point if we're allocating memory for a
+   Hammerfall or a Hammerfall/Light, assume the worst and allocate
+   space for the maximum number of channels.
+		   
+   See note in rme9652.h about why we allocate for an extra channel.  
+*/
+
+#define TOTAL_SIZE (26+1)*(RME9652_CHANNEL_BUFFER_BYTES)
+#define NBUFS   2*RME9652_CARDS
+
+#define RME9652_BUF_ALLOCATED 0x1
+#define RME9652_BUF_USED      0x2
+
+typedef struct rme9652_buf_stru rme9652_buf_t;
+
+struct rme9652_buf_stru {
+	struct pci_dev *pci;
+	void *buf;
+	dma_addr_t addr;
+	char flags;
+};
+
+static rme9652_buf_t rme9652_buffers[NBUFS];
+
+/* These are here so that we have absolutely no dependencies on any
+   other modules. Dependencies can (1) cause us to lose in the rush
+   for 2x 1.6MB chunks of contiguous memory and (2) make driver
+   debugging difficult because unloading and reloading the snd module
+   causes us to have to do the same for this one. Since on 2.2
+   kernels, and before, we can rarely if ever allocate memory after
+   starting things running, this would be bad.
+*/
+
+/* remove hack for pci_alloc_consistent to avoid dependecy on snd module */
+#ifdef HACK_PCI_ALLOC_CONSISTENT
+#undef pci_alloc_consistent
+#endif
+
+static void *rme9652_malloc_pages(struct pci_dev *pci,
+				  unsigned long size,
+				  dma_addr_t *dmaaddr)
+{
+	void *res;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0)
+	res = (void *) pci_alloc_consistent(pci, size, dmaaddr);
+#else
+	int pg;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	res = (void *)__get_free_pages(GFP_KERNEL, pg);
+	if (res != NULL)
+		*dmaaddr = virt_to_bus(res);
+#endif
+	if (res != NULL) {
+		mem_map_t *page = virt_to_page(res);
+		mem_map_t *last_page = page + (size + PAGE_SIZE - 1) / PAGE_SIZE;
+		while (page < last_page)
+			set_bit(PG_reserved, &(page++)->flags);
+	}
+	return res;
+}
+
+static void rme9652_free_pages(struct pci_dev *pci, unsigned long size,
+			       void *ptr, dma_addr_t dmaaddr)
+{
+	mem_map_t *page, *last_page;
+
+	if (ptr == NULL)
+		return;
+	page = virt_to_page(ptr);
+	last_page = virt_to_page(ptr) + (size + PAGE_SIZE - 1) / PAGE_SIZE;
+	while (page < last_page)
+		clear_bit(PG_reserved, &(page++)->flags);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0)
+	pci_free_consistent(pci, size, ptr, dmaaddr);
+#else
+	{
+		int pg;
+		for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+		if (bus_to_virt(dmaaddr) != ptr) {
+			printk(KERN_ERR "rme9652_free_pages: dmaaddr != ptr\n");
+			return;
+		}
+		free_pages((unsigned long)ptr, pg);
+	}
+#endif
+}
+
+void *snd_rme9652_get_buffer (int card, dma_addr_t *dmaaddr)
+
+{
+	int i;
+	rme9652_buf_t *rbuf;
+
+	if (card < 0 || card >= RME9652_CARDS) {
+		printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card);
+		return NULL;
+	}
+	for (i = card * 2; i < card * 2 + 2; i++) {
+		rbuf = &rme9652_buffers[i];
+		if (rbuf->flags == RME9652_BUF_ALLOCATED) {
+			rbuf->flags |= RME9652_BUF_USED;
+			MOD_INC_USE_COUNT;
+			*dmaaddr = rbuf->addr;
+			return rbuf->buf;
+		}
+	}
+
+	return NULL;
+}
+
+void snd_rme9652_free_buffer (int card, void *addr)
+
+{
+	int i;
+	rme9652_buf_t *rbuf;
+
+	if (card < 0 || card >= RME9652_CARDS) {
+		printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card);
+		return;
+	}
+	for (i = card * 2; i < card * 2 + 2; i++) {
+		rbuf = &rme9652_buffers[i];
+		if (rbuf->buf == addr) {
+			MOD_DEC_USE_COUNT;
+			rbuf->flags &= ~RME9652_BUF_USED;
+			return;
+		}
+	}
+
+	printk ("RME9652 memory allocator: unknown buffer address passed to free buffer");
+}
+
+static void __exit rme9652_free_buffers (void)
+
+{
+	int i;
+	rme9652_buf_t *rbuf;
+
+	for (i = 0; i < NBUFS; i++) {
+
+		/* We rely on general module code to prevent
+		   us from being unloaded with buffers in use.
+
+		   However, not quite. Do not release memory
+		   if it is still marked as in use. This might
+		   be unnecessary.
+		*/
+
+		rbuf = &rme9652_buffers[i];
+
+		if (rbuf->flags == RME9652_BUF_ALLOCATED) {
+			rme9652_free_pages (rbuf->pci, TOTAL_SIZE, rbuf->buf, rbuf->addr);
+			rbuf->buf = NULL;
+			rbuf->flags = 0;
+		}
+	}
+}				 
+
+static int __init alsa_rme9652_mem_init(void)
+{
+	int i;
+	struct pci_dev *pci;
+	rme9652_buf_t *rbuf;
+
+	/* make sure our buffer records are clean */
+
+	for (i = 0; i < NBUFS; i++) {
+		rbuf = &rme9652_buffers[i];
+		rbuf->pci = NULL;
+		rbuf->buf = NULL;
+		rbuf->flags = 0;
+	}
+
+	/* ensure sane values for the number of buffers */
+
+	/* Remember: 2 buffers per card, one for capture, one for
+	   playback.
+	*/
+	
+	i = 0;	/* card number */
+	rbuf = rme9652_buffers;
+	pci_for_each_dev(pci) {
+		int k;
+		if (pci->vendor != 0x10ee || pci->device != 0x3fc4)
+			continue;
+
+		if (!snd_enable[i])
+			continue;
+
+		for (k = 0; k < 2; ++k) {
+			rbuf->buf = rme9652_malloc_pages(pci, TOTAL_SIZE, &rbuf->addr);
+			if (rbuf->buf == NULL) {
+				rme9652_free_buffers();
+				printk(KERN_ERR "RME9652 memory allocator: no memory available for card %d buffer %d\n", i, k + 1);
+				return -ENOMEM;
+			}
+			rbuf->flags = RME9652_BUF_ALLOCATED;
+			rbuf++;
+		}
+		i++;
+	}
+
+	if (i == 0)
+		printk(KERN_ERR "RME9652 memory allocator: no RME9652 card found...\n");
+	
+	return 0;
+}
+
+static void __exit alsa_rme9652_mem_exit(void)
+{
+	rme9652_free_buffers();
+}
+
+module_init(alsa_rme9652_mem_init)
+module_exit(alsa_rme9652_mem_exit)
+
+EXPORT_SYMBOL(snd_rme9652_get_buffer);
+EXPORT_SYMBOL(snd_rme9652_free_buffer);
diff -Nru linux/sound/pci/sonicvibes.c linux-2.4.19-pre5-mjc/sound/pci/sonicvibes.c
--- linux/sound/pci/sonicvibes.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/sonicvibes.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1592 @@
+/*
+ *  Driver for S3 SonicVibes soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *  BUGS:
+ *    It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB?
+ *    Driver sometimes hangs... Nobody knows why at this moment...
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#ifndef LINUX_2_2
+#include <linux/gameport.h>
+#endif
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("S3 SonicVibes PCI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{S3,SonicVibes PCI}}");
+
+#ifndef PCI_VENDOR_ID_S3
+#define PCI_VENDOR_ID_S3             0x5333
+#endif
+#ifndef PCI_DEVICE_ID_S3_SONICVIBES
+#define PCI_DEVICE_ID_S3_SONICVIBES  0xca00
+#endif
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static unsigned int snd_dmaio = 0x7a00;	/* DDMA i/o address */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_reverb, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_reverb, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mge, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mge, "MIC Gain Enable for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_mge, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_dmaio, "i");
+MODULE_PARM_DESC(snd_dmaio, "DDMA i/o base address for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_dmaio, "global," SNDRV_PORT_DESC);
+
+/*
+ * Enhanced port direct registers
+ */
+
+#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x)
+
+#define SV_REG_CONTROL	0x00	/* R/W: CODEC/Mixer control register */
+#define   SV_ENHANCED	  0x01	/* audio mode select - enhanced mode */
+#define   SV_TEST	  0x02	/* test bit */
+#define   SV_REVERB	  0x04	/* reverb enable */
+#define   SV_WAVETABLE	  0x08	/* wavetable active / FM active if not set */
+#define   SV_INTA	  0x20	/* INTA driving - should be always 1 */
+#define   SV_RESET	  0x80	/* reset chip */
+#define SV_REG_IRQMASK	0x01	/* R/W: CODEC/Mixer interrupt mask register */
+#define   SV_DMAA_MASK	  0x01	/* mask DMA-A interrupt */
+#define   SV_DMAC_MASK	  0x04	/* mask DMA-C interrupt */
+#define   SV_SPEC_MASK	  0x08	/* special interrupt mask - should be always masked */
+#define   SV_UD_MASK	  0x40	/* Up/Down button interrupt mask */
+#define   SV_MIDI_MASK	  0x80	/* mask MIDI interrupt */
+#define SV_REG_STATUS	0x02	/* R/O: CODEC/Mixer status register */
+#define   SV_DMAA_IRQ	  0x01	/* DMA-A interrupt */
+#define   SV_DMAC_IRQ	  0x04	/* DMA-C interrupt */
+#define   SV_SPEC_IRQ	  0x08	/* special interrupt */
+#define   SV_UD_IRQ	  0x40	/* Up/Down interrupt */
+#define   SV_MIDI_IRQ	  0x80	/* MIDI interrupt */
+#define SV_REG_INDEX	0x04	/* R/W: CODEC/Mixer index address register */
+#define   SV_MCE          0x40	/* mode change enable */
+#define   SV_TRD	  0x80	/* DMA transfer request disabled */
+#define SV_REG_DATA	0x05	/* R/W: CODEC/Mixer index data register */
+
+/*
+ * Enhanced port indirect registers
+ */
+
+#define SV_IREG_LEFT_ADC	0x00	/* Left ADC Input Control */
+#define SV_IREG_RIGHT_ADC	0x01	/* Right ADC Input Control */
+#define SV_IREG_LEFT_AUX1	0x02	/* Left AUX1 Input Control */
+#define SV_IREG_RIGHT_AUX1	0x03	/* Right AUX1 Input Control */
+#define SV_IREG_LEFT_CD		0x04	/* Left CD Input Control */
+#define SV_IREG_RIGHT_CD	0x05	/* Right CD Input Control */
+#define SV_IREG_LEFT_LINE	0x06	/* Left Line Input Control */
+#define SV_IREG_RIGHT_LINE	0x07	/* Right Line Input Control */
+#define SV_IREG_MIC		0x08	/* MIC Input Control */
+#define SV_IREG_GAME_PORT	0x09	/* Game Port Control */
+#define SV_IREG_LEFT_SYNTH	0x0a	/* Left Synth Input Control */
+#define SV_IREG_RIGHT_SYNTH	0x0b	/* Right Synth Input Control */
+#define SV_IREG_LEFT_AUX2	0x0c	/* Left AUX2 Input Control */
+#define SV_IREG_RIGHT_AUX2	0x0d	/* Right AUX2 Input Control */
+#define SV_IREG_LEFT_ANALOG	0x0e	/* Left Analog Mixer Output Control */
+#define SV_IREG_RIGHT_ANALOG	0x0f	/* Right Analog Mixer Output Control */
+#define SV_IREG_LEFT_PCM	0x10	/* Left PCM Input Control */
+#define SV_IREG_RIGHT_PCM	0x11	/* Right PCM Input Control */
+#define SV_IREG_DMA_DATA_FMT	0x12	/* DMA Data Format */
+#define SV_IREG_PC_ENABLE	0x13	/* Playback/Capture Enable Register */
+#define SV_IREG_UD_BUTTON	0x14	/* Up/Down Button Register */
+#define SV_IREG_REVISION	0x15	/* Revision */
+#define SV_IREG_ADC_OUTPUT_CTRL	0x16	/* ADC Output Control */
+#define SV_IREG_DMA_A_UPPER	0x18	/* DMA A Upper Base Count */
+#define SV_IREG_DMA_A_LOWER	0x19	/* DMA A Lower Base Count */
+#define SV_IREG_DMA_C_UPPER	0x1c	/* DMA C Upper Base Count */
+#define SV_IREG_DMA_C_LOWER	0x1d	/* DMA C Lower Base Count */
+#define SV_IREG_PCM_RATE_LOW	0x1e	/* PCM Sampling Rate Low Byte */
+#define SV_IREG_PCM_RATE_HIGH	0x1f	/* PCM Sampling Rate High Byte */
+#define SV_IREG_SYNTH_RATE_LOW	0x20	/* Synthesizer Sampling Rate Low Byte */
+#define SV_IREG_SYNTH_RATE_HIGH 0x21	/* Synthesizer Sampling Rate High Byte */
+#define SV_IREG_ADC_CLOCK	0x22	/* ADC Clock Source Selection */
+#define SV_IREG_ADC_ALT_RATE	0x23	/* ADC Alternative Sampling Rate Selection */
+#define SV_IREG_ADC_PLL_M	0x24	/* ADC PLL M Register */
+#define SV_IREG_ADC_PLL_N	0x25	/* ADC PLL N Register */
+#define SV_IREG_SYNTH_PLL_M	0x26	/* Synthesizer PLL M Register */
+#define SV_IREG_SYNTH_PLL_N	0x27	/* Synthesizer PLL N Register */
+#define SV_IREG_MPU401		0x2a	/* MPU-401 UART Operation */
+#define SV_IREG_DRIVE_CTRL	0x2b	/* Drive Control */
+#define SV_IREG_SRS_SPACE	0x2c	/* SRS Space Control */
+#define SV_IREG_SRS_CENTER	0x2d	/* SRS Center Control */
+#define SV_IREG_WAVE_SOURCE	0x2e	/* Wavetable Sample Source Select */
+#define SV_IREG_ANALOG_POWER	0x30	/* Analog Power Down Control */
+#define SV_IREG_DIGITAL_POWER	0x31	/* Digital Power Down Control */
+
+#define SV_IREG_ADC_PLL		SV_IREG_ADC_PLL_M
+#define SV_IREG_SYNTH_PLL	SV_IREG_SYNTH_PLL_M
+
+/*
+ *  DMA registers
+ */
+
+#define SV_DMA_ADDR0		0x00
+#define SV_DMA_ADDR1		0x01
+#define SV_DMA_ADDR2		0x02
+#define SV_DMA_ADDR3		0x03
+#define SV_DMA_COUNT0		0x04
+#define SV_DMA_COUNT1		0x05
+#define SV_DMA_COUNT2		0x06
+#define SV_DMA_MODE		0x0b
+#define SV_DMA_RESET		0x0d
+#define SV_DMA_MASK		0x0f
+
+/*
+ *  Record sources
+ */
+
+#define SV_RECSRC_RESERVED	(0x00<<5)
+#define SV_RECSRC_CD		(0x01<<5)
+#define SV_RECSRC_DAC		(0x02<<5)
+#define SV_RECSRC_AUX2		(0x03<<5)
+#define SV_RECSRC_LINE		(0x04<<5)
+#define SV_RECSRC_AUX1		(0x05<<5)
+#define SV_RECSRC_MIC		(0x06<<5)
+#define SV_RECSRC_OUT		(0x07<<5)
+
+/*
+ *  constants
+ */
+
+#define SV_FULLRATE		48000
+#define SV_REFFREQUENCY		24576000
+#define SV_ADCMULT		512
+
+#define SV_MODE_PLAY		1
+#define SV_MODE_CAPTURE		2
+
+/*
+
+ */
+
+typedef struct _snd_sonicvibes sonicvibes_t;
+#define chip_t sonicvibes_t
+
+struct _snd_sonicvibes {
+	unsigned long dma1size;
+	unsigned long dma2size;
+	int irq;
+
+	unsigned long sb_port;
+	struct resource *res_sb_port;
+	unsigned long enh_port;
+	struct resource *res_enh_port;
+	unsigned long synth_port;
+	struct resource *res_synth_port;
+	unsigned long midi_port;
+	struct resource *res_midi_port;
+	unsigned long game_port;
+	unsigned int dmaa_port;
+	struct resource *res_dmaa;
+	unsigned int dmac_port;
+	struct resource *res_dmac;
+
+	unsigned char enable;
+	unsigned char irqmask;
+	unsigned char revision;
+	unsigned char format;
+	unsigned char srs_space;
+	unsigned char srs_center;
+	unsigned char mpu_switch;
+	unsigned char wave_source;
+
+	unsigned int mode;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_rawmidi_t *rmidi;
+	snd_hwdep_t *fmsynth;	/* S3FM */
+
+	spinlock_t reg_lock;
+	snd_info_entry_t *proc_entry;
+
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	snd_kcontrol_t *master_mute;
+	snd_kcontrol_t *master_volume;
+
+#ifndef LINUX_2_2
+	struct gameport gameport;
+#endif
+};
+
+static struct pci_device_id snd_sonic_ids[] __devinitdata = {
+	{ 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+        { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_sonic_ids);
+
+static ratden_t sonicvibes_adc_clock = {
+	num_min: 4000 * 65536,
+	num_max: 48000UL * 65536,
+	num_step: 1,
+	den: 65536,
+};
+static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = {
+	nrats: 1,
+	rats: &sonicvibes_adc_clock,
+};
+
+/*
+ *  common I/O routines
+ */
+
+static inline void snd_sonicvibes_setdmaa(sonicvibes_t * sonic,
+					  unsigned int addr,
+					  unsigned int count)
+{
+	count--;
+	outl(addr, sonic->dmaa_port + SV_DMA_ADDR0);
+	outl(count, sonic->dmaa_port + SV_DMA_COUNT0);
+	outb(0x18, sonic->dmaa_port + SV_DMA_MODE);
+#if 0
+	printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0));
+#endif
+}
+
+static inline void snd_sonicvibes_setdmac(sonicvibes_t * sonic,
+					  unsigned int addr,
+					  unsigned int count)
+{
+	/* note: dmac is working in word mode!!! */
+	count >>= 1;
+	count--;
+	outl(addr, sonic->dmac_port + SV_DMA_ADDR0);
+	outl(count, sonic->dmac_port + SV_DMA_COUNT0);
+	outb(0x14, sonic->dmac_port + SV_DMA_MODE);
+#if 0
+	printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0));
+#endif
+}
+
+static inline unsigned int snd_sonicvibes_getdmaa(sonicvibes_t * sonic)
+{
+	return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1;
+}
+
+static inline unsigned int snd_sonicvibes_getdmac(sonicvibes_t * sonic)
+{
+	/* note: dmac is working in word mode!!! */
+	return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1;
+}
+
+static void snd_sonicvibes_out1(sonicvibes_t * sonic,
+				unsigned char reg,
+				unsigned char value)
+{
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	outb(value, SV_REG(sonic, DATA));
+	udelay(10);
+}
+
+static void snd_sonicvibes_out(sonicvibes_t * sonic,
+			       unsigned char reg,
+			       unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	outb(value, SV_REG(sonic, DATA));
+	udelay(10);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static unsigned char snd_sonicvibes_in1(sonicvibes_t * sonic, unsigned char reg)
+{
+	unsigned char value;
+
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	value = inb(SV_REG(sonic, DATA));
+	udelay(10);
+	return value;
+}
+
+static unsigned char snd_sonicvibes_in(sonicvibes_t * sonic, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char value;
+
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	value = inb(SV_REG(sonic, DATA));
+	udelay(10);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return value;
+}
+
+#ifdef CONFIG_SND_DEBUG
+void snd_sonicvibes_debug(sonicvibes_t * sonic)
+{
+	printk("SV REGS:          INDEX = 0x%02x  ", inb(SV_REG(sonic, INDEX)));
+	printk("                 STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS)));
+	printk("  0x00: left input      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x00));
+	printk("  0x20: synth rate low  = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20));
+	printk("  0x01: right input     = 0x%02x  ", snd_sonicvibes_in(sonic, 0x01));
+	printk("  0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21));
+	printk("  0x02: left AUX1       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x02));
+	printk("  0x22: ADC clock       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22));
+	printk("  0x03: right AUX1      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x03));
+	printk("  0x23: ADC alt rate    = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23));
+	printk("  0x04: left CD         = 0x%02x  ", snd_sonicvibes_in(sonic, 0x04));
+	printk("  0x24: ADC pll M       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24));
+	printk("  0x05: right CD        = 0x%02x  ", snd_sonicvibes_in(sonic, 0x05));
+	printk("  0x25: ADC pll N       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25));
+	printk("  0x06: left line       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x06));
+	printk("  0x26: Synth pll M     = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26));
+	printk("  0x07: right line      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x07));
+	printk("  0x27: Synth pll N     = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27));
+	printk("  0x08: MIC             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x08));
+	printk("  0x28: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28));
+	printk("  0x09: Game port       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x09));
+	printk("  0x29: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29));
+	printk("  0x0a: left synth      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0a));
+	printk("  0x2a: MPU401          = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a));
+	printk("  0x0b: right synth     = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0b));
+	printk("  0x2b: drive ctrl      = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b));
+	printk("  0x0c: left AUX2       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0c));
+	printk("  0x2c: SRS space       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c));
+	printk("  0x0d: right AUX2      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0d));
+	printk("  0x2d: SRS center      = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d));
+	printk("  0x0e: left analog     = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0e));
+	printk("  0x2e: wave source     = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e));
+	printk("  0x0f: right analog    = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0f));
+	printk("  0x2f: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f));
+	printk("  0x10: left PCM        = 0x%02x  ", snd_sonicvibes_in(sonic, 0x10));
+	printk("  0x30: analog power    = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30));
+	printk("  0x11: right PCM       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x11));
+	printk("  0x31: analog power    = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31));
+	printk("  0x12: DMA data format = 0x%02x  ", snd_sonicvibes_in(sonic, 0x12));
+	printk("  0x32: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32));
+	printk("  0x13: P/C enable      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x13));
+	printk("  0x33: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33));
+	printk("  0x14: U/D button      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x14));
+	printk("  0x34: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34));
+	printk("  0x15: revision        = 0x%02x  ", snd_sonicvibes_in(sonic, 0x15));
+	printk("  0x35: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35));
+	printk("  0x16: ADC output ctrl = 0x%02x  ", snd_sonicvibes_in(sonic, 0x16));
+	printk("  0x36: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36));
+	printk("  0x17: ---             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x17));
+	printk("  0x37: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37));
+	printk("  0x18: DMA A upper cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x18));
+	printk("  0x38: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38));
+	printk("  0x19: DMA A lower cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x19));
+	printk("  0x39: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39));
+	printk("  0x1a: ---             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1a));
+	printk("  0x3a: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a));
+	printk("  0x1b: ---             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1b));
+	printk("  0x3b: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b));
+	printk("  0x1c: DMA C upper cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1c));
+	printk("  0x3c: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c));
+	printk("  0x1d: DMA C upper cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1d));
+	printk("  0x3d: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d));
+	printk("  0x1e: PCM rate low    = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1e));
+	printk("  0x3e: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e));
+	printk("  0x1f: PCM rate high   = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1f));
+	printk("  0x3f: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f));
+}
+
+#endif
+
+static void snd_sonicvibes_setfmt(sonicvibes_t * sonic,
+                                  unsigned char mask,
+                                  unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX));
+	if (mask) {
+		sonic->format = inb(SV_REG(sonic, DATA));
+		udelay(10);
+	}
+	sonic->format = (sonic->format & mask) | value;
+	outb(sonic->format, SV_REG(sonic, DATA));
+	udelay(10);
+	outb(0, SV_REG(sonic, INDEX));
+	udelay(10);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static void snd_sonicvibes_pll(unsigned int rate,
+			       unsigned int *res_r,
+			       unsigned int *res_m,
+			       unsigned int *res_n)
+{
+	unsigned int r, m = 0, n = 0;
+	unsigned int xm, xn, xr, xd, metric = ~0U;
+
+	if (rate < 625000 / SV_ADCMULT)
+		rate = 625000 / SV_ADCMULT;
+	if (rate > 150000000 / SV_ADCMULT)
+		rate = 150000000 / SV_ADCMULT;
+	/* slight violation of specs, needed for continuous sampling rates */
+	for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1);
+	for (xn = 3; xn < 33; xn++)	/* 35 */
+		for (xm = 3; xm < 257; xm++) {
+			xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn;
+			if (xr >= rate)
+				xd = xr - rate;
+			else
+				xd = rate - xr;
+			if (xd < metric) {
+				metric = xd;
+				m = xm - 2;
+				n = xn - 2;
+			}
+		}
+	*res_r = r;
+	*res_m = m;
+	*res_n = n;
+#if 0
+	printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
+	printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
+#endif
+}
+
+static void snd_sonicvibes_setpll(sonicvibes_t * sonic,
+                                  unsigned char reg,
+                                  unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int r, m, n;
+
+	snd_sonicvibes_pll(rate, &r, &m, &n);
+	if (sonic != NULL) {
+		spin_lock_irqsave(&sonic->reg_lock, flags);
+		snd_sonicvibes_out1(sonic, reg, m);
+		snd_sonicvibes_out1(sonic, reg + 1, r | n);
+		spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	}
+}
+
+static void snd_sonicvibes_set_adc_rate(sonicvibes_t * sonic, unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int div;
+	unsigned char clock;
+
+	div = 48000 / rate;
+	if (div > 8)
+		div = 8;
+	if ((48000 / div) == rate) {	/* use the alternate clock */
+		clock = 0x10;
+	} else {			/* use the PLL source */
+		clock = 0x00;
+		snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate);
+	}
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4);
+	snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static int snd_sonicvibes_hw_constraint_dac_rate(snd_pcm_hw_params_t *params,
+						 snd_pcm_hw_rule_t *rule)
+{
+	unsigned int rate, div, r, m, n;
+
+	if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min == 
+	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) {
+		rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min;
+		div = 48000 / rate;
+		if (div > 8)
+			div = 8;
+		if ((48000 / div) == rate) {
+			params->rate_num = rate;
+			params->rate_den = 1;
+		} else {
+			snd_sonicvibes_pll(rate, &r, &m, &n);
+			snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL);
+			snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL);
+			params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r;
+			params->rate_den = (SV_ADCMULT/512) * (m+2);
+		}
+	}
+	return 0;
+}
+
+static void snd_sonicvibes_set_dac_rate(sonicvibes_t * sonic, unsigned int rate)
+{
+	unsigned int div;
+	unsigned long flags;
+
+	div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE;
+	if (div > 65535)
+		div = 65535;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8);
+	snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static int snd_sonicvibes_trigger(sonicvibes_t * sonic, int what, int cmd)
+{
+	int result = 0;
+
+	spin_lock(&sonic->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (!(sonic->enable & what)) {
+			sonic->enable |= what;
+			snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
+		}
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (sonic->enable & what) {
+			sonic->enable &= ~what;
+			snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
+		}
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock(&sonic->reg_lock);
+	return result;
+}
+
+static void snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return);
+	unsigned char status;
+
+	status = inb(SV_REG(sonic, STATUS));
+	if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ)))
+		return;
+	if (status == 0xff) {	/* failure */
+		outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK));
+		snd_printk("IRQ failure - interrupts disabled!!\n");
+		return;
+	}
+	if (sonic->pcm) {
+		if (status & SV_DMAA_IRQ)
+			snd_pcm_period_elapsed(sonic->playback_substream);
+		if (status & SV_DMAC_IRQ)
+			snd_pcm_period_elapsed(sonic->capture_substream);
+	}
+	if (sonic->rmidi) {
+		if (status & SV_MIDI_IRQ)
+			snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data, regs);
+	}
+	if (status & SV_UD_IRQ) {
+		unsigned char udreg;
+		int vol, oleft, oright, mleft, mright;
+
+		spin_lock(&sonic->reg_lock);
+		udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON);
+		vol = udreg & 0x3f;
+		if (!(udreg & 0x40))
+			vol = -vol;
+		oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG);
+		oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG);
+		oleft &= 0x1f;
+		oright &= 0x1f;
+		oleft += vol;
+		if (oleft < 0)
+			oleft = 0;
+		if (oleft > 0x1f)
+			oleft = 0x1f;
+		oright += vol;
+		if (oright < 0)
+			oright = 0;
+		if (oright > 0x1f)
+			oright = 0x1f;
+		if (udreg & 0x80) {
+			mleft ^= 0x80;
+			mright ^= 0x80;
+		}
+		oleft |= mleft & 0x80;
+		oright |= mright & 0x80;
+		snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft);
+		snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright);
+		spin_unlock(&sonic->reg_lock);
+		snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id);
+		snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id);
+	}
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_sonicvibes_playback_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	return snd_sonicvibes_trigger(sonic, 1, cmd);
+}
+
+static int snd_sonicvibes_capture_trigger(snd_pcm_substream_t * substream,
+					  int cmd)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	return snd_sonicvibes_trigger(sonic, 2, cmd);
+}
+
+static int snd_sonicvibes_hw_params(snd_pcm_substream_t * substream,
+				    snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sonicvibes_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned char fmt = 0;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	sonic->p_dma_size = size;
+	count--;
+	if (runtime->channels > 1)
+		fmt |= 1;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		fmt |= 2;
+	snd_sonicvibes_setfmt(sonic, ~3, fmt);
+	snd_sonicvibes_set_dac_rate(sonic, runtime->rate);
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned char fmt = 0;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	sonic->c_dma_size = size;
+	count >>= 1;
+	count--;
+	if (runtime->channels > 1)
+		fmt |= 0x10;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		fmt |= 0x20;
+	snd_sonicvibes_setfmt(sonic, ~0x30, fmt);
+	snd_sonicvibes_set_adc_rate(sonic, runtime->rate);
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_sonicvibes_playback_pointer(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(sonic->enable & 1))
+		return 0;
+	ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	if (!(sonic->enable & 2))
+		return 0;
+	ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_sonicvibes_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	32,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sonicvibes_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	32,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	sonic->mode |= SV_MODE_PLAY;
+	sonic->playback_substream = substream;
+	runtime->hw = snd_sonicvibes_playback;
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, 0, SNDRV_PCM_HW_PARAM_RATE, -1);
+	return 0;
+}
+
+static int snd_sonicvibes_capture_open(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	sonic->mode |= SV_MODE_CAPTURE;
+	sonic->capture_substream = substream;
+	runtime->hw = snd_sonicvibes_capture;
+	snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_sonicvibes_hw_constraints_adc_clock);
+	return 0;
+}
+
+static int snd_sonicvibes_playback_close(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+
+	sonic->playback_substream = NULL;
+	sonic->mode &= ~SV_MODE_PLAY;
+	return 0;
+}
+
+static int snd_sonicvibes_capture_close(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+
+	sonic->capture_substream = NULL;
+	sonic->mode &= ~SV_MODE_CAPTURE;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_sonicvibes_playback_ops = {
+	open:		snd_sonicvibes_playback_open,
+	close:		snd_sonicvibes_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_sonicvibes_hw_params,
+	hw_free:	snd_sonicvibes_hw_free,
+	prepare:	snd_sonicvibes_playback_prepare,
+	trigger:	snd_sonicvibes_playback_trigger,
+	pointer:	snd_sonicvibes_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sonicvibes_capture_ops = {
+	open:		snd_sonicvibes_capture_open,
+	close:		snd_sonicvibes_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_sonicvibes_hw_params,
+	hw_free:	snd_sonicvibes_hw_free,
+	prepare:	snd_sonicvibes_capture_prepare,
+	trigger:	snd_sonicvibes_capture_trigger,
+	pointer:	snd_sonicvibes_capture_pointer,
+};
+
+static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, pcm->private_data, return);
+	sonic->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)
+		return err;
+	snd_assert(pcm != NULL, return -EINVAL);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops);
+
+	pcm->private_data = sonic;
+	pcm->private_free = snd_sonicvibes_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "S3 SonicVibes");
+	sonic->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer part
+ */
+
+#define SONICVIBES_MUX(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_sonicvibes_info_mux, \
+  get: snd_sonicvibes_get_mux, put: snd_sonicvibes_put_mux }
+
+static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[7] = {
+		"CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 7;
+	if (uinfo->value.enumerated.item >= 7)
+		uinfo->value.enumerated.item = 6;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
+	ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right, oval1, oval2;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] >= 7 ||
+	    ucontrol->value.enumerated.item[1] >= 7)
+		return -EINVAL;
+	left = (ucontrol->value.enumerated.item[0] + 1) << 5;
+	right = (ucontrol->value.enumerated.item[1] + 1) << 5;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC);
+	oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC);
+	left = (oval1 & ~SV_RECSRC_OUT) | left;
+	right = (oval2 & ~SV_RECSRC_OUT) | right;
+	change = left != oval1 || right != oval2;
+	snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left);
+	snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return change;
+}
+
+#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_sonicvibes_info_single, \
+  get: snd_sonicvibes_get_single, put: snd_sonicvibes_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask;
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val, oval;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	oval = snd_sonicvibes_in1(sonic, reg);
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	snd_sonicvibes_out1(sonic, reg, val);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return change;
+}
+
+#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_sonicvibes_info_double, \
+  get: snd_sonicvibes_get_double, put: snd_sonicvibes_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask;
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2, oval1, oval2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	oval1 = snd_sonicvibes_in1(sonic, left_reg);
+	oval2 = snd_sonicvibes_in1(sonic, right_reg);
+	val1 = (oval1 & ~(mask << shift_left)) | val1;
+	val2 = (oval2 & ~(mask << shift_right)) | val2;
+	change = val1 != oval1 || val2 != oval2;
+	snd_sonicvibes_out1(sonic, left_reg, val1);
+	snd_sonicvibes_out1(sonic, right_reg, val2);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return change;
+}
+
+#define SONICVIBES_CONTROLS (sizeof(snd_sonicvibes_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sonicvibes_controls[] __devinitdata = {
+SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0),
+SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1),
+SONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1),
+SONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1),
+SONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0),
+SONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1),
+SONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0),
+SONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1),
+SONICVIBES_MUX("Capture Source", 0)
+};
+
+static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, _snd_kcontrol_chip(kcontrol), return);
+	sonic->master_mute = NULL;
+	sonic->master_volume = NULL;
+}
+
+static int __devinit snd_sonicvibes_mixer(sonicvibes_t * sonic)
+{
+	snd_card_t *card;
+	snd_kcontrol_t *kctl;
+	int idx, err;
+
+	snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL);
+	card = sonic->card;
+	strcpy(card->mixername, "S3 SonicVibes");
+
+	for (idx = 0; idx < SONICVIBES_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0)
+			return err;
+		switch (idx) {
+		case 0:
+		case 1: kctl->private_free = snd_sonicvibes_master_free; break;
+		}
+	}
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_sonicvibes_proc_read(snd_info_entry_t *entry, 
+				     snd_info_buffer_t * buffer)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, entry->private_data, return);
+	unsigned char tmp;
+
+	tmp = sonic->srs_space & 0x0f;
+	snd_iprintf(buffer, "SRS 3D           : %s\n",
+		    sonic->srs_space & 0x80 ? "off" : "on");
+	snd_iprintf(buffer, "SRS Space        : %s\n",
+		    tmp == 0x00 ? "100%" :
+		    tmp == 0x01 ? "75%" :
+		    tmp == 0x02 ? "50%" :
+		    tmp == 0x03 ? "25%" : "0%");
+	tmp = sonic->srs_center & 0x0f;
+	snd_iprintf(buffer, "SRS Center       : %s\n",
+		    tmp == 0x00 ? "100%" :
+		    tmp == 0x01 ? "75%" :
+		    tmp == 0x02 ? "50%" :
+		    tmp == 0x03 ? "25%" : "0%");
+	tmp = sonic->wave_source & 0x03;
+	snd_iprintf(buffer, "WaveTable Source : %s\n",
+		    tmp == 0x00 ? "on-board ROM" :
+		    tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus");
+	tmp = sonic->mpu_switch;
+	snd_iprintf(buffer, "Onboard synth    : %s\n", tmp & 0x01 ? "on" : "off");
+	snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off");
+	snd_iprintf(buffer, "MIDI to ext. Tx  : %s\n", tmp & 0x04 ? "on" : "off");
+}
+
+static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(sonic->card, "sonicvibes", sonic->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = sonic;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_sonicvibes_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	sonic->proc_entry = entry;
+}
+
+static void snd_sonicvibes_proc_done(sonicvibes_t * sonic)
+{
+	if (sonic->proc_entry) {
+		snd_info_unregister(sonic->proc_entry);
+		sonic->proc_entry = NULL;
+	}
+}
+
+/*
+
+ */
+
+static snd_kcontrol_new_t snd_sonicvibes_game_control __devinitdata =
+SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0);
+
+static int snd_sonicvibes_free(sonicvibes_t *sonic)
+{
+#ifndef LINUX_2_2
+	if (sonic->gameport.io)
+		gameport_unregister_port(&sonic->gameport);
+#endif
+	snd_sonicvibes_proc_done(sonic);
+	pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port);
+	pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port);
+	if (sonic->res_sb_port) {
+		release_resource(sonic->res_sb_port);
+		kfree_nocheck(sonic->res_sb_port);
+	}
+	if (sonic->res_enh_port) {
+		release_resource(sonic->res_enh_port);
+		kfree_nocheck(sonic->res_enh_port);
+	}
+	if (sonic->res_synth_port) {
+		release_resource(sonic->res_synth_port);
+		kfree_nocheck(sonic->res_synth_port);
+	}
+	if (sonic->res_midi_port) {
+		release_resource(sonic->res_midi_port);
+		kfree_nocheck(sonic->res_midi_port);
+	}
+	if (sonic->res_dmaa) {
+		release_resource(sonic->res_dmaa);
+		kfree_nocheck(sonic->res_dmaa);
+	}
+	if (sonic->res_dmac) {
+		release_resource(sonic->res_dmac);
+		kfree_nocheck(sonic->res_dmac);
+	}
+	if (sonic->irq >= 0)
+		free_irq(sonic->irq, (void *)sonic);
+	snd_magic_kfree(sonic);
+	return 0;
+}
+
+static int snd_sonicvibes_dev_free(snd_device_t *device)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, device->device_data, return -ENXIO);
+	return snd_sonicvibes_free(sonic);
+}
+
+static int __devinit snd_sonicvibes_create(snd_card_t * card,
+					struct pci_dev *pci,
+					int reverb,
+					int mge,
+					sonicvibes_t ** rsonic)
+{
+	sonicvibes_t *sonic;
+	unsigned int dmaa, dmac;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_sonicvibes_dev_free,
+	};
+
+	*rsonic = NULL;
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 24 bits */
+        if (!pci_dma_supported(pci, 0x00ffffff)) {
+                snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+                return -ENXIO;
+        }
+	pci_set_dma_mask(pci, 0x00ffffff);
+
+	sonic = snd_magic_kcalloc(sonicvibes_t, 0, GFP_KERNEL);
+	if (sonic == NULL)
+		return -ENOMEM;
+	spin_lock_init(&sonic->reg_lock);
+	sonic->card = card;
+	sonic->pci = pci;
+	sonic->irq = -1;
+	sonic->sb_port = pci_resource_start(pci, 0);
+	if ((sonic->res_sb_port = request_region(sonic->sb_port, 0x10, "S3 SonicVibes SB")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab SB port at 0x%lx-0x%lx\n", sonic->sb_port, sonic->sb_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	sonic->enh_port = pci_resource_start(pci, 1);
+	if ((sonic->res_enh_port = request_region(sonic->enh_port, 0x10, "S3 SonicVibes Enhanced")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab PCM port at 0x%lx-0x%lx\n", sonic->enh_port, sonic->enh_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	sonic->synth_port = pci_resource_start(pci, 2);
+	if ((sonic->res_synth_port = request_region(sonic->synth_port, 4, "S3 SonicVibes Synth")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab synth port at 0x%lx-0x%lx\n", sonic->synth_port, sonic->synth_port + 4 - 1);
+		return -EBUSY;
+	}
+	sonic->midi_port = pci_resource_start(pci, 3);
+	if ((sonic->res_midi_port = request_region(sonic->midi_port, 4, "S3 SonicVibes Midi")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab MIDI port at 0x%lx-0x%lx\n", sonic->midi_port, sonic->midi_port + 4 - 1);
+		return -EBUSY;
+	}
+	sonic->game_port = pci_resource_start(pci, 4);
+	if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) {
+		snd_magic_kfree(sonic);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	sonic->irq = pci->irq;
+
+	pci_read_config_dword(pci, 0x40, &dmaa);
+	pci_read_config_dword(pci, 0x48, &dmac);
+	snd_dmaio &= ~0x0f;
+	dmaa &= ~0x0f;
+	dmac &= ~0x0f;
+	if (!dmaa) {
+		dmaa = snd_dmaio;
+		snd_dmaio += 0x10;
+		snd_printk("BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa);
+	}
+	if (!dmac) {
+		dmac = snd_dmaio;
+		snd_dmaio += 0x10;
+		snd_printk("BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac);
+	}
+	pci_write_config_dword(pci, 0x40, dmaa);
+	pci_write_config_dword(pci, 0x48, dmac);
+
+	if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1);
+		return -EBUSY;
+	}
+	if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1);
+		return -EBUSY;
+	}
+
+	pci_read_config_dword(pci, 0x40, &sonic->dmaa_port);
+	pci_read_config_dword(pci, 0x48, &sonic->dmac_port);
+	sonic->dmaa_port &= ~0x0f;
+	sonic->dmac_port &= ~0x0f;
+	pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9);	/* enable + enhanced */
+	pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9);	/* enable */
+	/* ok.. initialize S3 SonicVibes chip */
+	outb(SV_RESET, SV_REG(sonic, CONTROL));		/* reset chip */
+	udelay(100);
+	outb(0, SV_REG(sonic, CONTROL));	/* release reset */
+	udelay(100);
+	outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL));
+	inb(SV_REG(sonic, STATUS));	/* clear IRQs */
+#if 1
+	snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0);	/* drive current 16mA */
+#else
+	snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40);	/* drive current 8mA */
+#endif
+	snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0);	/* disable playback & capture */
+	outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK));
+	inb(SV_REG(sonic, STATUS));	/* clear IRQs */
+	snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0);	/* use PLL as clock source */
+	snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0);	/* power up analog parts */
+	snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0);	/* power up digital parts */
+	snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000);
+	snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80);	/* SRS space off */
+	snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */
+	snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05);	/* MPU-401 switch */
+	snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00);	/* onboard ROM */
+	snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff);
+	snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf);
+	snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc);
+#if 0
+	snd_sonicvibes_debug(sonic);
+#endif
+	sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION);
+	snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+	snd_sonicvibes_proc_init(sonic);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) {
+		snd_sonicvibes_free(sonic);
+		return err;
+	}
+
+	*rsonic = sonic;
+	return 0;
+}
+
+/*
+ *  MIDI section
+ */
+
+#define SONICVIBES_MIDI_CONTROLS (sizeof(snd_sonicvibes_midi_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] __devinitdata = {
+SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0),
+SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0),
+SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0),
+SONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0),
+SONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0)
+};
+
+static int snd_sonicvibes_midi_input_open(mpu401_t * mpu)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return -EIO);
+	outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
+	return 0;
+}
+
+static void snd_sonicvibes_midi_input_close(mpu401_t * mpu)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return);
+	outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
+}
+
+static int __devinit snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi)
+{
+	mpu401_t * mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENXIO);
+	snd_card_t *card = sonic->card;
+	snd_rawmidi_str_t *dir;
+	int idx, err;
+
+	mpu->private_data = sonic;
+	mpu->open_input = snd_sonicvibes_midi_input_open;
+	mpu->close_input = snd_sonicvibes_midi_input_close;
+	dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+	for (idx = 0; idx < SONICVIBES_MIDI_CONTROLS; idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0)
+			return err;
+	return 0;
+}
+
+static int __devinit snd_sonic_probe(struct pci_dev *pci,
+				     const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	sonicvibes_t *sonic;
+	snd_rawmidi_t *midi_uart;
+	opl3_t *opl3;
+	int idx, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+ 
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	for (idx = 0; idx < 5; idx++) {
+		if (pci_resource_start(pci, idx) == 0 ||
+		    !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+			snd_card_free(card);
+			return -ENODEV;
+		}
+	}
+	if ((err = snd_sonicvibes_create(card, pci,
+					 snd_reverb[dev] ? 1 : 0,
+					 snd_mge[dev] ? 1 : 0,
+					 &sonic)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sonicvibes_mixer(sonic)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
+				       sonic->midi_port, 1,
+				       sonic->irq, 0,
+				       &midi_uart)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_sonicvibes_midi(sonic, midi_uart);
+	if ((err = snd_opl3_create(card, sonic->synth_port,
+				   sonic->synth_port + 2,
+				   OPL3_HW_OPL3_SV, 1, &opl3)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifndef LINUX_2_2
+	sonic->gameport.io = sonic->game_port;
+	gameport_register_port(&sonic->gameport);
+#endif
+	strcpy(card->driver, "SonicVibes");
+	strcpy(card->shortname, "S3 SonicVibes");
+	sprintf(card->longname, "%s rev %i at 0x%lx, irq %i",
+		card->shortname,
+		sonic->revision,
+		pci_resource_start(pci, 1),
+		sonic->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_sonic_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "S3 SonicVibes",
+	id_table: snd_sonic_ids,
+	probe: snd_sonic_probe,
+	remove: __devexit_p(snd_sonic_remove),
+};
+
+static int __init alsa_card_sonicvibes_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "S3 SonicVibes soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_sonicvibes_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_sonicvibes_init)
+module_exit(alsa_card_sonicvibes_exit)
+
+#ifndef MODULE
+
+/* format is: snd-sonicvibes=snd_enable,snd_index,snd_id,
+			     snd_reverb,snd_mge,snd_dmaio */
+
+static int __init alsa_card_sonicvibes_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_reverb[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mge[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_dmaio) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-sonicvibes=", alsa_card_sonicvibes_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/trident/Makefile linux-2.4.19-pre5-mjc/sound/pci/trident/Makefile
--- linux/sound/pci/trident/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/trident/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,27 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _trident.o
+
+list-multi   := snd-trident.o snd-trident-synth.o
+
+export-objs  := trident_main.o
+
+snd-trident-objs := trident.o trident_main.o trident_memory.o
+snd-trident-synth-objs := trident_synth.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_TRIDENT) += snd-trident-synth.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-trident.o: $(snd-trident-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-objs)
+
+snd-trident-synth.o: $(snd-trident-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-synth-objs)
diff -Nru linux/sound/pci/trident/trident.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident.c
--- linux/sound/pci/trident/trident.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,263 @@
+/*
+ *  Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard
+ *
+ *  Driver was originated by Trident <audio@tridentmicro.com>
+ *  			     Fri Feb 19 15:55:28 MST 1999
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/trident.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, <audio@tridentmicro.com>");
+MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Trident,4DWave DX},"
+		"{Trident,4DWave NX},"
+		"{SiS,SI7018 PCI Audio},"
+		"{Best Union,Miss Melody 4DWave PCI},"
+		"{HIS,4DWave PCI},"
+		"{Warpspeed,ONSpeed 4DWave PCI},"
+		"{Aztech Systems,PCI 64-Q3D},"
+		"{Addonics,SV 750},"
+		"{CHIC,True Sound 4Dwave},"
+		"{Shark,Predator4D-PCI},"
+		"{Jaton,SonicWave 4D},"
+		"{Hoontech,SoundTrack Digital 4DWave NX}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
+static int snd_wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Trident 4DWave PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for Trident 4DWave PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Trident 4DWave PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Number of hardware channels assigned for PCM.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}");
+MODULE_PARM(snd_wavetable_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_wavetable_size, "Maximum memory size in kB for wavetable synth.");
+MODULE_PARM_SYNTAX(snd_wavetable_size, SNDRV_ENABLED ",default:8192,skill:advanced");
+
+static struct pci_device_id snd_trident_ids[] __devinitdata = {
+	{ 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Trident 4DWave DX PCI Audio */
+	{ 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Trident 4DWave NX PCI Audio */
+	{ 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* SiS SI7018 PCI Audio */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_trident_ids);
+
+static int __devinit snd_trident_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	trident_t *trident;
+	const char *str;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_trident_create(card, pci,
+				      snd_pcm_channels[dev],
+				      2,
+				      snd_wavetable_size[dev],
+				      &trident)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_trident_pcm(trident, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+	case TRIDENT_DEVICE_ID_NX:
+		if ((err = snd_trident_foldback_pcm(trident, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		break;
+	}
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		if ((err = snd_trident_spdif_pcm(trident, 2, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
+				       trident->midi_port, 1,
+				       trident->irq, 0, &trident->rmidi)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+		str = "TRID4DWAVEDX";
+		break;
+	case TRIDENT_DEVICE_ID_NX:
+		str = "TRID4DWAVENX";
+		break;
+	case TRIDENT_DEVICE_ID_SI7018:
+		str = "SI7018";
+		break;
+	default:
+		str = "Unknown";
+	}
+	strcpy(card->driver, str);
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		strcpy(card->shortname, "SiS ");
+	} else {
+		strcpy(card->shortname, "Trident ");
+	}
+	strcat(card->shortname, card->driver);
+	sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d",
+		card->shortname, trident->port, trident->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, trident);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_trident_remove(struct pci_dev *pci)
+{
+	trident_t *trident = snd_magic_cast(trident_t, pci_get_drvdata(pci), return);
+	if (trident)
+		snd_card_free(trident->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+#ifdef CONFIG_PM
+#ifndef PCI_OLD_SUSPEND
+static int snd_card_trident_suspend(struct pci_dev *pci, u32 state)
+{
+	trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO);
+	snd_trident_suspend(chip);
+	return 0;
+}
+static int snd_card_trident_resume(struct pci_dev *pci)
+{
+	trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO);
+	snd_trident_resume(chip);
+	return 0;
+}
+#else
+static void snd_card_trident_suspend(struct pci_dev *pci)
+{
+	trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return);
+	snd_trident_suspend(chip);
+}
+static void snd_card_trident_resume(struct pci_dev *pci)
+{
+	trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return);
+	snd_trident_resume(chip);
+}
+#endif
+#endif
+
+static struct pci_driver driver = {
+	name: "Trident4DWaveAudio",
+	id_table: snd_trident_ids,
+	probe: snd_trident_probe,
+	remove: __devexit_p(snd_trident_remove),
+#ifdef CONFIG_PM
+	suspend: snd_card_trident_suspend,
+	resume: snd_card_trident_resume,
+#endif
+};
+
+static int __init alsa_card_trident_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Trident 4DWave PCI soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_trident_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_trident_init)
+module_exit(alsa_card_trident_exit)
+
+#ifndef MODULE
+
+/* format is: snd-trident=snd_enable,snd_index,snd_id,
+			  snd_pcm_channels,snd_wavetable_size */
+
+static int __init alsa_card_trident_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_wavetable_size[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-trident=", alsa_card_trident_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/trident/trident_main.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident_main.c
--- linux/sound/pci/trident/trident_main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident_main.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,3636 @@
+/*
+ *  Maintained by Jaroslav Kysela <perex@suse.cz>
+ *  Originated by audio@tridentmicro.com
+ *  Fri Feb 19 15:55:28 MST 1999
+ *  Routines for control of Trident 4DWave (DX and NX) chip
+ *
+ *  BUGS:
+ *
+ *  TODO:
+ *    ---
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/trident.h>
+#include <sound/asoundef.h>
+
+#define chip_t trident_t
+
+static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);
+static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);
+static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_PM
+static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state);
+#endif
+
+/*
+ *  common I/O routines
+ */
+
+
+#if 0
+static void snd_trident_print_voice_regs(trident_t *trident, int voice)
+{
+	unsigned int val, tmp;
+
+	printk("Trident voice %i:\n", voice);
+	outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
+	val = inl(TRID_REG(trident, CH_LBA));
+	printk("LBA: 0x%x\n", val);
+	val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+	printk("GVSel: %i\n", val >> 31);
+	printk("Pan: 0x%x\n", (val >> 24) & 0x7f);
+	printk("Vol: 0x%x\n", (val >> 16) & 0xff);
+	printk("CTRL: 0x%x\n", (val >> 12) & 0x0f);
+	printk("EC: 0x%x\n", val & 0x0fff);
+	if (trident->device != TRIDENT_DEVICE_ID_NX) {
+		val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
+		printk("CSO: 0x%x\n", val >> 16);
+		printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff);
+		printk("FMS: 0x%x\n", val & 0x0f);
+		val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
+		printk("ESO: 0x%x\n", val >> 16);
+		printk("Delta: 0x%x\n", val & 0xffff);
+		val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+	} else {		// TRIDENT_DEVICE_ID_NX
+		val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
+		tmp = (val >> 24) & 0xff;
+		printk("CSO: 0x%x\n", val & 0x00ffffff);
+		val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
+		tmp |= (val >> 16) & 0xff00;
+		printk("Delta: 0x%x\n", tmp);
+		printk("ESO: 0x%x\n", val & 0x00ffffff);
+		val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
+		printk("Alpha: 0x%x\n", val >> 20);
+		printk("FMS: 0x%x\n", (val >> 16) & 0x0f);
+	}
+	printk("FMC: 0x%x\n", (val >> 14) & 3);
+	printk("RVol: 0x%x\n", (val >> 7) & 0x7f);
+	printk("CVol: 0x%x\n", val & 0x7f);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+   unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+  
+   Description: This routine will do all of the reading from the external
+                CODEC (AC97).
+  
+   Parameters:  ac97 - ac97 codec structure
+                reg - CODEC register index, from AC97 Hal.
+ 
+   returns:     16 bit value read from the AC97.
+  
+  ---------------------------------------------------------------------------*/
+static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	unsigned int data = 0, treg;
+	unsigned short count = 0xffff;
+	unsigned long flags;
+	trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return -ENXIO);
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	if (trident->device == TRIDENT_DEVICE_ID_DX) {
+		data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
+		outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
+		do {
+			data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
+			if ((data & DX_AC97_BUSY_READ) == 0)
+				break;
+		} while (--count);
+	} else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
+		treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
+		outl(data, TRID_REG(trident, treg));
+		do {
+			data = inl(TRID_REG(trident, treg));
+			if ((data & 0x00000C00) == 0)
+				break;
+		} while (--count);
+	} else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+		if (ac97->num == 1)
+			data |= SI_AC97_SECONDARY;
+		outl(data, TRID_REG(trident, SI_AC97_READ));
+		do {
+			data = inl(TRID_REG(trident, SI_AC97_READ));
+			if ((data & (SI_AC97_BUSY_READ)) == 0)
+				break;
+		} while (--count);
+	}
+
+	if (count == 0) {
+		snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data);
+		data = 0;
+	}
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return ((unsigned short) (data >> 16));
+}
+
+/*---------------------------------------------------------------------------
+   void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+  
+   Description: This routine will do all of the writing to the external
+                CODEC (AC97).
+  
+   Parameters:	ac97 - ac97 codec structure
+   	        reg - CODEC register index, from AC97 Hal.
+                data  - Lower 16 bits are the data to write to CODEC.
+  
+   returns:     TRUE if everything went ok, else FALSE.
+  
+  ---------------------------------------------------------------------------*/
+static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+{
+	unsigned int address, data;
+	unsigned short count = 0xffff;
+	unsigned long flags;
+	trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return);
+
+	data = ((unsigned long) wdata) << 16;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	if (trident->device == TRIDENT_DEVICE_ID_DX) {
+		address = DX_ACR0_AC97_W;
+
+		/* read AC-97 write register status */
+		do {
+			if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
+				break;
+		} while (--count);
+
+		data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
+	} else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		address = NX_ACR1_AC97_W;
+
+		/* read AC-97 write register status */
+		do {
+			if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
+				break;
+		} while (--count);
+
+		data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
+	} else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		address = SI_AC97_WRITE;
+
+		/* read AC-97 write register status */
+		do {
+			if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
+				break;
+		} while (--count);
+
+		data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+		if (ac97->num == 1)
+			data |= SI_AC97_SECONDARY;
+	} else {
+		address = 0;	/* keep GCC happy */
+		count = 0;	/* return */
+	}
+
+	if (count == 0) {
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		return;
+	}
+	outl(data, TRID_REG(trident, address));
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+   void snd_trident_enable_eso(trident_t *trident)
+  
+   Description: This routine will enable end of loop interrupts.
+                End of loop interrupts will occur when a running
+                channel reaches ESO.
+                Also enables middle of loop interrupts.
+  
+   Parameters:  trident - pointer to target device class for 4DWave.
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_enable_eso(trident_t * trident)
+{
+	unsigned int val;
+
+	val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+	val |= ENDLP_IE;
+	val |= MIDLP_IE;
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+		val |= BANK_B_EN;
+	outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+   void snd_trident_disable_eso(trident_t *trident)
+  
+   Description: This routine will disable end of loop interrupts.
+                End of loop interrupts will occur when a running
+                channel reaches ESO.
+                Also disables middle of loop interrupts.
+  
+   Parameters:  
+                trident - pointer to target device class for 4DWave.
+  
+   returns:     TRUE if everything went ok, else FALSE.
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_disable_eso(trident_t * trident)
+{
+	unsigned int tmp;
+
+	tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+	tmp &= ~ENDLP_IE;
+	tmp &= ~MIDLP_IE;
+	outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+   void snd_trident_start_voice(trident_t * trident, unsigned int voice)
+
+    Description: Start a voice, any channel 0 thru 63.
+                 This routine automatically handles the fact that there are
+                 more than 32 channels available.
+
+    Parameters : voice - Voice number 0 thru n.
+                 trident - pointer to target device class for 4DWave.
+
+    Return Value: None.
+
+  ---------------------------------------------------------------------------*/
+
+void snd_trident_start_voice(trident_t * trident, unsigned int voice)
+{
+	unsigned int mask = 1 << (voice & 0x1f);
+	unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
+
+	outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+   void snd_trident_stop_voice(trident_t * trident, unsigned int voice)
+
+    Description: Stop a voice, any channel 0 thru 63.
+                 This routine automatically handles the fact that there are
+                 more than 32 channels available.
+
+    Parameters : voice - Voice number 0 thru n.
+                 trident - pointer to target device class for 4DWave.
+
+    Return Value: None.
+
+  ---------------------------------------------------------------------------*/
+
+void snd_trident_stop_voice(trident_t * trident, unsigned int voice)
+{
+	unsigned int mask = 1 << (voice & 0x1f);
+	unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
+
+	outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+    int snd_trident_allocate_pcm_channel(trident_t *trident)
+  
+    Description: Allocate hardware channel in Bank B (32-63).
+  
+    Parameters :  trident - pointer to target device class for 4DWave.
+  
+    Return Value: hardware channel - 32-63 or -1 when no channel is available
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_pcm_channel(trident_t * trident)
+{
+	int idx;
+
+	if (trident->ChanPCMcnt >= trident->ChanPCM)
+		return -1;
+	for (idx = 31; idx >= 0; idx--) {
+		if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
+			trident->ChanMap[T4D_BANK_B] |= 1 << idx;
+			trident->ChanPCMcnt++;
+			return idx + 32;
+		}
+	}
+	return -1;
+}
+
+/*---------------------------------------------------------------------------
+    void snd_trident_free_pcm_channel(int channel)
+  
+    Description: Free hardware channel in Bank B (32-63)
+  
+    Parameters :  trident - pointer to target device class for 4DWave.
+	          channel - hardware channel number 0-63
+  
+    Return Value: none
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_pcm_channel(trident_t *trident, int channel)
+{
+	if (channel < 32 || channel > 63)
+		return;
+	channel &= 0x1f;
+	if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
+		trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
+		trident->ChanPCMcnt--;
+	}
+}
+
+/*---------------------------------------------------------------------------
+    unsigned int snd_trident_allocate_synth_channel(void)
+  
+    Description: Allocate hardware channel in Bank A (0-31).
+  
+    Parameters :  trident - pointer to target device class for 4DWave.
+  
+    Return Value: hardware channel - 0-31 or -1 when no channel is available
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_synth_channel(trident_t * trident)
+{
+	int idx;
+
+	for (idx = 31; idx >= 0; idx--) {
+		if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
+			trident->ChanMap[T4D_BANK_A] |= 1 << idx;
+			trident->synth.ChanSynthCount++;
+			return idx;
+		}
+	}
+	return -1;
+}
+
+/*---------------------------------------------------------------------------
+    void snd_trident_free_synth_channel( int channel )
+  
+    Description: Free hardware channel in Bank B (0-31).
+  
+    Parameters :  trident - pointer to target device class for 4DWave.
+	          channel - hardware channel number 0-63
+  
+    Return Value: none
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_synth_channel(trident_t *trident, int channel)
+{
+	if (channel < 0 || channel > 31)
+		return;
+	channel &= 0x1f;
+	if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
+		trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
+		trident->synth.ChanSynthCount--;
+	}
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_write_voice_regs
+  
+   Description: This routine will complete and write the 5 hardware channel
+                registers to hardware.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                voice - synthesizer voice structure
+                Each register field.
+  
+  ---------------------------------------------------------------------------*/
+
+void snd_trident_write_voice_regs(trident_t * trident,
+				  snd_trident_voice_t * voice)
+{
+	unsigned int FmcRvolCvol;
+	unsigned int regs[5];
+
+	regs[1] = voice->LBA;
+	regs[4] = (voice->GVSel << 31) |
+		  ((voice->Pan & 0x0000007f) << 24) |
+		  ((voice->CTRL & 0x0000000f) << 12);
+	FmcRvolCvol = ((voice->FMC & 3) << 14) |
+	              ((voice->RVol & 0x7f) << 7) |
+	              (voice->CVol & 0x7f);
+
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_SI7018:
+		regs[4] |= voice->number > 31 ?
+				(voice->Vol & 0x000003ff) :
+				((voice->Vol & 0x00003fc) << (16-2)) |
+				(voice->EC & 0x00000fff);
+		regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f);
+		regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
+		regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
+		break;
+	case TRIDENT_DEVICE_ID_DX:
+		regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
+			   (voice->EC & 0x00000fff);
+		regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f);
+		regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
+		regs[3] = FmcRvolCvol;
+		break;
+	case TRIDENT_DEVICE_ID_NX:
+		regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
+			   (voice->EC & 0x00000fff);
+		regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
+		regs[2] = ((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff);
+		regs[3] = (voice->Alpha << 20) | ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
+		break;
+	default:
+		snd_BUG();
+	}
+
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	outl(regs[0], TRID_REG(trident, CH_START + 0));
+	outl(regs[1], TRID_REG(trident, CH_START + 4));
+	outl(regs[2], TRID_REG(trident, CH_START + 8));
+	outl(regs[3], TRID_REG(trident, CH_START + 12));
+	outl(regs[4], TRID_REG(trident, CH_START + 16));
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_write_cso_reg
+  
+   Description: This routine will write the new CSO offset
+                register to hardware.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                voice - synthesizer voice structure
+                CSO - new CSO value
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CSO)
+{
+	voice->CSO = CSO;
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	if (trident->device != TRIDENT_DEVICE_ID_NX) {
+		outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+	} else {
+		outl((voice->Delta << 24) | (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+	}
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_write_vol_reg
+  
+   Description: This routine will write the new voice volume
+                register to hardware.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                voice - synthesizer voice structure
+                Vol - new voice volume
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_vol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Vol)
+{
+	voice->Vol = Vol;
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+	case TRIDENT_DEVICE_ID_NX:
+		outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
+		break;
+	case TRIDENT_DEVICE_ID_SI7018:
+		// printk("voice->Vol = 0x%x\n", voice->Vol);
+		outw((voice->CTRL << 12) | voice->Vol, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+		break;
+	}
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_write_pan_reg
+  
+   Description: This routine will write the new voice pan
+                register to hardware.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                voice - synthesizer voice structure
+                Pan - new pan value
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_pan_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Pan)
+{
+	voice->Pan = Pan;
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_write_rvol_reg
+  
+   Description: This routine will write the new reverb volume
+                register to hardware.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                voice - synthesizer voice structure
+                RVol - new reverb volume
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_rvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int RVol)
+{
+	voice->RVol = RVol;
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f),
+	     TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_write_cvol_reg
+  
+   Description: This routine will write the new chorus volume
+                register to hardware.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                voice - synthesizer voice structure
+                CVol - new chorus volume
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CVol)
+{
+	voice->CVol = CVol;
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f),
+	     TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_convert_rate
+
+   Description: This routine converts rate in HZ to hardware delta value.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                rate - Real or Virtual channel number.
+  
+   Returns:     Delta value.
+  
+  ---------------------------------------------------------------------------*/
+unsigned int snd_trident_convert_rate(unsigned int rate)
+{
+	unsigned int delta;
+
+	// We special case 44100 and 8000 since rounding with the equation
+	// does not give us an accurate enough value. For 11025 and 22050
+	// the equation gives us the best answer. All other frequencies will
+	// also use the equation. JDW
+	if (rate == 44100)
+		delta = 0xeb3;
+	else if (rate == 8000)
+		delta = 0x2ab;
+	else if (rate == 48000)
+		delta = 0x1000;
+	else
+		delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+	return delta;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_convert_adc_rate
+
+   Description: This routine converts rate in HZ to hardware delta value.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                rate - Real or Virtual channel number.
+  
+   Returns:     Delta value.
+  
+  ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
+{
+	unsigned int delta;
+
+	// We special case 44100 and 8000 since rounding with the equation
+	// does not give us an accurate enough value. For 11025 and 22050
+	// the equation gives us the best answer. All other frequencies will
+	// also use the equation. JDW
+	if (rate == 44100)
+		delta = 0x116a;
+	else if (rate == 8000)
+		delta = 0x6000;
+	else if (rate == 48000)
+		delta = 0x1000;
+	else
+		delta = ((48000 << 12) / rate) & 0x0000ffff;
+	return delta;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spurious_threshold
+
+   Description: This routine converts rate in HZ to spurious threshold.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                rate - Real or Virtual channel number.
+  
+   Returns:     Delta value.
+  
+  ---------------------------------------------------------------------------*/
+unsigned int snd_trident_spurious_threshold(unsigned int rate, unsigned int period_size)
+{
+	unsigned int res = (rate * period_size) / 48000;
+	if (res < 64)
+		res = res / 2;
+	else
+		res -= 32;
+	return res;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_control_mode
+
+   Description: This routine returns a control mode for a PCM channel.
+  
+   Paramters:   trident - pointer to target device class for 4DWave.
+                substream  - PCM substream
+  
+   Returns:     Control value.
+  
+  ---------------------------------------------------------------------------*/
+unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream)
+{
+	unsigned int CTRL;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	/* set ctrl mode
+	   CTRL default: 8-bit (unsigned) mono, loop mode enabled
+	 */
+	CTRL = 0x00000001;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		CTRL |= 0x00000008;	// 16-bit data
+	if (snd_pcm_format_signed(runtime->format))
+		CTRL |= 0x00000002;	// signed data
+	if (runtime->channels > 1)
+		CTRL |= 0x00000004;	// stereo data
+	return CTRL;
+}
+
+/*
+ *  PCM part
+ */
+
+/*---------------------------------------------------------------------------
+   snd_trident_ioctl
+  
+   Description: Device I/O control handler for playback/capture parameters.
+  
+   Paramters:   substream  - PCM substream class
+                cmd     - what ioctl message to process
+                arg     - additional message infoarg     
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_ioctl(snd_pcm_substream_t * substream,
+			     unsigned int cmd,
+			     void *arg)
+{
+	/* FIXME: it seems that with small periods the behaviour of
+	          trident hardware is unpredictable and interrupt generator
+	          is broken */
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_playback_hw_params
+  
+   Description: Set the hardware parameters for the playback device.
+  
+   Parameters:  substream  - PCM substream class
+		hw_params  - hardware parameters
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_playback_hw_params(snd_pcm_substream_t * substream,
+					  snd_pcm_hw_params_t * hw_params)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice->extra;
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0 && trident->tlb.entries) {
+		if (voice->memblk)
+			snd_trident_free_pages(trident, voice->memblk);
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		if (voice->memblk == NULL)
+			return -ENOMEM;
+	}
+
+	/* voice management */
+
+	if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
+		if (evoice == NULL) {
+			evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+			if (evoice == NULL)
+				return -ENOMEM;
+			voice->extra = evoice;
+			evoice->substream = substream;
+		}
+	} else {
+		if (evoice != NULL) {
+			snd_trident_free_voice(trident, evoice);
+			voice->extra = evoice = NULL;
+		}
+	}
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_playback_hw_free
+  
+   Description: Release the hardware resources for the playback device.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice ? voice->extra : NULL;
+
+	if (trident->tlb.entries && voice && voice->memblk) {
+		snd_trident_free_pages(trident, voice->memblk);
+		voice->memblk = NULL;
+	}
+	snd_pcm_lib_free_pages(substream);
+	if (evoice != NULL) {
+		snd_trident_free_voice(trident, evoice);
+		voice->extra = NULL;
+	}
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_playback_prepare
+  
+   Description: Prepare playback device for playback.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_playback_prepare(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice->extra;
+	snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number];
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);	
+
+	/* set delta (rate) value */
+	voice->Delta = snd_trident_convert_rate(runtime->rate);
+	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+	/* set Loop Begin Address */
+	if (voice->memblk)
+		voice->LBA = voice->memblk->offset;
+	else
+		voice->LBA = runtime->dma_addr;
+ 
+	voice->CSO = 0;
+	voice->ESO = runtime->buffer_size - 1;	/* in samples */
+	voice->CTRL = snd_trident_control_mode(substream);
+	voice->FMC = 3;
+	voice->GVSel = 1;
+	voice->EC = 0;
+	voice->Alpha = 0;
+	voice->FMS = 0;
+	voice->Vol = mix->vol;
+	voice->RVol = mix->rvol;
+	voice->CVol = mix->cvol;
+	voice->Pan = mix->pan;
+	voice->Attribute = 0;
+
+	snd_trident_write_voice_regs(trident, voice);
+
+	if (evoice != NULL) {
+		evoice->Delta = voice->Delta;
+		evoice->spurious_threshold = voice->spurious_threshold;
+		evoice->LBA = voice->LBA;
+		evoice->CSO = 0;
+		evoice->ESO = (runtime->period_size * 2) - 1; /* in samples */
+		evoice->CTRL = voice->CTRL;
+		evoice->FMC = 3;
+		evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+		evoice->EC = 0;
+		evoice->Alpha = 0;
+		evoice->FMS = 0;
+		evoice->Vol = 0x3ff;			/* mute */
+		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+		evoice->Pan = 0x7f;			/* mute */
+#if 0
+		evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
+				    (1<<(24-16))|(0x1f<<(19-16));
+#else
+		evoice->Attribute = 0;
+#endif
+		snd_trident_write_voice_regs(trident, evoice);
+	}
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_capture_hw_params
+  
+   Description: Set the hardware parameters for the capture device.
+  
+   Parameters:  substream  - PCM substream class
+		hw_params  - hardware parameters
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0 && trident->tlb.entries) {
+		if (voice->memblk)
+			snd_trident_free_pages(trident, voice->memblk);
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		if (voice->memblk == NULL)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_capture_hw_free
+  
+   Description: Release the hardware resources for the capture device.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+
+	if (trident->tlb.entries && voice && voice->memblk) {
+		snd_trident_free_pages(trident, voice->memblk);
+		voice->memblk = NULL;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_capture_prepare
+  
+   Description: Prepare capture device for playback.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_prepare(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned int val, ESO_bytes;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+
+	// Initilize the channel and set channel Mode
+	outb(0, TRID_REG(trident, LEGACY_DMAR15));
+
+	// Set DMA channel operation mode register
+	outb(0x54, TRID_REG(trident, LEGACY_DMAR11));
+
+	// Set channel buffer Address
+	voice->LBA = runtime->dma_addr;
+	outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
+	if (voice->memblk)
+		voice->LBA = voice->memblk->offset;
+
+	// set ESO
+	ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
+	outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
+	outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
+	ESO_bytes++;
+
+	// Set channel sample rate, 4.12 format
+	val = ((unsigned int) 48000L << 12) / runtime->rate;
+	outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
+
+	// Set channel interrupt blk length
+	if (snd_pcm_format_width(runtime->format) == 16) {
+		val = (unsigned short) ((ESO_bytes >> 1) - 1);
+	} else {
+		val = (unsigned short) (ESO_bytes - 1);
+	}
+
+	outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));
+
+	// Right now, set format and start to run captureing, 
+	// continuous run loop enable.
+	trident->bDMAStart = 0x19;	// 0001 1001b
+
+	if (snd_pcm_format_width(runtime->format) == 16)
+		trident->bDMAStart |= 0x80;
+	if (snd_pcm_format_signed(runtime->format))
+		trident->bDMAStart |= 0x20;
+	if (runtime->channels > 1)
+		trident->bDMAStart |= 0x40;
+
+	// Prepare capture intr channel
+
+	voice->Delta = snd_trident_convert_rate(runtime->rate);
+	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+	// Set voice parameters
+	voice->CSO = 0;
+	/* the +2 is a correction for a h/w problem. if not
+	   used, the ESO interrupt is received before the capture pointer
+	   has actually reached the ESO point. this causes errors in
+	   the mid-level code.
+	*/
+	voice->ESO = (runtime->period_size * 2) + 2 - 1;
+	voice->CTRL = snd_trident_control_mode(substream);
+	voice->FMC = 3;
+	voice->RVol = 0x7f;
+	voice->CVol = 0x7f;
+	voice->GVSel = 1;
+	voice->Pan = 0x7f;		/* mute */
+	voice->Vol = 0x3ff;		/* mute */
+	voice->EC = 0;
+	voice->Alpha = 0;
+	voice->FMS = 0;
+	voice->Attribute = 0;
+
+	snd_trident_write_voice_regs(trident, voice);
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_si7018_capture_hw_params
+  
+   Description: Set the hardware parameters for the capture device.
+  
+   Parameters:  substream  - PCM substream class
+		hw_params  - hardware parameters
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_hw_params(snd_pcm_substream_t * substream,
+						snd_pcm_hw_params_t * hw_params)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice->extra;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+
+	/* voice management */
+
+	if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
+		if (evoice == NULL) {
+			evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+			if (evoice == NULL)
+				return -ENOMEM;
+			voice->extra = evoice;
+			evoice->substream = substream;
+		}
+	} else {
+		if (evoice != NULL) {
+			snd_trident_free_voice(trident, evoice);
+			voice->extra = evoice = NULL;
+		}
+	}
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_si7018_capture_hw_free
+  
+   Description: Release the hardware resources for the capture device.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice ? voice->extra : NULL;
+
+	snd_pcm_lib_free_pages(substream);
+	if (evoice != NULL) {
+		snd_trident_free_voice(trident, evoice);
+		voice->extra = NULL;
+	}
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_si7018_capture_prepare
+  
+   Description: Prepare capture device for playback.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_prepare(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice->extra;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+
+	voice->LBA = runtime->dma_addr;
+	voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
+	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+	// Set voice parameters
+	voice->CSO = 0;
+	voice->ESO = runtime->buffer_size - 1;		/* in samples */
+	voice->CTRL = snd_trident_control_mode(substream);
+	voice->FMC = 0;
+	voice->RVol = 0;
+	voice->CVol = 0;
+	voice->GVSel = 1;
+	voice->Pan = T4D_DEFAULT_PCM_PAN;
+	voice->Vol = 0;
+	voice->EC = 0;
+	voice->Alpha = 0;
+	voice->FMS = 0;
+
+	voice->Attribute = (2 << (30-16)) |
+			   (2 << (26-16)) |
+			   (2 << (24-16)) |
+			   (1 << (23-16));
+
+	snd_trident_write_voice_regs(trident, voice);
+
+	if (evoice != NULL) {
+		evoice->Delta = snd_trident_convert_rate(runtime->rate);
+		evoice->spurious_threshold = voice->spurious_threshold;
+		evoice->LBA = voice->LBA;
+		evoice->CSO = 0;
+		evoice->ESO = (runtime->period_size * 2) + 8 - 1; /* in samples, 8 means correction */
+		evoice->CTRL = voice->CTRL;
+		evoice->FMC = 3;
+		evoice->GVSel = 0;
+		evoice->EC = 0;
+		evoice->Alpha = 0;
+		evoice->FMS = 0;
+		evoice->Vol = 0x3ff;			/* mute */
+		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+		evoice->Pan = 0x7f;			/* mute */
+		evoice->Attribute = 0;
+		snd_trident_write_voice_regs(trident, evoice);
+	}
+	
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_foldback_hw_params
+  
+   Description: Set the hardware parameters for the foldback device.
+  
+   Parameters:  substream  - PCM substream class
+		hw_params  - hardware parameters
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_hw_params(snd_pcm_substream_t * substream,
+					  snd_pcm_hw_params_t * hw_params)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice->extra;
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0 && trident->tlb.entries) {
+		if (voice->memblk)
+			snd_trident_free_pages(trident, voice->memblk);
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		if (voice->memblk == NULL)
+			return -ENOMEM;
+	}
+
+	/* voice management */
+
+	if (params_buffer_size(hw_params) / 2 != params_buffer_size(hw_params)) {
+		if (evoice == NULL) {
+			evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+			if (evoice == NULL)
+				return -ENOMEM;
+			voice->extra = evoice;
+			evoice->substream = substream;
+		}
+	} else {
+		if (evoice != NULL) {
+			snd_trident_free_voice(trident, evoice);
+			voice->extra = evoice = NULL;
+		}
+	}
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_foldback_hw_free
+  
+   Description: Release the hardware resources for the foldback device.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_hw_free(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice ? voice->extra : NULL;
+
+	if (trident->tlb.entries && voice && voice->memblk) {
+		snd_trident_free_pages(trident, voice->memblk);
+		voice->memblk = NULL;
+	}
+	snd_pcm_lib_free_pages(substream);
+	if (evoice != NULL) {
+		snd_trident_free_voice(trident, evoice);
+		voice->extra = NULL;
+	}
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_foldback_prepare
+  
+   Description: Prepare foldback capture device for playback.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_prepare(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	snd_trident_voice_t *evoice = voice->extra;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+
+	/* Set channel buffer Address */
+	if (voice->memblk)
+		voice->LBA = voice->memblk->offset;
+	else
+		voice->LBA = runtime->dma_addr;
+
+	/* set target ESO for channel */
+	voice->ESO = runtime->buffer_size - 1;	/* in samples */
+
+	/* set sample rate */
+	voice->Delta = 0x1000;
+	voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
+
+	voice->CSO = 0;
+	voice->CTRL = snd_trident_control_mode(substream);
+	voice->FMC = 3;
+	voice->RVol = 0x7f;
+	voice->CVol = 0x7f;
+	voice->GVSel = 1;
+	voice->Pan = 0x7f;	/* mute */
+	voice->Vol = 0x3ff;	/* mute */
+	voice->EC = 0;
+	voice->Alpha = 0;
+	voice->FMS = 0;
+	voice->Attribute = 0;
+
+	/* set up capture channel */
+	outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));
+
+	snd_trident_write_voice_regs(trident, voice);
+
+	if (evoice != NULL) {
+		evoice->Delta = voice->Delta;
+		evoice->spurious_threshold = voice->spurious_threshold;
+		evoice->LBA = voice->LBA;
+		evoice->CSO = 0;
+		evoice->ESO = (runtime->period_size * 2) - 1; /* in samples */
+		evoice->CTRL = voice->CTRL;
+		evoice->FMC = 3;
+		evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+		evoice->EC = 0;
+		evoice->Alpha = 0;
+		evoice->FMS = 0;
+		evoice->Vol = 0x3ff;			/* mute */
+		evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+		evoice->Pan = 0x7f;			/* mute */
+		evoice->Attribute = 0;
+		snd_trident_write_voice_regs(trident, evoice);
+	}
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif_hw_params
+  
+   Description: Set the hardware parameters for the spdif device.
+  
+   Parameters:  substream  - PCM substream class
+		hw_params  - hardware parameters
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_hw_params(snd_pcm_substream_t * substream,
+				       snd_pcm_hw_params_t * hw_params)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned long flags;
+	unsigned int old_bits = 0, change = 0;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0 && trident->tlb.entries) {
+		if (voice->memblk)
+			snd_trident_free_pages(trident, voice->memblk);
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		if (voice->memblk == NULL)
+			return -ENOMEM;
+	}
+
+	/* prepare SPDIF channel */
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	old_bits = trident->spdif_pcm_bits;
+	if (old_bits & IEC958_AES0_PROFESSIONAL)
+		trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
+	else
+		trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
+	if (params_rate(hw_params) >= 48000) {
+		trident->spdif_pcm_ctrl = 0x3c;	// 48000 Hz
+		trident->spdif_pcm_bits |=
+			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+				IEC958_AES0_PRO_FS_48000 :
+				(IEC958_AES3_CON_FS_48000 << 24);
+	}
+	else if (params_rate(hw_params) >= 44100) {
+		trident->spdif_pcm_ctrl = 0x3e;	// 44100 Hz
+		trident->spdif_pcm_bits |=
+			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+				IEC958_AES0_PRO_FS_44100 :
+				(IEC958_AES3_CON_FS_44100 << 24);
+	}
+	else {
+		trident->spdif_pcm_ctrl = 0x3d;	// 32000 Hz
+		trident->spdif_pcm_bits |=
+			trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+				IEC958_AES0_PRO_FS_32000 :
+				(IEC958_AES3_CON_FS_32000 << 24);
+	}
+	change = old_bits != trident->spdif_pcm_bits;
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+
+	if (change)
+		snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif_hw_free
+  
+   Description: Release the hardware resources for the spdif device.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_hw_free(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+
+	if (trident->tlb.entries && voice && voice->memblk) {
+		snd_trident_free_pages(trident, voice->memblk);
+		voice->memblk = NULL;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif_prepare
+  
+   Description: Prepare SPDIF device for playback.
+  
+   Parameters:  substream  - PCM substream class
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned int RESO, LBAO;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);	
+
+	/* set delta (rate) value */
+	voice->Delta = snd_trident_convert_rate(runtime->rate);
+	voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+	/* set Loop Back Address */
+	LBAO = runtime->dma_addr;
+	if (voice->memblk)
+		voice->LBA = voice->memblk->offset;
+	else
+		voice->LBA = LBAO;
+
+	/* set target ESO for channel */
+	RESO = runtime->buffer_size - 1;
+	voice->ESO = (runtime->period_size * 2) - 1;
+
+	/* set ctrl mode */
+	voice->CTRL = snd_trident_control_mode(substream);
+
+	voice->FMC = 3;
+	voice->RVol = 0x7f;
+	voice->CVol = 0x7f;
+	voice->GVSel = 1;
+	voice->Pan = 0x7f;
+	voice->Vol = 0x3ff;
+	voice->EC = 0;
+	voice->CSO = 0;
+	voice->Alpha = 0;
+	voice->FMS = 0;
+	voice->Attribute = 0;
+
+	/* prepare surrogate IRQ channel */
+	snd_trident_write_voice_regs(trident, voice);
+
+	outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
+	outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
+	outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
+	outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
+	outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));
+
+	/* set SPDIF setting */
+	outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_trigger
+  
+   Description: Start/stop devices
+  
+   Parameters:  substream  - PCM substream class
+   		cmd	- trigger command (STOP, GO)
+  
+   Returns:     Error status
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_trigger(snd_pcm_substream_t *substream,
+			       int cmd)
+				    
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_substream_t *s;
+	unsigned int what, whati, capture_flag, spdif_flag;
+	snd_trident_voice_t *voice, *evoice;
+	unsigned int val, go;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		go = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		go = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	what = whati = capture_flag = spdif_flag = 0;
+	s = substream;
+	spin_lock(&trident->reg_lock);
+	val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+	do {
+		if ((trident_t *) _snd_pcm_chip(s->pcm) == trident) {
+			voice = (snd_trident_voice_t *) s->runtime->private_data;
+			evoice = voice->extra;
+			what |= 1 << (voice->number & 0x1f);
+			if (evoice == NULL) {
+				whati |= 1 << (voice->number & 0x1f);
+			} else {
+				what |= 1 << (evoice->number & 0x1f);
+				whati |= 1 << (evoice->number & 0x1f);
+			}
+			if (go) {
+				voice->running = 1;
+				voice->stimer = val;
+			} else {
+				voice->running = 0;
+			}
+			snd_pcm_trigger_done(s, substream);
+			if (voice->capture)
+				capture_flag = 1;
+			if (voice->spdif)
+				spdif_flag = 1;
+		}
+		s = s->link_next;
+	} while (s != substream);
+	if (spdif_flag) {
+		outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+		outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	}
+	if (!go)
+		outl(what, TRID_REG(trident, T4D_STOP_B));
+	val = inl(TRID_REG(trident, T4D_AINTEN_B));
+	if (go) {
+		val |= whati;
+	} else {
+		val &= ~whati;
+	}
+	outl(val, TRID_REG(trident, T4D_AINTEN_B));
+	if (go) {
+		outl(what, TRID_REG(trident, T4D_START_B));
+		
+		if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
+			outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
+	} else {
+		if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
+			outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
+	}
+	spin_unlock(&trident->reg_lock);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_playback_pointer
+  
+   Description: This routine return the playback position
+                
+   Parameters:	substream  - PCM substream class
+
+   Returns:     position of buffer
+  
+  ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_playback_pointer(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned int cso;
+
+	if (!voice->running)
+		return 0;
+
+	spin_lock(&trident->reg_lock);
+
+	outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+
+	if (trident->device != TRIDENT_DEVICE_ID_NX) {
+		cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+	} else {		// ID_4DWAVE_NX
+		cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
+	}
+
+	if (++cso > runtime->buffer_size)
+		cso = runtime->buffer_size;
+
+	spin_unlock(&trident->reg_lock);
+
+	return cso;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_capture_pointer
+  
+   Description: This routine return the capture position
+                
+   Paramters:   pcm1    - PCM device class
+
+   Returns:     position of buffer
+  
+  ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_capture_pointer(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned int result;
+
+	if (!voice->running)
+		return 0;
+
+	result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
+	if (runtime->channels > 1)
+		result >>= 1;
+	result = runtime->buffer_size - result;
+
+	// printk("capture result = 0x%x, cso = 0x%x\n", result, cso);
+
+	return result;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif_pointer
+  
+   Description: This routine return the SPDIF playback position
+                
+   Parameters:	substream  - PCM substream class
+
+   Returns:     position of buffer
+  
+  ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_spdif_pointer(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	unsigned int result;
+
+	if (!voice->running)
+		return 0;
+
+	result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+
+	return result;
+}
+
+/*
+ *  Playback support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(256*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(256*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *  Foldback capture support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_foldback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_48000,
+	rate_min:		48000,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+/*
+ *  SPDIF playback support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_spdif =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000),
+	rate_min:		32000,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+	unsigned long flags;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+	trident_t *trident;
+
+	if (voice) {
+		trident = voice->trident;
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		snd_trident_free_voice(trident, voice);
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+	}
+}
+
+static int snd_trident_playback_open(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice;
+
+	spin_lock_irq(&trident->reg_lock);
+	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+	if (voice == NULL) {
+		spin_unlock_irq(&trident->reg_lock);
+		return -EAGAIN;
+	}
+	spin_unlock_irq(&trident->reg_lock);
+	snd_trident_pcm_mixer_build(trident, voice, substream);
+	voice->substream = substream;
+	runtime->private_data = voice;
+	runtime->private_free = snd_trident_pcm_free_substream;
+	runtime->hw = snd_trident_playback;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_playback_close
+  
+   Description: This routine will close the 4DWave playback device. For now 
+                we will simply free the dma transfer buffer.
+                
+   Parameters:	substream  - PCM substream class
+
+  ---------------------------------------------------------------------------*/
+static int snd_trident_playback_close(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+
+	snd_trident_pcm_mixer_free(trident, voice, substream);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif_open
+  
+   Description: This routine will open the 4DWave SPDIF device.
+
+   Parameters:	substream  - PCM substream class
+
+   Returns:     status  - success or failure flag
+  
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_open(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_trident_voice_t *voice;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	
+	spin_lock_irq(&trident->reg_lock);
+	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+	if (voice == NULL) {
+		spin_unlock_irq(&trident->reg_lock);
+		return -EAGAIN;
+	}
+	voice->spdif = 1;
+	voice->substream = substream;
+	trident->spdif_pcm_bits = trident->spdif_bits;
+	spin_unlock_irq(&trident->reg_lock);
+
+	runtime->private_data = voice;
+	runtime->private_free = snd_trident_pcm_free_substream;
+	runtime->hw = snd_trident_spdif;
+
+	trident->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
+
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+	return 0;
+}
+
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif_close
+  
+   Description: This routine will close the 4DWave SPDIF device.
+                
+   Parameters:	substream  - PCM substream class
+
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_close(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+
+	spin_lock_irq(&trident->reg_lock);
+	// restore default SPDIF setting
+	outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+	spin_unlock_irq(&trident->reg_lock);
+	trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_capture_open
+  
+   Description: This routine will open the 4DWave capture device.
+
+   Parameters:	substream  - PCM substream class
+
+   Returns:     status  - success or failure flag
+
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_open(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_trident_voice_t *voice;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irq(&trident->reg_lock);
+	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+	if (voice == NULL) {
+		spin_unlock_irq(&trident->reg_lock);
+		return -EAGAIN;
+	}
+	voice->capture = 1;
+	spin_unlock_irq(&trident->reg_lock);
+	voice->substream = substream;
+	runtime->private_data = voice;
+	runtime->private_free = snd_trident_pcm_free_substream;
+	runtime->hw = snd_trident_capture;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_capture_close
+  
+   Description: This routine will close the 4DWave capture device. For now 
+                we will simply free the dma transfer buffer.
+                
+   Parameters:	substream  - PCM substream class
+
+  ---------------------------------------------------------------------------*/
+static int snd_trident_capture_close(snd_pcm_substream_t * substream)
+{
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_foldback_open
+  
+   Description: This routine will open the 4DWave foldback capture device.
+
+   Parameters:	substream  - PCM substream class
+
+   Returns:     status  - success or failure flag
+
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_open(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_trident_voice_t *voice;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irq(&trident->reg_lock);
+	voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+	if (voice == NULL) {
+		spin_unlock_irq(&trident->reg_lock);
+		return -EAGAIN;
+	}
+	if (trident->tlb.entries) {
+		voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+		if (voice->memblk == NULL) {
+			snd_trident_free_voice(trident, voice);
+			spin_unlock_irq(&trident->reg_lock);
+			return -ENOMEM;
+		}
+	}
+	voice->substream = substream;
+	voice->foldback_chan = substream->number;
+	spin_unlock_irq(&trident->reg_lock);
+	runtime->private_data = voice;
+	runtime->private_free = snd_trident_pcm_free_substream;
+	runtime->hw = snd_trident_foldback;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_foldback_close
+  
+   Description: This routine will close the 4DWave foldback capture device. 
+		For now we will simply free the dma transfer buffer.
+                
+   Parameters:	substream  - PCM substream class
+
+  ---------------------------------------------------------------------------*/
+static int snd_trident_foldback_close(snd_pcm_substream_t * substream)
+{
+	trident_t *trident = snd_pcm_substream_chip(substream);
+	snd_trident_voice_t *voice;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	voice = (snd_trident_voice_t *) runtime->private_data;
+	
+	/* stop capture channel */
+	spin_lock_irq(&trident->reg_lock);
+	outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
+	spin_unlock_irq(&trident->reg_lock);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   PCM operations
+  ---------------------------------------------------------------------------*/
+
+static snd_pcm_ops_t snd_trident_playback_ops = {
+	open:		snd_trident_playback_open,
+	close:		snd_trident_playback_close,
+	ioctl:		snd_trident_ioctl,
+	hw_params:	snd_trident_playback_hw_params,
+	hw_free:	snd_trident_playback_hw_free,
+	prepare:	snd_trident_playback_prepare,
+	trigger:	snd_trident_trigger,
+	pointer:	snd_trident_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_capture_ops = {
+	open:		snd_trident_capture_open,
+	close:		snd_trident_capture_close,
+	ioctl:		snd_trident_ioctl,
+	hw_params:	snd_trident_capture_hw_params,
+	hw_free:	snd_trident_capture_hw_free,
+	prepare:	snd_trident_capture_prepare,
+	trigger:	snd_trident_trigger,
+	pointer:	snd_trident_capture_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_si7018_capture_ops = {
+	open:		snd_trident_capture_open,
+	close:		snd_trident_capture_close,
+	ioctl:		snd_trident_ioctl,
+	hw_params:	snd_trident_si7018_capture_hw_params,
+	hw_free:	snd_trident_si7018_capture_hw_free,
+	prepare:	snd_trident_si7018_capture_prepare,
+	trigger:	snd_trident_trigger,
+	pointer:	snd_trident_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_foldback_ops = {
+	open:		snd_trident_foldback_open,
+	close:		snd_trident_foldback_close,
+	ioctl:		snd_trident_ioctl,
+	hw_params:	snd_trident_foldback_hw_params,
+	hw_free:	snd_trident_foldback_hw_free,
+	prepare:	snd_trident_foldback_prepare,
+	trigger:	snd_trident_trigger,
+	pointer:	snd_trident_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_trident_spdif_ops = {
+	open:		snd_trident_spdif_open,
+	close:		snd_trident_spdif_close,
+	ioctl:		snd_trident_ioctl,
+	hw_params:	snd_trident_spdif_hw_params,
+	hw_free:	snd_trident_spdif_hw_free,
+	prepare:	snd_trident_spdif_prepare,
+	trigger:	snd_trident_trigger,
+	pointer:	snd_trident_spdif_pointer,
+};
+
+/*---------------------------------------------------------------------------
+   snd_trident_pcm_free
+  
+   Description: This routine release the 4DWave private data.
+                
+   Paramters:   private_data - pointer to 4DWave device info.
+
+   Returns:     None
+  
+  ---------------------------------------------------------------------------*/
+static void snd_trident_pcm_free(snd_pcm_t *pcm)
+{
+	trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return);
+	trident->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm)
+{
+	trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return);
+	trident->foldback = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm)
+{
+	trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return);
+	trident->spdif = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_pcm
+  
+   Description: This routine registers the 4DWave device for PCM support.
+                
+   Paramters:   trident - pointer to target device class for 4DWave.
+
+   Returns:     None
+  
+  ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = trident;
+	pcm->private_free = snd_trident_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+				trident->device != TRIDENT_DEVICE_ID_SI7018 ?
+					&snd_trident_capture_ops :
+					&snd_trident_si7018_capture_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "Trident 4DWave");
+	trident->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_foldback_pcm
+  
+   Description: This routine registers the 4DWave device for foldback PCM support.
+                
+   Paramters:   trident - pointer to target device class for 4DWave.
+
+   Returns:     None
+  
+  ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *foldback;
+	int err;
+	int num_chan = 3;
+	snd_pcm_substream_t *substream;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if (trident->device == TRIDENT_DEVICE_ID_NX)
+		num_chan = 4;
+	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0)
+		return err;
+
+	foldback->private_data = trident;
+	foldback->private_free = snd_trident_foldback_pcm_free;
+	snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
+	foldback->info_flags = 0;
+	strcpy(foldback->name, "Trident 4DWave");
+	substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+	strcpy(substream->name, "Front Mixer");
+	substream = substream->next;
+	strcpy(substream->name, "Reverb Mixer");
+	substream = substream->next;
+	strcpy(substream->name, "Chorus Mixer");
+	if (num_chan == 4) {
+		substream = substream->next;
+		strcpy(substream->name, "Second AC'97 ADC");
+	}
+	trident->foldback = foldback;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = foldback;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_spdif
+  
+   Description: This routine registers the 4DWave-NX device for SPDIF support.
+                
+   Paramters:   trident - pointer to target device class for 4DWave-NX.
+
+   Returns:     None
+  
+  ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *spdif;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0)
+		return err;
+
+	spdif->private_data = trident;
+	spdif->private_free = snd_trident_spdif_pcm_free;
+	snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
+	spdif->info_flags = 0;
+	strcpy(spdif->name, "Trident 4DWave IEC958");
+	trident->spdif = spdif;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = spdif;
+	return 0;
+}
+
+/*
+ *  Mixer part
+ */
+
+
+/*---------------------------------------------------------------------------
+    snd_trident_spdif_control
+
+    Description: enable/disable S/PDIF out from ac97 mixer
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_trident_spdif_control_get(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	val = trident->spdif_ctrl;
+	ucontrol->value.integer.value[0] = val == kcontrol->private_value;
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+static int snd_trident_spdif_control_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char val;
+	int change;
+
+	val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	/* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
+	change = trident->spdif_ctrl != val;
+	trident->spdif_ctrl = val;
+	if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) {
+		outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+		outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	}
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+	info:		snd_trident_spdif_control_info,
+	get:		snd_trident_spdif_control_get,
+	put:		snd_trident_spdif_control_put,
+	private_value:  0x28,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_spdif_default
+
+    Description: put/get the S/PDIF default settings
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_trident_spdif_default_get(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+static int snd_trident_spdif_default_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = (ucontrol->value.iec958.status[0] << 0) |
+	      (ucontrol->value.iec958.status[1] << 8) |
+	      (ucontrol->value.iec958.status[2] << 16) |
+	      (ucontrol->value.iec958.status[3] << 24);
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	change = trident->spdif_bits != val;
+	trident->spdif_bits = val;
+	if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0)
+		outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_trident_spdif_default_info,
+	get:		snd_trident_spdif_default_get,
+	put:		snd_trident_spdif_default_put
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_spdif_mask
+
+    Description: put/get the S/PDIF mask
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_trident_spdif_mask_get(snd_kcontrol_t * kcontrol,
+				      snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	info:		snd_trident_spdif_mask_info,
+	get:		snd_trident_spdif_mask_get,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_spdif_stream
+
+    Description: put/get the S/PDIF stream settings
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_trident_spdif_stream_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+static int snd_trident_spdif_stream_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = (ucontrol->value.iec958.status[0] << 0) |
+	      (ucontrol->value.iec958.status[1] << 8) |
+	      (ucontrol->value.iec958.status[2] << 16) |
+	      (ucontrol->value.iec958.status[3] << 24);
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	change = trident->spdif_pcm_bits != val;
+	trident->spdif_pcm_bits = val;
+	if (trident->spdif != NULL)
+		outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_trident_spdif_stream_info,
+	get:		snd_trident_spdif_stream_get,
+	put:		snd_trident_spdif_stream_put
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_ac97_control
+
+    Description: enable/disable rear path for ac97
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_ac97_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_trident_ac97_control_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+	ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+static int snd_trident_ac97_control_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char val;
+	int change = 0;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+	val &= ~(1 << kcontrol->private_value);
+	if (ucontrol->value.integer.value[0])
+		val |= 1 << kcontrol->private_value;
+	change = val != trident->ac97_ctrl;
+	trident->ac97_ctrl = val;
+	outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "Rear Path",
+	info:		snd_trident_ac97_control_info,
+	get:		snd_trident_ac97_control_get,
+	put:		snd_trident_ac97_control_put,
+	private_value:  4,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_vol_control
+
+    Description: wave & music volume control
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_trident_vol_control_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+
+	val = trident->musicvol_wavevol;
+	ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff);
+	ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff);
+	return 0;
+}
+
+static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	val = trident->musicvol_wavevol;
+	val &= ~(0xffff << kcontrol->private_value);
+	val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
+	        ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
+	change = val != trident->musicvol_wavevol;
+	outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "Music Playback Volume",
+	info:		snd_trident_vol_control_info,
+	get:		snd_trident_vol_control_get,
+	put:		snd_trident_vol_control_put,
+	private_value:  16,
+};
+
+static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "Wave Playback Volume",
+	info:		snd_trident_vol_control_info,
+	get:		snd_trident_vol_control_get,
+	put:		snd_trident_vol_control_put,
+	private_value:  0,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_pcm_vol_control
+
+    Description: PCM front volume control
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+		uinfo->value.integer.max = 1023;
+	return 0;
+}
+
+static int snd_trident_pcm_vol_control_get(snd_kcontrol_t * kcontrol,
+					   snd_ctl_elem_value_t * ucontrol)
+{
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		ucontrol->value.integer.value[0] = 1023 - mix->vol;
+	} else {
+		ucontrol->value.integer.value[0] = 255 - (mix->vol>>2);
+	}
+	return 0;
+}
+
+static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol,
+					   snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+	unsigned int val;
+	int change = 0;
+
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		val = 1023 - (ucontrol->value.integer.value[0] & 1023);
+	} else {
+		val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
+	}
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	change = val != mix->vol;
+	mix->vol = val;
+	if (mix->voice != NULL)
+		snd_trident_write_vol_reg(trident, mix->voice, val);
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "PCM Front Playback Volume",
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	info:		snd_trident_pcm_vol_control_info,
+	get:		snd_trident_pcm_vol_control_get,
+	put:		snd_trident_pcm_vol_control_put,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_pcm_pan_control
+
+    Description: PCM front pan control
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_pan_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_trident_pcm_pan_control_get(snd_kcontrol_t * kcontrol,
+					   snd_ctl_elem_value_t * ucontrol)
+{
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = mix->pan;
+	if (ucontrol->value.integer.value[0] & 0x40) {
+		ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f));
+	} else {
+		ucontrol->value.integer.value[0] |= 0x40;
+	}
+	return 0;
+}
+
+static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol,
+					   snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+	unsigned char val;
+	int change = 0;
+
+	if (ucontrol->value.integer.value[0] & 0x40)
+		val = ucontrol->value.integer.value[0] & 0x3f;
+	else
+		val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	change = val != mix->pan;
+	mix->pan = val;
+	if (mix->voice != NULL)
+		snd_trident_write_pan_reg(trident, mix->voice, val);
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "PCM Pan Playback Control",
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	info:		snd_trident_pcm_pan_control_info,
+	get:		snd_trident_pcm_pan_control_get,
+	put:		snd_trident_pcm_pan_control_put,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_pcm_rvol_control
+
+    Description: PCM reverb volume control
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_rvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_trident_pcm_rvol_control_get(snd_kcontrol_t * kcontrol,
+					    snd_ctl_elem_value_t * ucontrol)
+{
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = 127 - mix->rvol;
+	return 0;
+}
+
+static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol,
+					    snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+	unsigned short val;
+	int change = 0;
+
+	val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	change = val != mix->rvol;
+	mix->rvol = val;
+	if (mix->voice != NULL)
+		snd_trident_write_rvol_reg(trident, mix->voice, val);
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "PCM Reverb Playback Volume",
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	info:		snd_trident_pcm_rvol_control_info,
+	get:		snd_trident_pcm_rvol_control_get,
+	put:		snd_trident_pcm_rvol_control_put,
+};
+
+/*---------------------------------------------------------------------------
+    snd_trident_pcm_cvol_control
+
+    Description: PCM chorus volume control
+  ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_cvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_trident_pcm_cvol_control_get(snd_kcontrol_t * kcontrol,
+					    snd_ctl_elem_value_t * ucontrol)
+{
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = 127 - mix->cvol;
+	return 0;
+}
+
+static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol,
+					    snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	trident_t *trident = snd_kcontrol_chip(kcontrol);
+	snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value;
+	unsigned short val;
+	int change = 0;
+
+	val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	change = val != mix->cvol;
+	mix->cvol = val;
+	if (mix->voice != NULL)
+		snd_trident_write_cvol_reg(trident, mix->voice, val);
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "PCM Chorus Playback Volume",
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	info:		snd_trident_pcm_cvol_control_info,
+	get:		snd_trident_pcm_cvol_control_get,
+	put:		snd_trident_pcm_cvol_control_put,
+};
+
+static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int activate)
+{
+	snd_runtime_check(kctl != NULL, return);
+	if (activate)
+		kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	else
+		kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+}
+
+static void snd_trident_notify_pcm_change(snd_card_t * card, snd_trident_pcm_mixer_t * tmix, int activate)
+{
+	snd_trident_notify_pcm_change1(card, tmix->ctl_vol, activate);
+	snd_trident_notify_pcm_change1(card, tmix->ctl_pan, activate);
+	snd_trident_notify_pcm_change1(card, tmix->ctl_rvol, activate);
+	snd_trident_notify_pcm_change1(card, tmix->ctl_cvol, activate);
+}
+
+static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream)
+{
+	snd_trident_pcm_mixer_t *tmix;
+
+	snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL);
+	tmix = &trident->pcm_mixer[substream->number];
+	tmix->voice = voice;
+	tmix->vol = T4D_DEFAULT_PCM_VOL;
+	tmix->pan = T4D_DEFAULT_PCM_PAN;
+	tmix->rvol = T4D_DEFAULT_PCM_RVOL;
+	tmix->cvol = T4D_DEFAULT_PCM_CVOL;
+	snd_trident_notify_pcm_change(trident->card, tmix, 1);
+	return 0;
+}
+
+static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream)
+{
+	snd_trident_pcm_mixer_t *tmix;
+
+	snd_assert(trident != NULL && substream != NULL, return -EINVAL);
+	tmix = &trident->pcm_mixer[substream->number];
+	tmix->voice = NULL;
+	snd_trident_notify_pcm_change(trident->card, tmix, 0);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_mixer
+  
+   Description: This routine registers the 4DWave device for mixer support.
+                
+   Paramters:   trident - pointer to target device class for 4DWave.
+
+   Returns:     None
+  
+  ---------------------------------------------------------------------------*/
+
+static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device)
+{
+	ac97_t _ac97, *ac97;
+	snd_card_t * card = trident->card;
+	snd_kcontrol_t *kctl;
+	snd_ctl_elem_value_t uctl;
+	int idx, err;
+
+	memset(&uctl, 0, sizeof(uctl));
+
+	memset(&_ac97, 0, sizeof(_ac97));
+	_ac97.write = snd_trident_codec_write;
+	_ac97.read = snd_trident_codec_read;
+	_ac97.private_data = trident;
+	if ((err = snd_ac97_mixer(trident->card, &_ac97, &ac97)) < 0)
+		return err;
+
+	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0)
+			return err;
+		kctl->put(kctl, &uctl);
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0)
+			return err;
+		kctl->put(kctl, &uctl);
+		outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+	} else {
+		outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+	}
+
+	for (idx = 0; idx < 32; idx++) {
+		snd_trident_pcm_mixer_t *tmix;
+		
+		tmix = &trident->pcm_mixer[idx];
+		tmix->voice = NULL;
+		if ((kctl = tmix->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)tmix;
+		kctl->id.index = idx;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		
+		if ((kctl = tmix->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)tmix;
+		kctl->id.index = idx;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+
+		if ((kctl = tmix->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)tmix;
+		kctl->id.index = idx;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+
+		if ((kctl = tmix->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL)
+			return -ENOMEM;
+		kctl->private_value = (long)tmix;
+		kctl->id.index = idx;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+	}
+
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0)
+			return err;
+		kctl->put(kctl, &uctl);
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0)
+			return err;
+		kctl->put(kctl, &uctl);
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0)
+			return err;
+		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0)
+			return err;
+		kctl->id.device = pcm_spdif_device;
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0)
+			return err;
+		kctl->id.device = pcm_spdif_device;
+		trident->spdif_pcm_ctl = kctl;
+	}
+
+	return 0;
+}
+
+/*  
+ *  /proc interface
+ */
+
+static void snd_trident_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	trident_t *trident = snd_magic_cast(trident_t, entry->private_data, return);
+	char *s;
+
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_SI7018:
+		s = "SiS 7018 Audio";
+		break;
+	case TRIDENT_DEVICE_ID_DX:
+		s = "Trident 4DWave PCI DX";
+		break;
+	case TRIDENT_DEVICE_ID_NX:
+		s = "Trident 4DWave PCI NX";
+		break;
+	default:
+		s = "???";
+	}
+	snd_iprintf(buffer, "%s\n\n", s);
+	snd_iprintf(buffer, "Spurious IRQs    : %d\n", trident->spurious_irq_count);
+	snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
+		snd_iprintf(buffer, "Rear Speakers    : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
+		if (trident->tlb.entries) {
+			snd_iprintf(buffer,"\nVirtual Memory\n");
+			snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
+			snd_iprintf(buffer, "Memory Used    : %d\n", trident->tlb.memhdr->used);
+			snd_iprintf(buffer, "Memory Free    : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
+		}
+	}
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	snd_iprintf(buffer,"\nWavetable Synth\n");
+	snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
+	snd_iprintf(buffer, "Memory Used    : %d\n", trident->synth.current_size);
+	snd_iprintf(buffer, "Memory Free    : %d\n", (trident->synth.max_size-trident->synth.current_size));
+#endif
+}
+
+static void __devinit snd_trident_proc_init(trident_t * trident)
+{
+	snd_info_entry_t *entry;
+	char *s = "trident";
+	
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+		s = "sis7018";
+	if ((entry = snd_info_create_card_entry(trident->card, s, trident->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = trident;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_trident_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	trident->proc_entry = entry;
+}
+
+static void snd_trident_proc_done(trident_t * trident)
+{
+	if (trident->proc_entry) {
+		snd_info_unregister(trident->proc_entry);
+		trident->proc_entry = NULL;
+	}
+}
+
+static int snd_trident_dev_free(snd_device_t *device)
+{
+	trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO);
+	return snd_trident_free(trident);
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_create
+  
+   Description: This routine will create the device specific class for
+                the 4DWave card. It will also perform basic initialization.
+                
+   Paramters:   card  - which card to create
+                pci   - interface to PCI bus resource info
+                dma1ptr - playback dma buffer
+                dma2ptr - capture dma buffer
+                irqptr  -  interrupt resource info
+
+   Returns:     4DWave device class private data
+  
+  ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_create(snd_card_t * card,
+		       struct pci_dev *pci,
+		       int pcm_streams,
+		       int pcm_spdif_device,
+		       int max_wavetable_size,
+		       trident_t ** rtrident)
+{
+	trident_t *trident;
+	unsigned int i;
+	int err;
+	signed long end_time;
+	snd_trident_voice_t *voice;
+	snd_trident_pcm_mixer_t *tmix;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_trident_dev_free,
+	};
+
+	*rtrident = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 30 bits */
+	if (!pci_dma_supported(pci, 0x3fffffff)) {
+		snd_printk("architecture does not support 30bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x3fffffff);
+	
+	trident = snd_magic_kcalloc(trident_t, 0, GFP_KERNEL);
+	if (trident == NULL)
+		return -ENOMEM;
+	trident->device = (pci->vendor << 16) | pci->device;
+	trident->card = card;
+	trident->pci = pci;
+	spin_lock_init(&trident->reg_lock);
+	spin_lock_init(&trident->event_lock);
+	spin_lock_init(&trident->voice_alloc);
+	if (pcm_streams < 1)
+		pcm_streams = 1;
+	if (pcm_streams > 32)
+		pcm_streams = 32;
+	trident->ChanPCM = pcm_streams;
+	if (max_wavetable_size < 0 )
+		max_wavetable_size = 0;
+	trident->synth.max_size = max_wavetable_size * 1024;
+	trident->port = pci_resource_start(pci, 0);
+	trident->irq = -1;
+
+	trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
+	pci_set_master(pci);
+	trident->port = pci_resource_start(pci, 0);
+
+	if ((trident->res_port = request_region(trident->port, 0x100, "Trident Audio")) == NULL) {
+		snd_trident_free(trident);
+		snd_printk("unable to grab I/O region 0x%lx-0x%lx\n", trident->port, trident->port + 0x100 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) {
+		snd_trident_free(trident);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	trident->irq = pci->irq;
+
+	/* allocate 16k-aligned TLB for NX cards */
+	trident->tlb.entries = NULL;
+	trident->tlb.buffer = NULL;
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		/* allocate and setup TLB page table */
+		/* each entry has 4 bytes (physical PCI address) */
+		/* TLB array must be aligned to 16kB !!! so we allocate
+		   32kB region and correct offset when necessary */
+		trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr);
+		if (trident->tlb.buffer == NULL) {
+			snd_trident_free(trident);
+			snd_printk("unable to allocate TLB buffer\n");
+			return -ENOMEM;
+		}
+		trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1));
+		trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1);
+		/* allocate shadow TLB page table (virtual addresses) */
+		trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long));
+		if (trident->tlb.shadow_entries == NULL) {
+			snd_trident_free(trident);
+			snd_printk("unable to allocate shadow TLB entries\n");
+			return -ENOMEM;
+		}
+		/* allocate and setup silent page and initialise TLB entries */
+		trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr);
+		if (trident->tlb.silent_page == 0UL) {
+			snd_trident_free(trident);
+			snd_printk("unable to allocate silent page\n");
+			return -ENOMEM;
+		}
+		memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE);
+		for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) {
+			trident->tlb.entries[i] = trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1);
+			trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page;
+		}
+
+		/* use emu memory block manager code to manage tlb page allocation */
+		trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
+		if (trident->tlb.memhdr == NULL) {
+			snd_trident_free(trident);
+			return -ENOMEM;
+		}
+		trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t);
+	}
+
+	/* reset the legacy configuration and whole audio/wavetable block */
+	if (trident->device == TRIDENT_DEVICE_ID_DX ||
+	    trident->device == TRIDENT_DEVICE_ID_NX) {
+		pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
+		pci_write_config_byte(pci, 0x44, 0);	/* ports */
+		pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
+		if (trident->device == TRIDENT_DEVICE_ID_DX) {
+			pci_write_config_byte(pci, 0x46, 4); /* reset */
+			udelay(100);
+			pci_write_config_byte(pci, 0x46, 0); /* release reset */
+			udelay(100);
+		} else /* NX */ {
+			pci_write_config_byte(pci, 0x46, 1); /* reset */
+			udelay(100);
+			pci_write_config_byte(pci, 0x46, 0); /* release reset */
+			udelay(100);
+		}
+	}
+	
+	/* initialize chip */
+
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+		/* warm reset of the AC'97 codec */
+		outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+		udelay(100);
+		outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+		/* DAC on, disable SB IRQ and try to force ADC valid signal */
+		trident->ac97_ctrl = 0x0000004a;
+		outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+		/* wait, until the codec is ready */
+		end_time = (jiffies + (HZ * 3) / 4) + 1;
+		do {
+			if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0)
+				goto __dx_ok;
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		} while (end_time - (signed long)jiffies >= 0);
+		snd_printk("AC'97 codec ready error\n");
+		snd_trident_free(trident);
+		return -EIO;
+	      __dx_ok:
+		break;
+	case TRIDENT_DEVICE_ID_NX:
+		/* warm reset of the AC'97 codec */
+		outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+		udelay(100);
+		outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+		/* wait, until the codec is ready */
+		end_time = (jiffies + (HZ * 3) / 4) + 1;
+		do {
+			if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0)
+				goto __nx_ok;
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		} while (end_time - (signed long)jiffies >= 0);
+		snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
+		snd_trident_free(trident);
+		return -EIO;
+	      __nx_ok:
+		/* DAC on */
+		trident->ac97_ctrl = 0x00000002;
+		outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+		/* disable SB IRQ */
+		outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT));
+		break;
+	case TRIDENT_DEVICE_ID_SI7018:
+		pci_write_config_byte(pci, 0x46, 0x04);	/* SOFTWARE RESET */
+		udelay(100);
+		pci_write_config_byte(pci, 0x46, 0x00);
+		udelay(100);
+		/* disable AC97 GPIO interrupt */
+		outb(0x00, TRID_REG(trident, SI_AC97_GPIO));
+		/* initialize serial interface, force cold reset */
+		i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET;
+		outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+		udelay(1000);
+		/* remove cold reset */
+		i &= ~COLD_RESET;
+		outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+		udelay(2000);
+		/* wait, until the codec is ready */
+		end_time = (jiffies + (HZ * 3) / 4) + 1;
+		do {
+			if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0)
+				goto __si7018_ok;
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		} while (end_time - (signed long)jiffies >= 0);
+		snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
+		snd_trident_free(trident);
+		return -EIO;
+	      __si7018_ok:
+		/* enable 64 channel mode */
+		outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR));
+		break;
+	}
+
+	outl(0xffffffff, TRID_REG(trident, T4D_STOP_A));
+	outl(0xffffffff, TRID_REG(trident, T4D_STOP_B));
+	outl(0, TRID_REG(trident, T4D_AINTEN_A));
+	outl(0, TRID_REG(trident, T4D_AINTEN_B));
+
+	if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) {
+		snd_trident_free(trident);
+		return err;
+	}
+	
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		if (trident->tlb.entries != NULL) {
+			/* enable virtual addressing via TLB */
+			i = trident->tlb.entries_dmaaddr;
+			i |= 0x00000001;
+			outl(i, TRID_REG(trident, NX_TLBC));
+		} else {
+			outl(0, TRID_REG(trident, NX_TLBC));
+		}
+		/* initialize S/PDIF */
+		trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
+		outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+		outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	}
+
+	/* initialise synth voices */
+	for (i = 0; i < 64; i++) {
+		voice = &trident->synth.voices[i];
+		voice->number = i;
+		voice->trident = trident;
+	}
+	/* initialize pcm mixer entries */
+	for (i = 0; i < 32; i++) {
+		tmix = &trident->pcm_mixer[i];
+		tmix->vol = T4D_DEFAULT_PCM_VOL;
+		tmix->pan = T4D_DEFAULT_PCM_PAN;
+		tmix->rvol = T4D_DEFAULT_PCM_RVOL;
+		tmix->cvol = T4D_DEFAULT_PCM_CVOL;
+	}
+
+	snd_trident_enable_eso(trident);
+
+#ifdef CONFIG_PM
+	card->set_power_state = snd_trident_set_power_state;
+	card->power_state_private_data = trident;
+#endif
+
+	snd_trident_proc_init(trident);
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
+		snd_trident_free(trident);
+		return err;
+	}
+	*rtrident = trident;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_free
+  
+   Description: This routine will free the device specific class for
+            q    the 4DWave card. 
+                
+   Paramters:   trident  - device specific private data for 4DWave card
+
+   Returns:     None.
+  
+  ---------------------------------------------------------------------------*/
+
+int snd_trident_free(trident_t *trident)
+{
+	snd_trident_disable_eso(trident);
+	// Disable S/PDIF out
+	if (trident->device == TRIDENT_DEVICE_ID_NX)
+		outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	snd_trident_proc_done(trident);
+	if (trident->tlb.buffer) {
+		outl(0, TRID_REG(trident, NX_TLBC));
+		if (trident->tlb.memhdr)
+			snd_util_memhdr_free(trident->tlb.memhdr);
+		if (trident->tlb.silent_page)
+			snd_free_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+		if (trident->tlb.shadow_entries)
+			vfree(trident->tlb.shadow_entries);
+		snd_free_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, trident->tlb.buffer, trident->tlb.buffer_dmaaddr);
+	}
+	if (trident->irq >= 0)
+		free_irq(trident->irq, (void *)trident);
+	if (trident->res_port) {
+		release_resource(trident->res_port);
+		kfree_nocheck(trident->res_port);
+	}
+	snd_magic_kfree(trident);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_interrupt
+  
+   Description: ISR for Trident 4DWave device
+                
+   Paramters:   trident  - device specific private data for 4DWave card
+
+   Problems:    It seems that Trident chips generates interrupts more than
+                one time in special cases. The spurious interrupts are
+                detected via sample timer (T4D_STIMER) and computing
+                corresponding delta value. The limits are detected with
+                the method try & fail so it is possible that it won't
+                work on all computers. [jaroslav]
+
+   Returns:     None.
+  
+  ---------------------------------------------------------------------------*/
+
+static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	trident_t *trident = snd_magic_cast(trident_t, dev_id, return);
+	unsigned int audio_int, chn_int, stimer, channel, mask;
+	int delta;
+	snd_trident_voice_t *voice;
+
+	audio_int = inl(TRID_REG(trident, T4D_MISCINT));
+	if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0)
+		return;
+	if (audio_int & ADDRESS_IRQ) {
+		// get interrupt status for all channels
+		spin_lock(&trident->reg_lock);
+		stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+		chn_int = inl(TRID_REG(trident, T4D_AINT_A));
+		if (chn_int == 0)
+			goto __skip1;
+		outl(chn_int, TRID_REG(trident, T4D_AINT_A));	/* ack */
+	      __skip1:
+		chn_int = inl(TRID_REG(trident, T4D_AINT_B));
+		if (chn_int == 0)
+			goto __skip2;
+		for (channel = 63; channel >= 32; channel--) {
+			mask = 1 << (channel&0x1f);
+			if ((chn_int & mask) == 0)
+				continue;
+			voice = &trident->synth.voices[channel];
+			if (!voice->pcm || voice->substream == NULL) {
+				outl(mask, TRID_REG(trident, T4D_STOP_B));
+				continue;
+			}
+			delta = (int)stimer - (int)voice->stimer;
+			if (delta < 0)
+				delta = -delta;
+			if (delta < voice->spurious_threshold) {
+				/* do some statistics here */
+				trident->spurious_irq_count++;
+				if (trident->spurious_irq_max_delta < delta)
+					trident->spurious_irq_max_delta = delta;
+				continue;
+			}
+			voice->stimer = stimer;
+			if (voice->extra) {
+				/* update CSO for extra voice to preserve sync */
+				snd_trident_stop_voice(trident, voice->extra->number);
+				snd_trident_write_cso_reg(trident, voice->extra, 0);
+				snd_trident_start_voice(trident, voice->extra->number);
+			} else if (voice->spdif) {
+				snd_trident_stop_voice(trident, voice->number);
+				snd_trident_write_cso_reg(trident, voice, 0);
+				snd_trident_start_voice(trident, voice->number);
+			}
+			spin_unlock(&trident->reg_lock);
+			snd_pcm_period_elapsed(voice->substream);
+			spin_lock(&trident->reg_lock);
+		}
+		outl(chn_int, TRID_REG(trident, T4D_AINT_B));	/* ack */
+	      __skip2:
+		spin_unlock(&trident->reg_lock);
+	}
+	if (audio_int & MPU401_IRQ) {
+		if (trident->rmidi) {
+			snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data, regs);
+		} else {
+			inb(TRID_REG(trident, T4D_MPUR0));
+		}
+	}
+	// outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
+}
+
+/*---------------------------------------------------------------------------
+   snd_trident_attach_synthesizer, snd_trident_detach_synthesizer
+  
+   Description: Attach/detach synthesizer hooks
+                
+   Paramters:   trident  - device specific private data for 4DWave card
+
+   Returns:     None.
+  
+  ---------------------------------------------------------------------------*/
+int snd_trident_attach_synthesizer(trident_t *trident)
+{	
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
+			       sizeof(trident_t*), &trident->seq_dev) >= 0) {
+		strcpy(trident->seq_dev->name, "4DWave");
+		*(trident_t**)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
+	}
+#endif
+	return 0;
+}
+
+int snd_trident_detach_synthesizer(trident_t *trident)
+{
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	if (trident->seq_dev) {
+		snd_device_free(trident->card, trident->seq_dev);
+		trident->seq_dev = NULL;
+	}
+#endif
+	return 0;
+}
+
+snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port)
+{
+	snd_trident_voice_t *pvoice;
+	unsigned long flags;
+	int idx;
+
+	spin_lock_irqsave(&trident->voice_alloc, flags);
+	if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
+		idx = snd_trident_allocate_pcm_channel(trident);
+		if(idx < 0) {
+			spin_unlock_irqrestore(&trident->voice_alloc, flags);
+			return NULL;
+		}
+		pvoice = &trident->synth.voices[idx];
+		pvoice->use = 1;
+		pvoice->pcm = 1;
+		pvoice->capture = 0;
+		pvoice->spdif = 0;
+		pvoice->memblk = NULL;
+		spin_unlock_irqrestore(&trident->voice_alloc, flags);
+		return pvoice;
+	}
+	if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
+		idx = snd_trident_allocate_synth_channel(trident);
+		if(idx < 0) {
+			spin_unlock_irqrestore(&trident->voice_alloc, flags);
+			return NULL;
+		}
+		pvoice = &trident->synth.voices[idx];
+		pvoice->use = 1;
+		pvoice->synth = 1;
+		pvoice->client = client;
+		pvoice->port = port;
+		pvoice->memblk = NULL;
+		spin_unlock_irqrestore(&trident->voice_alloc, flags);
+		return pvoice;
+	}
+	if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
+	}
+	spin_unlock_irqrestore(&trident->voice_alloc, flags);
+	return NULL;
+}
+
+void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice)
+{
+	unsigned long flags;
+	void (*private_free)(snd_trident_voice_t *);
+	void *private_data;
+
+	if (voice == NULL || !voice->use)
+		return;
+	snd_trident_clear_voices(trident, voice->number, voice->number);
+	spin_lock_irqsave(&trident->voice_alloc, flags);
+	private_free = voice->private_free;
+	private_data = voice->private_data;
+	voice->private_free = NULL;
+	voice->private_data = NULL;
+	if (voice->pcm)
+		snd_trident_free_pcm_channel(trident, voice->number);
+	if (voice->synth)
+		snd_trident_free_synth_channel(trident, voice->number);
+	voice->use = voice->pcm = voice->synth = voice->midi = 0;
+	voice->capture = voice->spdif = 0;
+	voice->sample_ops = NULL;
+	voice->substream = NULL;
+	voice->extra = NULL;
+	spin_unlock_irqrestore(&trident->voice_alloc, flags);
+	if (private_free)
+		private_free(voice);
+}
+
+void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max)
+{
+	unsigned int i, val, mask[2] = { 0, 0 };
+
+	snd_assert(v_min <= 63, return);
+	snd_assert(v_max <= 63, return);
+	for (i = v_min; i <= v_max; i++)
+		mask[i >> 5] |= 1 << (i & 0x1f);
+	if (mask[0]) {
+		outl(mask[0], TRID_REG(trident, T4D_STOP_A));
+		val = inl(TRID_REG(trident, T4D_AINTEN_A));
+		outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A));
+	}
+	if (mask[1]) {
+		outl(mask[1], TRID_REG(trident, T4D_STOP_B));
+		val = inl(TRID_REG(trident, T4D_AINTEN_B));
+		outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B));
+	}
+}
+
+#ifdef CONFIG_PM
+
+void snd_trident_suspend(trident_t *trident)
+{
+	snd_card_t *card = trident->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+	snd_pcm_suspend_all(trident->pcm);
+	if (trident->foldback)
+		snd_pcm_suspend_all(trident->foldback);
+	if (trident->spdif)
+		snd_pcm_suspend_all(trident->spdif);
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+	case TRIDENT_DEVICE_ID_NX:
+		break;			/* TODO */
+	case TRIDENT_DEVICE_ID_SI7018:
+		break;
+	}
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+void snd_trident_resume(trident_t *trident)
+{
+	snd_card_t *card = trident->card;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+	case TRIDENT_DEVICE_ID_NX:
+		break;			/* TODO */
+	case TRIDENT_DEVICE_ID_SI7018:
+		break;
+	}
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+	snd_power_unlock(card);
+}
+
+static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	trident_t *chip = snd_magic_cast(trident_t, card->power_state_private_data, return -ENXIO);
+        
+	switch (power_state) {
+        case SNDRV_CTL_POWER_D0:
+        case SNDRV_CTL_POWER_D1:
+        case SNDRV_CTL_POWER_D2:
+        	snd_trident_resume(chip);
+                break;
+	case SNDRV_CTL_POWER_D3hot:
+        case SNDRV_CTL_POWER_D3cold:
+		snd_trident_suspend(chip);
+		break;
+        default:
+	        return -EINVAL;
+        }
+        return 0;
+}
+
+#endif /* CONFIG_PM */
+
+EXPORT_SYMBOL(snd_trident_alloc_voice);
+EXPORT_SYMBOL(snd_trident_free_voice);
+EXPORT_SYMBOL(snd_trident_start_voice);
+EXPORT_SYMBOL(snd_trident_stop_voice);
+EXPORT_SYMBOL(snd_trident_write_voice_regs);
+EXPORT_SYMBOL(snd_trident_clear_voices);
+/* trident_memory.c symbols */
+EXPORT_SYMBOL(snd_trident_synth_alloc);
+EXPORT_SYMBOL(snd_trident_synth_free);
+EXPORT_SYMBOL(snd_trident_synth_bzero);
+EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
diff -Nru linux/sound/pci/trident/trident_memory.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident_memory.c
--- linux/sound/pci/trident/trident_memory.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident_memory.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,428 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *  Copyright (c) by Scott McNab <sdm@fractalgraphics.com.au>
+ *
+ *  Trident 4DWave-NX memory page allocation (TLB area)
+ *  Trident chip can handle only 16MByte of the memory at the same time.
+ *
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/trident.h>
+
+/* page arguments of these two macros are Trident page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_tlb_bus(trident,page,ptr,addr) \
+	do { (trident)->tlb.entries[page] = (addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1); \
+	     (trident)->tlb.shadow_entries[page] = (ptr); } while (0)
+#define __tlb_to_ptr(trident,page) \
+	(void*)((trident)->tlb.shadow_entries[page])
+#define __tlb_to_addr(trident,page) \
+	(dma_addr_t)((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1))
+
+#if PAGE_SIZE == 4096
+/* page size == SNDRV_TRIDENT_PAGE_SIZE */
+#define ALIGN_PAGE_SIZE		PAGE_SIZE	/* minimum page size for allocation */
+#define MAX_ALIGN_PAGES		SNDRV_TRIDENT_MAX_PAGES	/* maxmium aligned pages */
+/* fill TLB entrie(s) corresponding to page with ptr */
+#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr)
+/* fill TLB entrie(s) corresponding to page with silence pointer */
+#define set_silent_tlb(trident,page)	__set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr)
+/* get aligned page from offset address */
+#define get_aligned_page(offset)	((offset) >> 12)
+/* get offset address from aligned page */
+#define aligned_page_offset(page)	((page) << 12)
+/* get buffer address from aligned page */
+#define page_to_ptr(trident,page)	__tlb_to_ptr(trident, page)
+/* get PCI physical address from aligned page */
+#define page_to_addr(trident,page)	__tlb_to_addr(trident, page)
+
+#elif PAGE_SIZE == 8192
+/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/
+#define ALIGN_PAGE_SIZE		PAGE_SIZE
+#define MAX_ALIGN_PAGES		(SNDRV_TRIDENT_MAX_PAGES / 2)
+#define get_aligned_page(offset)	((offset) >> 13)
+#define aligned_page_offset(page)	((page) << 13)
+#define page_to_ptr(trident,page)	__tlb_to_ptr(trident, (page) << 1)
+#define page_to_addr(trident,page)	__tlb_to_addr(trident, (page) << 1)
+
+/* fill TLB entries -- we need to fill two entries */
+static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr)
+{
+	page <<= 1;
+	__set_tlb_bus(trident, page, ptr, addr);
+	__set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE);
+}
+static inline void set_silent_tlb(trident_t *trident, int page)
+{
+	page <<= 1;
+	__set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+	__set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+}
+
+#else
+/* arbitrary size */
+#define UNIT_PAGES		(PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE)
+#define ALIGN_PAGE_SIZE		(SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES)
+#define MAX_ALIGN_PAGES		(SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES)
+/* Note: if alignment doesn't match to the maximum size, the last few blocks
+ * become unusable.  To use such blocks, you'll need to check the validity
+ * of accessing page in set_tlb_bus and set_silent_tlb.  search_empty()
+ * should also check it, too.
+ */
+#define get_aligned_page(offset)	((offset) / ALIGN_PAGE_SIZE)
+#define aligned_page_offset(page)	((page) * ALIGN_PAGE_SIZE)
+#define page_to_ptr(trident,page)	__tlb_to_ptr(trident, (page) * UNIT_PAGES)
+#define page_to_addr(trident,page)	__tlb_to_addr(trident, (page) * UNIT_PAGES)
+
+/* fill TLB entries -- UNIT_PAGES entries must be filled */
+static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++) {
+		__set_tlb_bus(trident, page, ptr, addr);
+		ptr += SNDRV_TRIDENT_PAGE_SIZE;
+		addr += SNDRV_TRIDENT_PAGE_SIZE;
+	}
+}
+static inline void set_silent_tlb(trident_t *trident, int page)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++)
+		__set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+}
+
+#endif /* PAGE_SIZE */
+
+/* calculate buffer pointer from offset address */
+inline static void *offset_ptr(trident_t *trident, int offset)
+{
+	char *ptr;
+	ptr = page_to_ptr(trident, get_aligned_page(offset));
+	ptr += offset % ALIGN_PAGE_SIZE;
+	return (void*)ptr;
+}
+
+/* first and last (aligned) pages of memory block */
+#define firstpg(blk)	(((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->first_page)
+#define lastpg(blk)	(((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->last_page)
+
+/*
+ * search empty pages which may contain given size
+ */
+static snd_util_memblk_t *
+search_empty(snd_util_memhdr_t *hdr, int size)
+{
+	snd_util_memblk_t *blk, *prev;
+	int page, psize;
+	struct list_head *p;
+
+	psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1);
+	prev = NULL;
+	page = 0;
+	list_for_each(p, &hdr->block) {
+		blk = list_entry(p, snd_util_memblk_t, list);
+		if (page + psize <= firstpg(blk))
+			goto __found_pages;
+		page = lastpg(blk) + 1;
+	}
+	if (page + psize > MAX_ALIGN_PAGES)
+		return NULL;
+
+__found_pages:
+	/* create a new memory block */
+	blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev);
+	if (blk == NULL)
+		return NULL;
+	blk->offset = aligned_page_offset(page); /* set aligned offset */
+	firstpg(blk) = page;
+	lastpg(blk) = page + psize - 1;
+	return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(void *pages)
+{
+	unsigned long ptr = (unsigned long)virt_to_phys(pages);
+	if (ptr & ~0x3fffffffUL) {
+		snd_printk("max memory size is 1GB!!\n");
+		return 0;
+	}
+	if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) {
+		snd_printk("page is not aligned\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * page allocation for DMA
+ */
+snd_util_memblk_t *
+snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size)
+{
+	unsigned long ptr;
+	snd_util_memhdr_t *hdr;
+	snd_util_memblk_t *blk;
+	int page;
+
+	snd_assert(trident != NULL, return NULL);
+	snd_assert(size > 0 && size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+	hdr = trident->tlb.memhdr;
+	snd_assert(hdr != NULL, return NULL);
+
+	if (! is_valid_page(pages))
+		return NULL;
+
+	down(&hdr->block_mutex);
+	blk = search_empty(hdr, size);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	/* set TLB entries */
+	ptr = (unsigned long)pages;
+	for (page = firstpg(blk); page <= lastpg(blk); page++) {
+		set_tlb_bus(trident, page, ptr, addr);
+		ptr += ALIGN_PAGE_SIZE;
+		addr += ALIGN_PAGE_SIZE;
+	}
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk)
+{
+	snd_util_memhdr_t *hdr;
+	int page;
+
+	snd_assert(trident != NULL, return -EINVAL);
+	snd_assert(blk != NULL, return -EINVAL);
+
+	hdr = trident->tlb.memhdr;
+	down(&hdr->block_mutex);
+	/* reset TLB entries */
+	for (page = firstpg(blk); page <= lastpg(blk); page++)
+		set_silent_tlb(trident, page);
+	/* free memory block */
+	__snd_util_mem_free(hdr, blk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+
+/*----------------------------------------------------------------
+ * memory allocation using multiple pages (for synth)
+ *----------------------------------------------------------------
+ * Unlike the DMA allocation above, non-contiguous pages are
+ * assigned to TLB.
+ *----------------------------------------------------------------*/
+
+/*
+ */
+static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk);
+static int synth_free_pages(trident_t *hw, snd_util_memblk_t *blk);
+
+/*
+ * allocate a synth sample area
+ */
+snd_util_memblk_t *
+snd_trident_synth_alloc(trident_t *hw, unsigned int size)
+{
+	snd_util_memblk_t *blk;
+	snd_util_memhdr_t *hdr = hw->tlb.memhdr; 
+
+	down(&hdr->block_mutex);
+	blk = __snd_util_mem_alloc(hdr, size);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	if (synth_alloc_pages(hw, blk)) {
+		__snd_util_mem_free(hdr, blk);
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+
+/*
+ * free a synth sample area
+ */
+int
+snd_trident_synth_free(trident_t *hw, snd_util_memblk_t *blk)
+{
+	snd_util_memhdr_t *hdr = hw->tlb.memhdr; 
+
+	down(&hdr->block_mutex);
+	synth_free_pages(hw, blk);
+	 __snd_util_mem_free(hdr, blk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+
+/*
+ * reset TLB entry and free kernel page
+ */
+static void clear_tlb(trident_t *trident, int page)
+{
+	void *ptr = page_to_ptr(trident, page);
+	dma_addr_t addr = page_to_addr(trident, page);
+	set_silent_tlb(trident, page);
+	snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr);
+}
+
+/* check new allocation range */
+static void get_single_page_range(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk, int *first_page_ret, int *last_page_ret)
+{
+	struct list_head *p;
+	snd_util_memblk_t *q;
+	int first_page, last_page;
+	first_page = firstpg(blk);
+	if ((p = blk->list.prev) != &hdr->block) {
+		q = list_entry(p, snd_util_memblk_t, list);
+		if (lastpg(q) == first_page)
+			first_page++;  /* first page was already allocated */
+	}
+	last_page = lastpg(blk);
+	if ((p = blk->list.next) != &hdr->block) {
+		q = list_entry(p, snd_util_memblk_t, list);
+		if (firstpg(q) == last_page)
+			last_page--; /* last page was already allocated */
+	}
+	*first_page_ret = first_page;
+	*last_page_ret = last_page;
+}
+
+/*
+ * allocate kernel pages and assign them to TLB
+ */
+static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk)
+{
+	int page, first_page, last_page;
+	void *ptr;
+	dma_addr_t addr;
+
+	firstpg(blk) = get_aligned_page(blk->offset);
+	lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1);
+	get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page);
+
+	/* allocate a kernel page for each Trident page -
+	 * fortunately Trident page size and kernel PAGE_SIZE is identical!
+	 */
+	for (page = first_page; page <= last_page; page++) {
+		ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr);
+		if (ptr == NULL)
+			goto __fail;
+		if (! is_valid_page(ptr)) {
+			snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr);
+			goto __fail;
+		}
+		set_tlb_bus(hw, page, (unsigned long)ptr, addr);
+	}
+	return 0;
+
+__fail:
+	/* release allocated pages */
+	last_page = page - 1;
+	for (page = first_page; page <= last_page; page++)
+		clear_tlb(hw, page);
+
+	return -ENOMEM;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(trident_t *trident, snd_util_memblk_t *blk)
+{
+	int page, first_page, last_page;
+
+	get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page);
+	for (page = first_page; page <= last_page; page++)
+		clear_tlb(trident, page);
+
+	return 0;
+}
+
+/*
+ * bzero(blk + offset, size)
+ */
+int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+
+	offset += blk->offset;
+	end_offset = offset + size;
+	page = get_aligned_page(offset) + 1;
+	do {
+		nextofs = aligned_page_offset(page);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		memset(offset_ptr(trident, offset), 0, temp);
+		offset = nextofs;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+
+	offset += blk->offset;
+	end_offset = offset + size;
+	page = get_aligned_page(offset) + 1;
+	do {
+		nextofs = aligned_page_offset(page);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		if (copy_from_user(offset_ptr(trident, offset), data, temp))
+			return -EFAULT;
+		offset = nextofs;
+		data += temp;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
diff -Nru linux/sound/pci/trident/trident_synth.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident_synth.c
--- linux/sound/pci/trident/trident_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident_synth.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1029 @@
+/*
+ *  Routines for Trident 4DWave NX/DX soundcards - Synthesizer
+ *  Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/trident.h>
+#include <sound/seq_device.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
+MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
+MODULE_LICENSE("GPL");
+
+/* linear to log pan conversion table (4.2 channel attenuation format) */
+static unsigned int pan_table[63] = {
+	7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, 
+	6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, 
+	5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, 
+	3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, 
+	3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, 
+	2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, 
+	1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, 
+	1588, 1543, 1499, 1456, 1415, 1375, 1336
+};
+
+#define LOG_TABLE_SIZE 386
+
+/* Linear half-attenuation to log conversion table in the format:
+ *   {linear volume, logarithmic attenuation equivalent}, ...
+ *
+ * Provides conversion from a linear half-volume value in the range
+ * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
+ * Halving the linear volume is equivalent to an additional 6dB of 
+ * logarithmic attenuation. The algorithm used in log_from_linear()
+ * therefore uses this table as follows:
+ * 
+ * - loop and for every time the volume is less than half the maximum 
+ *   volume (16384), add another 6dB and halve the maximum value used
+ *   for this comparison.
+ * - when the volume is greater than half the maximum volume, take
+ *   the difference of the volume to half volume (in the range [0,8192])
+ *   and look up the log_table[] to find the nearest entry.
+ * - take the logarithic component of this entry and add it to the 
+ *   resulting attenuation.
+ *
+ * Thus this routine provides a linear->log conversion for a range of
+ * [0,16384] using only 386 table entries
+ *
+ * Note: although this table stores log attenuation in 8.8 format, values
+ * were only calculated for 6 bits fractional precision, since that is
+ * the most precision offered by the trident hardware.
+ */
+
+static unsigned short log_table[LOG_TABLE_SIZE*2] =
+{
+	4, 0x0604, 19, 0x0600, 34, 0x05fc, 
+	49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, 
+	123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, 
+	198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, 
+	274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, 
+	350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, 
+	428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, 
+	506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, 
+	584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, 
+	663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, 
+	743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, 
+	824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, 
+	906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, 
+	988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, 
+	1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, 
+	1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, 
+	1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, 
+	1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, 
+	1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, 
+	1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, 
+	1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, 
+	1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, 
+	1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, 
+	1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, 
+	1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, 
+	2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, 
+	2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, 
+	2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, 
+	2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, 
+	2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, 
+	2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, 
+	2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, 
+	2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, 
+	2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, 
+	2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, 
+	2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, 
+	3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, 
+	3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, 
+	3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, 
+	3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, 
+	3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, 
+	3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, 
+	3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, 
+	3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, 
+	3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, 
+	4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, 
+	4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, 
+	4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, 
+	4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, 
+	4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, 
+	4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, 
+	4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, 
+	4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, 
+	4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, 
+	5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, 
+	5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, 
+	5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, 
+	5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, 
+	5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, 
+	5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, 
+	5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, 
+	5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, 
+	6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, 
+	6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, 
+	6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, 
+	6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, 
+	6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, 
+	6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, 
+	6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, 
+	6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, 
+	7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, 
+	7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, 
+	7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, 
+	7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, 
+	7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, 
+	7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, 
+	7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, 
+	8133, 0x0008, 8162, 0x0004, 8192, 0x0000
+};
+
+static unsigned short lookup_volume_table( unsigned short value )
+{
+	/* This code is an optimised version of:
+	 *   int i = 0;
+	 *   while( volume_table[i*2] < value )
+	 *       i++;
+	 *   return volume_table[i*2+1];
+	 */
+	unsigned short *ptr = log_table;
+	while( *ptr < value )
+		ptr += 2;
+	return *(ptr+1);
+}
+
+/* this function calculates a 8.8 fixed point logarithmic attenuation
+ * value from a linear volume value in the range 0 to 16384 */
+static unsigned short log_from_linear( unsigned short value )
+{
+	if (value >= 16384)
+		return 0x0000;
+	if (value) {
+		unsigned short result = 0;
+		int v, c;
+		for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
+			if( value >= v ) {
+				result += lookup_volume_table( (value - v) << c );
+				return result;
+			}
+			result += 0x0605;	/* 6.0205 (result of -20*log10(0.5)) */
+		}
+	}
+	return 0xffff;
+}
+
+/*
+ * Sample handling operations
+ */
+
+static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position);
+static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode);
+static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq);
+static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume);
+static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop);
+static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position);
+static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data);
+
+static snd_trident_sample_ops_t sample_ops =
+{
+	sample_start,
+	sample_stop,
+	sample_freq,
+	sample_volume,
+	sample_loop,
+	sample_pos,
+	sample_private1
+};
+
+static void snd_trident_simple_init(snd_trident_voice_t * voice)
+{
+	//voice->handler_wave = interrupt_wave;
+	//voice->handler_volume = interrupt_volume;
+	//voice->handler_effect = interrupt_effect;
+	//voice->volume_change = NULL;
+	voice->sample_ops = &sample_ops;
+}
+
+static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position)
+{
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+	unsigned long flags;
+	unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
+	unsigned int value;
+	unsigned int shift = 0;
+
+	instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+		voice->GVSel = 1;	/* route to Wave volume */
+
+	voice->CTRL = 0;
+	voice->Alpha = 0;
+	voice->FMS = 0;
+
+	loop_start = simple->loop_start >> 4;
+	loop_end = simple->loop_end >> 4;
+	sample_start = (simple->start + position) >> 4;
+	if( sample_start >= simple->size )
+		sample_start = simple->start >> 4;
+	sample_end = simple->size;
+	start_offset = position >> 4;
+
+	if (simple->format & SIMPLE_WAVE_16BIT) {
+		voice->CTRL |= 8;
+		shift++;
+	}
+	if (simple->format & SIMPLE_WAVE_STEREO) {
+		voice->CTRL |= 4;
+		shift++;
+	}
+	if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
+		voice->CTRL |= 2;
+
+	voice->LBA = simple->address.memory;
+
+	if (simple->format & SIMPLE_WAVE_LOOP) {
+		voice->CTRL |= 1;
+		voice->LBA += loop_start << shift;
+		if( start_offset >= loop_start ) {
+			voice->CSO = start_offset - loop_start;
+			voice->negCSO = 0;
+		} else {
+			voice->CSO = loop_start - start_offset;
+			voice->negCSO = 1;
+		}
+		voice->ESO = loop_end - loop_start - 1;
+	} else {
+		voice->LBA += start_offset << shift;
+		voice->CSO = sample_start;
+		voice->ESO = sample_end - 1;
+		voice->negCSO = 0;
+	}
+
+	if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
+		snd_trident_stop_voice(trident, voice->number);
+		voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+	}
+
+	/* set CSO sign */
+	value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+	if( voice->negCSO ) {
+		value |= 1 << (voice->number&31);
+	} else {
+		value &= ~(1 << (voice->number&31));
+	}
+	outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+
+	voice->Attribute = 0;	
+	snd_trident_write_voice_regs(trident, voice);
+	snd_trident_start_voice(trident, voice->number);
+	voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode)
+{
+	unsigned long flags;
+
+	if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
+		return;
+
+	switch (mode) {
+	default:
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		snd_trident_stop_voice(trident, voice->number);
+		voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		break;
+	case SAMPLE_STOP_LOOP:	/* disable loop only */
+		voice->CTRL &= ~1;
+		spin_lock_irqsave(&trident->reg_lock, flags);
+		outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+		outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
+		spin_unlock_irqrestore(&trident->reg_lock, flags);
+		break;
+	}
+}
+
+static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq)
+{
+	unsigned long flags;
+	freq >>= 4;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	if (freq == 44100)
+		voice->Delta = 0xeb3;
+	else if (freq == 8000)
+		voice->Delta = 0x2ab;
+	else if (freq == 48000)
+		voice->Delta = 0x1000;
+	else
+		voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
+
+	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
+		outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
+	} else {
+		outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
+	}
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume)
+{
+	unsigned long flags;
+	unsigned short value;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	voice->GVSel = 0;	/* use global music volume */
+	voice->FMC = 0x03;	/* fixme: can we do something useful with FMC? */
+	if (volume->volume >= 0) {
+		volume->volume &= 0x3fff;
+		/* linear volume -> logarithmic attenuation conversion
+		 * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
+		 * Vol register used when additional attenuation is required */
+		voice->RVol = 0;
+		voice->CVol = 0;
+		value = log_from_linear( volume->volume );
+		voice->Vol = 0;
+		voice->EC = (value & 0x3fff) >> 2;
+		if (value > 0x3fff) {
+			voice->EC |= 0xfc0;
+			if (value < 0x5f00 )
+				voice->Vol = ((value >> 8) - 0x3f) << 5;
+			else {
+				voice->Vol = 0x3ff;
+				voice->EC = 0xfff;
+			}
+		}
+	}
+	if (volume->lr >= 0) {
+		volume->lr &= 0x3fff;
+		/* approximate linear pan by attenuating channels */
+		if (volume->lr >= 0x2000) {	/* attenuate left (pan right) */
+			value = 0x3fff - volume->lr;
+			for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
+				if (value >= pan_table[voice->Pan] )
+					break;
+		} else {			/* attenuate right (pan left) */
+			for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
+				if (volume->lr >= pan_table[voice->Pan] )
+					break;
+			voice->Pan |= 0x40;
+		}
+	}
+	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
+		 ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
+		 (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+	value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
+	outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop)
+{
+	unsigned long flags;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+	unsigned int loop_start, loop_end;
+
+	instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+
+	loop_start = loop->start >> 4;
+	loop_end = loop->end >> 4;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+
+	voice->LBA = simple->address.memory + loop_start;
+	voice->CSO = 0;
+	voice->ESO = loop_end - loop_start - 1;
+
+	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
+	outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
+		outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
+		outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+	} else {
+		outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
+		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+	}
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position)
+{
+	unsigned long flags;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+	unsigned int value;
+
+	instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+
+	if (simple->format & SIMPLE_WAVE_LOOP) {
+		if( position >= simple->loop_start ) {
+			voice->CSO = (position - simple->loop_start) >> 4;
+			voice->negCSO = 0;
+		} else {
+			voice->CSO = (simple->loop_start - position) >> 4;
+			voice->negCSO = 1;
+		}
+	} else {
+		voice->CSO = position >> 4;
+		voice->negCSO = 0;
+	}
+
+	/* set CSO sign */
+	value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+	if( voice->negCSO ) {
+		value |= 1 << (voice->number&31);
+	} else {
+		value &= ~(1 << (voice->number&31));
+	}
+	outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+	
+
+	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+		outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+	} else {
+		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+	}
+
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data)
+{
+}
+
+/*
+ * Memory management / sample loading
+ */
+
+static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr,
+					 char *data, long len, int atomic)
+{
+	trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO);
+	unsigned char *block = NULL;
+	int size = instr->size;
+	int shift = 0;
+
+	if (instr->format & SIMPLE_WAVE_BACKWARD ||
+	    instr->format & SIMPLE_WAVE_BIDIR ||
+	    instr->format & SIMPLE_WAVE_ULAW) 
+		return -EINVAL;	/* not supported */
+
+	if (instr->format & SIMPLE_WAVE_16BIT)
+		shift++;
+	if (instr->format & SIMPLE_WAVE_STEREO)
+		shift++;
+	size <<= shift;
+
+	if (trident->synth.current_size + size > trident->synth.max_size)
+		return -ENOMEM;
+
+	if (verify_area(VERIFY_READ, data, size))
+		return -EFAULT;
+
+	if (trident->tlb.entries) {
+		snd_util_memblk_t *memblk;
+		memblk = snd_trident_synth_alloc(trident,size); 
+		if (memblk == NULL)
+			return -ENOMEM;
+		if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
+			snd_trident_synth_free(trident, memblk);
+			return -EFAULT;
+		}
+		instr->address.ptr = (unsigned char*)memblk;
+		instr->address.memory = memblk->offset;
+	} else {
+		dma_addr_t addr;
+		block = (unsigned char *) snd_malloc_pci_pages(trident->pci, size, &addr);
+		if (block == NULL)
+			return -ENOMEM;
+
+		if (copy_from_user(block, data, size)) {
+			snd_free_pci_pages(trident->pci, size, block, addr);
+			return -EFAULT;
+		}
+		instr->address.ptr = block;
+		instr->address.memory = addr;
+	}
+
+	trident->synth.current_size += size;
+	return 0;
+}
+
+static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr,
+					 char *data, long len, int atomic)
+{
+	//trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO);
+	int size = instr->size;
+	int shift = 0;
+
+	if (instr->format & SIMPLE_WAVE_16BIT)
+		shift++;
+	if (instr->format & SIMPLE_WAVE_STEREO)
+		shift++;
+	size <<= shift;
+
+	if (verify_area(VERIFY_WRITE, data, size))
+		return -EFAULT;
+
+	/* FIXME: not implemented yet */
+
+	return -EBUSY;
+}
+
+static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr,
+					    int atomic)
+{
+	trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO);
+	int size = instr->size;
+
+	if (trident->tlb.entries) {
+		snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr;
+		if (memblk)
+			snd_trident_synth_free(trident, memblk);
+		else
+			return -EFAULT;
+	} else {
+		kfree(instr->address.ptr);
+	}
+
+	if (instr->format & SIMPLE_WAVE_16BIT)
+		size <<= 1;
+	if (instr->format & SIMPLE_WAVE_STEREO)
+		size <<= 1;
+
+	trident->synth.current_size -= size;
+	if (trident->synth.current_size < 0)	/* shouldnt need this check... */
+		trident->synth.current_size = 0;
+
+	return 0;
+}
+
+static void select_instrument(trident_t * trident, snd_trident_voice_t * v)
+{
+	snd_seq_kinstr_t *instr;
+	instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
+	if (instr != NULL) {
+		if (instr->ops) {
+			if (instr->ops->instr_type == snd_seq_simple_id)
+				snd_trident_simple_init(v);
+		}
+		snd_seq_instr_free_use(trident->synth.ilist, instr);
+	}
+}
+
+/*
+
+ */
+
+static void event_sample(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+	v->instr.std = ev->data.sample.param.sample.std;
+	if (v->instr.std & 0xff000000) {	/* private instrument */
+		v->instr.std &= 0x00ffffff;
+		v->instr.std |= (unsigned int)ev->source.client << 24;
+	}
+	v->instr.bank = ev->data.sample.param.sample.bank;
+	v->instr.prg = ev->data.sample.param.sample.prg;
+	select_instrument(p->trident, v);
+}
+
+static void event_cluster(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+	v->instr.cluster = ev->data.sample.param.cluster.cluster;
+	select_instrument(p->trident, v);
+}
+
+static void event_start(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_start)
+		v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
+}
+
+static void event_stop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
+}
+
+static void event_freq(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_freq)
+		v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
+}
+
+static void event_volume(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_volume)
+		v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
+}
+
+static void event_loop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_loop)
+		v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
+}
+
+static void event_position(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_pos)
+		v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
+}
+
+static void event_private1(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v)
+{
+	if (v->sample_ops && v->sample_ops->sample_private1)
+		v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
+}
+
+typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v);
+
+static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
+{
+	event_sample,
+	event_cluster,
+	event_start,
+	event_stop,
+	event_freq,
+	event_volume,
+	event_loop,
+	event_position,
+	event_private1
+};
+
+static void snd_trident_sample_event(snd_seq_event_t * ev, snd_trident_port_t * p)
+{
+	int idx, voice;
+	trident_t *trident = p->trident;
+	snd_trident_voice_t *v;
+	unsigned long flags;
+
+	idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+	if (idx < 0 || idx > 8)
+		return;
+	for (voice = 0; voice < 64; voice++) {
+		v = &trident->synth.voices[voice];
+		if (v->use && v->client == ev->source.client &&
+		    v->port == ev->source.port &&
+		    v->index == ev->data.sample.channel) {
+			spin_lock_irqsave(&trident->event_lock, flags);
+			trident_sample_event_handlers[idx] (ev, p, v);
+			spin_unlock_irqrestore(&trident->event_lock, flags);
+			return;
+		}
+	}
+}
+
+/*
+
+ */
+
+static void snd_trident_synth_free_voices(trident_t * trident, int client, int port)
+{
+	int idx;
+	snd_trident_voice_t *voice;
+
+	for (idx = 0; idx < 32; idx++) {
+		voice = &trident->synth.voices[idx];
+		if (voice->use && voice->client == client && voice->port == port)
+			snd_trident_free_voice(trident, voice);
+	}
+}
+
+static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info)
+{
+	snd_trident_port_t *port = (snd_trident_port_t *) private_data;
+	trident_t *trident = port->trident;
+	snd_trident_voice_t *voice;
+	int idx;
+	unsigned long flags;
+
+	if (info->voices > 32)
+		return -EINVAL;
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	for (idx = 0; idx < info->voices; idx++) {
+		voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+		if (voice == NULL) {
+			snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+			spin_unlock_irqrestore(&trident->reg_lock, flags);
+			return -EBUSY;
+		}
+		voice->index = idx;
+		voice->Vol = 0x3ff;
+		voice->EC = 0x0fff;
+	}
+#if 0
+	for (idx = 0; idx < info->midi_voices; idx++) {
+		port->midi_has_voices = 1;
+		voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
+		if (voice == NULL) {
+			snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+			spin_unlock_irqrestore(&trident->reg_lock, flags);
+			return -EBUSY;
+		}
+		voice->Vol = 0x3ff;
+		voice->EC = 0x0fff;
+	}
+#endif
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info)
+{
+	snd_trident_port_t *port = (snd_trident_port_t *) private_data;
+	trident_t *trident = port->trident;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->reg_lock, flags);
+	snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+	spin_unlock_irqrestore(&trident->reg_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_trident_synth_free_private_instruments(snd_trident_port_t * p, int client)
+{
+	snd_seq_instr_header_t ifree;
+
+	memset(&ifree, 0, sizeof(ifree));
+	ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+	snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
+}
+
+int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+	snd_trident_port_t *p = (snd_trident_port_t *) private_data;
+
+	if (p == NULL)
+		return -EINVAL;
+	if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+	    ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+		snd_trident_sample_event(ev, p);
+		return 0;
+	}
+	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+	    ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+		if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+			snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
+			return 0;
+		}
+	}
+	if (direct) {
+		if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+			snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
+					    p->trident->synth.ilist, ev,
+					    p->trident->synth.seq_client, atomic, hop);
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static void snd_trident_synth_instr_notify(void *private_data,
+					   snd_seq_kinstr_t * instr,
+					   int what)
+{
+	int idx;
+	trident_t *trident = snd_magic_cast(trident_t, private_data, return);
+	snd_trident_voice_t *pvoice;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trident->event_lock, flags);
+	for (idx = 0; idx < 64; idx++) {
+		pvoice = &trident->synth.voices[idx];
+		if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+			if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+				pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
+			} else {
+				snd_trident_stop_voice(trident, pvoice->number);
+				pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&trident->event_lock, flags);
+}
+
+/*
+
+ */
+
+static void snd_trident_synth_free_port(void *private_data)
+{
+	snd_trident_port_t *p = (snd_trident_port_t *) private_data;
+
+	if (p)
+		snd_midi_channel_free_set(p->chset);
+}
+
+static int snd_trident_synth_create_port(trident_t * trident, int idx)
+{
+	snd_trident_port_t *p;
+	snd_seq_port_callback_t callbacks;
+	char name[32];
+	char *str;
+	int result;
+
+	p = &trident->synth.seq_ports[idx];
+	p->chset = snd_midi_channel_alloc_set(16);
+	if (p->chset == NULL)
+		return -ENOMEM;
+	p->chset->private_data = p;
+	p->trident = trident;
+	p->client = trident->synth.seq_client;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.owner = THIS_MODULE;
+	callbacks.use = snd_trident_synth_use;
+	callbacks.unuse = snd_trident_synth_unuse;
+	callbacks.event_input = snd_trident_synth_event_input;
+	callbacks.private_free = snd_trident_synth_free_port;
+	callbacks.private_data = p;
+
+	str = "???";
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:	str = "Trident 4DWave-DX"; break;
+	case TRIDENT_DEVICE_ID_NX:	str = "Trident 4DWave-NX"; break;
+	case TRIDENT_DEVICE_ID_SI7018:	str = "SiS 7018"; break;
+	}
+	sprintf(name, "%s port %i", str, idx);
+	p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
+						   &callbacks,
+						   SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GS |
+						   SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+						   SNDRV_SEQ_PORT_TYPE_SYNTH,
+						   name);
+	if (p->chset->port < 0) {
+		result = p->chset->port;
+		snd_trident_synth_free_port(p);
+		return result;
+	}
+	p->port = p->chset->port;
+	return 0;
+}
+
+/*
+
+ */
+
+static int snd_trident_synth_new_device(snd_seq_device_t *dev)
+{
+	trident_t *trident;
+	int client, i;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	snd_seq_port_subscribe_t sub;
+	snd_simple_ops_t *simpleops;
+	char *str;
+
+	trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (trident == NULL)
+		return -EINVAL;
+
+	trident->synth.seq_client = -1;
+
+	/* allocate new client */
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = trident;
+	callbacks.allow_output = callbacks.allow_input = 1;
+	client = trident->synth.seq_client =
+	    snd_seq_create_kernel_client(trident->card, 1, &callbacks);
+	if (client < 0)
+		return client;
+
+	/* change name of client */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	str = "???";
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:	str = "Trident 4DWave-DX"; break;
+	case TRIDENT_DEVICE_ID_NX:	str = "Trident 4DWave-NX"; break;
+	case TRIDENT_DEVICE_ID_SI7018:	str = "SiS 7018"; break;
+	}
+	sprintf(cinfo.name, str);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	for (i = 0; i < 4; i++)
+		snd_trident_synth_create_port(trident, i);
+
+	trident->synth.ilist = snd_seq_instr_list_new();
+	if (trident->synth.ilist == NULL) {
+		snd_seq_delete_kernel_client(client);
+		trident->synth.seq_client = -1;
+		return -ENOMEM;
+	}
+	trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+
+	simpleops = &trident->synth.simple_ops;
+	snd_seq_simple_init(simpleops, trident, NULL);
+	simpleops->put_sample = snd_trident_simple_put_sample;
+	simpleops->get_sample = snd_trident_simple_get_sample;
+	simpleops->remove_sample = snd_trident_simple_remove_sample;
+	simpleops->notify = snd_trident_synth_instr_notify;
+
+	memset(&sub, 0, sizeof(sub));
+	sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	sub.dest.client = client;
+	sub.dest.port = 0;
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+
+	return 0;
+}
+
+static int snd_trident_synth_delete_device(snd_seq_device_t *dev)
+{
+	trident_t *trident;
+
+	trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (trident == NULL)
+		return -EINVAL;
+
+	if (trident->synth.seq_client >= 0) {
+		snd_seq_delete_kernel_client(trident->synth.seq_client);
+		trident->synth.seq_client = -1;
+	}
+	if (trident->synth.ilist)
+		snd_seq_instr_list_free(&trident->synth.ilist);
+	return 0;
+}
+
+static int __init alsa_trident_synth_init(void)
+{
+	static snd_seq_dev_ops_t ops =
+	{
+		snd_trident_synth_new_device,
+		snd_trident_synth_delete_device
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
+					      sizeof(trident_t*));
+}
+
+static void __exit alsa_trident_synth_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
+}
+
+module_init(alsa_trident_synth_init)
+module_exit(alsa_trident_synth_exit)
diff -Nru linux/sound/pci/via686.c linux-2.4.19-pre5-mjc/sound/pci/via686.c
--- linux/sound/pci/via686.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/via686.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1290 @@
+/*
+ *   ALSA driver for VIA VT82C686A (South Bridge)
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82C686A");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{VIA,VT82C686A,pci},{VIA,VT82C686B}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for VIA 82C686A bridge.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for VIA 82C686A bridge.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82C686A bridge.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
+MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
+
+/*
+ *  Direct registers
+ */
+
+#ifndef PCI_DEVICE_ID_VIA_82C686_5
+#define PCI_DEVICE_ID_VIA_82C686_5	0x3058
+#endif
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+
+/* offsets */
+#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
+#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
+#define   VIA_REG_STAT_PAUSED		0x40	/* RO */
+#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
+#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
+#define   VIA_REG_STAT_EOL		0x02	/* RWC */
+#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
+#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
+#define   VIA_REG_CTRL_START		0x80	/* WO */
+#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
+#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
+#define   VIA_REG_CTRL_RESET		0x01	/* RW - probably reset? undocumented */
+#define VIA_REG_OFFSET_TYPE		0x02	/* byte - channel type */
+#define   VIA_REG_TYPE_AUTOSTART	0x80	/* RW - autostart at EOL */
+#define   VIA_REG_TYPE_16BIT		0x20	/* RW */
+#define   VIA_REG_TYPE_STEREO		0x10	/* RW */
+#define   VIA_REG_TYPE_INT_LLINE	0x00
+#define   VIA_REG_TYPE_INT_LSAMPLE	0x04
+#define   VIA_REG_TYPE_INT_LESSONE	0x08
+#define   VIA_REG_TYPE_INT_MASK		0x0c
+#define   VIA_REG_TYPE_INT_EOL		0x02
+#define   VIA_REG_TYPE_INT_FLAG		0x01
+#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
+#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count */
+/* playback block */
+#define VIA_REG_PLAYBACK_STATUS		0x00	/* byte - channel status */
+#define VIA_REG_PLAYBACK_CONTROL	0x01	/* byte - channel control */
+#define VIA_REG_PLAYBACK_TYPE		0x02	/* byte - channel type */
+#define VIA_REG_PLAYBACK_TABLE_PTR	0x04	/* dword - channel table pointer */
+#define VIA_REG_PLAYBACK_CURR_PTR	0x04	/* dword - channel current pointer */
+#define VIA_REG_PLAYBACK_CURR_COUNT	0x0c	/* dword - channel current count */
+/* capture block */
+#define VIA_REG_CAPTURE_STATUS		0x10	/* byte - channel status */
+#define VIA_REG_CAPTURE_CONTROL		0x11	/* byte - channel control */
+#define VIA_REG_CAPTURE_TYPE		0x12	/* byte - channel type */
+#define VIA_REG_CAPTURE_TABLE_PTR	0x14	/* dword - channel table pointer */
+#define VIA_REG_CAPTURE_CURR_PTR	0x14	/* dword - channel current pointer */
+#define VIA_REG_CAPTURE_CURR_COUNT	0x1c	/* dword - channel current count */
+/* FM block */
+#define VIA_REG_FM_STATUS		0x20	/* byte - channel status */
+#define VIA_REG_FM_CONTROL		0x21	/* byte - channel control */
+#define VIA_REG_FM_TYPE			0x22	/* byte - channel type */
+#define VIA_REG_FM_TABLE_PTR		0x24	/* dword - channel table pointer */
+#define VIA_REG_FM_CURR_PTR		0x24	/* dword - channel current pointer */
+#define VIA_REG_FM_CURR_COUNT		0x2c	/* dword - channel current count */
+/* AC'97 */
+#define VIA_REG_AC97			0x80	/* dword */
+#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
+#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
+#define   VIA_REG_AC97_CODEC_ID_PRIMARY	0x00
+#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
+#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
+#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
+#define   VIA_REG_AC97_BUSY		(1<<24)
+#define   VIA_REG_AC97_READ		(1<<23)
+#define   VIA_REG_AC97_CMD_SHIFT	16
+#define   VIA_REG_AC97_CMD_MASK		0x7e
+#define   VIA_REG_AC97_DATA_SHIFT	0
+#define   VIA_REG_AC97_DATA_MASK	0xffff
+#define VIA_REG_SGD_SHADOW		0x84	/* dword */
+
+/*
+ *
+ */
+
+#define VIA_MAX_FRAGS			32
+
+/*
+ *  
+ */
+
+typedef struct {
+	unsigned long reg_offset;
+	unsigned int *table;
+	dma_addr_t table_addr;
+        snd_pcm_substream_t *substream;
+	dma_addr_t physbuf;
+        unsigned int size;
+        unsigned int fragsize;
+	unsigned int frags;
+	unsigned int lastptr;
+	unsigned int lastcount;
+} viadev_t;
+
+typedef struct _snd_via686a via686a_t;
+#define chip_t via686a_t
+
+struct _snd_via686a {
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned char revision;
+
+	unsigned char old_legacy;
+	unsigned char old_legacy_cfg;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm_fm;
+	viadev_t playback;
+	viadev_t capture;
+	viadev_t playback_fm;
+
+	snd_rawmidi_t *rmidi;
+
+	ac97_t *ac97;
+	unsigned int ac97_clock;
+	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */
+
+	spinlock_t reg_lock;
+	spinlock_t ac97_lock;
+	snd_info_entry_t *proc_entry;
+
+	void *tables;
+	dma_addr_t tables_addr;
+};
+
+static struct pci_device_id snd_via686a_ids[] __devinitdata = {
+	{ 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* 686A */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via686a_ids);
+
+/*
+ *  Basic I/O
+ */
+
+static inline unsigned int snd_via686a_codec_xread(via686a_t *chip)
+{
+	return inl(VIAREG(chip, AC97));
+}
+ 
+static inline void snd_via686a_codec_xwrite(via686a_t *chip, unsigned int val)
+{
+	outl(val, VIAREG(chip, AC97));
+}
+ 
+static int snd_via686a_codec_ready(via686a_t *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val;
+	
+	while (timeout-- > 0) {
+		udelay(1);
+		if (!((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+			return val & 0xffff;
+	}
+	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via686a_codec_xread(chip));
+	return -EIO;
+}
+ 
+static int snd_via686a_codec_valid(via686a_t *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val;
+	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+					 VIA_REG_AC97_SECONDARY_VALID;
+	
+	while (timeout-- > 0) {
+		udelay(1);
+		if ((val = snd_via686a_codec_xread(chip)) & stat)
+			return val & 0xffff;
+	}
+	snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via686a_codec_xread(chip));
+	return -EIO;
+}
+ 
+static void snd_via686a_codec_wait(ac97_t *ac97)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
+	int err;
+	err = snd_via686a_codec_ready(chip, ac97->num);
+	/* here we need to wait fairly for long time.. */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/2);
+}
+
+static void snd_via686a_codec_write(ac97_t *ac97,
+				    unsigned short reg,
+				    unsigned short val)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
+	unsigned int xval;
+	
+	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+	xval |= val << VIA_REG_AC97_DATA_SHIFT;
+	spin_lock(&chip->ac97_lock);
+	snd_via686a_codec_xwrite(chip, xval);
+	snd_via686a_codec_ready(chip, ac97->num);
+	spin_unlock(&chip->ac97_lock);
+}
+
+static unsigned short snd_via686a_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return ~0);
+	unsigned int xval, val = 0xffff;
+	int again = 0;
+
+	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval = (!ac97->num ? VIA_REG_AC97_PRIMARY_VALID : VIA_REG_AC97_SECONDARY_VALID);
+	xval |= VIA_REG_AC97_READ;
+	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+	spin_lock(&chip->ac97_lock);
+      	while (1) {
+      		if (again++ > 3) {
+		        spin_unlock(&chip->ac97_lock);
+		      	return 0xffff;
+		}
+		snd_via686a_codec_xwrite(chip, xval);
+		if (snd_via686a_codec_ready(chip, ac97->num) < 0)
+			continue;
+		if (snd_via686a_codec_valid(chip, ac97->num) >= 0) {
+			udelay(25);
+			val = snd_via686a_codec_xread(chip);
+			break;
+		}
+	}
+	spin_unlock(&chip->ac97_lock);
+	return val & 0xffff;
+}
+
+#if 0
+static void snd_via686a_channel_print(via686a_t *chip, viadev_t *viadev)
+{
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n",
+			port,
+			inb(port + VIA_REG_OFFSET_STATUS),
+			inb(port + VIA_REG_OFFSET_CONTROL),
+			inb(port + VIA_REG_OFFSET_TYPE),
+			inl(port + VIA_REG_OFFSET_CURR_PTR),
+			inl(port + VIA_REG_OFFSET_CURR_COUNT));
+}
+#endif
+
+static void snd_via686a_channel_reset(via686a_t *chip, viadev_t *viadev)
+{
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL);
+	udelay(50);
+	outb(0x00, port + VIA_REG_OFFSET_CONTROL);
+	outb(0xff, port + VIA_REG_OFFSET_STATUS);
+	outb(0x00, port + VIA_REG_OFFSET_TYPE);
+	outl(0, port + VIA_REG_OFFSET_CURR_PTR);
+}
+
+static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd)
+{
+	unsigned char val = 0;
+	unsigned long port = chip->port + viadev->reg_offset;
+	
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = VIA_REG_CTRL_START;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val = VIA_REG_CTRL_PAUSE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		val = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	outb(val, port + VIA_REG_OFFSET_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP)
+		snd_via686a_channel_reset(chip, viadev);
+	return 0;
+}
+
+static void snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev,
+				      snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int idx, frags;
+	unsigned int *table = viadev->table;
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	viadev->physbuf = runtime->dma_addr;
+	viadev->size = snd_pcm_lib_buffer_bytes(substream);
+	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
+	viadev->frags = runtime->periods;
+	viadev->lastptr = ~0;
+	viadev->lastcount = ~0;
+
+	snd_via686a_channel_reset(chip, viadev);
+	outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
+	outb(VIA_REG_TYPE_AUTOSTART |
+	     (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
+	     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
+	     ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
+	     VIA_REG_TYPE_INT_EOL |
+	     VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE);
+	if (viadev->size == viadev->fragsize) {
+		table[0] = cpu_to_le32(viadev->physbuf);
+		table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */
+				       viadev->fragsize);
+	} else {
+		frags = viadev->size / viadev->fragsize;
+		for (idx = 0; idx < frags - 1; idx++) {
+			table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize));
+			table[(idx << 1) + 1] = cpu_to_le32(0x40000000 |	/* flag */
+							    viadev->fragsize);
+		}
+		table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize));
+		table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 |	/* EOL */
+							  viadev->fragsize);
+	}
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev)
+{
+	snd_pcm_period_elapsed(viadev->substream);
+	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
+}
+
+static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return);
+	unsigned int status;
+
+	status = inl(VIAREG(chip, SGD_SHADOW));
+	if ((status & 0x00000077) == 0) {
+		if (chip->rmidi != NULL) {
+			snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+		}
+		return;
+	}
+	if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via686a_update(chip, &chip->playback);
+	if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via686a_update(chip, &chip->playback_fm);
+	if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via686a_update(chip, &chip->capture);
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_via686a_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via686a_trigger(chip, &chip->playback, cmd);
+}
+
+static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via686a_trigger(chip, &chip->capture, cmd);
+}
+
+static int snd_via686a_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_via686a_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	snd_via686a_setup_periods(chip, &chip->playback, substream);
+	return 0;
+}
+
+static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	snd_via686a_setup_periods(chip, &chip->capture, substream);
+	return 0;
+}
+
+static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev)
+{
+	unsigned int val, ptr, count;
+	// unsigned int tmp;
+	
+	ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset);
+	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset);
+	if (ptr == viadev->lastptr && count > viadev->lastcount)
+		ptr += 8;
+	if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE))
+		return 0;
+	// tmp =
+	val = (((unsigned int)(ptr - viadev->table_addr) / 8) - 1) % viadev->frags;
+	val *= viadev->fragsize;
+	val += viadev->fragsize - count;
+	viadev->lastptr = ptr;
+	viadev->lastcount = count;
+	// printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, tmp, count, val);
+	return val;
+}
+
+static snd_pcm_uframes_t snd_via686a_playback_pointer(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback));
+}
+
+static snd_pcm_uframes_t snd_via686a_capture_pointer(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->capture));
+}
+
+static snd_pcm_hardware_t snd_via686a_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		VIA_MAX_FRAGS,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_via686a_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		VIA_MAX_FRAGS,
+	fifo_size:		0,
+};
+
+static int snd_via686a_playback_open(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback.substream = substream;
+	runtime->hw = snd_via686a_playback;
+	runtime->hw.rates = chip->ac97->rates_front_dac;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_via686a_capture_open(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture.substream = substream;
+	runtime->hw = snd_via686a_capture;
+	runtime->hw.rates = chip->ac97->rates_adc;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_via686a_playback_close(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback.substream = NULL;
+	/* disable DAC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
+	return 0;
+}
+
+static int snd_via686a_capture_close(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture.substream = NULL;
+	/* disable ADC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_via686a_playback_ops = {
+	open:		snd_via686a_playback_open,
+	close:		snd_via686a_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_via686a_hw_params,
+	hw_free:	snd_via686a_hw_free,
+	prepare:	snd_via686a_playback_prepare,
+	trigger:	snd_via686a_playback_trigger,
+	pointer:	snd_via686a_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_via686a_capture_ops = {
+	open:		snd_via686a_capture_open,
+	close:		snd_via686a_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_via686a_hw_params,
+	hw_free:	snd_via686a_hw_free,
+	prepare:	snd_via686a_capture_prepare,
+	trigger:	snd_via686a_capture_trigger,
+	pointer:	snd_via686a_capture_pointer,
+};
+
+static void snd_via686a_pcm_free(snd_pcm_t *pcm)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "VIA 82C686A", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686a_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_via686a_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "VIA 82C686A");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = NULL;
+	return 0;
+}
+
+#if 0
+
+/*
+ *  PCM code - FM channel
+ */
+
+static int snd_via686a_playback_fm_ioctl(snd_pcm_substream_t * substream,
+					 unsigned int cmd,
+					 void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_via686a_playback_fm_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via686a_trigger(chip, &chip->playback_fm, cmd);
+}
+
+static int snd_via686a_playback_fm_prepare(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	snd_via686a_setup_periods(chip, &chip->playback_fm, substream);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_via686a_playback_fm_pointer(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback_fm));
+}
+
+static snd_pcm_hardware_t snd_via686a_playback_fm =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_KNOT,
+	rate_min:		24000,
+	rate_max:		24000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		VIA_MAX_FRAGS,
+	fifo_size:		0,
+};
+
+static int snd_via686a_playback_fm_open(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma_fm_size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+		return -ENOMEM;
+	chip->playback_fm.substream = substream;
+	runtime->hw = snd_via686a_playback_fm;
+#if 0
+	runtime->hw.rates = chip->ac97->rates_front_dac;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.min_rate = 48000;
+#endif
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_via686a_playback_fm_close(snd_pcm_substream_t * substream)
+{
+	via686a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->playback_fm.substream = NULL;
+	snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+	/* disable DAC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_via686a_playback_fm_ops = {
+	open:		snd_via686a_playback_fm_open,
+	close:		snd_via686a_playback_fm_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	prepare:	snd_via686a_playback_fm_prepare,
+	trigger:	snd_via686a_playback_fm_trigger,
+	pointer:	snd_via686a_playback_fm_pointer,
+};
+
+static void snd_via686a_pcm_fm_free(void *private_data)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, private_data, return);
+	chip->pcm_fm = NULL;
+	snd_pcm_lib_preallocate_pci_free_for_all(ensoniq->pci, pcm);
+}
+
+static int __devinit snd_via686a_pcm_fm(via686a_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "VIA 82C686A - FM DAC", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_fm_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_via686a_pcm_fm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "VIA 82C686A - FM DAC");
+
+	snd_pcm_add_buffer_bytes_controls(pcm);
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024);
+
+	chip->pcm_fm = pcm;
+	if (rpcm)
+		*rpcm = NULL;
+	return 0;
+}
+
+#endif
+
+/*
+ *  Mixer part
+ */
+
+static void snd_via686a_codec_init(ac97_t *ac97)
+{
+	// via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
+
+	/* disable DAC & ADC power */
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
+	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
+	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+}
+
+static void snd_via686a_mixer_free_ac97(ac97_t *ac97)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+}
+
+static int __devinit snd_via686a_mixer(via686a_t *chip)
+{
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_via686a_codec_write;
+	ac97.read = snd_via686a_codec_read;
+	ac97.init = snd_via686a_codec_init;
+	ac97.wait = snd_via686a_codec_wait;
+	ac97.private_data = chip;
+	ac97.private_free = snd_via686a_mixer_free_ac97;
+	ac97.clock = chip->ac97_clock;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ * joystick
+ */
+
+static int snd_via686a_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_via686a_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via686a_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val;
+
+	pci_read_config_word(chip->pci, 0x42, &val);
+	ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0;
+	return 0;
+}
+
+static int snd_via686a_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via686a_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val, oval;
+
+	pci_read_config_word(chip->pci, 0x42, &oval);
+	val = oval & ~0x08;
+	if (ucontrol->value.integer.value[0])
+		val |= 0x08;
+	if (val != oval) {
+		pci_write_config_word(chip->pci, 0x42, val);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_via686a_joystick_control __devinitdata = {
+	name: "Joystick",
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	info: snd_via686a_joystick_info,
+	get: snd_via686a_joystick_get,
+	put: snd_via686a_joystick_put,
+};
+
+/*
+ *
+ */
+
+static int __devinit snd_via686a_chip_init(via686a_t *chip)
+{
+	ac97_t ac97;
+	unsigned int val;
+	int max_count;
+	unsigned char pval;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+
+#if 0 /* broken on K7M? */
+	/* disable all legacy ports */
+	pci_write_config_byte(chip->pci, 0x42, 0);
+#endif
+
+	/* deassert ACLink reset, force SYNC */
+	pci_write_config_byte(chip->pci, 0x41, 0xe0);
+	udelay(100);
+	pci_write_config_byte(chip->pci, 0x41, 0x00);
+	udelay(100);
+	/* ACLink on, deassert ACLink reset, VSR, SGD data out */
+	/* note - FM data out has trouble with non VRA codecs !! */
+	pci_write_config_byte(chip->pci, 0x41, 0xcc);
+	udelay(100);
+
+	/* wait until codec ready */
+	max_count = ((3 * HZ) / 4) + 1;
+	do {
+		pci_read_config_byte(chip->pci, 0x40, &pval);
+		if (pval & 0x01) /* primary codec ready */
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+
+	if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
+
+	/* and then reset codec.. */
+	snd_via686a_codec_write(&ac97, AC97_RESET, 0x0000);
+
+	/* check the primary codec */
+	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_PRIMARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_PRIMARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	max_count = ((3 * HZ) / 4) + 1;
+	do {
+		if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_PRIMARY_VALID)
+			goto __ac97_ok1;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+	snd_printk("Primary AC'97 codec is not valid [0x%x]\n", val);
+
+      __ac97_ok1:
+#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
+	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_SECONDARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	max_count = ((3 * HZ) / 4) + 1;
+	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_SECONDARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	do {
+		if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+			chip->ac97_secondary = 1;
+			goto __ac97_ok2;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+	/* This is ok, the most of motherboards have only one codec */
+
+      __ac97_ok2:
+#endif
+
+#if 0
+	{
+	unsigned char cmdb;
+
+	pci_read_config_byte(chip->pci, 0x40, &cmdb);
+	printk("PCI[0x40] = 0x%x\n", cmdb);
+	pci_read_config_byte(chip->pci, 0x42, &cmdb);
+	printk("PCI[0x42] = 0x%x\n", cmdb);
+	pci_read_config_byte(chip->pci, 0x43, &cmdb);
+	printk("PCI[0x43] = 0x%x\n", cmdb);
+	pci_read_config_byte(chip->pci, 0x44, &cmdb);
+	printk("PCI[0x44] = 0x%x\n", cmdb);
+	pci_read_config_byte(chip->pci, 0x48, &cmdb);
+	printk("PCI[0x48] = 0x%x\n", cmdb);
+	}
+#endif
+
+	/* route FM trap to IRQ, disable FM trap */
+	pci_write_config_byte(chip->pci, 0x48, 0);
+	
+	/* disable all GPI interrupts */
+	outl(0, chip->port + 0x8c);
+
+	/* disable interrupts */
+	snd_via686a_channel_reset(chip, &chip->playback);
+	snd_via686a_channel_reset(chip, &chip->capture);
+	snd_via686a_channel_reset(chip, &chip->playback_fm);
+	return 0;
+}
+
+static int snd_via686a_free(via686a_t *chip)
+{
+	if (chip->irq < 0)
+		goto __end_hw;
+	/* disable interrupts */
+	snd_via686a_channel_reset(chip, &chip->playback);
+	snd_via686a_channel_reset(chip, &chip->capture);
+	snd_via686a_channel_reset(chip, &chip->playback_fm);
+	/* --- */
+      __end_hw:
+	synchronize_irq();
+	if (chip->tables)
+		snd_free_pci_pages(chip->pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, chip->tables, chip->tables_addr);
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	pci_write_config_byte(chip->pci, 0x42, chip->old_legacy);
+	pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_via686a_dev_free(snd_device_t *device)
+{
+	via686a_t *chip = snd_magic_cast(via686a_t, device->device_data, return -ENXIO);
+	return snd_via686a_free(chip);
+}
+
+static int __devinit snd_via686a_create(snd_card_t * card,
+				     struct pci_dev *pci,
+				     unsigned int ac97_clock,
+				     unsigned char old_legacy,
+				     unsigned char old_legacy_cfg,
+				     via686a_t ** r_via)
+{
+	via686a_t *chip;
+	int err;
+        static snd_device_ops_t ops = {
+		dev_free:	snd_via686a_dev_free,
+        };
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	if ((chip = snd_magic_kcalloc(via686a_t, 0, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	chip->old_legacy = old_legacy;
+	chip->old_legacy_cfg = old_legacy_cfg;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->ac97_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 256, "VIA686A")) == NULL) {
+		snd_via686a_free(chip);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_via686a_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA686A", (void *)chip)) {
+		snd_via686a_free(chip);
+		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	if (ac97_clock >= 8000 && ac97_clock <= 48000)
+		chip->ac97_clock = ac97_clock;
+	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
+	synchronize_irq();
+
+	/* initialize offsets */
+	chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
+	chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
+	chip->playback_fm.reg_offset = VIA_REG_FM_STATUS;
+
+	/* allocate buffer descriptor lists */
+	/* the start of each lists must be aligned to 8 bytes */
+	chip->tables = (unsigned int *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, &chip->tables_addr);
+	if (chip->tables == NULL) {
+		snd_via686a_free(chip);
+		return -ENOMEM;
+	}
+	/* tables must be aligned to 8 bytes, but the kernel pages
+	   are much bigger, so we don't care */
+	chip->playback.table = chip->tables;
+	chip->playback.table_addr = chip->tables_addr;
+	chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2;
+	chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2;
+	chip->playback_fm.table = chip->capture.table + VIA_MAX_FRAGS * 2;
+	chip->playback_fm.table_addr = chip->capture.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2;
+
+	if ((err = snd_via686a_chip_init(chip)) < 0) {
+		snd_via686a_free(chip);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_via686a_free(chip);
+		return err;
+	}
+
+	*r_via = chip;
+	return 0;
+}
+
+static int __devinit snd_via686a_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	via686a_t *chip;
+	int pcm_dev = 0;
+	unsigned char legacy;
+	unsigned char legacy_cfg;
+	int rev_h = 0, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	pci_read_config_byte(pci, 0x42, &legacy);
+	pci_read_config_byte(pci, 0x43, &legacy_cfg);
+
+	if ((err = snd_via686a_create(card,
+				      pci,
+				      snd_ac97_clock[dev],
+				      legacy,
+				      legacy_cfg,
+				      &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+
+	if (snd_via686a_mixer(chip) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_via686a_pcm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#if 0
+	if (snd_via686a_pcm_fm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	legacy |= 0x40;		/* disable MIDI */
+	legacy &= ~0x08;	/* disable joystick */
+	if (chip->revision >= 0x20) {
+		if (check_region(pci_resource_start(pci, 2), 4)) {
+			rev_h = 0;
+			legacy &= ~0x80;	/* disable PCI I/O 2 */
+		} else {
+			rev_h = 1;
+			legacy |= 0x80;		/* enable PCI I/O 2 */
+		}
+	}
+	pci_write_config_byte(pci, 0x42, legacy);
+	pci_write_config_byte(pci, 0x43, legacy_cfg);
+	if (rev_h && snd_mpu_port[dev] >= 0x200) {	/* force MIDI */
+		legacy |= 0x02;	/* enable MPU */
+		pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01);
+	} else {
+		if (rev_h && (legacy & 0x02)) {
+			snd_mpu_port[dev] = pci_resource_start(pci, 2);
+			if (snd_mpu_port[dev] < 0x200)	/* bad value */
+				legacy &= ~0x02;	/* disable MIDI */
+		} else {
+			switch (snd_mpu_port[dev]) {	/* force MIDI */
+			case 0x300:
+			case 0x310:
+			case 0x320:
+			case 0x330:
+				legacy_cfg &= ~(3 << 2);
+				legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2;
+				legacy |= 0x02;
+				break;
+			default:			/* no, use BIOS settings */
+				if (legacy & 0x02)
+					snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2);
+			}
+		}
+	}
+	pci_write_config_byte(pci, 0x42, legacy);
+	pci_write_config_byte(pci, 0x43, legacy_cfg);
+	if (legacy & 0x02) {
+		if (check_region(snd_mpu_port[dev], 2)) {
+			printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]);
+			legacy &= ~0x02;
+			pci_write_config_byte(pci, 0x42, legacy);
+			goto __skip_mpu;
+		}
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A,
+					snd_mpu_port[dev], 0,
+					pci->irq, 0,
+					&chip->rmidi) < 0) {
+			printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]);
+			legacy &= ~0x02;
+			pci_write_config_byte(pci, 0x42, legacy);
+			goto __skip_mpu;
+		}
+		legacy &= ~0x40;	/* enable MIDI interrupt */
+		pci_write_config_byte(pci, 0x42, legacy);
+	      __skip_mpu:
+		;
+	}
+	
+	/* card switches */
+	err = snd_ctl_add(card, snd_ctl_new1(&snd_via686a_joystick_control, chip));
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "VIA686A");
+	strcpy(card->shortname, "VIA 82C686A/B");
+	
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_via686a_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "VIA 82C686A/B",
+	id_table: snd_via686a_ids,
+	probe: snd_via686a_probe,
+	remove: __devexit_p(snd_via686a_remove),
+};
+
+static int __init alsa_card_via686a_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "VIA 82C686A soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_via686a_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via686a_init)
+module_exit(alsa_card_via686a_exit)
+
+#ifndef MODULE
+
+/* format is: snd-via686a=snd_enable,snd_index,snd_id,
+			  snd_mpu_port,snd_ac97_clock */
+
+static int __init alsa_card_via686a_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-via686a=", alsa_card_via686a_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/via8233.c linux-2.4.19-pre5-mjc/sound/pci/via8233.c
--- linux/sound/pci/via8233.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/via8233.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,915 @@
+/*
+ *   ALSA driver for VIA VT8233 (South Bridge)
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>,
+ *                         Tjeerd.Mulder@fujitsu-siemens.com
+ *
+ *   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
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Tjeerd.Mulder@fujitsu-siemens.com");
+MODULE_DESCRIPTION("VIA VT8233");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{VIA,VT8233,pci}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for VIA 8233 bridge.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for VIA 8233 bridge.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 8233 bridge.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
+MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
+
+/*
+ *  Direct registers
+ */
+
+#ifndef PCI_DEVICE_ID_VIA_8233_5
+#define PCI_DEVICE_ID_VIA_8233_5	0x3059
+#endif
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+
+/* offsets */
+#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
+#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
+#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
+#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
+#define   VIA_REG_STAT_EOL		0x02	/* RWC */
+#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
+#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
+#define   VIA_REG_CTRL_START		0x80	/* WO */
+#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
+#define   VIA_REG_CTRL_AUTOSTART	0x20
+#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
+#define   VIA_REG_CTRL_INT_STOP		0x04		
+#define   VIA_REG_CTRL_INT_EOL		0x02
+#define   VIA_REG_CTRL_INT_FLAG		0x01
+#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
+#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
+#define VIA_REG_OFFSET_TYPE		0x08	/* long - stop index, channel type, sample rate */
+#define   VIA_REG_TYPE_16BIT		0x00200000	/* RW */
+#define   VIA_REG_TYPE_STEREO		0x00100000	/* RW */
+#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
+#define VIA_REG_OFFSET_CURR_INDEX	0x0f	/* byte - channel current index */
+
+#define VIA_NUM_OF_DMA_CHANNELS	2
+/* playback block */
+#define VIA_REG_PLAYBACK_STATUS		0x00	/* byte - channel status */
+#define VIA_REG_PLAYBACK_CONTROL	0x01	/* byte - channel control */
+#define VIA_REG_PLAYBACK_VOLUME_L	0x02	/* byte */
+#define VIA_REG_PLAYBACK_VOLUME_R	0x03	/* byte */
+#define VIA_REG_PLAYBACK_TABLE_PTR	0x04	/* dword - channel table pointer */
+#define VIA_REG_PLAYBACK_CURR_PTR	0x04	/* dword - channel current pointer */
+#define VIA_REG_PLAYBACK_TYPE		0x08    /* long - stop index, channel type, sample rate */ /* byte - channel type */
+#define VIA_REG_PLAYBACK_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
+#define VIA_REG_PLAYBACK_CURR_INDEX	0x0f	/* byte - channel current index */
+/* capture block */
+#define VIA_REG_CAPTURE_STATUS		0x60	/* byte - channel status */
+#define VIA_REG_CAPTURE_CONTROL		0x61	/* byte - channel control */
+#define VIA_REG_CAPTURE_FIFO		0x62	/* byte - bit 6 = fifo  enable */
+#define   VIA_REG_CAPTURE_FIFO_ENABLE	0x40
+#define VIA_REG_CAPTURE_CHANNEL		0x63	/* byte - input select */
+#define   VIA_REG_CAPTURE_CHANNEL_MIC	0x4
+#define   VIA_REG_CAPTURE_CHANNEL_LINE	0
+#define VIA_REG_CAPTURE_TABLE_PTR	0x64	/* dword - channel table pointer */
+#define VIA_REG_CAPTURE_CURR_PTR	0x64	/* dword - channel current pointer */
+#define VIA_REG_CAPTURE_TYPE		0x68	/* byte - channel type */
+#define VIA_REG_CAPTURE_CURR_COUNT	0x6c	/* dword - channel current count (24 bit) */
+#define VIA_REG_CAPTURE_CURR_INDEX	0x6f	/* byte - channel current index */
+/* AC'97 */
+#define VIA_REG_AC97			0x80	/* dword */
+#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
+#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
+#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
+#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
+#define   VIA_REG_AC97_ANY_VALID	(VIA_REG_AC97_PRIMARY_VALID | VIA_REG_AC97_SECONDARY_VALID | (1<<28)| (1<<29))
+#define   VIA_REG_AC97_BUSY		(1<<24)
+#define   VIA_REG_AC97_READ		(1<<23)
+#define   VIA_REG_AC97_CMD_SHIFT	16
+#define   VIA_REG_AC97_CMD_MASK		0x7e
+#define   VIA_REG_AC97_DATA_SHIFT	0
+#define   VIA_REG_AC97_DATA_MASK	0xffff
+#define VIA_REG_SGD_SHADOW		0x84	/* dword */
+
+/*
+ *
+ */
+
+#define VIA_MAX_FRAGS			32
+
+/*
+ *  
+ */
+
+typedef struct {
+	unsigned long reg_offset;
+	unsigned int *table;
+	dma_addr_t table_addr;
+        snd_pcm_substream_t *substream;
+	dma_addr_t physbuf;
+        unsigned int size;
+        unsigned int fragsize;
+	unsigned int frags;
+} viadev_t;
+
+typedef struct _snd_via8233 via8233_t;
+#define chip_t via8233_t
+
+struct _snd_via8233 {
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned char revision;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+
+	snd_pcm_t *pcm;
+	viadev_t playback;
+	viadev_t capture;
+
+	ac97_t *ac97;
+	unsigned int ac97_clock;
+
+	spinlock_t reg_lock;
+	spinlock_t update_lock;
+	snd_info_entry_t *proc_entry;
+
+	void *tables;
+	dma_addr_t tables_addr;
+};
+
+static struct pci_device_id snd_via8233_ids[] __devinitdata = {
+	{ 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* VT8233 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via8233_ids);
+
+/*
+ *  Basic I/O
+ */
+
+static inline unsigned int snd_via8233_codec_xread(via8233_t *chip)
+{
+	/* this acces should be atomic */
+	return inl(VIAREG(chip, AC97));
+}
+ 
+static inline void snd_via8233_codec_xwrite(via8233_t *chip, unsigned int val)
+{
+	/* this acces should be atomic */
+	outl(val, VIAREG(chip, AC97));
+}
+ 
+static int snd_via8233_codec_ready(via8233_t *chip, int secondary)
+{
+	int time;
+
+	time = 1000;
+	do {
+		udelay(1);
+		if ((snd_via8233_codec_xread(chip) & VIA_REG_AC97_BUSY) == 0)
+			return 0;
+	} while (time--);
+	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via8233_codec_xread(chip));
+	return -EIO;
+}
+ 
+static void snd_via8233_codec_write(ac97_t *ac97,
+				    unsigned short reg,
+				    unsigned short val)
+{
+	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
+	unsigned int xval;
+	
+	xval = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+	xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+	xval |= val << VIA_REG_AC97_DATA_SHIFT;
+	spin_lock(&chip->reg_lock);
+	snd_via8233_codec_ready(chip, ac97->num);
+	snd_via8233_codec_xwrite(chip, xval);
+	snd_via8233_codec_ready(chip, ac97->num);
+	spin_unlock(&chip->reg_lock);
+}
+
+static unsigned short snd_via8233_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return ~0);
+	unsigned int val;
+	int valid = ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+	int i;
+	
+	val = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT;
+	val |= valid;
+	val |= VIA_REG_AC97_READ;
+	val |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+	spin_lock(&chip->reg_lock);
+	snd_via8233_codec_ready(chip, ac97->num);
+	snd_via8233_codec_xwrite(chip, val);
+	snd_via8233_codec_ready(chip, ac97->num);
+	for (i=1000; i--;) {
+		val = snd_via8233_codec_xread(chip);
+		if (val & valid) {
+			spin_unlock(&chip->reg_lock);
+			return (unsigned short)val;
+		}
+	}
+	spin_unlock(&chip->reg_lock);
+	snd_printk("codec_read: codec %i is not valid [0x%x]\n", ac97->num, val);
+	/* have to return some value, this is better then 0 */
+	return ~0;
+}
+
+#if 0
+static void snd_via8233_channel_print(via8233_t *chip, viadev_t *viadev)
+{
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n",
+			port,
+			inb(port + VIA_REG_OFFSET_STATUS),
+			inb(port + VIA_REG_OFFSET_CONTROL),
+			inl(port + VIA_REG_OFFSET_TYPE),
+			inl(port + VIA_REG_OFFSET_CURR_PTR),
+			inl(port + VIA_REG_OFFSET_CURR_COUNT));
+}
+#endif
+
+static void snd_via8233_channel_reset(via8233_t *chip, viadev_t *viadev)
+{
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	outb(VIA_REG_CTRL_TERMINATE, port + VIA_REG_OFFSET_CONTROL);
+	udelay(50);
+	/* disable interrupts */
+	outb(0, port + VIA_REG_OFFSET_CONTROL);
+	/* clear interrupts */
+	outb(0x3, port + VIA_REG_OFFSET_STATUS);
+}
+
+static int snd_via8233_trigger(via8233_t *chip, viadev_t *viadev, int cmd)
+{
+	unsigned char val;
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = VIA_REG_CTRL_INT | VIA_REG_CTRL_START;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = VIA_REG_CTRL_TERMINATE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val = VIA_REG_CTRL_INT | VIA_REG_CTRL_PAUSE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		val = VIA_REG_CTRL_INT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	outb(val, port + VIA_REG_OFFSET_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP)
+		snd_via8233_channel_reset(chip, viadev);
+	return 0;
+}
+
+static void snd_via8233_setup_periods(via8233_t *chip, viadev_t *viadev,
+					snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int idx, frags;
+	unsigned int *table = viadev->table;
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	viadev->physbuf = runtime->dma_addr;
+	viadev->size = snd_pcm_lib_buffer_bytes(substream);
+	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
+	viadev->frags = runtime->periods;
+
+	snd_via8233_channel_reset(chip, viadev);
+	outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
+	outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
+	     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
+			0xff000000,    /* STOP index is never reached */
+			port + VIA_REG_OFFSET_TYPE);
+
+	if (viadev->size == viadev->fragsize) {
+		table[0] = cpu_to_le32(viadev->physbuf);
+		table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */
+				       viadev->fragsize);
+	} else {
+		frags = viadev->size / viadev->fragsize;
+		for (idx = 0; idx < frags - 1; idx++) {
+			table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize));
+			table[(idx << 1) + 1] = cpu_to_le32(0x40000000 |	/* flag */
+							    viadev->fragsize);
+		}
+		table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize));
+		table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 |	/* EOL */
+							  viadev->fragsize);
+	}
+#if 0
+	printk("%s: size = %d  frags = %d  fragsize = %d\n",  __FUNCTION__, viadev->size, frags, viadev->fragsize);
+	for (idx=0; idx < frags; idx++)
+		printk("    address %x,  count %x\n", table[idx*2], table[idx*2+1]);
+#endif
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static inline void snd_via8233_update(via8233_t *chip, viadev_t *viadev)
+{
+	snd_pcm_period_elapsed(viadev->substream);
+	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
+}
+
+static void snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	via8233_t *chip = snd_magic_cast(via8233_t, dev_id, return);
+
+	if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via8233_update(chip, &chip->playback);
+	if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via8233_update(chip, &chip->capture);
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_via8233_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via8233_trigger(chip, &chip->playback, cmd);
+}
+
+static int snd_via8233_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	return snd_via8233_trigger(chip, &chip->capture, cmd);
+}
+
+static int snd_via8233_hw_params(snd_pcm_substream_t * substream,
+					  snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_via8233_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_via8233_playback_prepare(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long tmp;
+
+	if (inb(VIAREG(chip, PLAYBACK_STATUS)) & VIA_REG_STAT_ACTIVE)
+		return 0;
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	snd_via8233_setup_periods(chip, &chip->playback, substream);
+	/* I don't understand this stuff but its from the documentation and this way it works */
+	outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L));
+	outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R));
+	tmp = inl(VIAREG(chip, PLAYBACK_TYPE)) & ~0xfffff;
+	outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_TYPE));
+	return 0;
+}
+
+static int snd_via8233_capture_prepare(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	snd_via8233_setup_periods(chip, &chip->capture, substream);
+	outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL));
+	outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO));
+	return 0;
+}
+
+static inline unsigned int snd_via8233_cur_ptr(via8233_t *chip, viadev_t *viadev)
+{
+	unsigned int val, count;
+
+	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff;
+	/* The via686a does not have this current index register,
+	 * this register makes life easier for us here. */
+	val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->frags;
+	val *= viadev->fragsize;
+	val += viadev->fragsize - count;
+	// printk("pointer: ptr = 0x%x, count = 0x%x, val = 0x%x\n", ptr, count, val);
+	return val;
+}
+
+static snd_pcm_uframes_t snd_via8233_playback_pointer(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->playback));
+}
+
+static snd_pcm_uframes_t snd_via8233_capture_pointer(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->capture));
+}
+
+static snd_pcm_hardware_t snd_via8233_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		VIA_MAX_FRAGS,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_via8233_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			0,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	128 * 1024,
+	period_bytes_min:	32,
+	period_bytes_max:	128 * 1024,
+	periods_min:		1,
+	periods_max:		VIA_MAX_FRAGS,
+	fifo_size:		0,
+};
+
+static int snd_via8233_playback_open(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback.substream = substream;
+	runtime->hw = snd_via8233_playback;
+	runtime->hw.rates = chip->ac97->rates_front_dac;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_via8233_capture_open(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture.substream = substream;
+	runtime->hw = snd_via8233_capture;
+	runtime->hw.rates = chip->ac97->rates_adc;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_via8233_playback_close(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+
+	snd_via8233_channel_reset(chip, &chip->playback);
+	chip->playback.substream = NULL;
+	/* disable DAC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
+	return 0;
+}
+
+static int snd_via8233_capture_close(snd_pcm_substream_t * substream)
+{
+	via8233_t *chip = snd_pcm_substream_chip(substream);
+
+	snd_via8233_channel_reset(chip, &chip->capture);
+	chip->capture.substream = NULL;
+	/* disable ADC power */
+	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	return 0;
+}
+
+static snd_pcm_ops_t snd_via8233_playback_ops = {
+	open:		snd_via8233_playback_open,
+	close:		snd_via8233_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_via8233_hw_params,
+	hw_free:	snd_via8233_hw_free,
+	prepare:	snd_via8233_playback_prepare,
+	trigger:	snd_via8233_playback_trigger,
+	pointer:	snd_via8233_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_via8233_capture_ops = {
+	open:		snd_via8233_capture_open,
+	close:		snd_via8233_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_via8233_hw_params,
+	hw_free:	snd_via8233_hw_free,
+	prepare:	snd_via8233_capture_prepare,
+	trigger:	snd_via8233_capture_trigger,
+	pointer:	snd_via8233_capture_pointer,
+};
+
+static void snd_via8233_pcm_free(snd_pcm_t *pcm)
+{
+	via8233_t *chip = snd_magic_cast(via8233_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "VIA 8233", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_via8233_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "VIA 8233");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = NULL;
+	return 0;
+}
+
+/*
+ *  Mixer part
+ */
+
+static void snd_via8233_codec_init(ac97_t *ac97)
+{
+	// via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
+
+	/* disable DAC & ADC power */
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
+	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
+	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+}
+
+static void snd_via8233_mixer_free_ac97(ac97_t *ac97)
+{
+	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+}
+
+static int __devinit snd_via8233_mixer(via8233_t *chip)
+{
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_via8233_codec_write;
+	ac97.read = snd_via8233_codec_read;
+	ac97.init = snd_via8233_codec_init;
+	ac97.private_data = chip;
+	ac97.private_free = snd_via8233_mixer_free_ac97;
+	ac97.clock = chip->ac97_clock;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *
+ */
+
+static int __devinit snd_via8233_chip_init(via8233_t *chip)
+{
+	ac97_t ac97;
+	unsigned char stat;
+	int i;
+	
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+
+	/* deassert ACLink reset */
+	pci_write_config_byte(chip->pci, 0x41, 0x40);
+	udelay(100);
+	/* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+	pci_write_config_byte(chip->pci, 0x41, 0x60);
+	udelay(2);
+	/* ACLink on, deassert ACLink reset, VSR, SGD data out */
+	pci_write_config_byte(chip->pci, 0x41, 0xcc);
+
+	/* Wait for codec ready to be accessed. */
+	for (i=HZ; i--; ) {
+		pci_read_config_byte(chip->pci, 0x40, &stat);
+		if (stat & 1)
+			break;
+		if (!i) {
+			snd_printk("chip_init: failed to access primary codec.\n");
+			return ~0;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+	snd_via8233_codec_ready(chip, 0);
+	snd_via8233_codec_write(&ac97, AC97_RESET, 0x0000);
+	snd_via8233_codec_read(&ac97, 0);
+
+	/* disable interrupts */
+	snd_via8233_channel_reset(chip, &chip->playback);
+	snd_via8233_channel_reset(chip, &chip->capture);
+	return 0;
+}
+
+static int snd_via8233_free(via8233_t *chip)
+{
+	if (chip->irq < 0)
+		goto __end_hw;
+	/* disable interrupts */
+	snd_via8233_channel_reset(chip, &chip->playback);
+	snd_via8233_channel_reset(chip, &chip->capture);
+	/* --- */
+      __end_hw:
+	synchronize_irq();
+	if (chip->tables)
+		snd_free_pci_pages(chip->pci,
+				   VIA_NUM_OF_DMA_CHANNELS * sizeof(unsigned int) * VIA_MAX_FRAGS * 2,
+				   chip->tables,
+				   chip->tables_addr);
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_via8233_dev_free(snd_device_t *device)
+{
+	via8233_t *chip = snd_magic_cast(via8233_t, device->device_data, return -ENXIO);
+	return snd_via8233_free(chip);
+}
+
+static int __devinit snd_via8233_create(snd_card_t * card,
+				     struct pci_dev *pci,
+				     unsigned int ac97_clock,
+				     via8233_t ** r_via)
+{
+	via8233_t *chip;
+	int err;
+        static snd_device_ops_t ops = {
+		dev_free:	snd_via8233_dev_free,
+        };
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	if ((chip = snd_magic_kcalloc(via8233_t, 0, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->update_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 256, "VIA8233")) == NULL) {
+		snd_via8233_free(chip);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_via8233_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA8233", (void *)chip)) {
+		snd_via8233_free(chip);
+		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	if (ac97_clock >= 8000 && ac97_clock <= 48000)
+		chip->ac97_clock = ac97_clock;
+	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
+	synchronize_irq();
+
+	/* initialize offsets */
+	chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
+	chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
+
+	/* allocate buffer descriptor lists */
+	/* the start of each lists must be aligned to 8 bytes */
+	chip->tables = (unsigned int *)snd_malloc_pci_pages(pci,
+				VIA_NUM_OF_DMA_CHANNELS * sizeof(unsigned int) * VIA_MAX_FRAGS * 2,
+				&chip->tables_addr);
+	if (chip->tables == NULL) {
+		snd_via8233_free(chip);
+		return -ENOMEM;
+	}
+	/* tables must be aligned to 8 bytes, but the kernel pages
+	   are much bigger, so we don't care */
+	chip->playback.table = chip->tables;
+	chip->playback.table_addr = chip->tables_addr;
+	chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2;
+	chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2;
+	if ((err = snd_via8233_chip_init(chip)) < 0) {
+		snd_via8233_free(chip);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_via8233_free(chip);
+		return err;
+	}
+
+	/* The 8233 ac97 controller does not implement the master bit
+	 * in the pci command register. IMHO this is a violation of the PCI spec.
+	 * We call pci_set_master here because it does not hurt. */
+	pci_set_master(pci);
+
+	*r_via = chip;
+	return 0;
+}
+
+static int __devinit snd_via8233_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	via8233_t *chip;
+	int pcm_dev = 0;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_via8233_create(card,
+				      pci,
+				      snd_ac97_clock[dev],
+				      &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+
+	if (snd_via8233_mixer(chip) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_via8233_pcm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "VIA8233");
+	strcpy(card->shortname, "VIA 8233");
+	
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_via8233_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "VIA 8233",
+	id_table: snd_via8233_ids,
+	probe: snd_via8233_probe,
+	remove: __devexit_p(snd_via8233_remove),
+};
+
+static int __init alsa_card_via8233_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "VIA 8233 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_via8233_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via8233_init)
+module_exit(alsa_card_via8233_exit)
+
+#ifndef MODULE
+
+/* format is: snd-via8233=snd_enable,snd_index,snd_id,snd_ac97_clock */
+
+static int __init alsa_card_via8233_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-via8233=", alsa_card_via8233_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/ymfpci/Makefile linux-2.4.19-pre5-mjc/sound/pci/ymfpci/Makefile
--- linux/sound/pci/ymfpci/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _ymfpci.o
+
+list-multi   := snd-ymfpci.o
+
+snd-ymfpci-objs := ymfpci.o ymfpci_main.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o
+
+include $(TOPDIR)/Rules.make
+
+snd-ymfpci.o: $(snd-ymfpci-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-ymfpci-objs)
diff -Nru linux/sound/pci/ymfpci/ymfpci.c linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci.c
--- linux/sound/pci/ymfpci/ymfpci.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,327 @@
+/*
+ *  The driver for the Yamaha's DS1/DS1E cards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/ymfpci.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Yamaha DS-XG PCI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Yamaha,YMF724},"
+		"{Yamaha,YMF724F},"
+		"{Yamaha,YMF740},"
+		"{Yamaha,YMF740C},"
+		"{Yamaha,YMF744},"
+		"{Yamaha,YMF754}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static long snd_fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for the Yamaha DS-XG PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for the Yamaha DS-XG PCI soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Yamaha DS-XG soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 Port.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM OPL-3 Port.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED);
+
+static struct pci_device_id snd_ymfpci_ids[] __devinitdata = {
+        { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF724 */
+        { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF724F */
+        { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF740 */
+        { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF740C */
+        { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF744 */
+        { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF754 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids);
+
+static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
+					   const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	ymfpci_t *chip;
+	opl3_t *opl3;
+	char *str;
+	int err;
+	u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	switch (id->device) {
+	case 0x0004: str = "YMF724"; break;
+	case 0x000d: str = "YMF724F"; break;
+	case 0x000a: str = "YMF740"; break;
+	case 0x000c: str = "YMF740C"; break;
+	case 0x0010: str = "YMF744"; break;
+	case 0x0012: str = "YMF754"; break;
+	default: str = "???"; break;
+	}
+
+	legacy_ctrl = 0;
+	legacy_ctrl2 = 0x0800;	/* SMOD = 01 */
+
+	if (id->device >= 0x0010) { /* YMF 744/754 */
+		if (snd_fm_port[dev] < 0)
+			snd_fm_port[dev] = pci_resource_start(pci, 1);
+		else if (check_region(snd_fm_port[dev], 4))
+			snd_fm_port[dev] = -1;
+		if (snd_fm_port[dev] >= 0) {
+			legacy_ctrl |= 2;
+			pci_write_config_word(pci, PCIR_DSXG_FMBASE, snd_fm_port[dev]);
+		}
+		if (snd_mpu_port[dev] < 0)
+			snd_mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
+		else if (check_region(snd_mpu_port[dev], 2))
+			snd_mpu_port[dev] = -1;
+		if (snd_mpu_port[dev] >= 0) {
+			legacy_ctrl |= 8;
+			pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, snd_mpu_port[dev]);
+			snd_printd("MPU401 supported on 0x%lx\n", snd_mpu_port[dev]);
+		}
+	} else {
+		switch (snd_fm_port[dev]) {
+		case 0x388: legacy_ctrl2 |= 0; break;
+		case 0x398: legacy_ctrl2 |= 1; break;
+		case 0x3a0: legacy_ctrl2 |= 2; break;
+		case 0x3a8: legacy_ctrl2 |= 3; break;
+		default: snd_fm_port[dev] = -1; break;
+		}
+		if (snd_fm_port[dev] > 0 && check_region(snd_fm_port[dev], 4) == 0)
+			legacy_ctrl |= 2;
+		else {
+			legacy_ctrl2 &= ~3;
+			snd_fm_port[dev] = -1;
+		}
+		switch (snd_mpu_port[dev]) {
+		case 0x330: legacy_ctrl2 |= 0 << 4; break;
+		case 0x300: legacy_ctrl2 |= 1 << 4; break;
+		case 0x332: legacy_ctrl2 |= 2 << 4; break;
+		case 0x334: legacy_ctrl2 |= 3 << 4; break;
+		default: snd_mpu_port[dev] = -1; break;
+		}
+		if (snd_mpu_port[dev] > 0 && check_region(snd_mpu_port[dev], 2) == 0) {
+			snd_printd("MPU401 supported on 0x%lx\n", snd_mpu_port[dev]);
+			legacy_ctrl |= 8;
+		} else {
+			legacy_ctrl2 &= ~(3 << 4);
+			snd_mpu_port[dev] = -1;
+		}
+	}
+	if (snd_mpu_port[dev] > 0) {
+		legacy_ctrl |= 0x10; /* MPU401 irq enable */
+		legacy_ctrl2 |= 1 << 15; /* IMOD */
+	}
+	pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl);
+	//snd_printdd("legacy_ctrl = 0x%x\n", legacy_ctrl);
+	pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+	//snd_printdd("legacy_ctrl2 = 0x%x\n", legacy_ctrl2);
+	pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
+	if ((err = snd_ymfpci_create(card, pci,
+				     old_legacy_ctrl,
+			 	     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_mpu_port[dev] > 0) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
+					       snd_mpu_port[dev], 0,
+					       pci->irq, 0, &chip->rawmidi)) < 0) {
+			printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", snd_mpu_port[dev]);
+		} else {
+			legacy_ctrl &= ~0x10; /* disable MPU401 irq */
+			pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+		}
+	}
+	if (snd_fm_port[dev] > 0) {
+		if ((err = snd_opl3_create(card,
+					   snd_fm_port[dev],
+					   snd_fm_port[dev] + 2,
+					   OPL3_HW_OPL3, 0, &opl3)) < 0) {
+			printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", snd_fm_port[dev]);
+		} else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			snd_printk("cannot create opl3 hwdep\n");
+			return err;
+		}
+	}
+	if ((err = snd_ymfpci_joystick(chip)) < 0) {
+		printk(KERN_WARNING "ymfpci: cannot initialize joystick, skipping...\n");
+	}
+	strcpy(card->driver, str);
+	sprintf(card->shortname, "Yamaha DS-XG PCI (%s)", str);
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname,
+		chip->reg_area_virt,
+		chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, chip);
+	dev++;
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifndef PCI_OLD_SUSPEND
+static int snd_card_ymfpci_suspend(struct pci_dev *pci, u32 state)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO);
+	snd_ymfpci_suspend(chip);
+	return 0;
+}
+static int snd_card_ymfpci_resume(struct pci_dev *pci)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO);
+	snd_ymfpci_resume(chip);
+	return 0;
+}
+#else
+static void snd_card_ymfpci_suspend(struct pci_dev *pci)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return);
+	snd_ymfpci_suspend(chip);
+}
+static void snd_card_ymfpci_resume(struct pci_dev *pci)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return);
+	snd_ymfpci_resume(chip);
+}
+#endif
+#endif
+
+static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return);
+	if (chip)
+		snd_card_free(chip->card);
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "Yamaha DS-XG PCI",
+	id_table: snd_ymfpci_ids,
+	probe: snd_card_ymfpci_probe,
+	remove: __devexit_p(snd_card_ymfpci_remove),
+#ifdef CONFIG_PM
+	suspend: snd_card_ymfpci_suspend,
+	resume: snd_card_ymfpci_resume,
+#endif	
+};
+
+static int __init alsa_card_ymfpci_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "Yamaha DS-XG PCI soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ymfpci_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ymfpci_init)
+module_exit(alsa_card_ymfpci_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ymfpci=snd_enable,snd_index,snd_id,
+			 snd_fm_port,snd_mpu_port */
+
+static int __init alsa_card_ymfpci_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
+	       get_option(&str,&snd_index[nr_dev]) == 2 &&
+	       get_id(&str,&snd_id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-ymfpci=", alsa_card_ymfpci_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/pci/ymfpci/ymfpci_image.h linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_image.h
--- linux/sound/pci/ymfpci/ymfpci_image.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_image.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,1565 @@
+#ifndef _HWMCODE_
+#define _HWMCODE_
+
+static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = {
+	0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+	0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+	0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+	0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x001A82, 0x032D0D, 0x000810, 0x10043A,
+	0x02D38D, 0x000810, 0x18043A, 0x00010D,
+	0x020015, 0x0000FD, 0x000020, 0x038860,
+	0x039060, 0x038060, 0x038040, 0x038040,
+	0x038040, 0x018040, 0x000A7D, 0x038040,
+	0x038040, 0x018040, 0x200402, 0x000882,
+	0x08001A, 0x000904, 0x015986, 0x000007,
+	0x260007, 0x000007, 0x000007, 0x018A06,
+	0x000007, 0x030C8D, 0x000810, 0x18043A,
+	0x260007, 0x00087D, 0x018042, 0x00160A,
+	0x04A206, 0x000007, 0x00218D, 0x000810,
+	0x08043A, 0x21C206, 0x000007, 0x0007FD,
+	0x018042, 0x08000A, 0x000904, 0x029386,
+	0x000195, 0x090D04, 0x000007, 0x000820,
+	0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
+	0x032206, 0x018040, 0x000A7D, 0x038042,
+	0x13804A, 0x18000A, 0x001820, 0x059060,
+	0x058860, 0x018040, 0x0000FD, 0x018042,
+	0x70000A, 0x000115, 0x071144, 0x032386,
+	0x030000, 0x007020, 0x034A06, 0x018040,
+	0x00348D, 0x000810, 0x08043A, 0x21EA06,
+	0x000007, 0x02D38D, 0x000810, 0x18043A,
+	0x018206, 0x000007, 0x240007, 0x000F8D,
+	0x000810, 0x00163A, 0x002402, 0x005C02,
+	0x0028FD, 0x000020, 0x018040, 0x08000D,
+	0x000815, 0x510984, 0x000007, 0x00004D,
+	0x000E5D, 0x000E02, 0x00418D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00008D,
+	0x000924, 0x000F02, 0x00458D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x018386, 0x000007, 0x01AA06, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x218086, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x055A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x034986, 0x000007, 0x002104, 0x034986,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x06C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00688D, 0x000810, 0x08043A, 0x288A06,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x060206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x215886, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x212086, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x07DA86, 0x00057D, 0x002820,
+	0x03B060, 0x07F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x07FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x008D8D, 0x000810,
+	0x08043A, 0x288A06, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x095186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x007FBD, 0x383DC4,
+	0x000007, 0x001A7D, 0x001375, 0x018042,
+	0x09004A, 0x10000A, 0x0B8D04, 0x139504,
+	0x000007, 0x000820, 0x019060, 0x001104,
+	0x212086, 0x010040, 0x0017FD, 0x018042,
+	0x08000A, 0x000904, 0x212286, 0x000007,
+	0x00197D, 0x038042, 0x09804A, 0x10000A,
+	0x000924, 0x001664, 0x0011FD, 0x038042,
+	0x2B804A, 0x19804A, 0x00008D, 0x218944,
+	0x000007, 0x002244, 0x0AE186, 0x000007,
+	0x001A64, 0x002A24, 0x00197D, 0x080102,
+	0x100122, 0x000820, 0x039060, 0x018040,
+	0x003DFD, 0x00008D, 0x000820, 0x018040,
+	0x001375, 0x001A7D, 0x010042, 0x09804A,
+	0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
+	0x309144, 0x000007, 0x00060D, 0x000A15,
+	0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
+	0x000464, 0x01B3E4, 0x0232E4, 0x000464,
+	0x000464, 0x000464, 0x000464, 0x00040D,
+	0x08B1C4, 0x000007, 0x000820, 0x000BF5,
+	0x030040, 0x00197D, 0x038042, 0x09804A,
+	0x000A24, 0x08000A, 0x080E64, 0x000007,
+	0x100122, 0x000820, 0x031060, 0x010040,
+	0x0064AC, 0x00027D, 0x000020, 0x018040,
+	0x00107D, 0x018042, 0x0011FD, 0x3B804A,
+	0x09804A, 0x20000A, 0x000095, 0x1A1144,
+	0x00A144, 0x0D2086, 0x00040D, 0x00B984,
+	0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
+	0x09804A, 0x28000A, 0x000095, 0x010924,
+	0x002A64, 0x0D1186, 0x000007, 0x002904,
+	0x0D2286, 0x000007, 0x0D2A06, 0x080002,
+	0x00008D, 0x00387D, 0x000820, 0x018040,
+	0x00127D, 0x018042, 0x10000A, 0x003904,
+	0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
+	0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
+	0x000015, 0x00082D, 0x02C78D, 0x000820,
+	0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
+	0x0E7186, 0x400025, 0x00008D, 0x110944,
+	0x000007, 0x00018D, 0x109504, 0x000007,
+	0x009164, 0x000424, 0x000424, 0x000424,
+	0x100102, 0x280002, 0x02C68D, 0x000820,
+	0x0EC206, 0x00018D, 0x00042D, 0x00008D,
+	0x109504, 0x000007, 0x00020D, 0x109184,
+	0x000007, 0x02C70D, 0x000820, 0x00008D,
+	0x0038FD, 0x018040, 0x003BFD, 0x001020,
+	0x03A860, 0x000815, 0x313184, 0x212184,
+	0x000007, 0x03B060, 0x03A060, 0x018040,
+	0x0022FD, 0x000095, 0x010924, 0x000424,
+	0x000424, 0x001264, 0x100102, 0x000820,
+	0x039060, 0x018040, 0x001924, 0x00FB8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x000424,
+	0x000424, 0x00117D, 0x018042, 0x08000A,
+	0x000A24, 0x280502, 0x280C02, 0x09800D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x0022FD, 0x018042, 0x08000A, 0x000095,
+	0x280DC4, 0x011924, 0x00197D, 0x018042,
+	0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
+	0x113144, 0x0A8D04, 0x000007, 0x080A44,
+	0x129504, 0x000007, 0x0023FD, 0x001020,
+	0x038040, 0x101244, 0x000007, 0x000820,
+	0x039060, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x10FA86, 0x000007,
+	0x003BFD, 0x000100, 0x000A10, 0x0B807A,
+	0x13804A, 0x090984, 0x000007, 0x000095,
+	0x013D04, 0x118086, 0x10000A, 0x100002,
+	0x090984, 0x000007, 0x038042, 0x11804A,
+	0x090D04, 0x000007, 0x10000A, 0x090D84,
+	0x000007, 0x00257D, 0x000820, 0x018040,
+	0x00010D, 0x000810, 0x28143A, 0x00127D,
+	0x018042, 0x20000A, 0x00197D, 0x018042,
+	0x00117D, 0x31804A, 0x10000A, 0x003124,
+	0x01280D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x300102, 0x003124, 0x000424, 0x000424,
+	0x001224, 0x280502, 0x001A4C, 0x130186,
+	0x700002, 0x00002D, 0x030000, 0x00387D,
+	0x018042, 0x10000A, 0x132A06, 0x002124,
+	0x0000AD, 0x100002, 0x00010D, 0x000924,
+	0x006B24, 0x01368D, 0x00397D, 0x000820,
+	0x058040, 0x038042, 0x09844A, 0x000606,
+	0x08040A, 0x003264, 0x00008D, 0x000A24,
+	0x001020, 0x00227D, 0x018040, 0x013C0D,
+	0x000810, 0x08043A, 0x29D206, 0x000007,
+	0x002820, 0x00207D, 0x018040, 0x00117D,
+	0x038042, 0x13804A, 0x33800A, 0x00387D,
+	0x018042, 0x08000A, 0x000904, 0x163A86,
+	0x000007, 0x00008D, 0x030964, 0x01478D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x380102,
+	0x000424, 0x000424, 0x001224, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x14A286,
+	0x000007, 0x280502, 0x001A4C, 0x163986,
+	0x000007, 0x032164, 0x00632C, 0x003DFD,
+	0x018042, 0x08000A, 0x000095, 0x090904,
+	0x000007, 0x000820, 0x001A4C, 0x156186,
+	0x018040, 0x030000, 0x157A06, 0x002124,
+	0x00010D, 0x000924, 0x006B24, 0x015B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x003A64,
+	0x000095, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x01628D, 0x000810, 0x08043A, 0x29D206,
+	0x000007, 0x14D206, 0x000007, 0x007020,
+	0x08010A, 0x10012A, 0x0020FD, 0x038860,
+	0x039060, 0x018040, 0x00227D, 0x018042,
+	0x003DFD, 0x08000A, 0x31844A, 0x000904,
+	0x16D886, 0x18008B, 0x00008D, 0x189904,
+	0x00312C, 0x17AA06, 0x000007, 0x00324C,
+	0x173386, 0x000007, 0x001904, 0x173086,
+	0x000007, 0x000095, 0x199144, 0x00222C,
+	0x003124, 0x00636C, 0x000E3D, 0x001375,
+	0x000BFD, 0x010042, 0x09804A, 0x10000A,
+	0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
+	0x000007, 0x00008D, 0x189904, 0x00226C,
+	0x00322C, 0x30050A, 0x301DAB, 0x002083,
+	0x0018FD, 0x018042, 0x08000A, 0x018924,
+	0x300502, 0x001083, 0x001875, 0x010042,
+	0x10000A, 0x00008D, 0x010924, 0x001375,
+	0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
+	0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
+	0x305C8B, 0x006083, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x187A86, 0x000007,
+	0x001E2D, 0x0005FD, 0x018042, 0x08000A,
+	0x028924, 0x280502, 0x00060D, 0x000810,
+	0x280C3A, 0x00008D, 0x000810, 0x28143A,
+	0x0A808D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001275, 0x030042, 0x21004A,
+	0x00008D, 0x1A0944, 0x000007, 0x01980D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0001F5, 0x030042, 0x0D004A, 0x10000A,
+	0x089144, 0x000007, 0x000820, 0x010040,
+	0x0025F5, 0x0A3144, 0x000007, 0x000820,
+	0x032860, 0x030040, 0x00217D, 0x038042,
+	0x0B804A, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00008D, 0x000124, 0x00012C,
+	0x000E64, 0x001A64, 0x00636C, 0x08010A,
+	0x10012A, 0x000820, 0x031060, 0x030040,
+	0x0020FD, 0x018042, 0x08000A, 0x00227D,
+	0x018042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00197D, 0x018042, 0x08000A,
+	0x0022FD, 0x038042, 0x10000A, 0x000820,
+	0x031060, 0x030040, 0x090D04, 0x000007,
+	0x000820, 0x030040, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x038042, 0x13804A, 0x19804A, 0x110D04,
+	0x198D04, 0x000007, 0x08000A, 0x001020,
+	0x031860, 0x030860, 0x030040, 0x00008D,
+	0x0B0944, 0x000007, 0x000820, 0x010040,
+	0x0005F5, 0x030042, 0x08000A, 0x000820,
+	0x010040, 0x0000F5, 0x010042, 0x08000A,
+	0x000904, 0x1C6086, 0x001E75, 0x030042,
+	0x01044A, 0x000C0A, 0x1C7206, 0x000007,
+	0x000402, 0x000C02, 0x00177D, 0x001AF5,
+	0x018042, 0x03144A, 0x031C4A, 0x03244A,
+	0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
+	0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
+	0x030042, 0x0B004A, 0x1B804A, 0x13804A,
+	0x20000A, 0x089144, 0x19A144, 0x0389E4,
+	0x0399EC, 0x005502, 0x005D0A, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x006502, 0x006D0A, 0x030042, 0x0B004A,
+	0x19004A, 0x2B804A, 0x13804A, 0x21804A,
+	0x30000A, 0x089144, 0x19A144, 0x2AB144,
+	0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
+	0x03A9E4, 0x000702, 0x00107D, 0x000415,
+	0x018042, 0x08000A, 0x0109E4, 0x000F02,
+	0x002AF5, 0x0019FD, 0x010042, 0x09804A,
+	0x10000A, 0x000934, 0x001674, 0x0029F5,
+	0x010042, 0x10000A, 0x00917C, 0x002075,
+	0x010042, 0x08000A, 0x000904, 0x1ED286,
+	0x0026F5, 0x0027F5, 0x030042, 0x09004A,
+	0x10000A, 0x000A3C, 0x00167C, 0x001A75,
+	0x000BFD, 0x010042, 0x51804A, 0x48000A,
+	0x160007, 0x001075, 0x010042, 0x282C0A,
+	0x281D12, 0x282512, 0x001F32, 0x1E0007,
+	0x0E0007, 0x001975, 0x010042, 0x002DF5,
+	0x0D004A, 0x10000A, 0x009144, 0x1FB286,
+	0x010042, 0x28340A, 0x000E5D, 0x00008D,
+	0x000375, 0x000820, 0x010040, 0x05D2F4,
+	0x54D104, 0x00735C, 0x205386, 0x000007,
+	0x0C0007, 0x080007, 0x0A0007, 0x02040D,
+	0x000810, 0x08043A, 0x332206, 0x000007,
+	0x205A06, 0x000007, 0x080007, 0x002275,
+	0x010042, 0x20000A, 0x002104, 0x212086,
+	0x001E2D, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x209286, 0x000007, 0x002010,
+	0x30043A, 0x00057D, 0x0180C3, 0x08000A,
+	0x028924, 0x280502, 0x280C02, 0x0A810D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x0004FD, 0x018042, 0x70000A, 0x030000,
+	0x007020, 0x06FA06, 0x018040, 0x02180D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x218A86, 0x000007, 0x01F206, 0x000007,
+	0x000875, 0x0009FD, 0x00010D, 0x220A06,
+	0x000295, 0x000B75, 0x00097D, 0x00000D,
+	0x000515, 0x010042, 0x18000A, 0x001904,
+	0x287886, 0x0006F5, 0x001020, 0x010040,
+	0x0004F5, 0x000820, 0x010040, 0x000775,
+	0x010042, 0x09804A, 0x10000A, 0x001124,
+	0x000904, 0x22BA86, 0x000815, 0x080102,
+	0x101204, 0x22DA06, 0x000575, 0x081204,
+	0x000007, 0x100102, 0x000575, 0x000425,
+	0x021124, 0x100102, 0x000820, 0x031060,
+	0x010040, 0x001924, 0x287886, 0x00008D,
+	0x000464, 0x009D04, 0x278886, 0x180102,
+	0x000575, 0x010042, 0x28040A, 0x00018D,
+	0x000924, 0x280D02, 0x00000D, 0x000924,
+	0x281502, 0x10000D, 0x000820, 0x0002F5,
+	0x010040, 0x200007, 0x001175, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x23C286,
+	0x000007, 0x000100, 0x080B20, 0x130B60,
+	0x1B0B60, 0x030A60, 0x010040, 0x050042,
+	0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
+	0x0006F5, 0x010042, 0x28140A, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x24CA86, 0x004015, 0x000095, 0x010D04,
+	0x24B886, 0x100022, 0x10002A, 0x24E206,
+	0x000007, 0x333104, 0x2AA904, 0x000007,
+	0x032124, 0x280502, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x00292C, 0x00636C,
+	0x25F386, 0x000007, 0x02B164, 0x000464,
+	0x000464, 0x00008D, 0x000A64, 0x280D02,
+	0x10008D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x00008D, 0x38B904, 0x000007,
+	0x03296C, 0x30010A, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x25BA86, 0x000007,
+	0x02312C, 0x28050A, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x267A86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x26C086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x26CA86, 0x000007, 0x003124, 0x300502,
+	0x003924, 0x300583, 0x000883, 0x0005F5,
+	0x010042, 0x28040A, 0x00008D, 0x008124,
+	0x280D02, 0x00008D, 0x008124, 0x281502,
+	0x10018D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001025, 0x000575, 0x030042,
+	0x09004A, 0x10000A, 0x0A0904, 0x121104,
+	0x000007, 0x001020, 0x050860, 0x050040,
+	0x0006FD, 0x018042, 0x09004A, 0x10000A,
+	0x0000A5, 0x0A0904, 0x121104, 0x000007,
+	0x000820, 0x019060, 0x010040, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x284286,
+	0x000007, 0x230A06, 0x000007, 0x000606,
+	0x000007, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x289286, 0x000007, 0x000100,
+	0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
+	0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
+	0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
+	0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
+	0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
+	0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
+	0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
+	0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
+	0x000606, 0x018040, 0x00008D, 0x000A64,
+	0x280D02, 0x000A24, 0x00027D, 0x018042,
+	0x10000A, 0x001224, 0x0003FD, 0x018042,
+	0x08000A, 0x000904, 0x2A8286, 0x000007,
+	0x00018D, 0x000A24, 0x000464, 0x000464,
+	0x080102, 0x000924, 0x000424, 0x000424,
+	0x100102, 0x02000D, 0x009144, 0x2AD986,
+	0x000007, 0x0001FD, 0x018042, 0x08000A,
+	0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x00027D, 0x001020, 0x000606, 0x018040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x2B2A86, 0x000007, 0x00037D, 0x018042,
+	0x08000A, 0x000904, 0x2B5A86, 0x000007,
+	0x000075, 0x002E7D, 0x010042, 0x0B804A,
+	0x000020, 0x000904, 0x000686, 0x010040,
+	0x31844A, 0x30048B, 0x000883, 0x00008D,
+	0x000810, 0x28143A, 0x00008D, 0x000810,
+	0x280C3A, 0x000675, 0x010042, 0x08000A,
+	0x003815, 0x010924, 0x280502, 0x0B000D,
+	0x000820, 0x0002F5, 0x010040, 0x000606,
+	0x220007, 0x000464, 0x000464, 0x000606,
+	0x000007, 0x000134, 0x007F8D, 0x00093C,
+	0x281D12, 0x282512, 0x001F32, 0x0E0007,
+	0x00010D, 0x00037D, 0x000820, 0x018040,
+	0x05D2F4, 0x000007, 0x080007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2D0286,
+	0x000007, 0x000606, 0x000007, 0x000007,
+	0x000012, 0x100007, 0x320007, 0x600007,
+	0x100080, 0x48001A, 0x004904, 0x2D6186,
+	0x000007, 0x001210, 0x58003A, 0x000145,
+	0x5C5D04, 0x000007, 0x000080, 0x48001A,
+	0x004904, 0x2DB186, 0x000007, 0x001210,
+	0x50003A, 0x005904, 0x2E0886, 0x000045,
+	0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
+	0x004224, 0x500102, 0x200502, 0x000082,
+	0x40001A, 0x004104, 0x2E3986, 0x000007,
+	0x003865, 0x40001A, 0x004020, 0x00104D,
+	0x04C184, 0x301B86, 0x000040, 0x040007,
+	0x000165, 0x000145, 0x004020, 0x000040,
+	0x000765, 0x080080, 0x40001A, 0x004104,
+	0x2EC986, 0x000007, 0x001210, 0x40003A,
+	0x004104, 0x2F2286, 0x00004D, 0x0000CD,
+	0x004810, 0x20043A, 0x000882, 0x40001A,
+	0x004104, 0x2F3186, 0x000007, 0x004820,
+	0x005904, 0x300886, 0x000040, 0x0007E5,
+	0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
+	0x4216E0, 0x021260, 0x000040, 0x000032,
+	0x400075, 0x00007D, 0x07D574, 0x200512,
+	0x000082, 0x40001A, 0x004104, 0x2FE186,
+	0x000007, 0x037206, 0x640007, 0x060007,
+	0x0000E5, 0x000020, 0x000040, 0x000A65,
+	0x000020, 0x020040, 0x020040, 0x000040,
+	0x000165, 0x000042, 0x70000A, 0x007104,
+	0x30A286, 0x000007, 0x018206, 0x640007,
+	0x050000, 0x007020, 0x000040, 0x037206,
+	0x640007, 0x000007, 0x00306D, 0x028860,
+	0x029060, 0x08000A, 0x028860, 0x008040,
+	0x100012, 0x00100D, 0x009184, 0x314186,
+	0x000E0D, 0x009184, 0x325186, 0x000007,
+	0x300007, 0x001020, 0x003B6D, 0x008040,
+	0x000080, 0x08001A, 0x000904, 0x316186,
+	0x000007, 0x001220, 0x000DED, 0x008040,
+	0x008042, 0x10000A, 0x40000D, 0x109544,
+	0x000007, 0x001020, 0x000DED, 0x008040,
+	0x008042, 0x20040A, 0x000082, 0x08001A,
+	0x000904, 0x31F186, 0x000007, 0x003B6D,
+	0x008042, 0x08000A, 0x000E15, 0x010984,
+	0x329B86, 0x600007, 0x08001A, 0x000C15,
+	0x010984, 0x328386, 0x000020, 0x1A0007,
+	0x0002ED, 0x008040, 0x620007, 0x00306D,
+	0x028042, 0x0A804A, 0x000820, 0x0A804A,
+	0x000606, 0x10804A, 0x000007, 0x282512,
+	0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
+	0x000786, 0x000007, 0x0C0007, 0x0A0007,
+	0x1C0007, 0x003465, 0x020040, 0x004820,
+	0x025060, 0x40000A, 0x024060, 0x000040,
+	0x454944, 0x000007, 0x004020, 0x003AE5,
+	0x000040, 0x0028E5, 0x000042, 0x48000A,
+	0x004904, 0x386886, 0x002C65, 0x000042,
+	0x40000A, 0x0000D5, 0x454104, 0x000007,
+	0x000655, 0x054504, 0x34F286, 0x0001D5,
+	0x054504, 0x34F086, 0x002B65, 0x000042,
+	0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
+	0x000007, 0x454504, 0x000007, 0x0000CD,
+	0x444944, 0x000007, 0x454504, 0x000007,
+	0x00014D, 0x554944, 0x000007, 0x045144,
+	0x34E986, 0x002C65, 0x000042, 0x48000A,
+	0x4CD104, 0x000007, 0x04C144, 0x34F386,
+	0x000007, 0x160007, 0x002CE5, 0x040042,
+	0x40000A, 0x004020, 0x000040, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x356086,
+	0x000007, 0x002402, 0x36A206, 0x005C02,
+	0x0025E5, 0x000042, 0x40000A, 0x004274,
+	0x002AE5, 0x000042, 0x40000A, 0x004274,
+	0x500112, 0x0029E5, 0x000042, 0x40000A,
+	0x004234, 0x454104, 0x000007, 0x004020,
+	0x000040, 0x003EE5, 0x000020, 0x000040,
+	0x002DE5, 0x400152, 0x50000A, 0x045144,
+	0x364A86, 0x0000C5, 0x003EE5, 0x004020,
+	0x000040, 0x002BE5, 0x000042, 0x40000A,
+	0x404254, 0x000007, 0x002AE5, 0x004020,
+	0x000040, 0x500132, 0x040134, 0x005674,
+	0x0029E5, 0x020042, 0x42000A, 0x000042,
+	0x50000A, 0x05417C, 0x0028E5, 0x000042,
+	0x48000A, 0x0000C5, 0x4CC144, 0x371086,
+	0x0026E5, 0x0027E5, 0x020042, 0x40004A,
+	0x50000A, 0x00423C, 0x00567C, 0x0028E5,
+	0x004820, 0x000040, 0x281D12, 0x282512,
+	0x001F72, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x37AA86, 0x0E0007, 0x160007,
+	0x1E0007, 0x003EE5, 0x000042, 0x40000A,
+	0x004104, 0x37E886, 0x002D65, 0x000042,
+	0x28340A, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
+	0x54D104, 0x00735C, 0x385186, 0x000007,
+	0x000606, 0x080007, 0x0C0007, 0x080007,
+	0x0A0007, 0x0001E5, 0x020045, 0x004020,
+	0x000060, 0x000365, 0x000040, 0x002E65,
+	0x001A20, 0x0A1A60, 0x000040, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x000606, 0x50004A, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+// --------------------------------------------
+//  DS-1E Controller InstructionRAM Code
+//	1999/06/21
+//	Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09  creat
+// 04/12  stop nise fix
+// 06/21  WorkingOff timming
+static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x00800D, 0x000810, 0x20043A, 0x001A82,
+	0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+	0x000810, 0x18043A, 0x00010D, 0x020015,
+	0x0000FD, 0x000020, 0x038860, 0x039060,
+	0x038060, 0x038040, 0x038040, 0x038040,
+	0x018040, 0x000A7D, 0x038040, 0x038040,
+	0x018040, 0x200402, 0x000882, 0x08001A,
+	0x000904, 0x017186, 0x000007, 0x260007,
+	0x400007, 0x000007, 0x03258D, 0x000810,
+	0x18043A, 0x260007, 0x284402, 0x00087D,
+	0x018042, 0x00160A, 0x05A206, 0x000007,
+	0x440007, 0x00230D, 0x000810, 0x08043A,
+	0x22FA06, 0x000007, 0x0007FD, 0x018042,
+	0x08000A, 0x000904, 0x02AB86, 0x000195,
+	0x090D04, 0x000007, 0x000820, 0x0000F5,
+	0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+	0x018040, 0x000A7D, 0x038042, 0x13804A,
+	0x18000A, 0x001820, 0x059060, 0x058860,
+	0x018040, 0x0000FD, 0x018042, 0x70000A,
+	0x000115, 0x071144, 0x033B86, 0x030000,
+	0x007020, 0x036206, 0x018040, 0x00360D,
+	0x000810, 0x08043A, 0x232206, 0x000007,
+	0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+	0x000007, 0x240007, 0x000F8D, 0x000810,
+	0x00163A, 0x002402, 0x005C02, 0x0028FD,
+	0x000020, 0x018040, 0x08000D, 0x000815,
+	0x510984, 0x000007, 0x00004D, 0x000E5D,
+	0x000E02, 0x00430D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x00008D, 0x000924,
+	0x000F02, 0x00470D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x480480, 0x001210,
+	0x28043A, 0x00778D, 0x000810, 0x280C3A,
+	0x00068D, 0x000810, 0x28143A, 0x284402,
+	0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+	0x000820, 0x0002FD, 0x018040, 0x260007,
+	0x200007, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x051286, 0x000007, 0x240007,
+	0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x019B86, 0x000007, 0x01B206, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x22B886, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x065A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x036186, 0x000007, 0x002104, 0x036186,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x07C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00788D, 0x000810, 0x08043A, 0x2A1206,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x070206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x229086, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x225886, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x08DA86, 0x00057D, 0x002820,
+	0x03B060, 0x08F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x08FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x009D8D, 0x000810,
+	0x08043A, 0x2A1206, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x0A5186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x00107D, 0x018042,
+	0x08000A, 0x000215, 0x010984, 0x3A8186,
+	0x000007, 0x007FBD, 0x383DC4, 0x000007,
+	0x001A7D, 0x001375, 0x018042, 0x09004A,
+	0x10000A, 0x0B8D04, 0x139504, 0x000007,
+	0x000820, 0x019060, 0x001104, 0x225886,
+	0x010040, 0x0017FD, 0x018042, 0x08000A,
+	0x000904, 0x225A86, 0x000007, 0x00197D,
+	0x038042, 0x09804A, 0x10000A, 0x000924,
+	0x001664, 0x0011FD, 0x038042, 0x2B804A,
+	0x19804A, 0x00008D, 0x218944, 0x000007,
+	0x002244, 0x0C1986, 0x000007, 0x001A64,
+	0x002A24, 0x00197D, 0x080102, 0x100122,
+	0x000820, 0x039060, 0x018040, 0x003DFD,
+	0x00008D, 0x000820, 0x018040, 0x001375,
+	0x001A7D, 0x010042, 0x09804A, 0x10000A,
+	0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+	0x000007, 0x00060D, 0x000A15, 0x000C1D,
+	0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+	0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+	0x000464, 0x000464, 0x00040D, 0x08B1C4,
+	0x000007, 0x000820, 0x000BF5, 0x030040,
+	0x00197D, 0x038042, 0x09804A, 0x000A24,
+	0x08000A, 0x080E64, 0x000007, 0x100122,
+	0x000820, 0x031060, 0x010040, 0x0064AC,
+	0x00027D, 0x000020, 0x018040, 0x00107D,
+	0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+	0x20000A, 0x000095, 0x1A1144, 0x00A144,
+	0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+	0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+	0x28000A, 0x000095, 0x010924, 0x002A64,
+	0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+	0x000007, 0x0E6206, 0x080002, 0x00008D,
+	0x00387D, 0x000820, 0x018040, 0x00127D,
+	0x018042, 0x10000A, 0x003904, 0x0F0986,
+	0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+	0x000025, 0x0FB206, 0x00002D, 0x000015,
+	0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+	0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+	0x400025, 0x00008D, 0x110944, 0x000007,
+	0x00018D, 0x109504, 0x000007, 0x009164,
+	0x000424, 0x000424, 0x000424, 0x100102,
+	0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+	0x00018D, 0x00042D, 0x00008D, 0x109504,
+	0x000007, 0x00020D, 0x109184, 0x000007,
+	0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+	0x018040, 0x003BFD, 0x001020, 0x03A860,
+	0x000815, 0x313184, 0x212184, 0x000007,
+	0x03B060, 0x03A060, 0x018040, 0x0022FD,
+	0x000095, 0x010924, 0x000424, 0x000424,
+	0x001264, 0x100102, 0x000820, 0x039060,
+	0x018040, 0x001924, 0x010F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x000424, 0x000424,
+	0x00117D, 0x018042, 0x08000A, 0x000A24,
+	0x280502, 0x280C02, 0x09800D, 0x000820,
+	0x0002FD, 0x018040, 0x200007, 0x0022FD,
+	0x018042, 0x08000A, 0x000095, 0x280DC4,
+	0x011924, 0x00197D, 0x018042, 0x0011FD,
+	0x09804A, 0x10000A, 0x0000B5, 0x113144,
+	0x0A8D04, 0x000007, 0x080A44, 0x129504,
+	0x000007, 0x0023FD, 0x001020, 0x038040,
+	0x101244, 0x000007, 0x000820, 0x039060,
+	0x018040, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x123286, 0x000007, 0x003BFD,
+	0x000100, 0x000A10, 0x0B807A, 0x13804A,
+	0x090984, 0x000007, 0x000095, 0x013D04,
+	0x12B886, 0x10000A, 0x100002, 0x090984,
+	0x000007, 0x038042, 0x11804A, 0x090D04,
+	0x000007, 0x10000A, 0x090D84, 0x000007,
+	0x00257D, 0x000820, 0x018040, 0x00010D,
+	0x000810, 0x28143A, 0x00127D, 0x018042,
+	0x20000A, 0x00197D, 0x018042, 0x00117D,
+	0x31804A, 0x10000A, 0x003124, 0x013B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x300102,
+	0x003124, 0x000424, 0x000424, 0x001224,
+	0x280502, 0x001A4C, 0x143986, 0x700002,
+	0x00002D, 0x030000, 0x00387D, 0x018042,
+	0x10000A, 0x146206, 0x002124, 0x0000AD,
+	0x100002, 0x00010D, 0x000924, 0x006B24,
+	0x014A0D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x003264, 0x00008D, 0x000A24, 0x001020,
+	0x00227D, 0x018040, 0x014F8D, 0x000810,
+	0x08043A, 0x2B5A06, 0x000007, 0x002820,
+	0x00207D, 0x018040, 0x00117D, 0x038042,
+	0x13804A, 0x33800A, 0x00387D, 0x018042,
+	0x08000A, 0x000904, 0x177286, 0x000007,
+	0x00008D, 0x030964, 0x015B0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x380102, 0x000424,
+	0x000424, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x280502, 0x001A4C, 0x177186, 0x000007,
+	0x032164, 0x00632C, 0x003DFD, 0x018042,
+	0x08000A, 0x000095, 0x090904, 0x000007,
+	0x000820, 0x001A4C, 0x169986, 0x018040,
+	0x030000, 0x16B206, 0x002124, 0x00010D,
+	0x000924, 0x006B24, 0x016F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x003A64, 0x000095,
+	0x001224, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x171286, 0x000007, 0x01760D,
+	0x000810, 0x08043A, 0x2B5A06, 0x000007,
+	0x160A06, 0x000007, 0x007020, 0x08010A,
+	0x10012A, 0x0020FD, 0x038860, 0x039060,
+	0x018040, 0x00227D, 0x018042, 0x003DFD,
+	0x08000A, 0x31844A, 0x000904, 0x181086,
+	0x18008B, 0x00008D, 0x189904, 0x00312C,
+	0x18E206, 0x000007, 0x00324C, 0x186B86,
+	0x000007, 0x001904, 0x186886, 0x000007,
+	0x000095, 0x199144, 0x00222C, 0x003124,
+	0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+	0x010042, 0x09804A, 0x10000A, 0x038AEC,
+	0x0393EC, 0x00224C, 0x18E186, 0x000007,
+	0x00008D, 0x189904, 0x00226C, 0x00322C,
+	0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+	0x018042, 0x08000A, 0x018924, 0x300502,
+	0x001083, 0x001875, 0x010042, 0x10000A,
+	0x00008D, 0x010924, 0x001375, 0x330542,
+	0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+	0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+	0x006083, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x19B286, 0x000007, 0x001E2D,
+	0x0005FD, 0x018042, 0x08000A, 0x028924,
+	0x280502, 0x00060D, 0x000810, 0x280C3A,
+	0x00008D, 0x000810, 0x28143A, 0x0A808D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x001275, 0x030042, 0x21004A, 0x00008D,
+	0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+	0x030042, 0x0D004A, 0x10000A, 0x089144,
+	0x000007, 0x000820, 0x010040, 0x0025F5,
+	0x0A3144, 0x000007, 0x000820, 0x032860,
+	0x030040, 0x00217D, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00008D, 0x000124, 0x00012C, 0x000E64,
+	0x001A64, 0x00636C, 0x08010A, 0x10012A,
+	0x000820, 0x031060, 0x030040, 0x0020FD,
+	0x018042, 0x08000A, 0x00227D, 0x018042,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00197D, 0x018042, 0x08000A, 0x0022FD,
+	0x038042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x090D04, 0x000007, 0x000820,
+	0x030040, 0x038042, 0x0B804A, 0x10000A,
+	0x000820, 0x031060, 0x030040, 0x038042,
+	0x13804A, 0x19804A, 0x110D04, 0x198D04,
+	0x000007, 0x08000A, 0x001020, 0x031860,
+	0x030860, 0x030040, 0x00008D, 0x0B0944,
+	0x000007, 0x000820, 0x010040, 0x0005F5,
+	0x030042, 0x08000A, 0x000820, 0x010040,
+	0x0000F5, 0x010042, 0x08000A, 0x000904,
+	0x1D9886, 0x001E75, 0x030042, 0x01044A,
+	0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+	0x000C02, 0x00177D, 0x001AF5, 0x018042,
+	0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+	0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+	0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x005502, 0x005D0A, 0x030042, 0x0B004A,
+	0x1B804A, 0x13804A, 0x20000A, 0x089144,
+	0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+	0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+	0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+	0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+	0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+	0x000702, 0x00107D, 0x000415, 0x018042,
+	0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+	0x0019FD, 0x010042, 0x09804A, 0x10000A,
+	0x000934, 0x001674, 0x0029F5, 0x010042,
+	0x10000A, 0x00917C, 0x002075, 0x010042,
+	0x08000A, 0x000904, 0x200A86, 0x0026F5,
+	0x0027F5, 0x030042, 0x09004A, 0x10000A,
+	0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+	0x010042, 0x51804A, 0x48000A, 0x160007,
+	0x001075, 0x010042, 0x282C0A, 0x281D12,
+	0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+	0x001975, 0x010042, 0x002DF5, 0x0D004A,
+	0x10000A, 0x009144, 0x20EA86, 0x010042,
+	0x28340A, 0x000E5D, 0x00008D, 0x000375,
+	0x000820, 0x010040, 0x05D2F4, 0x54D104,
+	0x00735C, 0x218B86, 0x000007, 0x0C0007,
+	0x080007, 0x0A0007, 0x02178D, 0x000810,
+	0x08043A, 0x34B206, 0x000007, 0x219206,
+	0x000007, 0x080007, 0x002275, 0x010042,
+	0x20000A, 0x002104, 0x225886, 0x001E2D,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x21CA86, 0x000007, 0x002010, 0x30043A,
+	0x00057D, 0x0180C3, 0x08000A, 0x028924,
+	0x280502, 0x280C02, 0x0A810D, 0x000820,
+	0x0002F5, 0x010040, 0x220007, 0x0004FD,
+	0x018042, 0x70000A, 0x030000, 0x007020,
+	0x07FA06, 0x018040, 0x022B8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x22C286,
+	0x000007, 0x020206, 0x000007, 0x000875,
+	0x0009FD, 0x00010D, 0x234206, 0x000295,
+	0x000B75, 0x00097D, 0x00000D, 0x000515,
+	0x010042, 0x18000A, 0x001904, 0x2A0086,
+	0x0006F5, 0x001020, 0x010040, 0x0004F5,
+	0x000820, 0x010040, 0x000775, 0x010042,
+	0x09804A, 0x10000A, 0x001124, 0x000904,
+	0x23F286, 0x000815, 0x080102, 0x101204,
+	0x241206, 0x000575, 0x081204, 0x000007,
+	0x100102, 0x000575, 0x000425, 0x021124,
+	0x100102, 0x000820, 0x031060, 0x010040,
+	0x001924, 0x2A0086, 0x00008D, 0x000464,
+	0x009D04, 0x291086, 0x180102, 0x000575,
+	0x010042, 0x28040A, 0x00018D, 0x000924,
+	0x280D02, 0x00000D, 0x000924, 0x281502,
+	0x10000D, 0x000820, 0x0002F5, 0x010040,
+	0x200007, 0x001175, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x24FA86, 0x000007,
+	0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+	0x030A60, 0x010040, 0x050042, 0x3D004A,
+	0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+	0x010042, 0x28140A, 0x0004F5, 0x010042,
+	0x08000A, 0x000315, 0x010D04, 0x260286,
+	0x004015, 0x000095, 0x010D04, 0x25F086,
+	0x100022, 0x10002A, 0x261A06, 0x000007,
+	0x333104, 0x2AA904, 0x000007, 0x032124,
+	0x280502, 0x284402, 0x001124, 0x400102,
+	0x000424, 0x000424, 0x003224, 0x00292C,
+	0x00636C, 0x277386, 0x000007, 0x02B164,
+	0x000464, 0x000464, 0x00008D, 0x000A64,
+	0x280D02, 0x10008D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x00008D, 0x38B904,
+	0x000007, 0x03296C, 0x30010A, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x270286,
+	0x000007, 0x00212C, 0x28050A, 0x00316C,
+	0x00046C, 0x00046C, 0x28450A, 0x001124,
+	0x006B64, 0x100102, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x004124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x27FA86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x284086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x284A86, 0x000007, 0x284402, 0x003124,
+	0x300502, 0x003924, 0x300583, 0x000883,
+	0x0005F5, 0x010042, 0x28040A, 0x00008D,
+	0x008124, 0x280D02, 0x00008D, 0x008124,
+	0x281502, 0x10018D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001025, 0x000575,
+	0x030042, 0x09004A, 0x10000A, 0x0A0904,
+	0x121104, 0x000007, 0x001020, 0x050860,
+	0x050040, 0x0006FD, 0x018042, 0x09004A,
+	0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+	0x000007, 0x000820, 0x019060, 0x010040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x29CA86, 0x000007, 0x244206, 0x000007,
+	0x000606, 0x000007, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x2A1A86, 0x000007,
+	0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+	0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+	0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+	0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+	0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+	0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+	0x038A60, 0x000606, 0x018040, 0x00008D,
+	0x000A64, 0x280D02, 0x000A24, 0x00027D,
+	0x018042, 0x10000A, 0x001224, 0x0003FD,
+	0x018042, 0x08000A, 0x000904, 0x2C0A86,
+	0x000007, 0x00018D, 0x000A24, 0x000464,
+	0x000464, 0x080102, 0x000924, 0x000424,
+	0x000424, 0x100102, 0x02000D, 0x009144,
+	0x2C6186, 0x000007, 0x0001FD, 0x018042,
+	0x08000A, 0x000A44, 0x2C4386, 0x018042,
+	0x0A000D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00027D, 0x001020, 0x000606,
+	0x018040, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x2CB286, 0x000007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2CE286,
+	0x000007, 0x000075, 0x002E7D, 0x010042,
+	0x0B804A, 0x000020, 0x000904, 0x000686,
+	0x010040, 0x31844A, 0x30048B, 0x000883,
+	0x00008D, 0x000810, 0x28143A, 0x00008D,
+	0x000810, 0x280C3A, 0x000675, 0x010042,
+	0x08000A, 0x003815, 0x010924, 0x280502,
+	0x0B000D, 0x000820, 0x0002F5, 0x010040,
+	0x000606, 0x220007, 0x000464, 0x000464,
+	0x000606, 0x000007, 0x000134, 0x007F8D,
+	0x00093C, 0x281D12, 0x282512, 0x001F32,
+	0x0E0007, 0x00010D, 0x00037D, 0x000820,
+	0x018040, 0x05D2F4, 0x000007, 0x080007,
+	0x00037D, 0x018042, 0x08000A, 0x000904,
+	0x2E8A86, 0x000007, 0x000606, 0x000007,
+	0x000007, 0x000012, 0x100007, 0x320007,
+	0x600007, 0x460007, 0x100080, 0x48001A,
+	0x004904, 0x2EF186, 0x000007, 0x001210,
+	0x58003A, 0x000145, 0x5C5D04, 0x000007,
+	0x000080, 0x48001A, 0x004904, 0x2F4186,
+	0x000007, 0x001210, 0x50003A, 0x005904,
+	0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+	0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+	0x200502, 0x000082, 0x40001A, 0x004104,
+	0x2FC986, 0x000007, 0x003865, 0x40001A,
+	0x004020, 0x00104D, 0x04C184, 0x31AB86,
+	0x000040, 0x040007, 0x000165, 0x000145,
+	0x004020, 0x000040, 0x000765, 0x080080,
+	0x40001A, 0x004104, 0x305986, 0x000007,
+	0x001210, 0x40003A, 0x004104, 0x30B286,
+	0x00004D, 0x0000CD, 0x004810, 0x20043A,
+	0x000882, 0x40001A, 0x004104, 0x30C186,
+	0x000007, 0x004820, 0x005904, 0x319886,
+	0x000040, 0x0007E5, 0x200480, 0x2816A0,
+	0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+	0x000040, 0x000032, 0x400075, 0x00007D,
+	0x07D574, 0x200512, 0x000082, 0x40001A,
+	0x004104, 0x317186, 0x000007, 0x038A06,
+	0x640007, 0x0000E5, 0x000020, 0x000040,
+	0x000A65, 0x000020, 0x020040, 0x020040,
+	0x000040, 0x000165, 0x000042, 0x70000A,
+	0x007104, 0x323286, 0x000007, 0x060007,
+	0x019A06, 0x640007, 0x050000, 0x007020,
+	0x000040, 0x038A06, 0x640007, 0x000007,
+	0x00306D, 0x028860, 0x029060, 0x08000A,
+	0x028860, 0x008040, 0x100012, 0x00100D,
+	0x009184, 0x32D186, 0x000E0D, 0x009184,
+	0x33E186, 0x000007, 0x300007, 0x001020,
+	0x003B6D, 0x008040, 0x000080, 0x08001A,
+	0x000904, 0x32F186, 0x000007, 0x001220,
+	0x000DED, 0x008040, 0x008042, 0x10000A,
+	0x40000D, 0x109544, 0x000007, 0x001020,
+	0x000DED, 0x008040, 0x008042, 0x20040A,
+	0x000082, 0x08001A, 0x000904, 0x338186,
+	0x000007, 0x003B6D, 0x008042, 0x08000A,
+	0x000E15, 0x010984, 0x342B86, 0x600007,
+	0x08001A, 0x000C15, 0x010984, 0x341386,
+	0x000020, 0x1A0007, 0x0002ED, 0x008040,
+	0x620007, 0x00306D, 0x028042, 0x0A804A,
+	0x000820, 0x0A804A, 0x000606, 0x10804A,
+	0x000007, 0x282512, 0x001F32, 0x05D2F4,
+	0x54D104, 0x00735C, 0x000786, 0x000007,
+	0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+	0x020040, 0x004820, 0x025060, 0x40000A,
+	0x024060, 0x000040, 0x454944, 0x000007,
+	0x004020, 0x003AE5, 0x000040, 0x0028E5,
+	0x000042, 0x48000A, 0x004904, 0x39F886,
+	0x002C65, 0x000042, 0x40000A, 0x0000D5,
+	0x454104, 0x000007, 0x000655, 0x054504,
+	0x368286, 0x0001D5, 0x054504, 0x368086,
+	0x002B65, 0x000042, 0x003AE5, 0x50004A,
+	0x40000A, 0x45C3D4, 0x000007, 0x454504,
+	0x000007, 0x0000CD, 0x444944, 0x000007,
+	0x454504, 0x000007, 0x00014D, 0x554944,
+	0x000007, 0x045144, 0x367986, 0x002C65,
+	0x000042, 0x48000A, 0x4CD104, 0x000007,
+	0x04C144, 0x368386, 0x000007, 0x160007,
+	0x002CE5, 0x040042, 0x40000A, 0x004020,
+	0x000040, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x36F086, 0x000007, 0x002402,
+	0x383206, 0x005C02, 0x0025E5, 0x000042,
+	0x40000A, 0x004274, 0x002AE5, 0x000042,
+	0x40000A, 0x004274, 0x500112, 0x0029E5,
+	0x000042, 0x40000A, 0x004234, 0x454104,
+	0x000007, 0x004020, 0x000040, 0x003EE5,
+	0x000020, 0x000040, 0x002DE5, 0x400152,
+	0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+	0x003EE5, 0x004020, 0x000040, 0x002BE5,
+	0x000042, 0x40000A, 0x404254, 0x000007,
+	0x002AE5, 0x004020, 0x000040, 0x500132,
+	0x040134, 0x005674, 0x0029E5, 0x020042,
+	0x42000A, 0x000042, 0x50000A, 0x05417C,
+	0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+	0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+	0x020042, 0x40004A, 0x50000A, 0x00423C,
+	0x00567C, 0x0028E5, 0x004820, 0x000040,
+	0x281D12, 0x282512, 0x001F72, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x393A86,
+	0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+	0x000042, 0x40000A, 0x004104, 0x397886,
+	0x002D65, 0x000042, 0x28340A, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+	0x39E186, 0x000007, 0x000606, 0x080007,
+	0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+	0x020045, 0x004020, 0x000060, 0x000365,
+	0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+	0x000040, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x000606, 0x50004A,
+	0x0017FD, 0x018042, 0x08000A, 0x000904,
+	0x225A86, 0x000007, 0x00107D, 0x018042,
+	0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+	0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+	0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+	0x018042, 0x0010FD, 0x09804A, 0x38000A,
+	0x000095, 0x010924, 0x003A64, 0x3B8186,
+	0x000007, 0x003904, 0x3B9286, 0x000007,
+	0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+	0x00387D, 0x018040, 0x700002, 0x00117D,
+	0x018042, 0x00197D, 0x29804A, 0x30000A,
+	0x380002, 0x003124, 0x000424, 0x000424,
+	0x002A24, 0x280502, 0x00068D, 0x000810,
+	0x28143A, 0x00750D, 0x00B124, 0x002264,
+	0x3D0386, 0x284402, 0x000810, 0x280C3A,
+	0x0B800D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00758D, 0x00B124, 0x100102,
+	0x012144, 0x3E4986, 0x001810, 0x10003A,
+	0x00387D, 0x018042, 0x08000A, 0x000904,
+	0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+	0x00008D, 0x023164, 0x000A64, 0x280D02,
+	0x0B808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00387D, 0x018042, 0x08000A,
+	0x000904, 0x3E3286, 0x030000, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x3D8286,
+	0x000007, 0x002810, 0x28043A, 0x00750D,
+	0x030924, 0x002264, 0x280D02, 0x02316C,
+	0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x00008D, 0x000A24,
+	0x3E4A06, 0x100102, 0x001810, 0x10003A,
+	0x0000BD, 0x003810, 0x30043A, 0x00187D,
+	0x018042, 0x0018FD, 0x09804A, 0x20000A,
+	0x0000AD, 0x028924, 0x07212C, 0x001010,
+	0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+	0x002083, 0x00137D, 0x038042, 0x33844A,
+	0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+	0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+	0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+	0x020924, 0x00068D, 0x00A96C, 0x00009D,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+	0x284402, 0x001810, 0x28143A, 0x0C008D,
+	0x000820, 0x0002FD, 0x018040, 0x220007,
+	0x003904, 0x225886, 0x001E0D, 0x00057D,
+	0x018042, 0x20000A, 0x020924, 0x0000A5,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x402A86, 0x000007, 0x280502, 0x280C02,
+	0x002010, 0x28143A, 0x0C010D, 0x000820,
+	0x0002FD, 0x018040, 0x225A06, 0x220007,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+#endif	//_HWMCODE_
diff -Nru linux/sound/pci/ymfpci/ymfpci_main.c linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_main.c
--- linux/sound/pci/ymfpci/ymfpci_main.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_main.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,2088 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of YMF724/740/744/754 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/ymfpci.h>
+#include <sound/asoundef.h>
+#include <sound/mpu401.h>
+
+#define chip_t ymfpci_t
+
+/*
+ *  constants
+ */
+
+/*
+ *  common I/O routines
+ */
+
+static void snd_ymfpci_irq_wait(ymfpci_t *chip);
+
+static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset)
+{
+	return readb(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val)
+{
+	writeb(val, chip->reg_area_virt + offset);
+}
+
+static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset)
+{
+	return readw(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val)
+{
+	writew(val, chip->reg_area_virt + offset);
+}
+
+static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset)
+{
+	return readl(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val)
+{
+	writel(val, chip->reg_area_virt + offset);
+}
+
+static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary, int sched)
+{
+	signed long end_time;
+	u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
+	
+	end_time = (jiffies + ((3 * HZ) / 4)) + 1;
+	do {
+		if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
+			return 0;
+		if (sched) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		}
+	} while (end_time - (signed long)jiffies >= 0);
+	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
+	return -EBUSY;
+}
+
+static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return);
+	u32 cmd;
+	
+	snd_ymfpci_codec_ready(chip, 0, 0);
+	cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
+	snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd);
+}
+
+static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return -ENXIO);
+
+	if (snd_ymfpci_codec_ready(chip, 0, 0))
+		return ~0;
+	snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);
+	if (snd_ymfpci_codec_ready(chip, 0, 0))
+		return ~0;
+	if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) {
+		int i;
+		for (i = 0; i < 600; i++)
+			snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
+	}
+	return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
+}
+
+/*
+ *  Misc routines
+ */
+
+static u32 snd_ymfpci_calc_delta(u32 rate)
+{
+	switch (rate) {
+	case 8000:	return 0x02aaab00;
+	case 11025:	return 0x03accd00;
+	case 16000:	return 0x05555500;
+	case 22050:	return 0x07599a00;
+	case 32000:	return 0x0aaaab00;
+	case 44100:	return 0x0eb33300;
+	default:	return ((rate << 16) / 375) << 5;
+	}
+}
+
+static u32 def_rate[8] = {
+	100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
+};
+
+static u32 snd_ymfpci_calc_lpfK(u32 rate)
+{
+	u32 i;
+	static u32 val[8] = {
+		0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
+		0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
+	};
+	
+	if (rate == 44100)
+		return 0x40000000;	/* FIXME: What's the right value? */
+	for (i = 0; i < 8; i++)
+		if (rate <= def_rate[i])
+			return val[i];
+	return val[0];
+}
+
+static u32 snd_ymfpci_calc_lpfQ(u32 rate)
+{
+	u32 i;
+	static u32 val[8] = {
+		0x35280000, 0x34A70000, 0x32020000, 0x31770000,
+		0x31390000, 0x31C90000, 0x33D00000, 0x40000000
+	};
+	
+	if (rate == 44100)
+		return 0x370A0000;
+	for (i = 0; i < 8; i++)
+		if (rate <= def_rate[i])
+			return val[i];
+	return val[0];
+}
+
+/*
+ *  Hardware start management
+ */
+
+static void snd_ymfpci_hw_start(ymfpci_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->start_count++ > 0)
+		goto __end;
+	snd_ymfpci_writel(chip, YDSXGR_MODE,
+			  snd_ymfpci_readl(chip, YDSXGR_MODE) | 3);
+	chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
+      __end:
+      	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_ymfpci_hw_stop(ymfpci_t *chip)
+{
+	unsigned long flags;
+	long timeout = 1000;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (--chip->start_count > 0)
+		goto __end;
+	snd_ymfpci_writel(chip, YDSXGR_MODE,
+			  snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3);
+	while (timeout-- > 0) {
+		if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0)
+			break;
+	}
+	if (atomic_read(&chip->interrupt_sleep_count)) {
+		atomic_set(&chip->interrupt_sleep_count, 0);
+		wake_up(&chip->interrupt_sleep);
+	}
+      __end:
+      	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ *  Playback voice management
+ */
+
+static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+	ymfpci_voice_t *voice, *voice2;
+	int idx;
+	
+	*rvoice = NULL;
+	for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) {
+		voice = &chip->voices[idx];
+		voice2 = pair ? &chip->voices[idx+1] : NULL;
+		if (voice->use || (voice2 && voice2->use))
+			continue;
+		voice->use = 1;
+		if (voice2)
+			voice2->use = 1;
+		switch (type) {
+		case YMFPCI_PCM:
+			voice->pcm = 1;
+			if (voice2)
+				voice2->pcm = 1;
+			break;
+		case YMFPCI_SYNTH:
+			voice->synth = 1;
+			break;
+		case YMFPCI_MIDI:
+			voice->midi = 1;
+			break;
+		}
+		snd_ymfpci_hw_start(chip);
+		if (voice2)
+			snd_ymfpci_hw_start(chip);
+		*rvoice = voice;
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+	unsigned long flags;
+	int result;
+	
+	snd_assert(rvoice != NULL, return -EINVAL);
+	snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);
+	
+	spin_lock_irqsave(&chip->voice_lock, flags);
+	for (;;) {
+		result = voice_alloc(chip, type, pair, rvoice);
+		if (result == 0 || type != YMFPCI_PCM)
+			break;
+		/* TODO: synth/midi voice deallocation */
+		break;
+	}
+	spin_unlock_irqrestore(&chip->voice_lock, flags);	
+	return result;		
+}
+
+int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice)
+{
+	unsigned long flags;
+	
+	snd_assert(pvoice != NULL, return -EINVAL);
+	snd_ymfpci_hw_stop(chip);
+	spin_lock_irqsave(&chip->voice_lock, flags);
+	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+	pvoice->ypcm = NULL;
+	pvoice->interrupt = NULL;
+	spin_unlock_irqrestore(&chip->voice_lock, flags);
+	return 0;
+}
+
+/*
+ *  PCM part
+ */
+
+static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice)
+{
+	ymfpci_pcm_t *ypcm;
+	u32 pos, delta;
+	
+	if ((ypcm = voice->ypcm) == NULL)
+		return;
+	if (ypcm->substream == NULL)
+		return;
+	spin_lock(&chip->reg_lock);
+	if (ypcm->running) {
+		pos = le32_to_cpu(voice->bank[chip->active_bank].start);
+		if (pos < ypcm->last_pos)
+			delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+		else
+			delta = pos - ypcm->last_pos;
+		ypcm->period_pos += delta;
+		ypcm->last_pos = pos;
+		while (ypcm->period_pos >= ypcm->period_size) {
+			// printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+			spin_unlock(&chip->reg_lock);
+			snd_pcm_period_elapsed(ypcm->substream);
+			spin_lock(&chip->reg_lock);
+			ypcm->period_pos -= ypcm->period_size;
+		}
+	}
+	spin_unlock(&chip->reg_lock);
+}
+
+static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return);
+	ymfpci_t *chip = ypcm->chip;
+	u32 pos, delta;
+	
+	spin_lock(&chip->reg_lock);
+	if (ypcm->running) {
+		pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
+		if (pos < ypcm->last_pos)
+			delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+		else
+			delta = pos - ypcm->last_pos;
+		ypcm->period_pos += delta;
+		ypcm->last_pos = pos;
+		while (ypcm->period_pos >= ypcm->period_size) {
+			ypcm->period_pos = 0;
+			// printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+			spin_unlock(&chip->reg_lock);
+			snd_pcm_period_elapsed(substream);
+			spin_lock(&chip->reg_lock);
+		}
+	}
+	spin_unlock(&chip->reg_lock);
+}
+
+static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO);
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	if (ypcm->voices[0] == NULL) {
+		result = -EINVAL;
+		goto __unlock;
+	}
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr);
+		if (ypcm->voices[1] != NULL)
+			chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr);
+		ypcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
+		if (ypcm->voices[1] != NULL)
+			chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
+		ypcm->running = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+      __unlock:
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO);
+	int result = 0;
+	u32 tmp;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
+		snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
+		ypcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
+		snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
+		ypcm->running = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
+{
+	int err;
+
+	if (ypcm->voices[1] != NULL && voices < 2) {
+		snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]);
+		ypcm->voices[1] = NULL;
+	}
+	if (voices == 1 && ypcm->voices[0] != NULL)
+		return 0;		/* already allocated */
+	if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)
+		return 0;		/* already allocated */
+	if (voices > 1) {
+		if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
+			snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]);
+			ypcm->voices[0] = NULL;
+		}		
+	}
+	err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
+	if (err < 0)
+		return err;
+	ypcm->voices[0]->ypcm = ypcm;
+	ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt;
+	if (voices > 1) {
+		ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1];
+		ypcm->voices[1]->ypcm = ypcm;
+	}
+	return 0;
+}
+
+static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
+				      int rate, int w_16, unsigned long addr,
+				      unsigned int end, int eff2)
+{
+	u32 format;
+	u32 delta = snd_ymfpci_calc_delta(rate);
+	u32 lpfQ = snd_ymfpci_calc_lpfQ(rate);
+	u32 lpfK = snd_ymfpci_calc_lpfK(rate);
+	snd_ymfpci_playback_bank_t *bank;
+	unsigned int nbank;
+
+	snd_assert(voice != NULL, return);
+	format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);
+	for (nbank = 0; nbank < 2; nbank++) {
+		bank = &voice->bank[nbank];
+		bank->format = cpu_to_le32(format);
+		bank->loop_default = 0;
+		bank->base = cpu_to_le32(addr);
+		bank->loop_start = 0;
+		bank->loop_end = cpu_to_le32(end);
+		bank->loop_frac = 0;
+		bank->eg_gain_end = cpu_to_le32(0x40000000);
+		bank->lpfQ = cpu_to_le32(lpfQ);
+		bank->status = 0;
+		bank->num_of_frames = 0;
+		bank->loop_count = 0;
+		bank->start = 0;
+		bank->start_frac = 0;
+		bank->delta =
+		bank->delta_end = cpu_to_le32(delta);
+		bank->lpfK =
+		bank->lpfK_end = cpu_to_le32(lpfK);
+		bank->eg_gain = cpu_to_le32(0x40000000);
+		bank->lpfD1 =
+		bank->lpfD2 = 0;
+
+		bank->left_gain = 
+		bank->right_gain =
+		bank->left_gain_end =
+		bank->right_gain_end =
+		bank->eff1_gain =
+		bank->eff2_gain =
+		bank->eff3_gain =
+		bank->eff1_gain_end =
+		bank->eff2_gain_end =
+		bank->eff3_gain_end = 0;
+
+		if (!stereo) {
+			if (!eff2) {
+				bank->left_gain = 
+				bank->right_gain =
+				bank->left_gain_end =
+				bank->right_gain_end = cpu_to_le32(0x40000000);
+			} else {
+				bank->eff2_gain =
+				bank->eff2_gain_end =
+				bank->eff3_gain =
+				bank->eff3_gain_end = cpu_to_le32(0x40000000);
+			}
+		} else {
+			if (!eff2) {
+				if ((voice->number & 1) == 0) {
+					bank->left_gain =
+					bank->left_gain_end = cpu_to_le32(0x40000000);
+				} else {
+					bank->format |= cpu_to_le32(1);
+					bank->right_gain =
+					bank->right_gain_end = cpu_to_le32(0x40000000);
+				}
+			} else {
+				if ((voice->number & 1) == 0) {
+					bank->eff3_gain =
+					bank->eff3_gain_end = cpu_to_le32(0x40000000);
+				} else {
+					bank->format |= cpu_to_le32(1);
+					bank->eff2_gain =
+					bank->eff2_gain_end = cpu_to_le32(0x40000000);
+				}
+			}
+		}
+	}
+}
+
+static int snd_ymfpci_ac3_init(ymfpci_t *chip)
+{
+	unsigned char *ptr;
+	dma_addr_t ptr_addr;
+
+	if (chip->ac3_tmp_base != NULL)
+		return -EBUSY;
+	if ((ptr = snd_malloc_pci_pages(chip->pci, 4096, &ptr_addr)) == NULL)
+		return -ENOMEM;
+
+	chip->ac3_tmp_base = ptr;
+	chip->ac3_tmp_base_addr = ptr_addr;
+	chip->bank_effect[3][0]->base =
+	chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr);
+	chip->bank_effect[3][0]->loop_end =
+	chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024);
+	chip->bank_effect[4][0]->base =
+	chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr + 2048);
+	chip->bank_effect[4][0]->loop_end =
+	chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024);
+
+	spin_lock_irq(&chip->reg_lock);
+	snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+			  snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3);
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_ymfpci_ac3_done(ymfpci_t *chip)
+{
+	spin_lock_irq(&chip->reg_lock);
+	snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+			  snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3));
+	spin_unlock_irq(&chip->reg_lock);
+	snd_ymfpci_irq_wait(chip);
+	if (chip->ac3_tmp_base) {
+		snd_free_pci_pages(chip->pci, 4096, chip->ac3_tmp_base, chip->ac3_tmp_base_addr);
+		chip->ac3_tmp_base = NULL;
+	}
+	return 0;
+}
+
+static int snd_ymfpci_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0)
+		return err;
+	if (ypcm->spdif || ypcm->mode4ch)
+		if ((err = snd_ymfpci_ac3_init(chip)) < 0)
+			return err;
+	return 0;
+}
+
+static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm;
+	
+	
+	if (runtime->private_data == NULL)
+		return 0;
+	ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+
+	/* wait, until the PCI operations are not finished */
+	snd_ymfpci_irq_wait(chip);
+	snd_pcm_lib_free_pages(substream);
+	if (ypcm->voices[1]) {
+		snd_ymfpci_voice_free(chip, ypcm->voices[1]);
+		ypcm->voices[1] = NULL;
+	}
+	if (ypcm->voices[0]) {
+		snd_ymfpci_voice_free(chip, ypcm->voices[0]);
+		ypcm->voices[0] = NULL;
+	}
+	if (ypcm->spdif || ypcm->mode4ch)
+		snd_ymfpci_ac3_done(chip);
+	return 0;
+}
+
+static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream)
+{
+	// ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+	int nvoice;
+
+	ypcm->period_size = runtime->period_size;
+	ypcm->buffer_size = runtime->buffer_size;
+	ypcm->period_pos = 0;
+	ypcm->last_pos = 0;
+	for (nvoice = 0; nvoice < runtime->channels; nvoice++)
+		snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice],
+					  runtime->channels == 2,
+					  runtime->rate,
+					  snd_pcm_format_width(runtime->format) == 16,
+					  runtime->dma_addr,
+					  ypcm->buffer_size,
+					  ypcm->spdif || ypcm->mode4ch);
+	return 0;
+}
+
+static int snd_ymfpci_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ymfpci_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+	/* wait, until the PCI operations are not finished */
+	snd_ymfpci_irq_wait(chip);
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ymfpci_capture_prepare(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+	snd_ymfpci_capture_bank_t * bank;
+	int nbank;
+	u32 rate, format;
+
+	ypcm->period_size = runtime->period_size;
+	ypcm->buffer_size = runtime->buffer_size;
+	ypcm->period_pos = 0;
+	ypcm->last_pos = 0;
+	ypcm->shift = 0;
+	rate = ((48000 * 4096) / runtime->rate) - 1;
+	format = 0;
+	if (runtime->channels == 2) {
+		format |= 2;
+		ypcm->shift++;
+	}
+	if (snd_pcm_format_width(runtime->format) == 8)
+		format |= 1;
+	else
+		ypcm->shift++;
+	switch (ypcm->capture_bank_number) {
+	case 0:
+		snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format);
+		snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate);
+		break;
+	case 1:
+		snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format);
+		snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate);
+		break;
+	}
+	for (nbank = 0; nbank < 2; nbank++) {
+		bank = chip->bank_capture[ypcm->capture_bank_number][nbank];
+		bank->base = cpu_to_le32(runtime->dma_addr);
+		bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift);
+		bank->start = 0;
+		bank->num_of_loops = 0;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ymfpci_playback_pointer(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+	ymfpci_voice_t *voice = ypcm->voices[0];
+
+	if (!(ypcm->running && voice))
+		return 0;
+	return le32_to_cpu(voice->bank[chip->active_bank].start);
+}
+
+static snd_pcm_uframes_t snd_ymfpci_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+
+	if (!ypcm->running)
+		return 0;
+	return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
+}
+
+static void snd_ymfpci_irq_wait(ymfpci_t *chip)
+{
+	wait_queue_t wait;
+	int loops = 4;
+
+	while (loops-- > 0) {
+		if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0)
+		 	continue;
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&chip->interrupt_sleep, &wait);
+		atomic_inc(&chip->interrupt_sleep_count);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/20);
+		remove_wait_queue(&chip->interrupt_sleep, &wait);
+	}
+}
+
+static void snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, dev_id, return);
+	u32 status, nvoice, mode;
+	ymfpci_voice_t *voice;
+
+	status = snd_ymfpci_readl(chip, YDSXGR_STATUS);
+	if (status & 0x80000000) {
+		chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
+		spin_lock(&chip->voice_lock);
+		for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
+			voice = &chip->voices[nvoice];
+			if (voice->interrupt)
+				voice->interrupt(chip, voice);
+		}
+		for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
+			if (chip->capture_substream[nvoice])
+				snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]);
+		}
+#if 0
+		for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) {
+			if (chip->effect_substream[nvoice])
+				snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]);
+		}
+#endif
+		spin_unlock(&chip->voice_lock);
+		spin_lock(&chip->reg_lock);
+		snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000);
+		mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2;
+		snd_ymfpci_writel(chip, YDSXGR_MODE, mode);
+		spin_unlock(&chip->reg_lock);
+
+		if (atomic_read(&chip->interrupt_sleep_count)) {
+			atomic_set(&chip->interrupt_sleep_count, 0);
+			wake_up(&chip->interrupt_sleep);
+		}
+	}
+
+	status = snd_ymfpci_readl(chip, YDSXGR_INTFLAG);
+	if (status & 1) {
+		/* timer handler */
+		snd_ymfpci_writel(chip, YDSXGR_INTFLAG, ~0);
+	}
+
+	if (chip->rawmidi)
+		snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs);
+}
+
+static snd_pcm_hardware_t snd_ymfpci_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	256 * 1024, /* FIXME: enough? */
+	period_bytes_min:	64,
+	period_bytes_max:	256 * 1024, /* FIXME: enough? */
+	periods_min:		3,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ymfpci_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_RESUME),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	256 * 1024, /* FIXME: enough? */
+	period_bytes_min:	64,
+	period_bytes_max:	256 * 1024, /* FIXME: enough? */
+	periods_min:		3,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return);
+	
+	if (ypcm)
+		snd_magic_kfree(ypcm);
+}
+
+static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm;
+
+	ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL);
+	if (ypcm == NULL)
+		return -ENOMEM;
+	ypcm->chip = chip;
+	ypcm->type = PLAYBACK_VOICE;
+	ypcm->substream = substream;
+	runtime->hw = snd_ymfpci_playback;
+	runtime->private_data = ypcm;
+	runtime->private_free = snd_ymfpci_pcm_free_substream;
+	/* FIXME? True value is 256/48 = 5.33333 ms */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+	return 0;
+}
+
+static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm;
+	unsigned long flags;
+	int err;
+	
+	if ((err = snd_ymfpci_playback_open(substream)) < 0)
+		return err;
+	ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0);
+	ypcm->spdif = 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+			  snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2);
+	snd_ymfpci_writel(chip, YDSXGR_MODE,
+			  snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30));
+	chip->spdif_pcm_bits = chip->spdif_bits;
+	snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+
+	/* FIXME? True value is 256/48 = 5.33333 ms */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+	return 0;
+}
+
+static int snd_ymfpci_playback_4ch_open(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm;
+	unsigned long flags;
+	int err;
+	
+	if ((err = snd_ymfpci_playback_open(substream)) < 0)
+		return err;
+	ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0);
+	ypcm->mode4ch = 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
+			  (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0030) | 0x0010);
+	snd_ymfpci_writel(chip, YDSXGR_MODE,
+			  snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* FIXME? True value is 256/48 = 5.33333 ms */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+	return 0;
+}
+
+static int snd_ymfpci_capture_open(snd_pcm_substream_t * substream,
+				   u32 capture_bank_number)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm;
+
+	ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL);
+	if (ypcm == NULL)
+		return -ENOMEM;
+	ypcm->chip = chip;
+	ypcm->type = capture_bank_number + CAPTURE_REC;
+	ypcm->substream = substream;	
+	ypcm->capture_bank_number = capture_bank_number;
+	chip->capture_substream[capture_bank_number] = substream;
+	runtime->hw = snd_ymfpci_capture;
+	/* FIXME? True value is 256/48 = 5.33333 ms */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+	runtime->private_data = ypcm;
+	runtime->private_free = snd_ymfpci_pcm_free_substream;
+	snd_ymfpci_hw_start(chip);
+	return 0;
+}
+
+static int snd_ymfpci_capture_rec_open(snd_pcm_substream_t * substream)
+{
+	return snd_ymfpci_capture_open(substream, 0);
+}
+
+static int snd_ymfpci_capture_ac97_open(snd_pcm_substream_t * substream)
+{
+	return snd_ymfpci_capture_open(substream, 1);
+}
+
+static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream)
+{
+	return 0;
+}
+
+static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ymfpci_writel(chip, YDSXGR_MODE,
+			  snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30));
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+			  snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	chip->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+	return snd_ymfpci_playback_close(substream);
+}
+
+static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ymfpci_writel(chip, YDSXGR_MODE,
+			  snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30));
+	snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
+			  (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return snd_ymfpci_playback_close(substream);
+}
+
+static int snd_ymfpci_capture_close(snd_pcm_substream_t * substream)
+{
+	ymfpci_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO);
+
+	if (ypcm != NULL) {
+		chip->capture_substream[ypcm->capture_bank_number] = NULL;
+		snd_ymfpci_hw_stop(chip);
+	}
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_ops = {
+	open:			snd_ymfpci_playback_open,
+	close:			snd_ymfpci_playback_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_ymfpci_playback_hw_params,
+	hw_free:		snd_ymfpci_playback_hw_free,
+	prepare:		snd_ymfpci_playback_prepare,
+	trigger:		snd_ymfpci_playback_trigger,
+	pointer:		snd_ymfpci_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = {
+	open:			snd_ymfpci_capture_rec_open,
+	close:			snd_ymfpci_capture_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_ymfpci_capture_hw_params,
+	hw_free:		snd_ymfpci_capture_hw_free,
+	prepare:		snd_ymfpci_capture_prepare,
+	trigger:		snd_ymfpci_capture_trigger,
+	pointer:		snd_ymfpci_capture_pointer,
+};
+
+static void snd_ymfpci_pcm_free(snd_pcm_t *pcm)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = {
+	open:			snd_ymfpci_capture_ac97_open,
+	close:			snd_ymfpci_capture_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_ymfpci_capture_hw_params,
+	hw_free:		snd_ymfpci_capture_hw_free,
+	prepare:		snd_ymfpci_capture_prepare,
+	trigger:		snd_ymfpci_capture_trigger,
+	pointer:		snd_ymfpci_capture_pointer,
+};
+
+static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return);
+	chip->pcm2 = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI - AC'97", device, 0, 1, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm2_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI - AC'97");
+	chip->pcm2 = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = {
+	open:			snd_ymfpci_playback_spdif_open,
+	close:			snd_ymfpci_playback_spdif_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_ymfpci_playback_hw_params,
+	hw_free:		snd_ymfpci_playback_hw_free,
+	prepare:		snd_ymfpci_playback_prepare,
+	trigger:		snd_ymfpci_playback_trigger,
+	pointer:		snd_ymfpci_playback_pointer,
+};
+
+static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return);
+	chip->pcm_spdif = NULL;
+}
+
+int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm_spdif_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI - IEC958");
+	chip->pcm_spdif = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = {
+	open:			snd_ymfpci_playback_4ch_open,
+	close:			snd_ymfpci_playback_4ch_close,
+	ioctl:			snd_pcm_lib_ioctl,
+	hw_params:		snd_ymfpci_playback_hw_params,
+	hw_free:		snd_ymfpci_playback_hw_free,
+	prepare:		snd_ymfpci_playback_prepare,
+	trigger:		snd_ymfpci_playback_trigger,
+	pointer:		snd_ymfpci_playback_pointer,
+};
+
+static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return);
+	chip->pcm_4ch = NULL;
+}
+
+int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm_4ch_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI - Rear PCM");
+	chip->pcm_4ch = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static int snd_ymfpci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ymfpci_spdif_default_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ymfpci_spdif_default_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
+	      (ucontrol->value.iec958.status[1] << 8);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	change = chip->spdif_bits != val;
+	chip->spdif_bits = val;
+	if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL)
+		snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_spdif_default __devinitdata =
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_ymfpci_spdif_default_info,
+	get:		snd_ymfpci_spdif_default_get,
+	put:		snd_ymfpci_spdif_default_put
+};
+
+static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol,
+				      snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = 0x3e;
+	ucontrol->value.iec958.status[1] = 0xff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_ymfpci_spdif_mask_info,
+	get:		snd_ymfpci_spdif_mask_get,
+};
+
+static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ymfpci_spdif_stream_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
+	      (ucontrol->value.iec958.status[1] << 8);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	change = chip->spdif_pcm_bits != val;
+	chip->spdif_pcm_bits = val;
+	if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2))
+		snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata =
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_ymfpci_spdif_stream_info,
+	get:		snd_ymfpci_spdif_stream_get,
+	put:		snd_ymfpci_spdif_stream_put
+};
+
+/*
+ *  Mixer controls
+ */
+
+#define YMFPCI_SINGLE(xname, xindex, reg) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ymfpci_info_single, \
+  get: snd_ymfpci_get_single, put: snd_ymfpci_put_single, \
+  private_value: reg }
+
+static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	unsigned int mask = 1;
+
+	switch (kcontrol->private_value) {
+	case YDSXGR_SPDIFOUTCTRL: break;
+	case YDSXGR_SPDIFINCTRL: break;
+	default: return -EINVAL;
+	}
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	unsigned int shift = 0, mask = 1, invert = 0;
+	
+	switch (kcontrol->private_value) {
+	case YDSXGR_SPDIFOUTCTRL: break;
+	case YDSXGR_SPDIFINCTRL: break;
+	default: return -EINVAL;
+	}
+	ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value;
+	unsigned int shift = 0, mask = 1, invert = 0;
+	int change;
+	unsigned int val, oval;
+	
+	switch (kcontrol->private_value) {
+	case YDSXGR_SPDIFOUTCTRL: break;
+	case YDSXGR_SPDIFINCTRL: break;
+	default: return -EINVAL;
+	}
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_ymfpci_readl(chip, reg);
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	snd_ymfpci_writel(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define YMFPCI_DOUBLE(xname, xindex, reg) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ymfpci_info_double, \
+  get: snd_ymfpci_get_double, put: snd_ymfpci_put_double, \
+  private_value: reg }
+
+static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	unsigned int reg = kcontrol->private_value;
+	unsigned int mask = 16383;
+
+	if (reg < 0x80 || reg >= 0xc0)
+		return -EINVAL;
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int reg = kcontrol->private_value;
+	unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+	unsigned int val;
+	
+	if (reg < 0x80 || reg >= 0xc0)
+		return -EINVAL;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = snd_ymfpci_readl(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int reg = kcontrol->private_value;
+	unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+	int change;
+	unsigned int val1, val2, oval;
+	
+	if (reg < 0x80 || reg >= 0xc0)
+		return -EINVAL;
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_ymfpci_readl(chip, reg);
+	val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+	change = val1 != oval;
+	snd_ymfpci_writel(chip, reg, val1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define YMFPCI_CONTROLS (sizeof(snd_ymfpci_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ymfpci_controls[] __devinitdata = {
+YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL),
+YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL),
+YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL),
+YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL),
+YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL),
+YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL),
+YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL),
+YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL)
+};
+
+/*
+ *  Mixer routines
+ */
+
+static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+}
+
+int __devinit snd_ymfpci_mixer(ymfpci_t *chip)
+{
+	ac97_t ac97;
+	snd_kcontrol_t *kctl;
+	int err, idx;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_ymfpci_codec_write;
+	ac97.read = snd_ymfpci_codec_read;
+	ac97.private_data = chip;
+	ac97.private_free = snd_ymfpci_mixer_free_ac97;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+
+	for (idx = 0; idx < YMFPCI_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0)
+			return err;
+	}
+
+	/* add S/PDIF control */
+	snd_assert(chip->pcm_spdif != NULL, return -EIO);
+	if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)
+		return err;
+	kctl->id.device = chip->pcm_spdif->device;
+	if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0)
+		return err;
+	kctl->id.device = chip->pcm_spdif->device;
+	if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0)
+		return err;
+	kctl->id.device = chip->pcm_spdif->device;
+	chip->spdif_pcm_ctl = kctl;
+
+	return 0;
+}
+
+
+/*
+ * joystick support
+ */
+
+static int ymfpci_joystick_ports[4] = {
+	0x201, 0x202, 0x204, 0x205
+};
+
+static void setup_joystick_base(ymfpci_t *chip)
+{
+	if (chip->pci->device >= 0x0010) /* YMF 744/754 */
+		pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE,
+				      ymfpci_joystick_ports[chip->joystick_port]);
+	else {
+		u16 legacy_ctrl2;
+		pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY, &legacy_ctrl2);
+		legacy_ctrl2 &= ~(3 << 6);
+		legacy_ctrl2 |= chip->joystick_port << 6;
+		pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
+	}
+}
+
+static int snd_ymfpci_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ymfpci_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val;
+
+	pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &val);
+	ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0;
+	return 0;
+}
+
+static int snd_ymfpci_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val, oval;
+
+	pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &oval);
+	val = oval & ~0x04;
+	if (ucontrol->value.integer.value[0])
+		val |= 0x04;
+	if (val != oval) {
+		setup_joystick_base(chip);
+		pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, val);
+		return 1;
+	}
+	return 0;
+}
+
+static int snd_ymfpci_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+        uinfo->count = 1;
+        uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= 4)
+		uinfo->value.enumerated.item = 3;
+	sprintf(uinfo->value.enumerated.name, "port 0x%x", ymfpci_joystick_ports[uinfo->value.enumerated.item]);
+        return 0;
+}
+
+static int snd_ymfpci_joystick_addr_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = chip->joystick_port;
+	return 0;
+}
+
+static int snd_ymfpci_joystick_addr_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	if (ucontrol->value.integer.value[0] != chip->joystick_port) {
+		snd_assert(ucontrol->value.integer.value[0] >= 0 && ucontrol->value.integer.value[0] < 4, return -EINVAL);
+		chip->joystick_port = ucontrol->value.integer.value[0];
+		setup_joystick_base(chip);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_control_joystick __devinitdata = {
+	name: "Joystick",
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	info: snd_ymfpci_joystick_info,
+	get: snd_ymfpci_joystick_get,
+	put: snd_ymfpci_joystick_put,
+};
+
+static snd_kcontrol_new_t snd_ymfpci_control_joystick_addr __devinitdata = {
+	name: "Joystick Address",
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	info: snd_ymfpci_joystick_addr_info,
+	get: snd_ymfpci_joystick_addr_get,
+	put: snd_ymfpci_joystick_addr_put,
+};
+
+int __devinit snd_ymfpci_joystick(ymfpci_t *chip)
+{
+	int err;
+
+	chip->joystick_port = 0; /* default */
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick, chip))) < 0)
+		return err;
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick_addr, chip))) < 0)
+		return err;
+	return 0;
+}
+
+
+/*
+ *  proc interface
+ */
+
+static void snd_ymfpci_proc_read(snd_info_entry_t *entry, 
+				 snd_info_buffer_t * buffer)
+{
+	// ymfpci_t *chip = snd_magic_cast(ymfpci_t, private_data, return);
+	
+	snd_iprintf(buffer, "YMFPCI\n\n");
+}
+
+static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip)
+{
+	snd_info_entry_t *entry;
+	
+	entry = snd_info_create_card_entry(card, "ymfpci", card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 4096;
+		entry->c.text.read = snd_ymfpci_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_unregister(entry);
+			entry = NULL;
+		}
+	}
+	chip->proc_entry = entry;
+	return 0;
+}
+
+static int snd_ymfpci_proc_done(ymfpci_t *chip)
+{
+	if (chip->proc_entry)
+		snd_info_unregister((snd_info_entry_t *) chip->proc_entry);
+	return 0;
+}
+
+/*
+ *  initialization routines
+ */
+
+static void snd_ymfpci_aclink_reset(struct pci_dev * pci)
+{
+	u8 cmd;
+
+	pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd);
+#if 0 // force to reset
+	if (cmd & 0x03) {
+#endif
+		pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
+		pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03);
+		pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
+		pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0);
+		pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0);
+#if 0
+	}
+#endif
+}
+
+static void snd_ymfpci_enable_dsp(ymfpci_t *chip)
+{
+	snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001);
+}
+
+static void snd_ymfpci_disable_dsp(ymfpci_t *chip)
+{
+	u32 val;
+	int timeout = 1000;
+
+	val = snd_ymfpci_readl(chip, YDSXGR_CONFIG);
+	if (val)
+		snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000);
+	while (timeout-- > 0) {
+		val = snd_ymfpci_readl(chip, YDSXGR_STATUS);
+		if ((val & 0x00000002) == 0)
+			break;
+	}
+}
+
+#include "ymfpci_image.h"
+
+static void snd_ymfpci_download_image(ymfpci_t *chip)
+{
+	int i;
+	u16 ctrl;
+	unsigned long *inst;
+
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
+	snd_ymfpci_disable_dsp(chip);
+	snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000);
+	snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000);
+	snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000);
+	snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000);
+	snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000);
+	snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000);
+	snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000);
+	ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+	snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+
+	/* setup DSP instruction code */
+	for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
+		snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]);
+
+	/* setup control instruction code */
+	switch (chip->device_id) {
+	case PCI_DEVICE_ID_YAMAHA_724F:
+	case PCI_DEVICE_ID_YAMAHA_740C:
+	case PCI_DEVICE_ID_YAMAHA_744:
+	case PCI_DEVICE_ID_YAMAHA_754:
+		inst = CntrlInst1E;
+		break;
+	default:
+		inst = CntrlInst;
+		break;
+	}
+	for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
+		snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
+
+	snd_ymfpci_enable_dsp(chip);
+}
+
+static int __devinit snd_ymfpci_memalloc(ymfpci_t *chip)
+{
+	long size, playback_ctrl_size;
+	int voice, bank, reg;
+	u8 *ptr;
+	dma_addr_t ptr_addr;
+
+	playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES;
+	chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2;
+	chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2;
+	chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2;
+	chip->work_size = YDSXG_DEFAULT_WORK_SIZE;
+	
+	size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) +
+	       ((chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0x00ff) & ~0x00ff) +
+	       ((chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0x00ff) & ~0x00ff) +
+	       ((chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0x00ff) & ~0x00ff) +
+	       chip->work_size;
+	/* work_ptr must be aligned to 256 bytes, but it's already
+	   covered with the kernel page allocation mechanism */
+	if ((ptr = snd_malloc_pci_pages(chip->pci, size, &ptr_addr)) == NULL)
+		return -ENOMEM;
+	memset(ptr, 0, size);	/* for sure */
+	chip->work_ptr = ptr;
+	chip->work_ptr_addr = ptr_addr;
+	chip->work_ptr_size = size;
+
+	chip->bank_base_playback = ptr;
+	chip->bank_base_playback_addr = ptr_addr;
+	chip->ctrl_playback = (u32 *)ptr;
+	chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
+	ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+	ptr_addr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+	for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
+		chip->voices[voice].number = voice;
+		chip->voices[voice].bank = (snd_ymfpci_playback_bank_t *)ptr;
+		chip->voices[voice].bank_addr = ptr_addr;
+		for (bank = 0; bank < 2; bank++) {
+			chip->bank_playback[voice][bank] = (snd_ymfpci_playback_bank_t *)ptr;
+			ptr += chip->bank_size_playback;
+			ptr_addr += chip->bank_size_playback;
+		}
+	}
+	ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+	ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+	chip->bank_base_capture = ptr;
+	chip->bank_base_capture_addr = ptr_addr;
+	for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			chip->bank_capture[voice][bank] = (snd_ymfpci_capture_bank_t *)ptr;
+			ptr += chip->bank_size_capture;
+			ptr_addr += chip->bank_size_capture;
+		}
+	ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+	ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+	chip->bank_base_effect = ptr;
+	chip->bank_base_effect_addr = ptr_addr;
+	for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			chip->bank_effect[voice][bank] = (snd_ymfpci_effect_bank_t *)ptr;
+			ptr += chip->bank_size_effect;
+			ptr_addr += chip->bank_size_effect;
+		}
+	ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+	ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+	chip->work_base = ptr;
+	chip->work_base_addr = ptr_addr;
+	
+	snd_assert(ptr + chip->work_size == chip->work_ptr + chip->work_ptr_size, );
+
+	snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);
+	snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr);
+	snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr);
+	snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr);
+	snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2);
+
+	/* S/PDIF output initialization */
+	chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff;
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0);
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+
+	/* S/PDIF input initialization */
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0);
+
+	/* digital mixer setup */
+	for (reg = 0x80; reg < 0xc0; reg += 4)
+		snd_ymfpci_writel(chip, reg, 0);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff);
+	
+	return 0;
+}
+
+static int snd_ymfpci_free(ymfpci_t *chip)
+{
+	u16 ctrl;
+
+	snd_assert(chip != NULL, return -EINVAL);
+	snd_ymfpci_proc_done(chip);
+
+	if (chip->res_reg_area) {	/* don't touch busy hardware */
+		snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+		snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
+		snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0);
+		snd_ymfpci_disable_dsp(chip);
+		snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0);
+		ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+		snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+	}
+
+	/* Set PCI device to D3 state */
+	// pci_set_power_state(chip->pci, 3);
+
+#ifdef CONFIG_PM
+	if (chip->saved_regs)
+		kfree(chip->saved_regs);
+#endif
+	if (chip->reg_area_virt)
+		iounmap((void *)chip->reg_area_virt);
+	if (chip->work_ptr)
+		snd_free_pci_pages(chip->pci, chip->work_ptr_size, chip->work_ptr, chip->work_ptr_addr);
+	
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	if (chip->res_reg_area) {
+		release_resource(chip->res_reg_area);
+		kfree_nocheck(chip->res_reg_area);
+	}
+
+	pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
+	
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_ymfpci_dev_free(snd_device_t *device)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, device->device_data, return -ENXIO);
+	return snd_ymfpci_free(chip);
+}
+
+#ifdef CONFIG_PM
+static int saved_regs_index[] = {
+	/* spdif */
+	YDSXGR_SPDIFOUTCTRL,
+	YDSXGR_SPDIFOUTSTATUS,
+	YDSXGR_SPDIFINCTRL,
+	/* volumes */
+	YDSXGR_PRIADCLOOPVOL,
+	YDSXGR_NATIVEDACINVOL,
+	YDSXGR_NATIVEDACOUTVOL,
+	// YDSXGR_BUF441OUTVOL,
+	YDSXGR_NATIVEADCINVOL,
+	YDSXGR_SPDIFLOOPVOL,
+	YDSXGR_SPDIFOUTVOL,
+	YDSXGR_ZVOUTVOL,
+	/* address bases */
+	YDSXGR_PLAYCTRLBASE,
+	YDSXGR_RECCTRLBASE,
+	YDSXGR_EFFCTRLBASE,
+	YDSXGR_WORKBASE,
+	/* capture set up */
+	YDSXGR_MAPOFREC,
+	YDSXGR_RECFORMAT,
+	YDSXGR_RECSLOTSR,
+	YDSXGR_ADCFORMAT,
+	YDSXGR_ADCSLOTSR,
+};
+#define YDSXGR_NUM_SAVED_REGS	(sizeof(saved_regs_index)/sizeof(saved_regs_index[0]))
+
+void snd_ymfpci_suspend(ymfpci_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int i;
+	
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+	snd_pcm_suspend_all(chip->pcm);
+	snd_pcm_suspend_all(chip->pcm2);
+	snd_pcm_suspend_all(chip->pcm_spdif);
+	snd_pcm_suspend_all(chip->pcm_4ch);
+	for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
+		chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
+	chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+	snd_ymfpci_disable_dsp(chip);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+void snd_ymfpci_resume(ymfpci_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int i;
+
+	snd_power_lock(card);
+
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	pci_enable_device(chip->pci);
+	pci_set_master(chip->pci);
+	snd_ymfpci_aclink_reset(chip->pci);
+	snd_ymfpci_codec_ready(chip, 0, 0);
+	snd_ymfpci_download_image(chip);
+	udelay(100);
+
+	for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
+		snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]);
+
+	snd_ac97_resume(chip->ac97);
+
+	/* start hw again */
+	if (chip->start_count > 0) {
+		spin_lock(&chip->reg_lock);
+		snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode);
+		chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT);
+		spin_unlock(&chip->reg_lock);
+	}
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static int snd_ymfpci_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	ymfpci_t *chip = snd_magic_cast(ymfpci_t, card->power_state_private_data, return -ENXIO);
+
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_ymfpci_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_ymfpci_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+int __devinit snd_ymfpci_create(snd_card_t * card,
+				struct pci_dev * pci,
+				unsigned short old_legacy_ctrl,
+				ymfpci_t ** rchip)
+{
+	ymfpci_t *chip;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_ymfpci_dev_free,
+	};
+	
+	*rchip = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = snd_magic_kcalloc(ymfpci_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->old_legacy_ctrl = old_legacy_ctrl;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->voice_lock);
+	init_waitqueue_head(&chip->interrupt_sleep);
+	atomic_set(&chip->interrupt_sleep_count, 0);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->device_id = pci->device;
+	pci_read_config_byte(pci, PCI_REVISION_ID, (u8 *)&chip->rev);
+	chip->reg_area_phys = pci_resource_start(pci, 0);
+	chip->reg_area_virt = (unsigned long)ioremap_nocache(chip->reg_area_phys, 0x8000);
+	pci_set_master(pci);
+
+	if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
+		snd_ymfpci_free(chip);
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) {
+		snd_ymfpci_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+
+	snd_ymfpci_aclink_reset(pci);
+	if (snd_ymfpci_codec_ready(chip, 0, 1) < 0) {
+		snd_ymfpci_free(chip);
+		return -EIO;
+	}
+
+	snd_ymfpci_download_image(chip);
+
+	udelay(100); /* seems we need a delay after downloading image.. */
+
+	if (snd_ymfpci_memalloc(chip) < 0) {
+		snd_ymfpci_free(chip);
+		return -EIO;
+	}
+
+#ifdef CONFIG_PM
+	chip->saved_regs = kmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32), GFP_KERNEL);
+	if (chip->saved_regs == NULL) {
+		snd_ymfpci_free(chip);
+		return -ENOMEM;
+	}
+	card->set_power_state = snd_ymfpci_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	snd_ymfpci_proc_init(card, chip);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_ymfpci_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
diff -Nru linux/sound/ppc/Config.in linux-2.4.19-pre5-mjc/sound/ppc/Config.in
--- linux/sound/ppc/Config.in	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/Config.in	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,8 @@
+# ALSA PowerMac drivers
+
+mainmenu_option next_comment
+comment 'ALSA PowerMac devices'
+
+dep_tristate 'PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)' CONFIG_SND_POWERMAC $CONFIG_SND
+
+endmenu
diff -Nru linux/sound/ppc/Makefile linux-2.4.19-pre5-mjc/sound/ppc/Makefile
--- linux/sound/ppc/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,18 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := ppc.o
+
+list-multi   := snd-powermac.o
+
+snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
+
+include $(TOPDIR)/Rules.make
+
+snd-powermac.o: $(snd-powermac-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-powermac-objs)
diff -Nru linux/sound/ppc/awacs.c linux-2.4.19-pre5-mjc/sound/ppc/awacs.c
--- linux/sound/ppc/awacs.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/awacs.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,420 @@
+/*
+ * PMac AWACS lowlevel functions
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ * code based on dmasound.c.
+ *
+ *   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
+ */
+
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "pmac.h"
+
+#define chip_t pmac_t
+
+
+/*
+ * write AWACS register
+ */
+static void
+snd_pmac_awacs_write(pmac_t *chip, int val)
+{
+	long timeout = 5000000;
+
+	if (chip->model <= PMAC_SCREAMER)
+		return;
+
+	while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
+		if (! --timeout) {
+			snd_printd("snd_pmac_awacs_write timeout\n");
+			break;
+		}
+	}
+	out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
+}
+
+static void
+snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val)
+{
+	snd_pmac_awacs_write(chip, val | (reg << 12));
+	chip->awacs_reg[reg] = val;
+}
+
+static void
+snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val)
+{
+	snd_pmac_awacs_write(chip, val | (reg << 12));
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static void
+screamer_recalibrate(pmac_t *chip)
+{
+	/* Sorry for the horrible delays... I hope to get that improved
+	 * by making the whole PM process asynchronous in a future version
+	 */
+	mdelay(750);
+	snd_pmac_awacs_write_noreg(chip, 1,
+				   chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE);
+	mdelay(1000);
+	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
+}
+#endif
+
+
+/*
+ * additional callback to set the pcm format
+ */
+static void snd_pmac_awacs_set_format(pmac_t *chip)
+{
+	chip->awacs_reg[1] &= ~MASK_SAMPLERATE;
+	chip->awacs_reg[1] |= chip->rate_index << 3;
+	snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
+}
+
+
+#ifdef PMAC_AMP_AVAIL
+/* Turn on sound output, needed on G3 desktop powermacs */
+/* vol = 0 - 31, stereo */
+static void
+snd_pmac_awacs_enable_amp(pmac_t *chip, int lvol, int rvol)
+{
+	struct adb_request req;
+
+	if (! chip->amp_only)
+		return;
+
+	/* turn on headphones */
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 4, 0);
+	while (!req.complete) cuda_poll();
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 6, 0);
+	while (!req.complete) cuda_poll();
+
+	/* turn on speaker */
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 3, lvol & 0xff);
+	while (!req.complete) cuda_poll();
+	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
+		     0x8a, 5, rvol & 0xff);
+	while (!req.complete) cuda_poll();
+
+	cuda_request(&req, NULL, 5, CUDA_PACKET,
+		     CUDA_GET_SET_IIC, 0x8a, 1, 0x29);
+	while (!req.complete) cuda_poll();
+
+	/* update */
+	chip->amp_vol[0] = lvol;
+	chip->amp_vol[1] = rvol;
+}
+#endif
+
+/*
+ * AWACS volume callbacks
+ */
+/*
+ * volumes: 0-15 stereo
+ */
+static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 15;
+	return 0;
+}
+ 
+static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int n = kcontrol->private_value & 0xff;
+	int lshift = (kcontrol->private_value >> 8) & 0xff;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = 0x0f - ((chip->awacs_reg[n] >> lshift) & 0xf);
+	ucontrol->value.integer.value[1] = 0x0f - (chip->awacs_reg[n] & 0xf);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int n = kcontrol->private_value & 0xff;
+	int lshift = (kcontrol->private_value >> 8) & 0xff;
+	int rn, oldval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oldval = chip->awacs_reg[n];
+	rn = oldval & ~(0xf | (0xf << lshift));
+	rn |= ((0x0f - (ucontrol->value.integer.value[0] & 0xf)) << lshift);
+	rn |= 0x0f - (ucontrol->value.integer.value[1] & 0xf);
+	snd_pmac_awacs_write_reg(chip, n, rn);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return oldval != rn;
+}
+
+
+#define AWACS_VOLUME(xname, xreg, xshift) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \
+  info: snd_pmac_awacs_info_volume, \
+  get: snd_pmac_awacs_get_volume, \
+  put: snd_pmac_awacs_put_volume, \
+  private_value: (xreg) | (xshift << 8) }
+
+/*
+ * mute master/ogain for AWACS: mono
+ */
+static int snd_pmac_awacs_info_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+ 
+static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int mask = kcontrol->private_value & 0xff;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->awacs_reg[1] & mask) ? 0 : 1;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int mask = kcontrol->private_value & 0xff;
+	int val, changed;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = chip->awacs_reg[1] & ~mask;
+	if (! ucontrol->value.integer.value[0])
+		val |= mask;
+	snd_pmac_awacs_write_reg(chip, 1, val);
+	changed = chip->awacs_reg[1] != val;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return changed;
+}
+
+#define AWACS_SWITCH(xname, xreg, xmask) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \
+  info: snd_pmac_awacs_info_switch, \
+  get: snd_pmac_awacs_get_switch, \
+  put: snd_pmac_awacs_put_switch, \
+  private_value: (xreg) | ((xmask) << 8) }
+
+
+#ifdef PMAC_AMP_AVAIL
+/*
+ * Master volume for awacs revision 3
+ */
+static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+ 
+static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = chip->amp_vol[0];
+	ucontrol->value.integer.value[1] = chip->amp_vol[1];
+	return 0;
+}
+
+static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int changed = ucontrol->value.integer.value[0] != chip->amp_vol[0] ||
+		ucontrol->value.integer.value[1] != chip->amp_vol[1];
+	snd_pmac_awacs_enable_amp(chip,
+				  ucontrol->value.integer.value[0],
+				  ucontrol->value.integer.value[1]);
+	return changed;
+}
+#endif /* PMAC_AMP_AVAIL */
+
+
+/*
+ * lists of mixer elements
+ */
+static snd_kcontrol_new_t snd_pmac_awacs_mixers1[] = {
+	AWACS_VOLUME("Master Playback Volume", 2, 6),
+	AWACS_SWITCH("Master Playback Switch", 1, MASK_AMUTE),
+	AWACS_SWITCH("Master Capture Switch", 1, MASK_LOOPTHRU),
+};
+
+static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] = {
+	AWACS_VOLUME("Capture Volume", 0, 4),
+	AWACS_SWITCH("Line Capture Switch", 0, MASK_MUX_AUDIN),
+	AWACS_SWITCH("CD Capture Switch", 0, MASK_MUX_CD),
+	AWACS_SWITCH("Mic Capture Switch", 0, MASK_MUX_MIC),
+	AWACS_SWITCH("Mic Boost", 0, MASK_GAINLINE),
+};
+
+static snd_kcontrol_new_t snd_pmac_awacs_speaker_mixers[] = {
+	AWACS_VOLUME("PC Speaker Playback Volume", 4, 6),
+	AWACS_SWITCH("PC Speaker Playback Switch", 1, MASK_CMUTE),
+};
+
+#ifdef PMAC_AMP_AVAIL
+static snd_kcontrol_new_t snd_pmac_awacs_amp_mixers[] = {
+	{
+		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+		name: "PC Speaker Playback Volume", index: 0, 
+		info: snd_pmac_awacs_info_volume_amp, \
+		get: snd_pmac_awacs_get_volume_amp,
+		put: snd_pmac_awacs_put_volume_amp,
+	},
+};
+#endif
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+/*
+ * add new mixer elements to the card
+ */
+static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers)
+{
+	int i, err;
+
+	for (i = 0; i < nums; i++) {
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static void snd_pmac_awacs_resume(pmac_t *chip)
+{
+	snd_pmac_awacs_write_reg(chip, 0, chip->awacs_reg[0]);
+	snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
+	snd_pmac_awacs_write_reg(chip, 2, chip->awacs_reg[2]);
+	snd_pmac_awacs_write_reg(chip, 4, chip->awacs_reg[4]);
+	if (chip->model == PMAC_SCREAMER) {
+		snd_pmac_awacs_write_reg(chip, 5, chip->awacs_reg[5]);
+		snd_pmac_awacs_write_reg(chip, 6, chip->awacs_reg[6]);
+		snd_pmac_awacs_write_reg(chip, 7, chip->awacs_reg[7]);
+		screamer_recalibrate(chip);
+	}
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+/*
+ * initialize chip
+ */
+int __init
+snd_pmac_awacs_init(pmac_t *chip)
+{
+	int err, vol;
+
+	snd_pmac_awacs_write_reg(chip, 0, MASK_MUX_CD);
+	/* FIXME: Only machines with external SRS module need MASK_PAROUT */
+	chip->awacs_reg[1] = MASK_LOOPTHRU;
+	if (chip->has_iic || chip->device_id == 0x5 ||
+	    /*chip->_device_id == 0x8 || */
+	    chip->device_id == 0xb)
+		chip->awacs_reg[1] |= MASK_PAROUT;
+	snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
+	/* get default volume from nvram */
+	vol = (~nvram_read_byte(0x1308) & 7) << 1;
+	snd_pmac_awacs_write_reg(chip, 2, vol + (vol << 6));
+	snd_pmac_awacs_write_reg(chip, 4, vol + (vol << 6));
+	if (chip->model == PMAC_SCREAMER) {
+		snd_pmac_awacs_write_reg(chip, 5, 0);
+		snd_pmac_awacs_write_reg(chip, 6, 0);
+		snd_pmac_awacs_write_reg(chip, 7, 0);
+	}
+
+#ifdef CONFIG_PMAC_PBOOK
+	/* Recalibrate chip */
+	if (chip->model == PMAC_SCREAMER)
+		screamer_recalibrate(chip);
+#endif
+
+	if (chip->model <= PMAC_SCREAMER && chip->revision == 0) {
+		chip->revision =
+			(in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
+		if (chip->revision == 3) {
+#ifdef PMAC_AMP_AVAIL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+			if (adb_hardware == ADB_VIACUDA)
+				chip->amp_only = 1;
+#elif defined(CONFIG_ADB_CUDA)
+			if (sys_ctrler == SYS_CTRLER_CUDA)
+				chip->amp_only = 1;
+#endif
+			if (chip->amp_only) {
+				/*chip->amp_vol[0] = chip->amp_vol[1] = 31;*/
+				snd_pmac_awacs_enable_amp(chip, chip->amp_vol[0], chip->amp_vol[1]);
+			}
+#endif /* PMAC_AMP_AVAIL */
+		}
+	}
+
+	/*
+	 * build mixers
+	 */
+	strcpy(chip->card->mixername, "PowerMac AWACS");
+
+	if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers1),
+				snd_pmac_awacs_mixers1)) < 0)
+		return err;
+#ifdef PMAC_AMP_AVAIL
+	if (chip->amp_only) {
+		if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_amp_mixers),
+					snd_pmac_awacs_amp_mixers)) < 0)
+			return err;
+	} else {
+#endif /* PMAC_AMP_AVAIL */
+		if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_speaker_mixers),
+					snd_pmac_awacs_speaker_mixers)) < 0)
+			return err;
+#ifdef PMAC_AMP_AVAIL
+	}
+#endif /* PMAC_AMP_AVAIL */
+	if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers2),
+				snd_pmac_awacs_mixers2)) < 0)
+		return err;
+
+	/*
+	 * set lowlevel callbacks
+	 */
+	chip->set_format = snd_pmac_awacs_set_format;
+#ifdef CONFIG_PMAC_PBOOK
+	chip->resume = snd_pmac_awacs_resume;
+#endif
+
+	return 0;
+}
diff -Nru linux/sound/ppc/awacs.h linux-2.4.19-pre5-mjc/sound/ppc/awacs.h
--- linux/sound/ppc/awacs.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/awacs.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,182 @@
+/*
+ * Driver for PowerMac AWACS onboard soundchips
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   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
+ */
+
+
+#ifndef __AWACS_H
+#define __AWACS_H
+
+/*******************************/
+/* AWACs Audio Register Layout */
+/*******************************/
+
+struct awacs_regs {
+    unsigned	control;	/* Audio control register */
+    unsigned	pad0[3];
+    unsigned	codec_ctrl;	/* Codec control register */
+    unsigned	pad1[3];
+    unsigned	codec_stat;	/* Codec status register */
+    unsigned	pad2[3];
+    unsigned	clip_count;	/* Clipping count register */
+    unsigned	pad3[3];
+    unsigned	byteswap;	/* Data is little-endian if 1 */
+};
+
+/*******************/
+/* Audio Bit Masks */
+/*******************/
+
+/* Audio Control Reg Bit Masks */
+/* ----- ------- --- --- ----- */
+#define MASK_ISFSEL	(0xf)		/* Input SubFrame Select */
+#define MASK_OSFSEL	(0xf << 4)	/* Output SubFrame Select */
+#define MASK_RATE	(0x7 << 8)	/* Sound Rate */
+#define MASK_CNTLERR	(0x1 << 11)	/* Error */
+#define MASK_PORTCHG	(0x1 << 12)	/* Port Change */
+#define MASK_IEE	(0x1 << 13)	/* Enable Interrupt on Error */
+#define MASK_IEPC	(0x1 << 14)	/* Enable Interrupt on Port Change */
+#define MASK_SSFSEL	(0x3 << 15)	/* Status SubFrame Select */
+
+/* Audio Codec Control Reg Bit Masks */
+/* ----- ----- ------- --- --- ----- */
+#define MASK_NEWECMD	(0x1 << 24)	/* Lock: don't write to reg when 1 */
+#define MASK_EMODESEL	(0x3 << 22)	/* Send info out on which frame? */
+#define MASK_EXMODEADDR	(0x3ff << 12)	/* Extended Mode Address -- 10 bits */
+#define MASK_EXMODEDATA	(0xfff)		/* Extended Mode Data -- 12 bits */
+
+/* Audio Codec Control Address Values / Masks */
+/* ----- ----- ------- ------- ------ - ----- */
+#define MASK_ADDR0	(0x0 << 12)	/* Expanded Data Mode Address 0 */
+#define MASK_ADDR_MUX	MASK_ADDR0	/* Mux Control */
+#define MASK_ADDR_GAIN	MASK_ADDR0
+
+#define MASK_ADDR1	(0x1 << 12)	/* Expanded Data Mode Address 1 */
+#define MASK_ADDR_MUTE	MASK_ADDR1
+#define MASK_ADDR_RATE	MASK_ADDR1
+
+#define MASK_ADDR2	(0x2 << 12)	/* Expanded Data Mode Address 2 */
+#define MASK_ADDR_VOLA	MASK_ADDR2	/* Volume Control A -- Headphones */
+#define MASK_ADDR_VOLHD MASK_ADDR2
+
+#define MASK_ADDR4	(0x4 << 12)	/* Expanded Data Mode Address 4 */
+#define MASK_ADDR_VOLC	MASK_ADDR4	/* Volume Control C -- Speaker */
+#define MASK_ADDR_VOLSPK MASK_ADDR4
+
+/* additional registers of screamer */
+#define MASK_ADDR5	(0x5 << 12)	/* Expanded Data Mode Address 5 */
+#define MASK_ADDR6	(0x6 << 12)	/* Expanded Data Mode Address 6 */
+#define MASK_ADDR7	(0x7 << 12)	/* Expanded Data Mode Address 7 */
+
+/* Address 0 Bit Masks & Macros */
+/* ------- - --- ----- - ------ */
+#define MASK_GAINRIGHT	(0xf)		/* Gain Right Mask */
+#define MASK_GAINLEFT	(0xf << 4)	/* Gain Left Mask */
+#define MASK_GAINLINE	(0x1 << 8)	/* Disable Mic preamp */
+#define MASK_GAINMIC	(0x0 << 8)	/* Enable Mic preamp */
+
+#define MASK_MUX_CD	(0x1 << 9)	/* Select CD in MUX */
+#define MASK_MUX_MIC	(0x1 << 10)	/* Select Mic in MUX */
+#define MASK_MUX_AUDIN	(0x1 << 11)	/* Select Audio In in MUX */
+#define MASK_MUX_LINE	MASK_MUX_AUDIN
+
+#define GAINRIGHT(x)	((x) & MASK_GAINRIGHT)
+#define GAINLEFT(x)	(((x) << 4) & MASK_GAINLEFT)
+
+/* Address 1 Bit Masks */
+/* ------- - --- ----- */
+#define MASK_ADDR1RES1	(0x3)		/* Reserved */
+#define MASK_RECALIBRATE (0x1 << 2)	/* Recalibrate */
+#define MASK_SAMPLERATE	(0x7 << 3)	/* Sample Rate: */
+#define MASK_LOOPTHRU	(0x1 << 6)	/* Loopthrough Enable */
+#define MASK_CMUTE	(0x1 << 7)	/* Output C (Speaker) Mute when 1 */
+#define MASK_SPKMUTE	MASK_CMUTE
+#define MASK_ADDR1RES2	(0x1 << 8)	/* Reserved */
+#define MASK_AMUTE	(0x1 << 9)	/* Output A (Headphone) Mute when 1 */
+#define MASK_HDMUTE	MASK_AMUTE
+#define MASK_PAROUT	(0x3 << 10)	/* Parallel Out (???) */
+
+#define SAMPLERATE_48000	(0x0 << 3)	/* 48 or 44.1 kHz */
+#define SAMPLERATE_32000	(0x1 << 3)	/* 32 or 29.4 kHz */
+#define SAMPLERATE_24000	(0x2 << 3)	/* 24 or 22.05 kHz */
+#define SAMPLERATE_19200	(0x3 << 3)	/* 19.2 or 17.64 kHz */
+#define SAMPLERATE_16000	(0x4 << 3)	/* 16 or 14.7 kHz */
+#define SAMPLERATE_12000	(0x5 << 3)	/* 12 or 11.025 kHz */
+#define SAMPLERATE_9600		(0x6 << 3)	/* 9.6 or 8.82 kHz */
+#define SAMPLERATE_8000		(0x7 << 3)	/* 8 or 7.35 kHz */
+
+/* Address 2 & 4 Bit Masks & Macros */
+/* ------- - - - --- ----- - ------ */
+#define MASK_OUTVOLRIGHT (0xf)		/* Output Right Volume */
+#define MASK_ADDR2RES1	(0x2 << 4)	/* Reserved */
+#define MASK_ADDR4RES1	MASK_ADDR2RES1
+#define MASK_OUTVOLLEFT	(0xf << 6)	/* Output Left Volume */
+#define MASK_ADDR2RES2	(0x2 << 10)	/* Reserved */
+#define MASK_ADDR4RES2	MASK_ADDR2RES2
+
+#define VOLRIGHT(x)	(((~(x)) & MASK_OUTVOLRIGHT))
+#define VOLLEFT(x)	(((~(x)) << 6) & MASK_OUTVOLLEFT)
+
+/* Audio Codec Status Reg Bit Masks */
+/* ----- ----- ------ --- --- ----- */
+#define MASK_EXTEND	(0x1 << 23)	/* Extend */
+#define MASK_VALID	(0x1 << 22)	/* Valid Data? */
+#define MASK_OFLEFT	(0x1 << 21)	/* Overflow Left */
+#define MASK_OFRIGHT	(0x1 << 20)	/* Overflow Right */
+#define MASK_ERRCODE	(0xf << 16)	/* Error Code */
+#define MASK_REVISION	(0xf << 12)	/* Revision Number */
+#define MASK_MFGID	(0xf << 8)	/* Mfg. ID */
+#define MASK_CODSTATRES	(0xf << 4)	/* bits 4 - 7 reserved */
+#define MASK_INPPORT	(0xf)		/* Input Port */
+#define MASK_HDPCONN	8		/* headphone plugged in */
+
+/* Clipping Count Reg Bit Masks */
+/* -------- ----- --- --- ----- */
+#define MASK_CLIPLEFT	(0xff << 7)	/* Clipping Count, Left Channel */
+#define MASK_CLIPRIGHT	(0xff)		/* Clipping Count, Right Channel */
+
+/* DBDMA ChannelStatus Bit Masks */
+/* ----- ------------- --- ----- */
+#define MASK_CSERR	(0x1 << 7)	/* Error */
+#define MASK_EOI	(0x1 << 6)	/* End of Input -- only for Input Channel */
+#define MASK_CSUNUSED	(0x1f << 1)	/* bits 1-5 not used */
+#define MASK_WAIT	(0x1)		/* Wait */
+
+/* Various Rates */
+/* ------- ----- */
+#define RATE_48000	(0x0 << 8)	/* 48 kHz */
+#define RATE_44100	(0x0 << 8)	/* 44.1 kHz */
+#define RATE_32000	(0x1 << 8)	/* 32 kHz */
+#define RATE_29400	(0x1 << 8)	/* 29.4 kHz */
+#define RATE_24000	(0x2 << 8)	/* 24 kHz */
+#define RATE_22050	(0x2 << 8)	/* 22.05 kHz */
+#define RATE_19200	(0x3 << 8)	/* 19.2 kHz */
+#define RATE_17640	(0x3 << 8)	/* 17.64 kHz */
+#define RATE_16000	(0x4 << 8)	/* 16 kHz */
+#define RATE_14700	(0x4 << 8)	/* 14.7 kHz */
+#define RATE_12000	(0x5 << 8)	/* 12 kHz */
+#define RATE_11025	(0x5 << 8)	/* 11.025 kHz */
+#define RATE_9600	(0x6 << 8)	/* 9.6 kHz */
+#define RATE_8820	(0x6 << 8)	/* 8.82 kHz */
+#define RATE_8000	(0x7 << 8)	/* 8 kHz */
+#define RATE_7350	(0x7 << 8)	/* 7.35 kHz */
+
+#define RATE_LOW	1	/* HIGH = 48kHz, etc;  LOW = 44.1kHz, etc. */
+
+
+#endif /* __AWACS_H */
diff -Nru linux/sound/ppc/burgundy.c linux-2.4.19-pre5-mjc/sound/ppc/burgundy.c
--- linux/sound/ppc/burgundy.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/burgundy.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,378 @@
+/*
+ * PMac Burgundy lowlevel functions
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ * code based on dmasound.c.
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "pmac.h"
+#include "burgundy.h"
+
+#define chip_t pmac_t
+
+
+/* Waits for busy flag to clear */
+inline static void
+snd_pmac_burgundy_busy_wait(pmac_t *chip)
+{
+	while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD)
+		;
+}
+
+inline static void
+snd_pmac_burgundy_extend_wait(pmac_t *chip)
+{
+	while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND))
+		;
+	while (in_le32(&chip->awacs->codec_stat) & MASK_EXTEND)
+		;
+}
+
+static void
+snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val)
+{
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
+	snd_pmac_burgundy_busy_wait(chip);
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
+	snd_pmac_burgundy_busy_wait(chip);
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
+	snd_pmac_burgundy_busy_wait(chip);
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
+	snd_pmac_burgundy_busy_wait(chip);
+}
+
+static unsigned
+snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr)
+{
+	unsigned val = 0;
+	unsigned long flags;
+
+	/* should have timeouts here */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x100000);
+	snd_pmac_burgundy_busy_wait(chip);
+	snd_pmac_burgundy_extend_wait(chip);
+	val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff;
+
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x100100);
+	snd_pmac_burgundy_busy_wait(chip);
+	snd_pmac_burgundy_extend_wait(chip);
+	val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8;
+
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x100200);
+	snd_pmac_burgundy_busy_wait(chip);
+	snd_pmac_burgundy_extend_wait(chip);
+	val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16;
+
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x100300);
+	snd_pmac_burgundy_busy_wait(chip);
+	snd_pmac_burgundy_extend_wait(chip);
+	val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24;
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return val;
+}
+
+static void
+snd_pmac_burgundy_wcb(pmac_t *chip, unsigned int addr, unsigned int val)
+{
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
+	snd_pmac_burgundy_busy_wait(chip);
+}
+
+static unsigned
+snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr)
+{
+	unsigned val = 0;
+	unsigned long flags;
+
+	/* should have timeouts here */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	out_le32(&chip->awacs->codec_ctrl, addr + 0x100000);
+	snd_pmac_burgundy_busy_wait(chip);
+	snd_pmac_burgundy_extend_wait(chip);
+	val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff;
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return val;
+}
+
+/*
+ * Burgundy volume: 0 - 100, stereo
+ */
+static void
+snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, int shift)
+{
+	int hardvolume, lvolume, rvolume;
+
+	lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0;
+	rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0;
+
+	hardvolume = lvolume + (rvolume << shift);
+	if (shift == 8)
+		hardvolume |= hardvolume << 16;
+
+	snd_pmac_burgundy_wcw(chip, address, hardvolume);
+}
+
+static void
+snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, int shift)
+{
+	int wvolume;
+
+	wvolume = snd_pmac_burgundy_rcw(chip, address);
+
+	volume[0] = wvolume & 0xff;
+	if (volume[0] >= BURGUNDY_VOLUME_OFFSET)
+		volume[0] -= BURGUNDY_VOLUME_OFFSET;
+	else
+		volume[0] = 0;
+	volume[1] = (wvolume >> shift) & 0xff;
+	if (volume[1] >= BURGUNDY_VOLUME_OFFSET)
+		volume[1] -= BURGUNDY_VOLUME_OFFSET;
+	else
+		volume[1] = 0;
+}
+
+
+/*
+ */
+
+#define BASE2ADDR(base)	((base) << 12)
+#define ADDR2BASE(addr)	((addr) >> 12)
+
+static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 100;
+	return 0;
+}
+
+static int snd_pmac_burgundy_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, shift);
+	return 0;
+}
+
+static int snd_pmac_burgundy_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	long nvoices[2];
+
+	snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, shift);
+	snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift);
+	return (nvoices[0] != ucontrol->value.integer.value[0] ||
+		nvoices[1] != ucontrol->value.integer.value[1]);
+}
+
+#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+  info: snd_pmac_burgundy_info_volume,\
+  get: snd_pmac_burgundy_get_volume,\
+  put: snd_pmac_burgundy_put_volume,\
+  private_value: ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) }
+
+/* lineout/speaker */
+
+static int snd_pmac_burgundy_info_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	int stereo = (kcontrol->private_value >> 24) & 1;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = stereo + 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_pmac_burgundy_get_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int lmask = kcontrol->private_value & 0xff;
+	int rmask = (kcontrol->private_value >> 8) & 0xff;
+	int stereo = (kcontrol->private_value >> 24) & 1;
+	int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
+	ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0;
+	if (stereo)
+		ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0;
+	return 0;
+}
+
+static int snd_pmac_burgundy_put_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int lmask = kcontrol->private_value & 0xff;
+	int rmask = (kcontrol->private_value >> 8) & 0xff;
+	int stereo = (kcontrol->private_value >> 24) & 1;
+	int val, oval;
+	oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
+	val = oval & ~(lmask | rmask);
+	if (ucontrol->value.integer.value[0])
+		val |= lmask;
+	if (stereo && ucontrol->value.integer.value[1])
+		val |= rmask;
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val);
+	return val != oval;
+}
+
+#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+  info: snd_pmac_burgundy_info_switch_out,\
+  get: snd_pmac_burgundy_get_switch_out,\
+  put: snd_pmac_burgundy_put_switch_out,\
+  private_value: ((lmask) | ((rmask) << 8) | ((stereo) << 24)) }
+
+/* line/speaker output volume */
+static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	int stereo = (kcontrol->private_value >> 24) & 1;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = stereo + 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 15;
+	return 0;
+}
+
+static int snd_pmac_burgundy_get_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+	int stereo = (kcontrol->private_value >> 24) & 1;
+	int oval;
+
+	oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff;
+	ucontrol->value.integer.value[0] = oval & 0xf;
+	if (stereo)
+		ucontrol->value.integer.value[1] = (oval >> 4) & 0xf;
+	return 0;
+}
+
+static int snd_pmac_burgundy_put_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+	int stereo = (kcontrol->private_value >> 24) & 1;
+	int oval, val;
+
+	oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff;
+	val = ucontrol->value.integer.value[0];
+	if (stereo)
+		val |= ucontrol->value.integer.value[1] << 4;
+	else
+		val |= ucontrol->value.integer.value[0] << 4;
+	val = ~val & 0xff;
+	snd_pmac_burgundy_wcb(chip, addr, val);
+	return val != oval;
+}
+
+#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+  info: snd_pmac_burgundy_info_volume_out,\
+  get: snd_pmac_burgundy_get_volume_out,\
+  put: snd_pmac_burgundy_put_volume_out,\
+  private_value: (ADDR2BASE(addr) | ((stereo) << 24)) }
+
+static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] = {
+	BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
+	BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16),
+	BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16),
+	BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16),
+	BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0),
+	/*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/
+	BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1),
+	BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0),
+	BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1),
+};	
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+
+/*
+ * initialize burgundy
+ */
+int __init snd_pmac_burgundy_init(pmac_t *chip)
+{
+	int i, err;
+
+	/* Checks to see the chip is alive and kicking */
+	if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) {
+		printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n");
+		return 1;
+	}
+
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES,
+			   DEF_BURGUNDY_OUTPUTENABLES);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+			   DEF_BURGUNDY_MORE_OUTPUTENABLES);
+	snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
+			   DEF_BURGUNDY_OUTPUTSELECTS);
+
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21,
+			   DEF_BURGUNDY_INPSEL21);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3,
+			   DEF_BURGUNDY_INPSEL3);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD,
+			   DEF_BURGUNDY_GAINCD);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE,
+			   DEF_BURGUNDY_GAINLINE);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC,
+			   DEF_BURGUNDY_GAINMIC);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM,
+			   DEF_BURGUNDY_GAINMODEM);
+
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER,
+			   DEF_BURGUNDY_ATTENSPEAKER);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT,
+			   DEF_BURGUNDY_ATTENLINEOUT);
+	snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP,
+			   DEF_BURGUNDY_ATTENHP);
+
+	snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME,
+			   DEF_BURGUNDY_MASTER_VOLUME);
+	snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD,
+			   DEF_BURGUNDY_VOLCD);
+	snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE,
+			   DEF_BURGUNDY_VOLLINE);
+	snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC,
+			   DEF_BURGUNDY_VOLMIC);
+
+	/*
+	 * build burgundy mixers
+	 */
+	strcpy(chip->card->mixername, "PowerMac Burgundy");
+
+	for (i = 0; i < num_controls(snd_pmac_burgundy_mixers); i++) {
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
diff -Nru linux/sound/ppc/burgundy.h linux-2.4.19-pre5-mjc/sound/ppc/burgundy.h
--- linux/sound/ppc/burgundy.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/burgundy.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,95 @@
+/*
+ * Driver for PowerMac Burgundy onboard soundchips
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   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
+ */
+
+
+#ifndef __BURGUNDY_H
+#define __BURGUNDY_H
+
+#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
+#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
+
+#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
+
+#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
+#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
+
+#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
+
+#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
+
+#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
+#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
+#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
+#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
+#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
+#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
+#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+
+/* These are all default values for the burgundy */
+#define DEF_BURGUNDY_INPSEL21 (0xAA)
+#define DEF_BURGUNDY_INPSEL3 (0x0A)
+
+#define DEF_BURGUNDY_GAINCD (0x33)
+#define DEF_BURGUNDY_GAINLINE (0x44)
+#define DEF_BURGUNDY_GAINMIC (0x44)
+#define DEF_BURGUNDY_GAINMODEM (0x06)
+
+/* Remember: lowest volume here is 0x9b */
+#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
+#define DEF_BURGUNDY_VOLLINE (0x00000000)
+#define DEF_BURGUNDY_VOLMIC (0x00000000)
+#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
+
+#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
+#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
+
+/* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */
+#define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD)
+
+#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
+
+#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
+#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
+#define DEF_BURGUNDY_ATTENHP (0xCC)
+
+/* OUTPUTENABLES bits */
+#define BURGUNDY_OUTPUT_LEFT	0x02
+#define BURGUNDY_OUTPUT_RIGHT	0x04
+#define BURGUNDY_OUTPUT_INTERN	0x80
+
+/* volume offset */
+#define BURGUNDY_VOLUME_OFFSET	155
+
+#endif /* __BURGUNDY_H */
diff -Nru linux/sound/ppc/daca.c linux-2.4.19-pre5-mjc/sound/ppc/daca.c
--- linux/sound/ppc/daca.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/daca.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,274 @@
+/*
+ * PMac DACA lowlevel functions
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "pmac.h"
+
+#define chip_t pmac_t
+
+/* i2c address */
+#define DACA_I2C_ADDR	0x4d
+
+/* registers */
+#define DACA_REG_SR	0x01
+#define DACA_REG_AVOL	0x02
+#define DACA_REG_GCFG	0x03
+
+/* maximum volume value */
+#define DACA_VOL_MAX	0x38
+
+
+typedef struct pmac_daca_t {
+	pmac_keywest_t i2c;
+	int left_vol, right_vol;
+	unsigned int deemphasis : 1;
+	unsigned int amp_on : 1;
+} pmac_daca_t;
+
+
+/*
+ * initialize / detect DACA
+ */
+static int daca_init_client(pmac_t *chip, pmac_keywest_t *i2c)
+{
+	unsigned short wdata = 0x00;
+	/* SR: no swap, 1bit delay, 32-48kHz */
+	/* GCFG: power amp inverted, DAC on */
+	if (snd_pmac_keywest_write_byte(i2c, DACA_REG_SR, 0x08) < 0 ||
+	    snd_pmac_keywest_write_byte(i2c, DACA_REG_GCFG, 0x05) < 0)
+		return -EINVAL;
+	return snd_pmac_keywest_write(i2c, DACA_REG_AVOL, 2, (unsigned char*)&wdata);
+}
+
+/*
+ * update volume
+ */
+static int daca_set_volume(pmac_daca_t *mix)
+{
+	unsigned char data[2];
+  
+	if (! mix->i2c.base)
+		return -ENODEV;
+  
+	if (mix->right_vol > DACA_VOL_MAX)
+		data[0] = DACA_VOL_MAX;
+	else
+		data[0] = mix->right_vol;
+	if (mix->left_vol > DACA_VOL_MAX)
+		data[1] = DACA_VOL_MAX;
+	else
+		data[1] = mix->left_vol;
+	data[1] |= mix->deemphasis ? 0x40 : 0;
+	if (snd_pmac_keywest_write(&mix->i2c, DACA_REG_AVOL, 2, data) < 0) {
+		snd_printk("failed to set volume \n");  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
+
+/* deemphasis switch */
+static int daca_info_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int daca_get_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_daca_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0;
+	return 0;
+}
+
+static int daca_put_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_daca_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->deemphasis != ucontrol->value.integer.value[0];
+	if (change) {
+		mix->deemphasis = ucontrol->value.integer.value[0];
+		daca_set_volume(mix);
+	}
+	return change;
+}
+
+/* output volume */
+static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = DACA_VOL_MAX;
+	return 0;
+}
+
+static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_daca_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->left_vol;
+	ucontrol->value.integer.value[1] = mix->right_vol;
+	return 0;
+}
+
+static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_daca_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->left_vol != ucontrol->value.integer.value[0] ||
+		mix->right_vol != ucontrol->value.integer.value[1];
+	if (change) {
+		mix->left_vol = ucontrol->value.integer.value[0];
+		mix->right_vol = ucontrol->value.integer.value[1];
+		daca_set_volume(mix);
+	}
+	return change;
+}
+
+/* amplifier switch */
+#define daca_info_amp	daca_info_deemphasis
+
+static int daca_get_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_daca_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0;
+	return 0;
+}
+
+static int daca_put_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_daca_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->amp_on != ucontrol->value.integer.value[0];
+	if (change) {
+		mix->amp_on = ucontrol->value.integer.value[0];
+		snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG,
+					    mix->amp_on ? 0x05 : 0x04);
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t daca_mixers[] = {
+	{ iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  name: "Deemphasis Switch",
+	  info: daca_info_deemphasis,
+	  get: daca_get_deemphasis,
+	  put: daca_put_deemphasis
+	},
+	{ iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  name: "Master Playback Volume",
+	  info: daca_info_volume,
+	  get: daca_get_volume,
+	  put: daca_put_volume
+	},
+	{ iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  name: "Power Amplifier Switch",
+	  info: daca_info_amp,
+	  get: daca_get_amp,
+	  put: daca_put_amp
+	},
+};
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+
+#ifdef CONFIG_PMAC_PBOOK
+static void daca_resume(pmac_t *chip)
+{
+	pmac_daca_t *mix = chip->mixer_data;
+	snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_SR, 0x08);
+	snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG,
+				    mix->amp_on ? 0x05 : 0x04);
+	daca_set_volume(mix);
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+
+static void daca_cleanup(pmac_t *chip)
+{
+	pmac_daca_t *mix = chip->mixer_data;
+	if (! mix)
+		return;
+	snd_pmac_keywest_cleanup(&mix->i2c);
+	kfree(mix);
+	chip->mixer_data = NULL;
+}
+
+/* exported */
+int __init snd_pmac_daca_init(pmac_t *chip)
+{
+	int i, err;
+	pmac_daca_t *mix;
+
+	mix = kmalloc(sizeof(*mix), GFP_KERNEL);
+	if (! mix)
+		return -ENOMEM;
+	memset(mix, 0, sizeof(*mix));
+	chip->mixer_data = mix;
+	chip->mixer_free = daca_cleanup;
+	mix->amp_on = 1; /* default on */
+
+	if ((err = snd_pmac_keywest_find(chip, &mix->i2c, DACA_I2C_ADDR, daca_init_client)) < 0)
+		return err;
+
+	/*
+	 * build mixers
+	 */
+	strcpy(chip->card->mixername, "PowerMac DACA");
+
+	for (i = 0; i < num_controls(daca_mixers); i++) {
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0)
+			return err;
+	}
+
+#ifdef CONFIG_PMAC_PBOOK
+	chip->resume = daca_resume;
+#endif
+
+	return 0;
+}
diff -Nru linux/sound/ppc/keywest.c linux-2.4.19-pre5-mjc/sound/ppc/keywest.c
--- linux/sound/ppc/keywest.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/keywest.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,228 @@
+/*
+ * Keywest i2c code
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ * based on i2c-keywest.c from lm_sensors.
+ *    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ *
+ *   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
+ */
+
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "pmac.h"
+
+/* The Tumbler audio equalizer can be really slow sometimes */
+#define KW_POLL_SANITY 10000
+
+/* address indices */
+#define KW_ADDR_MODE	0
+#define KW_ADDR_CONTROL	1
+#define KW_ADDR_STATUS	2
+#define KW_ADDR_ISR	3
+#define KW_ADDR_IER	4
+#define KW_ADDR_ADDR	5
+#define KW_ADDR_SUBADDR	6
+#define KW_ADDR_DATA	7
+
+#define KW_I2C_ADDR(i2c,type)	((i2c)->base + (type) * (i2c)->steps)
+
+/* keywest needs a small delay to defuddle itself after changing a setting */
+inline static void keywest_writeb_wait(pmac_keywest_t *i2c, int addr, int value)
+{
+	writeb(value, KW_I2C_ADDR(i2c, addr));
+	udelay(10);
+}
+
+inline static void keywest_writeb(pmac_keywest_t *i2c, int addr, int value)
+{
+	writeb(value, KW_I2C_ADDR(i2c, addr));
+}
+
+inline unsigned char keywest_readb(pmac_keywest_t *i2c, int addr)
+{
+	return readb(KW_I2C_ADDR(i2c, addr));
+}
+
+static int keywest_poll_interrupt(pmac_keywest_t *i2c)
+{
+	int i, res;
+	for (i = 0; i < KW_POLL_SANITY; i++) {
+		udelay(100);
+		res = keywest_readb(i2c, KW_ADDR_ISR) & 0x0f;
+		if (res > 0)
+			return res;
+	}
+
+	//snd_printd("Sanity check failed!  Expected interrupt never happened.\n");
+	return -ENODEV;
+}
+
+
+static void keywest_reset(pmac_keywest_t *i2c)
+{
+	int interrupt_state;
+
+	/* Clear all past interrupts */
+	interrupt_state = keywest_readb(i2c, KW_ADDR_ISR) & 0x0f;
+	if (interrupt_state > 0)
+		keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state);
+}
+
+static int keywest_start(pmac_keywest_t *i2c, unsigned char cmd, int is_read)
+{
+	int interrupt_state;
+	int ack;
+
+	keywest_reset(i2c);
+
+	/* Set up address and r/w bit */
+	keywest_writeb_wait(i2c, KW_ADDR_ADDR, (i2c->addr << 1) | (is_read ? 1 : 0));
+
+	/* Set up 'sub address' which I'm guessing is the command field? */
+	keywest_writeb_wait(i2c, KW_ADDR_SUBADDR, cmd);
+	
+	/* Start sending address */
+	keywest_writeb_wait(i2c, KW_ADDR_CONTROL, keywest_readb(i2c, KW_ADDR_CONTROL) | 2);
+	interrupt_state = keywest_poll_interrupt(i2c);
+	if (interrupt_state < 0)
+		return interrupt_state;
+
+	ack = keywest_readb(i2c, KW_ADDR_STATUS) & 0x0f;
+	if ((ack & 0x02) == 0) {
+		snd_printd("Ack Status on addr expected but got: 0x%02x on addr: 0x%02x\n", ack, i2c->addr);
+		return -EINVAL;
+	} 
+	return interrupt_state;
+}
+
+/* exported */
+int snd_pmac_keywest_write(pmac_keywest_t *i2c, unsigned char cmd, int len, unsigned char *data)
+{
+	int interrupt_state;
+	int error_state = 0;
+	int i;
+
+	snd_assert(len >= 1 && len <= 32, return -EINVAL);
+
+	if ((interrupt_state = keywest_start(i2c, cmd, 0)) < 0)
+		return -EINVAL;
+
+	for(i = 0; i < len; i++) {
+		keywest_writeb_wait(i2c, KW_ADDR_DATA, data[i]);
+
+		/* Clear interrupt and go */
+		keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state);
+
+		interrupt_state = keywest_poll_interrupt(i2c);
+		if (interrupt_state < 0) {
+			error_state = -EINVAL;
+			interrupt_state = 0;
+		}
+		if ((keywest_readb(i2c, KW_ADDR_STATUS) & 0x02) == 0) {
+			snd_printd("Ack Expected by not received(block)!\n");
+			error_state = -EINVAL;
+		}
+	}
+
+	/* Send stop */
+	keywest_writeb_wait(i2c, KW_ADDR_CONTROL,
+			    keywest_readb(i2c, KW_ADDR_CONTROL) | 4);
+
+	keywest_writeb_wait(i2c, KW_ADDR_CONTROL, interrupt_state);
+		
+	interrupt_state = keywest_poll_interrupt(i2c);
+	if (interrupt_state < 0) {
+		error_state = -EINVAL;
+		interrupt_state = 0;
+	}
+	keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state);
+
+	return error_state;
+}
+
+/* exported */
+void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c)
+{
+	if (i2c->base) {
+		iounmap((void*)i2c->base);
+		i2c->base = 0;
+	}
+}
+
+/* exported */
+int snd_pmac_keywest_find(pmac_t *chip, pmac_keywest_t *i2c, int addr,
+			  int (*init_client)(pmac_t *, pmac_keywest_t *))
+{
+	struct device_node *i2c_device;
+	void **temp;
+	void *base = NULL;
+	u32 steps = 0;
+
+	i2c_device = find_compatible_devices("i2c", "keywest");
+	
+	if (i2c_device == 0) {
+		printk(KERN_ERR "pmac: No Keywest i2c devices found.\n");
+		return -ENODEV;
+	}
+	
+	for (; i2c_device; i2c_device = i2c_device->next) {
+		snd_printd("Keywest device found: %s\n", i2c_device->full_name);
+		temp = (void **) get_property(i2c_device, "AAPL,address", NULL);
+		if (temp != NULL) {
+			base = *temp;
+		} else {
+			snd_printd("pmac: no 'address' prop!\n");
+			continue;
+		}
+
+		temp = (void **) get_property(i2c_device, "AAPL,address-step", NULL);
+		if (temp != NULL) {
+			steps = *(uint *)temp;
+		} else {
+			snd_printd("pmac: no 'address-step' prop!\n");
+			continue;
+		}
+		
+		i2c->base = (unsigned long)ioremap((unsigned long)base, steps * 8);
+		i2c->steps = steps;
+
+		/* Select standard sub mode 
+		 *  
+		 * ie for <Address><Ack><Command><Ack><data><Ack>... style transactions
+		 */
+		keywest_writeb_wait(i2c, KW_ADDR_MODE, 0x08);
+
+		/* Enable interrupts */
+		keywest_writeb_wait(i2c, KW_ADDR_IER, 1 + 2 + 4 + 8);
+
+		keywest_reset(i2c);
+
+		i2c->addr = addr;
+		if (init_client(chip, i2c) < 0) {
+			snd_pmac_keywest_cleanup(i2c);
+			continue;
+		}
+
+		return 0; /* ok */
+	}
+	
+	return -ENODEV;
+}
diff -Nru linux/sound/ppc/pmac.c linux-2.4.19-pre5-mjc/sound/ppc/pmac.c
--- linux/sound/ppc/pmac.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/pmac.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1431 @@
+/*
+ * PMac DBDMA lowlevel functions
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ * code based on dmasound.c.
+ *
+ *   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
+ */
+
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include "pmac.h"
+#include <sound/pcm_params.h>
+#ifdef CONFIG_PPC_HAS_FEATURE_CALLS
+#include <asm/pmac_feature.h>
+#else
+#include <asm/feature.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define pmu_suspend()	/**/
+#define pmu_resume()	/**/
+#endif
+
+
+#define chip_t pmac_t
+
+
+#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK)
+static int snd_pmac_register_sleep_notifier(pmac_t *chip);
+static int snd_pmac_unregister_sleep_notifier(pmac_t *chip);
+static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state);
+#endif
+
+
+/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */
+static int awacs_freqs[8] = {
+	44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
+};
+/* fixed frequency table for tumbler */
+static int tumbler_freqs[2] = {
+	48000, 44100
+};
+
+/*
+ * allocate DBDMA command arrays
+ */
+static int snd_pmac_dbdma_alloc(pmac_dbdma_t *rec, int size)
+{
+	rec->space = kmalloc(sizeof(struct dbdma_cmd) * (size + 1), GFP_KERNEL);
+	if (rec->space == NULL)
+		return -ENOMEM;
+	rec->size = size;
+	memset(rec->space, 0, sizeof(struct dbdma_cmd) * (size + 1));
+	rec->cmds = (void*)DBDMA_ALIGN(rec->space);
+	rec->addr = virt_to_bus(rec->cmds);
+	return 0;
+}
+
+static void snd_pmac_dbdma_free(pmac_dbdma_t *rec)
+{
+	if (rec && rec->space)
+		kfree(rec->space);
+}
+
+
+/*
+ * pcm stuff
+ */
+
+/*
+ * look up frequency table
+ */
+
+static unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate)
+{
+	int i, ok, found;
+
+	ok = rec->cur_freqs;
+	if (rate > chip->freq_table[0])
+		return 0;
+	found = 0;
+	for (i = 0; i < chip->num_freqs; i++, ok >>= 1) {
+		if (! (ok & 1)) continue;
+		found = i;
+		if (rate >= chip->freq_table[i])
+			break;
+	}
+	return found;
+}
+
+/*
+ * check whether another stream is active
+ */
+static inline int another_stream(int stream)
+{
+	return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+}
+
+/*
+ * allocate buffers
+ */
+static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs,
+				  snd_pcm_hw_params_t *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params));
+}
+
+/*
+ * release buffers
+ */
+static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs)
+{
+	snd_pcm_lib_free_pages(subs);
+	return 0;
+}
+
+/*
+ * get a stream of the opposite direction
+ */
+static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream)
+{
+	switch (stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		return &chip->playback;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		return &chip->capture;
+	default:
+		snd_BUG();
+		return NULL;
+	}
+}
+
+/*
+ * wait while run status is on
+ */
+inline static void
+snd_pmac_wait_ack(pmac_stream_t *rec)
+{
+	int timeout = 50000;
+	while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0)
+		udelay(1);
+}
+
+/*
+ * set the format and rate to the chip.
+ * call the lowlevel function if defined (e.g. for AWACS).
+ */
+static void snd_pmac_pcm_set_format(pmac_t *chip)
+{
+	/* set up frequency and format */
+	out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8));
+	out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE);
+	if (chip->set_format)
+		chip->set_format(chip);
+}
+
+/*
+ * stop the DMA transfer
+ */
+inline static void snd_pmac_dma_stop(pmac_stream_t *rec)
+{
+	out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
+	snd_pmac_wait_ack(rec);
+}
+
+/*
+ * set the command pointer address
+ */
+inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd)
+{
+	out_le32(&rec->dma->cmdptr, cmd->addr);
+}
+
+/*
+ * start the DMA
+ */
+inline static void snd_pmac_dma_run(pmac_stream_t *rec, int status)
+{
+	out_le32(&rec->dma->control, status | (status << 16));
+}
+
+
+/*
+ * prepare playback/capture stream
+ */
+static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs)
+{
+	int i;
+	volatile struct dbdma_cmd *cp;
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	int rate_index;
+	long offset;
+	pmac_stream_t *astr;
+	
+	rec->dma_size = snd_pcm_lib_buffer_bytes(subs);
+	rec->period_size = snd_pcm_lib_period_bytes(subs);
+	rec->nperiods = rec->dma_size / rec->period_size;
+	rec->cur_period = 0;
+	rate_index = snd_pmac_rate_index(chip, rec, runtime->rate);
+
+	/* set up constraints */
+	astr = snd_pmac_get_stream(chip, another_stream(rec->stream));
+	snd_runtime_check(astr, return -EINVAL);
+	astr->cur_freqs = 1 << rate_index;
+	astr->cur_formats = 1 << runtime->format;
+	chip->rate_index = rate_index;
+	chip->format = runtime->format;
+
+	/* We really want to execute a DMA stop command, after the AWACS
+	 * is initialized.
+	 * For reasons I don't understand, it stops the hissing noise
+	 * common to many PowerBook G3 systems (like mine :-).
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_pmac_dma_stop(rec);
+	if (rec->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP);
+		snd_pmac_dma_set_command(rec, &chip->extra_dma);
+		snd_pmac_dma_run(rec, RUN);
+	}
+	offset = runtime->dma_addr;
+	for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) {
+		st_le32(&cp->phy_addr, offset);
+		st_le16(&cp->req_count, rec->period_size);
+		/*st_le16(&cp->res_count, 0);*/
+		st_le16(&cp->xfer_status, 0);
+		offset += rec->period_size;
+	}
+	/* make loop */
+	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
+	st_le32(&cp->cmd_dep, rec->cmd.addr);
+
+	snd_pmac_dma_stop(rec);
+	snd_pmac_dma_set_command(rec, &rec->cmd);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return 0;
+}
+
+
+/*
+ * stop beep if running (no spinlock!)
+ */
+static void snd_pmac_beep_stop(pmac_t *chip)
+{
+	pmac_beep_t *beep = chip->beep;
+
+	if (beep && beep->running) {
+		beep->running = 0;
+		del_timer(&beep->timer);
+		snd_pmac_dma_stop(&chip->playback);
+		st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP);
+	}
+}
+
+/*
+ * PCM trigger/stop
+ */
+static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec,
+				snd_pcm_substream_t *subs, int cmd)
+{
+	unsigned long flags;
+	volatile struct dbdma_cmd *cp;
+	int i, command;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (rec->running)
+			return -EBUSY;
+		command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			   OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS;
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_pmac_beep_stop(chip);
+		snd_pmac_pcm_set_format(chip);
+		for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
+			out_le16(&cp->command, command);
+		snd_pmac_dma_set_command(rec, &rec->cmd);
+		(void)in_le32(&rec->dma->status);
+		snd_pmac_dma_run(rec, RUN|WAKE);
+		rec->running = 1;
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		rec->running = 0;
+		/*printk("stopped!!\n");*/
+		snd_pmac_dma_stop(rec);
+		for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
+			out_le16(&cp->command, DBDMA_STOP);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * return the current pointer
+ */
+inline
+static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec,
+					      snd_pcm_substream_t *subs)
+{
+	int count;
+
+#if 0 /* hmm.. how can we get the current dma pointer?? */
+	if (! rec->cmd.cmds[rec->cur_period].xfer_status) {
+		count = in_le16(&rec->cmd.cmds[rec->cur_period].res_count);
+		count = rec->period_size - count;
+	} else
+#endif
+		count = 0;
+	count += rec->cur_period * rec->period_size;
+	/*printk("pointer=%d\n", count);*/
+	return bytes_to_frames(subs->runtime, count);
+}
+
+/*
+ * playback
+ */
+
+static int snd_pmac_playback_prepare(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+	return snd_pmac_pcm_prepare(chip, &chip->playback, subs);
+}
+
+static int snd_pmac_playback_trigger(snd_pcm_substream_t *subs,
+				     int cmd)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+	return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd);
+}
+
+static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+	return snd_pmac_pcm_pointer(chip, &chip->playback, subs);
+}
+
+
+/*
+ * capture
+ */
+
+static int snd_pmac_capture_prepare(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+	return snd_pmac_pcm_prepare(chip, &chip->capture, subs);
+}
+
+static int snd_pmac_capture_trigger(snd_pcm_substream_t *subs,
+				    int cmd)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+	return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd);
+}
+
+static snd_pcm_uframes_t snd_pmac_capture_pointer(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+	return snd_pmac_pcm_pointer(chip, &chip->capture, subs);
+}
+
+
+/*
+ * update playback/capture pointer from interrupts
+ */
+static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec)
+{
+	volatile struct dbdma_cmd *cp;
+	int c;
+	int stat;
+
+	spin_lock(&chip->reg_lock);
+	if (rec->running) {
+		cp = &rec->cmd.cmds[rec->cur_period];
+		for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */
+			stat = ld_le16(&cp->xfer_status);
+			if (! (stat & ACTIVE))
+				break;
+			/*printk("update frag %d\n", rec->cur_period);*/
+			st_le16(&cp->xfer_status, 0);
+			st_le16(&cp->req_count, rec->period_size);
+			/*st_le16(&cp->res_count, 0);*/
+			rec->cur_period++;
+			if (rec->cur_period >= rec->nperiods) {
+				rec->cur_period = 0;
+				cp = rec->cmd.cmds;
+			} else
+				cp++;
+			spin_unlock(&chip->reg_lock);
+			snd_pcm_period_elapsed(rec->substream);
+			spin_lock(&chip->reg_lock);
+		}
+	}
+	spin_unlock(&chip->reg_lock);
+}
+
+
+/*
+ * hw info
+ */
+
+static snd_pcm_hardware_t snd_pmac_playback =
+{
+	info:		(SNDRV_PCM_INFO_INTERLEAVED |
+			 SNDRV_PCM_INFO_MMAP |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_RESUME),
+	formats:	SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:		SNDRV_PCM_RATE_8000_44100,
+	rate_min:	7350,
+	rate_max:	44100,
+	channels_min:	2,
+	channels_max:	2,
+	buffer_bytes_max:	32768,
+	period_bytes_min:	256,
+	period_bytes_max:	16384,
+	periods_min:		1,
+	periods_max:		PMAC_MAX_FRAGS,
+};
+
+static snd_pcm_hardware_t snd_pmac_capture =
+{
+	info:		(SNDRV_PCM_INFO_INTERLEAVED |
+			 SNDRV_PCM_INFO_MMAP |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_RESUME),
+	formats:	SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:		SNDRV_PCM_RATE_8000_44100,
+	rate_min:	7350,
+	rate_max:	44100,
+	channels_min:	2,
+	channels_max:	2,
+	buffer_bytes_max:	32768,
+	period_bytes_min:	256,
+	period_bytes_max:	16384,
+	periods_min:		1,
+	periods_max:		PMAC_MAX_FRAGS,
+};
+
+
+#if 0 // NYI
+static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_rule_t *rule)
+{
+	pmac_t *chip = rule->private;
+	pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]);
+	int i, freq_table[8], num_freqs;
+
+	snd_runtime_check(rec, return -EINVAL);
+	num_freqs = 0;
+	for (i = chip->num_freqs - 1; i >= 0; i--) {
+		if (rec->cur_freqs & (1 << i))
+			freq_table[num_freqs++] = chip->freq_table[i];
+	}
+
+	return snd_interval_list(hw_param_interval(params, rule->var),
+				 num_freqs, freq_table, 0);
+}
+
+static int snd_pmac_hw_rule_format(snd_pcm_hw_params_t *params,
+				   snd_pcm_hw_rule_t *rule)
+{
+	pmac_t *chip = rule->private;
+	pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]);
+
+	snd_runtime_check(rec, return -EINVAL);
+	return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+				   rec->cur_formats);
+}
+#endif // NYI
+
+static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs)
+{
+	snd_pcm_runtime_t *runtime = subs->runtime;
+	int i, j, fflags;
+	static int typical_freqs[] = {
+		48000,
+		44100,
+		22050,
+		11025,
+		0,
+	};
+	static int typical_freq_flags[] = {
+		SNDRV_PCM_RATE_48000,
+		SNDRV_PCM_RATE_44100,
+		SNDRV_PCM_RATE_22050,
+		SNDRV_PCM_RATE_11025,
+		0,
+	};
+
+	/* look up frequency table and fill bit mask */
+	runtime->hw.rates = 0;
+	fflags = chip->freqs_ok;
+	for (i = 0; typical_freqs[i]; i++) {
+		for (j = 0; j < chip->num_freqs; j++) {
+			if ((chip->freqs_ok & (1 << j)) &&
+			    chip->freq_table[j] == typical_freqs[i]) {
+				runtime->hw.rates |= typical_freq_flags[i];
+				fflags &= ~(1 << j);
+				break;
+			}
+		}
+	}
+	if (fflags) /* rest */
+		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+
+	/* check for minimum and maximum rates */
+	for (i = 0; i < chip->num_freqs; i++) {
+		if (chip->freqs_ok & (1 << i)) {
+			runtime->hw.rate_max = chip->freq_table[i];
+			break;
+		}
+	}
+	for (i = chip->num_freqs - 1; i >= 0; i--) {
+		if (chip->freqs_ok & (1 << i)) {
+			runtime->hw.rate_min = chip->freq_table[i];
+			break;
+		}
+	}
+	runtime->hw.formats = chip->formats_ok;
+	if (chip->can_duplex)
+		runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+	runtime->private_data = rec;
+	rec->substream = subs;
+
+#if 0 /* FIXME: still under development.. */
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+			    snd_pmac_hw_rule_rate, chip, rec->stream, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+			    snd_pmac_hw_rule_format, chip, rec->stream, -1);
+#endif
+
+	runtime->hw.periods_max = rec->cmd.size - 1;
+
+	if (chip->can_duplex)
+		snd_pcm_set_sync(subs);
+
+	return 0;
+}
+
+static int snd_pmac_pcm_close(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs)
+{
+	pmac_stream_t *astr;
+
+	snd_pmac_dma_stop(rec);
+
+	astr = snd_pmac_get_stream(chip, another_stream(rec->stream));
+	snd_runtime_check(astr, return -EINVAL);
+
+	/* reset constraints */
+	astr->cur_freqs = chip->freqs_ok;
+	astr->cur_formats = chip->formats_ok;
+	
+	return 0;
+}
+
+static int snd_pmac_playback_open(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+
+	subs->runtime->hw = snd_pmac_playback;
+	return snd_pmac_pcm_open(chip, &chip->playback, subs);
+}
+
+static int snd_pmac_capture_open(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+
+	subs->runtime->hw = snd_pmac_capture;
+	return snd_pmac_pcm_open(chip, &chip->capture, subs);
+}
+
+static int snd_pmac_playback_close(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+
+	return snd_pmac_pcm_close(chip, &chip->playback, subs);
+}
+
+static int snd_pmac_capture_close(snd_pcm_substream_t *subs)
+{
+	pmac_t *chip = snd_pcm_substream_chip(subs);
+
+	return snd_pmac_pcm_close(chip, &chip->capture, subs);
+}
+
+/*
+ */
+
+static snd_pcm_ops_t snd_pmac_playback_ops = {
+	open:		snd_pmac_playback_open,
+	close:		snd_pmac_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_pmac_pcm_hw_params,
+	hw_free:	snd_pmac_pcm_hw_free,
+	prepare:	snd_pmac_playback_prepare,
+	trigger:	snd_pmac_playback_trigger,
+	pointer:	snd_pmac_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_pmac_capture_ops = {
+	open:		snd_pmac_capture_open,
+	close:		snd_pmac_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_pmac_pcm_hw_params,
+	hw_free:	snd_pmac_pcm_hw_free,
+	prepare:	snd_pmac_capture_prepare,
+	trigger:	snd_pmac_capture_trigger,
+	pointer:	snd_pmac_capture_pointer,
+};
+
+int __init snd_pmac_pcm_new(pmac_t *chip)
+{
+	snd_pcm_t *pcm;
+	int err;
+	int num_captures = 1;
+
+	if (! chip->can_capture)
+		num_captures = 0;
+	err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops);
+	if (chip->can_capture)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcm = pcm;
+
+	chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE;
+	if (chip->can_byte_swap)
+		chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE;
+
+	chip->playback.cur_formats = chip->formats_ok;
+	chip->capture.cur_formats = chip->formats_ok;
+	chip->playback.cur_freqs = chip->freqs_ok;
+	chip->capture.cur_freqs = chip->freqs_ok;
+
+	return 0;
+}
+
+
+
+/*
+ * beep stuff
+ */
+
+/*
+ * Stuff for outputting a beep.  The values range from -327 to +327
+ * so we can multiply by an amplitude in the range 0..100 to get a
+ * signed short value to put in the output buffer.
+ */
+static short beep_wform[256] = {
+	0,	40,	79,	117,	153,	187,	218,	245,
+	269,	288,	304,	316,	323,	327,	327,	324,
+	318,	310,	299,	288,	275,	262,	249,	236,
+	224,	213,	204,	196,	190,	186,	183,	182,
+	182,	183,	186,	189,	192,	196,	200,	203,
+	206,	208,	209,	209,	209,	207,	204,	201,
+	197,	193,	188,	183,	179,	174,	170,	166,
+	163,	161,	160,	159,	159,	160,	161,	162,
+	164,	166,	168,	169,	171,	171,	171,	170,
+	169,	167,	163,	159,	155,	150,	144,	139,
+	133,	128,	122,	117,	113,	110,	107,	105,
+	103,	103,	103,	103,	104,	104,	105,	105,
+	105,	103,	101,	97,	92,	86,	78,	68,
+	58,	45,	32,	18,	3,	-11,	-26,	-41,
+	-55,	-68,	-79,	-88,	-95,	-100,	-102,	-102,
+	-99,	-93,	-85,	-75,	-62,	-48,	-33,	-16,
+	0,	16,	33,	48,	62,	75,	85,	93,
+	99,	102,	102,	100,	95,	88,	79,	68,
+	55,	41,	26,	11,	-3,	-18,	-32,	-45,
+	-58,	-68,	-78,	-86,	-92,	-97,	-101,	-103,
+	-105,	-105,	-105,	-104,	-104,	-103,	-103,	-103,
+	-103,	-105,	-107,	-110,	-113,	-117,	-122,	-128,
+	-133,	-139,	-144,	-150,	-155,	-159,	-163,	-167,
+	-169,	-170,	-171,	-171,	-171,	-169,	-168,	-166,
+	-164,	-162,	-161,	-160,	-159,	-159,	-160,	-161,
+	-163,	-166,	-170,	-174,	-179,	-183,	-188,	-193,
+	-197,	-201,	-204,	-207,	-209,	-209,	-209,	-208,
+	-206,	-203,	-200,	-196,	-192,	-189,	-186,	-183,
+	-182,	-182,	-183,	-186,	-190,	-196,	-204,	-213,
+	-224,	-236,	-249,	-262,	-275,	-288,	-299,	-310,
+	-318,	-324,	-327,	-327,	-323,	-316,	-304,	-288,
+	-269,	-245,	-218,	-187,	-153,	-117,	-79,	-40,
+};
+
+#define BEEP_SRATE	22050	/* 22050 Hz sample rate */
+#define BEEP_BUFLEN	512
+#define BEEP_VOLUME	15	/* 0 - 100 */
+
+static void snd_pmac_beep_stop_callback(unsigned long data)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, (void*)data,);
+
+	spin_lock(&chip->reg_lock);
+	snd_pmac_beep_stop(chip);
+	snd_pmac_pcm_set_format(chip);
+	spin_unlock(&chip->reg_lock);
+}
+
+/* because mksound callback takes no private argument, we must keep
+   the chip pointer here as static variable.
+   This means that only one chip can beep.  Well, it's ok -
+   anyway we don't like hearing loud beeps from every chip
+   at the same time :)
+*/
+   
+static pmac_t *beeping_chip = NULL;
+
+static void snd_pmac_mksound(unsigned int hz, unsigned int ticks)
+{
+	pmac_t *chip;
+	pmac_stream_t *rec;
+	pmac_beep_t *beep;
+	unsigned long flags;
+	int beep_speed = 0;
+	int srate;
+	int period, ncycles, nsamples;
+	int i, j, f;
+	short *p;
+
+	if ((chip = beeping_chip) == NULL || (beep = chip->beep) == NULL)
+		return;
+	rec = &chip->playback;
+
+	beep_speed = snd_pmac_rate_index(chip, rec, BEEP_SRATE);
+	srate = chip->freq_table[beep_speed];
+
+	if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
+		/* this is a hack for broken X server code */
+		hz = 750;
+		ticks = 12;
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->playback.running || chip->capture.running || beep->running) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	beep->running = 1;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	if (hz == beep->hz && beep->volume == beep->volume_play) {
+		nsamples = beep->nsamples;
+	} else {
+		period = srate * 256 / hz;	/* fixed point */
+		ncycles = BEEP_BUFLEN * 256 / period;
+		nsamples = (period * ncycles) >> 8;
+		f = ncycles * 65536 / nsamples;
+		j = 0;
+		p = beep->buf;
+		for (i = 0; i < nsamples; ++i, p += 2) {
+			p[0] = p[1] = beep_wform[j >> 8] * beep->volume;
+			j = (j + f) & 0xffff;
+		}
+		beep->hz = hz;
+		beep->volume_play = beep->volume;
+		beep->nsamples = nsamples;
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (beep->running) {
+		if (ticks <= 0)
+			ticks = 1;
+		del_timer(&beep->timer);
+		beep->timer.expires = jiffies + ticks;
+		beep->timer.function = snd_pmac_beep_stop_callback;
+		beep->timer.data = (unsigned long)chip;
+		add_timer(&beep->timer);
+		snd_pmac_dma_stop(rec);
+		st_le16(&chip->extra_dma.cmds->req_count, nsamples * 4);
+		st_le16(&chip->extra_dma.cmds->xfer_status, 0);
+		st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr);
+		st_le32(&chip->extra_dma.cmds->phy_addr, beep->addr);
+		st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS);
+		out_le32(&chip->awacs->control,
+			 (in_le32(&chip->awacs->control) & ~0x1f00)
+			 | (beep_speed << 8));
+		out_le32(&chip->awacs->byteswap, 0);
+		snd_pmac_dma_set_command(rec, &chip->extra_dma);
+		snd_pmac_dma_run(rec, RUN);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ * beep volume mixer
+ */
+static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 100;
+	return 0;
+}
+
+static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	snd_runtime_check(chip->beep, return -ENXIO);
+	ucontrol->value.integer.value[0] = chip->beep->volume;
+	return 0;
+}
+
+static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	int oval;
+	snd_runtime_check(chip->beep, return -ENXIO);
+	oval = chip->beep->volume;
+	chip->beep->volume = ucontrol->value.integer.value[0];
+	return oval != chip->beep->volume;
+}
+
+static snd_kcontrol_new_t snd_pmac_beep_mixer = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Beep Playback Volume",
+	index: 0,
+	info: snd_pmac_info_beep,
+	get: snd_pmac_get_beep,
+	put: snd_pmac_put_beep,
+};
+
+static void snd_pmac_beep_free(snd_kcontrol_t *control)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, _snd_kcontrol_chip(control),);
+	if (chip->beep) {
+		/* restore */
+		kd_mksound = chip->beep->orig_mksound;
+		kfree(chip->beep->buf);
+		kfree(chip->beep);
+		chip->beep = NULL;
+	}
+}
+
+/* Initialize beep stuff */
+int __init snd_pmac_attach_beep(pmac_t *chip)
+{
+	pmac_beep_t *beep;
+	int err;
+
+	beep = kmalloc(sizeof(*beep), GFP_KERNEL);
+	if (! beep)
+		return -ENOMEM;
+
+	beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
+	if (! beep->buf) {
+		kfree(beep);
+		return -ENOMEM;
+	}
+	beep->addr = virt_to_bus(beep->buf);
+	init_timer(&beep->timer);
+	beep->timer.function = snd_pmac_beep_stop_callback;
+	beep->timer.data = (unsigned long) chip;
+	beep->orig_mksound = kd_mksound;
+	beep->volume = BEEP_VOLUME;
+	beep->running = 0;
+	beep->control = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
+	if (beep->control == NULL) {
+		kfree(beep);
+		return -ENOMEM;
+	}
+	beep->control->private_free = snd_pmac_beep_free;
+	if ((err = snd_ctl_add(chip->card, beep->control)) < 0) {
+		kfree(beep);
+		return err;
+	}
+
+	/* hook */
+	beeping_chip = chip;
+	chip->beep = beep;
+	kd_mksound = snd_pmac_mksound;
+
+	return 0;
+}
+
+static void snd_pmac_dbdma_reset(pmac_t *chip)
+{
+	out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	snd_pmac_wait_ack(&chip->playback);
+	out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	snd_pmac_wait_ack(&chip->capture);
+}
+
+
+/*
+ * interrupt handlers
+ */
+static void
+snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, devid, return);
+	snd_pmac_pcm_update(chip, &chip->playback);
+}
+
+
+static void
+snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, devid, return);
+	snd_pmac_pcm_update(chip, &chip->capture);
+}
+
+
+static void
+snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, devid, return);
+	int ctrl = in_le32(&chip->awacs->control);
+
+	printk("pmac: control interrupt.. 0x%x\n", ctrl);
+	if (ctrl & MASK_PORTCHG) {
+		/* do something when headphone is plugged/unplugged? */
+		if (chip->port_change)
+			chip->port_change(chip);
+	}
+	if (ctrl & MASK_CNTLERR) {
+		int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16;
+		if (err && chip->model <= PMAC_SCREAMER)
+			snd_printk("error %x\n", err);
+	}
+	/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
+	out_le32(&chip->awacs->control, ctrl);
+}
+
+
+/*
+ * feature
+ */
+#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK)
+static void snd_pmac_sound_feature(pmac_t *chip, int enable)
+{
+#ifdef CONFIG_PPC_HAS_FEATURE_CALLS
+	ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable);
+#else
+	if (chip->is_pbook_G3) {
+		pmu_suspend();
+		feature_clear(chip->node, FEATURE_Sound_power);
+		feature_clear(chip->node, FEATURE_Sound_CLK_enable);
+		mdelay(1000); /* XXX */
+		pmu_resume();
+	}
+	if (chip->is_pbook_3400) {
+		feature_set(chip->node, FEATURE_IOBUS_enable);
+		udelay(10);
+	}
+#endif
+}
+#else /* CONFIG_PM && CONFIG_PMAC_PBOOK */
+#define snd_pmac_sound_feature(chip,enable) /**/
+#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */
+
+/*
+ * release resources
+ */
+
+static int snd_pmac_free(pmac_t *chip)
+{
+	/* stop sounds */
+	if (chip->initialized) {
+		snd_pmac_dbdma_reset(chip);
+		/* disable interrupts from awacs interface */
+		out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff);
+	}
+
+	snd_pmac_sound_feature(chip, 0);
+#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK)
+	snd_pmac_unregister_sleep_notifier(chip);
+#endif
+
+	/* clean up mixer if any */
+	if (chip->mixer_free)
+		chip->mixer_free(chip);
+
+	/* release resources */
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void*)chip);
+	if (chip->tx_irq >= 0)
+		free_irq(chip->tx_irq, (void*)chip);
+	if (chip->rx_irq >= 0)
+		free_irq(chip->rx_irq, (void*)chip);
+	snd_pmac_dbdma_free(&chip->playback.cmd);
+	snd_pmac_dbdma_free(&chip->capture.cmd);
+	snd_pmac_dbdma_free(&chip->extra_dma);
+	if (chip->macio_base)
+		iounmap(chip->macio_base);
+	if (chip->latch_base)
+		iounmap(chip->latch_base);
+	if (chip->awacs)
+		iounmap((void*)chip->awacs);
+	if (chip->playback.dma)
+		iounmap((void*)chip->playback.dma);
+	if (chip->capture.dma)
+		iounmap((void*)chip->capture.dma);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+
+/*
+ * free the device
+ */
+static int snd_pmac_dev_free(snd_device_t *device)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, device->device_data, return -ENXIO);
+	return snd_pmac_free(chip);
+}
+
+
+/*
+ * detect a sound chip
+ */
+static int __init snd_pmac_detect(pmac_t *chip)
+{
+	struct device_node *sound;
+	unsigned int *prop, l;
+
+	if (_machine != _MACH_Pmac)
+		return -ENODEV;
+
+	chip->subframe = 0;
+	chip->revision = 0;
+	chip->freqs_ok = 0xff; /* all ok */
+	chip->model = PMAC_AWACS;
+	chip->can_byte_swap = 1;
+	chip->can_duplex = 1;
+	chip->can_capture = 1;
+	chip->num_freqs = 8;
+	chip->freq_table = awacs_freqs;
+
+	chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */
+
+	/* it seems the Pismo & iBook can't byte-swap in hardware. */
+	if (machine_is_compatible("PowerBook3,1") ||
+	    machine_is_compatible("PowerBook2,1"))
+		chip->can_byte_swap = 0 ;
+	if (machine_is_compatible("PowerBook2,1"))
+		chip->can_duplex = 0;
+
+	/* check machine type */
+	if (machine_is_compatible("AAPL,3400/2400")
+	    || machine_is_compatible("AAPL,3500"))
+		chip->is_pbook_3400 = 1;
+	else if (machine_is_compatible("PowerBook1,1")
+		 || machine_is_compatible("AAPL,PowerBook1998"))
+		chip->is_pbook_G3 = 1;
+	chip->node = find_devices("awacs");
+	if (chip->node)
+		return 0; /* ok */
+
+	/*
+	 * powermac G3 models have a node called "davbus"
+	 * with a child called "sound".
+	 */
+	chip->node = find_devices("davbus");
+	/*
+	 * if we didn't find a davbus device, try 'i2s-a' since
+	 * this seems to be what iBooks have
+	 */
+	if (! chip->node)
+		chip->node = find_devices("i2s-a");
+	if (! chip->node)
+		return -ENODEV;
+	sound = find_devices("sound");
+	while (sound && sound->parent != chip->node)
+		sound = sound->next;
+	if (! sound)
+		return -ENODEV;
+	prop = (unsigned int *) get_property(sound, "sub-frame", 0);
+	if (prop && *prop < 16)
+		chip->subframe = *prop;
+	/* This should be verified on older screamers */
+	if (device_is_compatible(sound, "screamer")) {
+		chip->model = PMAC_SCREAMER;
+		chip->can_byte_swap = 0;
+	}
+	if (device_is_compatible(sound, "burgundy")) {
+		chip->model = PMAC_BURGUNDY;
+		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
+	}
+	if (device_is_compatible(sound, "daca")) {
+		chip->model = PMAC_DACA;
+		chip->can_capture = 0;  /* no capture */
+		chip->can_duplex = 0;
+		chip->can_byte_swap = 0; /* no le support */
+		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
+	}
+	if (device_is_compatible(sound, "tumbler")) {
+		chip->model = PMAC_TUMBLER;
+		chip->can_capture = 0;  /* no capture */
+		chip->can_duplex = 0;
+		chip->can_byte_swap = 0; /* no le support */
+		chip->num_freqs = 2;
+		chip->freq_table = tumbler_freqs;
+		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
+	}
+	prop = (unsigned int *)get_property(sound, "device-id", 0);
+	if (prop)
+		chip->device_id = *prop;
+	chip->has_iic = (find_devices("perch") != NULL);
+
+	/* look for a property saying what sample rates
+	   are available */
+	prop = (unsigned int *) get_property(sound, "sample-rates", &l);
+	if (! prop)
+		prop = (unsigned int *) get_property(sound, "output-frame-rates", &l);
+	if (prop) {
+		int i;
+		chip->freqs_ok = 0;
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			for (i = 0; i < chip->num_freqs; ++i) {
+				if (r == chip->freq_table[i]) {
+					chip->freqs_ok |= (1 << i);
+					break;
+				}
+			}
+		}
+	} else {
+		/* assume only 44.1khz */
+		chip->freqs_ok = 1;
+	}
+	return 0;
+}
+
+
+/*
+ * create and detect a pmac chip record
+ */
+int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return)
+{
+	pmac_t *chip;
+	struct device_node *np;
+	int err;
+	static snd_device_ops_t ops = {
+		dev_free:	snd_pmac_dev_free,
+	};
+
+	snd_runtime_check(chip_return, return -EINVAL);
+	*chip_return = NULL;
+
+	chip = snd_magic_kcalloc(pmac_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->card = card;
+
+	spin_lock_init(&chip->reg_lock);
+	chip->irq = chip->tx_irq = chip->rx_irq = -1;
+
+	chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK;
+	chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE;
+
+	if ((err = snd_pmac_detect(chip)) < 0)
+		goto __error;
+
+	if (snd_pmac_dbdma_alloc(&chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
+	    snd_pmac_dbdma_alloc(&chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
+	    snd_pmac_dbdma_alloc(&chip->extra_dma, 2) < 0) {
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	np = chip->node;
+	if (np->n_addrs < 3 || np->n_intrs < 3) {
+		err = -ENODEV;
+		goto __error;
+	}
+
+	chip->awacs = (volatile struct awacs_regs *) ioremap(np->addrs[0].address, 0x1000);
+	chip->playback.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[1].address, 0x100);
+	chip->capture.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[2].address, 0x100);
+	if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0,
+			"PMac", (void*)chip)) {
+		snd_printk("unable to grab IRQ %d\n", np->intrs[0].line);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->irq = np->intrs[0].line;
+	if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0,
+			"PMac Output", (void*)chip)) {
+		snd_printk("unable to grab IRQ %d\n", np->intrs[1].line);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->tx_irq = np->intrs[1].line;
+	if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0,
+			"PMac Input", (void*)chip)) {
+		snd_printk("unable to grab IRQ %d\n", np->intrs[2].line);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->rx_irq = np->intrs[2].line;
+
+	snd_pmac_sound_feature(chip, 1);
+
+	/* reset */
+	out_le32(&chip->awacs->control, 0x11);
+
+	/* Powerbooks have odd ways of enabling inputs such as
+	   an expansion-bay CD or sound from an internal modem
+	   or a PC-card modem. */
+	if (chip->is_pbook_3400) {
+		/* Enable CD and PC-card sound inputs. */
+		/* This is done by reading from address
+		 * f301a000, + 0x10 to enable the expansion-bay
+		 * CD sound input, + 0x80 to enable the PC-card
+		 * sound input.  The 0x100 enables the SCSI bus
+		 * terminator power.
+		 */
+		chip->latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000);
+		in_8(chip->latch_base + 0x190);
+	} else if (chip->is_pbook_G3) {
+		struct device_node* mio;
+		for (mio = chip->node->parent; mio; mio = mio->parent) {
+			if (strcmp(mio->name, "mac-io") == 0
+			    && mio->n_addrs > 0) {
+				chip->macio_base = (unsigned char *) ioremap
+					(mio->addrs[0].address, 0x40);
+				break;
+			}
+		}
+		/* Enable CD sound input. */
+		/* The relevant bits for writing to this byte are 0x8f.
+		 * I haven't found out what the 0x80 bit does.
+		 * For the 0xf bits, writing 3 or 7 enables the CD
+		 * input, any other value disables it.  Values
+		 * 1, 3, 5, 7 enable the microphone.  Values 0, 2,
+		 * 4, 6, 8 - f enable the input from the modem.
+		 */
+		if (chip->macio_base)
+			out_8(chip->macio_base + 0x37, 3);
+	}
+
+	/* Reset dbdma channels */
+	snd_pmac_dbdma_reset(chip);
+
+#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK)
+	/* add sleep notifier */
+	snd_pmac_register_sleep_notifier(chip);
+	card->set_power_state = snd_pmac_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	chip->initialized = 1;
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+		goto __error;
+
+	*chip_return = chip;
+	return 0;
+
+ __error:
+	snd_pmac_free(chip);
+	return err;
+}
+
+
+/*
+ * sleep notify for powerbook
+ */
+
+#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK)
+
+/*
+ * Save state when going to sleep, restore it afterwards.
+ */
+
+static void snd_pmac_suspend(pmac_t *chip, int can_schedule)
+{
+	unsigned long flags;
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card, can_schedule);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	if (chip->suspend)
+		chip->suspend(chip);
+	snd_pcm_suspend_all(chip->pcm);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->beep && chip->beep->running)
+		snd_pmac_beep_stop(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	disable_irq(chip->irq);
+	disable_irq(chip->tx_irq);
+	disable_irq(chip->rx_irq);
+	snd_pmac_sound_feature(chip, 0);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+static void snd_pmac_resume(pmac_t *chip, int can_schedule)
+{
+	snd_card_t *card = chip->card;
+
+	snd_power_lock(card, can_schedule);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	snd_pmac_sound_feature(chip, 1);
+	if (chip->resume)
+		chip->resume(chip);
+	/* enable CD sound input */
+	if (chip->macio_base && chip->is_pbook_G3) {
+		out_8(chip->macio_base + 0x37, 3);
+	} else if (chip->is_pbook_3400) {
+		in_8(chip->latch_base + 0x190);
+	}
+
+	snd_pmac_pcm_set_format(chip);
+
+	enable_irq(chip->irq);
+	enable_irq(chip->tx_irq);
+	enable_irq(chip->rx_irq);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+/* the chip is stored statically by snd_pmac_register_sleep_notifier
+ * because we can't have any private data for notify callback.
+ */
+static pmac_t *sleeping_pmac = NULL;
+
+static int snd_pmac_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	pmac_t *chip;
+
+	chip = sleeping_pmac;
+	snd_runtime_check(chip, return 0);
+
+	switch (when) {
+	case PBOOK_SLEEP_NOW:
+		snd_pmac_suspend(chip, 0);
+		break;
+	case PBOOK_WAKE:
+		snd_pmac_resume(chip, 0);
+		break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+
+static struct pmu_sleep_notifier snd_pmac_sleep_notifier = {
+	snd_pmac_sleep_notify, SLEEP_LEVEL_SOUND,
+};
+
+static int __init snd_pmac_register_sleep_notifier(pmac_t *chip)
+{
+	/* should be protected here.. */
+	if (sleeping_pmac) {
+		snd_printd("sleep notifier already reigistered\n");
+		return -EBUSY;
+	}
+	sleeping_pmac = chip;
+	pmu_register_sleep_notifier(&snd_pmac_sleep_notifier);
+	chip->sleep_registered = 1;
+	return 0;
+}
+						    
+static int snd_pmac_unregister_sleep_notifier(pmac_t *chip)
+{
+	if (! chip->sleep_registered)
+		return 0;
+	/* should be protected here.. */
+	if (sleeping_pmac != chip)
+		return -ENODEV;
+	pmu_unregister_sleep_notifier(&snd_pmac_sleep_notifier);
+	sleeping_pmac = NULL;
+	return 0;
+}
+
+/* callback */
+static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, card->power_state_private_data, return -ENXIO);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		snd_pmac_resume(chip, 1);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		snd_pmac_suspend(chip, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */
diff -Nru linux/sound/ppc/pmac.h linux-2.4.19-pre5-mjc/sound/ppc/pmac.h
--- linux/sound/ppc/pmac.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/pmac.h	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,220 @@
+/*
+ * Driver for PowerMac onboard soundchips
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   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
+ */
+
+
+#ifndef __PMAC_H
+#define __PMAC_H
+
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include "awacs.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#include <asm/adb.h>
+#include <asm/cuda.h>
+#include <asm/pmu.h>
+#else /* 2.4.0 kernel */
+#include <linux/adb.h>
+#ifdef CONFIG_ADB_CUDA
+#include <linux/cuda.h>
+#endif
+#ifdef CONFIG_ADB_PMU
+#include <linux/pmu.h>
+#endif
+#endif
+#include <linux/nvram.h>
+#include <linux/vt_kern.h>
+#include <asm/dbdma.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) || defined(CONFIG_ADB_CUDA)
+#define PMAC_AMP_AVAIL
+#endif
+
+/* maximum number of fragments */
+#define PMAC_MAX_FRAGS		32
+
+
+/*
+ * typedefs
+ */
+typedef struct snd_pmac pmac_t;
+typedef struct snd_pmac_stream pmac_stream_t;
+typedef struct snd_pmac_beep pmac_beep_t;
+typedef struct snd_pmac_dbdma pmac_dbdma_t;
+
+
+/*
+ * DBDMA space
+ */
+struct snd_pmac_dbdma {
+	unsigned long addr;
+	struct dbdma_cmd *cmds;
+	void *space;
+	int size;
+};
+
+/*
+ * playback/capture stream
+ */
+struct snd_pmac_stream {
+	int running;	/* boolean */
+
+	int stream;	/* PLAYBACK/CAPTURE */
+
+	int dma_size; /* in bytes */
+	int period_size; /* in bytes */
+	int buffer_size; /* in kbytes */
+	int nperiods, cur_period;
+
+	pmac_dbdma_t cmd;
+	volatile struct dbdma_regs *dma;
+
+	snd_pcm_substream_t *substream;
+
+	unsigned int cur_freqs;		/* currently available frequences */
+	unsigned int cur_formats;	/* currently available formats */
+};
+
+
+/*
+ * beep using pcm
+ */
+struct snd_pmac_beep {
+	int running;	/* boolean */
+	int volume;	/* mixer volume: 0-100 */
+	int volume_play;	/* currently playing volume */
+	int hz;
+	int nsamples;
+	short *buf;		/* allocated wave buffer */
+	unsigned long addr;	/* physical address of buffer */
+	struct timer_list timer;	/* timer list for stopping beep */
+	void (*orig_mksound)(unsigned int, unsigned int);
+				/* pointer to restore */
+	snd_kcontrol_t *control;	/* mixer element */
+};
+
+
+/*
+ */
+
+enum snd_pmac_model {
+	PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER
+};
+
+struct snd_pmac {
+	snd_card_t *card;
+
+	/* h/w info */
+	struct device_node *node;
+	unsigned int revision;
+	unsigned int subframe;
+	unsigned int device_id;
+	enum snd_pmac_model model;
+
+	unsigned int has_iic : 1;
+	unsigned int is_pbook_3400 : 1;
+	unsigned int is_pbook_G3 : 1;
+
+	unsigned int can_byte_swap : 1;
+	unsigned int can_duplex : 1;
+	unsigned int can_capture : 1;
+
+#ifdef PMAC_AMP_AVAIL
+	unsigned int amp_only;
+	int amp_vol[2];
+#endif
+
+	unsigned int initialized : 1;
+	unsigned int feature_is_set : 1;
+
+	int num_freqs;
+	int *freq_table;
+	unsigned int freqs_ok;		/* bit flags */
+	unsigned int formats_ok;	/* pcm hwinfo */
+	int active;
+	int rate_index;
+	int format;			/* current format */
+
+	spinlock_t reg_lock;
+	volatile struct awacs_regs *awacs;
+	int awacs_reg[8]; /* register cache */
+
+	unsigned char *latch_base;
+	unsigned char *macio_base;
+
+	pmac_stream_t playback;
+	pmac_stream_t capture;
+
+	pmac_dbdma_t extra_dma;
+
+	int irq, tx_irq, rx_irq;
+
+	snd_pcm_t *pcm;
+
+	pmac_beep_t *beep;
+
+	unsigned int control_mask;	/* control mask */
+
+	/* mixer stuffs */
+	void *mixer_data;
+	void (*mixer_free)(pmac_t *);
+
+	/* lowlevel callbacks */
+	void (*set_format)(pmac_t *chip);
+	void (*port_change)(pmac_t *chip);
+#ifdef CONFIG_PMAC_PBOOK
+	unsigned int sleep_registered : 1;
+	void (*suspend)(pmac_t *chip);
+	void (*resume)(pmac_t *chip);
+#endif
+
+};
+
+
+/* exported functions */
+int snd_pmac_new(snd_card_t *card, pmac_t **chip_return);
+int snd_pmac_pcm_new(pmac_t *chip);
+int snd_pmac_attach_beep(pmac_t *chip);
+
+/* initialize mixer */
+int snd_pmac_awacs_init(pmac_t *chip);
+int snd_pmac_burgundy_init(pmac_t *chip);
+int snd_pmac_daca_init(pmac_t *chip);
+int snd_pmac_tumbler_init(pmac_t *chip);
+
+/* i2c functions */
+typedef struct snd_pmac_keywest {
+	unsigned long base;
+	int addr;
+	int steps;
+} pmac_keywest_t;
+
+int snd_pmac_keywest_find(pmac_t *chip, pmac_keywest_t *i2c, int addr, int (*init_client)(pmac_t *, pmac_keywest_t *));
+void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c);
+int snd_pmac_keywest_write(pmac_keywest_t *i2c, unsigned char cmd, int len, unsigned char *data);
+inline static int snd_pmac_keywest_write_byte(pmac_keywest_t *i2c, unsigned char cmd, unsigned char data)
+{
+	return snd_pmac_keywest_write(i2c, cmd, 1, &data);
+}
+
+
+#endif /* __PMAC_H */
diff -Nru linux/sound/ppc/powermac.c linux-2.4.19-pre5-mjc/sound/ppc/powermac.c
--- linux/sound/ppc/powermac.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/powermac.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,187 @@
+/*
+ * Driver for PowerMac AWACS
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include "pmac.h"
+#include "awacs.h"
+#include "burgundy.h"
+
+#define CHIP_NAME "PMac"
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("PowerMac");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Apple,PowerMac}}");
+MODULE_LICENSE("GPL");
+
+static int snd_index = SNDRV_DEFAULT_IDX1;		/* Index 0-MAX */
+static char *snd_id = SNDRV_DEFAULT_STR1;		/* ID for this card */
+static int snd_enable = 1;
+static int snd_enable_beep = 1;
+
+MODULE_PARM(snd_index, "i");
+MODULE_PARM_DESC(snd_index, "Index value for " CHIP_NAME " soundchip.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "s");
+MODULE_PARM_DESC(snd_id, "ID string for " CHIP_NAME " soundchip.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "i");
+MODULE_PARM_DESC(snd_enable, "Enable this soundchip.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_enable_beep, "i");
+MODULE_PARM_DESC(snd_enable_beep, "Enable beep using PCM.");
+MODULE_PARM_SYNTAX(snd_enable_beep, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+
+
+/*
+ * card entry
+ */
+
+static snd_card_t *snd_pmac_card = NULL;
+
+/*
+ */
+
+static int __init snd_pmac_probe(void)
+{
+	snd_card_t *card;
+	pmac_t *chip;
+	char *name_ext;
+	int err;
+
+	card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_pmac_new(card, &chip)) < 0)
+		goto __error;
+
+	switch (chip->model) {
+	case PMAC_BURGUNDY:
+		strcpy(card->driver, "PMac Burgundy");
+		strcpy(card->shortname, "PowerMac Burgundy");
+		sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
+			card->shortname, chip->device_id, chip->subframe);
+		if ((err = snd_pmac_burgundy_init(chip)) < 0)
+			goto __error;
+		break;
+	case PMAC_DACA:
+		strcpy(card->driver, "PMac DACA");
+		strcpy(card->shortname, "PowerMac DACA");
+		sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
+			card->shortname, chip->device_id, chip->subframe);
+		if ((err = snd_pmac_daca_init(chip)) < 0)
+			goto __error;
+		break;
+	case PMAC_TUMBLER:
+		strcpy(card->driver, "PMac Tumbler");
+		strcpy(card->shortname, "PowerMac Tumbler");
+		sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
+			card->shortname, chip->device_id, chip->subframe);
+		if ((err = snd_pmac_tumbler_init(chip)) < 0)
+			goto __error;
+		break;
+	case PMAC_AWACS:
+	case PMAC_SCREAMER:
+		name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS";
+		sprintf(card->driver, "PMac %s", name_ext);
+		sprintf(card->shortname, "PowerMac %s", name_ext);
+		if (chip->is_pbook_3400)
+			name_ext = " [PB3400]";
+		else if (chip->is_pbook_G3)
+			name_ext = " [PBG3]";
+		else
+			name_ext = "";
+		sprintf(card->longname, "%s%s Rev %d",
+			card->shortname, name_ext, chip->revision);
+		if ((err = snd_pmac_awacs_init(chip)) < 0)
+			goto __error;
+		break;
+	default:
+		snd_printk("unsupported hardware %d\n", chip->model);
+		err = -EINVAL;
+		goto __error;
+	}
+
+	if ((err = snd_pmac_pcm_new(chip)) < 0)
+		goto __error;
+	if (snd_enable_beep)
+		snd_pmac_attach_beep(chip);
+
+	if ((err = snd_card_register(card)) < 0)
+		goto __error;
+
+	snd_pmac_card = card;
+	return 0;
+
+__error:
+	snd_card_free(card);
+	return err;
+}
+
+
+/*
+ * MODULE sutff
+ */
+
+static int __init alsa_card_pmac_init(void)
+{
+	int err;
+	if ((err = snd_pmac_probe() < 0)) {
+#ifdef MODULE
+		printk(KERN_ERR "no PMac soundchip found\n");
+#endif
+		return err;
+	}
+	return 0;
+
+}
+
+static void __exit alsa_card_pmac_exit(void)
+{
+	if (snd_pmac_card)
+		snd_card_free(snd_pmac_card);
+}
+
+module_init(alsa_card_pmac_init)
+module_exit(alsa_card_pmac_exit)
+
+#ifndef MODULE
+
+/* format is: snd-pmac=snd_enable,snd_index,snd_id,snd_enable_beep
+ */
+
+static int __init alsa_card_pmac_setup(char *str)
+{
+	(void)(get_option(&str,&snd_enable) == 2 &&
+	       get_option(&str,&snd_index) == 2 &&
+	       get_id(&str,&snd_id) == 2 &&
+	       get_option(&str,&snd_enable_beep) == 2
+	       );
+	return 1;
+}
+
+__setup("snd-pmac=", alsa_card_pmac_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/ppc/tumbler.c linux-2.4.19-pre5-mjc/sound/ppc/tumbler.c
--- linux/sound/ppc/tumbler.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/ppc/tumbler.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,479 @@
+/*
+ * PMac Tumbler lowlevel functions
+ *
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include "pmac.h"
+
+// #define TUMBLER_TONE_CONTROL_SUPPORT
+
+#define chip_t pmac_t
+
+/* i2c address for tumbler */
+#define TAS_I2C_ADDR	0x34
+
+/* registers */
+#define TAS_REG_MCS	0x01
+#define TAS_REG_VOL	0x04
+#define TAS_VOL_MAX ((1<<20) - 1)
+
+#define TAS_REG_TREBLE	0x05
+#define TAS_VOL_MAX_TREBLE	0x96	/* 1 = max, 0x96 = min */
+#define TAS_REG_BASS	0x06
+#define TAS_VOL_MAX_BASS	0x86	/* 1 = max, 0x86 = min */
+
+#define TAS_MIXER_VOL_MAX	500
+
+typedef struct pmac_tumber_t {
+	pmac_keywest_t i2c;
+	void *amp_mute;
+	void *headphone_mute;
+	void *headphone_status;
+	int headphone_irq;
+	int left_vol, right_vol;
+	int bass_vol, treble_vol;
+} pmac_tumbler_t;
+
+
+/*
+ * initialize / detect tumbler
+ */
+static int tumbler_init_client(pmac_t *chip, pmac_keywest_t *i2c)
+{
+	/* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
+	return snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS,
+					   (1<<6)+(2<<4)+(2<<2)+0);
+}
+
+/*
+ * update volume
+ */
+static int tumbler_set_volume(pmac_tumbler_t *mix)
+{
+	unsigned char block[6];
+	unsigned int left_vol, right_vol;
+  
+	if (! mix->i2c.base)
+		return -ENODEV;
+  
+	left_vol = mix->left_vol << 6;
+	right_vol = mix->right_vol << 6;
+
+	if (left_vol > TAS_VOL_MAX)
+		left_vol = TAS_VOL_MAX;
+	if (right_vol > TAS_VOL_MAX)
+		right_vol = TAS_VOL_MAX;
+  
+	block[0] = (left_vol >> 16) & 0xff;
+	block[1] = (left_vol >> 8)  & 0xff;
+	block[2] = (left_vol >> 0)  & 0xff;
+
+	block[3] = (right_vol >> 16) & 0xff;
+	block[4] = (right_vol >> 8)  & 0xff;
+	block[5] = (right_vol >> 0)  & 0xff;
+  
+	if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_VOL, 6, block) < 0) {
+		snd_printk("failed to set volume \n");  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
+
+/* output volume */
+static int tumbler_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = TAS_MIXER_VOL_MAX;
+	return 0;
+}
+
+static int tumbler_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->left_vol;
+	ucontrol->value.integer.value[1] = mix->right_vol;
+	return 0;
+}
+
+static int tumbler_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->left_vol != ucontrol->value.integer.value[0] ||
+		mix->right_vol != ucontrol->value.integer.value[1];
+	if (change) {
+		mix->left_vol = ucontrol->value.integer.value[0];
+		mix->right_vol = ucontrol->value.integer.value[1];
+		tumbler_set_volume(mix);
+	}
+	return change;
+}
+
+
+#ifdef TUMBLER_TONE_CONTROL_SUPPORT
+static int tumbler_set_bass(pmac_tumbler_t *mix)
+{
+	unsigned char data;
+	int val;
+
+	if (! mix->i2c.base)
+		return -ENODEV;
+  
+	val = TAS_VOL_MAX_BASS - mix->bass_vol + 1;
+	if (val < 1)
+		data = 1;
+	else if (val > TAS_VOL_MAX_BASS)
+		data = TAS_VOL_MAX_BASS;
+	else
+		data = val;
+	if (snd_pmac_keywest_write(&mix->i2c TAS_REG_BASS, 1, &data) < 0) {
+		snd_printk("failed to set bass volume\n");  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
+static int tumbler_set_treble(pmac_tumbler_t *mix)
+{
+	unsigned char data;
+	int val;
+
+	if (! mix->i2c.base)
+		return -ENODEV;
+  
+	val = TAS_VOL_MAX_TREBLE - mix->treble_vol + 1;
+	if (val < 1)
+		data = 1;
+	else if (val > TAS_VOL_MAX_BASS)
+		data = TAS_VOL_MAX_BASS;
+	else
+		data = val;
+	if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_TREBLE, 1, &data) < 0) {
+		snd_printk("failed to set treble volume\n");  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
+/* bass volume */
+static int tumbler_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = TAS_VOL_MAX_BASS - 1;
+	return 0;
+}
+
+static int tumbler_get_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->bass_vol;
+	return 0;
+}
+
+static int tumbler_put_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->bass_vol != ucontrol->value.integer.value[0];
+	if (change) {
+		mix->bass_vol = ucontrol->value.integer.value[0];
+		tumbler_set_bass(mix);
+	}
+	return change;
+}
+
+static int tumbler_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = TAS_VOL_MAX_TREBLE - 1;
+	return 0;
+}
+
+static int tumbler_get_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->treble_vol;
+	return 0;
+}
+
+static int tumbler_put_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->treble_vol != ucontrol->value.integer.value[0];
+	if (change) {
+		mix->treble_vol = ucontrol->value.integer.value[0];
+		tumbler_set_treble(mix);
+	}
+	return change;
+}
+
+#endif
+
+
+static snd_kcontrol_new_t tumbler_mixers[] = {
+	{ iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  name: "Master Playback Volume",
+	  info: tumbler_info_volume,
+	  get: tumbler_get_volume,
+	  put: tumbler_put_volume
+	},
+#ifdef TUMBLER_TONE_CONTROL_SUPPORT
+	{ iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  name: "Tone Control - Bass",
+	  info: tumbler_info_bass,
+	  get: tumbler_get_bass,
+	  put: tumbler_put_bass
+	},
+	{ iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	  name: "Tone Control - Treble",
+	  info: tumbler_info_treble,
+	  get: tumbler_get_treble,
+	  put: tumbler_put_treble
+	},
+#endif
+};
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+/* mute either amp or headphone according to the plug status */
+static void tumbler_update_headphone(pmac_t *chip)
+{
+	pmac_tumbler_t *mix = chip->mixer_data;
+
+	if (! mix)
+		return;
+
+	if (readb(mix->headphone_status) & 2) {
+		writeb(4 + 1, mix->amp_mute);
+		writeb(4 + 0, mix->headphone_mute);
+	} else {
+		writeb(4 + 0, mix->amp_mute);
+		writeb(4 + 1, mix->headphone_mute);
+	}
+}
+
+/* interrupt - headphone plug changed */
+static void headphone_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	pmac_t *chip = snd_magic_cast(pmac_t, devid, return);
+	tumbler_update_headphone(chip);
+}
+
+/* look for audio-gpio device */
+static struct device_node *find_audio_device(const char *name)
+{
+	struct device_node *np;
+  
+	if (! (np = find_devices("gpio")))
+		return NULL;
+  
+	for (np = np->child; np; np = np->sibling) {
+		char *property = get_property(np, "audio-gpio", NULL);
+		if (property && strcmp(property, name) == 0)
+			return np;
+	}  
+	return NULL;
+}
+
+/* find an audio device and get its address */
+static unsigned long tumbler_find_device(const char *device)
+{
+	struct device_node *node;
+	void *base;
+
+	node = find_audio_device(device);
+	if (! node) {
+		snd_printd("cannot find device %s\n", device);
+		return 0;
+	}
+
+	base = (void *)get_property(node, "AAPL,address", NULL);
+	if (! base) {
+		snd_printd("cannot find address for device %s\n", device);
+		return 0;
+	}
+
+	return *(unsigned long *)base;
+}
+
+/* reset audio */
+static int tumbler_reset_audio(pmac_t *chip)
+{
+	unsigned long base;
+	void *map;
+
+	if (! (base = tumbler_find_device("audio-hw-reset")))
+		return -ENODEV;
+
+	map = ioremap(base, 1);
+	writeb(5, map);
+	mdelay(100);
+	writeb(4, map);
+	mdelay(1);
+	writeb(5, map);
+	mdelay(1);
+	iounmap(map);
+	return 0;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+/* resume mixer */
+static void tumbler_resume(pmac_t *chip)
+{
+	pmac_tumbler_t *mix = chip->mixer_data;
+	tumbler_reset_audio(chip);
+	snd_pmac_keywest_write_byte(&mix->i2c, TAS_REG_MCS,
+				    (1<<6)+(2<<4)+(2<<2)+0);
+	tumbler_set_volume(mix);
+	tumbler_update_headphone(chip); /* update mute */
+}
+#endif
+
+/* initialize tumbler */
+static int __init tumbler_init(pmac_t *chip)
+{
+	unsigned long base;
+	struct device_node *node;
+	int err;
+	pmac_tumbler_t *mix = chip->mixer_data;
+
+	snd_assert(mix, return -EINVAL);
+
+	/* reset audio */
+	if (tumbler_reset_audio(chip) < 0)
+		return -ENODEV;
+
+	/* get amp-mute */
+	if (! (base = tumbler_find_device("amp-mute")))
+		return -ENODEV;
+	mix->amp_mute = ioremap(base, 1);
+	if (! (base = tumbler_find_device("headphone-mute")))
+		return -ENODEV;
+	mix->headphone_mute = ioremap(base, 1);
+	if (! (base = tumbler_find_device("headphone-detect")))
+		return -ENODEV;
+	mix->headphone_status = ioremap(base, 1);
+
+	/* activate headphone status interrupts */
+	writeb(readb(mix->headphone_status) | (1<<7), mix->headphone_status);
+
+	if (! (node = find_audio_device("headphone-detect")))
+		return -ENODEV;
+	if (node->n_intrs == 0)
+		return -ENODEV;
+
+	if ((err = request_irq(node->intrs[0].line, headphone_intr, 0,
+			       "Tumbler Headphone Detection", chip)) < 0)
+		return err;
+	mix->headphone_irq = node->intrs[0].line;
+  
+	tumbler_update_headphone(chip);
+	return 0;
+}
+
+static void tumbler_cleanup(pmac_t *chip)
+{
+	pmac_tumbler_t *mix = chip->mixer_data;
+	if (! mix)
+		return;
+
+	if (mix->headphone_irq >= 0)
+		free_irq(mix->headphone_irq, chip);
+	if (mix->amp_mute)
+		iounmap(mix->amp_mute);
+	if (mix->headphone_mute)
+		iounmap(mix->headphone_mute);
+	if (mix->headphone_status)
+		iounmap(mix->headphone_status);
+	snd_pmac_keywest_cleanup(&mix->i2c);
+	kfree(mix);
+	chip->mixer_data = NULL;
+}
+
+/* exported */
+int __init snd_pmac_tumbler_init(pmac_t *chip)
+{
+	int i, err;
+	pmac_tumbler_t *mix;
+
+	mix = kmalloc(sizeof(*mix), GFP_KERNEL);
+	if (! mix)
+		return -ENOMEM;
+	memset(mix, 0, sizeof(*mix));
+	mix->headphone_irq = -1;
+
+	chip->mixer_data = mix;
+	chip->mixer_free = tumbler_cleanup;
+
+	if ((err = tumbler_init(chip)) < 0)
+		return err;
+
+	if ((err = snd_pmac_keywest_find(chip, &mix->i2c, TAS_I2C_ADDR, tumbler_init_client)) < 0)
+		return err;
+
+	/*
+	 * build mixers
+	 */
+	strcpy(chip->card->mixername, "PowerMac Tumbler");
+
+	for (i = 0; i < num_controls(tumbler_mixers); i++) {
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
+			return err;
+	}
+
+#ifdef CONFIG_PMAC_PBOOK
+	chip->resume = tumbler_resume;
+#endif
+
+	return 0;
+}
diff -Nru linux/sound/sound_core.c linux-2.4.19-pre5-mjc/sound/sound_core.c
--- linux/sound/sound_core.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/sound_core.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,573 @@
+/*
+ *	Sound core handling. Breaks out sound functions to submodules
+ *	
+ *	Author:		Alan Cox <alan.cox@linux.org>
+ *
+ *	Fixes:
+ *
+ *
+ *	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.
+ *
+ *                         --------------------
+ * 
+ *	Top level handler for the sound subsystem. Various devices can
+ *	plug into this. The fact they dont all go via OSS doesn't mean 
+ *	they don't have to implement the OSS API. There is a lot of logic
+ *	to keeping much of the OSS weight out of the code in a compatibility
+ *	module, but it's up to the driver to rember to load it...
+ *
+ *	The code provides a set of functions for registration of devices
+ *	by type. This is done rather than providing a single call so that
+ *	we can hide any future changes in the internals (eg when we go to
+ *	32bit dev_t) from the modules and their interface.
+ *
+ *	Secondly we need to allocate the dsp, dsp16 and audio devices as
+ *	one. Thus we misuse the chains a bit to simplify this.
+ *
+ *	Thirdly to make it more fun and for 2.3.x and above we do all
+ *	of this using fine grained locking.
+ *
+ *	FIXME: we have to resolve modules and fine grained load/unload
+ *	locking at some point in 2.3.x.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sound.h>
+#include <linux/major.h>
+#include <linux/kmod.h>
+#include <linux/devfs_fs_kernel.h>
+
+#define SOUND_STEP 16
+
+
+struct sound_unit
+{
+	int unit_minor;
+	struct file_operations *unit_fops;
+	struct sound_unit *next;
+	devfs_handle_t de;
+};
+
+#ifdef CONFIG_SOUND_ALSA_MSNDCLAS
+extern int msnd_classic_init(void);
+#endif
+#ifdef CONFIG_SOUND_ALSA_MSNDPIN
+extern int msnd_pinnacle_init(void);
+#endif
+
+/*
+ *	Low level list operator. Scan the ordered list, find a hole and
+ *	join into it. Called with the lock asserted
+ */
+
+static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int index, int low, int top)
+{
+	int n=low;
+
+	if (index < 0) {	/* first free */
+
+		while (*list && (*list)->unit_minor<n)
+			list=&((*list)->next);
+
+		while(n<top)
+		{
+			/* Found a hole ? */
+			if(*list==NULL || (*list)->unit_minor>n)
+				break;
+			list=&((*list)->next);
+			n+=SOUND_STEP;
+		}
+
+		if(n>=top)
+			return -ENOENT;
+	} else {
+		n = low+(index*16);
+		while (*list) {
+			if ((*list)->unit_minor==n)
+				return -EBUSY;
+			if ((*list)->unit_minor>n)
+				break;
+			list=&((*list)->next);
+		}
+	}	
+		
+	/*
+	 *	Fill it in
+	 */
+	 
+	s->unit_minor=n;
+	s->unit_fops=fops;
+	
+	/*
+	 *	Link it
+	 */
+	 
+	s->next=*list;
+	*list=s;
+	
+	
+	return n;
+}
+
+/*
+ *	Remove a node from the chain. Called with the lock asserted
+ */
+ 
+static void __sound_remove_unit(struct sound_unit **list, int unit)
+{
+	while(*list)
+	{
+		struct sound_unit *p=*list;
+		if(p->unit_minor==unit)
+		{
+			*list=p->next;
+			devfs_unregister (p->de);
+			kfree(p);
+			return;
+		}
+		list=&(p->next);
+	}
+	printk(KERN_ERR "Sound device %d went missing!\n", unit);
+}
+
+/*
+ *	This lock guards the sound loader list.
+ */
+
+static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ *	Allocate the controlling structure and add it to the sound driver
+ *	list. Acquires locks as needed
+ */
+
+static devfs_handle_t devfs_handle;
+ 
+static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode)
+{
+	int r;
+	struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL);
+	char name_buf[16];
+
+	if(s==NULL)
+		return -ENOMEM;
+		
+	spin_lock(&sound_loader_lock);
+	r=__sound_insert_unit(s,list,fops,index,low,top);
+	spin_unlock(&sound_loader_lock);
+	
+	if(r<0)
+	{
+		kfree(s);
+		return r;
+	}
+	
+	if (r < SOUND_STEP)
+		sprintf (name_buf, "%s", name);
+	else
+		sprintf (name_buf, "%s%d", name, r / SOUND_STEP);
+	s->de = devfs_register (devfs_handle, name_buf,
+				DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor,
+				S_IFCHR | mode, fops, NULL);
+	return r;
+}
+
+/*
+ *	Remove a unit. Acquires locks as needed. The drivers MUST have
+ *	completed the removal before their file operations become
+ *	invalid.
+ */
+ 	
+static void sound_remove_unit(struct sound_unit **list, int unit)
+{
+	spin_lock(&sound_loader_lock);
+	__sound_remove_unit(list, unit);
+	spin_unlock(&sound_loader_lock);
+}
+
+/*
+ *	Allocations
+ *
+ *	0	*16		Mixers
+ *	1	*8		Sequencers
+ *	2	*16		Midi
+ *	3	*16		DSP
+ *	4	*16		SunDSP
+ *	5	*16		DSP16
+ *	6	--		sndstat (obsolete)
+ *	7	*16		unused
+ *	8	--		alternate sequencer (see above)
+ *	9	*16		raw synthesizer access
+ *	10	*16		unused
+ *	11	*16		unused
+ *	12	*16		unused
+ *	13	*16		unused
+ *	14	*16		unused
+ *	15	*16		unused
+ */
+
+static struct sound_unit *chains[SOUND_STEP];
+
+/**
+ *	register_sound_special - register a special sound node
+ *	@fops: File operations for the driver
+ *	@unit: Unit number to allocate
+ *
+ *	Allocate a special sound device by minor number from the sound
+ *	subsystem. The allocated number is returned on succes. On failure
+ *	a negative error code is returned.
+ */
+ 
+int register_sound_special(struct file_operations *fops, int unit)
+{
+	const int chain = unit % (SOUND_STEP-1);
+	int max_unit = 128 + chain;
+	const char *name;
+	char _name[16];
+
+	switch (chain) {
+	    case 0:
+		name = "mixer";
+		break;
+	    case 1:
+		name = "sequencer";
+		if (unit >= SOUND_STEP)
+			goto __unknown;
+		max_unit = unit + 1;
+	    case 2:
+		name = "midi";
+		break;
+	    case 3:
+		name = "dsp";
+		break;
+	    case 4:
+		name = "audio";
+		break;
+	    case 8:
+		name = "sequencer2";
+		if (unit >= SOUND_STEP)
+			goto __unknown;
+		max_unit = unit + 1;
+		break;
+	    case 9:
+		name = "dmmidi";
+		break;
+	    case 10:
+		name = "dmfm";
+		break;
+	    case 12:
+		name = "adsp";
+		break;
+	    case 13:
+		name = "amidi";
+		break;
+	    case 14:
+		name = "admmidi";
+		break;
+	    default:
+	    	{
+		    __unknown:
+			sprintf(_name, "unknown%d", chain);
+		    	if (unit >= SOUND_STEP)
+		    		strcat(_name, "-");
+		    	name = _name;
+		}
+		break;
+	}
+	return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
+				 name, S_IRUSR | S_IWUSR);
+}
+ 
+EXPORT_SYMBOL(register_sound_special);
+
+/**
+ *	register_sound_mixer - register a mixer device
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a mixer device. Unit is the number of the mixer requested.
+ *	Pass -1 to request the next free mixer unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ */
+
+int register_sound_mixer(struct file_operations *fops, int dev)
+{
+	return sound_insert_unit(&chains[0], fops, dev, 0, 128,
+				 "mixer", S_IRUSR | S_IWUSR);
+}
+
+EXPORT_SYMBOL(register_sound_mixer);
+
+/**
+ *	register_sound_midi - register a midi device
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a midi device. Unit is the number of the midi device requested.
+ *	Pass -1 to request the next free midi unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ */
+
+int register_sound_midi(struct file_operations *fops, int dev)
+{
+	return sound_insert_unit(&chains[2], fops, dev, 2, 130,
+				 "midi", S_IRUSR | S_IWUSR);
+}
+
+EXPORT_SYMBOL(register_sound_midi);
+
+/*
+ *	DSP's are registered as a triple. Register only one and cheat
+ *	in open - see below.
+ */
+ 
+/**
+ *	register_sound_dsp - register a DSP device
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a DSP device. Unit is the number of the DSP requested.
+ *	Pass -1 to request the next free DSP unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ *
+ *	This function allocates both the audio and dsp device entries together
+ *	and will always allocate them as a matching pair - eg dsp3/audio3
+ */
+
+int register_sound_dsp(struct file_operations *fops, int dev)
+{
+	return sound_insert_unit(&chains[3], fops, dev, 3, 131,
+				 "dsp", S_IWUSR | S_IRUSR);
+}
+
+EXPORT_SYMBOL(register_sound_dsp);
+
+/**
+ *	register_sound_synth - register a synth device
+ *	@fops: File operations for the driver
+ *	@dev: Unit number to allocate
+ *
+ *	Allocate a synth device. Unit is the number of the synth device requested.
+ *	Pass -1 to request the next free synth unit. On success the allocated
+ *	number is returned, on failure a negative error code is returned.
+ */
+
+
+int register_sound_synth(struct file_operations *fops, int dev)
+{
+	return sound_insert_unit(&chains[9], fops, dev, 9, 137,
+				 "synth", S_IRUSR | S_IWUSR);
+}
+
+EXPORT_SYMBOL(register_sound_synth);
+
+/**
+ *	unregister_sound_special - unregister a special sound device
+ *	@unit: unit number to allocate
+ *
+ *	Release a sound device that was allocated with
+ *	register_sound_special(). The unit passed is the return value from
+ *	the register function.
+ */
+
+
+void unregister_sound_special(int unit)
+{
+	sound_remove_unit(&chains[unit&15], unit);
+}
+ 
+EXPORT_SYMBOL(unregister_sound_special);
+
+/**
+ *	unregister_sound_mixer - unregister a mixer
+ *	@unit: unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_mixer().
+ *	The unit passed is the return value from the register function.
+ */
+
+void unregister_sound_mixer(int unit)
+{
+	sound_remove_unit(&chains[0], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_mixer);
+
+/**
+ *	unregister_sound_midi - unregister a midi device
+ *	@unit: unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_midi().
+ *	The unit passed is the return value from the register function.
+ */
+
+void unregister_sound_midi(int unit)
+{
+	return sound_remove_unit(&chains[2], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_midi);
+
+/**
+ *	unregister_sound_dsp - unregister a DSP device
+ *	@unit: unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_dsp().
+ *	The unit passed is the return value from the register function.
+ *
+ *	Both of the allocated units are released together automatically.
+ */
+
+void unregister_sound_dsp(int unit)
+{
+	return sound_remove_unit(&chains[3], unit);
+}
+
+
+EXPORT_SYMBOL(unregister_sound_dsp);
+
+/**
+ *	unregister_sound_synth - unregister a synth device
+ *	@unit: unit number to allocate
+ *
+ *	Release a sound device that was allocated with register_sound_synth().
+ *	The unit passed is the return value from the register function.
+ */
+
+void unregister_sound_synth(int unit)
+{
+	return sound_remove_unit(&chains[9], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_synth);
+
+/*
+ *	Now our file operations
+ */
+
+static int soundcore_open(struct inode *, struct file *);
+
+static struct file_operations soundcore_fops=
+{
+	/* We must have an owner or the module locking fails */
+	owner:	THIS_MODULE,
+	open:	soundcore_open,
+};
+
+static struct sound_unit *__look_for_unit(int chain, int unit)
+{
+	struct sound_unit *s;
+	
+	s=chains[chain];
+	while(s && s->unit_minor <= unit)
+	{
+		if(s->unit_minor==unit)
+			return s;
+		s=s->next;
+	}
+	return NULL;
+}
+
+int soundcore_open(struct inode *inode, struct file *file)
+{
+	int chain;
+	int unit = minor(inode->i_rdev);
+	struct sound_unit *s;
+	struct file_operations *new_fops = NULL;
+
+	chain=unit&0x0F;
+	if(chain==4 || chain==5)	/* dsp/audio/dsp16 */
+	{
+		unit&=0xF0;
+		unit|=3;
+		chain=3;
+	}
+	
+	spin_lock(&sound_loader_lock);
+	s = __look_for_unit(chain, unit);
+	if (s)
+		new_fops = fops_get(s->unit_fops);
+	if (!new_fops) {
+		char mod[32];
+	
+		spin_unlock(&sound_loader_lock);
+		/*
+		 *  Please, don't change this order or code.
+		 *  For ALSA slot means soundcard and OSS emulation code
+		 *  comes as add-on modules which aren't depend on
+		 *  ALSA toplevel modules for soundcards, thus we need
+		 *  load them at first.	  [Jaroslav Kysela <perex@jcu.cz>]
+		 */
+		sprintf(mod, "sound-slot-%i", unit>>4);
+		request_module(mod);
+		sprintf(mod, "sound-service-%i-%i", unit>>4, chain);
+		request_module(mod);
+		spin_lock(&sound_loader_lock);
+		s = __look_for_unit(chain, unit);
+		if (s)
+			new_fops = fops_get(s->unit_fops);
+	}
+	if (new_fops) {
+		/*
+		 * We rely upon the fact that we can't be unloaded while the
+		 * subdriver is there, so if ->open() is successful we can
+		 * safely drop the reference counter and if it is not we can
+		 * revert to old ->f_op. Ugly, indeed, but that's the cost of
+		 * switching ->f_op in the first place.
+		 */
+		int err = 0;
+		struct file_operations *old_fops = file->f_op;
+		file->f_op = new_fops;
+		spin_unlock(&sound_loader_lock);
+		if(file->f_op->open)
+			err = file->f_op->open(inode,file);
+		if (err) {
+			fops_put(file->f_op);
+			file->f_op = fops_get(old_fops);
+		}
+		fops_put(old_fops);
+		return err;
+	}
+	spin_unlock(&sound_loader_lock);
+	return -ENODEV;
+}
+
+extern int mod_firmware_load(const char *, char **);
+EXPORT_SYMBOL(mod_firmware_load);
+
+
+MODULE_DESCRIPTION("Core sound module");
+MODULE_AUTHOR("Alan Cox");
+MODULE_LICENSE("GPL");
+
+static void __exit cleanup_soundcore(void)
+{
+	/* We have nothing to really do here - we know the lists must be
+	   empty */
+	devfs_unregister_chrdev(SOUND_MAJOR, "sound");
+	devfs_unregister (devfs_handle);
+}
+
+static int __init init_soundcore(void)
+{
+	if(devfs_register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1)
+	{
+		printk(KERN_ERR "soundcore: sound device already in use.\n");
+		return -EBUSY;
+	}
+	devfs_handle = devfs_mk_dir (NULL, "sound", NULL);
+
+	return 0;
+}
+
+module_init(init_soundcore);
+module_exit(cleanup_soundcore);
diff -Nru linux/sound/sound_firmware.c linux-2.4.19-pre5-mjc/sound/sound_firmware.c
--- linux/sound/sound_firmware.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/sound_firmware.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,78 @@
+#include <linux/vmalloc.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <asm/uaccess.h>
+
+static int errno;
+static int do_mod_firmware_load(const char *fn, char **fp)
+{
+	int fd;
+	long l;
+	char *dp;
+
+	fd = open(fn, 0, 0);
+	if (fd == -1)
+	{
+		printk(KERN_INFO "Unable to load '%s'.\n", fn);
+		return 0;
+	}
+	l = lseek(fd, 0L, 2);
+	if (l <= 0 || l > 131072)
+	{
+		printk(KERN_INFO "Invalid firmware '%s'\n", fn);
+		sys_close(fd);
+		return 0;
+	}
+	lseek(fd, 0L, 0);
+	dp = vmalloc(l);
+	if (dp == NULL)
+	{
+		printk(KERN_INFO "Out of memory loading '%s'.\n", fn);
+		sys_close(fd);
+		return 0;
+	}
+	if (read(fd, dp, l) != l)
+	{
+		printk(KERN_INFO "Failed to read '%s'.\n", fn);
+		vfree(dp);
+		sys_close(fd);
+		return 0;
+	}
+	close(fd);
+	*fp = dp;
+	return (int) l;
+}
+
+/**
+ *	mod_firmware_load - load sound driver firmware
+ *	@fn: filename
+ *	@fp: return for the buffer.
+ *
+ *	Load the firmware for a sound module (up to 128K) into a buffer.
+ *	The buffer is returned in *fp. It is allocated with vmalloc so is
+ *	virtually linear and not DMAable. The caller should free it with
+ *	vfree when finished.
+ *
+ *	The length of the buffer is returned on a successful load, the
+ *	value zero on a failure.
+ *
+ *	Caution: This API is not recommended. Firmware should be loaded via
+ *	an ioctl call and a setup application. This function may disappear
+ *	in future.
+ */
+ 
+int mod_firmware_load(const char *fn, char **fp)
+{
+	int r;
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	r = do_mod_firmware_load(fn, fp);
+	set_fs(fs);
+	return r;
+}
+
diff -Nru linux/sound/synth/Makefile linux-2.4.19-pre5-mjc/sound/synth/Makefile
--- linux/sound/synth/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/Makefile	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,27 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := synth.o
+
+subdir-y     := emux
+subdir-m     := $(subdir-y)
+
+list-multi   := snd-util-mem.o
+
+export-objs  := util_mem.o
+
+snd-util-mem-objs := util_mem.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
+obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-util-mem.o: $(snd-util-mem-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-util-mem-objs)
diff -Nru linux/sound/synth/emux/Makefile linux-2.4.19-pre5-mjc/sound/synth/emux/Makefile
--- linux/sound/synth/emux/Makefile	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/Makefile	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,24 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+O_TARGET     := _emux.o
+
+list-multi   := snd-emux-synth.o
+
+export-objs  := emux.o
+
+snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \
+		       emux_effect.o emux_proc.o emux_oss.o soundfont.o
+
+# Toplevel Module Dependency
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+  obj-$(CONFIG_SND_SBAWE) += snd-emux-synth.o
+  obj-$(CONFIG_SND_EMU10K1) += snd-emux-synth.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+snd-emux-synth.o: $(snd-emux-synth-objs)
+	$(LD) $(LD_RFLAG) -r -o $@ $(snd-emux-synth-objs)
diff -Nru linux/sound/synth/emux/emux.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux.c
--- linux/sound/synth/emux/emux.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,167 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU WaveTable chip
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+#include <linux/init.h>
+#include "emux_voice.h"
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu8000/Emu10k1
+ */
+int snd_emux_new(snd_emux_t **remu)
+{
+	snd_emux_t *emu;
+
+	*remu = NULL;
+	emu = snd_magic_kcalloc(snd_emux_t, 0, GFP_KERNEL);
+	if (emu == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&emu->voice_lock);
+	init_MUTEX(&emu->register_mutex);
+
+	emu->client = -1;
+#ifdef CONFIG_SND_OSSEMUL
+	emu->oss_synth = NULL;
+#endif
+	emu->max_voices = 0;
+	emu->use_time = 0;
+
+	emu->tlist.function = snd_emux_timer_callback;
+	emu->tlist.data = (unsigned long)emu;
+	emu->timer_active = 0;
+
+	*remu = emu;
+	return 0;
+}
+
+
+/*
+ */
+int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name)
+{
+	snd_sf_callback_t sf_cb;
+
+	snd_assert(emu->hw != NULL, return -EINVAL);
+	snd_assert(emu->max_voices > 0, return -EINVAL);
+	snd_assert(card != NULL, return -EINVAL);
+	snd_assert(name != NULL, return -EINVAL);
+
+	emu->card = card;
+	emu->name = snd_kmalloc_strdup(name, GFP_KERNEL);
+	emu->voices = snd_kcalloc(sizeof(snd_emux_voice_t) * emu->max_voices, GFP_KERNEL);
+	if (emu->voices == NULL)
+		return -ENOMEM;
+
+	/* create soundfont list */
+	memset(&sf_cb, 0, sizeof(sf_cb));
+	sf_cb.private_data = emu;
+	sf_cb.sample_new = (snd_sf_sample_new_t)emu->ops.sample_new;
+	sf_cb.sample_free = (snd_sf_sample_free_t)emu->ops.sample_free;
+	sf_cb.sample_reset = (snd_sf_sample_reset_t)emu->ops.sample_reset;
+	emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
+	if (emu->sflist == NULL)
+		return -ENOMEM;
+
+	snd_emux_init_voices(emu);
+
+	snd_emux_init_seq(emu, card, index);
+#ifdef CONFIG_SND_OSSEMUL
+	snd_emux_init_seq_oss(emu);
+#endif
+	snd_emux_init_virmidi(emu, card);
+
+#ifdef CONFIG_PROC_FS
+	snd_emux_proc_init(emu, card, index);
+#endif
+	return 0;
+}
+
+
+/*
+ */
+int snd_emux_free(snd_emux_t *emu)
+{
+	unsigned long flags;
+
+	if (! emu)
+		return -EINVAL;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->timer_active)
+		del_timer(&emu->tlist);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+#ifdef CONFIG_PROC_FS
+	snd_emux_proc_free(emu);
+#endif
+	snd_emux_delete_virmidi(emu);
+#ifdef CONFIG_SND_OSSEMUL
+	snd_emux_detach_seq_oss(emu);
+#endif
+	snd_emux_detach_seq(emu);
+
+	if (emu->sflist)
+		snd_sf_free(emu->sflist);
+
+	if (emu->voices)
+		kfree(emu->voices);
+
+	if (emu->name)
+		kfree(emu->name);
+
+	snd_magic_kfree(emu);
+	return 0;
+}
+
+
+EXPORT_SYMBOL(snd_emux_new);
+EXPORT_SYMBOL(snd_emux_register);
+EXPORT_SYMBOL(snd_emux_free);
+
+EXPORT_SYMBOL(snd_emux_terminate_all);
+EXPORT_SYMBOL(snd_emux_lock_voice);
+EXPORT_SYMBOL(snd_emux_unlock_voice);
+
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emux_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_emux_exit(void)
+{
+}
+
+module_init(alsa_emux_init)
+module_exit(alsa_emux_exit)
diff -Nru linux/sound/synth/emux/emux_effect.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_effect.c
--- linux/sound/synth/emux/emux_effect.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_effect.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,309 @@
+/*
+ *  Midi synth routines for the Emu8k/Emu10k1
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Contains code based on awe_wave.c by Takashi Iwai
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include "emux_voice.h"
+#include <linux/slab.h>
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+/*
+ * effects table
+ */
+
+#define xoffsetof(type,tag)	((long)(&((type)NULL)->tag) - (long)(NULL))
+
+#define parm_offset(tag)	xoffsetof(soundfont_voice_parm_t*, tag)
+
+#define PARM_IS_BYTE		(1 << 0)
+#define PARM_IS_WORD		(1 << 1)
+#define PARM_IS_ALIGNED		(3 << 2)
+#define PARM_IS_ALIGN_HI	(1 << 2)
+#define PARM_IS_ALIGN_LO	(2 << 2)
+#define PARM_IS_SIGNED		(1 << 4)
+
+#define PARM_WORD	(PARM_IS_WORD)
+#define PARM_BYTE_LO	(PARM_IS_BYTE|PARM_IS_ALIGN_LO)
+#define PARM_BYTE_HI	(PARM_IS_BYTE|PARM_IS_ALIGN_HI)
+#define PARM_BYTE	(PARM_IS_BYTE)
+#define PARM_SIGN_LO	(PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED)
+#define PARM_SIGN_HI	(PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED)
+
+static struct emux_parm_defs {
+	int type;	/* byte or word */
+	int low, high;	/* value range */
+	long offset;	/* offset in parameter record (-1 = not written) */
+	int update;	/* flgas for real-time update */
+} parm_defs[EMUX_NUM_EFFECTS] = {
+	{PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0},	/* env1 delay */
+	{PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0},	/* env1 attack */
+	{PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0},	/* env1 hold */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0},	/* env1 decay */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0},	/* env1 release */
+	{PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0},	/* env1 sustain */
+	{PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0},	/* env1 pitch */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0},	/* env1 fc */
+
+	{PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0},	/* env2 delay */
+	{PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0},	/* env2 attack */
+	{PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0},	/* env2 hold */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0},	/* env2 decay */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0},	/* env2 release */
+	{PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0},	/* env2 sustain */
+
+	{PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0},	/* lfo1 delay */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ},	/* lfo1 freq */
+	{PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ},	/* lfo1 vol */
+	{PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD},	/* lfo1 pitch */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD},	/* lfo1 cutoff */
+
+	{PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0},	/* lfo2 delay */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2},	/* lfo2 freq */
+	{PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2},	/* lfo2 pitch */
+
+	{PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH},	/* initial pitch */
+	{PARM_BYTE, 0, 0xff, parm_offset(chorus), 0},	/* chorus */
+	{PARM_BYTE, 0, 0xff, parm_offset(reverb), 0},	/* reverb */
+	{PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME},	/* cutoff */
+	{PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q},	/* resonance */
+
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* sample start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* loop start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* loop end */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* coarse sample start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* coarse loop start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* coarse loop end */
+	{PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME},	/* initial attenuation */
+};
+
+/* set byte effect value */
+static void
+effect_set_byte(unsigned char *valp, snd_midi_channel_t *chan, int type)
+{
+	short effect;
+	snd_emux_effect_table_t *fx = chan->private;
+
+	effect = fx->val[type];
+	if (fx->flag[type] == EMUX_FX_FLAG_ADD) {
+		if (parm_defs[type].type & PARM_IS_SIGNED)
+			effect += *(char*)valp;
+		else
+			effect += *valp;
+	}
+	if (effect < parm_defs[type].low)
+		effect = parm_defs[type].low;
+	else if (effect > parm_defs[type].high)
+		effect = parm_defs[type].high;
+	*valp = (unsigned char)effect;
+}
+
+/* set word effect value */
+static void
+effect_set_word(unsigned short *valp, snd_midi_channel_t *chan, int type)
+{
+	int effect;
+	snd_emux_effect_table_t *fx = chan->private;
+
+	effect = *(unsigned short*)&fx->val[type];
+	if (fx->flag[type] == EMUX_FX_FLAG_ADD)
+		effect += *valp;
+	if (effect < parm_defs[type].low)
+		effect = parm_defs[type].low;
+	else if (effect > parm_defs[type].high)
+		effect = parm_defs[type].high;
+	*valp = (unsigned short)effect;
+}
+
+/* address offset */
+static int
+effect_get_offset(snd_midi_channel_t *chan, int lo, int hi, int mode)
+{
+	int addr = 0;
+	snd_emux_effect_table_t *fx = chan->private;
+
+	if (fx->flag[hi])
+		addr = (short)fx->val[hi];
+	addr = addr << 15;
+	if (fx->flag[lo])
+		addr += (short)fx->val[lo];
+	if (!(mode & SNDRV_SFNT_SAMPLE_8BITS))
+		addr /= 2;
+	return addr;
+}
+
+#ifdef CONFIG_SND_OSSEMUL
+/* change effects - for OSS sequencer compatibility */
+void
+snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val)
+{
+	int mode;
+
+	if (type & 0x40)
+		mode = EMUX_FX_FLAG_OFF;
+	else if (type & 0x80)
+		mode = EMUX_FX_FLAG_ADD;
+	else
+		mode = EMUX_FX_FLAG_SET;
+	type &= 0x3f;
+
+	snd_emux_send_effect(port, chan, type, val, mode);
+}
+#endif
+
+/* Modify the effect value.
+ * if update is necessary, call emu8000_control
+ */
+void
+snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode)
+{
+	int i;
+	int offset;
+	unsigned char *srcp, *origp;
+	snd_emux_t *emu;
+	snd_emux_effect_table_t *fx;
+	unsigned long flags;
+
+	emu = port->emu;
+	fx = chan->private;
+	if (emu == NULL || fx == NULL)
+		return;
+	if (type < 0 || type >= EMUX_NUM_EFFECTS)
+		return;
+
+	fx->val[type] = val;
+	fx->flag[type] = mode;
+
+	/* do we need to modify the register in realtime ? */
+	if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0)
+		return;
+
+#ifdef SNDRV_LITTLE_ENDIAN
+	if (parm_defs[type].type & PARM_IS_ALIGN_HI)
+		offset++;
+#else
+	if (parm_defs[type].type & PARM_IS_ALIGN_LO)
+		offset++;
+#endif
+	/* modify the register values */
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		snd_emux_voice_t *vp = &emu->voices[i];
+		if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan)
+			continue;
+		srcp = (unsigned char*)&vp->reg.parm + offset;
+		origp = (unsigned char*)&vp->zone->v.parm + offset;
+		if (parm_defs[i].type & PARM_IS_BYTE) {
+			*srcp = *origp;
+			effect_set_byte(srcp, chan, type);
+		} else {
+			*(unsigned short*)srcp = *(unsigned short*)origp;
+			effect_set_word((unsigned short*)srcp, chan, type);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+	/* activate them */
+	snd_emux_update_channel(port, chan, parm_defs[type].update);
+}
+
+
+/* copy wavetable registers to voice table */
+void
+snd_emux_setup_effect(snd_emux_voice_t *vp)
+{
+	snd_midi_channel_t *chan = vp->chan;
+	snd_emux_effect_table_t *fx;
+	unsigned char *srcp;
+	int i;
+
+	if (! (fx = chan->private))
+		return;
+
+	/* modify the register values via effect table */
+	for (i = 0; i < EMUX_FX_END; i++) {
+		int offset;
+		if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0)
+			continue;
+#ifdef SNDRV_LITTLE_ENDIAN
+		if (parm_defs[i].type & PARM_IS_ALIGN_HI)
+			offset++;
+#else
+		if (parm_defs[i].type & PARM_IS_ALIGN_LO)
+			offset++;
+#endif
+		srcp = (unsigned char*)&vp->reg.parm + offset;
+		if (parm_defs[i].type & PARM_IS_BYTE)
+			effect_set_byte(srcp, chan, i);
+		else
+			effect_set_word((unsigned short*)srcp, chan, i);
+	}
+
+	/* correct sample and loop points */
+	vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START,
+					   EMUX_FX_COARSE_SAMPLE_START,
+					   vp->reg.sample_mode);
+
+	vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START,
+					       EMUX_FX_COARSE_LOOP_START,
+					       vp->reg.sample_mode);
+
+	vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END,
+					     EMUX_FX_COARSE_LOOP_END,
+					     vp->reg.sample_mode);
+}
+
+/*
+ * effect table
+ */
+void
+snd_emux_create_effect(snd_emux_port_t *p)
+{
+	int i;
+	p->effect = snd_kcalloc(sizeof(snd_emux_effect_table_t) * p->chset.max_channels, GFP_KERNEL);
+	if (p->effect) {
+		for (i = 0; i < p->chset.max_channels; i++)
+			p->chset.channels[i].private = p->effect + i;
+	} else {
+		for (i = 0; i < p->chset.max_channels; i++)
+			p->chset.channels[i].private = NULL;
+	}
+}
+
+void
+snd_emux_delete_effect(snd_emux_port_t *p)
+{
+	if (p->effect) {
+		kfree(p->effect);
+		p->effect = NULL;
+	}
+}
+
+void
+snd_emux_clear_effect(snd_emux_port_t *p)
+{
+	if (p->effect) {
+		memset(p->effect, 0, sizeof(snd_emux_effect_table_t) * p->chset.max_channels);
+	}
+}
+
+#endif /* SNDRV_EMUX_USE_RAW_EFFECT */
diff -Nru linux/sound/synth/emux/emux_nrpn.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_nrpn.c
--- linux/sound/synth/emux/emux_nrpn.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_nrpn.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,401 @@
+/*
+ *  NRPN / SYSEX callbacks for Emu8k/Emu10k1
+ *
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include "emux_voice.h"
+#include <sound/asoundef.h>
+
+#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+/*
+ * conversion from NRPN/control parameters to Emu8000 raw parameters
+ */
+
+/* NRPN / CC -> Emu8000 parameter converter */
+typedef struct {
+	int control;
+	int effect;
+	int (*convert)(int val);
+} nrpn_conv_table;
+
+/* effect sensitivity */
+
+#define FX_CUTOFF	0
+#define FX_RESONANCE	1
+#define FX_ATTACK	2
+#define FX_RELEASE	3
+#define FX_VIBRATE	4
+#define FX_VIBDEPTH	5
+#define FX_VIBDELAY	6
+#define FX_NUMS		7
+
+/*
+ * convert NRPN/control values
+ */
+
+static int send_converted_effect(nrpn_conv_table *table, int num_tables,
+				 snd_emux_port_t *port, snd_midi_channel_t *chan,
+				 int type, int val, int mode)
+{
+	int i, cval;
+	for (i = 0; i < num_tables; i++) {
+		if (table[i].control == type) {
+			cval = table[i].convert(val);
+			snd_emux_send_effect(port, chan, table[i].effect,
+					     cval, mode);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+#define DEF_FX_CUTOFF		170
+#define DEF_FX_RESONANCE	6
+#define DEF_FX_ATTACK		50
+#define DEF_FX_RELEASE		50
+#define DEF_FX_VIBRATE		30
+#define DEF_FX_VIBDEPTH		4
+#define DEF_FX_VIBDELAY		1500
+
+/* effect sensitivities for GS NRPN:
+ *  adjusted for chaos 8MB soundfonts
+ */
+static int gs_sense[] = 
+{
+	DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+	DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+/* effect sensitivies for XG controls:
+ * adjusted for chaos 8MB soundfonts
+ */
+static int xg_sense[] = 
+{
+	DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+	DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+
+/*
+ * AWE32 NRPN effects
+ */
+
+static int fx_delay(int val);
+static int fx_attack(int val);
+static int fx_hold(int val);
+static int fx_decay(int val);
+static int fx_the_value(int val);
+static int fx_twice_value(int val);
+static int fx_conv_pitch(int val);
+static int fx_conv_Q(int val);
+
+/* function for each NRPN */		/* [range]  units */
+#define fx_env1_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_env1_attack	fx_attack	/* [0,5940] 1msec */
+#define fx_env1_hold	fx_hold		/* [0,8191] 1msec */
+#define fx_env1_decay	fx_decay	/* [0,5940] 4msec */
+#define fx_env1_release	fx_decay	/* [0,5940] 4msec */
+#define fx_env1_sustain	fx_the_value	/* [0,127] 0.75dB */
+#define fx_env1_pitch	fx_the_value	/* [-127,127] 9.375cents */
+#define fx_env1_cutoff	fx_the_value	/* [-127,127] 56.25cents */
+
+#define fx_env2_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_env2_attack	fx_attack	/* [0,5940] 1msec */
+#define fx_env2_hold	fx_hold		/* [0,8191] 1msec */
+#define fx_env2_decay	fx_decay	/* [0,5940] 4msec */
+#define fx_env2_release	fx_decay	/* [0,5940] 4msec */
+#define fx_env2_sustain	fx_the_value	/* [0,127] 0.75dB */
+
+#define fx_lfo1_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_lfo1_freq	fx_twice_value	/* [0,127] 84mHz */
+#define fx_lfo1_volume	fx_twice_value	/* [0,127] 0.1875dB */
+#define fx_lfo1_pitch	fx_the_value	/* [-127,127] 9.375cents */
+#define fx_lfo1_cutoff	fx_twice_value	/* [-64,63] 56.25cents */
+
+#define fx_lfo2_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_lfo2_freq	fx_twice_value	/* [0,127] 84mHz */
+#define fx_lfo2_pitch	fx_the_value	/* [-127,127] 9.375cents */
+
+#define fx_init_pitch	fx_conv_pitch	/* [-8192,8192] cents */
+#define fx_chorus	fx_the_value	/* [0,255] -- */
+#define fx_reverb	fx_the_value	/* [0,255] -- */
+#define fx_cutoff	fx_twice_value	/* [0,127] 62Hz */
+#define fx_filterQ	fx_conv_Q	/* [0,127] -- */
+
+static int fx_delay(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_delay(val);
+}
+
+static int fx_attack(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_attack(val);
+}
+
+static int fx_hold(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_hold(val);
+}
+
+static int fx_decay(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_decay(val);
+}
+
+static int fx_the_value(int val)
+{
+	return (unsigned short)(val & 0xff);
+}
+
+static int fx_twice_value(int val)
+{
+	return (unsigned short)((val * 2) & 0xff);
+}
+
+static int fx_conv_pitch(int val)
+{
+	return (short)(val * 4096 / 1200);
+}
+
+static int fx_conv_Q(int val)
+{
+	return (unsigned short)((val / 8) & 0xff);
+}
+
+
+static nrpn_conv_table awe_effects[] =
+{
+	{ 0, EMUX_FX_LFO1_DELAY,	fx_lfo1_delay},
+	{ 1, EMUX_FX_LFO1_FREQ,	fx_lfo1_freq},
+	{ 2, EMUX_FX_LFO2_DELAY,	fx_lfo2_delay},
+	{ 3, EMUX_FX_LFO2_FREQ,	fx_lfo2_freq},
+
+	{ 4, EMUX_FX_ENV1_DELAY,	fx_env1_delay},
+	{ 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack},
+	{ 6, EMUX_FX_ENV1_HOLD,	fx_env1_hold},
+	{ 7, EMUX_FX_ENV1_DECAY,	fx_env1_decay},
+	{ 8, EMUX_FX_ENV1_SUSTAIN,	fx_env1_sustain},
+	{ 9, EMUX_FX_ENV1_RELEASE,	fx_env1_release},
+
+	{10, EMUX_FX_ENV2_DELAY,	fx_env2_delay},
+	{11, EMUX_FX_ENV2_ATTACK,	fx_env2_attack},
+	{12, EMUX_FX_ENV2_HOLD,	fx_env2_hold},
+	{13, EMUX_FX_ENV2_DECAY,	fx_env2_decay},
+	{14, EMUX_FX_ENV2_SUSTAIN,	fx_env2_sustain},
+	{15, EMUX_FX_ENV2_RELEASE,	fx_env2_release},
+
+	{16, EMUX_FX_INIT_PITCH,	fx_init_pitch},
+	{17, EMUX_FX_LFO1_PITCH,	fx_lfo1_pitch},
+	{18, EMUX_FX_LFO2_PITCH,	fx_lfo2_pitch},
+	{19, EMUX_FX_ENV1_PITCH,	fx_env1_pitch},
+	{20, EMUX_FX_LFO1_VOLUME,	fx_lfo1_volume},
+	{21, EMUX_FX_CUTOFF,		fx_cutoff},
+	{22, EMUX_FX_FILTERQ,	fx_filterQ},
+	{23, EMUX_FX_LFO1_CUTOFF,	fx_lfo1_cutoff},
+	{24, EMUX_FX_ENV1_CUTOFF,	fx_env1_cutoff},
+	{25, EMUX_FX_CHORUS,		fx_chorus},
+	{26, EMUX_FX_REVERB,		fx_reverb},
+};
+
+static int num_awe_effects = NELEM(awe_effects);
+
+
+/*
+ * GS(SC88) NRPN effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static int gs_cutoff(int val)
+{
+	return (val - 64) * gs_sense[FX_CUTOFF] / 50;
+}
+
+/* resonance: 0 to 15(max) */
+static int gs_filterQ(int val)
+{
+	return (val - 64) * gs_sense[FX_RESONANCE] / 50;
+}
+
+/* attack: */
+static int gs_attack(int val)
+{
+	return -(val - 64) * gs_sense[FX_ATTACK] / 50;
+}
+
+/* decay: */
+static int gs_decay(int val)
+{
+	return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* release: */
+static int gs_release(int val)
+{
+	return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* vibrato freq: 0.042Hz step, max=255 */
+static int gs_vib_rate(int val)
+{
+	return (val - 64) * gs_sense[FX_VIBRATE] / 50;
+}
+
+/* vibrato depth: max=127, 1 octave */
+static int gs_vib_depth(int val)
+{
+	return (val - 64) * gs_sense[FX_VIBDEPTH] / 50;
+}
+
+/* vibrato delay: -0.725msec step */
+static int gs_vib_delay(int val)
+{
+	return -(val - 64) * gs_sense[FX_VIBDELAY] / 50;
+}
+
+static nrpn_conv_table gs_effects[] =
+{
+	{32, EMUX_FX_CUTOFF,	gs_cutoff},
+	{33, EMUX_FX_FILTERQ,	gs_filterQ},
+	{99, EMUX_FX_ENV2_ATTACK, gs_attack},
+	{100, EMUX_FX_ENV2_DECAY, gs_decay},
+	{102, EMUX_FX_ENV2_RELEASE, gs_release},
+	{8, EMUX_FX_LFO1_FREQ, gs_vib_rate},
+	{9, EMUX_FX_LFO1_VOLUME, gs_vib_depth},
+	{10, EMUX_FX_LFO1_DELAY, gs_vib_delay},
+};
+
+static int num_gs_effects = NELEM(gs_effects);
+
+
+/*
+ * NRPN events
+ */
+void
+snd_emux_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset)
+{
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL, return);
+	snd_assert(chan != NULL, return);
+
+	if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 &&
+	    chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) {
+		int val;
+		/* Win/DOS AWE32 specific NRPNs */
+		/* both MSB/LSB necessary */
+		val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
+			chan->control[MIDI_CTL_LSB_DATA_ENTRY]; 
+		val -= 8192;
+		send_converted_effect
+			(awe_effects, num_awe_effects,
+			 port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB],
+			 val, EMUX_FX_FLAG_SET);
+		return;
+	}
+
+	if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS &&
+	    chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) {
+		int val;
+		/* GS specific NRPNs */
+		/* only MSB is valid */
+		val = chan->control[MIDI_CTL_MSB_DATA_ENTRY];
+		send_converted_effect
+			(gs_effects, num_gs_effects,
+			 port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB],
+			 val, EMUX_FX_FLAG_ADD);
+		return;
+	}
+}
+
+
+/*
+ * XG control effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static int xg_cutoff(int val)
+{
+	return (val - 64) * xg_sense[FX_CUTOFF] / 64;
+}
+
+/* resonance: 0(open) to 15(most nasal) */
+static int xg_filterQ(int val)
+{
+	return (val - 64) * xg_sense[FX_RESONANCE] / 64;
+}
+
+/* attack: */
+static int xg_attack(int val)
+{
+	return -(val - 64) * xg_sense[FX_ATTACK] / 64;
+}
+
+/* release: */
+static int xg_release(int val)
+{
+	return -(val - 64) * xg_sense[FX_RELEASE] / 64;
+}
+
+static nrpn_conv_table xg_effects[] =
+{
+	{71, EMUX_FX_CUTOFF,	xg_cutoff},
+	{74, EMUX_FX_FILTERQ,	xg_filterQ},
+	{72, EMUX_FX_ENV2_RELEASE, xg_release},
+	{73, EMUX_FX_ENV2_ATTACK, xg_attack},
+};
+
+static int num_xg_effects = NELEM(xg_effects);
+
+int
+snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param)
+{
+	return send_converted_effect(xg_effects, num_xg_effects,
+				     port, chan, param,
+				     chan->control[param],
+				     EMUX_FX_FLAG_ADD);
+}
+
+/*
+ * receive sysex
+ */
+void
+snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset)
+{
+	snd_emux_port_t *port;
+	snd_emux_t *emu;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL, return);
+	snd_assert(chset != NULL, return);
+	emu = port->emu;
+
+	switch (parsed) {
+	case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME:
+		snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
+		break;
+	default:
+		if (emu->ops.sysex)
+			emu->ops.sysex(emu, buf, len, parsed, chset);
+		break;
+	}
+}
+
diff -Nru linux/sound/synth/emux/emux_oss.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_oss.c
--- linux/sound/synth/emux/emux_oss.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_oss.c	Mon Apr  8 22:31:23 2002
@@ -0,0 +1,496 @@
+/*
+ *  Interface for OSS sequencer emulation
+ *
+ *  Copyright (C) 1999 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ * Changes
+ * 19990227   Steve Ratcliffe   Made separate file and merged in latest
+ * 				midi emulation.
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+
+#ifdef CONFIG_SND_OSSEMUL
+
+#include <asm/uaccess.h>
+#include <sound/core.h>
+#include "emux_voice.h"
+#include <sound/asoundef.h>
+
+static int snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure);
+static int snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg);
+static int snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg);
+static int snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count);
+static int snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg);
+static int snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
+static void reset_port_mode(snd_emux_port_t *port, int midi_mode);
+static void emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop);
+static void gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop);
+static void fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop);
+
+/* operators */
+static snd_seq_oss_callback_t oss_callback = {
+	owner: THIS_MODULE,
+	open: snd_emux_open_seq_oss,
+	close: snd_emux_close_seq_oss,
+	ioctl: snd_emux_ioctl_seq_oss,
+	load_patch: snd_emux_load_patch_seq_oss,
+	reset: snd_emux_reset_seq_oss,
+};
+
+
+/*
+ * register OSS synth
+ */
+
+void
+snd_emux_init_seq_oss(snd_emux_t *emu)
+{
+	snd_seq_oss_reg_t *arg;
+	snd_seq_device_t *dev;
+
+	if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS,
+			       sizeof(snd_seq_oss_reg_t), &dev) < 0)
+		return;
+
+	emu->oss_synth = dev;
+	strcpy(dev->name, emu->name);
+	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	arg->type = SYNTH_TYPE_SAMPLE;
+	arg->subtype = SAMPLE_TYPE_AWE32;
+	arg->nvoices = emu->max_voices;
+	arg->oper = oss_callback;
+	arg->private_data = emu;
+
+	/* register to OSS synth table */
+	snd_device_register(emu->card, dev);
+}
+
+
+/*
+ * unregister
+ */
+void
+snd_emux_detach_seq_oss(snd_emux_t *emu)
+{
+	if (emu->oss_synth) {
+		snd_device_free(emu->card, emu->oss_synth);
+		emu->oss_synth = NULL;
+	}
+}
+
+
+/* use port number as a unique soundfont client number */
+#define SF_CLIENT_NO(p)	((p) + 0x1000)
+
+/*
+ * open port for OSS sequencer
+ */
+static int
+snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+	snd_seq_port_callback_t callback;
+	char tmpname[64];
+
+	emu = snd_magic_cast(snd_emux_t, closure, return -EINVAL);
+	snd_assert(arg != NULL && emu != NULL, return -ENXIO);
+
+	down(&emu->register_mutex);
+
+	if (!snd_emux_inc_count(emu)) {
+		up(&emu->register_mutex);
+		return -EFAULT;
+	}
+
+	memset(&callback, 0, sizeof(callback));
+	callback.owner = THIS_MODULE;
+	callback.event_input = snd_emux_event_oss_input;
+
+	sprintf(tmpname, "%s OSS Port", emu->name);
+	p = snd_emux_create_port(emu, tmpname, 32,
+				 1, &callback);
+	if (p == NULL) {
+		snd_printk("can't create port\n");
+		snd_emux_dec_count(emu);
+		up(&emu->register_mutex);
+		return -ENOMEM;
+	}
+
+	/* fill the argument data */
+	arg->private_data = p;
+	arg->addr.client = p->chset.client;
+	arg->addr.port = p->chset.port;
+	p->oss_arg = arg;
+
+	reset_port_mode(p, arg->seq_mode);
+
+	snd_emux_reset_port(p);
+
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+#define DEFAULT_DRUM_FLAGS	((1<<9) | (1<<25))
+
+/*
+ * reset port mode
+ */
+static void
+reset_port_mode(snd_emux_port_t *port, int midi_mode)
+{
+	if (midi_mode) {
+		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI;
+		port->drum_flags = DEFAULT_DRUM_FLAGS;
+		port->volume_atten = 0;
+		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS;
+	} else {
+		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH;
+		port->drum_flags = 0;
+		port->volume_atten = 32;
+		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
+	}
+}
+
+
+/*
+ * close port
+ */
+static int
+snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL);
+	snd_assert(p != NULL, return -ENXIO);
+
+	emu = p->emu;
+	snd_assert(emu != NULL, return -ENXIO);
+
+	down(&emu->register_mutex);
+	snd_emux_sounds_off_all(p);
+	snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port));
+	snd_seq_event_port_detach(p->chset.client, p->chset.port);
+	snd_emux_dec_count(emu);
+
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+/*
+ * load patch
+ */
+static int
+snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format,
+			    const char *buf, int offs, int count)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+	int rc;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL);
+	snd_assert(p != NULL, return -ENXIO);
+
+	emu = p->emu;
+	snd_assert(emu != NULL, return -ENXIO);
+
+	if (format == GUS_PATCH)
+		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
+						 SF_CLIENT_NO(p->chset.port));
+	else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
+		soundfont_patch_info_t patch;
+		if (count < sizeof(patch))
+			rc = -EINVAL;
+		if (copy_from_user(&patch, buf, sizeof(patch)))
+			rc = -EFAULT;
+		if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
+		    patch.type <= SNDRV_SFNT_PROBE_DATA)
+			rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));
+		else {
+			if (emu->ops.load_fx)
+				rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count);
+			else
+				rc = -EINVAL;
+		}
+	} else
+		rc = 0;
+	return rc;
+}
+
+
+/*
+ * ioctl
+ */
+static int
+snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg)
+{
+	snd_emux_port_t *p;
+	snd_emux_t *emu;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL);
+	snd_assert(p != NULL, return -ENXIO);
+
+	emu = p->emu;
+	snd_assert(emu != NULL, return -ENXIO);
+
+	switch (cmd) {
+	case SNDCTL_SEQ_RESETSAMPLES:
+		snd_soundfont_remove_samples(emu->sflist);
+		return 0;
+			
+	case SNDCTL_SYNTH_MEMAVL:
+		if (emu->memhdr)
+			return snd_util_mem_avail(emu->memhdr);
+		return 0;
+	}
+
+	return 0;
+}
+
+
+/*
+ * reset device
+ */
+static int
+snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg)
+{
+	snd_emux_port_t *p;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL);
+	snd_assert(p != NULL, return -ENXIO);
+	snd_emux_reset_port(p);
+	return 0;
+}
+
+
+/*
+ * receive raw events: only SEQ_PRIVATE is accepted.
+ */
+static int
+snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private_data,
+			 int atomic, int hop)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+	unsigned char cmd, *data;
+
+	p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL);
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+	if (ev->type != SNDRV_SEQ_EVENT_OSS)
+		return snd_emux_event_input(ev, direct, private_data, atomic, hop);
+
+	data = ev->data.raw8.d;
+	/* only SEQ_PRIVATE is accepted */
+	if (data[0] != 0xfe)
+		return 0;
+	cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK;
+	if (data[2] & _EMUX_OSS_MODE_FLAG)
+		emuspec_control(emu, p, cmd, data, atomic, hop);
+	else
+		gusspec_control(emu, p, cmd, data, atomic, hop);
+	return 0;
+}
+
+
+/*
+ * OSS/AWE driver specific h/w controls
+ */
+static void
+emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd,
+		unsigned char *event, int atomic, int hop)
+{
+	int voice;
+	unsigned short p1;
+	short p2;
+	int i;
+	snd_midi_channel_t *chan;
+
+	voice = event[3];
+	if (voice < 0 || voice >= port->chset.max_channels)
+		chan = NULL;
+	else
+		chan = &port->chset.channels[voice];
+
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+
+	switch (cmd) {
+	case _EMUX_OSS_REMOVE_LAST_SAMPLES:
+		snd_soundfont_remove_unlocked(emu->sflist);
+		break;
+	case _EMUX_OSS_SEND_EFFECT:
+		if (chan)
+			snd_emux_send_effect_oss(port, chan, p1, p2);
+		break;
+		
+	case _EMUX_OSS_TERMINATE_ALL:
+		snd_emux_terminate_all(emu);
+		break;
+
+	case _EMUX_OSS_TERMINATE_CHANNEL:
+		/*snd_emux_mute_channel(emu, chan);*/
+		break;
+	case _EMUX_OSS_RESET_CHANNEL:
+		/*snd_emux_channel_init(chset, chan);*/
+		break;
+
+	case _EMUX_OSS_RELEASE_ALL:
+		fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop);
+		break;
+	case _EMUX_OSS_NOTEOFF_ALL:
+		fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop);
+		break;
+
+	case _EMUX_OSS_INITIAL_VOLUME:
+		if (p2) {
+			port->volume_atten = (short)p1;
+			snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
+		}
+		break;
+
+	case _EMUX_OSS_CHN_PRESSURE:
+		if (chan) {
+			chan->midi_pressure = p1;
+			snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2);
+		}
+		break;
+
+	case _EMUX_OSS_CHANNEL_MODE:
+		reset_port_mode(port, p1);
+		snd_emux_reset_port(port);
+		break;
+
+	case _EMUX_OSS_DRUM_CHANNELS:
+		port->drum_flags = *(unsigned int*)&event[4];
+		for (i = 0; i < port->chset.max_channels; i++) {
+			chan = &port->chset.channels[i];
+			chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
+		}
+		break;
+
+	case _EMUX_OSS_MISC_MODE:
+		if (p1 < EMUX_MD_END)
+			port->ctrls[p1] = p2;
+		break;
+	case _EMUX_OSS_DEBUG_MODE:
+		break;
+
+	default:
+		if (emu->ops.oss_ioctl)
+			emu->ops.oss_ioctl(emu, cmd, p1, p2);
+		break;
+	}
+}
+
+/*
+ * GUS specific h/w controls
+ */
+
+#include <linux/ultrasound.h>
+
+static void
+gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd,
+		unsigned char *event, int atomic, int hop)
+{
+	int voice;
+	unsigned short p1;
+	short p2;
+	int plong;
+	snd_midi_channel_t *chan;
+
+	if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH)
+		return;
+	if (cmd == _GUS_NUMVOICES)
+		return;
+	voice = event[3];
+	if (voice < 0 || voice >= port->chset.max_channels)
+		return;
+
+	chan = &port->chset.channels[voice];
+
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+	plong = *(int*) &event[4];
+
+	switch (cmd) {
+	case _GUS_VOICESAMPLE:
+		chan->midi_program = p1;
+		return;
+
+	case _GUS_VOICEBALA:
+		/* 0 to 15 --> 0 to 127 */
+		chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3;
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
+		return;
+
+	case _GUS_VOICEVOL:
+	case _GUS_VOICEVOL2:
+		/* not supported yet */
+		return;
+
+	case _GUS_RAMPRANGE:
+	case _GUS_RAMPRATE:
+	case _GUS_RAMPMODE:
+	case _GUS_RAMPON:
+	case _GUS_RAMPOFF:
+		/* volume ramping not supported */
+		return;
+
+	case _GUS_VOLUME_SCALE:
+		return;
+
+	case _GUS_VOICE_POS:
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START,
+				     (short)(plong & 0x7fff),
+				     EMUX_FX_FLAG_SET);
+		snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START,
+				     (plong >> 15) & 0xffff,
+				     EMUX_FX_FLAG_SET);
+#endif
+		return;
+	}
+}
+
+
+/*
+ * send an event to midi emulation
+ */
+static void
+fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop)
+{
+	snd_seq_event_t ev;
+	memset(&ev, 0, sizeof(ev));
+	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+	ev.data.control.channel = ch;
+	ev.data.control.param = param;
+	ev.data.control.value = val;
+	snd_emux_event_input(&ev, 0, port, atomic, hop);
+}
+
+#endif /* CONFIG_SND_OSSEMUL */
diff -Nru linux/sound/synth/emux/emux_proc.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_proc.c
--- linux/sound/synth/emux/emux_proc.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_proc.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,139 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Proc interface for Emu8k/Emu10k1 WaveTable synth
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+#include <sound/info.h>
+#include "emux_voice.h"
+
+#ifdef CONFIG_PROC_FS
+
+static void
+snd_emux_proc_info_read(snd_info_entry_t *entry, 
+			snd_info_buffer_t *buf)
+{
+	snd_emux_t *emu;
+	int i;
+
+	emu = snd_magic_cast(snd_emux_t, entry->private_data, return);
+	down(&emu->register_mutex);
+	if (emu->name)
+		snd_iprintf(buf, "Device: %s\n", emu->name);
+	snd_iprintf(buf, "Ports: %d\n", emu->num_ports);
+	snd_iprintf(buf, "Addresses:");
+	for (i = 0; i < emu->num_ports; i++)
+		snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]);
+	snd_iprintf(buf, "\n");
+	snd_iprintf(buf, "Use Counter: %d\n", emu->used);
+	snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices);
+	snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices);
+	if (emu->memhdr) {
+		snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size);
+		snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr));
+		snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks);
+	} else {
+		snd_iprintf(buf, "Memory Size: 0\n");
+	}
+	if (emu->sflist) {
+		down(&emu->sflist->presets_mutex);
+		snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size);
+		snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter);
+		snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter);
+		snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked);
+		snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked);
+		up(&emu->sflist->presets_mutex);
+	}
+#if 0  /* debug */
+	if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) {
+		snd_emux_voice_t *vp = &emu->voices[0];
+		snd_iprintf(buf, "voice 0: on\n");
+		snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n",
+			    vp->reg.parm.moddelay,
+			    vp->reg.parm.modatkhld,
+			    vp->reg.parm.moddcysus,
+			    vp->reg.parm.modrelease);
+		snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n",
+			    vp->reg.parm.voldelay,
+			    vp->reg.parm.volatkhld,
+			    vp->reg.parm.voldcysus,
+			    vp->reg.parm.volrelease);
+		snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n",
+			    vp->reg.parm.lfo1delay,
+			    vp->reg.parm.lfo2delay,
+			    vp->reg.parm.pefe);
+		snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n",
+			    vp->reg.parm.fmmod,
+			    vp->reg.parm.tremfrq,
+			    vp->reg.parm.fm2frq2);
+		snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n",
+			    vp->reg.parm.cutoff,
+			    vp->reg.parm.filterQ,
+			    vp->reg.parm.chorus,
+			    vp->reg.parm.reverb);
+		snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n",
+			    vp->avol, vp->acutoff, vp->apitch);
+		snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n",
+			    vp->apan, vp->aaux,
+			    vp->ptarget,
+			    vp->vtarget,
+			    vp->ftarget);
+		snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n",
+			    vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend);
+		snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset);
+	}
+#endif
+	up(&emu->register_mutex);
+}
+
+
+void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device)
+{
+	snd_info_entry_t *entry;
+	char name[64];
+
+	sprintf(name, "wavetableD%d", device);
+	entry = snd_info_create_card_entry(card, name, card->proc_root);
+	if (entry == NULL)
+		return;
+
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->private_data = emu;
+	entry->c.text.read_size = 1024;
+	entry->c.text.read = snd_emux_proc_info_read;
+	if (snd_info_register(entry) < 0)
+		snd_info_free_entry(entry);
+	else
+		emu->proc = entry;
+}
+
+void snd_emux_proc_free(snd_emux_t *emu)
+{
+	if (emu->proc) {
+		snd_info_unregister(emu->proc);
+		emu->proc = NULL;
+	}
+}
+
+#endif /* CONFIG_PROC_FS */
diff -Nru linux/sound/synth/emux/emux_seq.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_seq.c
--- linux/sound/synth/emux/emux_seq.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_seq.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,437 @@
+/*
+ *  Midi Sequencer interface routines.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#define __NO_VERSION__
+#include "emux_voice.h"
+#include <linux/slab.h>
+
+
+/* Prototypes for static functions */
+static void free_port(void *private);
+static void snd_emux_init_port(snd_emux_port_t *p);
+static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info);
+static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info);
+static int get_client(snd_card_t *card, int index, char *name);
+
+/*
+ * MIDI emulation operators
+ */
+static snd_midi_op_t emux_ops = {
+	snd_emux_note_on,
+	snd_emux_note_off,
+	snd_emux_key_press,
+	snd_emux_terminate_note,
+	snd_emux_control,
+	snd_emux_nrpn,
+	snd_emux_sysex,
+};
+
+
+/*
+ * number of MIDI channels
+ */
+#define MIDI_CHANNELS		16
+
+/*
+ * type flags for MIDI sequencer port
+ */
+#define DEFAULT_MIDI_TYPE	(SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_GM |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_GS |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_XG |\
+				 SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE)
+
+/*
+ */
+
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+
+/*
+ * Initialise the EMUX Synth by creating a client and registering
+ * a series of ports.
+ * Each of the ports will contain the 16 midi channels.  Applications
+ * can connect to these ports to play midi data.
+ */
+int
+snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index)
+{
+	int  i;
+	snd_seq_port_callback_t pinfo;
+	char tmpname[64];
+
+	sprintf(tmpname, "%s WaveTable", emu->name);
+	emu->client = get_client(card, index, tmpname);
+	if (emu->client < 0) {
+		snd_printk("can't create client\n");
+		return -ENODEV;
+	}
+
+	if (emu->num_ports < 0) {
+		snd_printk("seqports must be greater than zero\n");
+		emu->num_ports = 1;
+	} else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
+		snd_printk("too many ports."
+			   "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
+		emu->num_ports = SNDRV_EMUX_MAX_PORTS;
+	}
+
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.owner = THIS_MODULE;
+	pinfo.use = snd_emux_use;
+	pinfo.unuse = snd_emux_unuse;
+	pinfo.event_input = snd_emux_event_input;
+
+	for (i = 0; i < emu->num_ports; i++) {
+		snd_emux_port_t *p;
+
+		sprintf(tmpname, "%s Port %d", emu->name, i);
+		p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS,
+					 0, &pinfo);
+		if (p == NULL) {
+			snd_printk("can't create port\n");
+			return -ENOMEM;
+		}
+
+		p->port_mode =  SNDRV_EMUX_PORT_MODE_MIDI;
+		snd_emux_init_port(p);
+		emu->ports[i] = p->chset.port;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Detach from the ports that were set up for this synthesizer and
+ * destroy the kernel client.
+ */
+void
+snd_emux_detach_seq(snd_emux_t *emu)
+{
+	if (emu->voices)
+		snd_emux_terminate_all(emu);
+		
+	down(&emu->register_mutex);
+	if (emu->client >= 0) {
+		snd_seq_delete_kernel_client(emu->client);
+		emu->client = -1;
+	}
+	up(&emu->register_mutex);
+}
+
+
+/*
+ * create a sequencer port and channel_set
+ */
+
+snd_emux_port_t *
+snd_emux_create_port(snd_emux_t *emu, char *name,
+			int max_channels, int oss_port,
+			snd_seq_port_callback_t *callback)
+{
+	snd_emux_port_t *p;
+	int i, type, cap;
+
+	/* Allocate structures for this channel */
+	if ((p = snd_magic_kcalloc(snd_emux_port_t, 0, GFP_KERNEL)) == NULL) {
+		snd_printk("no memory\n");
+		return NULL;
+	}
+	p->chset.channels = snd_kcalloc(max_channels * sizeof(snd_midi_channel_t), GFP_KERNEL);
+	if (p->chset.channels == NULL) {
+		snd_printk("no memory\n");
+		snd_magic_kfree(p);
+		return NULL;
+	}
+	for (i = 0; i < max_channels; i++)
+		p->chset.channels[i].number = i;
+	p->chset.private_data = p;
+	p->chset.max_channels = max_channels;
+	p->emu = emu;
+	p->chset.client = emu->client;
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_create_effect(p);
+#endif
+	callback->private_free = free_port;
+	callback->private_data = p;
+
+	cap = SNDRV_SEQ_PORT_CAP_WRITE;
+	if (oss_port) {
+		type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
+	} else {
+		type = DEFAULT_MIDI_TYPE;
+		cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	}
+
+	p->chset.port = snd_seq_event_port_attach(emu->client, callback,
+						  cap, type, name);
+
+	return p;
+}
+
+
+/*
+ * release memory block for port
+ */
+static void
+free_port(void *private_data)
+{
+	snd_emux_port_t *p;
+
+	p = snd_magic_cast(snd_emux_port_t, private_data, return);
+	if (p) {
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		snd_emux_delete_effect(p);
+#endif
+		if (p->chset.channels)
+			kfree(p->chset.channels);
+		snd_magic_kfree(p);
+	}
+}
+
+
+#define DEFAULT_DRUM_FLAGS	(1<<9)
+
+/*
+ * initialize the port specific parameters
+ */
+static void
+snd_emux_init_port(snd_emux_port_t *p)
+{
+	p->drum_flags = DEFAULT_DRUM_FLAGS;
+	p->volume_atten = 0;
+
+	snd_emux_reset_port(p);
+}
+
+
+/*
+ * reset port
+ */
+void
+snd_emux_reset_port(snd_emux_port_t *port)
+{
+	int i;
+
+	/* stop all sounds */
+	snd_emux_sounds_off_all(port);
+
+	snd_midi_channel_set_clear(&port->chset);
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_clear_effect(port);
+#endif
+
+	/* set port specific control parameters */
+	port->ctrls[EMUX_MD_DEF_BANK] = 0;
+	port->ctrls[EMUX_MD_DEF_DRUM] = 0;
+	port->ctrls[EMUX_MD_REALTIME_PAN] = 1;
+
+	for (i = 0; i < port->chset.max_channels; i++) {
+		snd_midi_channel_t *chan = port->chset.channels + i;
+		chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
+	}
+}
+
+
+/*
+ * input sequencer event
+ */
+int
+snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data,
+		     int atomic, int hop)
+{
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL);
+	snd_assert(port != NULL && ev != NULL, return -EINVAL);
+
+	snd_midi_process_event(&emux_ops, ev, &port->chset);
+
+	return 0;
+}
+
+
+/*
+ * increment usage count
+ */
+int
+snd_emux_inc_count(snd_emux_t *emu)
+{
+	emu->used++;
+	if (!try_inc_mod_count(emu->ops.owner))
+		goto __error;
+	if (!try_inc_mod_count(emu->card->module)) {
+		dec_mod_count(emu->ops.owner);
+	      __error:
+		emu->used--;
+		return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * decrease usage count
+ */
+void
+snd_emux_dec_count(snd_emux_t *emu)
+{
+	dec_mod_count(emu->ops.owner);
+	emu->used--;
+	if (emu->used <= 0)
+		snd_emux_terminate_all(emu);
+	dec_mod_count(emu->card->module);
+}
+
+
+/*
+ * Routine that is called upon a first use of a particular port
+ */
+static int
+snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_emux_port_t *p;
+	snd_emux_t *emu;
+
+	p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL);
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+
+	down(&emu->register_mutex);
+	snd_emux_init_port(p);
+	snd_emux_inc_count(emu);
+	up(&emu->register_mutex);
+	return 0;
+}
+
+/*
+ * Routine that is called upon the last unuse() of a particular port.
+ */
+static int
+snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_emux_port_t *p;
+	snd_emux_t *emu;
+
+	p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL);
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+
+	down(&emu->register_mutex);
+	snd_emux_sounds_off_all(p);
+	snd_emux_dec_count(emu);
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+/*
+ * Create a sequencer client
+ */
+static int
+get_client(snd_card_t *card, int index, char *name)
+{
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	int client;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = NULL;
+	callbacks.allow_input = 1;
+	callbacks.allow_output = 1;
+
+	/* Find a free client, start from 1 as the MPU expects to use 0 */
+	client = snd_seq_create_kernel_client(card, index, &callbacks);
+	if (client < 0)
+		return client;
+
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, name);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	return client;
+}
+
+
+/*
+ * attach virtual rawmidi devices
+ */
+int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card)
+{
+	int i;
+
+	emu->vmidi = NULL;
+	if (emu->midi_ports <= 0)
+		return 0;
+
+	emu->vmidi = snd_kcalloc(sizeof(snd_rawmidi_t*) * emu->midi_ports, GFP_KERNEL);
+	if (emu->vmidi == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < emu->midi_ports; i++) {
+		snd_rawmidi_t *rmidi;
+		snd_virmidi_dev_t *rdev;
+		if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0)
+			goto __error;
+		rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue);
+		sprintf(rmidi->name, "%s Synth MIDI", emu->name);
+		rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
+		rdev->client = emu->client;
+		rdev->port = emu->ports[i];
+		if (snd_device_register(card, rmidi) < 0) {
+			snd_device_free(card, rmidi);
+			goto __error;
+		}
+		emu->vmidi[i] = rmidi;
+		//snd_printk("virmidi %d ok\n", i);
+	}
+	return 0;
+
+__error:
+	//snd_printk("error init..\n");
+	snd_emux_delete_virmidi(emu);
+	return -ENOMEM;
+}
+
+int snd_emux_delete_virmidi(snd_emux_t *emu)
+{
+	int i;
+
+	if (emu->vmidi == NULL)
+		return 0;
+
+	for (i = 0; i < emu->midi_ports; i++) {
+		if (emu->vmidi[i])
+			snd_device_free(emu->card, emu->vmidi[i]);
+	}
+	kfree(emu->vmidi);
+	emu->vmidi = NULL;
+	return 0;
+}
diff -Nru linux/sound/synth/emux/emux_synth.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_synth.c
--- linux/sound/synth/emux/emux_synth.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_synth.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,974 @@
+/*
+ *  Midi synth routines for the Emu8k/Emu10k1
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Contains code based on awe_wave.c by Takashi Iwai
+ *
+ *   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
+ *
+ */
+
+#define __NO_VERSION__
+#include "emux_voice.h"
+#include <sound/asoundef.h>
+
+/*
+ * Prototypes
+ */
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table);
+static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan);
+static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free);
+static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass);
+static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free);
+static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update);
+static void setup_voice(snd_emux_voice_t *vp);
+static int calc_pan(snd_emux_voice_t *vp);
+static int calc_volume(snd_emux_voice_t *vp);
+static int calc_pitch(snd_emux_voice_t *vp);
+
+
+/*
+ * Start a note.
+ */
+void
+snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	snd_emux_t *emu;
+	int i, key, nvoices;
+	snd_emux_voice_t *vp;
+	snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES];
+	unsigned long flags;
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.get_voice != NULL, return);
+	snd_assert(emu->ops.trigger != NULL, return);
+
+	key = note; /* remember the original note */
+	nvoices = get_zone(emu, port, &note, vel, chan, table);
+	if (! nvoices)
+		return;
+
+	/* exclusive note off */
+	for (i = 0; i < nvoices; i++) {
+		snd_sf_zone_t *zp = table[i];
+		if (zp && zp->v.exclusiveClass)
+			exclusive_note_off(emu, port, zp->v.exclusiveClass);
+	}
+
+#if 0 // seems not necessary
+	/* Turn off the same note on the same channel. */
+	terminate_note1(emu, key, chan, 0);
+#endif
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < nvoices; i++) {
+
+		/* set up each voice parameter */
+		/* at this stage, we don't trigger the voice yet. */
+
+		if (table[i] == NULL)
+			continue;
+
+		vp = emu->ops.get_voice(emu, port);
+		if (vp == NULL || vp->ch < 0)
+			continue;
+		snd_assert(vp->emu != NULL && vp->hw != NULL, return);
+		if (STATE_IS_PLAYING(vp->state))
+			emu->ops.terminate(vp);
+
+		vp->time = emu->use_time++;
+		vp->chan = chan;
+		vp->port = port;
+		vp->key = key;
+		vp->note = note;
+		vp->velocity = vel;
+		vp->zone = table[i];
+		if (vp->zone->sample)
+			vp->block = vp->zone->sample->block;
+		else
+			vp->block = NULL;
+
+		setup_voice(vp);
+
+		vp->state = SNDRV_EMUX_ST_STANDBY;
+		if (emu->ops.prepare) {
+			vp->state = SNDRV_EMUX_ST_OFF;
+			if (emu->ops.prepare(vp) >= 0)
+				vp->state = SNDRV_EMUX_ST_STANDBY;
+		}
+	}
+
+	/* start envelope now */
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->state == SNDRV_EMUX_ST_STANDBY &&
+		    vp->chan == chan) {
+			emu->ops.trigger(vp);
+			vp->state = SNDRV_EMUX_ST_ON;
+			vp->ontime = jiffies; /* remember the trigger timing */
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
+		/* clear voice position for the next note on this channel */
+		snd_emux_effect_table_t *fx = chan->private;
+		if (fx) {
+			fx->flag[EMUX_FX_SAMPLE_START] = 0;
+			fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0;
+		}
+	}
+#endif
+}
+
+/*
+ * Release a note in response to a midi note off.
+ */
+void
+snd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	int ch;
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.release != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (STATE_IS_PLAYING(vp->state) &&
+		    vp->chan == chan && vp->key == note) {
+			vp->time = emu->use_time++;
+			vp->state = SNDRV_EMUX_ST_RELEASED;
+			if (vp->ontime == jiffies) {
+				/* if note-off is sent too shortly after
+				 * note-on, emuX engine cannot produce the sound
+				 * correctly.  so we'll release this note
+				 * a bit later via timer callback.
+				 */
+				vp->state = SNDRV_EMUX_ST_PENDING;
+				if (! emu->timer_active) {
+					emu->tlist.expires = jiffies + 1;
+					add_timer(&emu->tlist);
+					emu->timer_active = 1;
+				}
+			} else
+				/* ok now release the note */
+				emu->ops.release(vp);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * timer callback
+ *
+ * release the pending note-offs
+ */
+void snd_emux_timer_callback(unsigned long data)
+{
+	snd_emux_t *emu = snd_magic_cast(snd_emux_t, (void*)data, return);
+	snd_emux_voice_t *vp;
+	int ch, do_again = 0;
+
+	spin_lock(&emu->voice_lock);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (vp->state == SNDRV_EMUX_ST_PENDING) {
+			if (vp->ontime == jiffies)
+				do_again++; /* release this at the next interrupt */
+			else {
+				emu->ops.release(vp);
+				vp->state = SNDRV_EMUX_ST_RELEASED;
+			}
+		}
+	}
+	if (do_again) {
+		emu->tlist.expires = jiffies + 1;
+		add_timer(&emu->tlist);
+		emu->timer_active = 1;
+	} else
+		emu->timer_active = 0;
+	spin_unlock(&emu->voice_lock);
+}
+
+/*
+ * key pressure change
+ */
+void
+snd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	int ch;
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (vp->state == SNDRV_EMUX_ST_ON &&
+		    vp->chan == chan && vp->key == note) {
+			vp->velocity = vel;
+			update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Modulate the voices which belong to the channel
+ */
+void
+snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update)
+{
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	int i;
+	unsigned long flags;
+
+	if (! update)
+		return;
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->chan == chan)
+			update_voice(emu, vp, update);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * Modulate all the voices which belong to the port.
+ */
+void
+snd_emux_update_port(snd_emux_port_t *port, int update)
+{
+	snd_emux_t *emu; 
+	snd_emux_voice_t *vp;
+	int i;
+	unsigned long flags;
+
+	if (! update)
+		return;
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->port == port)
+			update_voice(emu, vp, update);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Deal with a controler type event.  This includes all types of
+ * control events, not just the midi controllers
+ */
+void
+snd_emux_control(void *p, int type, snd_midi_channel_t *chan)
+{
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL && chan != NULL, return);
+
+	switch (type) {
+	case MIDI_CTL_MSB_MAIN_VOLUME:
+	case MIDI_CTL_MSB_EXPRESSION:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME);
+		break;
+		
+	case MIDI_CTL_MSB_PAN:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
+		break;
+
+	case MIDI_CTL_SOFT_PEDAL:
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		/* FIXME: this is an emulation */
+		snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160,
+				     EMUX_FX_FLAG_ADD);
+#endif
+		break;
+
+	case MIDI_CTL_PITCHBEND:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH);
+		break;
+
+	case MIDI_CTL_MSB_MODWHEEL:
+	case MIDI_CTL_CHAN_PRESSURE:
+		snd_emux_update_channel(port, chan,
+					SNDRV_EMUX_UPDATE_FMMOD |
+					SNDRV_EMUX_UPDATE_FM2FRQ2);
+		break;
+
+	}
+
+	if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) {
+		snd_emux_xg_control(port, chan, type);
+	}
+}
+
+
+/*
+ * for Emu10k1 - release at least 1 voice currently using
+ */
+int
+snd_emux_release_voice(snd_emux_t *emu)
+{
+	return 0;
+}
+
+
+/*
+ * terminate note - if free flag is true, free the terminated voice
+ */
+static void
+terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free)
+{
+	int  i;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) && vp->chan == chan &&
+		    vp->key == note)
+			terminate_voice(emu, vp, free);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * terminate note - exported for midi emulation
+ */
+void
+snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *port;
+
+	port = snd_magic_cast(snd_emux_port_t, p, return);
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.terminate != NULL, return);
+
+	terminate_note1(emu, note, chan, 1);
+}
+
+
+/*
+ * Terminate all the notes
+ */
+void
+snd_emux_terminate_all(snd_emux_t *emu)
+{
+	int i;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state))
+			terminate_voice(emu, vp, 0);
+		if (vp->state == SNDRV_EMUX_ST_OFF) {
+			if (emu->ops.free_voice)
+				emu->ops.free_voice(vp);
+			if (emu->ops.reset)
+				emu->ops.reset(emu, i);
+		}
+		vp->time = 0;
+	}
+	/* initialize allocation time */
+	emu->use_time = 0;
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Terminate all voices associated with the given port
+ */
+void
+snd_emux_sounds_off_all(snd_emux_port_t *port)
+{
+	int i;
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+
+	snd_assert(port != NULL, return);
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.terminate != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) &&
+		    vp->port == port)
+			terminate_voice(emu, vp, 0);
+		if (vp->state == SNDRV_EMUX_ST_OFF) {
+			if (emu->ops.free_voice)
+				emu->ops.free_voice(vp);
+			if (emu->ops.reset)
+				emu->ops.reset(emu, i);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Terminate all voices that have the same exclusive class.  This
+ * is mainly for drums.
+ */
+static void
+exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass)
+{
+	snd_emux_voice_t *vp;
+	int  i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) && vp->port == port &&
+		    vp->reg.exclusiveClass == exclass) {
+			terminate_voice(emu, vp, 0);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * terminate a voice
+ * if free flag is true, call free_voice after termination
+ */
+static void
+terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free)
+{
+	emu->ops.terminate(vp);
+	vp->time = emu->use_time++;
+	vp->chan = NULL;
+	vp->port = NULL;
+	vp->zone = NULL;
+	vp->block = NULL;
+	vp->state = SNDRV_EMUX_ST_OFF;
+	if (free && emu->ops.free_voice)
+		emu->ops.free_voice(vp);
+}
+
+
+/*
+ * Modulate the voice
+ */
+static void
+update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update)
+{
+	if (!STATE_IS_PLAYING(vp->state))
+		return;
+
+	if (vp->chan == NULL || vp->port == NULL)
+		return;
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		calc_volume(vp);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		calc_pitch(vp);
+	if (update & SNDRV_EMUX_UPDATE_PAN) {
+		if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN))
+			return;
+	}
+	emu->ops.update(vp, update);
+}
+
+
+#if 0 // not used
+/* table for volume target calculation */
+static unsigned short voltarget[16] = { 
+	0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58,
+	0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90
+};
+#endif
+
+#define LO_BYTE(v)	((v) & 0xff)
+#define HI_BYTE(v)	(((v) >> 8) & 0xff)
+
+/*
+ * Sets up the voice structure by calculating some values that
+ * will be needed later.
+ */
+static void
+setup_voice(snd_emux_voice_t *vp)
+{
+	soundfont_voice_parm_t *parm;
+	int pitch;
+
+	/* copy the original register values */
+	vp->reg = vp->zone->v;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_setup_effect(vp);
+#endif
+
+	/* reset status */
+	vp->apan = -1;
+	vp->avol = -1;
+	vp->apitch = -1;
+
+	calc_volume(vp);
+	calc_pitch(vp);
+	calc_pan(vp);
+
+	parm = &vp->reg.parm;
+
+	/* compute filter target and correct modulation parameters */
+	if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) {
+		parm->moddelay = 0xbfff;
+		pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch;
+		if (pitch > 0xffff)
+			pitch = 0xffff;
+		/* calculate filter target */
+		vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe);
+		LIMITVALUE(vp->ftarget, 0, 255);
+		vp->ftarget <<= 8;
+	} else {
+		vp->ftarget = parm->cutoff;
+		vp->ftarget <<= 8;
+		pitch = vp->apitch;
+	}
+
+	/* compute pitch target */
+	if (pitch != 0xffff) {
+		vp->ptarget = 1 << (pitch >> 12);
+		if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710;
+		if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710;
+		if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710;
+		vp->ptarget += (vp->ptarget >> 1);
+		if (vp->ptarget > 0xffff) vp->ptarget = 0xffff;
+	} else
+		vp->ptarget = 0xffff;
+
+	if (LO_BYTE(parm->modatkhld) >= 0x80) {
+		parm->modatkhld &= ~0xff;
+		parm->modatkhld |= 0x7f;
+	}
+
+	/* compute volume target and correct volume parameters */
+	vp->vtarget = 0;
+#if 0 /* FIXME: this leads to some clicks.. */
+	if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) {
+		parm->voldelay = 0xbfff;
+		vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4);
+	}
+#endif
+
+	if (LO_BYTE(parm->volatkhld) >= 0x80) {
+		parm->volatkhld &= ~0xff;
+		parm->volatkhld |= 0x7f;
+	}
+}
+
+/*
+ * calculate pitch parameter
+ */
+static unsigned char pan_volumes[256] = {
+0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a,
+0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52,
+0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75,
+0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92,
+0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab,
+0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0,
+0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1,
+0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf,
+0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9,
+0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1,
+0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7,
+0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,
+0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,
+0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+static int
+calc_pan(snd_emux_voice_t *vp)
+{
+	snd_midi_channel_t *chan = vp->chan;
+	int pan;
+
+	/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
+	if (vp->reg.fixpan > 0)	/* 0-127 */
+		pan = 255 - (int)vp->reg.fixpan * 2;
+	else {
+		pan = chan->control[MIDI_CTL_MSB_PAN] - 64;
+		if (vp->reg.pan >= 0) /* 0-127 */
+			pan += vp->reg.pan - 64;
+		pan = 127 - (int)pan * 2;
+	}
+	LIMITVALUE(pan, 0, 255);
+
+#if 1
+	/* using volume table */
+	if (vp->apan != (int)pan_volumes[pan]) {
+		vp->apan = pan_volumes[pan];
+		vp->aaux = pan_volumes[255 - pan];
+		return 1;
+	}
+	return 0;
+#else
+	/* assuming linear volume */
+	if (pan != vp->apan) {
+		vp->apan = pan;
+		if (pan == 0)
+			vp->aaux = 0xff;
+		else
+			vp->aaux = (-pan) & 0xff;
+		return 1;
+	} else
+		return 0;
+#endif
+}
+
+
+/*
+ * calculate volume attenuation
+ *
+ * Voice volume is controlled by volume attenuation parameter.
+ * So volume becomes maximum when avol is 0 (no attenuation), and
+ * minimum when 255 (-96dB or silence).
+ */
+
+/* tables for volume->attenuation calculation */
+static unsigned char voltab1[128] = {
+   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+   0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
+   0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
+   0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
+   0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char voltab2[128] = {
+   0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
+   0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
+   0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
+   0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
+   0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char expressiontab[128] = {
+   0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
+   0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
+   0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
+   0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
+   0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
+   0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
+   0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
+   0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
+   0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
+   0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * Magic to calculate the volume (actually attenuation) from all the
+ * voice and channels parameters.
+ */
+static int
+calc_volume(snd_emux_voice_t *vp)
+{
+	int vol;
+	int main_vol, expression_vol, master_vol;
+	snd_midi_channel_t *chan = vp->chan;
+	snd_emux_port_t *port = vp->port;
+
+	expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION];
+	LIMITMAX(vp->velocity, 127);
+	LIMITVALUE(expression_vol, 0, 127);
+	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
+		/* 0 - 127 */
+		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME];
+		vol = (vp->velocity * main_vol * expression_vol) / (127*127);
+		vol = vol * vp->reg.amplitude / 127;
+
+		LIMITVALUE(vol, 0, 127);
+
+		/* calc to attenuation */
+		vol = snd_sf_vol_table[vol];
+
+	} else {
+		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127;
+		LIMITVALUE(main_vol, 0, 127);
+
+		vol = voltab1[main_vol] + voltab2[vp->velocity];
+		vol = (vol * 8) / 3;
+		vol += vp->reg.attenuation;
+		vol += ((0x100 - vol) * expressiontab[expression_vol])/128;
+	}
+
+	master_vol = port->chset.gs_master_volume;
+	LIMITVALUE(master_vol, 0, 127);
+	vol += snd_sf_vol_table[master_vol];
+	vol += port->volume_atten;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	if (chan->private) {
+		snd_emux_effect_table_t *fx = chan->private;
+		vol += fx->val[EMUX_FX_ATTEN];
+	}
+#endif
+
+	LIMITVALUE(vol, 0, 255);
+	if (vp->avol == vol)
+		return 0; /* value unchanged */
+
+	vp->avol = vol;
+	if (!SF_IS_DRUM_BANK(get_bank(port, chan))
+	    && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) {
+		int atten;
+		if (vp->velocity < 70)
+			atten = 70;
+		else
+			atten = vp->velocity;
+		vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7;
+	} else {
+		vp->acutoff = vp->reg.parm.cutoff;
+	}
+
+	return 1; /* value changed */
+}
+
+/*
+ * calculate pitch offset
+ *
+ * 0xE000 is no pitch offset at 44100Hz sample.
+ * Every 4096 is one octave.
+ */
+
+static int
+calc_pitch(snd_emux_voice_t *vp)
+{
+	snd_midi_channel_t *chan = vp->chan;
+	int offset;
+
+	/* calculate offset */
+	if (vp->reg.fixkey >= 0) {
+		offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12;
+	} else {
+		offset = (vp->note - vp->reg.root) * 4096 / 12;
+	}
+	offset = (offset * vp->reg.scaleTuning) / 100;
+	offset += vp->reg.tune * 4096 / 1200;
+	if (chan->midi_pitchbend != 0) {
+		/* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */
+		offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072;
+	}
+
+	/* tuning via RPN:
+	 *   coarse = -8192 to 8192 (100 cent per 128)
+	 *   fine = -8192 to 8192 (max=100cent)
+	 */
+	/* 4096 = 1200 cents in emu8000 parameter */
+	offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128);
+	offset += chan->gm_rpn_fine_tuning / 24;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	/* add initial pitch correction */
+	if (chan->private) {
+		snd_emux_effect_table_t *fx = chan->private;
+		if (fx->flag[EMUX_FX_INIT_PITCH])
+			offset += fx->val[EMUX_FX_INIT_PITCH];
+	}
+#endif
+
+	/* 0xe000: root pitch */
+	offset += 0xe000 + vp->reg.rate_offset;
+	offset += vp->emu->pitch_shift;
+	LIMITVALUE(offset, 0, 0xffff);
+	if (offset == vp->apitch)
+		return 0; /* unchanged */
+	vp->apitch = offset;
+	return 1; /* value changed */
+}
+
+/*
+ * Get the bank number assigned to the channel
+ */
+static int
+get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan)
+{
+	int val;
+
+	switch (port->chset.midi_mode) {
+	case SNDRV_MIDI_MODE_XG:
+		val = chan->control[MIDI_CTL_MSB_BANK];
+		if (val == 127)
+			return 128; /* return drum bank */
+		return chan->control[MIDI_CTL_LSB_BANK];
+
+	case SNDRV_MIDI_MODE_GS:
+		if (chan->drum_channel)
+			return 128;
+		/* ignore LSB (bank map) */
+		return chan->control[MIDI_CTL_MSB_BANK];
+		
+	default:
+		if (chan->drum_channel)
+			return 128;
+		return chan->control[MIDI_CTL_MSB_BANK];
+	}
+}
+
+
+/* Look for the zones matching with the given note and velocity.
+ * The resultant zones are stored on table.
+ */
+static int
+get_zone(snd_emux_t *emu, snd_emux_port_t *port,
+	 int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table)
+{
+	int preset, bank, def_preset, def_bank;
+
+	bank = get_bank(port, chan);
+	preset = chan->midi_program;
+
+	if (SF_IS_DRUM_BANK(bank)) {
+		def_preset = port->ctrls[EMUX_MD_DEF_DRUM];
+		def_bank = bank;
+	} else {
+		def_preset = preset;
+		def_bank = port->ctrls[EMUX_MD_DEF_BANK];
+	}
+
+	return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank,
+					 def_preset, def_bank,
+					 table, SNDRV_EMUX_MAX_MULTI_VOICES);
+}
+
+/*
+ */
+void
+snd_emux_init_voices(snd_emux_t *emu)
+{
+	snd_emux_voice_t *vp;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		vp->ch = -1; /* not used */
+		vp->state = SNDRV_EMUX_ST_OFF;
+		vp->chan = NULL;
+		vp->port = NULL;
+		vp->time = 0;
+		vp->emu = emu;
+		vp->hw = emu->hw;
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ */
+void snd_emux_lock_voice(snd_emux_t *emu, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
+		emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
+	else
+		snd_printk("invalid voice for lock %d (state = %x)\n",
+			   voice, emu->voices[voice].state);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ */
+void snd_emux_unlock_voice(snd_emux_t *emu, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
+		emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
+	else
+		snd_printk("invalid voice for unlock %d (state = %x)\n",
+			   voice, emu->voices[voice].state);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
diff -Nru linux/sound/synth/emux/emux_voice.h linux-2.4.19-pre5-mjc/sound/synth/emux/emux_voice.h
--- linux/sound/synth/emux/emux_voice.h	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_voice.h	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,84 @@
+#ifndef __EMUX_VOICE_H
+#define __EMUX_VOICE_H
+
+/*
+ * A structure to keep track of each hardware voice
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+
+/* Prototypes for emux_seq.c */
+int snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index);
+void snd_emux_detach_seq(snd_emux_t *emu);
+snd_emux_port_t *snd_emux_create_port(snd_emux_t *emu, char *name, int max_channels, int type, snd_seq_port_callback_t *callback);
+void snd_emux_reset_port(snd_emux_port_t *port);
+int snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
+int snd_emux_inc_count(snd_emux_t *emu);
+void snd_emux_dec_count(snd_emux_t *emu);
+int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card);
+int snd_emux_delete_virmidi(snd_emux_t *emu);
+
+/* Prototypes for emux_synth.c */
+void snd_emux_init_voices(snd_emux_t *emu);
+
+void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan);
+void snd_emux_control(void *p, int type, struct snd_midi_channel *chan);
+
+void snd_emux_sounds_off_all(snd_emux_port_t *port);
+void snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update);
+void snd_emux_update_port(snd_emux_port_t *port, int update);
+
+void snd_emux_timer_callback(unsigned long data);
+
+/* emux_effect.c */
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+void snd_emux_create_effect(snd_emux_port_t *p);
+void snd_emux_delete_effect(snd_emux_port_t *p);
+void snd_emux_clear_effect(snd_emux_port_t *p);
+void snd_emux_setup_effect(snd_emux_voice_t *vp);
+void snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val);
+void snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode);
+#endif
+
+/* emux_nrpn.c */
+void snd_emux_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param);
+void snd_emux_nrpn(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+
+/* emux_oss.c */
+void snd_emux_init_seq_oss(snd_emux_t *emu);
+void snd_emux_detach_seq_oss(snd_emux_t *emu);
+
+/* emux_proc.c */
+#ifdef CONFIG_PROC_FS
+void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device);
+void snd_emux_proc_free(snd_emux_t *emu);
+#endif
+
+#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON)
+
+#endif
diff -Nru linux/sound/synth/emux/soundfont.c linux-2.4.19-pre5-mjc/sound/synth/emux/soundfont.c
--- linux/sound/synth/emux/soundfont.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/emux/soundfont.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,1462 @@
+/*
+ *  Soundfont generic routines.
+ *	It is intended that these should be used by any driver that is willing
+ *	to accept soundfont patches.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ */
+/*
+ * Deal with reading in of a soundfont.  Code follows the OSS way
+ * of doing things so that the old sfxload utility can be used.
+ * Everything may change when there is an alsa way of doing things.
+ */
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soundfont.h>
+
+/* Prototypes for static functions */
+
+static int open_patch(snd_sf_list_t *sflist, const char *data, int count, int client);
+static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name);
+static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name);
+static int close_patch(snd_sf_list_t *sflist);
+static int probe_data(snd_sf_list_t *sflist, int sample_id);
+static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp);
+static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf);
+static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp);
+static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf);
+static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp);
+static int load_map(snd_sf_list_t *sflist, const void *data, int count);
+static int load_info(snd_sf_list_t *sflist, const void *data, long count);
+static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr);
+static void init_voice_info(soundfont_voice_info_t *avp);
+static void init_voice_parm(soundfont_voice_parm_t *pp);
+static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp);
+static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id);
+static int load_data(snd_sf_list_t *sflist, const void *data, long count);
+static void rebuild_presets(snd_sf_list_t *sflist);
+static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur);
+static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp);
+static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key);
+static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level);
+static int get_index(int bank, int instr, int key);
+static void snd_sf_init(snd_sf_list_t *sflist);
+static void snd_sf_clear(snd_sf_list_t *sflist);
+
+/*
+ * lock access to sflist
+ */
+static int
+lock_preset(snd_sf_list_t *sflist, int nonblock)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->sf_locked && nonblock) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	down(&sflist->presets_mutex);
+	sflist->sf_locked = 1;
+	return 0;
+}
+
+
+/*
+ * remove lock
+ */
+static void
+unlock_preset(snd_sf_list_t *sflist)
+{
+	up(&sflist->presets_mutex);
+	sflist->sf_locked = 0;
+}
+
+
+/*
+ * close the patch if the patch was opened by this client.
+ */
+int
+snd_soundfont_close_check(snd_sf_list_t *sflist, int client)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client == client)  {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return close_patch(sflist);
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	return 0;
+}
+
+
+/*
+ * Deal with a soundfont patch.  Any driver could use these routines
+ * although it was designed for the AWE64.
+ *
+ * The sample_write and callargs pararameters allow a callback into
+ * the actual driver to write sample data to the board or whatever
+ * it wants to do with it.
+ */
+int
+snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client)
+{
+	soundfont_patch_info_t patch;
+	unsigned long flags;
+	int  rc;
+
+	if (count < sizeof(patch)) {
+		snd_printk("patch record too small %ld\n", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(&patch, data, sizeof(patch)))
+		return -EFAULT;
+
+	count -= sizeof(patch);
+	data += sizeof(patch);
+
+	if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) {
+		snd_printk("'The wrong kind of patch' %x\n", patch.key);
+		return -EINVAL;
+	}
+	if (count < patch.len) {
+		snd_printk("Patch too short %ld, need %d\n", count, patch.len);
+		return -EINVAL;
+	}
+	if (patch.len < 0) {
+		snd_printk("poor length %d\n", patch.len);
+		return -EINVAL;
+	}
+
+	if (patch.type == SNDRV_SFNT_OPEN_PATCH) {
+		/* grab sflist to open */
+		lock_preset(sflist, 0);
+		rc = open_patch(sflist, data, count, client);
+		unlock_preset(sflist);
+		return rc;
+	}
+
+	/* check if other client already opened patch */
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client != client) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	lock_preset(sflist, 0);
+	rc = -EINVAL;
+	switch (patch.type) {
+	case SNDRV_SFNT_LOAD_INFO:
+		rc = load_info(sflist, data, count);
+		break;
+	case SNDRV_SFNT_LOAD_DATA:
+		rc = load_data(sflist, data, count);
+		break;
+	case SNDRV_SFNT_CLOSE_PATCH:
+		rc = close_patch(sflist);
+		break;
+	case SNDRV_SFNT_REPLACE_DATA:
+		/*rc = replace_data(&patch, data, count);*/
+		break;
+	case SNDRV_SFNT_MAP_PRESET:
+		rc = load_map(sflist, data, count);
+		break;
+	case SNDRV_SFNT_PROBE_DATA:
+		rc = probe_data(sflist, patch.optarg);
+		break;
+	case SNDRV_SFNT_REMOVE_INFO:
+		/* patch must be opened */
+		if (sflist->currsf) {
+			snd_printk("soundfont: remove_info: patch not opened\n");
+			rc = -EINVAL;
+		} else {
+			int bank, instr;
+			bank = ((unsigned short)patch.optarg >> 8) & 0xff;
+			instr = (unsigned short)patch.optarg & 0xff;
+			if (! remove_info(sflist, sflist->currsf, bank, instr))
+				rc = -EINVAL;
+			else
+				rc = 0;
+		}
+		break;
+	}
+	unlock_preset(sflist);
+
+	return rc;
+}
+
+
+/* check if specified type is special font (GUS or preset-alias) */
+static inline int
+is_special_type(int type)
+{
+	type &= 0x0f;
+	return (type == SNDRV_SFNT_PAT_TYPE_GUS ||
+		type == SNDRV_SFNT_PAT_TYPE_MAP);
+}
+
+
+/* open patch; create sf list */
+static int
+open_patch(snd_sf_list_t *sflist, const char *data, int count, int client)
+{
+	soundfont_open_parm_t parm;
+	snd_soundfont_t *sf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client >= 0 || sflist->currsf) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	if (copy_from_user(&parm, data, sizeof(parm)))
+		return -EFAULT;
+
+	if (is_special_type(parm.type)) {
+		parm.type |= SNDRV_SFNT_PAT_SHARED;
+		sf = newsf(sflist, parm.type, NULL);
+	} else 
+		sf = newsf(sflist, parm.type, parm.name);
+	if (sf == NULL) {
+		return -ENOMEM;
+	}
+
+	sflist->open_client = client;
+	sflist->currsf = sf;
+
+	return 0;
+}
+
+/*
+ * Allocate a new soundfont structure.
+ */
+static snd_soundfont_t *
+newsf(snd_sf_list_t *sflist, int type, char *name)
+{
+	snd_soundfont_t *sf;
+
+	/* check the shared fonts */
+	if (type & SNDRV_SFNT_PAT_SHARED) {
+		for (sf = sflist->fonts; sf; sf = sf->next) {
+			if (is_identical_font(sf, type, name)) {
+				return sf;
+			}
+		}
+	}
+
+	/* not found -- create a new one */
+	sf = (snd_soundfont_t*)snd_kcalloc(sizeof(*sf), GFP_KERNEL);
+	if (sf == NULL)
+		return NULL;
+	sf->id = sflist->fonts_size;
+	sflist->fonts_size++;
+
+	/* prepend this record */
+	sf->next = sflist->fonts;
+	sflist->fonts = sf;
+
+	sf->type = type;
+	sf->zones = NULL;
+	sf->samples = NULL;
+	if (name)
+		memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN);
+
+	return sf;
+}
+
+/* check if the given name matches to the existing list */
+static int
+is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name)
+{
+	return ((sf->type & SNDRV_SFNT_PAT_SHARED) &&
+		(sf->type & 0x0f) == (type & 0x0f) &&
+		(name == NULL ||
+		 memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0));
+}
+
+/*
+ * Close the current patch.
+ */
+static int
+close_patch(snd_sf_list_t *sflist)
+{
+	sflist->currsf = NULL;
+	sflist->open_client = -1;
+
+	rebuild_presets(sflist);
+
+	return 0;
+
+}
+
+/* probe sample in the current list -- nothing to be loaded */
+static int
+probe_data(snd_sf_list_t *sflist, int sample_id)
+{
+	/* patch must be opened */
+	if (sflist->currsf) {
+		/* search the specified sample by optarg */
+		if (find_sample(sflist->currsf, sample_id))
+			return 0;
+	}
+	return -EINVAL;
+}
+
+/*
+ * increment zone counter
+ */
+static void
+set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp)
+{
+	zp->counter = sflist->zone_counter++;
+	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
+		sflist->zone_locked = sflist->zone_counter;
+}
+
+/*
+ * allocate a new zone record
+ */
+static snd_sf_zone_t *
+sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf)
+{
+	snd_sf_zone_t *zp;
+
+	if ((zp = snd_kcalloc(sizeof(*zp), GFP_KERNEL)) == NULL)
+		return NULL;
+	zp->next = sf->zones;
+	sf->zones = zp;
+
+	init_voice_info(&zp->v);
+
+	set_zone_counter(sflist, sf, zp);
+	return zp;
+}
+
+
+/*
+ * increment sample couter
+ */
+static void
+set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp)
+{
+	sp->counter = sflist->sample_counter++;
+	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
+		sflist->sample_locked = sflist->sample_counter;
+}
+
+/*
+ * allocate a new sample list record
+ */
+static snd_sf_sample_t *
+sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf)
+{
+	snd_sf_sample_t *sp;
+
+	if ((sp = snd_kcalloc(sizeof(*sp), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	sp->next = sf->samples;
+	sf->samples = sp;
+
+	set_sample_counter(sflist, sf, sp);
+	return sp;
+}
+
+/*
+ * delete sample list -- this is an exceptional job.
+ * only the last allocated sample can be deleted.
+ */
+static void
+sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp)
+{
+	/* only last sample is accepted */
+	if (sp == sf->samples) {
+		sf->samples = sp->next;
+		kfree(sp);
+	}
+}
+
+
+/* load voice map */
+static int
+load_map(snd_sf_list_t *sflist, const void *data, int count)
+{
+	snd_sf_zone_t *zp, *prevp;
+	snd_soundfont_t *sf;
+	soundfont_voice_map_t map;
+
+	/* get the link info */
+	if (count < sizeof(map))
+		return -EINVAL;
+	if (copy_from_user(&map, data, sizeof(map)))
+		return -EFAULT;
+
+	if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS)
+		return -EINVAL;
+	
+	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL);
+	if (sf == NULL)
+		return -ENOMEM;
+
+	prevp = NULL;
+	for (zp = sf->zones; zp; prevp = zp, zp = zp->next) {
+		if (zp->mapped &&
+		    zp->instr == map.map_instr &&
+		    zp->bank == map.map_bank &&
+		    zp->v.low == map.map_key &&
+		    zp->v.start == map.src_instr &&
+		    zp->v.end == map.src_bank &&
+		    zp->v.fixkey == map.src_key) {
+			/* the same mapping is already present */
+			/* relink this record to the link head */
+			if (prevp) {
+				prevp->next = zp->next;
+				zp->next = sf->zones;
+				sf->zones = zp;
+			}
+			/* update the counter */
+			set_zone_counter(sflist, sf, zp);
+			return 0;
+		}
+	}
+
+	/* create a new zone */
+	if ((zp = sf_zone_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+
+	zp->bank = map.map_bank;
+	zp->instr = map.map_instr;
+	zp->mapped = 1;
+	if (map.map_key >= 0) {
+		zp->v.low = map.map_key;
+		zp->v.high = map.map_key;
+	}
+	zp->v.start = map.src_instr;
+	zp->v.end = map.src_bank;
+	zp->v.fixkey = map.src_key;
+	zp->v.sf_id = sf->id;
+
+	add_preset(sflist, zp);
+
+	return 0;
+}
+
+
+/* remove the present instrument layers */
+static int
+remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr)
+{
+	snd_sf_zone_t *prev, *next, *p;
+	int removed = 0;
+
+	prev = NULL;
+	for (p = sf->zones; p; p = next) {
+		next = p->next;
+		if (! p->mapped &&
+		    p->bank == bank && p->instr == instr) {
+			/* remove this layer */
+			if (prev)
+				prev->next = next;
+			else
+				sf->zones = next;
+			removed++;
+			kfree(p);
+		} else
+			prev = p;
+	}
+	if (removed)
+		rebuild_presets(sflist);
+	return removed;
+}
+
+
+/*
+ * Read an info record from the user buffer and save it on the current
+ * open soundfont.
+ */
+static int
+load_info(snd_sf_list_t *sflist, const void *data, long count)
+{
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *zone;
+	soundfont_voice_rec_hdr_t hdr;
+	int i;
+
+	/* patch must be opened */
+	if ((sf = sflist->currsf) == NULL)
+		return -EINVAL;
+
+	if (is_special_type(sf->type))
+		return -EINVAL;
+
+	if (count < sizeof(hdr)) {
+		printk("Soundfont error: invalid patch zone length\n");
+		return -EINVAL;
+	}
+	if (copy_from_user((char*)&hdr, data, sizeof(hdr)))
+		return -EFAULT;
+	
+	data += sizeof(hdr);
+	count -= sizeof(hdr);
+
+	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+		printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices);
+		return -EINVAL;
+	}
+
+	if (count < sizeof(soundfont_voice_info_t)*hdr.nvoices) {
+		printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n",
+		       count, hdr.nvoices);
+		return -EINVAL;
+	}
+
+	switch (hdr.write_mode) {
+	case SNDRV_SFNT_WR_EXCLUSIVE:
+		/* exclusive mode - if the instrument already exists,
+		   return error */
+		for (zone = sf->zones; zone; zone = zone->next) {
+			if (!zone->mapped &&
+			    zone->bank == hdr.bank &&
+			    zone->instr == hdr.instr)
+				return -EINVAL;
+		}
+		break;
+	case SNDRV_SFNT_WR_REPLACE:
+		/* replace mode - remove the instrument if it already exists */
+		remove_info(sflist, sf, hdr.bank, hdr.instr);
+		break;
+	}
+
+	for (i = 0; i < hdr.nvoices; i++) {
+		snd_sf_zone_t tmpzone;
+
+		/* copy awe_voice_info parameters */
+		if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) {
+			return -EFAULT;
+		}
+
+		data += sizeof(tmpzone.v);
+		count -= sizeof(tmpzone.v);
+
+		tmpzone.bank = hdr.bank;
+		tmpzone.instr = hdr.instr;
+		tmpzone.mapped = 0;
+		tmpzone.v.sf_id = sf->id;
+		if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM)
+			init_voice_parm(&tmpzone.v.parm);
+
+		/* create a new zone */
+		if ((zone = sf_zone_new(sflist, sf)) == NULL) {
+			return -ENOMEM;
+		}
+
+		/* copy the temporary data */
+		zone->bank = tmpzone.bank;
+		zone->instr = tmpzone.instr;
+		zone->v = tmpzone.v;
+
+		/* look up the sample */
+		zone->sample = set_sample(sf, &zone->v);
+	}
+
+	return 0;
+}
+
+
+/* initialize voice_info record */
+static void
+init_voice_info(soundfont_voice_info_t *avp)
+{
+	memset(avp, 0, sizeof(*avp));
+
+	avp->root = 60;
+	avp->high = 127;
+	avp->velhigh = 127;
+	avp->fixkey = -1;
+	avp->fixvel = -1;
+	avp->fixpan = -1;
+	avp->pan = -1;
+	avp->amplitude = 127;
+	avp->scaleTuning = 100;
+
+	init_voice_parm(&avp->parm);
+}
+
+/* initialize voice_parm record:
+ * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
+ * Vibrato and Tremolo effects are zero.
+ * Cutoff is maximum.
+ * Chorus and Reverb effects are zero.
+ */
+static void
+init_voice_parm(soundfont_voice_parm_t *pp)
+{
+	memset(pp, 0, sizeof(*pp));
+
+	pp->moddelay = 0x8000;
+	pp->modatkhld = 0x7f7f;
+	pp->moddcysus = 0x7f7f;
+	pp->modrelease = 0x807f;
+
+	pp->voldelay = 0x8000;
+	pp->volatkhld = 0x7f7f;
+	pp->voldcysus = 0x7f7f;
+	pp->volrelease = 0x807f;
+
+	pp->lfo1delay = 0x8000;
+	pp->lfo2delay = 0x8000;
+
+	pp->cutoff = 0xff;
+}	
+
+/* search the specified sample */
+static snd_sf_sample_t *
+set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp)
+{
+	snd_sf_sample_t *sample;
+
+	sample = find_sample(sf, avp->sample);
+	if (sample == NULL)
+		return NULL;
+
+	/* add in the actual sample offsets:
+	 * The voice_info addresses define only the relative offset
+	 * from sample pointers.  Here we calculate the actual DRAM
+	 * offset from sample pointers.
+	 */
+	avp->start += sample->v.start;
+	avp->end += sample->v.end;
+	avp->loopstart += sample->v.loopstart;
+	avp->loopend += sample->v.loopend;
+
+	/* copy mode flags */
+	avp->sample_mode = sample->v.mode_flags;
+
+	return sample;
+}
+
+/* find the sample pointer with the given id in the soundfont */
+static snd_sf_sample_t *
+find_sample(snd_soundfont_t *sf, int sample_id)
+{
+	snd_sf_sample_t *p;
+
+	if (sf == NULL)
+		return NULL;
+
+	for (p = sf->samples; p; p = p->next) {
+		if (p->v.sample == sample_id)
+			return p;
+	}
+	return NULL;
+}
+
+
+/*
+ * Load sample information, this can include data to be loaded onto
+ * the soundcard.  It can also just be a pointer into soundcard ROM.
+ * If there is data it will be written to the soundcard via the callback
+ * routine.
+ */
+static int
+load_data(snd_sf_list_t *sflist, const void *data, long count)
+{
+	snd_soundfont_t *sf;
+	soundfont_sample_info_t sample_info;
+	snd_sf_sample_t *sp;
+	long off;
+
+	/* patch must be opened */
+	if ((sf = sflist->currsf) == NULL)
+		return -EINVAL;
+
+	if (is_special_type(sf->type))
+		return -EINVAL;
+
+	if (copy_from_user(&sample_info, data, sizeof(sample_info)))
+		return -EFAULT;
+
+	off = sizeof(sample_info);
+
+	if (sample_info.size != (count-off)/2)
+		return -EINVAL;
+
+	/* Check for dup */
+	if (find_sample(sf, sample_info.sample)) {
+		/* if shared sample, skip this data */
+		if (sf->type & SNDRV_SFNT_PAT_SHARED)
+			return 0;
+		return -EINVAL;
+	}
+
+	/* Allocate a new sample structure */
+	if ((sp = sf_sample_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+
+	sp->v = sample_info;
+	sp->v.sf_id = sf->id;
+	sp->v.dummy = 0;
+	sp->v.truesize = sp->v.size;
+
+	/*
+	 * If there is wave data then load it.
+	 */
+	if (sp->v.size > 0) {
+		int  rc;
+		rc = sflist->callback.sample_new
+			(sflist->callback.private_data, sp, sflist->memhdr,
+			 data + off, count - off);
+		if (rc < 0) {
+			sf_sample_delete(sflist, sf, sp);
+			return rc;
+		}
+		sflist->mem_used += sp->v.truesize;
+	}
+
+	return count;
+}
+
+
+/* log2_tbl[i] = log2(i+128) * 0x10000 */
+static int log_tbl[129] = {
+	0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa,
+	0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed,
+	0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08,
+	0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019,
+	0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a,
+	0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382,
+	0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404,
+	0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2,
+	0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9,
+	0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188,
+	0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89,
+	0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07,
+	0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c,
+	0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f,
+	0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8,
+	0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d,
+	0x80000,
+};
+
+/* convert from linear to log value
+ *
+ * conversion: value = log2(amount / base) * ratio
+ *
+ * argument:
+ *   amount = linear value (unsigned, 32bit max)
+ *   offset = base offset (:= log2(base) * 0x10000)
+ *   ratio = division ratio
+ *
+ */
+int
+snd_sf_linear_to_log(unsigned int amount, int offset, int ratio)
+{
+	int v;
+	int s, low, bit;
+	
+	if (amount < 2)
+		return 0;
+	for (bit = 0; ! (amount & 0x80000000L); bit++)
+		amount <<= 1;
+	s = (amount >> 24) & 0x7f;
+	low = (amount >> 16) & 0xff;
+	/* linear approxmimation by lower 8 bit */
+	v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8;
+	v -= offset;
+	v = (v * ratio) >> 16;
+	v += (24 - bit) * ratio;
+	return v;
+}
+
+#define OFFSET_MSEC		653117		/* base = 1000 */
+#define OFFSET_ABSCENT		851781		/* base = 8176 */
+#define OFFSET_SAMPLERATE	1011119		/* base = 44100 */
+
+#define ABSCENT_RATIO		1200
+#define TIMECENT_RATIO		1200
+#define SAMPLERATE_RATIO	4096
+
+/*
+ * mHz to abscent
+ * conversion: abscent = log2(MHz / 8176) * 1200
+ */
+static int
+freq_to_note(int mhz)
+{
+	return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO);
+}
+
+/* convert Hz to AWE32 rate offset:
+ * sample pitch offset for the specified sample rate
+ * rate=44100 is no offset, each 4096 is 1 octave (twice).
+ * eg, when rate is 22050, this offset becomes -4096.
+ *
+ * conversion: offset = log2(Hz / 44100) * 4096
+ */
+static int
+calc_rate_offset(int hz)
+{
+	return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO);
+}
+
+
+/* calculate GUS envelope time */
+static int
+calc_gus_envelope_time(int rate, int start, int end)
+{
+	int r, p, t;
+	r = (3 - ((rate >> 6) & 3)) * 3;
+	p = rate & 0x3f;
+	t = end - start;
+	if (t < 0) t = -t;
+	if (13 > r)
+		t = t << (13 - r);
+	else
+		t = t >> (r - 13);
+	return (t * 10) / (p * 441);
+}
+
+/* convert envelope time parameter to soundfont parameters */
+
+/* attack & decay/release time table (msec) */
+static short attack_time_tbl[128] = {
+32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
+707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
+361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
+180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
+90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
+45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
+22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
+};
+
+static short decay_time_tbl[128] = {
+32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
+2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
+1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
+691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
+345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
+172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
+86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
+43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
+};
+
+/* delay time = 0x8000 - msec/92 */
+int
+snd_sf_calc_parm_hold(int msec)
+{
+	int val = (0x7f * 92 - msec) / 92;
+	if (val < 1) val = 1;
+	if (val >= 126) val = 126;
+	return val;
+}
+
+/* search an index for specified time from given time table */
+static int
+calc_parm_search(int msec, short *table)
+{
+	int left = 1, right = 127, mid;
+	while (left < right) {
+		mid = (left + right) / 2;
+		if (msec < (int)table[mid])
+			left = mid + 1;
+		else
+			right = mid;
+	}
+	return left;
+}
+
+/* attack time: search from time table */
+int
+snd_sf_calc_parm_attack(int msec)
+{
+	return calc_parm_search(msec, attack_time_tbl);
+}
+
+/* decay/release time: search from time table */
+int
+snd_sf_calc_parm_decay(int msec)
+{
+	return calc_parm_search(msec, decay_time_tbl);
+}
+
+int snd_sf_vol_table[128] = {
+	255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
+	47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
+	31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
+	22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
+	15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
+	10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
+	6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
+	2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
+};
+
+
+#define calc_gus_sustain(val)  (0x7f - snd_sf_vol_table[(val)/2])
+#define calc_gus_attenuation(val)	snd_sf_vol_table[(val)/2]
+
+/* load GUS patch */
+static int
+load_guspatch(snd_sf_list_t *sflist, const char *data, long count, int client)
+{
+	struct patch_info patch;
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *zone;
+	snd_sf_sample_t *smp;
+	int note, sample_id;
+	int rc;
+
+	if (count < sizeof(patch)) {
+		snd_printk("patch record too small %ld\n", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(&patch, data, sizeof(patch)))
+		return -EFAULT;
+	
+	count -= sizeof(patch);
+	data += sizeof(patch);
+
+	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL);
+	if (sf == NULL)
+		return -ENOMEM;
+	if ((smp = sf_sample_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+	sample_id = sflist->sample_counter;
+	smp->v.sample = sample_id;
+	smp->v.start = 0;
+	smp->v.end = patch.len;
+	smp->v.loopstart = patch.loop_start;
+	smp->v.loopend = patch.loop_end;
+	smp->v.size = patch.len;
+
+	/* set up mode flags */
+	smp->v.mode_flags = 0;
+	if (!(patch.mode & WAVE_16_BITS))
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS;
+	if (patch.mode & WAVE_UNSIGNED)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED;
+	smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK;
+	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT;
+	if (patch.mode & WAVE_BIDIR_LOOP)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP;
+	if (patch.mode & WAVE_LOOP_BACK)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP;
+
+	if (patch.mode & WAVE_16_BITS) {
+		/* convert to word offsets */
+		smp->v.size /= 2;
+		smp->v.end /= 2;
+		smp->v.loopstart /= 2;
+		smp->v.loopend /= 2;
+	}
+	/*smp->v.loopend++;*/
+
+	smp->v.dummy = 0;
+	smp->v.truesize = 0;
+	smp->v.sf_id = sf->id;
+
+	/* set up voice info */
+	if ((zone = sf_zone_new(sflist, sf)) == NULL) {
+		sf_sample_delete(sflist, sf, smp);
+		return -ENOMEM;
+	}
+
+	/*
+	 * load wave data
+	 */
+	if (sflist->callback.sample_new) {
+		rc = sflist->callback.sample_new
+			(sflist->callback.private_data, smp, sflist->memhdr, data, count);
+		if (rc < 0) {
+			sf_sample_delete(sflist, sf, smp);
+			return rc;
+		}
+		/* memory offset is updated after */
+	}
+
+	/* update the memory offset here */
+	sflist->mem_used += smp->v.truesize;
+
+	zone->v.sample = sample_id; /* the last sample */
+	zone->v.rate_offset = calc_rate_offset(patch.base_freq);
+	note = freq_to_note(patch.base_note);
+	zone->v.root = note / 100;
+	zone->v.tune = -(note % 100);
+	zone->v.low = (freq_to_note(patch.low_note) + 99) / 100;
+	zone->v.high = freq_to_note(patch.high_note) / 100;
+	/* panning position; -128 - 127 => 0-127 */
+	zone->v.pan = (patch.panning + 128) / 2;
+#if 0
+	snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
+		   (int)patch.base_freq, zone->v.rate_offset,
+		   zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
+#endif
+
+	/* detuning is ignored */
+	/* 6points volume envelope */
+	if (patch.mode & WAVE_ENVELOPES) {
+		int attack, hold, decay, release;
+		attack = calc_gus_envelope_time
+			(patch.env_rate[0], 0, patch.env_offset[0]);
+		hold = calc_gus_envelope_time
+			(patch.env_rate[1], patch.env_offset[0],
+			 patch.env_offset[1]);
+		decay = calc_gus_envelope_time
+			(patch.env_rate[2], patch.env_offset[1],
+			 patch.env_offset[2]);
+		release = calc_gus_envelope_time
+			(patch.env_rate[3], patch.env_offset[1],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[4], patch.env_offset[3],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[5], patch.env_offset[4],
+			 patch.env_offset[5]);
+		zone->v.parm.volatkhld = 
+			(snd_sf_calc_parm_hold(hold) << 8) |
+			snd_sf_calc_parm_attack(attack);
+		zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+			snd_sf_calc_parm_decay(decay);
+		zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release);
+		zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
+#if 0
+		snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
+			   zone->v.parm.volatkhld,
+			   zone->v.parm.voldcysus,
+			   zone->v.parm.volrelease,
+			   zone->v.attenuation);
+#endif
+	}
+
+	/* fast release */
+	if (patch.mode & WAVE_FAST_RELEASE) {
+		zone->v.parm.volrelease = 0x807f;
+	}
+
+	/* tremolo effect */
+	if (patch.mode & WAVE_TREMOLO) {
+		int rate = (patch.tremolo_rate * 1000 / 38) / 42;
+		zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+	}
+	/* vibrato effect */
+	if (patch.mode & WAVE_VIBRATO) {
+		int rate = (patch.vibrato_rate * 1000 / 38) / 42;
+		zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+	}
+	
+	/* scale_freq, scale_factor, volume, and fractions not implemented */
+
+	if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT))
+		zone->v.mode = SNDRV_SFNT_MODE_LOOPING;
+	else
+		zone->v.mode = 0;
+
+	/* append to the tail of the list */
+	/*zone->bank = ctrls[AWE_MD_GUS_BANK];*/
+	zone->bank = 0;
+	zone->instr = patch.instr_no;
+	zone->mapped = 0;
+	zone->v.sf_id = sf->id;
+
+	zone->sample = set_sample(sf, &zone->v);
+
+	/* rebuild preset now */
+	add_preset(sflist, zone);
+
+	return 0;
+}
+
+/* load GUS patch */
+int
+snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data,
+			    long count, int client)
+{
+	int rc;
+	lock_preset(sflist, 0);
+	rc = load_guspatch(sflist, data, count, client);
+	unlock_preset(sflist);
+	return rc;
+}
+
+
+/*
+ * Rebuild the preset table.  This is like a hash table in that it allows
+ * quick access to the zone information.  For each preset there are zone
+ * structures linked by next_instr and by next_zone.  Former is the whole
+ * link for this preset, and latter is the link for zone (i.e. instrument/
+ * bank/key combination).
+ */
+static void
+rebuild_presets(snd_sf_list_t *sflist)
+{
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *cur;
+
+	/* clear preset table */
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	/* search all fonts and insert each font */
+	for (sf = sflist->fonts; sf; sf = sf->next) {
+		for (cur = sf->zones; cur; cur = cur->next) {
+			if (! cur->mapped && cur->sample == NULL) {
+				/* try again to search the corresponding sample */
+				cur->sample = set_sample(sf, &cur->v);
+				if (cur->sample == NULL)
+					continue;
+			}
+
+			add_preset(sflist, cur);
+		}
+	}
+}
+
+
+/*
+ * add the given zone to preset table
+ */
+static void
+add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur)
+{
+	snd_sf_zone_t *zone;
+	int index;
+
+	zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low);
+	if (zone && zone->v.sf_id != cur->v.sf_id) {
+		/* different instrument was already defined */
+		snd_sf_zone_t *p;
+		/* compare the allocated time */
+		for (p = zone; p; p = p->next_zone) {
+			if (p->counter > cur->counter)
+				/* the current is older.. skipped */
+				return;
+		}
+		/* remove old zones */
+		delete_preset(sflist, zone);
+		zone = NULL; /* do not forget to clear this! */
+	}
+
+	/* prepend this zone */
+	if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0)
+		return;
+	cur->next_zone = zone; /* zone link */
+	cur->next_instr = sflist->presets[index]; /* preset table link */
+	sflist->presets[index] = cur;
+}
+
+/*
+ * delete the given zones from preset_table
+ */
+static void
+delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp)
+{
+	int index;
+	snd_sf_zone_t *p;
+
+	if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0)
+		return;
+	for (p = sflist->presets[index]; p; p = p->next_instr) {
+		while (p->next_instr == zp) {
+			p->next_instr = zp->next_instr;
+			zp = zp->next_zone;
+			if (zp == NULL)
+				return;
+		}
+	}
+}
+
+
+/*
+ * Search matching zones from preset table.
+ * The note can be rewritten by preset mapping (alias).
+ * The found zones are stored on 'table' array.  max_layers defines
+ * the maximum number of elements in this array.
+ * This function returns the number of found zones.  0 if not found.
+ */
+int
+snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel,
+			  int preset, int bank,
+			  int def_preset, int def_bank,
+			  snd_sf_zone_t **table, int max_layers)
+{
+	int nvoices;
+
+	if (lock_preset(sflist, 1))
+		return 0;
+
+	nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0);
+	if (! nvoices) {
+		if (preset != def_preset || bank != def_bank)
+			nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0);
+	}
+	unlock_preset(sflist);
+
+	return nvoices;
+}
+
+
+/*
+ * search the first matching zone
+ */
+static snd_sf_zone_t *
+search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key)
+{
+	int index;
+	snd_sf_zone_t *zp;
+
+	if ((index = get_index(bank, preset, key)) < 0)
+		return NULL;
+	for (zp = sflist->presets[index]; zp; zp = zp->next_instr) {
+		if (zp->instr == preset && zp->bank == bank)
+			return zp;
+	}
+	return NULL;
+}
+
+
+/*
+ * search matching zones from sflist.  can be called recursively.
+ */
+static int
+search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level)
+{
+	snd_sf_zone_t *zp;
+	int nvoices;
+
+	zp = search_first_zone(sflist, bank, preset, *notep);
+	nvoices = 0;
+	for (; zp; zp = zp->next_zone) {
+		if (*notep >= zp->v.low && *notep <= zp->v.high &&
+		    vel >= zp->v.vellow && vel <= zp->v.velhigh) {
+			if (zp->mapped) {
+				/* search preset mapping (aliasing) */
+				int key = zp->v.fixkey;
+				preset = zp->v.start;
+				bank = zp->v.end;
+
+				if (level > 5) /* too deep alias level */
+					return 0;
+				if (key < 0)
+					key = *notep;
+				nvoices = search_zones(sflist, &key, vel,
+						       preset, bank, table,
+						       max_layers, level + 1);
+				if (nvoices > 0)
+					*notep = key;
+				break;
+			}
+			table[nvoices++] = zp;
+			if (nvoices >= max_layers)
+				break;
+		}
+	}
+
+	return nvoices;
+}
+
+
+/* calculate the index of preset table:
+ * drums are mapped from 128 to 255 according to its note key.
+ * other instruments are mapped from 0 to 127.
+ * if the index is out of range, return -1.
+ */
+static int
+get_index(int bank, int instr, int key)
+{
+	int index;
+	if (SF_IS_DRUM_BANK(bank))
+		index = key + SF_MAX_INSTRUMENTS;
+	else
+		index = instr;
+	index = index % SF_MAX_PRESETS;
+	if (index < 0)
+		return -1;
+	return index;
+}
+
+/*
+ * Initialise the sflist structure.
+ */
+static void
+snd_sf_init(snd_sf_list_t *sflist)
+{
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	sflist->mem_used = 0;
+	sflist->currsf = NULL;
+	sflist->open_client = -1;
+	sflist->fonts = NULL;
+	sflist->fonts_size = 0;
+	sflist->zone_counter = 0;
+	sflist->sample_counter = 0;
+	sflist->zone_locked = 0;
+	sflist->sample_locked = 0;
+}
+
+/*
+ * Release all list records
+ */
+static void
+snd_sf_clear(snd_sf_list_t *sflist)
+{
+	snd_soundfont_t *sf, *nextsf;
+	snd_sf_zone_t *zp, *nextzp;
+	snd_sf_sample_t *sp, *nextsp;
+
+	for (sf = sflist->fonts; sf; sf = nextsf) {
+		nextsf = sf->next;
+		for (zp = sf->zones; zp; zp = nextzp) {
+			nextzp = zp->next;
+			kfree(zp);
+		}
+		for (sp = sf->samples; sp; sp = nextsp) {
+			nextsp = sp->next;
+			if (sflist->callback.sample_free)
+				sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr);
+			kfree(sp);
+		}
+		kfree(sf);
+	}
+
+	snd_sf_init(sflist);
+}
+
+
+/*
+ * Create a new sflist structure
+ */
+snd_sf_list_t *
+snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr)
+{
+	snd_sf_list_t *sflist;
+
+	if ((sflist = snd_kcalloc(sizeof(snd_sf_list_t), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	init_MUTEX(&sflist->presets_mutex);
+	spin_lock_init(&sflist->lock);
+	sflist->sf_locked = 0;
+	sflist->memhdr = hdr;
+
+	if (callback)
+		sflist->callback = *callback;
+
+	snd_sf_init(sflist);
+	return sflist;
+}
+
+
+/*
+ * Free everything allocated off the sflist structure.
+ */
+void
+snd_sf_free(snd_sf_list_t *sflist)
+{
+	if (sflist == NULL)
+		return;
+	
+	lock_preset(sflist, 0);
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+	snd_sf_clear(sflist);
+	unlock_preset(sflist);
+
+	kfree(sflist);
+}
+
+/*
+ * Remove all samples
+ * The soundcard should be silet before calling this function.
+ */
+int
+snd_soundfont_remove_samples(snd_sf_list_t *sflist)
+{
+	lock_preset(sflist, 0);
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+	snd_sf_clear(sflist);
+	unlock_preset(sflist);
+
+	return 0;
+}
+
+/*
+ * Remove unlocked samples.
+ * The soundcard should be silet before calling this function.
+ */
+int
+snd_soundfont_remove_unlocked(snd_sf_list_t *sflist)
+{
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *zp, *nextzp;
+	snd_sf_sample_t *sp, *nextsp;
+
+	if (lock_preset(sflist, 1))
+		return -EBUSY;
+
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+
+	/* to be sure */
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	for (sf = sflist->fonts; sf; sf = sf->next) {
+		for (zp = sf->zones; zp; zp = nextzp) {
+			if (zp->counter < sflist->zone_locked)
+				break;
+			nextzp = zp->next;
+			sf->zones = nextzp;
+			kfree(zp);
+		}
+
+		for (sp = sf->samples; sp; sp = nextsp) {
+			if (sp->counter < sflist->sample_locked)
+				break;
+			nextsp = sp->next;
+			sf->samples = nextsp;
+			sflist->mem_used -= sp->v.truesize;
+			if (sflist->callback.sample_free)
+				sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr);
+			kfree(sp);
+		}
+	}
+
+	sflist->zone_counter = sflist->zone_locked;
+	sflist->sample_counter = sflist->sample_locked;
+
+	rebuild_presets(sflist);
+
+	unlock_preset(sflist);
+	return 0;
+}
+
+/*
+ * Return the used memory size (in words)
+ */
+int
+snd_soundfont_mem_used(snd_sf_list_t *sflist)
+{
+	return sflist->mem_used;
+}
diff -Nru linux/sound/synth/util_mem.c linux-2.4.19-pre5-mjc/sound/synth/util_mem.c
--- linux/sound/synth/util_mem.c	Wed Dec 31 19:00:00 1969
+++ linux-2.4.19-pre5-mjc/sound/synth/util_mem.c	Mon Apr  8 22:31:24 2002
@@ -0,0 +1,207 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Generic memory management routines for soundcard memory allocation
+ *
+ *   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
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/util_mem.h>
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation");
+MODULE_LICENSE("GPL");
+
+#define get_memblk(p)	list_entry(p, snd_util_memblk_t, list)
+
+/*
+ * create a new memory manager
+ */
+snd_util_memhdr_t *
+snd_util_memhdr_new(int memsize)
+{
+	snd_util_memhdr_t *hdr;
+
+	hdr = snd_kcalloc(sizeof(*hdr), GFP_KERNEL);
+	if (hdr == NULL)
+		return NULL;
+	hdr->size = memsize;
+	init_MUTEX(&hdr->block_mutex);
+	INIT_LIST_HEAD(&hdr->block);
+
+	return hdr;
+}
+
+/*
+ * free a memory manager
+ */
+void snd_util_memhdr_free(snd_util_memhdr_t *hdr)
+{
+	struct list_head *p;
+
+	snd_assert(hdr != NULL, return);
+	/* release all blocks */
+	while ((p = hdr->block.next) != &hdr->block) {
+		list_del(p);
+		kfree(get_memblk(p));
+	}
+	kfree(hdr);
+}
+
+/*
+ * allocate a memory block (without mutex)
+ */
+snd_util_memblk_t *
+__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size)
+{
+	snd_util_memblk_t *blk;
+	snd_util_unit_t units, prev_offset;
+	struct list_head *p;
+
+	snd_assert(hdr != NULL, return NULL);
+	snd_assert(size > 0, return NULL);
+
+	/* word alignment */
+	units = size;
+	if (units & 1)
+		units++;
+	if (units > hdr->size)
+		return NULL;
+
+	/* look for empty block */
+	prev_offset = 0;
+	list_for_each(p, &hdr->block) {
+		blk = get_memblk(p);
+		if (blk->offset - prev_offset >= units)
+			goto __found;
+		prev_offset = blk->offset + blk->size;
+	}
+	if (hdr->size - prev_offset < units)
+		return NULL;
+
+__found:
+	return __snd_util_memblk_new(hdr, units, p->prev);
+}
+
+
+/*
+ * create a new memory block with the given size
+ * the block is linked next to prev
+ */
+snd_util_memblk_t *
+__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units,
+		      struct list_head *prev)
+{
+	snd_util_memblk_t *blk;
+
+	blk = kmalloc(sizeof(snd_util_memblk_t) + hdr->block_extra_size, GFP_KERNEL);
+	if (blk == NULL)
+		return NULL;
+
+	if (! prev || prev == &hdr->block)
+		blk->offset = 0;
+	else {
+		snd_util_memblk_t *p = get_memblk(prev);
+		blk->offset = p->offset + p->size;
+	}
+	blk->size = units;
+	list_add(&blk->list, prev);
+	hdr->nblocks++;
+	hdr->used += units;
+	return blk;
+}
+
+
+/*
+ * allocate a memory block (with mutex)
+ */
+snd_util_memblk_t *
+snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size)
+{
+	snd_util_memblk_t *blk;
+	down(&hdr->block_mutex);
+	blk = __snd_util_mem_alloc(hdr, size);
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+
+/*
+ * remove the block from linked-list and free resource
+ * (without mutex)
+ */
+void
+__snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk)
+{
+	list_del(&blk->list);
+	hdr->nblocks--;
+	hdr->used -= blk->size;
+	kfree(blk);
+}
+
+/*
+ * free a memory block (with mutex)
+ */
+int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk)
+{
+	snd_assert(hdr && blk, return -EINVAL);
+
+	down(&hdr->block_mutex);
+	__snd_util_mem_free(hdr, blk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+/*
+ * return available memory size
+ */
+int snd_util_mem_avail(snd_util_memhdr_t *hdr)
+{
+	unsigned int size;
+	down(&hdr->block_mutex);
+	size = hdr->size - hdr->used;
+	up(&hdr->block_mutex);
+	return size;
+}
+
+
+EXPORT_SYMBOL(snd_util_memhdr_new);
+EXPORT_SYMBOL(snd_util_memhdr_free);
+EXPORT_SYMBOL(snd_util_mem_alloc);
+EXPORT_SYMBOL(snd_util_mem_free);
+EXPORT_SYMBOL(snd_util_mem_avail);
+EXPORT_SYMBOL(__snd_util_mem_alloc);
+EXPORT_SYMBOL(__snd_util_mem_free);
+EXPORT_SYMBOL(__snd_util_memblk_new);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_util_mem_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_util_mem_exit(void)
+{
+}
+
+module_init(alsa_util_mem_init)
+module_exit(alsa_util_mem_exit)
